Skip to content

Commit 868b105

Browse files
authored
Merge branch 'main' into dependabot/github_actions/voidzero-dev/setup-vp-1.15.0
2 parents bee229a + 2c7f567 commit 868b105

15 files changed

Lines changed: 1888 additions & 659 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Pre-commit testing rule: run `vp check` + `vp test` + (for UI/RAG changes) `pnpm
2121

2222
## Architecture
2323

24-
CloakPDF is a **100% client-side** React 19 + TypeScript 6 single-page app served as static assets via Cloudflare Workers (`wrangler.jsonc`). Every PDF operation runs in the browser; no file ever crosses the network.
24+
CloakPDF is a **100% client-side** React 19 + TypeScript 7 single-page app served as static assets via Cloudflare Workers (`wrangler.jsonc`). Every PDF operation runs in the browser; no file ever crosses the network.
2525

2626
### View routing
2727

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Everything runs client-side — there is no server to upload to. No accounts, no
5252

5353
| Area | Technology |
5454
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
55-
| Framework | [React 19](https://react.dev/) + [TypeScript 6](https://www.typescriptlang.org/) |
55+
| Framework | [React 19](https://react.dev/) + [TypeScript 7](https://www.typescriptlang.org/) |
5656
| Styling | [Tailwind CSS 4](https://tailwindcss.com/) |
5757
| Build & tooling | [Vite+ (`vp`)](https://viteplus.dev/) |
5858
| PDF manipulation | [@pdfme/pdf-lib](https://github.com/pdfme/pdf-lib) |

package.json

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,16 @@
1717
"dependencies": {
1818
"@huggingface/transformers": "^4.2.0",
1919
"@langchain/community": "^1.1.29",
20-
"@langchain/core": "^1.2.1",
20+
"@langchain/core": "^1.2.2",
2121
"@langchain/langgraph": "^1.4.7",
22-
"@llamaindex/liteparse-wasm": "^2.2.1",
22+
"@llamaindex/liteparse-wasm": "^2.5.0",
2323
"@pdf-lib/fontkit": "^1.1.1",
24-
"@pdfme/pdf-lib": "^6.1.10",
25-
"jszip": "^3.10.1",
26-
"lucide-react": "^1.21.0",
27-
"motion": "^12.42.0",
24+
"@pdfme/pdf-lib": "^6.1.11",
25+
"lucide-react": "^1.23.0",
26+
"motion": "^12.42.2",
2827
"node-forge": "^1.4.0",
2928
"ogl": "^1.0.11",
30-
"pdfjs-dist": "^6.0.227",
31-
"qrcode-generator": "^2.0.4",
29+
"pdfjs-dist": "^6.1.200",
3230
"react": "^19.2.7",
3331
"react-dom": "^19.2.7",
3432
"react-markdown": "^10.1.0",
@@ -37,20 +35,20 @@
3735
"workbox-window": "^7.4.1"
3836
},
3937
"devDependencies": {
40-
"@tailwindcss/vite": "^4.3.1",
38+
"@tailwindcss/vite": "^4.3.2",
4139
"@types/node-forge": "^1.3.14",
4240
"@types/react": "^19.2.17",
4341
"@types/react-dom": "^19.2.3",
4442
"@vite-pwa/assets-generator": "^1.0.2",
45-
"@vitejs/plugin-react": "^6.0.2",
46-
"@voidzero-dev/vite-plus-core": "^0.1.24",
47-
"puppeteer-core": "^25.2.0",
48-
"tailwindcss": "^4.3.1",
49-
"typescript": "^6.0.3",
50-
"vite": "npm:@voidzero-dev/vite-plus-core@^0.1.24",
43+
"@vitejs/plugin-react": "^6.0.3",
44+
"@voidzero-dev/vite-plus-core": "^0.2.4",
45+
"puppeteer-core": "^25.3.0",
46+
"tailwindcss": "^4.3.2",
47+
"typescript": "^7.0.2",
48+
"vite": "npm:@voidzero-dev/vite-plus-core@^0.2.4",
5149
"vite-plugin-pwa": "^1.3.0",
52-
"vite-plus": "^0.1.24",
53-
"vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.24"
50+
"vite-plus": "^0.2.4",
51+
"vitest": "^4.1.10"
5452
},
5553
"packageManager": "pnpm@10.32.1"
5654
}

pnpm-lock.yaml

Lines changed: 955 additions & 600 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# pnpm 10 reads dependency overrides from here (no longer from package.json's
2-
# "pnpm.overrides", which it silently ignores). vite/vitest are remapped to the
3-
# Vite+ drop-ins; the rest pin transitive deps past known advisories:
2+
# "pnpm.overrides", which it silently ignores). `vite` is remapped to the Vite+
3+
# core drop-in; vitest is now the real package bundled by Vite+ 0.2.x (no alias).
4+
# The rest pin transitive deps past known advisories:
45
# - serialize-javascript >=7.0.3 (XSS, GHSA-76p7-773f-r4q5)
56
# - form-data >=4.0.6 (unsafe boundary, GHSA-hmw2-7cc7-3qxx)
67
overrides:
7-
vite: "npm:@voidzero-dev/vite-plus-core@^0.1.24"
8-
vitest: "npm:@voidzero-dev/vite-plus-test@^0.1.24"
8+
vite: "npm:@voidzero-dev/vite-plus-core@^0.2.4"
99
serialize-javascript: ">=7.0.3"
1010
form-data: ">=4.0.6"

src/editor/ExportModal.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,14 @@ export function ExportButton() {
330330
downloadBlob(rendered[0].blob, `${baseName}.png`);
331331
return;
332332
}
333-
const JSZip = (await import("jszip")).default;
334-
const zip = new JSZip();
335-
for (const { pageIndex, blob } of rendered) {
336-
zip.file(`${baseName}_p${String(pageIndex + 1).padStart(3, "0")}.png`, blob);
337-
}
338-
downloadBlob(await zip.generateAsync({ type: "blob" }), `${baseName}_images.zip`);
333+
const { makeZip } = await import("../utils/zip.ts");
334+
const zipBlob = await makeZip(
335+
rendered.map(({ pageIndex, blob }) => ({
336+
name: `${baseName}_p${String(pageIndex + 1).padStart(3, "0")}.png`,
337+
data: blob,
338+
})),
339+
);
340+
downloadBlob(zipBlob, `${baseName}_images.zip`);
339341
});
340342
return;
341343
}
@@ -352,12 +354,14 @@ export function ExportButton() {
352354
void runTask("Splitting pages…", async () => {
353355
const parts = Array.from({ length: doc.pageCount }, (_, i) => [i]);
354356
const pdfs = await splitPdfIntoParts(await flattenedFile(), parts);
355-
const JSZip = (await import("jszip")).default;
356-
const zip = new JSZip();
357-
pdfs.forEach((bytes, i) => {
358-
zip.file(`${baseName}_p${String(i + 1).padStart(3, "0")}.pdf`, bytes);
359-
});
360-
downloadBlob(await zip.generateAsync({ type: "blob" }), `${baseName}_pages.zip`);
357+
const { makeZip } = await import("../utils/zip.ts");
358+
const zipBlob = await makeZip(
359+
pdfs.map((bytes, i) => ({
360+
name: `${baseName}_p${String(i + 1).padStart(3, "0")}.pdf`,
361+
data: bytes,
362+
})),
363+
);
364+
downloadBlob(zipBlob, `${baseName}_pages.zip`);
361365
});
362366
return;
363367
}

src/editor/panels/CodeStampTool.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414

1515
import { Layers } from "lucide-react";
1616
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
17-
import qrcode from "qrcode-generator";
18-
import { addCodeStampAt, type CodeStampType, encodeCode128B } from "../../utils/pdf-operations.ts";
17+
import {
18+
addCodeStampAt,
19+
type CodeStampType,
20+
encodeCode128B,
21+
encodeQr,
22+
} from "../../utils/pdf-operations.ts";
1923
import { ColorPicker, hexToRgb } from "../../components/ColorPicker.tsx";
2024
import { useEditorActions, useEditorRead, useToolSlice } from "../EditorContext.tsx";
2125
import { PrimaryAction } from "./PrimaryAction.tsx";
@@ -77,10 +81,8 @@ function renderCode(
7781
if (!text) return null;
7882
if (type === "qr") {
7983
try {
80-
const qr = qrcode(0, "M");
81-
qr.addData(text);
82-
qr.make();
83-
const n = qr.getModuleCount();
84+
const qr = encodeQr(text, "M");
85+
const n = qr.size;
8486
const quiet = 4;
8587
const cell = 6;
8688
const dim = (n + quiet * 2) * cell;

src/standalone/ExtractImages.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,13 @@ export default function ExtractImages() {
295295
const img = selectedImages[0];
296296
downloadBlob(img.blob, `${baseName}_p${img.page}_img${img.indexOnPage + 1}.png`);
297297
} else {
298-
const JSZip = (await import("jszip")).default;
299-
const zip = new JSZip();
300-
for (const img of selectedImages) {
301-
zip.file(`${baseName}_p${img.page}_img${img.indexOnPage + 1}.png`, img.blob);
302-
}
303-
const zipBlob = await zip.generateAsync({ type: "blob" });
298+
const { makeZip } = await import("../utils/zip.ts");
299+
const zipBlob = await makeZip(
300+
selectedImages.map((img) => ({
301+
name: `${baseName}_p${img.page}_img${img.indexOnPage + 1}.png`,
302+
data: img.blob,
303+
})),
304+
);
304305
downloadBlob(zipBlob, `${baseName}_images.zip`);
305306
}
306307
}, "Failed to download images. Please try again.");

src/utils/pdf-operations.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export {
4444

4545
export type { CodeStampType, CodeArtOptions, CodePlacement } from "./pdf/codes.ts";
4646
export { addCodeStampAt, encodeCode128B } from "./pdf/codes.ts";
47+
export type { QrEcl, QrMatrix } from "./pdf/qr.ts";
48+
export { encodeQr } from "./pdf/qr.ts";
4749

4850
export type { TokenContext, TokenDef } from "./pdf/tokens.ts";
4951
export { STAMP_TOKENS, resolveStampTokens, baseFileName } from "./pdf/tokens.ts";

src/utils/pdf/codes.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
*
44
* Both symbologies are drawn as **vector** rectangles straight into the page
55
* content stream (no canvas, no embedded raster), so they stay razor-sharp at
6-
* any zoom or print DPI and add only a few KB. QR matrices come from the tiny
7-
* dependency-free `qrcode-generator`; Code 128 is encoded here by a compact
8-
* self-contained encoder so we pull in no barcode library.
6+
* any zoom or print DPI and add only a few KB. QR matrices come from our own
7+
* dependency-free encoder ([qr.ts](./qr.ts)); Code 128 is encoded here by a
8+
* compact self-contained encoder — no QR or barcode library is pulled in.
99
*
1010
* Use cases this unlocks that no other in-browser PDF editor offers: dropping a
1111
* scannable verification URL, case number, or document hash onto a corner of
@@ -14,7 +14,7 @@
1414
*/
1515

1616
import { PDFDocument, rgb, StandardFonts } from "@pdfme/pdf-lib";
17-
import qrcode from "qrcode-generator";
17+
import { encodeQr } from "./qr.ts";
1818

1919
export type CodeStampType = "qr" | "barcode";
2020

@@ -218,10 +218,8 @@ export async function addCodeStampAt(
218218
let qrCount = 0;
219219
let qrIsDark: (row: number, col: number) => boolean = () => false;
220220
if (options.type === "qr") {
221-
const qr = qrcode(0, "M");
222-
qr.addData(content);
223-
qr.make();
224-
qrCount = qr.getModuleCount();
221+
const qr = encodeQr(content, "M");
222+
qrCount = qr.size;
225223
qrIsDark = (row, col) => qr.isDark(row, col);
226224
}
227225
const barPattern = options.type === "barcode" ? encodeCode128B(content) : "";

0 commit comments

Comments
 (0)