mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08: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/mermaid/dist/",
|
||||||
"node_modules/jquery/dist/",
|
"node_modules/jquery/dist/",
|
||||||
"node_modules/jquery-hotkeys/",
|
"node_modules/jquery-hotkeys/",
|
||||||
"node_modules/print-this/",
|
|
||||||
"node_modules/split.js/dist/",
|
"node_modules/split.js/dist/",
|
||||||
"node_modules/panzoom/dist/",
|
"node_modules/panzoom/dist/",
|
||||||
"node_modules/i18next/",
|
"node_modules/i18next/",
|
||||||
|
21
package-lock.json
generated
21
package-lock.json
generated
@ -78,7 +78,6 @@
|
|||||||
"normalize-strings": "1.1.1",
|
"normalize-strings": "1.1.1",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"panzoom": "9.4.3",
|
"panzoom": "9.4.3",
|
||||||
"print-this": "2.0.0",
|
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
@ -12681,15 +12680,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/ora": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
||||||
@ -13495,17 +13485,6 @@
|
|||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"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": {
|
"node_modules/proc-log": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
|
||||||
|
@ -123,7 +123,6 @@
|
|||||||
"normalize-strings": "1.1.1",
|
"normalize-strings": "1.1.1",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"panzoom": "9.4.3",
|
"panzoom": "9.4.3",
|
||||||
"print-this": "2.0.0",
|
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
|
@ -81,6 +81,10 @@ export type CommandMappings = {
|
|||||||
showOptions: CommandData & {
|
showOptions: CommandData & {
|
||||||
section: string;
|
section: string;
|
||||||
};
|
};
|
||||||
|
showExportDialog: CommandData & {
|
||||||
|
notePath: string;
|
||||||
|
defaultType: "single";
|
||||||
|
};
|
||||||
showDeleteNotesDialog: CommandData & {
|
showDeleteNotesDialog: CommandData & {
|
||||||
branchIdsToDelete: string[];
|
branchIdsToDelete: string[];
|
||||||
callback: (value: ResolveOptions) => void;
|
callback: (value: ResolveOptions) => void;
|
||||||
|
@ -51,10 +51,6 @@ const RELATION_MAP: Library = {
|
|||||||
css: ["stylesheets/relation_map.css"]
|
css: ["stylesheets/relation_map.css"]
|
||||||
};
|
};
|
||||||
|
|
||||||
const PRINT_THIS: Library = {
|
|
||||||
js: ["node_modules/print-this/printThis.js"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const CALENDAR_WIDGET: Library = {
|
const CALENDAR_WIDGET: Library = {
|
||||||
css: ["stylesheets/calendar.css"]
|
css: ["stylesheets/calendar.css"]
|
||||||
};
|
};
|
||||||
@ -193,7 +189,6 @@ export default {
|
|||||||
CODE_MIRROR,
|
CODE_MIRROR,
|
||||||
ESLINT,
|
ESLINT,
|
||||||
RELATION_MAP,
|
RELATION_MAP,
|
||||||
PRINT_THIS,
|
|
||||||
CALENDAR_WIDGET,
|
CALENDAR_WIDGET,
|
||||||
KATEX,
|
KATEX,
|
||||||
WHEEL_ZOOM,
|
WHEEL_ZOOM,
|
||||||
|
@ -5,8 +5,15 @@ import dialogService from "../../services/dialog.js";
|
|||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import ws from "../../services/ws.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 { 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 = `
|
const TPL = `
|
||||||
<div class="dropdown note-actions">
|
<div class="dropdown note-actions">
|
||||||
@ -52,8 +59,12 @@ const TPL = `
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li data-trigger-command="printActiveNote" class="dropdown-item print-active-note-button">
|
<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>
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
@ -100,17 +111,37 @@ const TPL = `
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
export default class NoteActionsWidget extends NoteContextAwareWidget {
|
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() {
|
isEnabled() {
|
||||||
return this.note?.type !== "launcher";
|
return this.note?.type !== "launcher";
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
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.$convertNoteIntoAttachmentButton = this.$widget.find("[data-trigger-command='convertNoteIntoAttachment']");
|
||||||
this.$findInTextButton = this.$widget.find(".find-in-text-button");
|
this.$findInTextButton = this.$widget.find(".find-in-text-button");
|
||||||
this.$printActiveNoteButton = this.$widget.find(".print-active-note-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.$showSourceButton = this.$widget.find(".show-source-button");
|
||||||
this.$showAttachmentsButton = this.$widget.find(".show-attachments-button");
|
this.$showAttachmentsButton = this.$widget.find(".show-attachments-button");
|
||||||
this.$renderNoteButton = this.$widget.find(".render-note-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 = this.$widget.find(".export-note-button");
|
||||||
this.$exportNoteButton.on("click", () => {
|
this.$exportNoteButton.on("click", () => {
|
||||||
if (this.$exportNoteButton.hasClass("disabled")) {
|
if (this.$exportNoteButton.hasClass("disabled") || !this.noteContext?.notePath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +160,11 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.$importNoteButton = this.$widget.find(".import-files-button");
|
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"));
|
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 = this.$widget.find(".delete-note-button");
|
||||||
this.$deleteNoteButton.on("click", () => {
|
this.$deleteNoteButton.on("click", () => {
|
||||||
if (this.note.noteId === "root") {
|
if (!this.note || this.note.noteId === "root") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +181,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshVisibility(note) {
|
async refreshVisibility(note: FNote) {
|
||||||
const isInOptions = note.noteId.startsWith("_options");
|
const isInOptions = note.noteId.startsWith("_options");
|
||||||
|
|
||||||
this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment());
|
this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment());
|
||||||
@ -156,7 +191,10 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
this.toggleDisabled(this.$showAttachmentsButton, !isInOptions);
|
this.toggleDisabled(this.$showAttachmentsButton, !isInOptions);
|
||||||
this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "geoMap"].includes(note.type));
|
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");
|
this.$renderNoteButton.toggle(note.type === "render");
|
||||||
|
|
||||||
@ -177,11 +215,11 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async convertNoteIntoAttachmentCommand() {
|
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;
|
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) {
|
if (!newAttachment) {
|
||||||
toastService.showMessage(t("note_actions.convert_into_attachment_failed", { title: this.note.title }));
|
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) {
|
if (enable) {
|
||||||
$el.removeAttr("disabled");
|
$el.removeAttr("disabled");
|
||||||
} else {
|
} else {
|
||||||
@ -206,7 +244,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }) {
|
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
if (loadResults.isNoteReloaded(this.noteId)) {
|
if (loadResults.isNoteReloaded(this.noteId)) {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
@ -32,6 +32,7 @@ import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js";
|
|||||||
import MindMapWidget from "./type_widgets/mind_map.js";
|
import MindMapWidget from "./type_widgets/mind_map.js";
|
||||||
import { getStylesheetUrl, isSyntaxHighlightEnabled } from "../services/syntax_highlight.js";
|
import { getStylesheetUrl, isSyntaxHighlightEnabled } from "../services/syntax_highlight.js";
|
||||||
import GeoMapTypeWidget from "./type_widgets/geo_map.js";
|
import GeoMapTypeWidget from "./type_widgets/geo_map.js";
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-detail">
|
<div class="note-detail">
|
||||||
@ -249,45 +250,18 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await libraryLoader.requireLibrary(libraryLoader.PRINT_THIS);
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
let $promotedAttributes = $("");
|
async exportAsPdfEvent() {
|
||||||
|
if (!this.noteContext.isActive()) {
|
||||||
if (this.note.getPromotedDefinitionAttributes().length > 0) {
|
return;
|
||||||
$promotedAttributes = (await attributeRenderer.renderNormalAttributes(this.note)).$renderedAttributes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { assetPath } = window.glob;
|
const { ipcRenderer } = utils.dynamicRequire("electron");
|
||||||
const cssToLoad = [
|
ipcRenderer.send("export-as-pdf", {
|
||||||
`${assetPath}/node_modules/codemirror/lib/codemirror.css`,
|
title: this.note.title,
|
||||||
`${assetPath}/libraries/ckeditor/ckeditor-content.css`,
|
landscape: this.note.hasAttribute("label", "printLandscape")
|
||||||
`${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
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,40 +1,162 @@
|
|||||||
@media print {
|
:root {
|
||||||
html body {
|
--main-background-color: white;
|
||||||
/* https://github.com/zadam/trilium/issues/3202 */
|
--root-background: var(--main-background-color);
|
||||||
color: black;
|
--launcher-pane-background-color: var(--main-background-color);
|
||||||
}
|
--main-text-color: black;
|
||||||
|
--input-text-color: var(--main-text-color);
|
||||||
.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%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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);
|
border-color: var(--hover-item-border-color);
|
||||||
background: var(--hover-item-background-color);
|
background: var(--hover-item-background-color);
|
||||||
color: var(--hover-item-text-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;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck-content p code {
|
@media (screen) {
|
||||||
border: 1px solid var(--card-border-color);
|
.ck-content p code {
|
||||||
box-shadow: var(--card-box-shadow);
|
border: 1px solid var(--card-border-color);
|
||||||
border-radius: 6px;
|
box-shadow: var(--card-box-shadow);
|
||||||
background-color: var(--card-background-color);
|
border-radius: 6px;
|
||||||
|
background-color: var(--card-background-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-printable:not(.word-wrap) pre code {
|
.note-detail-printable:not(.word-wrap) pre code {
|
||||||
|
@ -672,7 +672,8 @@
|
|||||||
"save_revision": "Save revision",
|
"save_revision": "Save revision",
|
||||||
"convert_into_attachment_failed": "Converting note '{{title}}' failed.",
|
"convert_into_attachment_failed": "Converting note '{{title}}' failed.",
|
||||||
"convert_into_attachment_successful": "Note '{{title}}' has been converted to attachment.",
|
"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": {
|
"onclick_button": {
|
||||||
"no_click_handler": "Button widget '{{componentId}}' has no defined click handler"
|
"no_click_handler": "Button widget '{{componentId}}' has no defined click handler"
|
||||||
|
@ -824,7 +824,8 @@
|
|||||||
"search_in_note": "Caută în notiță",
|
"search_in_note": "Caută în notiță",
|
||||||
"convert_into_attachment_failed": "Nu s-a putut converti notița „{{title}}”.",
|
"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_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": {
|
"note_erasure_timeout": {
|
||||||
"deleted_notes_erased": "Notițele șterse au fost eliminate permanent.",
|
"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/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/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/")));
|
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"),
|
description: t("keyboard_actions.print-active-note"),
|
||||||
scope: "window"
|
scope: "window"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
actionName: "exportAsPdf",
|
||||||
|
defaultShortcuts: [],
|
||||||
|
description: t("keyboard_actions.export-as-pdf"),
|
||||||
|
scope: "window"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
actionName: "openNoteExternally",
|
actionName: "openNoteExternally",
|
||||||
defaultShortcuts: [],
|
defaultShortcuts: [],
|
||||||
|
@ -75,6 +75,7 @@ const enum KeyboardActionNamesEnum {
|
|||||||
toggleRibbonTabSimilarNotes,
|
toggleRibbonTabSimilarNotes,
|
||||||
toggleRightPane,
|
toggleRightPane,
|
||||||
printActiveNote,
|
printActiveNote,
|
||||||
|
exportAsPdf,
|
||||||
openNoteExternally,
|
openNoteExternally,
|
||||||
renderActiveNote,
|
renderActiveNote,
|
||||||
runActiveNote,
|
runActiveNote,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import url from "url";
|
import url from "url";
|
||||||
import port from "./port.js";
|
import port from "./port.js";
|
||||||
@ -7,12 +8,13 @@ import sqlInit from "./sql_init.js";
|
|||||||
import cls from "./cls.js";
|
import cls from "./cls.js";
|
||||||
import keyboardActionsService from "./keyboard_actions.js";
|
import keyboardActionsService from "./keyboard_actions.js";
|
||||||
import remoteMain from "@electron/remote/main/index.js";
|
import remoteMain from "@electron/remote/main/index.js";
|
||||||
import type { App, BrowserWindow, BrowserWindowConstructorOptions, WebContents } from "electron";
|
import { BrowserWindow, shell, type App, type BrowserWindowConstructorOptions, type WebContents } from "electron";
|
||||||
import { ipcMain } from "electron";
|
import { dialog, ipcMain } from "electron";
|
||||||
import { isDev, isMac, isWindows } from "./utils.js";
|
import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js";
|
||||||
|
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
// Prevent the window being garbage collected
|
// Prevent the window being garbage collected
|
||||||
let mainWindow: BrowserWindow | null;
|
let mainWindow: BrowserWindow | null;
|
||||||
@ -46,6 +48,50 @@ ipcMain.on("create-extra-window", (event, arg) => {
|
|||||||
createExtraWindow(arg.extraWindowHash);
|
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) {
|
async function createMainWindow(app: App) {
|
||||||
if ("setUserTasks" in app) {
|
if ("setUserTasks" in app) {
|
||||||
app.setUserTasks([
|
app.setUserTasks([
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<link href="<%= assetPath %>/stylesheets/style.css" rel="stylesheet">
|
<link href="<%= assetPath %>/stylesheets/style.css" rel="stylesheet">
|
||||||
|
<link href="<%= assetPath %>/stylesheets/print.css" rel="stylesheet" media="print">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$("body").show();
|
$("body").show();
|
||||||
|
@ -129,6 +129,7 @@
|
|||||||
<link href="<%= themeCssUrl %>" rel="stylesheet">
|
<link href="<%= themeCssUrl %>" rel="stylesheet">
|
||||||
<% } %>
|
<% } %>
|
||||||
<link href="<%= assetPath %>/stylesheets/style.css" 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">
|
<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",
|
"force-save-revision": "Force creating / saving new note revision of the active note",
|
||||||
"show-help": "Shows built-in Help / cheatsheet",
|
"show-help": "Shows built-in Help / cheatsheet",
|
||||||
"toggle-book-properties": "Toggle Book Properties",
|
"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": {
|
"login": {
|
||||||
"title": "Login",
|
"title": "Login",
|
||||||
@ -253,5 +254,11 @@
|
|||||||
},
|
},
|
||||||
"content_renderer": {
|
"content_renderer": {
|
||||||
"note-cannot-be-displayed": "This note type cannot be displayed."
|
"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",
|
"unhoist": "Defocalizează complet",
|
||||||
"zoom-in": "Mărește zoom-ul",
|
"zoom-in": "Mărește zoom-ul",
|
||||||
"zoom-out": "Micșorează 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": {
|
"login": {
|
||||||
"button": "Autentifică",
|
"button": "Autentifică",
|
||||||
@ -254,5 +255,11 @@
|
|||||||
},
|
},
|
||||||
"content_renderer": {
|
"content_renderer": {
|
||||||
"note-cannot-be-displayed": "Acest tip de notiță nu poate fi afișat."
|
"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