feat: Add licensing page and FAQ script, update site content and navigation, and refine PDF tool logic for multi tool
This commit is contained in:
23
src/js/faq.ts
Normal file
23
src/js/faq.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// Simple FAQ accordion handler for standalone pages
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const faqAccordion = document.getElementById('faq-accordion');
|
||||
if (faqAccordion) {
|
||||
faqAccordion.addEventListener('click', (e) => {
|
||||
const questionButton = (e.target as HTMLElement).closest('.faq-question');
|
||||
if (!questionButton) return;
|
||||
|
||||
const faqItem = questionButton.parentElement;
|
||||
const answer = faqItem?.querySelector('.faq-answer') as HTMLElement;
|
||||
|
||||
if (!faqItem || !answer) return;
|
||||
|
||||
faqItem.classList.toggle('open');
|
||||
|
||||
if (faqItem.classList.contains('open')) {
|
||||
answer.style.maxHeight = answer.scrollHeight + 'px';
|
||||
} else {
|
||||
answer.style.maxHeight = '0px';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { showLoader, hideLoader, showAlert } from '../ui.ts';
|
||||
import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.ts';
|
||||
import { state } from '../state.ts';
|
||||
import { renderPagesProgressively, cleanupLazyRendering } from '../utils/render-utils.ts';
|
||||
import { renderPagesProgressively, cleanupLazyRendering, createPlaceholder } from '../utils/render-utils.ts';
|
||||
|
||||
import { createIcons, icons } from 'lucide';
|
||||
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
|
||||
@@ -202,7 +202,7 @@ async function renderPageMergeThumbnails() {
|
||||
onProgress: (current, total) => {
|
||||
currentPageNumber++;
|
||||
showLoader(
|
||||
`Rendering page previews: ${currentPageNumber}/${totalPages}`
|
||||
`Rendering page previews...`
|
||||
);
|
||||
},
|
||||
onBatchComplete: () => {
|
||||
|
||||
@@ -17,9 +17,10 @@ interface PageData {
|
||||
pageIndex: number;
|
||||
rotation: number;
|
||||
visualRotation: number;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
pdfDoc: PDFLibDocument;
|
||||
originalPageIndex: number;
|
||||
fileName: string; // Added for lazy loading identification
|
||||
}
|
||||
|
||||
function generateId(): string {
|
||||
@@ -104,7 +105,7 @@ function showLoading(current: number, total: number) {
|
||||
loader.classList.remove('hidden');
|
||||
const percentage = Math.round((current / total) * 100);
|
||||
progress.style.width = `${percentage}%`;
|
||||
text.textContent = `Rendering pages... ${current} of ${total}`;
|
||||
text.textContent = `Rendering pages...`;
|
||||
}
|
||||
|
||||
async function withButtonLoading(buttonId: string, action: () => Promise<void>) {
|
||||
@@ -198,6 +199,18 @@ function initializeTool() {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('select-all-btn')?.addEventListener('click', () => {
|
||||
if (isRendering) return;
|
||||
snapshot();
|
||||
selectAll();
|
||||
});
|
||||
|
||||
document.getElementById('deselect-all-btn')?.addEventListener('click', () => {
|
||||
if (isRendering) return;
|
||||
snapshot();
|
||||
deselectAll();
|
||||
});
|
||||
|
||||
document.getElementById('export-pdf-btn')?.addEventListener('click', () => {
|
||||
if (isRendering) return;
|
||||
if (allPages.length === 0) {
|
||||
@@ -278,7 +291,7 @@ function initializeTool() {
|
||||
}
|
||||
|
||||
function resetAll() {
|
||||
renderCancelled = true;
|
||||
renderCancelled = true;
|
||||
isRendering = false;
|
||||
snapshot();
|
||||
allPages = [];
|
||||
@@ -286,7 +299,7 @@ function resetAll() {
|
||||
splitMarkers.clear();
|
||||
currentPdfDocs = [];
|
||||
pageCanvasCache.clear();
|
||||
cleanupLazyRendering();
|
||||
cleanupLazyRendering();
|
||||
|
||||
if (sortableInstance) {
|
||||
sortableInstance.destroy();
|
||||
@@ -356,6 +369,7 @@ async function loadPdfs(files: File[]) {
|
||||
canvas: null, // Will be filled when rendered
|
||||
pdfDoc,
|
||||
originalPageIndex: i,
|
||||
fileName: file.name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -402,6 +416,9 @@ async function loadPdfs(files: File[]) {
|
||||
if (renderCancelled) {
|
||||
renderCancelled = false;
|
||||
}
|
||||
if (allPages.length === 0) {
|
||||
resetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,14 +448,22 @@ function createPageElement(canvas: HTMLCanvasElement | null, index: number): HTM
|
||||
card.className = 'bg-gray-800 rounded-lg border-2 border-gray-700 p-2 relative group cursor-move';
|
||||
card.dataset.pageIndex = index.toString();
|
||||
card.dataset.pageId = pageData.id; // Set ID for reconciliation
|
||||
|
||||
// Add attributes for lazy loading identification
|
||||
card.dataset.pageNumber = (pageData.pageIndex + 1).toString();
|
||||
if (pageData.fileName) {
|
||||
card.dataset.fileName = pageData.fileName;
|
||||
}
|
||||
if (!pageData.canvas) {
|
||||
card.dataset.lazyLoad = 'true';
|
||||
}
|
||||
|
||||
if (selectedPages.has(index)) {
|
||||
card.classList.add('border-indigo-500', 'ring-2', 'ring-indigo-500');
|
||||
}
|
||||
|
||||
const preview = document.createElement('div');
|
||||
preview.className = 'bg-white rounded mb-2 overflow-hidden w-full flex items-center justify-center relative';
|
||||
preview.style.minHeight = '160px';
|
||||
preview.style.height = '250px';
|
||||
preview.className = 'bg-white rounded mb-2 overflow-hidden w-full flex items-center justify-center relative h-36 sm:h-64';
|
||||
|
||||
if (canvas) {
|
||||
const previewCanvas = canvas;
|
||||
@@ -574,6 +599,9 @@ function setupSortable() {
|
||||
fallbackTolerance: 3,
|
||||
delay: 200,
|
||||
delayOnTouchOnly: true,
|
||||
scroll: document.getElementById('main-scroll-container'),
|
||||
scrollSensitivity: 100, // Increase sensitivity for smoother scrolling
|
||||
bubbleScroll: false, // Prevent bubbling scroll to parent
|
||||
onEnd: (evt) => {
|
||||
const oldIndex = evt.oldIndex!;
|
||||
const newIndex = evt.newIndex!;
|
||||
@@ -724,6 +752,7 @@ async function handleInsertPdf(e: Event) {
|
||||
canvas: null, // Placeholder
|
||||
pdfDoc,
|
||||
originalPageIndex: i,
|
||||
fileName: file.name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -752,9 +781,7 @@ async function handleInsertPdf(e: Event) {
|
||||
if (preview) {
|
||||
// Re-create the preview content
|
||||
preview.innerHTML = '';
|
||||
preview.className = 'bg-white rounded mb-2 overflow-hidden w-full flex items-center justify-center relative';
|
||||
(preview as HTMLElement).style.minHeight = '160px';
|
||||
(preview as HTMLElement).style.height = '250px';
|
||||
preview.className = 'bg-white rounded mb-2 overflow-hidden w-full flex items-center justify-center relative h-36 sm:h-64';
|
||||
|
||||
const previewCanvas = canvas;
|
||||
previewCanvas.className = 'max-w-full max-h-full object-contain';
|
||||
@@ -814,6 +841,7 @@ function addBlankPage() {
|
||||
canvas,
|
||||
pdfDoc: null as any,
|
||||
originalPageIndex: -1,
|
||||
fileName: '', // Blank page has no file
|
||||
};
|
||||
|
||||
allPages.push(blankPageData);
|
||||
@@ -1066,6 +1094,12 @@ function updatePageDisplay() {
|
||||
};
|
||||
}
|
||||
|
||||
// Update visual rotation
|
||||
const canvas = card.querySelector('canvas');
|
||||
if (canvas) {
|
||||
canvas.style.transform = `rotate(${pageData.visualRotation}deg)`;
|
||||
}
|
||||
|
||||
// Update action buttons
|
||||
const actionsInner = card.querySelector('.flex.items-center.gap-1.bg-gray-900\\/90');
|
||||
if (actionsInner) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="overflow-hidden">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@@ -18,7 +18,7 @@
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
|
||||
/* Fix for mobile toolbar to prevent movement during drag operations */
|
||||
.toolbar-container {
|
||||
-webkit-user-select: none;
|
||||
@@ -28,7 +28,7 @@
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
|
||||
/* Prevent layout shifts during dragging */
|
||||
body.dragging {
|
||||
overflow: hidden;
|
||||
@@ -36,10 +36,10 @@
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="antialiased bg-gray-900 h-screen overflow-hidden">
|
||||
<body class="antialiased bg-gray-900 h-[100dvh] overflow-hidden flex flex-col">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="bg-gray-800 border-b border-gray-700 sticky top-0 z-30">
|
||||
<nav class="bg-gray-800 border-b border-gray-700 flex-none z-30">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
@@ -58,10 +58,10 @@
|
||||
</nav>
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="flex flex-col h-[calc(100vh-4rem)]">
|
||||
<div class="flex flex-col flex-1 overflow-hidden relative">
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="bg-gray-800 border-b border-gray-700 p-2 sm:p-3 overflow-x-auto scrollbar-hide toolbar-container sm:sticky sm:top-16 sm:z-20">
|
||||
<div class="bg-gray-800 border-b border-gray-700 p-2 sm:p-3 toolbar-container flex-none z-20">
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-center sm:justify-start gap-2 bg-gray-900 p-2 sm:p-4 rounded-lg w-full">
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-auto p-3 sm:p-4 sm:pt-20 sm:pb-8 md:pt-4 md:pb-4">
|
||||
<div id="main-scroll-container" class="flex-1 overflow-auto p-3 pb-24 sm:p-4 sm:pb-8 md:pt-4 md:pb-4">
|
||||
<div id="upload-area"
|
||||
class="hidden border-2 border-dashed border-gray-600 rounded-lg p-6 sm:p-12 text-center max-w-full cursor-pointer"
|
||||
onclick="document.getElementById('pdf-file-input').click()">
|
||||
|
||||
Reference in New Issue
Block a user