mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	Merge pull request #1091 from TriliumNext/feature/different_printing_mechanism
Export as PDF
This commit is contained in:
		
						commit
						c09ef76f87
					
				@ -81,7 +81,6 @@ const copy = async () => {
 | 
			
		||||
        "node_modules/mermaid/dist/",
 | 
			
		||||
        "node_modules/jquery/dist/",
 | 
			
		||||
        "node_modules/jquery-hotkeys/",
 | 
			
		||||
        "node_modules/print-this/",
 | 
			
		||||
        "node_modules/split.js/dist/",
 | 
			
		||||
        "node_modules/panzoom/dist/",
 | 
			
		||||
        "node_modules/i18next/",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -78,7 +78,6 @@
 | 
			
		||||
        "normalize-strings": "1.1.1",
 | 
			
		||||
        "normalize.css": "8.0.1",
 | 
			
		||||
        "panzoom": "9.4.3",
 | 
			
		||||
        "print-this": "2.0.0",
 | 
			
		||||
        "rand-token": "1.0.1",
 | 
			
		||||
        "react": "18.3.1",
 | 
			
		||||
        "react-dom": "18.3.1",
 | 
			
		||||
@ -12681,15 +12680,6 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/sindresorhus"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/opencollective-postinstall": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "bin": {
 | 
			
		||||
        "opencollective-postinstall": "index.js"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ora": {
 | 
			
		||||
      "version": "5.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
 | 
			
		||||
@ -13495,17 +13485,6 @@
 | 
			
		||||
        "url": "https://github.com/prettier/prettier?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/print-this": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/print-this/-/print-this-2.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-/v1/tXs4BQGpEF7OYKe05h4xiQR09Q4HgASL28pngx6aedCQaB1OlHs8t9RDVgUayXHDWHG9V5EBjPlXb46k4w==",
 | 
			
		||||
      "hasInstallScript": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "jquery": ">=1.11",
 | 
			
		||||
        "opencollective-postinstall": "^2.0.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/proc-log": {
 | 
			
		||||
      "version": "2.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
 | 
			
		||||
 | 
			
		||||
@ -123,7 +123,6 @@
 | 
			
		||||
    "normalize-strings": "1.1.1",
 | 
			
		||||
    "normalize.css": "8.0.1",
 | 
			
		||||
    "panzoom": "9.4.3",
 | 
			
		||||
    "print-this": "2.0.0",
 | 
			
		||||
    "rand-token": "1.0.1",
 | 
			
		||||
    "react": "18.3.1",
 | 
			
		||||
    "react-dom": "18.3.1",
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,10 @@ export type CommandMappings = {
 | 
			
		||||
    showOptions: CommandData & {
 | 
			
		||||
        section: string;
 | 
			
		||||
    };
 | 
			
		||||
    showExportDialog: CommandData & {
 | 
			
		||||
        notePath: string;
 | 
			
		||||
        defaultType: "single";
 | 
			
		||||
    };
 | 
			
		||||
    showDeleteNotesDialog: CommandData & {
 | 
			
		||||
        branchIdsToDelete: string[];
 | 
			
		||||
        callback: (value: ResolveOptions) => void;
 | 
			
		||||
 | 
			
		||||
@ -51,10 +51,6 @@ const RELATION_MAP: Library = {
 | 
			
		||||
    css: ["stylesheets/relation_map.css"]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const PRINT_THIS: Library = {
 | 
			
		||||
    js: ["node_modules/print-this/printThis.js"]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CALENDAR_WIDGET: Library = {
 | 
			
		||||
    css: ["stylesheets/calendar.css"]
 | 
			
		||||
};
 | 
			
		||||
@ -193,7 +189,6 @@ export default {
 | 
			
		||||
    CODE_MIRROR,
 | 
			
		||||
    ESLINT,
 | 
			
		||||
    RELATION_MAP,
 | 
			
		||||
    PRINT_THIS,
 | 
			
		||||
    CALENDAR_WIDGET,
 | 
			
		||||
    KATEX,
 | 
			
		||||
    WHEEL_ZOOM,
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,15 @@ import dialogService from "../../services/dialog.js";
 | 
			
		||||
import server from "../../services/server.js";
 | 
			
		||||
import toastService from "../../services/toast.js";
 | 
			
		||||
import ws from "../../services/ws.js";
 | 
			
		||||
import appContext from "../../components/app_context.js";
 | 
			
		||||
import appContext, { type EventData } from "../../components/app_context.js";
 | 
			
		||||
import { t } from "../../services/i18n.js";
 | 
			
		||||
import type FNote from "../../entities/fnote.js";
 | 
			
		||||
import type { FAttachmentRow } from "../../entities/fattachment.js";
 | 
			
		||||
 | 
			
		||||
// TODO: Deduplicate with server
 | 
			
		||||
interface ConvertToAttachmentResponse {
 | 
			
		||||
    attachment: FAttachmentRow;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="dropdown note-actions">
 | 
			
		||||
@ -52,8 +59,12 @@ const TPL = `
 | 
			
		||||
        </li>
 | 
			
		||||
 | 
			
		||||
        <li data-trigger-command="printActiveNote" class="dropdown-item print-active-note-button">
 | 
			
		||||
            <span class="bx bx-printer"></span> ${t("note_actions.print_note")}<kbd data-command="printActiveNote"></kbd></li>
 | 
			
		||||
            <span class="bx bx-printer"></span> ${t("note_actions.print_note")}<kbd data-command="printActiveNote"></kbd>
 | 
			
		||||
        </li>
 | 
			
		||||
 | 
			
		||||
        <li data-trigger-command="exportAsPdf" class="dropdown-item export-as-pdf-button">
 | 
			
		||||
            <span class="bx bxs-file-pdf"></span> ${t("note_actions.print_pdf")}<kbd data-command="exportAsPdf"></kbd>
 | 
			
		||||
        </li>
 | 
			
		||||
 | 
			
		||||
        <div class="dropdown-divider"></div>
 | 
			
		||||
 | 
			
		||||
@ -100,17 +111,37 @@ const TPL = `
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
    private $convertNoteIntoAttachmentButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $findInTextButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $printActiveNoteButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $exportAsPdfButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $showSourceButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $showAttachmentsButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $renderNoteButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $saveRevisionButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $exportNoteButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $importNoteButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $openNoteExternallyButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $openNoteCustomButton!: JQuery<HTMLElement>;
 | 
			
		||||
    private $deleteNoteButton!: JQuery<HTMLElement>;
 | 
			
		||||
 | 
			
		||||
    isEnabled() {
 | 
			
		||||
        return this.note?.type !== "launcher";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    doRender() {
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
        this.$widget.on("show.bs.dropdown", () => this.refreshVisibility(this.note));
 | 
			
		||||
        this.$widget.on("show.bs.dropdown", () => {
 | 
			
		||||
            if (this.note) {
 | 
			
		||||
                this.refreshVisibility(this.note);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$convertNoteIntoAttachmentButton = this.$widget.find("[data-trigger-command='convertNoteIntoAttachment']");
 | 
			
		||||
        this.$findInTextButton = this.$widget.find(".find-in-text-button");
 | 
			
		||||
        this.$printActiveNoteButton = this.$widget.find(".print-active-note-button");
 | 
			
		||||
        this.$exportAsPdfButton = this.$widget.find(".export-as-pdf-button");
 | 
			
		||||
        this.$showSourceButton = this.$widget.find(".show-source-button");
 | 
			
		||||
        this.$showAttachmentsButton = this.$widget.find(".show-attachments-button");
 | 
			
		||||
        this.$renderNoteButton = this.$widget.find(".render-note-button");
 | 
			
		||||
@ -118,7 +149,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
        this.$exportNoteButton = this.$widget.find(".export-note-button");
 | 
			
		||||
        this.$exportNoteButton.on("click", () => {
 | 
			
		||||
            if (this.$exportNoteButton.hasClass("disabled")) {
 | 
			
		||||
            if (this.$exportNoteButton.hasClass("disabled") || !this.noteContext?.notePath) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -129,7 +160,11 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$importNoteButton = this.$widget.find(".import-files-button");
 | 
			
		||||
        this.$importNoteButton.on("click", () => this.triggerCommand("showImportDialog", { noteId: this.noteId }));
 | 
			
		||||
        this.$importNoteButton.on("click", () => {
 | 
			
		||||
            if (this.noteId) {
 | 
			
		||||
                this.triggerCommand("showImportDialog", { noteId: this.noteId });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$widget.on("click", ".dropdown-item", () => this.$widget.find("[data-bs-toggle='dropdown']").dropdown("toggle"));
 | 
			
		||||
 | 
			
		||||
@ -138,7 +173,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
 | 
			
		||||
        this.$deleteNoteButton = this.$widget.find(".delete-note-button");
 | 
			
		||||
        this.$deleteNoteButton.on("click", () => {
 | 
			
		||||
            if (this.note.noteId === "root") {
 | 
			
		||||
            if (!this.note || this.note.noteId === "root") {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -146,7 +181,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshVisibility(note) {
 | 
			
		||||
    async refreshVisibility(note: FNote) {
 | 
			
		||||
        const isInOptions = note.noteId.startsWith("_options");
 | 
			
		||||
 | 
			
		||||
        this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment());
 | 
			
		||||
@ -156,7 +191,10 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        this.toggleDisabled(this.$showAttachmentsButton, !isInOptions);
 | 
			
		||||
        this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "geoMap"].includes(note.type));
 | 
			
		||||
 | 
			
		||||
        this.toggleDisabled(this.$printActiveNoteButton, ["text", "code"].includes(note.type));
 | 
			
		||||
        const canPrint = ["text", "code"].includes(note.type);
 | 
			
		||||
        this.toggleDisabled(this.$printActiveNoteButton, canPrint);
 | 
			
		||||
        this.toggleDisabled(this.$exportAsPdfButton, canPrint);
 | 
			
		||||
        this.$exportAsPdfButton.toggleClass("hidden-ext", !utils.isElectron());
 | 
			
		||||
 | 
			
		||||
        this.$renderNoteButton.toggle(note.type === "render");
 | 
			
		||||
 | 
			
		||||
@ -177,11 +215,11 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async convertNoteIntoAttachmentCommand() {
 | 
			
		||||
        if (!(await dialogService.confirm(t("note_actions.convert_into_attachment_prompt", { title: this.note.title })))) {
 | 
			
		||||
        if (!this.note || !(await dialogService.confirm(t("note_actions.convert_into_attachment_prompt", { title: this.note.title })))) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const { attachment: newAttachment } = await server.post(`notes/${this.noteId}/convert-to-attachment`);
 | 
			
		||||
        const { attachment: newAttachment } = await server.post<ConvertToAttachmentResponse>(`notes/${this.noteId}/convert-to-attachment`);
 | 
			
		||||
 | 
			
		||||
        if (!newAttachment) {
 | 
			
		||||
            toastService.showMessage(t("note_actions.convert_into_attachment_failed", { title: this.note.title }));
 | 
			
		||||
@ -198,7 +236,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleDisabled($el, enable) {
 | 
			
		||||
    toggleDisabled($el: JQuery<HTMLElement>, enable: boolean) {
 | 
			
		||||
        if (enable) {
 | 
			
		||||
            $el.removeAttr("disabled");
 | 
			
		||||
        } else {
 | 
			
		||||
@ -206,7 +244,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    entitiesReloadedEvent({ loadResults }) {
 | 
			
		||||
    entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
 | 
			
		||||
        if (loadResults.isNoteReloaded(this.noteId)) {
 | 
			
		||||
            this.refresh();
 | 
			
		||||
        }
 | 
			
		||||
@ -32,6 +32,7 @@ import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js";
 | 
			
		||||
import MindMapWidget from "./type_widgets/mind_map.js";
 | 
			
		||||
import { getStylesheetUrl, isSyntaxHighlightEnabled } from "../services/syntax_highlight.js";
 | 
			
		||||
import GeoMapTypeWidget from "./type_widgets/geo_map.js";
 | 
			
		||||
import utils from "../services/utils.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="note-detail">
 | 
			
		||||
@ -249,45 +250,18 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await libraryLoader.requireLibrary(libraryLoader.PRINT_THIS);
 | 
			
		||||
        window.print();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        let $promotedAttributes = $("");
 | 
			
		||||
 | 
			
		||||
        if (this.note.getPromotedDefinitionAttributes().length > 0) {
 | 
			
		||||
            $promotedAttributes = (await attributeRenderer.renderNormalAttributes(this.note)).$renderedAttributes;
 | 
			
		||||
    async exportAsPdfEvent() {
 | 
			
		||||
        if (!this.noteContext.isActive()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const { assetPath } = window.glob;
 | 
			
		||||
        const cssToLoad = [
 | 
			
		||||
            `${assetPath}/node_modules/codemirror/lib/codemirror.css`,
 | 
			
		||||
            `${assetPath}/libraries/ckeditor/ckeditor-content.css`,
 | 
			
		||||
            `${assetPath}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
 | 
			
		||||
            `${assetPath}/node_modules/katex/dist/katex.min.css`,
 | 
			
		||||
            `${assetPath}/stylesheets/print.css`,
 | 
			
		||||
            `${assetPath}/stylesheets/relation_map.css`,
 | 
			
		||||
            `${assetPath}/stylesheets/ckeditor-theme.css`
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if (isSyntaxHighlightEnabled()) {
 | 
			
		||||
            cssToLoad.push(getStylesheetUrl("default:vs"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$widget.find(".note-detail-printable:visible").printThis({
 | 
			
		||||
            header: $("<div>").append($("<h2>").text(this.note.title)).append($promotedAttributes).prop("outerHTML"),
 | 
			
		||||
 | 
			
		||||
            footer: `
 | 
			
		||||
<script src="${assetPath}/node_modules/katex/dist/katex.min.js"></script>
 | 
			
		||||
<script src="${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
 | 
			
		||||
<script src="${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
 | 
			
		||||
<script>
 | 
			
		||||
    document.body.className += ' ck-content printed-content';
 | 
			
		||||
 | 
			
		||||
    renderMathInElement(document.body, {trust: true});
 | 
			
		||||
</script>
 | 
			
		||||
`,
 | 
			
		||||
            importCSS: false,
 | 
			
		||||
            loadCSS: cssToLoad,
 | 
			
		||||
            debug: true
 | 
			
		||||
        const { ipcRenderer } = utils.dynamicRequire("electron");
 | 
			
		||||
        ipcRenderer.send("export-as-pdf", {
 | 
			
		||||
            title: this.note.title,
 | 
			
		||||
            landscape: this.note.hasAttribute("label", "printLandscape")
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,40 +1,162 @@
 | 
			
		||||
@media print {
 | 
			
		||||
    html body {
 | 
			
		||||
        /* https://github.com/zadam/trilium/issues/3202 */
 | 
			
		||||
        color: black;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .no-print,
 | 
			
		||||
    .no-print * {
 | 
			
		||||
        display: none !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .relation-map-wrapper {
 | 
			
		||||
        height: 100vh !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .table thead th,
 | 
			
		||||
    .table td,
 | 
			
		||||
    .table th {
 | 
			
		||||
        /* Fix center vertical alignment of table cells */
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pre {
 | 
			
		||||
        box-shadow: unset !important;
 | 
			
		||||
        border: 0.75pt solid gray !important;
 | 
			
		||||
        border-radius: 2pt !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    span[style] {
 | 
			
		||||
        print-color-adjust: exact;
 | 
			
		||||
        -webkit-print-color-adjust: exact;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Fix visibility of checkbox checkmarks
 | 
			
		||||
       see https://github.com/TriliumNext/Notes/issues/901 */
 | 
			
		||||
    .ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input[checked]::after {
 | 
			
		||||
        /* fallback to default ck-editor green */
 | 
			
		||||
        border-color: hsl(126, 64%, 41%);
 | 
			
		||||
    }
 | 
			
		||||
:root {
 | 
			
		||||
    --main-background-color: white;
 | 
			
		||||
    --root-background: var(--main-background-color);
 | 
			
		||||
    --launcher-pane-background-color: var(--main-background-color);
 | 
			
		||||
    --main-text-color: black;
 | 
			
		||||
    --input-text-color: var(--main-text-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.no-print,
 | 
			
		||||
.no-print *,
 | 
			
		||||
.tab-row-container,
 | 
			
		||||
.tab-row-widget,
 | 
			
		||||
#launcher-pane,
 | 
			
		||||
#left-pane,
 | 
			
		||||
#right-pane,
 | 
			
		||||
.title-row .note-icon-widget,
 | 
			
		||||
.title-row .button-widget,
 | 
			
		||||
.ribbon-container,
 | 
			
		||||
.promoted-attributes-widget,
 | 
			
		||||
.scroll-padding-widget,
 | 
			
		||||
.note-list-widget,
 | 
			
		||||
.spacer {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
}    
 | 
			
		||||
 | 
			
		||||
body.mobile #mobile-sidebar-wrapper,
 | 
			
		||||
body.mobile .classic-toolbar-widget,
 | 
			
		||||
body.mobile .action-button {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body.mobile #detail-container {
 | 
			
		||||
    max-height: unset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body.mobile .note-title-widget {
 | 
			
		||||
    padding: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body,
 | 
			
		||||
#root-widget,
 | 
			
		||||
#rest-pane > div.component:first-child,
 | 
			
		||||
.note-detail-printable,
 | 
			
		||||
.note-detail-editable-text-editor {
 | 
			
		||||
    height: unset !important;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.note-title-widget input,
 | 
			
		||||
.note-detail-editable-text,
 | 
			
		||||
.note-detail-editable-text-editor {
 | 
			
		||||
    padding: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html,
 | 
			
		||||
body {
 | 
			
		||||
    width: unset !important;
 | 
			
		||||
    height: unset !important;
 | 
			
		||||
    overflow: visible;
 | 
			
		||||
    position: unset;
 | 
			
		||||
    /* https://github.com/zadam/trilium/issues/3202 */
 | 
			
		||||
    color: black;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#root-widget,
 | 
			
		||||
#horizontal-main-container,
 | 
			
		||||
#rest-pane,
 | 
			
		||||
#vertical-main-container,
 | 
			
		||||
#center-pane,
 | 
			
		||||
.split-note-container-widget,
 | 
			
		||||
.note-split:not(.hidden-ext),
 | 
			
		||||
body.mobile #mobile-rest-container {
 | 
			
		||||
    display: block !important;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    border-radius: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#center-pane,
 | 
			
		||||
#rest-pane,
 | 
			
		||||
.note-split,
 | 
			
		||||
body.mobile #detail-container {
 | 
			
		||||
    width: unset !important;
 | 
			
		||||
    max-width: unset !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.component {
 | 
			
		||||
    contain: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Respect page breaks */
 | 
			
		||||
.page-break {
 | 
			
		||||
    page-break-after: always;
 | 
			
		||||
    break-after: always;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.page-break > * {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.relation-map-wrapper {
 | 
			
		||||
    height: 100vh !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table thead th,
 | 
			
		||||
.table td,
 | 
			
		||||
.table th {
 | 
			
		||||
    /* Fix center vertical alignment of table cells */
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pre {
 | 
			
		||||
    box-shadow: unset !important;
 | 
			
		||||
    border: 0.75pt solid gray !important;
 | 
			
		||||
    border-radius: 2pt !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th,
 | 
			
		||||
span[style] {
 | 
			
		||||
    print-color-adjust: exact;
 | 
			
		||||
    -webkit-print-color-adjust: exact;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Text note specific fixes
 | 
			
		||||
 */
 | 
			
		||||
.ck-widget {
 | 
			
		||||
    outline: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ck-placeholder,
 | 
			
		||||
.ck-widget__type-around,
 | 
			
		||||
.ck-widget__selection-handle {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,
 | 
			
		||||
.ck-widget.table td.ck-editor__nested-editable:focus,
 | 
			
		||||
.ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused,
 | 
			
		||||
.ck-widget.table th.ck-editor__nested-editable:focus {
 | 
			
		||||
    background: unset !important;
 | 
			
		||||
    outline: unset !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fix visibility of checkbox checkmarks
 | 
			
		||||
    see https://github.com/TriliumNext/Notes/issues/901 */
 | 
			
		||||
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input[checked]::after {
 | 
			
		||||
    /* fallback to default ck-editor green */
 | 
			
		||||
    border-color: hsl(126, 64%, 41%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.include-note .include-note-content {
 | 
			
		||||
    max-height: unset !important;
 | 
			
		||||
    overflow: unset !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Code note specific fixes.
 | 
			
		||||
 */
 | 
			
		||||
.note-detail-code pre {
 | 
			
		||||
    border: unset !important;
 | 
			
		||||
    border-radius: unset !important;
 | 
			
		||||
}
 | 
			
		||||
@ -1604,4 +1604,4 @@ body.electron.platform-darwin:not(.native-titlebar) .tab-row-container {
 | 
			
		||||
    border-color: var(--hover-item-border-color);
 | 
			
		||||
    background: var(--hover-item-background-color);
 | 
			
		||||
    color: var(--hover-item-text-color);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -104,11 +104,13 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ck-content p code {
 | 
			
		||||
    border: 1px solid var(--card-border-color);
 | 
			
		||||
    box-shadow: var(--card-box-shadow);
 | 
			
		||||
    border-radius: 6px;
 | 
			
		||||
    background-color: var(--card-background-color);
 | 
			
		||||
@media (screen) {
 | 
			
		||||
    .ck-content p code {
 | 
			
		||||
        border: 1px solid var(--card-border-color);
 | 
			
		||||
        box-shadow: var(--card-box-shadow);
 | 
			
		||||
        border-radius: 6px;
 | 
			
		||||
        background-color: var(--card-background-color);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.note-detail-printable:not(.word-wrap) pre code {
 | 
			
		||||
 | 
			
		||||
@ -672,7 +672,8 @@
 | 
			
		||||
    "save_revision": "Save revision",
 | 
			
		||||
    "convert_into_attachment_failed": "Converting note '{{title}}' failed.",
 | 
			
		||||
    "convert_into_attachment_successful": "Note '{{title}}' has been converted to attachment.",
 | 
			
		||||
    "convert_into_attachment_prompt": "Are you sure you want to convert note '{{title}}' into an attachment of the parent note?"
 | 
			
		||||
    "convert_into_attachment_prompt": "Are you sure you want to convert note '{{title}}' into an attachment of the parent note?",
 | 
			
		||||
    "print_pdf": "Export as PDF..."
 | 
			
		||||
  },
 | 
			
		||||
  "onclick_button": {
 | 
			
		||||
    "no_click_handler": "Button widget '{{componentId}}' has no defined click handler"
 | 
			
		||||
 | 
			
		||||
@ -824,7 +824,8 @@
 | 
			
		||||
    "search_in_note": "Caută în notiță",
 | 
			
		||||
    "convert_into_attachment_failed": "Nu s-a putut converti notița „{{title}}”.",
 | 
			
		||||
    "convert_into_attachment_successful": "Notița „{{title}}” a fost convertită în atașament.",
 | 
			
		||||
    "convert_into_attachment_prompt": "Doriți convertirea notiței „{{title}}” într-un atașament al notiței părinte?"
 | 
			
		||||
    "convert_into_attachment_prompt": "Doriți convertirea notiței „{{title}}” într-un atașament al notiței părinte?",
 | 
			
		||||
    "print_pdf": "Exportare ca PDF..."
 | 
			
		||||
  },
 | 
			
		||||
  "note_erasure_timeout": {
 | 
			
		||||
    "deleted_notes_erased": "Notițele șterse au fost eliminate permanent.",
 | 
			
		||||
 | 
			
		||||
@ -66,8 +66,6 @@ async function register(app: express.Application) {
 | 
			
		||||
 | 
			
		||||
    app.use(`/${assetPath}/node_modules/jquery-hotkeys/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/jquery-hotkeys/")));
 | 
			
		||||
 | 
			
		||||
    app.use(`/${assetPath}/node_modules/print-this/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/print-this/")));
 | 
			
		||||
 | 
			
		||||
    app.use(`/${assetPath}/node_modules/split.js/dist/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/split.js/dist/")));
 | 
			
		||||
 | 
			
		||||
    app.use(`/${assetPath}/node_modules/panzoom/dist/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/panzoom/dist/")));
 | 
			
		||||
 | 
			
		||||
@ -503,6 +503,12 @@ function getDefaultKeyboardActions() {
 | 
			
		||||
            description: t("keyboard_actions.print-active-note"),
 | 
			
		||||
            scope: "window"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            actionName: "exportAsPdf",
 | 
			
		||||
            defaultShortcuts: [],
 | 
			
		||||
            description: t("keyboard_actions.export-as-pdf"),
 | 
			
		||||
            scope: "window"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            actionName: "openNoteExternally",
 | 
			
		||||
            defaultShortcuts: [],
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,7 @@ const enum KeyboardActionNamesEnum {
 | 
			
		||||
    toggleRibbonTabSimilarNotes,
 | 
			
		||||
    toggleRightPane,
 | 
			
		||||
    printActiveNote,
 | 
			
		||||
    exportAsPdf,
 | 
			
		||||
    openNoteExternally,
 | 
			
		||||
    renderActiveNote,
 | 
			
		||||
    runActiveNote,
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import fs from "fs/promises";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import url from "url";
 | 
			
		||||
import port from "./port.js";
 | 
			
		||||
@ -7,12 +8,13 @@ import sqlInit from "./sql_init.js";
 | 
			
		||||
import cls from "./cls.js";
 | 
			
		||||
import keyboardActionsService from "./keyboard_actions.js";
 | 
			
		||||
import remoteMain from "@electron/remote/main/index.js";
 | 
			
		||||
import type { App, BrowserWindow, BrowserWindowConstructorOptions, WebContents } from "electron";
 | 
			
		||||
import { ipcMain } from "electron";
 | 
			
		||||
import { isDev, isMac, isWindows } from "./utils.js";
 | 
			
		||||
import { BrowserWindow, shell, type App, type BrowserWindowConstructorOptions, type WebContents } from "electron";
 | 
			
		||||
import { dialog, ipcMain } from "electron";
 | 
			
		||||
import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js";
 | 
			
		||||
 | 
			
		||||
import { fileURLToPath } from "url";
 | 
			
		||||
import { dirname } from "path";
 | 
			
		||||
import { t } from "i18next";
 | 
			
		||||
 | 
			
		||||
// Prevent the window being garbage collected
 | 
			
		||||
let mainWindow: BrowserWindow | null;
 | 
			
		||||
@ -46,6 +48,50 @@ ipcMain.on("create-extra-window", (event, arg) => {
 | 
			
		||||
    createExtraWindow(arg.extraWindowHash);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface ExportAsPdfOpts {
 | 
			
		||||
    title: string;
 | 
			
		||||
    landscape: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
 | 
			
		||||
    const browserWindow = BrowserWindow.fromWebContents(e.sender);
 | 
			
		||||
    if (!browserWindow) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const filePath = dialog.showSaveDialogSync(browserWindow, {
 | 
			
		||||
        defaultPath: formatDownloadTitle(opts.title, "file", "application/pdf"),
 | 
			
		||||
        filters: [
 | 
			
		||||
            {
 | 
			
		||||
                name: t("pdf.export_filter"),
 | 
			
		||||
                extensions: [ "pdf" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    });
 | 
			
		||||
    if (!filePath) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let buffer: Buffer;
 | 
			
		||||
    try {
 | 
			
		||||
        buffer = await browserWindow.webContents.printToPDF({
 | 
			
		||||
            landscape: opts.landscape
 | 
			
		||||
        });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-export-message"));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        await fs.writeFile(filePath, buffer);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-save-message"));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shell.openPath(filePath);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function createMainWindow(app: App) {
 | 
			
		||||
    if ("setUserTasks" in app) {
 | 
			
		||||
        app.setUserTasks([
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,7 @@
 | 
			
		||||
<% } %>
 | 
			
		||||
 | 
			
		||||
<link href="<%= assetPath %>/stylesheets/style.css" rel="stylesheet">
 | 
			
		||||
<link href="<%= assetPath %>/stylesheets/print.css" rel="stylesheet" media="print">
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
    $("body").show();
 | 
			
		||||
 | 
			
		||||
@ -129,6 +129,7 @@
 | 
			
		||||
    <link href="<%= themeCssUrl %>" rel="stylesheet">
 | 
			
		||||
<% } %>
 | 
			
		||||
<link href="<%= assetPath %>/stylesheets/style.css" rel="stylesheet">
 | 
			
		||||
<link href="<%= assetPath %>/stylesheets/print.css" rel="stylesheet" media="print">
 | 
			
		||||
 | 
			
		||||
<link rel="stylesheet" type="text/css" href="<%= assetPath %>/node_modules/boxicons/css/boxicons.min.css">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,8 @@
 | 
			
		||||
    "force-save-revision": "Force creating / saving new note revision of the active note",
 | 
			
		||||
    "show-help": "Shows built-in Help / cheatsheet",
 | 
			
		||||
    "toggle-book-properties": "Toggle Book Properties",
 | 
			
		||||
    "toggle-classic-editor-toolbar": "Toggle the Formatting tab for the editor with fixed toolbar"
 | 
			
		||||
    "toggle-classic-editor-toolbar": "Toggle the Formatting tab for the editor with fixed toolbar",
 | 
			
		||||
    "export-as-pdf": "Exports the current note as a PDF"
 | 
			
		||||
  },
 | 
			
		||||
  "login": {
 | 
			
		||||
    "title": "Login",
 | 
			
		||||
@ -253,5 +254,11 @@
 | 
			
		||||
  },
 | 
			
		||||
  "content_renderer": {
 | 
			
		||||
    "note-cannot-be-displayed": "This note type cannot be displayed."
 | 
			
		||||
  },
 | 
			
		||||
  "pdf": {
 | 
			
		||||
    "export_filter": "PDF Document (*.pdf)",
 | 
			
		||||
    "unable-to-export-message": "The current note could not be exported as a PDF.",
 | 
			
		||||
    "unable-to-export-title": "Unable to export as PDF",
 | 
			
		||||
    "unable-to-save-message": "The selected file could not be written to. Try again or select another destination."
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,8 @@
 | 
			
		||||
    "unhoist": "Defocalizează complet",
 | 
			
		||||
    "zoom-in": "Mărește zoom-ul",
 | 
			
		||||
    "zoom-out": "Micșorează zoom-ul",
 | 
			
		||||
    "toggle-classic-editor-toolbar": "Comută tab-ul „Formatare” pentru editorul cu bară fixă"
 | 
			
		||||
    "toggle-classic-editor-toolbar": "Comută tab-ul „Formatare” pentru editorul cu bară fixă",
 | 
			
		||||
    "export-as-pdf": "Exportă notița curentă ca PDF"
 | 
			
		||||
  },
 | 
			
		||||
  "login": {
 | 
			
		||||
    "button": "Autentifică",
 | 
			
		||||
@ -254,5 +255,11 @@
 | 
			
		||||
  },
 | 
			
		||||
  "content_renderer": {
 | 
			
		||||
    "note-cannot-be-displayed": "Acest tip de notiță nu poate fi afișat."
 | 
			
		||||
  },
 | 
			
		||||
  "pdf": {
 | 
			
		||||
    "export_filter": "Document PDF (*.pdf)",
 | 
			
		||||
    "unable-to-export-message": "Notița curentă nu a putut fi exportată ca PDF.",
 | 
			
		||||
    "unable-to-export-title": "Nu s-a putut exporta ca PDF",
 | 
			
		||||
    "unable-to-save-message": "Nu s-a putut scrie fișierul selectat. Încercați din nou sau selectați altă destinație."
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user