From 4d4e63998f702508944407cd8fae680e2ab800b4 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Apr 2026 22:58:15 +0300 Subject: [PATCH] feat(print/pdf): add toggle for landscape --- .../src/translations/en/translation.json | 3 +- apps/client/src/widgets/NoteDetail.tsx | 4 +- .../src/widgets/dialogs/print_preview.tsx | 95 ++++++++++++++++--- apps/server/src/services/window.ts | 2 +- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 9fb2b1ec0f..4cb0a287da 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2308,7 +2308,8 @@ "print_preview": { "title": "Print preview", "close": "Close", - "save": "Save as PDF" + "save": "Save as PDF", + "landscape": "Landscape" }, "pdf": { "attachments_one": "{{count}} attachment", diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index 26765d46d5..476c8fe1b7 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -147,9 +147,9 @@ export default function NoteDetail() { toast.closePersistent("printing"); handlePrintReport(printReport); }; - const onPreviewResult = (_e: any, { buffer, title }: { buffer: Uint8Array; title: string }) => { + const onPreviewResult = (_e: any, { buffer, title, notePath, pageSize, landscape }: { buffer: Uint8Array; title: string; notePath: string; pageSize: string; landscape: boolean }) => { toast.closePersistent("printing"); - appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, title }); + appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, title, notePath, pageSize, landscape }); }; ipcRenderer.on("print-progress", onPrintProgress); ipcRenderer.on("print-done", onPrintDone); diff --git a/apps/client/src/widgets/dialogs/print_preview.tsx b/apps/client/src/widgets/dialogs/print_preview.tsx index ca8cfdfd46..58307c6ea2 100644 --- a/apps/client/src/widgets/dialogs/print_preview.tsx +++ b/apps/client/src/widgets/dialogs/print_preview.tsx @@ -1,29 +1,52 @@ -import { useRef, useState } from "preact/hooks"; -import Modal from "../react/Modal"; -import PdfViewer from "../type_widgets/file/PdfViewer"; +import { useCallback, useRef, useState } from "preact/hooks"; + +import { t } from "../../services/i18n"; +import toast from "../../services/toast"; +import { dynamicRequire, isElectron } from "../../services/utils"; import Button from "../react/Button"; import { useTriliumEvent } from "../react/hooks"; -import { t } from "../../services/i18n"; -import { dynamicRequire } from "../../services/utils"; +import Modal from "../react/Modal"; +import PdfViewer from "../type_widgets/file/PdfViewer"; +import { OptionsRowWithToggle } from "../type_widgets/options/components/OptionsRow"; +import OptionsSection from "../type_widgets/options/components/OptionsSection"; export interface PrintPreviewData { pdfBuffer: Uint8Array; title: string; + notePath: string; + pageSize: string; + landscape: boolean; } export default function PrintPreviewDialog() { const [shown, setShown] = useState(false); const [pdfUrl, setPdfUrl] = useState(); + const [landscape, setLandscape] = useState(false); + const [loading, setLoading] = useState(false); const bufferRef = useRef(); const titleRef = useRef(""); + const notePathRef = useRef(""); + const pageSizeRef = useRef(""); + + const updatePreview = useCallback((buffer: Uint8Array) => { + bufferRef.current = buffer; + + // Revoke old URL before creating new one. + if (pdfUrl) { + URL.revokeObjectURL(pdfUrl); + } + + const blob = new Blob([buffer as BlobPart], { type: "application/pdf" }); + setPdfUrl(URL.createObjectURL(blob)); + setLoading(false); + }, [pdfUrl]); useTriliumEvent("showPrintPreview", (data: PrintPreviewData) => { - bufferRef.current = data.pdfBuffer; titleRef.current = data.title; - - const blob = new Blob([data.pdfBuffer as BlobPart], { type: "application/pdf" }); - const url = URL.createObjectURL(blob); - setPdfUrl(url); + notePathRef.current = data.notePath; + pageSizeRef.current = data.pageSize; + setLandscape(data.landscape); + updatePreview(data.pdfBuffer); setShown(true); }); @@ -34,6 +57,7 @@ export default function PrintPreviewDialog() { setPdfUrl(undefined); } bufferRef.current = undefined; + setLoading(false); } function handleSave() { @@ -47,6 +71,32 @@ export default function PrintPreviewDialog() { handleClose(); } + function handleLandscapeToggle(newValue: boolean) { + setLandscape(newValue); + regeneratePreview(newValue); + } + + function regeneratePreview(newLandscape: boolean) { + if (!isElectron()) return; + + setLoading(true); + const { ipcRenderer } = dynamicRequire("electron"); + + // Listen for the result once. + const onResult = (_e: any, { buffer }: { buffer: Uint8Array }) => { + toast.closePersistent("printing"); + updatePreview(buffer); + }; + ipcRenderer.once("export-as-pdf-preview-result", onResult); + + ipcRenderer.send("export-as-pdf-preview", { + title: titleRef.current, + notePath: notePathRef.current, + pageSize: pageSizeRef.current, + landscape: newLandscape + }); + } + return (