Skip to content

Commit 457b78d

Browse files
committed
Add Preview Button
1 parent 3857e2d commit 457b78d

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

‎css/app.css‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,38 @@ body {
418418
Responsive tweaks
419419
============================================================ */
420420

421+
/* Preview dialog */
422+
#previewDialog {
423+
width: min(90vw, 1140px);
424+
height: 90vh;
425+
padding: 0;
426+
border: none;
427+
border-radius: 0.5rem;
428+
box-shadow: 0 0.5rem 2rem rgba(0,0,0,.4);
429+
overflow: hidden;
430+
}
431+
#previewDialog[open] {
432+
display: flex;
433+
flex-direction: column;
434+
}
435+
#previewDialog::backdrop {
436+
background: rgba(0,0,0,.5);
437+
}
438+
#previewDialogHeader {
439+
display: flex;
440+
align-items: center;
441+
justify-content: space-between;
442+
padding: 0.5rem 1rem;
443+
border-bottom: 1px solid var(--bs-border-color, #dee2e6);
444+
flex-shrink: 0;
445+
}
446+
#previewDialog iframe {
447+
flex: 1;
448+
width: 100%;
449+
border: none;
450+
min-height: 0;
451+
}
452+
421453
@media (max-width: 576px) {
422454
.split-input {
423455
width: 4rem !important;

‎index.html‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@
8585
</div>
8686
</div>
8787

88+
<!-- PDF Preview Dialog -->
89+
<dialog id="previewDialog">
90+
<div id="previewDialogHeader">
91+
<span id="previewModalLabel"></span>
92+
<button id="previewCloseBtn" class="btn-close" aria-label="Close"></button>
93+
</div>
94+
<iframe id="previewFrame" src=""></iframe>
95+
</dialog>
96+
8897
<!-- Load pdf-lib, PDF.js, fflate, then app.js -->
8998
<script src="js/pdf-lib.min.js"></script>
9099
<script src="js/pdf.min.js"></script>

‎js/app.js‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ document.addEventListener('DOMContentLoaded', () => {
112112
btnRedo.addEventListener('click', redo);
113113
btnClearAll.addEventListener('click', handleClearAll);
114114

115+
// Preview dialog — revoke blob URL on close to free memory
116+
const previewDialog = document.getElementById('previewDialog');
117+
const closePreview = () => {
118+
const frame = document.getElementById('previewFrame');
119+
URL.revokeObjectURL(frame.src);
120+
frame.src = '';
121+
previewDialog.close();
122+
};
123+
document.getElementById('previewCloseBtn').addEventListener('click', closePreview);
124+
previewDialog.addEventListener('cancel', closePreview);
125+
115126
// Context menu — hide on outside click or scroll
116127
document.addEventListener('click', e => {
117128
if (!contextMenu.contains(e.target)) hideContextMenu();
@@ -357,6 +368,23 @@ function downloadBlob(blob, filename) {
357368
URL.revokeObjectURL(url);
358369
}
359370

371+
/** Open the assembled PDF in a preview modal (uses the browser's native viewer). */
372+
async function previewDoc(docId) {
373+
const doc = getDoc(docId);
374+
if (!doc || doc.pages.length === 0) return;
375+
bsToast.show('Building preview…', 'secondary');
376+
try {
377+
const bytes = await buildPdf(doc.pages);
378+
const blob = new Blob([bytes], { type: 'application/pdf' });
379+
const url = URL.createObjectURL(blob);
380+
document.getElementById('previewModalLabel').textContent = doc.name || 'Preview';
381+
document.getElementById('previewFrame').src = url;
382+
document.getElementById('previewDialog').showModal();
383+
} catch (e) {
384+
bsToast.show('Preview failed: ' + e.message, 'danger');
385+
}
386+
}
387+
360388
/** Sanitise a doc name for use in filenames. */
361389
function safeFilename(name) {
362390
return name.replace(/\.pdf$/i, '').replace(/[/\\?%*:|"<>]/g, '-') || 'pdfworker';
@@ -491,6 +519,16 @@ function createDocPane(doc) {
491519
handleDocDownload(doc.id, btnDownload);
492520
});
493521

522+
// Preview button
523+
const btnPreview = document.createElement('button');
524+
btnPreview.className = 'btn btn-outline-secondary btn-sm';
525+
btnPreview.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="me-1" viewBox="0 0 16 16"><path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/><path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/></svg>Preview`;
526+
btnPreview.title = 'Preview this document as a PDF';
527+
btnPreview.addEventListener('click', e => {
528+
e.stopPropagation();
529+
previewDoc(doc.id);
530+
});
531+
494532
// Burst button
495533
const btnBurst = document.createElement('button');
496534
btnBurst.className = 'btn btn-outline-secondary btn-sm';
@@ -568,6 +606,7 @@ function createDocPane(doc) {
568606

569607
header.appendChild(nameInput);
570608
header.appendChild(btnDownload);
609+
header.appendChild(btnPreview);
571610
header.appendChild(btnBurst);
572611
header.appendChild(btnZip);
573612
header.appendChild(btnSplitToggle);

0 commit comments

Comments
 (0)