mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 10:02:59 +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/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