diff --git a/crates/context/assets/component-review.js b/crates/context/assets/component-review.js index 73399d7fc..2979ff6ce 100644 --- a/crates/context/assets/component-review.js +++ b/crates/context/assets/component-review.js @@ -1,4 +1,4 @@ -(()=>{function Li(p,o){let x=o?.changes[p],e=o?.feedback?.[p],n=e?.decision??(o?.submitted?o.draft.decisions[p]:void 0),t=x?.kind==="unchanged"&&(x.carried??(o?.submitted&&n?.action==="approve"))===!0;return{change:x,prior:n,feedbackRound:e?.round??o?.packet.round,carried:t,label:x?.kind==="added"?"New component":x?.kind==="changed"?"Review again":t?"Approval kept":n?.action==="revise"?"Changes still requested":"Awaiting review"}}function R(p,o,x){let e=o.decisions[p.id],n=e?.revision===p.revision?e:void 0,t=Li(p.id,x);if(n?.action==="approve")return{kind:"approved",label:t.carried?"Approval kept":"Approved",priority:3};if(n?.action==="revise")return{kind:"feedback",label:"Feedback ready",priority:2};return{kind:"pending",label:t.change?.kind==="changed"?"Review again":t.change?.kind==="added"?"New · review needed":"Not reviewed",priority:t.change?.kind==="changed"||t.change?.kind==="added"?0:1}}function ko(p){return{packetRevision:p.revision,decisions:{},missing:[],inventoryConfirmed:!1}}function Io(p){return Object.values(p).every(Number.isFinite)&&p.x>=0&&p.y>=0&&p.w>0&&p.h>0&&p.x+p.w<=1.00001&&p.y+p.h<=1.00001}function wi(p,o){let x=p.components.map((h)=>o.decisions[h.id]?.revision===h.revision?o.decisions[h.id]:void 0),e=x.filter((h)=>h?.action==="approve").length,n=x.filter((h)=>h?.action==="revise").length,t=x.length-e-n,ni=n>0||o.missing.length>0;return{approved:e,revisions:n,pending:t,hasFeedback:ni,canSubmit:o.packetRevision===p.revision&&o.missing.every((h)=>h.name.trim()&&Io(h.box))&&(ni||!t&&o.inventoryConfirmed)}}function jo(p,o){let x={...o.decisions};for(let e of p.components)if(!x[e.id]||x[e.id].revision!==e.revision)x[e.id]={revision:e.revision,action:"approve",feedback:"",split:!1};return{...o,decisions:x}}function qo(p,o){if(!wi(p,o).canSubmit)throw Error("Review is incomplete or stale");return{schemaVersion:1,requestId:p.id,...structuredClone(o)}}function ci(p){let o=p.preview.kind==="page"||p.preview.sourceKind==="page",x=p.preview.sourceKind==="page";return{code:o,captured:x,label:o?p.medium.match(/html|css|svg/i)?p.medium:"HTML / CSS / SVG":"Raster",caption:o?x?p.preview.isolation?"Component only":"Region capture":"Live component":"Produced asset",fileLabel:x?"Open captured preview":"Open source image"}}function Mo(p,o,x){let e=p.components.findIndex((n)=>n.id===x);for(let n=1;n<=p.components.length;n++){let t=p.components[(e+n)%p.components.length];if(R(t,o).kind==="pending")return t.id}}function Ri(p,o,x){return x==="all"||R(p,o).kind==="pending"===(x==="pending")}function Di(p,o){if(!o.reviewGroup||!ci(o).code)return[o];return p.components.filter((x)=>x.reviewGroup===o.reviewGroup&&ci(x).code)}function Ni(p,o,x,e,n=[]){return(e?Di(p,x):[x]).filter((t)=>t.id===x.id||R(t,o).kind==="pending"&&!n.includes(t.id))}function Si(p,o,x,e,n){let t=n==="fit"?Math.min(1,x/p,e/o):n;return{scale:t,width:p*t,height:o*t}}function Fi(p,o,x,e){if(x<=0||e<=x)return 0;return Math.max(0,Math.min(1,((p-o)/x-0.08)/0.84))*(e-x)}var Ho=` +(()=>{function Ni(p,o){let x=o?.changes[p],e=o?.feedback?.[p],n=e?.decision??(o?.submitted?o.draft.decisions[p]:void 0),a=x?.kind==="unchanged"&&(x.carried??(o?.submitted&&n?.action==="approve"))===!0;return{change:x,prior:n,feedbackRound:e?.round??o?.packet.round,carried:a,label:x?.kind==="added"?"New component":x?.kind==="changed"?"Review again":a?"Approval kept":n?.action==="revise"?"Changes still requested":"Awaiting review"}}function I(p,o,x){let e=o.decisions[p.id],n=e?.revision===p.revision?e:void 0,a=Ni(p.id,x);if(n?.action==="approve")return{kind:"approved",label:a.carried?"Approval kept":"Approved",priority:3};if(n?.action==="revise")return{kind:"feedback",label:"Feedback ready",priority:2};return{kind:"pending",label:a.change?.kind==="changed"?"Review again":a.change?.kind==="added"?"New · review needed":"Not reviewed",priority:a.change?.kind==="changed"||a.change?.kind==="added"?0:1}}function Jo(p){return{packetRevision:p.revision,decisions:{},missing:[],inventoryConfirmed:!1}}function ir(p){return Object.values(p).every(Number.isFinite)&&p.x>=0&&p.y>=0&&p.w>0&&p.h>0&&p.x+p.w<=1.00001&&p.y+p.h<=1.00001}function zi(p,o){let x=p.components.map((g)=>o.decisions[g.id]?.revision===g.revision?o.decisions[g.id]:void 0),e=x.filter((g)=>g?.action==="approve").length,n=x.filter((g)=>g?.action==="revise").length,a=x.length-e-n,b=n>0||o.missing.length>0;return{approved:e,revisions:n,pending:a,hasFeedback:b,canSubmit:o.packetRevision===p.revision&&o.missing.every((g)=>g.name.trim()&&ir(g.box))&&(b||!a&&o.inventoryConfirmed)}}function $o(p,o){let x={...o.decisions};for(let e of p.components)if(!x[e.id]||x[e.id].revision!==e.revision)x[e.id]={revision:e.revision,action:"approve",feedback:"",split:!1};return{...o,decisions:x}}function Ko(p,o){if(!zi(p,o).canSubmit)throw Error("Review is incomplete or stale");return{schemaVersion:1,requestId:p.id,...structuredClone(o)}}function yi(p){let o=p.preview.kind==="page"||p.preview.sourceKind==="page",x=p.preview.sourceKind==="page";return{code:o,captured:x,label:o?p.medium.match(/html|css|svg/i)?p.medium:"HTML / CSS / SVG":"Raster",caption:o?x?p.preview.isolation?"Component only":"Region capture":"Live component":"Produced asset",fileLabel:x?"Open captured preview":"Open source image"}}function Qo(p,o,x){let e=p.components.findIndex((n)=>n.id===x);for(let n=1;n<=p.components.length;n++){let a=p.components[(e+n)%p.components.length];if(I(a,o).kind==="pending")return a.id}}function Wi(p,o){if(!o.reviewGroup||!yi(o).code)return[o];return p.components.filter((x)=>x.reviewGroup===o.reviewGroup&&yi(x).code)}function Mi(p,o,x,e,n=[]){let a=e?Wi(p,x):[x];if(a.length===1)return[x];return a.filter((b)=>I(b,o).kind==="pending"&&(b.id===x.id||!n.includes(b.id)))}function Yo(p,o,x){let e=new Set;return p.components.flatMap((n)=>{if(e.has(n.id))return[];let a=Wi(p,n);a.forEach((s)=>e.add(s.id));let b=a.filter((s)=>I(s,o,x).kind==="pending"),g=a.filter((s)=>I(s,o,x).kind==="feedback"),z=b[0]??g[0]??n,_=b.length?"pending":g.length?"feedback":"approved";return[{id:n.id,members:a,representative:z,pending:b.length,kind:_,label:a.length>1?n.reviewGroup.replace(/[-_]+/g," ").replace(/^./,(s)=>s.toUpperCase()):n.name,box:{x:Math.min(...a.map((s)=>s.box.x)),y:Math.min(...a.map((s)=>s.box.y)),w:Math.max(...a.map((s)=>s.box.x+s.box.w))-Math.min(...a.map((s)=>s.box.x)),h:Math.max(...a.map((s)=>s.box.y+s.box.h))-Math.min(...a.map((s)=>s.box.y))},stateLabel:b.length?a.length>1?`${b.length} to review`:I(z,o,x).label:g.length?a.length>1?`${g.length} ${g.length===1?"needs":"need"} work`:"Feedback ready":"Approved"}]})}function io(p,o){let x=new Set(o.preview.isolation?.excludedComponents??[]),e=o.box,n=p.components.filter((a)=>{if(a.id===o.id)return!1;if(x.has(a.id))return!0;let b=a.box,g=Math.max(0,Math.min(e.x+e.w,b.x+b.w)-Math.max(e.x,b.x))*Math.max(0,Math.min(e.y+e.h,b.y+b.h)-Math.max(e.y,b.y));return b.w*b.h=0.98});return{description:o.note.trim(),related:n,excluded:n.filter((a)=>x.has(a.id))}}function oo(p,o,x,e,n){let a=n==="fit"?Math.min(1,x/p,e/o):n;return{scale:a,width:p*a,height:o*a}}function ro(p,o,x,e){if(x<=0||e<=x)return 0;return Math.max(0,Math.min(1,((p-o)/x-0.08)/0.84))*(e-x)}var _o=` :host{height:var(--component-review-height,100dvh);min-height:0;overflow:hidden} .review{height:100%;max-width:none;min-height:0;padding:0;display:flex;flex-direction:column;overflow:hidden;background:var(--color-bg,#fafafa)} .review>header{flex-shrink:0;padding:16px 24px;margin:0;border-bottom:1px solid var(--line);gap:16px}.review>header>div{min-width:0}.review h1{font-size:30px;line-height:1}.review>header p{font-size:12px;margin-top:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.review .badge{font-size:11px} @@ -53,7 +53,7 @@ .review>.inventory-section{height:160px;padding:6px 14px}.inventory-section>.section-head{flex-direction:row;flex-wrap:nowrap;align-items:center;gap:6px;min-height:34px;margin-bottom:4px}.inventory-section h2{display:none}.inventory-filters{flex:1;width:auto;min-width:0;padding:2px}.inventory-filters button{padding:4px 6px;font-size:10px;min-height:28px}.inventory-filters b{margin-left:3px}.tray-actions #show-all{display:none}.tray-actions #toggle-tray{padding:6px;min-width:32px;min-height:32px}.tray-actions #toggle-tray span{display:none}.tray-actions svg{width:16px;height:16px}.review>.inventory-section.tray-collapsed{height:46px;padding:6px 14px}.review>.inventory-section.tray-expanded{height:160px}.inventory .item{flex-basis:130px;grid-template-rows:auto 1fr auto;padding:6px}.inventory .item-thumb{display:none}.inventory.all{display:flex;overflow-x:auto;overflow-y:hidden}.inventory .item strong{font-size:11px;min-height:22px}.inventory .state{font-size:10px}.inventory .item-number{font-size:10px;min-height:14px}.inventory-empty{padding:8px 0;font-size:12px} .review>footer{padding:8px 14px calc(8px + env(safe-area-inset-bottom));gap:8px;flex-direction:column;align-items:stretch}.review>footer>div:first-child{gap:10px;justify-content:space-between}.review>footer #approve-rest{font-size:10px;min-height:32px;max-width:47%;padding:5px 8px}.review>footer .check{font-size:10px;max-width:48%;gap:4px}.review>footer .check input{width:14px;height:14px}.review>footer .submit-area{justify-content:space-between;gap:10px}.review>footer .submit-area p{font-size:10px;max-width:22ch}.review>footer .primary{min-height:34px;font-size:12px;padding:6px 10px} } -`;var Vo=` +`;var Lo=` .review-peers{display:grid;gap:8px;padding:8px 0 12px;font-size:12px} .review-peers strong span{font-weight:400}.review-peers>div{display:flex;flex-wrap:wrap;gap:4px} .review-peers button{min-width:28px;min-height:28px;padding:3px;border:1px solid var(--line);border-radius:4px;background:var(--paper);color:inherit} @@ -94,7 +94,7 @@ @media(prefers-reduced-motion:reduce){*{scroll-behavior:auto}} .item-medium{margin-left:auto;font-weight:400;display:flex;align-items:center;gap:4px;font-size:10px;color:var(--muted);min-height:16px}.item-medium .utility-icon{width:14px;height:14px;flex-shrink:0}.material>.utility-icon{width:18px;height:18px;align-self:center;color:var(--teal)} .feedback-actions{display:flex;align-items:center;gap:10px;margin-top:12px;flex-wrap:wrap}.feedback-actions .primary{margin-left:auto;display:inline-flex;align-items:center;gap:10px}.feedback-actions svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.5}.feedback-hint,.shortcut-hint{font-size:12px;color:var(--muted)}.feedback-hint{margin-top:6px}.shortcut-hint{flex-basis:100%;text-align:right}.record-verdict{display:flex;flex-wrap:wrap;align-items:baseline;gap:5px 12px;font-size:13px}.record-verdict span{font-size:11px;color:var(--muted)}.review>.record-footer{font-size:12px;color:var(--muted);flex-wrap:wrap;gap:6px 16px}.saved-feedback{font-size:13px;white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0}.decision-notice{display:flex;align-items:center;gap:12px;font-size:12px;margin-bottom:10px}.decision-notice>span{flex:1;overflow-wrap:anywhere}.decision-notice button{color:var(--teal);flex-shrink:0}.review-summary{padding:4px 2px}.completion-mark{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:#e6efe8;color:#28625e;float:left;margin:0 10px 8px 0}.completion-mark svg{width:24px;height:24px;fill:none;stroke:currentColor;stroke-width:1.6}.completion-link{display:inline-flex;align-items:center;gap:8px;color:var(--teal);border:0;background:transparent;padding:0;font-size:12px;text-align:left}.completion-link svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;flex-shrink:0}.queue-complete #approve-rest{display:none}.review-summary h2{font-size:22px;line-height:32px}.review-summary p{clear:both;margin-top:8px;color:var(--muted)}.summary-decisions{margin-top:24px;display:grid}.summary-decisions button{display:grid;grid-template-columns:1fr auto;gap:6px 16px;text-align:left;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:12px 0;background:transparent}.summary-decisions strong{font-size:13px;font-weight:500}.summary-decisions span{font-size:12px;color:var(--teal)}.summary-decisions small{grid-column:1/-1;font-size:12px;color:var(--muted);white-space:pre-wrap;overflow-wrap:anywhere} -`+Ho+` +`+_o+` .review-form:has(#feedback-form){max-height:75%;min-height:0;flex-shrink:1;display:flex;flex-direction:column;overflow:hidden;padding-top:8px} #feedback-form{display:flex;flex-direction:column;flex:1;min-height:0} /* Leave room inside the scrollport for the 2px focus ring and 3px offset. */ @@ -156,47 +156,81 @@ .assembled-review .decisions>button{flex:1;min-width:0} .assembled-review .page-review-status{text-align:left} } -`;var Oo={chevronDown:'',code:'',image:'',expand:'',compact:'',hideTray:'',showTray:'',next:'',mark:'',close:'',undo:'',external:'',zoom:''};function Y(p){return``}var l=(p)=>p.replace(/[&<>"']/g,(o)=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[o]),ri=(p)=>`${p*100}%`,ei=(p)=>{let o=new URL(p,location.href);if(!["http:","https:"].includes(o.protocol))throw Error("Unsupported preview URL");return l(o.href)};function Jo(p,o,x){let e=p.attachShadow({mode:"open"}),n=structuredClone(x.initialDraft??ko(o)),t=o.stage==="hero"&&o.components.length===1&&!n.missing.length,ni=()=>[...o.components].sort((s,b)=>R(s,n,x.history).priority-R(b,n,x.history).priority),h=ni().find((s)=>R(s,n,x.history).kind!=="approved")?.id??o.components[0]?.id,z=wi(o,n).pending?"pending":"reviewed",_=!1,N=!1,m=x.completed??!1,ki="",V={},y=!!x.completed||!wi(o,n).pending,B=null,li=!1,$o=/Mac|iPhone|iPad/.test(navigator.platform)?"⌘Enter":"Ctrl+Enter",ii=!1,k=!1,ji=!1,Xi=null,oi=!1,$=!0,di=!1,J="comp",Ci=J,D="fit",mi="checker",W=!1,pi="isolated",Ei,Pi="fit",S=null,F=null,C=null,bi='',qi='',io=(s)=>`left:${ri(s.x)};top:${ri(s.y)};width:${ri(s.w)};height:${ri(s.h)}`;function hi(s){e.getElementById(s)?.focus({preventScroll:!0})}function Ko(s){let b=Mo(o,n,s);if(y=!b,b)h=b;J="component",W=!1,ii=!1,D="fit",pi="isolated",li=!1,z=y?"reviewed":"pending";let Z=()=>{f(),hi(y?"review-summary":V[h]?"feedback":"approve")};if(y&&k&&Xi)Xi(Z);else Z()}async function oo(){if(N||m||W||Object.keys(V).length)return;N=!0,ki="",f();try{await x.onSubmit(qo(o,n)),m=!0,y=!0,_=!1,z="reviewed"}catch(s){ki=s instanceof Error?s.message:"Could not save. Try again."}finally{N=!1,f()}}function Qi(s){if(N||m||W||ji)return;let b=o.components.find((L)=>L.id===h);if(!b)return;let Z=n.decisions[b.id],vi=Z?.revision===b.revision?Z:void 0,gi=Ni(o,n,b,li,Object.keys(V));B={id:b.id,name:gi.length>1?`${b.reviewGroup} · ${gi.length} instances`:b.name,action:s,previous:Object.fromEntries(gi.map((L)=>[L.id,n.decisions[L.id]?{...n.decisions[L.id]}:void 0]))};let Mi=V[b.id]??vi;for(let L of gi)n.decisions[L.id]={revision:L.revision,action:s,feedback:s==="revise"?Mi?.feedback??"":"",split:s==="revise"&&(Mi?.split??!1)};if(delete V[b.id],di)$=!0,di=!1;if(t)n.inventoryConfirmed=s==="approve",B=null,oo();else Ko(b.id)}function Yo(){if(N||m||W||ji)return;let s=o.components.find((vi)=>vi.id===h);if(!s)return;let b=n.decisions[s.id],Z=b?.revision===s.revision?b:void 0;if(V[s.id]??={feedback:Z?.feedback??"",split:Z?.split??!1},$&&(e.querySelector(".workbench")?.clientHeight??0)<420)di=!0,$=!1;y=!1,f(),hi("feedback")}function ro(s){let b=`missing-${crypto.randomUUID()}`;n.missing.push({id:b,name:"Missing component",feedback:"",box:s}),n.inventoryConfirmed=!1,y=!1,h=b,J="component",_=!1,S=null,F=null,f(),e.querySelector("#missing-name")?.focus()}function f(){if(C?.disconnect(),m||y&&!t)k=!1;let s=e.activeElement,b=s?.id,Z=s?.dataset.select,vi=window.scrollX,gi=window.scrollY,Mi=e.querySelector(".inventory")?.scrollLeft??0,L=Ei===h,_o=J==="component"&&(!L||Ci!==J);Ci=J;let Lo=L?e.querySelector(".inspection-content")?.scrollTop??0:0,No=L&&(e.querySelector(".changed-files")?.open??!1),Xo=e.querySelector(".comparison-slot")?.clientHeight??200,eo=e.querySelector(".pan-viewport"),no=Ei===h&&Pi===D,Eo=no?eo?.scrollLeft??0:0,Qo=no?eo?.scrollTop??0:0;Ei=h,Pi=D;let g=o.components.find((i)=>i.id===h),j=n.missing.find((i)=>i.id===h),po=g?.box??j?.box,ao=g?o.components.indexOf(g)+1:o.components.length+n.missing.findIndex((i)=>i.id===h)+1,xo=g?n.decisions[g.id]:void 0,P=xo?.revision===g?.revision?xo:void 0,Hi=g?V[g.id]:void 0,ai=Object.keys(V).length>0,Wi=g?Di(o,g):[],ui=g?Ni(o,n,g,li,Object.keys(V)):[],Wo=g?!o.components.some((i)=>!ui.some((r)=>r.id===i.id)&&R(i,n).kind==="pending"):!1,Zi=B&&!m?`
${l(B.name)} ${B.action==="approve"?"approved":"flagged for repair"}.
`:"",v=wi(o,n),q=x.history,X=g?Li(g.id,q):void 0,xi=q?.packet.components.find((i)=>i.id===g?.id),U=W&&!!xi,d=U?xi:g,E=U?q.packet:o,to=Object.values(q?.changes??{}),lo=to.filter((i)=>i.kind==="changed").length,ho=to.filter((i)=>i.kind==="added").length,Ui=o.components.filter((i)=>R(i,n,q).kind==="approved"&&Li(i.id,q).carried).length,Vi=(i)=>{let r=R(i,n,q);return m&&r.kind==="feedback"?{...r,label:"Changes requested"}:r},Zo=v.approved+v.revisions+n.missing.length,go=(z==="pending"?ni():o.components).filter((i)=>Ri(i,n,z)),Uo=[v.revisions+n.missing.length?`${v.revisions+n.missing.length} feedback ready`:"",Ui?`${Ui} ${Ui===1?"approval":"approvals"} kept`:"",lo?`${lo} changed`:"",ho?`${ho} added`:"",q?.removed.length?`${q.removed.length} removed`:""].filter(Boolean).join(" · "),Go=ki||(ai?"Save or cancel your open feedback before sending.":m?x.preview?"Preview submitted. No run changed.":"Review submitted.":v.hasFeedback?"Ready to send for corrections.":v.pending?`${v.pending} left to review`:!n.inventoryConfirmed?"Confirm the map is complete.":"Ready to continue."),I=d?ci(d):null,Gi=d?.preview.kind==="image"&&!I?.code,Ti=Gi||d?.material?.alpha==="transparent",G=!!(d?.context&&pi==="context"),fo=d&&(G?d.context?.kind!=="image":d.preview.kind==="page"),Ai=G&&d?.context?d.context.url:d?.preview.url,To=I?.code?`${I.label} · ${I.captured?"captured from code":"live preview"}`:d?.material?`${d.material.alpha==="transparent"?"Transparent":d.material.alpha==="opaque"?"Opaque":"Transparency unverified"} ${d.material.format}`:"Raster · transparency unverified";e.innerHTML=`
-

${m?"Review record.":t?"Review the assembled page.":"Review the components."}

${l(o.title)} · Round ${o.round}

${m?'Submitted · read-only':x.preview?'Interactive preview':""}
+ +/* Grouped review is the default; instances are an explicit drill-down. */ +.pin small{font:600 10px/1 var(--font-body, sans-serif);margin-left:3px}.pin:has(small){width:auto;min-width:32px;padding:0 7px} +.instance-region{opacity:.55;border-width:1px;pointer-events:none} +.group-overview>.compare,.group-overview>.view-controls{display:none} +.group-overview{position:relative}.group-overview>.review-peers{padding:0 44px 4px 0;min-height:36px}.group-overview>.compare-toolbar{position:absolute;right:0;top:0;justify-content:flex-end;margin:0} +.group-overview>.compare-toolbar>:not(.comparison-actions),.group-overview .source-link{display:none} +.group-overview .comparison-actions{margin-left:auto} +.review-peers{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px} +.group-hint{font-size:12px;color:var(--muted)} +.instance-grid{display:grid;gap:0;flex:none;grid-auto-rows:max-content} +.instance-grid-labels{display:grid;grid-template-columns:1fr 1fr;gap:16px;color:var(--muted);font-size:12px;padding:8px 0} +.instance-row{display:block;height:auto;width:100%;text-align:left;padding:12px 0;border:0;border-top:1px solid var(--line);border-radius:0;background:transparent;color:inherit} +.instance-row:hover{background:#eef3f1} +.instance-caption{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;margin-bottom:8px} +.instance-caption>span{color:var(--muted);font-size:11px} +.instance-pair{display:grid;grid-template-columns:1fr 1fr;align-items:center;gap:16px} +.instance-reference{justify-self:center;display:block;position:relative;overflow:hidden;max-height:180px;width:100%;background:var(--comp-background)} +.instance-reference img{position:absolute;max-width:none;height:auto} +.instance-produced{justify-self:center;display:flex;align-items:center;justify-content:center;min-height:36px;max-height:180px;overflow:hidden} +.instance-produced img{display:block;width:100%;height:100%;object-fit:contain} +.instance-row.approved{opacity:.65}.instance-row.feedback .instance-caption>span{color:var(--warn)} +.item strong small{font-size:11px;white-space:nowrap;color:var(--muted)} +#comparison-dialog .group-overview{overflow:auto;min-height:0} +#comparison-dialog .group-overview>.compare-toolbar{top:0} +#comparison-dialog .group-overview>.expanded-title{padding-right:44px} + +.review-scope{font-size:12px;line-height:1.5;margin:8px 0 12px;color:var(--color-text,#292929);text-align:left;white-space:normal} +.review-scope p{margin:4px 0;overflow-wrap:anywhere}.review-scope strong{font-weight:600;margin-right:5px} +.review-scope .separate-reviews,.review-scope .scope-excluded{font-size:11px;color:var(--muted)} +.instance-row .review-scope{margin:12px 0 0} +#comparison-dialog .review-scope{flex:none} +`;var or={chevronDown:'',code:'',image:'',expand:'',compact:'',hideTray:'',showTray:'',next:'',mark:'',close:'',undo:'',external:'',zoom:''};function W(p){return``}var l=(p)=>p.replace(/[&<>"']/g,(o)=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[o]),di=(p)=>`${p*100}%`,S=(p)=>{let o=new URL(p,location.href);if(!["http:","https:"].includes(o.protocol))throw Error("Unsupported preview URL");return l(o.href)};function Xo(p,o,x){let e=p.attachShadow({mode:"open"}),n=structuredClone(x.initialDraft??Jo(o)),a=o.stage==="hero"&&o.components.length===1&&!n.missing.length,b=()=>[...o.components].sort((f,m)=>I(f,n,x.history).priority-I(m,n,x.history).priority),g=b().find((f)=>I(f,n,x.history).kind!=="approved")?.id??o.components[0]?.id,z=zi(o,n).pending?"pending":"reviewed",_=!1,s=!1,y=x.completed??!1,Vi="",q={},k=!!x.completed||!zi(o,n).pending,F=null,C=!0,Eo=/Mac|iPhone|iPad/.test(navigator.platform)?"⌘Enter":"Ctrl+Enter",ti=!1,H=!1,Ji=!1,Zi=null,li=!1,L=!0,mi=!1,K="comp",eo=K,ei="fit",ki="checker",T=!1,hi="isolated",Gi,no="fit",ni=null,pi=null,ai=null,ji='',$i='',po=(f,m)=>{if(f.stage==="hero")return"";let Q=io(f,m);if(!Q.related.length)return"";let gi=Q.related.map((P)=>l(P.name)).join(" · ");return`

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(" · ")}

`:""}
`},Ti=(f)=>`left:${di(f.x)};top:${di(f.y)};width:${di(f.w)};height:${di(f.h)}`;function bi(f){e.getElementById(f)?.focus({preventScroll:!0})}function No(f){let m=Qo(o,n,f);if(k=!m,m)g=m;K="component",T=!1,ti=!1,ei="fit",hi="isolated",C=!0,z=k?"reviewed":"pending";let Q=()=>{w(),bi(k?"review-summary":q[g]?"feedback":"approve")};if(k&&H&&Zi)Zi(Q);else Q()}async function ao(){if(s||y||T||Object.keys(q).length)return;s=!0,Vi="",w();try{await x.onSubmit(Ko(o,n)),y=!0,k=!0,_=!1,z="reviewed"}catch(f){Vi=f instanceof Error?f.message:"Could not save. Try again."}finally{s=!1,w()}}function Ui(f){if(s||y||T||Ji)return;let m=o.components.find((Z)=>Z.id===g);if(!m||!Mi(o,n,m,C,Object.keys(q)).length)return;let Q=n.decisions[m.id],gi=Q?.revision===m.revision?Q:void 0,P=Mi(o,n,m,C,Object.keys(q));F={id:m.id,name:P.length>1?`${m.reviewGroup} · ${P.length} instances`:m.name,action:f,previous:Object.fromEntries(P.map((Z)=>[Z.id,n.decisions[Z.id]?{...n.decisions[Z.id]}:void 0]))};let Ki=q[m.id]??gi;for(let Z of P)n.decisions[Z.id]={revision:Z.revision,action:f,feedback:f==="revise"?Ki?.feedback??"":"",split:f==="revise"&&(Ki?.split??!1)};if(delete q[m.id],mi)L=!0,mi=!1;if(a)n.inventoryConfirmed=f==="approve",F=null,ao();else No(m.id)}function Wo(){if(s||y||T||Ji)return;let f=o.components.find((gi)=>gi.id===g);if(!f||!Mi(o,n,f,C,Object.keys(q)).length)return;let m=n.decisions[f.id],Q=m?.revision===f.revision?m:void 0;if(q[f.id]??={feedback:Q?.feedback??"",split:Q?.split??!1},L&&(e.querySelector(".workbench")?.clientHeight??0)<420)mi=!0,L=!1;k=!1,w(),bi("feedback")}function xo(f){let m=`missing-${crypto.randomUUID()}`;n.missing.push({id:m,name:"Missing component",feedback:"",box:f}),n.inventoryConfirmed=!1,k=!1,g=m,K="component",_=!1,ni=null,pi=null,w(),e.querySelector("#missing-name")?.focus()}function w(){if(ai?.disconnect(),y||k&&!a)H=!1;let f=e.activeElement,m=f?.id,Q=f?.dataset.select,gi=window.scrollX,P=window.scrollY,Ki=e.querySelector(".inventory")?.scrollLeft??0,Z=Gi===g,Zo=K==="component"&&(!Z||eo!==K);eo=K;let Go=Z?e.querySelector(".inspection-content")?.scrollTop??0:0,To=Z&&(e.querySelector(".changed-files")?.open??!1),Uo=e.querySelector(".comparison-slot")?.clientHeight??200,to=e.querySelector(".pan-viewport"),lo=Gi===g&&no===ei,Ao=lo?to?.scrollLeft??0:0,Bo=lo?to?.scrollTop??0:0;Gi=g,no=ei;let c=o.components.find((i)=>i.id===g),M=n.missing.find((i)=>i.id===g),Y=Yo(o,n,x.history),X=Y.find((i)=>i.members.some((r)=>r.id===g)),R=!!X&&X.members.length>1&&C,ho=R?X.box:c?.box??M?.box,go=X?Y.indexOf(X)+1:Y.length+n.missing.findIndex((i)=>i.id===g)+1,fo=c?n.decisions[c.id]:void 0,xi=fo?.revision===c?.revision?fo:void 0,Qi=c?q[c.id]:void 0,fi=Object.keys(q).length>0,so=c?Wi(o,c):[],si=c?Mi(o,n,c,C,Object.keys(q)):[],Io=c?!o.components.some((i)=>!si.some((r)=>r.id===i.id)&&I(i,n).kind==="pending"):!1,Ai=F&&!y?`
${l(F.name)} ${F.action==="approve"?"approved":"flagged for repair"}.
`:"",j=zi(o,n),V=x.history,U=c?Ni(c.id,V):void 0,wi=V?.packet.components.find((i)=>i.id===c?.id),ii=T&&!!wi,d=ii?wi:c,E=ii?V.packet:o,wo=Object.values(V?.changes??{}),co=wo.filter((i)=>i.kind==="changed").length,mo=wo.filter((i)=>i.kind==="added").length,Bi=o.components.filter((i)=>I(i,n,V).kind==="approved"&&Ni(i.id,V).carried).length,Ii=(i)=>{let r=I(i,n,V);return y&&r.kind==="feedback"?{...r,label:"Changes requested"}:r},qi=Y.filter((i)=>i.kind==="pending").length,Ro=Y.length-qi+n.missing.length,bo=Y.filter((i)=>z==="all"||i.kind==="pending"===(z==="pending")).map((i)=>i.representative),Oo=(i)=>Y.find((r)=>r.members.some((t)=>t.id===i))?.label??i,Do=[j.revisions+n.missing.length?`${j.revisions+n.missing.length} feedback ready`:"",Bi?`${Bi} ${Bi===1?"approval":"approvals"} kept`:"",co?`${co} changed`:"",mo?`${mo} added`:"",V?.removed.length?`${V.removed.length} removed`:""].filter(Boolean).join(" · "),So=Vi||(fi?"Save or cancel your open feedback before sending.":y?x.preview?"Preview submitted. No run changed.":"Review submitted.":j.hasFeedback?"Ready to send for corrections.":j.pending?`${qi} left to review`:!n.inventoryConfirmed?"Confirm the map is complete.":"Ready to continue."),oi=d?yi(d):null,Ri=d?.preview.kind==="image"&&!oi?.code,Oi=Ri||d?.material?.alpha==="transparent",O=!!(d?.context&&hi==="context"),vo=d&&(O?d.context?.kind!=="image":d.preview.kind==="page"),Di=O&&d?.context?d.context.url:d?.preview.url,uo=d?io(E,d):null,Fo=oi?.code?`${oi.label} · ${oi.captured?"captured from code":"live preview"}`:d?.material?`${d.material.alpha==="transparent"?"Transparent":d.material.alpha==="opaque"?"Opaque":"Transparency unverified"} ${d.material.format}`:"Raster · transparency unverified";e.innerHTML=`
+

${y?"Review record.":a?"Review the assembled page.":"Review the components."}

${l(o.title)} · Round ${o.round}

${y?'Submitted · read-only':x.preview?'Interactive preview':""}
${x.preview?'

Historical hotel artwork for testing this interface. Decisions stay in this preview; no run is changed.

':""} - ${q&&!t?`

${v.pending} ${v.pending===1?"component":"components"} to review${Uo}

${v.pending?``:""}${q.removed.length?`
Removed from the map

${q.removed.map((i)=>l(i.name)).join(" · ")}. Confirm these omissions are intentional before accepting the map.

`:""}
`:""} - ${!t?`
`:""} -
${!t?` + ${V&&!a?`

${qi} ${qi===1?"item":"items"} to review${Do}

${j.pending?``:""}${V.removed.length?`
Removed from the map

${V.removed.map((i)=>l(i.name)).join(" · ")}. Confirm these omissions are intentional before accepting the map.

`:""}
`:""} + ${!a?`
`:""} +
${!a?`
-

Approved comp

${!m?``:""}
+

Approved comp

${!y?``:""}
- Approved composition for ${l(o.title)} - ${po&&!y?`
`:""} - ${o.components.map((i,r)=>{let a=Vi(i);return``}).join("")} - ${n.missing.map((i,r)=>``).join("")} + Approved composition for ${l(o.title)} + ${ho&&!k?`
`:""} + ${Y.map((i,r)=>{let t=i.representative,h={kind:i.kind,label:i.stateLabel},B=!k&&X?.id===i.id;return``}).join("")} + ${R&&!k?X.members.map((i)=>`
`).join(""):""} + ${n.missing.map((i,r)=>``).join("")}
-
# To review${qi} ${m?"Changes requested":"Feedback ready"}${bi} Approved
+
# To review${$i} ${y?"Changes requested":"Feedback ready"}${ji} Approved
${_?'
Draw around the missing piece.
':""}
`:""} -
- ${!t?`

${y?"Review summary":`${ao} ${l(g?.name??j?.name??"Component")}`}

`:""}
- ${y&&!t?`

${m?"Review sent.":"All components reviewed."}

${v.approved} approved · ${v.revisions} flagged for repair${n.missing.length?` · ${n.missing.length} missing`:""}

${m?"Your decisions are saved.":v.hasFeedback?"Send your feedback to start the next repair round.":"Confirm nothing is missing, then approve and continue."}

${o.components.map((i)=>{let r=n.decisions[i.id],a=Vi(i);return``}).join("")}${n.missing.map((i)=>``).join("")}
${Zi?`
${Zi}
`:""}`:g?` - ${q?`
- ${U&&X?.prior?.action==="revise"?`

Previous feedback · Round ${X.feedbackRound}

${l(X.prior.feedback||"No written feedback was supplied.")}
${X.prior.split?"

Requested: split into separately reviewable components.

":""}
`:X?.carried?'

Unchanged · approval kept

':""} - ${X?.change?.kind==="changed"?`
${X.change.files.length?`${X.change.files.length} changed ${X.change.files.length===1?"file":"files"}`:X.change.reasons.includes("region")?"Region changed":xi?.note!==g.note?"Description changed · files unchanged":"Component definition changed · files unchanged"}${X.change.files.length?`
    ${X.change.files.map((i)=>`
  • ${l(i)}
  • `).join("")}
`:""}${xi&&xi.note!==g.note?`
Previous description
${l(xi.note)}
Current description
${l(g.note)}
`:""}
`:""} +
+ ${!a?`

${k?"Review summary":`${go} ${l(R?X.label:c?.name??M?.name??"Component")}`}

`:""}
+ ${k&&!a?`

${y?"Review sent.":"All components reviewed."}

${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."}

${Y.map((i)=>{let r=i.representative,t=n.decisions[r.id],h=Ii(r);return``}).join("")}${n.missing.map((i)=>``).join("")}
${Ai?`
${Ai}
`:""}`:c?` + ${V?`
+ ${ii&&U?.prior?.action==="revise"?`

Previous feedback · Round ${U.feedbackRound}

${l(U.prior.feedback||"No written feedback was supplied.")}
${U.prior.split?"

Requested: split into separately reviewable components.

":""}
`:U?.carried?'

Unchanged · approval kept

':""} + ${U?.change?.kind==="changed"?`
${U.change.files.length?`${U.change.files.length} changed ${U.change.files.length===1?"file":"files"}`:U.change.reasons.includes("region")?"Region changed":wi?.note!==c.note?"Description changed · files unchanged":"Component definition changed · files unchanged"}${U.change.files.length?`
    ${U.change.files.map((i)=>`
  • ${l(i)}
  • `).join("")}
`:""}${wi&&wi.note!==c.note?`
Previous description
${l(wi.note)}
Current description
${l(c.note)}
`:""}
`:""}
`:""} -

${l(d.name)}

${Wi.length>1?`
${l(g.reviewGroup)} · ${Wi.length} instances
${Wi.map((i)=>``).join("")}
`:""}
${xi?`
`:""}
${d?.preview.kind==="image"?`${Y("external")}`:""}
-
-
${U?`Comp · Round ${q.packet.round}`:t?"Approved comp":"In the comp"}
Reference region for ${l(d.name)}
-
${U?`Previous · Round ${q.packet.round}`:t?"Assembled page":G?"In context":q?`${I.caption} · Round ${o.round}`:I.caption}
${!fo?`Produced ${l(d.name)}`:``}${ii?`Reference overlay`:""}
+

${l(R?X.label:d.name)}

${so.length>1?`
${so.length} instances${!R?'':'Select to inspect'}
`:""}
${wi?`
`:""}
${d?.preview.kind==="image"?`${W("external")}`:""}
+ ${R?`
Full comp cropComponent preview
${X.members.map((i,r)=>{let t=Ii(i);return``}).join("")}
`:""} + ${!R&&d?po(E,d):""}
+
${ii?`Comp · Round ${V.packet.round}`:a?"Approved comp":uo?.related.length?"Full comp crop":"In the comp"}
Reference region for ${l(d.name)}
+
${ii?`Previous · Round ${V.packet.round}`:a?"Assembled page":O?"In context":V?`${oi.caption} · Round ${o.round}`:oi.caption}
${!vo?`Produced ${l(d.name)}`:``}${ti?`Reference overlay`:""}
- ${Ti||d.context?`
${d.context?`
`:""}${Ti?`
`:""}
`:""} -
${!t?`
${Y(I.code?"code":"image")}${l(To)}${d?.material?`${d.material.width} × ${d.material.height} px`:""}
${E.stage==="components"&&I?.captured&&!d?.preview.isolation?'

Legacy region capture · may include overlapping components.

':""}${d?.context?.layering&&(Gi||G)?`

${l(d.context.layering)}

`:""} -

${l(d.note)}

-
`:""}
${Zi}${U?'

Viewing the previous round. Return to Current to make a decision.

':""}${m?`
${P?.action==="approve"?"Approved":P?.action==="revise"?"Changes requested":"Not reviewed"}Submitted in round ${o.round} · read-only
`:`
${!t?`
Your review Round ${o.round}${U?"

Return to Current to review this round.

":""}
`:""}${P&&!t?``:""}
`} - ${Hi?`
`:P?.action==="revise"?`

${l(P.feedback||"No note — agent will diagnose.")}

`:""} - ${t?`

${l(ki||(m?"Your decision is saved.":N?"Sending…":Hi?"":"Approval confirms the composition and that nothing is missing."))}

`:""}
`:j?`

This piece will be added to the unresolved inventory.

${["x","y","w","h"].map((i)=>``).join("")}
`:"

No components supplied.

"} + ${Oi||d.context?`
${d.context?`
`:""}${Oi?`
`:""}
`:""} +
${!a&&!R?`
${W(oi.code?"code":"image")}${l(Fo)}${d?.material?`${d.material.width} × ${d.material.height} px`:""}
${E.stage==="components"&&oi?.captured&&!d?.preview.isolation?'

Legacy region capture · may include overlapping components.

':""}${d?.context?.layering&&(Ri||O)?`

${l(d.context.layering)}

`:""} +

${!uo?.related.length?l(d.note):""}

+
`:""}
${Ai}${ii?'

Viewing the previous round. Return to Current to make a decision.

':""}${y?`
${xi?.action==="approve"?"Approved":xi?.action==="revise"?"Changes requested":"Not reviewed"}Submitted in round ${o.round} · read-only
`:`
${!a?`
Your review Round ${o.round}${ii?"

Return to Current to review this round.

":""}
`:""}${xi&&!a&&!R?``:""}
`} + ${Qi?`
`:xi?.action==="revise"?`

${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."))}

`:""}
`:M?`

This piece will be added to the unresolved inventory.

${["x","y","w","h"].map((i)=>``).join("")}
`:"

No components supplied.

"}
- ${!t?`

Components

-
${go.map((i)=>{let r=o.components.indexOf(i),a=Vi(i);return``}).join("")}${(z==="pending"?[]:n.missing).map((i,r)=>``).join("")}${!go.length&&(z==="pending"||!n.missing.length)?`

${z==="pending"?"Nothing left to review. Your decisions are ready.":"No components reviewed yet."}

`:""}
`:""} - ${t?"":m?`
Round ${o.round} submitted · read-only${v.approved} approved · ${v.revisions} changes requested
`:`
${!v.pending&&!ai?``:""}

${l(Go)}

`} -
`;let K=e.querySelector("#comparison-dialog"),Q=e.querySelector(".comparison-panel"),ti=e.querySelector(".comparison-slot"),O=e.querySelector(".inspector > .review-form"),Ao=e.querySelector(".inspector"),so=()=>{if(K.append(Q),O)K.append(O)};if(k&&Q&&ti)ti.style.height=`${Xo}px`,so(),K.showModal();if(U)e.querySelectorAll(".decisions button,#feedback,#split,#save-feedback,#cancel-feedback,#undo-decision,#approve-rest,#submit,#inventory-confirm").forEach((i)=>i.disabled=!0);if(e.querySelector(".inspection-content").scrollTop=Lo,m||N)e.querySelectorAll(".decisions button,#save-feedback,#cancel-feedback,#undo-decision,#approve-rest,#mark,#inventory-confirm,#missing-name,#missing-feedback,#feedback,#split,#remove-missing,[data-coordinate]").forEach((i)=>i.disabled=!0);let co=e.querySelector(".inventory");if(co)co.scrollLeft=Mi;if(!L&&$)Array.from(e.querySelectorAll(".inventory [data-select]")).find((i)=>i.dataset.select===h)?.scrollIntoView({block:"nearest",inline:"nearest"});if(b)e.getElementById(b)?.focus({preventScroll:!0});else if(Z)Array.from(e.querySelectorAll(".item[data-select]")).find((i)=>i.dataset.select===Z)?.focus({preventScroll:!0});let u=(i,r)=>e.querySelector(`#${i}`)?.addEventListener("click",r);function wo(i,r=!1){if(_)return;if(li=!1,y=!1,h=i,J="component",ii=!1,D="fit",pi="isolated",W=!1,f(),r)e.querySelector("#expand-comparison")?.click()}e.querySelectorAll("[data-select]").forEach((i)=>i.onclick=()=>wo(i.dataset.select,i.classList.contains("pin")&&o.components.some((r)=>r.id===i.dataset.select))),u("previous-round",()=>{W=!0,f()}),u("current-round",()=>{W=!1,f()}),u("review-changes",()=>{let i=ni().filter((c)=>Vi(c).kind==="pending"),r=i.findIndex((c)=>c.id===h),a=i[(r+1)%i.length];if(a)y=!1,h=a.id,J="component",z="pending",W=!1,D="fit",ii=!1,pi="isolated",f()}),e.querySelectorAll("[data-filter]").forEach((i)=>i.onclick=()=>{z=i.dataset.filter,y=!v.pending&&z==="pending";let r=ni().filter((c)=>Ri(c,n,z));if(!(z!=="pending"&&n.missing.some((c)=>c.id===h))&&!r.some((c)=>c.id===h)&&r.length)h=r[0].id,J="component",W=!1,D="fit",ii=!1,pi="isolated";f()}),u("show-summary",()=>{y=!0,J="component",z="reviewed",f(),hi("review-summary")}),u("approve",()=>Qi("approve")),u("revise",Yo),u("clear",()=>{if(g)delete n.decisions[g.id],delete V[g.id];B=null,y=!1,z="pending",f()}),u("cancel-feedback",()=>{if(g)delete V[g.id];if(di)$=!0,di=!1;f(),hi("revise")}),e.querySelector("#feedback-form")?.addEventListener("submit",(i)=>{i.preventDefault(),Qi("revise")}),e.querySelector("#feedback")?.addEventListener("keydown",(i)=>{let r=i;if(r.key==="Enter"&&(r.metaKey||r.ctrlKey)&&!r.isComposing)r.preventDefault(),r.stopPropagation(),Qi("revise")}),u("undo-decision",()=>{if(!B||N||m)return;let i=B;for(let[r,a]of Object.entries(i.previous))if(a)n.decisions[r]=a;else delete n.decisions[r];delete V[i.id],h=i.id,y=!1,W=!1,J="component",z="all",B=null,f(),hi("approve")}),e.querySelector("#apply-group")?.addEventListener("change",(i)=>{li=i.target.checked,f()}),u("overlay",()=>{ii=!ii,f()}),u("isolated",()=>{pi="isolated",f()}),u("context",()=>{pi="context",f()}),e.querySelector("#zoom")?.addEventListener("change",(i)=>{let r=i.target.value;D=r==="fit"?"fit":Number(r),f()}),u("background-checker",()=>{mi="checker",f()}),u("background-page",()=>{mi="page",f()}),u("show-all",()=>{oi=!oi,$=!0,f()}),u("toggle-tray",()=>{di=!1,$=!$,f()}),u("show-comp",()=>{J="comp",f()}),u("show-component",()=>{J="component",f()}),u("mark",()=>{_=!_,f()}),u("add-box",()=>ro({x:0.35,y:0.35,w:0.2,h:0.2})),u("remove-missing",()=>{n.missing=n.missing.filter((i)=>i.id!==h),h=o.components[0]?.id,f()}),u("approve-rest",()=>{if(ai)return;n=jo(o,n),B=null,y=!0,z="reviewed",J="component",f(),hi("review-summary")}),e.querySelector("#inventory-confirm")?.addEventListener("change",(i)=>{n.inventoryConfirmed=i.target.checked,f()}),e.querySelector("#feedback")?.addEventListener("input",(i)=>{if(g&&V[g.id])V[g.id].feedback=i.target.value}),e.querySelector("#split")?.addEventListener("change",(i)=>{if(g&&V[g.id])V[g.id].split=i.target.checked}),e.querySelector("#missing-name")?.addEventListener("input",(i)=>{if(j)j.name=i.target.value;let r=e.querySelector("#submit");if(r)r.disabled=ai||N||m||!wi(o,n).canSubmit}),e.querySelector("#missing-feedback")?.addEventListener("input",(i)=>{if(j)j.feedback=i.target.value}),e.querySelectorAll("[data-coordinate]").forEach((i)=>i.addEventListener("change",()=>{if(!j)return;let r=i.dataset.coordinate,a=Number(i.value)/100;if(Number.isFinite(a))j.box[r]=Math.max(r==="w"||r==="h"?0.001:0,Math.min(1,a));j.box.w=Math.min(j.box.w,1-j.box.x),j.box.h=Math.min(j.box.h,1-j.box.y),f()})),u("submit",()=>{oo()});let T=e.querySelector(".map");function Bi(i){let r=T.getBoundingClientRect();return{x:Math.max(0,Math.min(1,(i.clientX-r.left)/r.width)),y:Math.max(0,Math.min(1,(i.clientY-r.top)/r.height))}}function mo(i){let r=Bi(i);return o.components.filter(({box:a})=>r.x>=a.x&&r.x<=a.x+a.w&&r.y>=a.y&&r.y<=a.y+a.h).sort((a,c)=>a.box.w*a.box.h-c.box.w*c.box.h)[0]}T?.addEventListener("click",(i)=>{if(_||i.target.closest("[data-select]"))return;let r=mo(i);if(r)wo(r.id,!0)}),T?.addEventListener("pointermove",(i)=>{if(!_)T.style.cursor=mo(i)?"zoom-in":""}),T?.addEventListener("pointerdown",(i)=>{if(!_)return;S=Bi(i),T.setPointerCapture(i.pointerId),i.preventDefault()}),T?.addEventListener("pointermove",(i)=>{if(!S)return;let r=Bi(i);F={x:Math.min(S.x,r.x),y:Math.min(S.y,r.y),w:Math.abs(r.x-S.x),h:Math.abs(r.y-S.y)};let a=e.querySelector(".draw-box");a.hidden=!1,a.style.cssText=io(F)}),T?.addEventListener("pointerup",()=>{if(F&&F.w>0.01&&F.h>0.01)ro(F);else S=null,F=null}),T?.addEventListener("pointercancel",()=>{S=null,F=null,f()});let bo=e.querySelector(".output"),Ji=e.querySelector("iframe"),vo=e.querySelector(".workbench"),fi=e.querySelector(".inspection-content"),Ii=e.querySelector(".inspector");function Oi(){if(!fi||!Ii)return;Ii.dataset.scrollAbove=String(fi.scrollTop>1),Ii.dataset.scrollBelow=String(fi.scrollHeight-fi.clientHeight-fi.scrollTop>1)}fi?.addEventListener("scroll",Oi,{passive:!0});function $i(){let i=e.querySelector(".map-space");if(i&&i.clientWidth&&i.clientHeight){let w=Si(o.comp.width,o.comp.height,Math.max(1,i.clientWidth-32),Math.max(1,i.clientHeight-32),"fit");T.style.width=`${w.width}px`,T.style.height=`${w.height}px`}let r=e.querySelector(".inspection-content"),a=Array.from(e.querySelectorAll(".pan-viewport"));if(r?.clientHeight){let w=k?Math.max(100,K.clientHeight-(Q?.querySelector(".compare-toolbar")?.clientHeight??0)-(Q?.querySelector(".expanded-title")?.clientHeight??0)-(Q?.querySelector(".view-controls")?.clientHeight??0)-(O?.getBoundingClientRect().height??0)-124):Math.min(t?Number.POSITIVE_INFINITY:248,Math.max(100,r.clientHeight-((a[0]?.getBoundingClientRect().top??r.getBoundingClientRect().top)-r.getBoundingClientRect().top+r.scrollTop)-(Q?.querySelector(".view-controls")?.clientHeight??0)-12));a.forEach((M)=>M.style.height=`${w}px`)}if(d&&a.length){let w=Si(d.box.w*E.comp.width,d.box.h*E.comp.height,Math.min(...a.map((M)=>M.clientWidth)),Math.min(...a.map((M)=>M.clientHeight)),D);e.querySelectorAll(".crop-stage").forEach((M)=>{M.style.width=`${w.width}px`,M.style.height=`${w.height}px`})}if(a.forEach((w)=>{let M=w.scrollWidth>w.clientWidth+1||w.scrollHeight>w.clientHeight+1;w.classList.toggle("pannable",M),w.style.cursor=k?"":"zoom-in",w.setAttribute("role",k?"region":"button"),w.title=k?M?"Move your pointer to pan. You can also scroll, swipe, or use arrow keys.":"":"Click to enlarge comparison"}),bo&&Ji&&d){let w=bo.clientWidth/(d.box.w*E.comp.width);Ji.style.transform=`scale(${w})`,Ji.style.left=`${-d.box.x*E.comp.width*w}px`,Ji.style.top=`${-d.box.y*E.comp.height*w}px`}let c=vo.getBoundingClientRect(),yi=e.querySelector(".region"),A=e.querySelector(".number"),Yi=e.querySelector(".connector path");if(yi&&A&&Yi){let w=yi.getBoundingClientRect(),M=A.getBoundingClientRect(),_i=w.right-c.left,H=w.top+w.height/2-c.top,zi=M.left-c.left-8,Bo=M.top+M.height/2-c.top;Yi.setAttribute("d",`M ${_i} ${H} H ${zi-14} V ${Bo} H ${zi}`)}}function si(i,r){if(!Q||!ti||i===k)return;let a=e.querySelector("#expand-comparison"),c=(k?K:Q).getBoundingClientRect(),yi=window.matchMedia("(prefers-reduced-motion: reduce)").matches,A=Q.querySelector(".pan-viewport"),Yi=A?A.scrollLeft/Math.max(1,A.scrollWidth-A.clientWidth):0,w=A?A.scrollTop/Math.max(1,A.scrollHeight-A.clientHeight):0,M=()=>Q.querySelectorAll(".pan-viewport").forEach((H)=>{H.scrollLeft=Yi*Math.max(0,H.scrollWidth-H.clientWidth),H.scrollTop=w*Math.max(0,H.scrollHeight-H.clientHeight)}),_i=()=>{if(ti.append(Q),O)Ao.append(O),O.inert=!1;K.close(),ti.style.height="",ji=!1,k=!1,a.innerHTML=Y("expand"),a.setAttribute("aria-label","Enlarge comparison"),a.title="Enlarge comparison",a.setAttribute("aria-expanded","false"),$i(),M(),a.focus({preventScroll:!0}),r?.()};if(i){ti.style.height=`${c.height}px`,so(),K.showModal(),k=!0,a.innerHTML=Y("compact"),a.setAttribute("aria-label","Restore comparison"),a.title="Restore comparison (Esc)",a.setAttribute("aria-expanded","true"),$i(),M(),a.focus({preventScroll:!0});let H=K.getBoundingClientRect();if(!yi)K.animate([{transform:`translate(${c.x-H.x}px,${c.y-H.y}px) scale(${c.width/H.width},${c.height/H.height})`,opacity:0.6},{transform:"none",opacity:1}],{duration:240,easing:"cubic-bezier(.2,.8,.2,1)"})}else{if(a.disabled)return;let H=ti.getBoundingClientRect();if(yi){_i();return}if(a.disabled=!0,ji=!0,O)O.inert=!0;let zi=K.animate([{transform:"none",opacity:1},{transform:`translate(${H.x-c.x}px,${H.y-c.y}px) scale(${H.width/c.width},${H.height/c.height})`,opacity:0.6}],{duration:200,easing:"cubic-bezier(.4,0,.2,1)",fill:"forwards"});zi.finished.then(()=>{if(K.isConnected)zi.cancel(),a.disabled=!1,_i()}).catch(()=>{})}}if(Xi=(i)=>si(!1,i),u("expand-comparison",()=>si(!k)),K.addEventListener("cancel",(i)=>{i.preventDefault(),i.stopPropagation(),si(!1)}),K.addEventListener("keydown",(i)=>{if(i.key==="Escape")i.preventDefault(),i.stopPropagation(),si(!1)}),C=new ResizeObserver(()=>{$i(),Oi()}),C.observe(vo),C.observe(K),O)C.observe(O);let uo=e.querySelector(".inspection-content");if(uo)C.observe(uo);let yo=e.querySelector(".repair-context");if(yo)C.observe(yo);let zo=Q?.querySelector(".compare-toolbar");if(zo)C.observe(zo);$i();let Ki=Array.from(e.querySelectorAll(".pan-viewport"));if(Ki.forEach((i)=>{i.scrollLeft=Eo,i.scrollTop=Qo}),Ki.forEach((i)=>{i.querySelectorAll("img").forEach((r)=>r.draggable=!1),i.addEventListener("click",()=>{if(!k)si(!0)}),i.addEventListener("pointermove",(r)=>{if(r.pointerType!=="mouse"||r.buttons||!i.classList.contains("pannable"))return;let a=i.getBoundingClientRect();i.scrollLeft=Fi(r.clientX,a.left,i.clientWidth,i.scrollWidth),i.scrollTop=Fi(r.clientY,a.top,i.clientHeight,i.scrollHeight)}),i.addEventListener("keydown",(r)=>{if(!k&&(r.key==="Enter"||r.key===" ")){r.preventDefault(),r.stopPropagation(),si(!0);return}let c={ArrowLeft:[-48,0],ArrowRight:[48,0],ArrowUp:[0,-48],ArrowDown:[0,48]}[r.key];if(!c)return;r.preventDefault(),r.stopPropagation(),i.scrollLeft+=c[0],i.scrollTop+=c[1]})}),Ki.forEach((i)=>i.addEventListener("scroll",()=>{for(let r of Ki)if(r!==i){if(r.scrollLeft!==i.scrollLeft)r.scrollLeft=i.scrollLeft;if(r.scrollTop!==i.scrollTop)r.scrollTop=i.scrollTop}})),_o&&window.matchMedia("(max-width:800px)").matches){let i=e.querySelector(".inspection-content"),r=e.querySelector(".compare");if(i&&r)i.scrollTop+=r.getBoundingClientRect().top-i.getBoundingClientRect().top}if(Oi(),window.scrollTo(vi,gi),!m)x.onDraftChange?.(structuredClone(n))}return f(),{destroy(){C?.disconnect(),e.replaceChildren()},getDraft(){return structuredClone(n)}}}async function Ro(){let p=document.getElementById("review"),o=await fetch("/packet",{cache:"no-store"});if(!o.ok)throw Error("The review packet could not be loaded. Reload to retry.");let x=await o.json();if(x.sourceStatus){let e=document.createElement("p");e.textContent=x.sourceStatus,p.before(e)}Jo(p,x.packet,{initialDraft:x.draft,history:x.history,completed:!!x.receipt,onSubmit:async(e)=>{let n=await fetch("/decision",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),t=await n.json();if(!n.ok)throw Error(t.error??"The review could not be saved. Try again.")}})}Ro().catch((p)=>{let o=document.createElement("p");o.textContent=p instanceof Error?p.message:String(p),document.getElementById("review")?.replaceChildren(o)});})(); + ${!a?`

Components

+
${bo.map((i)=>{let r=Y.find((B)=>B.members.some((G)=>G.id===i.id)),t=Y.indexOf(r),h={kind:r.kind,label:r.stateLabel};return``}).join("")}${(z==="pending"?[]:n.missing).map((i,r)=>``).join("")}${!bo.length&&(z==="pending"||!n.missing.length)?`

${z==="pending"?"Nothing left to review. Your decisions are ready.":"No components reviewed yet."}

`:""}
`:""} + ${a?"":y?`
Round ${o.round} submitted · read-only${j.approved} approved · ${j.revisions} changes requested
`:`
${!j.pending&&!fi?``:""}

${l(So)}

`} + `;let N=e.querySelector("#comparison-dialog"),A=e.querySelector(".comparison-panel"),ci=e.querySelector(".comparison-slot"),ri=e.querySelector(".inspector > .review-form"),Co=e.querySelector(".inspector"),yo=()=>{if(N.append(A),ri)N.append(ri)};if(H&&A&&ci)ci.style.height=`${Uo}px`,yo(),N.showModal();if(ii)e.querySelectorAll(".decisions button,#feedback,#split,#save-feedback,#cancel-feedback,#undo-decision,#approve-rest,#submit,#inventory-confirm").forEach((i)=>i.disabled=!0);if(e.querySelector(".inspection-content").scrollTop=Go,y||s)e.querySelectorAll(".decisions button,#save-feedback,#cancel-feedback,#undo-decision,#approve-rest,#mark,#inventory-confirm,#missing-name,#missing-feedback,#feedback,#split,#remove-missing,[data-coordinate]").forEach((i)=>i.disabled=!0);let zo=e.querySelector(".inventory");if(zo)zo.scrollLeft=Ki;if(!Z&&L)Array.from(e.querySelectorAll(".inventory [data-select]")).find((i)=>i.dataset.select===g)?.scrollIntoView({block:"nearest",inline:"nearest"});if(m)e.getElementById(m)?.focus({preventScroll:!0});else if(Q)Array.from(e.querySelectorAll(".item[data-select]")).find((i)=>i.dataset.select===Q)?.focus({preventScroll:!0});let u=(i,r)=>e.querySelector(`#${i}`)?.addEventListener("click",r);function Si(i,r=!1,t=!1){if(_)return;if(!t)i=Y.find((h)=>h.members.some((B)=>B.id===i))?.representative.id??i;if(C=!t,k=!1,g=i,K="component",ti=!1,ei="fit",hi="isolated",T=!1,w(),r)e.querySelector("#expand-comparison")?.click()}e.querySelectorAll("[data-select]").forEach((i)=>i.onclick=()=>Si(i.dataset.select,i.classList.contains("pin")&&o.components.some((r)=>r.id===i.dataset.select))),u("previous-round",()=>{T=!0,w()}),u("current-round",()=>{T=!1,w()}),u("review-changes",()=>{let i=b().filter((h)=>Ii(h).kind==="pending"),r=i.findIndex((h)=>h.id===g),t=i[(r+1)%i.length];if(t)C=!0,k=!1,g=t.id,K="component",z="pending",T=!1,ei="fit",ti=!1,hi="isolated",w()}),e.querySelectorAll("[data-filter]").forEach((i)=>i.onclick=()=>{z=i.dataset.filter,k=!j.pending&&z==="pending";let r=Y.filter((h)=>z==="all"||h.kind==="pending"===(z==="pending")).map((h)=>h.representative);if(!(z!=="pending"&&n.missing.some((h)=>h.id===g))&&!r.some((h)=>h.id===g)&&r.length)C=!0,g=r[0].id,K="component",T=!1,ei="fit",ti=!1,hi="isolated";w()}),u("show-summary",()=>{k=!0,K="component",z="reviewed",w(),bi("review-summary")}),u("approve",()=>Ui("approve")),u("revise",Wo),u("clear",()=>{if(c)delete n.decisions[c.id],delete q[c.id];F=null,k=!1,z="pending",w()}),u("cancel-feedback",()=>{if(c)delete q[c.id];if(mi)L=!0,mi=!1;w(),bi("revise")}),e.querySelector("#feedback-form")?.addEventListener("submit",(i)=>{i.preventDefault(),Ui("revise")}),e.querySelector("#feedback")?.addEventListener("keydown",(i)=>{let r=i;if(r.key==="Enter"&&(r.metaKey||r.ctrlKey)&&!r.isComposing)r.preventDefault(),r.stopPropagation(),Ui("revise")}),u("undo-decision",()=>{if(!F||s||y)return;let i=F;for(let[r,t]of Object.entries(i.previous))if(t)n.decisions[r]=t;else delete n.decisions[r];delete q[i.id],g=i.id,k=!1,T=!1,K="component",z="all",F=null,w(),bi("approve")}),u("all-instances",()=>{C=!0,g=X.representative.id,T=!1,w()}),e.querySelectorAll("[data-instance]").forEach((i)=>i.onclick=()=>Si(i.dataset.instance,!1,!0)),u("overlay",()=>{ti=!ti,w()}),u("isolated",()=>{hi="isolated",w()}),u("context",()=>{hi="context",w()}),e.querySelector("#zoom")?.addEventListener("change",(i)=>{let r=i.target.value;ei=r==="fit"?"fit":Number(r),w()}),u("background-checker",()=>{ki="checker",w()}),u("background-page",()=>{ki="page",w()}),u("show-all",()=>{li=!li,L=!0,w()}),u("toggle-tray",()=>{mi=!1,L=!L,w()}),u("show-comp",()=>{K="comp",w()}),u("show-component",()=>{K="component",w()}),u("mark",()=>{_=!_,w()}),u("add-box",()=>xo({x:0.35,y:0.35,w:0.2,h:0.2})),u("remove-missing",()=>{n.missing=n.missing.filter((i)=>i.id!==g),g=o.components[0]?.id,w()}),u("approve-rest",()=>{if(fi)return;n=$o(o,n),F=null,k=!0,z="reviewed",K="component",w(),bi("review-summary")}),e.querySelector("#inventory-confirm")?.addEventListener("change",(i)=>{n.inventoryConfirmed=i.target.checked,w()}),e.querySelector("#feedback")?.addEventListener("input",(i)=>{if(c&&q[c.id])q[c.id].feedback=i.target.value}),e.querySelector("#split")?.addEventListener("change",(i)=>{if(c&&q[c.id])q[c.id].split=i.target.checked}),e.querySelector("#missing-name")?.addEventListener("input",(i)=>{if(M)M.name=i.target.value;let r=e.querySelector("#submit");if(r)r.disabled=fi||s||y||!zi(o,n).canSubmit}),e.querySelector("#missing-feedback")?.addEventListener("input",(i)=>{if(M)M.feedback=i.target.value}),e.querySelectorAll("[data-coordinate]").forEach((i)=>i.addEventListener("change",()=>{if(!M)return;let r=i.dataset.coordinate,t=Number(i.value)/100;if(Number.isFinite(t))M.box[r]=Math.max(r==="w"||r==="h"?0.001:0,Math.min(1,t));M.box.w=Math.min(M.box.w,1-M.box.x),M.box.h=Math.min(M.box.h,1-M.box.y),w()})),u("submit",()=>{ao()});let D=e.querySelector(".map");function Fi(i){let r=D.getBoundingClientRect();return{x:Math.max(0,Math.min(1,(i.clientX-r.left)/r.width)),y:Math.max(0,Math.min(1,(i.clientY-r.top)/r.height))}}function ko(i){let r=Fi(i);return o.components.filter(({box:t})=>r.x>=t.x&&r.x<=t.x+t.w&&r.y>=t.y&&r.y<=t.y+t.h).sort((t,h)=>t.box.w*t.box.h-h.box.w*h.box.h)[0]}D?.addEventListener("click",(i)=>{if(_||i.target.closest("[data-select]"))return;let r=ko(i);if(r)Si(r.id,!0)}),D?.addEventListener("pointermove",(i)=>{if(!_)D.style.cursor=ko(i)?"zoom-in":""}),D?.addEventListener("pointerdown",(i)=>{if(!_)return;ni=Fi(i),D.setPointerCapture(i.pointerId),i.preventDefault()}),D?.addEventListener("pointermove",(i)=>{if(!ni)return;let r=Fi(i);pi={x:Math.min(ni.x,r.x),y:Math.min(ni.y,r.y),w:Math.abs(r.x-ni.x),h:Math.abs(r.y-ni.y)};let t=e.querySelector(".draw-box");t.hidden=!1,t.style.cssText=Ti(pi)}),D?.addEventListener("pointerup",()=>{if(pi&&pi.w>0.01&&pi.h>0.01)xo(pi);else ni=null,pi=null}),D?.addEventListener("pointercancel",()=>{ni=null,pi=null,w()});let jo=e.querySelector(".output"),Yi=e.querySelector("iframe"),qo=e.querySelector(".workbench"),vi=e.querySelector(".inspection-content"),Ci=e.querySelector(".inspector");function Pi(){if(!vi||!Ci)return;Ci.dataset.scrollAbove=String(vi.scrollTop>1),Ci.dataset.scrollBelow=String(vi.scrollHeight-vi.clientHeight-vi.scrollTop>1)}vi?.addEventListener("scroll",Pi,{passive:!0});function _i(){let i=e.querySelector(".map-space");if(i&&i.clientWidth&&i.clientHeight){let v=oo(o.comp.width,o.comp.height,Math.max(1,i.clientWidth-32),Math.max(1,i.clientHeight-32),"fit");D.style.width=`${v.width}px`,D.style.height=`${v.height}px`}let r=e.querySelector(".inspection-content"),t=Array.from(e.querySelectorAll(".pan-viewport"));if(r?.clientHeight){let v=H?Math.max(100,N.clientHeight-(A?.querySelector(".compare-toolbar")?.clientHeight??0)-(A?.querySelector(".expanded-title")?.clientHeight??0)-(A?.querySelector(".view-controls")?.clientHeight??0)-(ri?.getBoundingClientRect().height??0)-124):Math.min(a?Number.POSITIVE_INFINITY:248,Math.max(100,r.clientHeight-((t[0]?.getBoundingClientRect().top??r.getBoundingClientRect().top)-r.getBoundingClientRect().top+r.scrollTop)-(A?.querySelector(".view-controls")?.clientHeight??0)-12));t.forEach((J)=>J.style.height=`${v}px`)}if(d&&t.length&&!R){let v=oo(d.box.w*E.comp.width,d.box.h*E.comp.height,Math.min(...t.map((J)=>J.clientWidth)),Math.min(...t.map((J)=>J.clientHeight)),ei);e.querySelectorAll(".crop-stage").forEach((J)=>{J.style.width=`${v.width}px`,J.style.height=`${v.height}px`})}if(t.forEach((v)=>{let J=v.scrollWidth>v.clientWidth+1||v.scrollHeight>v.clientHeight+1;v.classList.toggle("pannable",J),v.style.cursor=H?"":"zoom-in",v.setAttribute("role",H?"region":"button"),v.title=H?J?"Move your pointer to pan. You can also scroll, swipe, or use arrow keys.":"":"Click to enlarge comparison"}),jo&&Yi&&d){let v=jo.clientWidth/(d.box.w*E.comp.width);Yi.style.transform=`scale(${v})`,Yi.style.left=`${-d.box.x*E.comp.width*v}px`,Yi.style.top=`${-d.box.y*E.comp.height*v}px`}let h=qo.getBoundingClientRect(),B=e.querySelector(".region"),G=e.querySelector(".number"),Xi=e.querySelector(".connector path");if(B&&G&&Xi){let v=B.getBoundingClientRect(),J=G.getBoundingClientRect(),Ei=v.right-h.left,$=v.top+v.height/2-h.top,Hi=J.left-h.left-8,Po=J.top+J.height/2-h.top;Xi.setAttribute("d",`M ${Ei} ${$} H ${Hi-14} V ${Po} H ${Hi}`)}}function ui(i,r){if(!A||!ci||i===H)return;let t=e.querySelector("#expand-comparison"),h=(H?N:A).getBoundingClientRect(),B=window.matchMedia("(prefers-reduced-motion: reduce)").matches,G=A.querySelector(".pan-viewport"),Xi=G?G.scrollLeft/Math.max(1,G.scrollWidth-G.clientWidth):0,v=G?G.scrollTop/Math.max(1,G.scrollHeight-G.clientHeight):0,J=()=>A.querySelectorAll(".pan-viewport").forEach(($)=>{$.scrollLeft=Xi*Math.max(0,$.scrollWidth-$.clientWidth),$.scrollTop=v*Math.max(0,$.scrollHeight-$.clientHeight)}),Ei=()=>{if(ci.append(A),ri)Co.append(ri),ri.inert=!1;N.close(),ci.style.height="",Ji=!1,H=!1,t.innerHTML=W("expand"),t.setAttribute("aria-label","Enlarge comparison"),t.title="Enlarge comparison",t.setAttribute("aria-expanded","false"),_i(),J(),t.focus({preventScroll:!0}),r?.()};if(i){ci.style.height=`${h.height}px`,yo(),N.showModal(),H=!0,t.innerHTML=W("compact"),t.setAttribute("aria-label","Restore comparison"),t.title="Restore comparison (Esc)",t.setAttribute("aria-expanded","true"),_i(),J(),t.focus({preventScroll:!0});let $=N.getBoundingClientRect();if(!B)N.animate([{transform:`translate(${h.x-$.x}px,${h.y-$.y}px) scale(${h.width/$.width},${h.height/$.height})`,opacity:0.6},{transform:"none",opacity:1}],{duration:240,easing:"cubic-bezier(.2,.8,.2,1)"})}else{if(t.disabled)return;let $=ci.getBoundingClientRect();if(B){Ei();return}if(t.disabled=!0,Ji=!0,ri)ri.inert=!0;let Hi=N.animate([{transform:"none",opacity:1},{transform:`translate(${$.x-h.x}px,${$.y-h.y}px) scale(${$.width/h.width},${$.height/h.height})`,opacity:0.6}],{duration:200,easing:"cubic-bezier(.4,0,.2,1)",fill:"forwards"});Hi.finished.then(()=>{if(N.isConnected)Hi.cancel(),t.disabled=!1,Ei()}).catch(()=>{})}}if(Zi=(i)=>ui(!1,i),u("expand-comparison",()=>ui(!H)),N.addEventListener("cancel",(i)=>{i.preventDefault(),i.stopPropagation(),ui(!1)}),N.addEventListener("keydown",(i)=>{if(i.key==="Escape")i.preventDefault(),i.stopPropagation(),ui(!1)}),ai=new ResizeObserver(()=>{_i(),Pi()}),ai.observe(qo),ai.observe(N),ri)ai.observe(ri);let Ho=e.querySelector(".inspection-content");if(Ho)ai.observe(Ho);let Mo=e.querySelector(".repair-context");if(Mo)ai.observe(Mo);let Vo=A?.querySelector(".compare-toolbar");if(Vo)ai.observe(Vo);_i();let Li=Array.from(e.querySelectorAll(".pan-viewport"));if(Li.forEach((i)=>{i.scrollLeft=Ao,i.scrollTop=Bo}),Li.forEach((i)=>{i.querySelectorAll("img").forEach((r)=>r.draggable=!1),i.addEventListener("click",()=>{if(!H)ui(!0)}),i.addEventListener("pointermove",(r)=>{if(r.pointerType!=="mouse"||r.buttons||!i.classList.contains("pannable"))return;let t=i.getBoundingClientRect();i.scrollLeft=ro(r.clientX,t.left,i.clientWidth,i.scrollWidth),i.scrollTop=ro(r.clientY,t.top,i.clientHeight,i.scrollHeight)}),i.addEventListener("keydown",(r)=>{if(!H&&(r.key==="Enter"||r.key===" ")){r.preventDefault(),r.stopPropagation(),ui(!0);return}let h={ArrowLeft:[-48,0],ArrowRight:[48,0],ArrowUp:[0,-48],ArrowDown:[0,48]}[r.key];if(!h)return;r.preventDefault(),r.stopPropagation(),i.scrollLeft+=h[0],i.scrollTop+=h[1]})}),Li.forEach((i)=>i.addEventListener("scroll",()=>{for(let r of Li)if(r!==i){if(r.scrollLeft!==i.scrollLeft)r.scrollLeft=i.scrollLeft;if(r.scrollTop!==i.scrollTop)r.scrollTop=i.scrollTop}})),Zo&&window.matchMedia("(max-width:800px)").matches){let i=e.querySelector(".inspection-content"),r=e.querySelector(".compare");if(i&&r)i.scrollTop+=r.getBoundingClientRect().top-i.getBoundingClientRect().top}if(Pi(),window.scrollTo(gi,P),!y)x.onDraftChange?.(structuredClone(n))}return w(),{destroy(){ai?.disconnect(),e.replaceChildren()},getDraft(){return structuredClone(n)}}}async function rr(){let p=document.getElementById("review"),o=await fetch("/packet",{cache:"no-store"});if(!o.ok)throw Error("The review packet could not be loaded. Reload to retry.");let x=await o.json();if(x.sourceStatus){let e=document.createElement("p");e.textContent=x.sourceStatus,p.before(e)}Xo(p,x.packet,{initialDraft:x.draft,history:x.history,completed:!!x.receipt,onSubmit:async(e)=>{let n=await fetch("/decision",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),a=await n.json();if(!n.ok)throw Error(a.error??"The review could not be saved. Try again.")}})}rr().catch((p)=>{let o=document.createElement("p");o.textContent=p instanceof Error?p.message:String(p),document.getElementById("review")?.replaceChildren(o)});})(); diff --git a/crates/context/src/component_review/manifest.rs b/crates/context/src/component_review/manifest.rs index 1231f1985..f4244cf3a 100644 --- a/crates/context/src/component_review/manifest.rs +++ b/crates/context/src/component_review/manifest.rs @@ -121,13 +121,21 @@ pub fn freeze(project: &Path, input: &Value) -> Result<(Value, BTreeMap errors.push(format!("component review omitted measured region {}", region["id"])), + Some(component) if matches!(region["kind"].as_str(), Some("text" | "control")) && component["preview"]["kind"] != "page" => { + errors.push(format!("semantic region {} requires a rendered code preview", region["id"])); + } + _ => {} } } + if !errors.is_empty() { + return Err(errors.join("\n")); + } } view(&mut packet["comp"], project, &mut files, &mut comp_files)?; @@ -170,8 +178,9 @@ pub fn freeze(project: &Path, input: &Value) -> Result<(Value, BTreeMap>().join("\n")); } let mut ids = BTreeSet::new(); let components = packet["components"] diff --git a/crates/context/src/component_review/tests.rs b/crates/context/src/component_review/tests.rs index a711778eb..a4d48ebf5 100644 --- a/crates/context/src/component_review/tests.rs +++ b/crates/context/src/component_review/tests.rs @@ -709,3 +709,18 @@ fn component_review_refuses_groups_that_mix_measured_roles() { fs::write(&path, spec.to_string()).unwrap(); assert_eq!(manifest::freeze(&f.project,&input).unwrap().0["components"].as_array().unwrap().len(),3); } + +#[test] +fn measured_inventory_reports_all_independent_failures_before_capture() { + let f = Fixture::new(); + fs::create_dir_all(f.project.join(".impeccable/build")).unwrap(); + fs::write(f.project.join(".impeccable/build/spec.json"), br#"{"regions":[{"id":"missing-one","kind":"plate"},{"id":"control","kind":"control"},{"id":"missing-two","kind":"text"}]}"#).unwrap(); + let mut input = f.manifest(); + input["stage"] = json!("components"); + input["components"][1]["preview"] = json!({"kind":"image","path":"art.png"}); + let error = store::prepare(&f.store, &f.project, &input).unwrap_err(); + assert!(error.contains("missing-one")); + assert!(error.contains("missing-two")); + assert!(error.contains("semantic region \"control\" requires a rendered code preview")); + assert!(!f.store.exists(), "invalid inventory must not publish a review"); +}