feat: Add VitePress docs, EPUB to PDF tool, Phosphor icons, and licensing updates

- Set up VitePress documentation site (docs:dev, docs:build, docs:preview)
- Added Getting Started, Tools Reference, Contributing, and Commercial License pages
- Created self-hosting guides for Docker, Vercel, Netlify, Cloudflare, AWS, Hostinger, Nginx, Apache
- Updated README with documentation link, sponsors section, and docs contribution guide

- Added EPUB to PDF converter using LibreOffice WASM

- Migrated to Phosphor Icons for consistent iconography

- Added donation ribbon banner on landing page
- Removed 'Like My Work?' section (replaced by ribbon)
- Updated licensing.html with delivery model, AGPL notice, invoicing, and no-refund policy

- Added Commercial License documentation page
- Updated translations table (Chinese added, marked non-English as In Progress)

- Added sponsors.yml workflow for auto-generating sponsor avatars
This commit is contained in:
abdullahalam123
2025-12-27 19:30:31 +05:30
parent 0e888743d3
commit f30a084fce
189 changed files with 59872 additions and 3300 deletions

View File

@@ -1,21 +1,24 @@
import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile, formatBytes } from '../utils/helpers.js';
import { downloadFile, formatBytes, parsePageRanges } from '../utils/helpers.js';
import { createIcons, icons } from 'lucide';
import { PDFDocument as PDFLibDocument } from 'pdf-lib';
interface DividePagesState {
file: File | null;
pdfDoc: PDFLibDocument | null;
totalPages: number;
}
const pageState: DividePagesState = {
file: null,
pdfDoc: null,
totalPages: 0,
};
function resetState() {
pageState.file = null;
pageState.pdfDoc = null;
pageState.totalPages = 0;
const fileDisplayArea = document.getElementById('file-display-area');
if (fileDisplayArea) fileDisplayArea.innerHTML = '';
@@ -28,6 +31,9 @@ function resetState() {
const splitTypeSelect = document.getElementById('split-type') as HTMLSelectElement;
if (splitTypeSelect) splitTypeSelect.value = 'vertical';
const pageRangeInput = document.getElementById('page-range') as HTMLInputElement;
if (pageRangeInput) pageRangeInput.value = '';
}
async function updateUI() {
@@ -73,10 +79,10 @@ async function updateUI() {
ignoreEncryption: true,
throwOnInvalidObject: false
});
pageState.totalPages = pageState.pdfDoc.getPageCount();
hideLoader();
const pageCount = pageState.pdfDoc.getPageCount();
metaSpan.textContent = `${formatBytes(pageState.file.size)}${pageCount} pages`;
metaSpan.textContent = `${formatBytes(pageState.file.size)}${pageState.totalPages} pages`;
if (toolOptions) toolOptions.classList.remove('hidden');
} catch (error) {
@@ -96,9 +102,25 @@ async function dividePages() {
return;
}
const pageRangeInput = document.getElementById('page-range') as HTMLInputElement;
const pageRangeValue = pageRangeInput?.value.trim().toLowerCase() || '';
const splitTypeSelect = document.getElementById('split-type') as HTMLSelectElement;
const splitType = splitTypeSelect.value;
let pagesToDivide: Set<number>;
if (pageRangeValue === '' || pageRangeValue === 'all') {
pagesToDivide = new Set(Array.from({ length: pageState.totalPages }, (_, i) => i + 1));
} else {
const parsedIndices = parsePageRanges(pageRangeValue, pageState.totalPages);
pagesToDivide = new Set(parsedIndices.map(i => i + 1));
if (pagesToDivide.size === 0) {
showAlert('Invalid Range', 'Please enter a valid page range (e.g., 1-5, 8, 11-13).');
return;
}
}
showLoader('Splitting PDF pages...');
try {
@@ -106,27 +128,33 @@ async function dividePages() {
const pages = pageState.pdfDoc.getPages();
for (let i = 0; i < pages.length; i++) {
const pageNum = i + 1;
const originalPage = pages[i];
const { width, height } = originalPage.getSize();
showLoader(`Processing page ${i + 1} of ${pages.length}...`);
showLoader(`Processing page ${pageNum} of ${pages.length}...`);
const [page1] = await newPdfDoc.copyPages(pageState.pdfDoc, [i]);
const [page2] = await newPdfDoc.copyPages(pageState.pdfDoc, [i]);
if (pagesToDivide.has(pageNum)) {
const [page1] = await newPdfDoc.copyPages(pageState.pdfDoc, [i]);
const [page2] = await newPdfDoc.copyPages(pageState.pdfDoc, [i]);
switch (splitType) {
case 'vertical':
page1.setCropBox(0, 0, width / 2, height);
page2.setCropBox(width / 2, 0, width / 2, height);
break;
case 'horizontal':
page1.setCropBox(0, height / 2, width, height / 2);
page2.setCropBox(0, 0, width, height / 2);
break;
switch (splitType) {
case 'vertical':
page1.setCropBox(0, 0, width / 2, height);
page2.setCropBox(width / 2, 0, width / 2, height);
break;
case 'horizontal':
page1.setCropBox(0, height / 2, width, height / 2);
page2.setCropBox(0, 0, width, height / 2);
break;
}
newPdfDoc.addPage(page1);
newPdfDoc.addPage(page2);
} else {
const [copiedPage] = await newPdfDoc.copyPages(pageState.pdfDoc, [i]);
newPdfDoc.addPage(copiedPage);
}
newPdfDoc.addPage(page1);
newPdfDoc.addPage(page2);
}
const newPdfBytes = await newPdfDoc.save();