feat: separate AGPL libraries and add dynamic WASM loading

- Add WASM settings page for configuring external AGPL modules
- Implement dynamic loading for PyMuPDF, Ghostscript, and CoherentPDF
- Add Cloudflare Worker proxy for serving WASM files with CORS
- Update all affected tool pages to check WASM availability
- Add showWasmRequiredDialog for missing module configuration

Documentation:
- Update README, licensing.html, and docs to clarify AGPL components
  are not bundled and must be configured separately
- Add WASM-PROXY.md deployment guide with recommended source URLs
- Rename "CPDF" to "CoherentPDF" for consistency
This commit is contained in:
alam00000
2026-01-27 15:26:11 +05:30
parent f6d432eaa7
commit 2c85ca74e9
75 changed files with 9696 additions and 6587 deletions

View File

@@ -8,8 +8,9 @@ import {
import { state } from '../state.js';
import { createIcons, icons } from 'lucide';
import { PDFDocument } from 'pdf-lib';
import { PyMuPDF } from '@bentopdf/pymupdf-wasm';
import { getWasmBaseUrl } from '../config/wasm-cdn-config.js';
import { isWasmAvailable, getWasmBaseUrl } from '../config/wasm-cdn-config.js';
import { showWasmRequiredDialog } from '../utils/wasm-provider.js';
import { loadPyMuPDF, isPyMuPDFAvailable } from '../utils/pymupdf-loader.js';
import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
@@ -60,8 +61,8 @@ async function performCondenseCompression(
removeThumbnails?: boolean;
}
) {
const pymupdf = new PyMuPDF(getWasmBaseUrl('pymupdf'));
await pymupdf.load();
// Load PyMuPDF dynamically from user-provided URL
const pymupdf = await loadPyMuPDF();
const preset =
CONDENSE_PRESETS[level as keyof typeof CONDENSE_PRESETS] ||
@@ -390,6 +391,15 @@ document.addEventListener('DOMContentLoaded', () => {
return;
}
// Check WASM availability for Condense mode
const algorithm = (
document.getElementById('compression-algorithm') as HTMLSelectElement
).value;
if (algorithm === 'condense' && !isPyMuPDFAvailable()) {
showWasmRequiredDialog('pymupdf');
return;
}
if (state.files.length === 1) {
const originalFile = state.files[0];