mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
fix(book): cover preview paints actual layers + book-aware project-title readouts
Two fixes wrapped together because they're both about the user not seeing a meaningful preview in book mode. 1. renderBookCoverPreview() now paints the actual back / spine / front layers into a single composite canvas the size of the full wrap-around spread, using the same renderCustomArtPreviewDOM helper the Pages-overview tiles use. Each layer's target is remapped to 'canvas' and offset by its panel's position within the spread (back: x=bleed, spine: x=bleed+trimW, front: x=bleed+ trimW+spineW). On top: a dashed-red trim guide, dashed fold-line dividers, subtle 'Back / Spine / Front' panel labels, and an empty-state hint when no cover artwork exists yet. The geometric panel-mock fallback is gone — the .bookCoverPanel CSS still ships but is now dead. 2. renderDimensions() no longer falls through to the DVD-cover else-branch in book mode. The 'Trim: 10.8 × 7.25 in / PDF page: 8.50 × 11.00 in (portrait)' strings that showed for books were the DVD totalTrimW + state.trimH leaking through. A dedicated book branch now reads from state.book.spec and shows 'Trim: 6 × 9 in / Spread: ... (with bleed)' when editing the cover, 'Page: ... (with bleed)' otherwise. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
faa4849523
commit
c66b1c03af
1 file changed
+99
-22
+99
-22
@@ -12638,33 +12638,96 @@
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Spread preview. Scale to fit in ~720 px wide; min 200 px.
|
||||
// Spread preview. Paint the actual cover layers into a single
|
||||
// composite canvas the size of the full wrap-around spread,
|
||||
// overlaid with subtle panel labels + fold-line dividers + the
|
||||
// red trim guide. Layers are stored in panel-local space
|
||||
// (book-cover-back / book-spine / book-cover-front, each at
|
||||
// x=0 = left edge of that panel's trim area), so we remap each
|
||||
// layer's target to 'canvas' and add the panel's offset within
|
||||
// the spread + the outer bleed.
|
||||
const previewMaxPx = 720;
|
||||
const pxPerIn = totalW > 0 ? Math.min(previewMaxPx / totalW, 60) : 60;
|
||||
const heightPx = Math.max(40, totalH * pxPerIn);
|
||||
const renderWidth = Math.max(40, Math.round(totalW * pxPerIn));
|
||||
wrap.innerHTML = '';
|
||||
|
||||
const addPanel = (cls, label, widthIn, dim) => {
|
||||
const panel = document.createElement('div');
|
||||
panel.className = `bookCoverPanel ${cls}`;
|
||||
panel.style.width = `${widthIn * pxPerIn}px`;
|
||||
panel.style.height = `${heightPx}px`;
|
||||
const lab = document.createElement('div');
|
||||
lab.className = 'bookCoverPanelLabel';
|
||||
lab.textContent = label;
|
||||
panel.appendChild(lab);
|
||||
if (dim) {
|
||||
const dimEl = document.createElement('div');
|
||||
dimEl.className = 'bookCoverPanelDim';
|
||||
dimEl.textContent = dim;
|
||||
panel.appendChild(dimEl);
|
||||
}
|
||||
wrap.appendChild(panel);
|
||||
};
|
||||
const backX = bleed;
|
||||
const spineX = bleed + (hasBack ? trimW : 0);
|
||||
const frontX = bleed + (hasBack ? trimW : 0) + (hasSpine ? spineW : 0);
|
||||
const panelY = bleed;
|
||||
|
||||
if (hasBack) addPanel('back', 'Back', trimW + bleed, `${fmt(trimW)} in`);
|
||||
if (hasSpine && spineW > 0) addPanel('spine', 'Spine', spineW, `${fmt(spineW)} in`);
|
||||
addPanel('front', 'Front', trimW + bleed, `${fmt(trimW)} in`);
|
||||
const compositeLayers = [];
|
||||
for (const l of (state.layers || [])) {
|
||||
if (l.hidden) continue;
|
||||
const t = String(l.target || '');
|
||||
let xo, yo;
|
||||
if (t === 'book-cover-back' && hasBack) { xo = backX; yo = panelY; }
|
||||
else if (t === 'book-spine' && hasSpine) { xo = spineX; yo = panelY; }
|
||||
else if (t === 'book-cover-front') { xo = frontX; yo = panelY; }
|
||||
else continue;
|
||||
compositeLayers.push({
|
||||
...l,
|
||||
target: 'canvas',
|
||||
x: (Number(l.x) || 0) + xo,
|
||||
y: (Number(l.y) || 0) + yo,
|
||||
});
|
||||
}
|
||||
compositeLayers.sort((a, b) => (a.z || 0) - (b.z || 0));
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.style.cssText = 'position:relative;display:inline-block;border:1px solid var(--line);box-shadow:0 1px 3px rgba(0,0,0,.08);background:#fff';
|
||||
try {
|
||||
renderCustomArtPreviewDOM(container, {
|
||||
customArt: {
|
||||
width: Math.round(totalW * 96),
|
||||
height: Math.round(totalH * 96),
|
||||
background: book.cover?.background || s.background || '#ffffff',
|
||||
},
|
||||
layers: compositeLayers,
|
||||
}, { compact: true, renderWidth });
|
||||
} catch (err) {
|
||||
console.warn('Cover preview paint failed:', err);
|
||||
}
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.cssText = 'position:absolute;inset:0;pointer-events:none';
|
||||
|
||||
// Trim guide — dashed red rectangle around the printable trim.
|
||||
const trimGuide = document.createElement('div');
|
||||
trimGuide.style.cssText = `position:absolute;left:${(bleed / totalW) * 100}%;top:${(bleed / totalH) * 100}%;width:${((totalW - 2 * bleed) / totalW) * 100}%;height:${((totalH - 2 * bleed) / totalH) * 100}%;border:1px dashed rgba(220,38,38,.55);box-sizing:border-box`;
|
||||
overlay.appendChild(trimGuide);
|
||||
|
||||
// Vertical fold lines at each visible panel boundary.
|
||||
const addFold = (xIn) => {
|
||||
const fold = document.createElement('div');
|
||||
fold.style.cssText = `position:absolute;top:${(bleed / totalH) * 100}%;height:${((totalH - 2 * bleed) / totalH) * 100}%;width:0;border-left:1px dashed rgba(15,23,42,.35);left:${(xIn / totalW) * 100}%`;
|
||||
overlay.appendChild(fold);
|
||||
};
|
||||
if (hasBack) addFold(spineX); // back / spine boundary
|
||||
if (hasSpine) addFold(spineX + spineW); // spine / front boundary
|
||||
|
||||
// Panel labels — bottom of each panel.
|
||||
const labelTopPct = ((bleed + trimH) / totalH) * 100 - 4;
|
||||
const addLabel = (text, centerXIn) => {
|
||||
const tag = document.createElement('div');
|
||||
tag.textContent = text;
|
||||
tag.style.cssText = `position:absolute;left:${(centerXIn / totalW) * 100}%;top:${labelTopPct}%;transform:translate(-50%, -50%);background:rgba(255,255,255,.85);color:var(--muted);font-size:.58rem;letter-spacing:.08em;text-transform:uppercase;padding:2px 6px;border-radius:3px;font-family:inherit`;
|
||||
overlay.appendChild(tag);
|
||||
};
|
||||
if (hasBack) addLabel('Back', backX + trimW / 2);
|
||||
if (hasSpine && spineW > 0) addLabel('Spine', spineX + spineW / 2);
|
||||
addLabel('Front', frontX + trimW / 2);
|
||||
|
||||
// Empty-state hint, centered, when no cover layers exist yet.
|
||||
if (!compositeLayers.length) {
|
||||
const empty = document.createElement('div');
|
||||
empty.style.cssText = 'position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:.85rem;text-align:center;padding:0 16px';
|
||||
empty.textContent = 'No cover artwork yet — click Edit cover to design the spread.';
|
||||
overlay.appendChild(empty);
|
||||
}
|
||||
|
||||
container.appendChild(overlay);
|
||||
wrap.appendChild(container);
|
||||
|
||||
// Ref status hint — published / unpublished / publication target.
|
||||
const refHint = document.getElementById('bookCoverRefHint');
|
||||
@@ -14288,6 +14351,20 @@
|
||||
document.querySelector('.printHelp').textContent = 'Paint a pixel-art sprite — publishes as its own design.';
|
||||
$('trimReadout').textContent = `${pa.width} × ${pa.height} px`;
|
||||
$('sheetReadout').textContent = 'Indexed pixel grid';
|
||||
} else if (state.templateMode === 'book') {
|
||||
const spec = state.book?.spec || {};
|
||||
const tW = Number(spec.trimW) || 0;
|
||||
const tH = Number(spec.trimH) || 0;
|
||||
const pages = bookPageCount();
|
||||
const editing = !!state.book?.editingCover;
|
||||
$('projectTitle').textContent = editing ? 'Book cover spread' : 'Book';
|
||||
document.querySelector('.printHelp').textContent = editing
|
||||
? `Back · spine · front laid out for ${spec.bookSize || `${fmt(tW)} × ${fmt(tH)} in`}.`
|
||||
: `${spec.bookSize || `${fmt(tW)} × ${fmt(tH)} in`} · ${pages} page${pages === 1 ? '' : 's'}.`;
|
||||
$('trimReadout').textContent = `Trim: ${fmt(tW)} × ${fmt(tH)} in`;
|
||||
$('sheetReadout').textContent = editing
|
||||
? `Spread: ${fmt(sheetW())} × ${fmt(sheetH())} in (with bleed)`
|
||||
: `Page: ${fmt(sheetW())} × ${fmt(sheetH())} in (with bleed)`;
|
||||
} else if (state.templateMode === 'jewel') {
|
||||
$('projectTitle').textContent = 'CD jewel insert preview';
|
||||
document.querySelector('.printHelp').textContent = 'Avery 8693 / 8943 layout: front and tray inserts on Letter paper.';
|
||||
|
||||
Reference in new issue
Block a user