diff --git a/README.md b/README.md index 7dd9483..900af65 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@

BentoPDF

-
- **BentoPDF** is a powerful, privacy-first, client-side PDF toolkit that is self hostable and allows you to manipulate, edit, merge, and process PDF files directly in your browser. No server-side processing is required, ensuring your files remain secure and private. ![Docker Pulls](https://img.shields.io/docker/pulls/bentopdf/bentopdf) [![Ko-fi](https://img.shields.io/badge/Buy%20me%20a%20Coffee-yellow?logo=kofi&style=flat-square)](https://ko-fi.com/alio0) ![GitHub Stars](https://img.shields.io/github/stars/alam00000/bentopdf?style=social) @@ -10,6 +8,17 @@ ![BentoPDF Tools](public/images/bentopdf-tools.png) +--- + +## 📜 Licensing + +BentoPDF is dual-licensed: + +- **AGPL-3.0** for open-source projects where you share your full source code publicly +- **Commercial License** for proprietary/closed-source applications - **[Get Lifetime License for $49](https://ko-fi.com/s/f32ca4cb75)** (a one-time lifetime purchase, includes all feature updates forever) + +For more details, see our [Licensing Page](https://bentopdf.com/licensing.html) +
## ⭐ Stargazers over time @@ -435,7 +444,3 @@ BentoPDF wouldn't be possible without the amazing open-source tools and librarie - **[cpdf](https://www.coherentpdf.com/)** – For content preserving pdf operations. Your work inspires and empowers developers everywhere. Thank you for making open-source amazing! - -## 📜 License - -This project is licensed under the **GNU AGPLv3**. See the [LICENSE](https://github.com/alam00000/bentopdf/blob/main/LICENSE) file for details. diff --git a/about.html b/about.html index c5051b2..9b48926 100644 --- a/about.html +++ b/about.html @@ -1,337 +1,276 @@ - - - - About Bentopdf - Fast, Private, and Free PDF Tools - - - - -
-

Company

- -
+
+
+

+ We believe PDF tools should be + fast, private, and free. +

+

No compromises.

+
-
-

Legal

- -
+
-
-

Follow Us

- +
+
+ +

+ Our Mission +

+

+ To provide the most comprehensive PDF toolbox that respects your + privacy and never asks for payment. We believe essential document + tools should be accessible to everyone, everywhere, without + barriers. +

+
+
+ +
+
+
+ Our Core Philosophy +

+ Privacy First. Always. +

+

+ In an era where data is a commodity, we take a different approach. + All processing for Bentopdf tools happens locally in your browser. + This means your files never touch our servers, we never see your + documents, and we don't track what you do. Your documents remain + completely and unequivocally private. It's not just a feature; + it's our foundation. +

+
+
+
+
+
+
- +
- - - - - +
+

+ Why Bentopdf? +

+
+
+ +
+

Built for Speed

+

+ No waiting for uploads or downloads to a server. By processing + files directly in your browser using modern web technologies + like WebAssembly, we offer unparalleled speed for all our tools. +

+
+
+
+ +
+

Completely Free

+

+ No trials, no subscriptions, no hidden fees, and no "premium" + features held hostage. We believe powerful PDF tools should be a + public utility, not a profit center. +

+
+
+
+ +
+

No Account Required

+

+ Start using any tool immediately. We don't need your email, a + password, or any personal information. Your workflow should be + frictionless and anonymous. +

+
+
+
+ +
+

Open Source Spirit

+

+ Built with transparency in mind. We leverage incredible + open-source libraries like PDF-lib and PDF.js, and believe in + the community-driven effort to make powerful tools accessible to + everyone. +

+
+
+
+
+ +
+ +
+

+ Ready to get started? +

+

+ Join thousands of users who trust Bentopdf for their daily document + needs. Experience the difference that privacy and performance can + make. +

+ + Explore All Tools + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/contact.html b/contact.html index 0edee53..25e0d7b 100644 --- a/contact.html +++ b/contact.html @@ -1,209 +1,173 @@ - - - - Contact Us - BentoPDF - - - - -
-

Company

- -
+
+
+

+ Get in Touch +

+

+ We'd love to hear from you. Whether you have a question, feedback, or + a feature request, please don't hesitate to reach out. +

+
-
-

Legal

- -
+
+

+ You can reach us directly by email at: + contact@bentopdf.com +

+
+
-
-

Follow Us

- + +
+ - - - - - + + + + + + \ No newline at end of file diff --git a/faq.html b/faq.html index 684c790..3359341 100644 --- a/faq.html +++ b/faq.html @@ -1,395 +1,320 @@ - - - - Frequently Asked Questions - BentoPDF - - - - -
-

Company

- -
+
+
+

+ Frequently Asked Questions +

+

+ Have questions? We've got answers. Here are some of the most common + things people ask about BentoPDF. +

+
-
-

Legal

- -
+
+
+ +

+ Are my files safe and private? +

+ +
+
+

+ Absolutely. This is the most important feature of + BentoPDF. All processing happens 100% locally in your web browser. + Your files are never uploaded to any server, which means we—or + anyone else—can never see them. Your privacy is guaranteed. +

+
+
-
-

Follow Us

- +
+ +

+ Is BentoPDF really free? What's the catch? +

+ +
+
+

+ Yes, it's completely free, and there's no catch. There are no + hidden fees, no subscription plans, no usage limits, and no + premium-only features. We believe essential tools should be + accessible to everyone. +

+
+
+ +
+ +

+ Do I need an internet connection to use the tools? +

+ +
+
+

+ After you load the website for the first time, most tools will + work completely offline! Because all the processing libraries are + loaded into your browser, you can disconnect from the internet and + continue to merge, split, compress, and edit your PDFs securely. + Some niche tools that require external data (like Markdown to PDF + with web images) may need a connection. +

+
+
+ +
+ +

+ Are there any file size or usage limitations? +

+ +
+
+

+ No, we do not impose any artificial limits on file size, the + number of files, or how many times you can use a tool. The only + practical limitation is the processing power and memory of your + own computer, as very large or complex files may take longer to + process. +

+
+
+ +
+ +

+ Why did my PDF fail to process? +

+ +
+
+

Failures are rare but can happen for a few reasons:

+
    +
  • + The PDF might be corrupted or not compliant with standard + specifications. +
  • +
  • + The file could be encrypted with a password you don't have. + Please use our Decrypt tool first. +
  • +
  • + The file might be a special "XFA" or dynamic form-based PDF, + which some of our tools cannot process. +
  • +
+
+
+ +
+ +

+ Do you track my activity on BentoPDF? +

+ +
+
+

+ We care about your privacy. BentoPDF does not track personal + information. We use + Simple Analytics + solely to see anonymous visit counts. This means we can know how + many users visit our site, but + we never know who you are. Simple Analytics is + fully GDPR-compliant and respects your privacy. +

+
+
+ +
+ +

+ What technology does BentoPDF use? +

+ +
+
+

+ BentoPDF is built on the power of modern web technologies. We + primarily use JavaScript libraries like + PDF-lib.js and PDFKit.js that + are compiled to run efficiently in your browser via WebAssembly. + This allows for powerful, server-like processing without your data + ever leaving your machine. +

+
+
+
+ +
+ +
+

+ Still have questions? +

+

+ If you can't find the answer you're looking for, feel free to reach + out to our support team. We're always Bento to help. +

+ + Contact Us + +
+
+ + +
+ - - - - - + + + + + + \ No newline at end of file diff --git a/index.html b/index.html index 1bc5e7e..abd02ce 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@ BentoPDF - The Privacy First PDF Toolkit + @@ -25,6 +26,7 @@ Home About Contact + Licensing All Tools
@@ -55,6 +57,7 @@ Home About Contact + Licensing All Tools @@ -616,6 +619,9 @@

Legal

+ + + + + + + \ No newline at end of file diff --git a/src/js/faq.ts b/src/js/faq.ts new file mode 100644 index 0000000..0328e22 --- /dev/null +++ b/src/js/faq.ts @@ -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'; + } + }); + } +}); diff --git a/src/js/logic/merge.ts b/src/js/logic/merge.ts index 0da2448..44da64a 100644 --- a/src/js/logic/merge.ts +++ b/src/js/logic/merge.ts @@ -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: () => { diff --git a/src/js/logic/pdf-multi-tool.ts b/src/js/logic/pdf-multi-tool.ts index c92036e..edd14e4 100644 --- a/src/js/logic/pdf-multi-tool.ts +++ b/src/js/logic/pdf-multi-tool.ts @@ -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) { @@ -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) { diff --git a/src/pages/pdf-multi-tool.html b/src/pages/pdf-multi-tool.html index 6e2ec1d..5490d14 100644 --- a/src/pages/pdf-multi-tool.html +++ b/src/pages/pdf-multi-tool.html @@ -1,5 +1,5 @@ - + @@ -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 @@ - + -