mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 15:46:30 +03:00
Consolidate comparison toolbar and normalize zoom control
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -35,13 +35,13 @@ export const appLayout = `
|
||||
.mobile-panes{position:relative;z-index:7;box-shadow:0 3px 6px #202b2510}
|
||||
/* Utility actions share a compact icon language; decisions retain explicit labels. */
|
||||
.utility-icon{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
|
||||
.review .icon-button{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:36px;height:36px;min-height:36px;padding:7px;text-decoration:none;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--muted)}
|
||||
.review .icon-button:hover{background:#e5ebe4;border-color:#b9c8bd;color:var(--teal)}
|
||||
.review .icon-button[aria-pressed=true]{background:#e5eee8;color:var(--teal)}
|
||||
.review .icon-button:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
|
||||
:is(.review,.comparison-panel) .icon-button{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:36px;height:36px;min-height:36px;padding:7px;text-decoration:none;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--muted)}
|
||||
:is(.review,.comparison-panel) .icon-button:hover{background:#e5ebe4;border-color:#b9c8bd;color:var(--teal)}
|
||||
:is(.review,.comparison-panel) .icon-button[aria-pressed=true]{background:#e5eee8;color:var(--teal)}
|
||||
:is(.review,.comparison-panel) .icon-button:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
|
||||
.review .label-icon{display:inline-flex;align-items:center;justify-content:center;gap:7px}
|
||||
.review .tray-actions .utility-icon{width:18px;height:18px;stroke-width:1.6}
|
||||
.material{align-items:center;margin-bottom:10px}.material .source-link{margin-left:auto;width:30px;height:30px;min-height:30px;padding:5px}
|
||||
.material{align-items:center;margin-bottom:10px}
|
||||
.preview-round{justify-content:flex-end;margin-bottom:10px}
|
||||
.compare-toolbar{margin-bottom:10px}
|
||||
.view-controls{min-height:0}.view-controls>.background-options{margin-left:auto;gap:2px}.view-controls .swatch-button{display:flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:5px}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** Small, consistent utility icons; callers supply accessible action labels. */
|
||||
const paths = {
|
||||
chevronDown: '<path d="m6 9 6 6 6-6"/>',
|
||||
code: '<path d="m8 6-6 6 6 6m8-12 6 6-6 6m-3-15-2 18"/>',
|
||||
image: '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8" cy="8" r="1.5"/><path d="m3 17 6-6 4 4 3-3 5 5"/>',
|
||||
expand: '<path d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M3 3l6 6m12-6-6 6M3 21l6-6m12 6-6-6"/>',
|
||||
|
||||
@@ -167,13 +167,13 @@ export function mountComponentReview(host: HTMLElement, packet: ReviewPacket, op
|
||||
</section>
|
||||
<section class="inspector" aria-label="Selected component">
|
||||
<div class="section-head"><h2>${finished ? 'Review summary' : `<span class="number">${index}</span> ${esc(c?.name ?? missing?.name ?? 'Component')}`}</h2></div><div class="inspection-content" role="region" aria-label="Component comparison" tabindex="0">
|
||||
${finished ? `<section class="review-summary" id="review-summary" tabindex="-1"><h2>Components reviewed.</h2><p>${stats.approved} approved · ${stats.revisions} flagged for repair${draft.missing.length ? ` · ${draft.missing.length} missing` : ''}</p><p>Check your decisions below. ${stats.hasFeedback ? 'Send feedback when you’re ready.' : 'Confirm nothing is missing, then approve and continue.'}</p><div class="summary-decisions">${packet.components.map(item=>{const decision=draft.decisions[item.id];return `<button data-select="${esc(item.id)}"><strong>${esc(item.name)}</strong><span>${decision?.action==='revise' ? 'Needs work' : 'Approved'}</span>${decision?.action==='revise' ? `<small>${esc(decision.feedback || 'No note — agent will diagnose.')}</small>` : ''}</button>`;}).join('')}${draft.missing.map(item=>`<button data-select="${esc(item.id)}"><strong>${esc(item.name)}</strong><span>Missing</span><small>${esc(item.feedback)}</small></button>`).join('')}</div></section></div><div class="review-form">${notice}</div>` : c ? `<div class="material">${icon(presentation!.code ? 'code' : 'image')}<strong>${esc(materialLabel)}</strong><span>${v?.material ? `${v.material.width} × ${v.material.height} px` : ''}</span>${v?.preview.kind==='image'?`<a class="icon-button source-link" href="${url(v!.preview.url)}" target="_blank" rel="noopener" aria-label="${presentation!.fileLabel}" title="${presentation!.fileLabel}">${icon('external')}</a>`:''}</div>
|
||||
${finished ? `<section class="review-summary" id="review-summary" tabindex="-1"><h2>Components reviewed.</h2><p>${stats.approved} approved · ${stats.revisions} flagged for repair${draft.missing.length ? ` · ${draft.missing.length} missing` : ''}</p><p>Check your decisions below. ${stats.hasFeedback ? 'Send feedback when you’re ready.' : 'Confirm nothing is missing, then approve and continue.'}</p><div class="summary-decisions">${packet.components.map(item=>{const decision=draft.decisions[item.id];return `<button data-select="${esc(item.id)}"><strong>${esc(item.name)}</strong><span>${decision?.action==='revise' ? 'Needs work' : 'Approved'}</span>${decision?.action==='revise' ? `<small>${esc(decision.feedback || 'No note — agent will diagnose.')}</small>` : ''}</button>`;}).join('')}${draft.missing.map(item=>`<button data-select="${esc(item.id)}"><strong>${esc(item.name)}</strong><span>Missing</span><small>${esc(item.feedback)}</small></button>`).join('')}</div></section></div><div class="review-form">${notice}</div>` : c ? `<div class="material">${icon(presentation!.code ? 'code' : 'image')}<strong>${esc(materialLabel)}</strong><span>${v?.material ? `${v.material.width} × ${v.material.height} px` : ''}</span></div>
|
||||
${history ? `<div class="repair-context">
|
||||
${repair?.prior?.action==='revise'?`<section class="previous-feedback" aria-label="Previous feedback"><h3>Previous feedback <span>Round ${repair.feedbackRound}</span></h3><p class="previous-verdict">Needs work</p><blockquote>${esc(repair.prior.feedback || 'No written feedback was supplied.')}</blockquote>${repair.prior.split?'<p>Requested: split into separately reviewable components.</p>':''}</section>`:repair?.carried?`<p class="kept-approval">Unchanged · approval kept</p>`:''}
|
||||
${repair?.change?.kind==='changed'?`<details class="changed-files" ${filesOpen?'open':''}><summary>${repair.change.files.length?`${repair.change.files.length} changed ${repair.change.files.length===1?'file':'files'}`:repair.change.reasons.includes('region')?'Region changed':priorComponent?.note!==c.note?'Description changed · files unchanged':'Component definition changed · files unchanged'}</summary>${repair.change.files.length?`<ul>${repair.change.files.map(path=>`<li>${esc(path)}</li>`).join('')}</ul>`:''}${priorComponent&&priorComponent.note!==c.note?`<dl class="description-diff"><dt>Previous description</dt><dd>${esc(priorComponent.note)}</dd><dt>Current description</dt><dd>${esc(c.note)}</dd></dl>`:''}</details>`:''}
|
||||
</div>`:''}
|
||||
${priorComponent?`<div class="preview-round"><div class="round-switch" role="group" aria-label="Preview version"><button id="current-round" aria-pressed="${!viewingPrevious}">Current · round ${packet.round}</button><button id="previous-round" aria-pressed="${viewingPrevious}">Previous · round ${history!.packet.round}</button></div></div>`:''}
|
||||
<div class="comparison-slot"><div class="comparison-panel"><h2 class="expanded-title">${esc(v!.name)}</h2><div class="compare-toolbar"><label title="Comparison zoom · based on comp pixels">${icon('zoom')}<select id="zoom" aria-label="Comparison zoom">${[['fit','Fit'],['1','100%'],['2','200%'],['4','400%']].map(([value,label])=>`<option value="${value}" ${String(zoom)===value?'selected':''}>${label}</option>`).join('')}</select></label><button id="overlay" class="overlay-control" aria-pressed="${overlay}"><svg viewBox="0 0 20 20" aria-hidden="true"><rect x="3" y="3" width="10" height="10"/><rect x="7" y="7" width="10" height="10"/></svg>Overlay comp</button><button id="expand-comparison" class="icon-button" aria-label="${expandedComparison?'Restore comparison':'Enlarge comparison'}" title="${expandedComparison?'Restore comparison (Esc)':'Enlarge comparison'}" aria-expanded="${expandedComparison}">${icon(expandedComparison?'compact':'expand')}</button></div>
|
||||
<div class="comparison-slot"><div class="comparison-panel"><h2 class="expanded-title">${esc(v!.name)}</h2><div class="compare-toolbar"><label class="zoom-control" title="Comparison zoom · based on comp pixels">${icon('zoom')}<select id="zoom" aria-label="Comparison zoom">${[['fit','Fit'],['1','100%'],['2','200%'],['4','400%']].map(([value,label])=>`<option value="${value}" ${String(zoom)===value?'selected':''}>${label}</option>`).join('')}</select>${icon('chevronDown')}</label><button id="overlay" class="overlay-control" aria-label="Overlay comp" title="Overlay approved comp" aria-pressed="${overlay}"><svg viewBox="0 0 20 20" aria-hidden="true"><rect x="3" y="3" width="10" height="10"/><rect x="7" y="7" width="10" height="10"/></svg>Overlay</button><div class="comparison-actions" role="group" aria-label="Comparison view actions"><button id="expand-comparison" class="icon-button" aria-label="${expandedComparison?'Restore comparison':'Enlarge comparison'}" title="${expandedComparison?'Restore comparison (Esc)':'Enlarge comparison'}" aria-expanded="${expandedComparison}">${icon(expandedComparison?'compact':'expand')}</button>${v?.preview.kind==='image'?`<a class="icon-button source-link" href="${url(v!.preview.url)}" target="_blank" rel="noopener" aria-label="${presentation!.fileLabel}" title="${presentation!.fileLabel}">${icon('external')}</a>`:''}</div></div>
|
||||
<div class="compare">
|
||||
<figure><figcaption>${viewingPrevious ? `Comp · Round ${history!.packet.round}` : 'In the comp'}</figcaption><div class="pan-viewport" aria-label="Reference comparison canvas" tabindex="0"><div class="crop-stage"><img class="crop-image" src="${url(vp.comp.url)}" alt="Reference region for ${esc(v!.name)}" style="width:${100/v!.box.w}%;left:${-100*v!.box.x/v!.box.w}%;top:${-100*v!.box.y/v!.box.h}%"></div></div></figure>
|
||||
<figure><figcaption>${viewingPrevious ? `Previous · Round ${history!.packet.round}` : history ? `Current · Round ${packet.round}` : useContext ? 'In the page' : presentation!.caption}</figcaption><div class="pan-viewport" aria-label="Produced comparison canvas" tabindex="0"><div class="output crop-stage ${isRaster&&!useContext&&!useFrame&&backdrop==='checker'?'checker':''}">${!useFrame ? `<img class="asset" src="${url(sourceUrl!)}" alt="Produced ${esc(v!.name)}" style="object-position:${esc(v!.preview.position ?? 'center')}">` : `<iframe aria-hidden="true" title="Rendered ${esc(v!.name)}" src="${url(sourceUrl!)}" sandbox="" tabindex="-1" width="${vp.comp.width}" height="${vp.comp.height}"></iframe>`}${overlay ? `<img class="crop-image overlay-image" src="${url(vp.comp.url)}" alt="Reference overlay" style="width:${100/v!.box.w}%;left:${-100*v!.box.x/v!.box.w}%;top:${-100*v!.box.y/v!.box.h}%">` : ''}</div></div></figure>
|
||||
|
||||
@@ -46,7 +46,7 @@ export const styles = `
|
||||
.feedback-actions .primary{margin-left:0}
|
||||
@media(max-height:800px),(max-width:800px){.feedback-actions .shortcut-hint{display:none}.feedback-actions .primary{margin-left:auto}.review-form .feedback textarea{min-height:56px}.review-form:has(#feedback-form) .decision-notice{margin-bottom:4px}}
|
||||
|
||||
.comparison-panel{min-width:0}.comparison-panel .expanded-title{display:none}.comparison-panel .compare-toolbar{justify-content:flex-start}.comparison-panel .overlay-control{margin-left:auto}.comparison-panel #expand-comparison{flex-shrink:0}.pan-viewport.pannable{cursor:move}.pan-viewport img{user-select:none;-webkit-user-drag:none}
|
||||
.comparison-panel{min-width:0}.comparison-panel .expanded-title{display:none}.comparison-panel .compare-toolbar{justify-content:flex-start}.comparison-panel .overlay-control{margin-left:0}.comparison-panel #expand-comparison{flex-shrink:0}.pan-viewport.pannable{cursor:move}.pan-viewport img{user-select:none;-webkit-user-drag:none}
|
||||
#comparison-dialog{position:fixed;inset:16px;width:calc(100vw - 32px);height:calc(100dvh - 32px);max-width:none;max-height:none;margin:0;padding:24px;background:var(--paper);color:inherit;border:1px solid var(--line);border-radius:8px;box-shadow:0 20px 60px #14292340;overflow:hidden;transform-origin:top left}
|
||||
#comparison-dialog::backdrop{background:#172821a6}
|
||||
#comparison-dialog .comparison-panel{height:100%;display:flex;flex-direction:column;gap:12px}
|
||||
@@ -56,4 +56,16 @@ export const styles = `
|
||||
#comparison-dialog .compare figure{display:flex;flex-direction:column;min-height:0}#comparison-dialog .pan-viewport{flex:1;min-height:0}#comparison-dialog .compare figcaption{flex-shrink:0}
|
||||
#comparison-dialog .view-controls{flex-shrink:0;margin:0}
|
||||
@media(max-width:600px){#comparison-dialog{inset:6px;width:calc(100vw - 12px);height:calc(100dvh - 12px);padding:14px}.comparison-panel .compare-toolbar{gap:6px}.comparison-panel .overlay-control{padding-inline:8px;font-size:12px}#comparison-dialog .compare{gap:8px}#comparison-dialog .compare figcaption{font-size:11px}}
|
||||
|
||||
.comparison-panel .compare-toolbar{gap:8px;padding:4px 0;min-width:0}
|
||||
.comparison-actions{display:flex;align-items:center;gap:2px;margin-left:auto;flex-shrink:0}
|
||||
.comparison-panel .zoom-control{position:relative;display:block;flex-shrink:0}
|
||||
.zoom-control select{appearance:none;-webkit-appearance:none;width:104px;height:36px;min-height:36px;padding:6px 30px 6px 32px;font-size:12px;line-height:1.4;border-color:var(--line);background:var(--paper);cursor:pointer}
|
||||
.zoom-control>.utility-icon{position:absolute;top:50%;transform:translateY(-50%);pointer-events:none}
|
||||
.zoom-control>.utility-icon:first-child{left:10px;width:15px;height:15px;color:var(--muted)}
|
||||
.zoom-control>.utility-icon:last-child{right:10px;width:13px;height:13px;color:var(--muted)}
|
||||
.comparison-panel .overlay-control{height:36px;min-height:36px;font-size:12px;padding:6px 10px;gap:6px}
|
||||
.comparison-panel .overlay-control svg{width:18px;height:18px;stroke-width:1.6}
|
||||
.comparison-actions .source-link{color:var(--muted)}
|
||||
@media(forced-colors:active){.zoom-control select{appearance:auto;padding:6px 8px}.zoom-control>.utility-icon{display:none}}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user