From 6d6272e5b6373dd9703782d6b3d4ea57e9d29730 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 1 Feb 2025 16:14:49 +0200 Subject: [PATCH 01/59] desktop app(export pdf): refactor --- src/services/window.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/window.ts b/src/services/window.ts index 01047a628..d8f86379f 100644 --- a/src/services/window.ts +++ b/src/services/window.ts @@ -79,8 +79,7 @@ ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => { displayHeaderFooter: true, headerTemplate: `
`, footerTemplate: ` -
- +
` }); From dd12d7f314d5ae800ca29b9c7812cfe671401629 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 1 Feb 2025 16:23:28 +0200 Subject: [PATCH 02/59] chore(client/ts): port export --- src/public/app/components/app_context.ts | 2 +- .../widgets/dialogs/{export.js => export.ts} | 54 +++++++++++++------ 2 files changed, 40 insertions(+), 16 deletions(-) rename src/public/app/widgets/dialogs/{export.js => export.ts} (84%) diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index dbfe78cd0..2be0d986b 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -83,7 +83,7 @@ export type CommandMappings = { }; showExportDialog: CommandData & { notePath: string; - defaultType: "single"; + defaultType: "single" | "subtree"; }; showDeleteNotesDialog: CommandData & { branchIdsToDelete: string[]; diff --git a/src/public/app/widgets/dialogs/export.js b/src/public/app/widgets/dialogs/export.ts similarity index 84% rename from src/public/app/widgets/dialogs/export.js rename to src/public/app/widgets/dialogs/export.ts index 5bc9b3707..b8b51d80f 100644 --- a/src/public/app/widgets/dialogs/export.js +++ b/src/public/app/widgets/dialogs/export.ts @@ -1,11 +1,12 @@ import treeService from "../../services/tree.js"; import utils from "../../services/utils.js"; import ws from "../../services/ws.js"; -import toastService from "../../services/toast.js"; +import toastService, { type ToastOptions } from "../../services/toast.js"; import froca from "../../services/froca.js"; import openService from "../../services/open.js"; import BasicWidget from "../basic_widget.js"; import { t } from "../../services/i18n.js"; +import type { EventData } from "../../components/app_context.js"; const TPL = ` `; export default class ExportDialog extends BasicWidget { + + private taskId: string; + private branchId: string | null; + private modal?: bootstrap.Modal; + private $form!: JQuery; + private $noteTitle!: JQuery; + private $subtreeFormats!: JQuery; + private $singleFormats!: JQuery; + private $subtreeType!: JQuery; + private $singleType!: JQuery; + private $exportButton!: JQuery; + private $opmlVersions!: JQuery; + constructor() { super(); @@ -125,6 +139,8 @@ export default class ExportDialog extends BasicWidget { doRender() { this.$widget = $(TPL); + // Remove once bootstrap is fixed. + // @ts-ignore this.modal = bootstrap.Modal.getOrCreateInstance(this.$widget); this.$form = this.$widget.find(".export-form"); this.$noteTitle = this.$widget.find(".export-note-title"); @@ -136,7 +152,7 @@ export default class ExportDialog extends BasicWidget { this.$opmlVersions = this.$widget.find(".opml-versions"); this.$form.on("submit", () => { - this.modal.hide(); + this.modal?.hide(); const exportType = this.$widget.find("input[name='export-type']:checked").val(); @@ -149,13 +165,15 @@ export default class ExportDialog extends BasicWidget { const exportVersion = exportFormat === "opml" ? this.$widget.find("input[name='opml-version']:checked").val() : "1.0"; - this.exportBranch(this.branchId, exportType, exportFormat, exportVersion); + if (this.branchId) { + this.exportBranch(this.branchId, String(exportType), String(exportFormat), String(exportVersion)); + } return false; }); this.$widget.find("input[name=export-type]").on("change", (e) => { - if (e.currentTarget.value === "subtree") { + if ((e.currentTarget as HTMLInputElement).value === "subtree") { if (this.$widget.find("input[name=export-subtree-format]:checked").length === 0) { this.$widget.find("input[name=export-subtree-format]:first").prop("checked", true); } @@ -173,7 +191,7 @@ export default class ExportDialog extends BasicWidget { }); this.$widget.find("input[name=export-subtree-format]").on("change", (e) => { - if (e.currentTarget.value === "opml") { + if ((e.currentTarget as HTMLInputElement).value === "opml") { this.$opmlVersions.slideDown(); } else { this.$opmlVersions.slideUp(); @@ -181,7 +199,7 @@ export default class ExportDialog extends BasicWidget { }); } - async showExportDialogEvent({ notePath, defaultType }) { + async showExportDialogEvent({ notePath, defaultType }: EventData<"showExportDialog">) { this.taskId = ""; this.$exportButton.removeAttr("disabled"); @@ -201,11 +219,15 @@ export default class ExportDialog extends BasicWidget { const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath); - this.branchId = await froca.getBranchId(parentNoteId, noteId); - this.$noteTitle.text(await treeService.getNoteTitle(noteId)); + if (parentNoteId) { + this.branchId = await froca.getBranchId(parentNoteId, noteId); + } + if (noteId) { + this.$noteTitle.text(await treeService.getNoteTitle(noteId)); + } } - exportBranch(branchId, type, format, version) { + exportBranch(branchId: string, type: string, format: string, version: string) { this.taskId = utils.randomString(10); const url = openService.getUrlForDownload(`api/branches/${branchId}/export/${type}/${format}/${version}/${this.taskId}`); @@ -215,12 +237,14 @@ export default class ExportDialog extends BasicWidget { } ws.subscribeToMessages(async (message) => { - const makeToast = (id, message) => ({ - id: id, - title: t("export.export_status"), - message: message, - icon: "arrow-square-up-right" - }); + function makeToast(id: string, message: string): ToastOptions { + return { + id: id, + title: t("export.export_status"), + message: message, + icon: "arrow-square-up-right" + }; + }; if (message.taskType !== "export") { return; From 9c4535badea54d06f2dc08d3f065a0249ad54a94 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 1 Feb 2025 16:31:25 +0200 Subject: [PATCH 03/59] desktop app(export pdf): enable document outline and PDF tags generation --- src/services/window.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/window.ts b/src/services/window.ts index d8f86379f..ff391875b 100644 --- a/src/services/window.ts +++ b/src/services/window.ts @@ -76,6 +76,8 @@ ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => { try { buffer = await browserWindow.webContents.printToPDF({ landscape: opts.landscape, + generateDocumentOutline: true, + generateTaggedPDF: true, displayHeaderFooter: true, headerTemplate: `
`, footerTemplate: ` From 8b8f0c289e9a1a71cd197f1686efbeee500c5bc9 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 1 Feb 2025 16:51:04 +0200 Subject: [PATCH 04/59] client(print): use a different base font size --- src/public/stylesheets/print.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/public/stylesheets/print.css b/src/public/stylesheets/print.css index 34521d733..1f6fdc287 100644 --- a/src/public/stylesheets/print.css +++ b/src/public/stylesheets/print.css @@ -4,6 +4,12 @@ --launcher-pane-background-color: var(--main-background-color); --main-text-color: black; --input-text-color: var(--main-text-color); + + --print-font-size: 11pt; +} + +.ck-content { + font-size: var(--print-font-size); } .no-print, From f2cc98eeec8d611fdf4150364ddb4e0f397f0edc Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sat, 1 Feb 2025 16:58:52 +0200 Subject: [PATCH 05/59] client(print): fix the content having a left margin when the note is read-only --- src/public/stylesheets/print.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/public/stylesheets/print.css b/src/public/stylesheets/print.css index 1f6fdc287..74153b94c 100644 --- a/src/public/stylesheets/print.css +++ b/src/public/stylesheets/print.css @@ -12,6 +12,10 @@ font-size: var(--print-font-size); } +.note-detail-readonly-text { + padding: 0 !important; +} + .no-print, .no-print *, .tab-row-container, From 9a3a6f90ea62369707b4836b27ba1e0ef690128f Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 1 Feb 2025 16:55:50 +0200 Subject: [PATCH 06/59] Add option to export --- src/public/app/widgets/dialogs/export.ts | 7 +++++++ src/public/translations/en/translation.json | 3 ++- src/services/export/pdf.ts | 0 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/services/export/pdf.ts diff --git a/src/public/app/widgets/dialogs/export.ts b/src/public/app/widgets/dialogs/export.ts index b8b51d80f..8f1fe5306 100644 --- a/src/public/app/widgets/dialogs/export.ts +++ b/src/public/app/widgets/dialogs/export.ts @@ -106,6 +106,13 @@ const TPL = ` ${t("export.format_markdown")}
+ +
+ +