A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 180 lines 10 kB view raw
1/// #if !BROWSER 2import {escapeHtml} from "../../util/escape"; 3import * as path from "path"; 4/// #endif 5import {hideMessage, showMessage} from "../../dialog/message"; 6import {fetchPost} from "../../util/fetch"; 7import {Dialog} from "../../dialog"; 8import {addScript} from "../util/addScript"; 9import {isMobile} from "../../util/functions"; 10import {Constants} from "../../constants"; 11import {highlightRender} from "../render/highlightRender"; 12import {processRender} from "../util/processCode"; 13import {isIPhone, isSafari, openByMobile, setStorageVal} from "../util/compatibility"; 14import {useShell} from "../../util/pathName"; 15 16export const afterExport = (exportPath: string, msgId: string) => { 17 /// #if !BROWSER 18 showMessage(`${window.siyuan.languages.exported} ${escapeHtml(exportPath)} 19<div class="fn__space"></div> 20<button class="b3-button b3-button--white">${window.siyuan.languages.showInFolder}</button>`, 6000, "info", msgId); 21 document.querySelector(`#message [data-id="${msgId}"] button`).addEventListener("click", () => { 22 useShell("showItemInFolder", path.join(exportPath)); 23 hideMessage(msgId); 24 }); 25 /// #endif 26}; 27 28export const exportImage = (id: string) => { 29 const exportDialog = new Dialog({ 30 title: window.siyuan.languages.exportAsImage, 31 content: `<div class="b3-dialog__content" style="${isMobile() ? "padding:8px;" : ""};background-color: var(--b3-theme-background)"> 32 <div style="${isMobile() ? "margin: 8px 0" : "padding: 48px;margin: 8px 0"}" class="export-img"> 33 <div ${isMobile() ? 'style="padding:8px"' : ""} class="protyle-wysiwyg${window.siyuan.config.editor.displayBookmarkIcon ? " protyle-wysiwyg--attr" : ""}"></div> 34 <div class="export-img__watermark"></div> 35 </div> 36</div> 37<div class="b3-dialog__action"> 38 <label class="fn__flex"> 39 ${window.siyuan.languages.exportPDF5} 40 <span class="fn__space"></span> 41 <input id="keepFold" class="b3-switch fn__flex-center" type="checkbox" ${window.siyuan.storage[Constants.LOCAL_EXPORTIMG].keepFold ? "checked" : ""}> 42 </label> 43 <label class="fn__flex" style="margin-left: 24px"> 44 ${window.siyuan.languages.export30} 45 <span class="fn__space"></span> 46 <input id="watermark" class="b3-switch fn__flex-center" type="checkbox" ${window.siyuan.storage[Constants.LOCAL_EXPORTIMG].watermark ? "checked" : ""}> 47 </label> 48 <span class="fn__flex-1 export-img__space"></span> 49 <button disabled class="b3-button b3-button--cancel">${window.siyuan.languages.cancel}</button><div class="fn__space"></div> 50 <button disabled class="b3-button b3-button--text">${window.siyuan.languages.confirm}</button> 51</div> 52 <div class="fn__loading"><img height="128px" width="128px" src="stage/loading-pure.svg"></div>`, 53 width: isMobile() ? "92vw" : "990px", 54 height: "70vh" 55 }); 56 exportDialog.element.setAttribute("data-key", Constants.DIALOG_EXPORTIMAGE); 57 const btnsElement = exportDialog.element.querySelectorAll(".b3-button"); 58 btnsElement[0].addEventListener("click", () => { 59 exportDialog.destroy(); 60 }); 61 btnsElement[1].addEventListener("click", async () => { 62 const msgId = showMessage(window.siyuan.languages.exporting, 0); 63 const containerElement = exportDialog.element.querySelector(".b3-dialog__container") as HTMLElement; 64 containerElement.style.height = ""; 65 /// #if MOBILE 66 containerElement.style.width = "100vw"; 67 /// #endif 68 const contentElement = exportDialog.element.querySelector(".b3-dialog__content") as HTMLElement; 69 contentElement.style.overflow = "hidden"; 70 setStorageVal(Constants.LOCAL_EXPORTIMG, window.siyuan.storage[Constants.LOCAL_EXPORTIMG]); 71 const plantumlElements = previewElement.querySelectorAll("[data-subtype='plantuml']"); 72 for (let i = 0; i < plantumlElements.length; i++) { 73 const objectElement = plantumlElements[i].querySelector("object"); 74 if (objectElement) { 75 const res = await fetch(objectElement.getAttribute("data")); 76 const response = await res.text(); 77 objectElement.insertAdjacentHTML("beforebegin", response as string); 78 objectElement.remove(); 79 } 80 } 81 previewElement.querySelectorAll(".protyle-linenumber__rows span").forEach((item, index) => { 82 item.textContent = (index + 1).toString(); 83 }); 84 setTimeout(() => { 85 addScript("/stage/protyle/js/html-to-image.min.js?v=1.11.13", "protyleHtml2image").then(async () => { 86 let blob = await window.htmlToImage.toBlob(exportDialog.element.querySelector(".b3-dialog__content")); 87 if (isIPhone() || isSafari()) { 88 await window.htmlToImage.toBlob(contentElement); 89 await window.htmlToImage.toBlob(contentElement); 90 await window.htmlToImage.toBlob(contentElement); 91 blob = await window.htmlToImage.toBlob(contentElement); 92 } 93 const formData = new FormData(); 94 formData.append("file", blob, btnsElement[1].getAttribute("data-title")); 95 formData.append("type", "image/png"); 96 fetchPost("/api/export/exportAsFile", formData, (response) => { 97 openByMobile(response.data.file); 98 }); 99 hideMessage(msgId); 100 exportDialog.destroy(); 101 }); 102 }, Constants.TIMEOUT_LOAD); 103 }); 104 const previewElement = exportDialog.element.querySelector(".protyle-wysiwyg") as HTMLElement; 105 const foldElement = (exportDialog.element.querySelector("#keepFold") as HTMLInputElement); 106 foldElement.addEventListener("change", () => { 107 btnsElement[0].setAttribute("disabled", "disabled"); 108 btnsElement[1].setAttribute("disabled", "disabled"); 109 btnsElement[1].parentElement.insertAdjacentHTML("afterend", '<div class="fn__loading"><img height="128px" width="128px" src="stage/loading-pure.svg"></div>'); 110 window.siyuan.storage[Constants.LOCAL_EXPORTIMG].keepFold = foldElement.checked; 111 fetchPost("/api/export/exportPreviewHTML", { 112 id, 113 keepFold: foldElement.checked, 114 image: true, 115 }, (response) => { 116 refreshPreview(response); 117 }); 118 }); 119 const watermarkElement = (exportDialog.element.querySelector("#watermark") as HTMLInputElement); 120 watermarkElement.addEventListener("change", () => { 121 window.siyuan.storage[Constants.LOCAL_EXPORTIMG].watermark = watermarkElement.checked; 122 updateWatermark(); 123 }); 124 const updateWatermark = () => { 125 const watermarkPreviewElement = exportDialog.element.querySelector(".export-img__watermark") as HTMLElement; 126 watermarkPreviewElement.innerHTML = ""; 127 if (watermarkElement.checked) { 128 if (window.siyuan.config.export.imageWatermarkDesc) { 129 watermarkPreviewElement.innerHTML = window.siyuan.config.export.imageWatermarkDesc; 130 } else if (window.siyuan.config.export.imageWatermarkStr) { 131 if (window.siyuan.config.export.imageWatermarkStr.startsWith("http")) { 132 watermarkPreviewElement.setAttribute("style", `background-image: url(${window.siyuan.config.export.imageWatermarkStr});background-repeat: repeat;position: absolute;top: 0;left: 0;width: 100%;height: 100%;border-radius: var(--b3-border-radius-b);`); 133 } else { 134 addScript("/stage/protyle/js/html-to-image.min.js?v=1.11.13", "protyleHtml2image").then(() => { 135 const width = Math.max(exportDialog.element.querySelector(".export-img").clientWidth / 3, 150); 136 watermarkPreviewElement.setAttribute("style", `width: ${width}px;height: ${width}px;display: flex;justify-content: center;align-items: center;color: var(--b3-border-color);font-size: 14px;`); 137 watermarkPreviewElement.innerHTML = `<div style="transform: rotate(-45deg)">${window.siyuan.config.export.imageWatermarkStr}</div>`; 138 window.htmlToImage.toCanvas(watermarkPreviewElement).then((canvas) => { 139 watermarkPreviewElement.innerHTML = ""; 140 watermarkPreviewElement.setAttribute("style", `background-image: url(${canvas.toDataURL("image/png")});background-repeat: repeat;position: absolute;top: 0;left: 0;width: 100%;height: 100%;border-radius: var(--b3-border-radius-b);`); 141 }); 142 }); 143 } 144 } 145 } else { 146 watermarkPreviewElement.removeAttribute("style"); 147 } 148 }; 149 const refreshPreview = (response: IWebSocketData) => { 150 previewElement.innerHTML = response.data.content; 151 previewElement.setAttribute("data-doc-type", response.data.type || "NodeDocument"); 152 Object.keys(response.data.attrs).forEach(key => { 153 previewElement.setAttribute(key, response.data.attrs[key]); 154 }); 155 previewElement.querySelectorAll(".code-block").forEach(item => { 156 item.setAttribute("linewrap", "true"); 157 }); 158 processRender(previewElement); 159 highlightRender(previewElement); 160 previewElement.querySelectorAll("table").forEach((item: HTMLElement) => { 161 if (item.clientWidth > item.parentElement.clientWidth) { 162 item.setAttribute("style", `margin-bottom:${item.parentElement.clientWidth * item.clientHeight / item.clientWidth - item.parentElement.clientHeight + 1}px;transform: scale(${item.parentElement.clientWidth / item.clientWidth});transform-origin: top left;`); 163 item.parentElement.style.overflow = "hidden"; 164 } 165 }); 166 167 updateWatermark(); 168 btnsElement[0].removeAttribute("disabled"); 169 btnsElement[1].removeAttribute("disabled"); 170 exportDialog.element.querySelector(".fn__loading").remove(); 171 }; 172 fetchPost("/api/export/exportPreviewHTML", { 173 id, 174 keepFold: foldElement.checked, 175 image: true, 176 }, (response) => { 177 refreshPreview(response); 178 btnsElement[1].setAttribute("data-title", response.data.name + ".png"); 179 }); 180};