mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
45 Commits
imagepathh
...
2.8.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfb5de1525 | ||
|
|
8071a2888b | ||
|
|
8b9abb13d0 | ||
|
|
6dbae61212 | ||
|
|
75c65d61c5 | ||
|
|
37e0de41af | ||
|
|
57f9e43508 | ||
|
|
3b7f931f28 | ||
|
|
b37a7aad4f | ||
|
|
667ab31ed9 | ||
|
|
b15ddef7fe | ||
|
|
ef785e5fb0 | ||
|
|
15ba4146ac | ||
|
|
9956fd1756 | ||
|
|
b6f2161f1c | ||
|
|
22b8b1f707 | ||
|
|
98f6871caa | ||
|
|
aae588249a | ||
|
|
ef890d51e3 | ||
|
|
b0bc03437a | ||
|
|
23b94da8f0 | ||
|
|
1f227ddd24 | ||
|
|
12152665af | ||
|
|
064e17b29d | ||
|
|
0aaba80c82 | ||
|
|
1744668fbd | ||
|
|
8e3e2ffb25 | ||
|
|
27fa270b42 | ||
|
|
15ece75b5d | ||
|
|
a796621f93 | ||
|
|
3c943c6685 | ||
|
|
4209774b4e | ||
|
|
b18637f7d0 | ||
|
|
af8a848d14 | ||
|
|
01e392158d | ||
|
|
fc47b7aa0d | ||
|
|
a0e0627a49 | ||
|
|
efcb0c0580 | ||
|
|
23d7105fb1 | ||
|
|
5d9565bd7c | ||
|
|
59785523ae | ||
|
|
2a21ed5fc7 | ||
|
|
3d3ce73fa1 | ||
|
|
c35bd385fe | ||
|
|
a790b04547 |
@@ -38,7 +38,7 @@ async function getImageSize(src: string): Promise<{ height: number; width: numbe
|
||||
export async function tex2dataURL(
|
||||
tex: string,
|
||||
scale: number = 4,
|
||||
app?: any
|
||||
plugin?: any
|
||||
): Promise<{
|
||||
mimeType: string;
|
||||
fileId: FileId;
|
||||
@@ -50,9 +50,9 @@ export async function tex2dataURL(
|
||||
let output: SVG<unknown, unknown, unknown>;
|
||||
|
||||
if(!adaptor) {
|
||||
if (app) {
|
||||
const file = app.vault.getAbstractFileByPath("preamble.sty");
|
||||
preamble = file ? await app.vault.read(file) : null;
|
||||
if (plugin) {
|
||||
const file = plugin.app.vault.getAbstractFileByPath(plugin.settings.latexPreambleLocation || "preamble.sty");
|
||||
preamble = file ? await plugin.app.vault.read(file) : null;
|
||||
}
|
||||
adaptor = liteAdaptor();
|
||||
RegisterHTMLHandler(adaptor);
|
||||
@@ -69,10 +69,18 @@ export async function tex2dataURL(
|
||||
|
||||
try {
|
||||
const node = html.convert(
|
||||
preamble ? `${preamble}${tex}` : tex,
|
||||
preamble ? `${preamble}\n${tex}` : tex,
|
||||
{ display: true, scale }
|
||||
);
|
||||
const svg = new DOMParser().parseFromString(adaptor.innerHTML(node), "image/svg+xml").firstChild as SVGSVGElement;
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2195
|
||||
//https://stackoverflow.com/a/77181931
|
||||
let styleNode = document.createElement('style');
|
||||
styleNode.setAttribute("type", "text/css");
|
||||
styleNode.appendChild(document.createTextNode(".mjx-solid { stroke-width: 80px; }"));
|
||||
svg.appendChild(styleNode);
|
||||
|
||||
if (svg) {
|
||||
if(svg.width.baseVal.valueInSpecifiedUnits < 2) {
|
||||
svg.width.baseVal.valueAsString = `${(svg.width.baseVal.valueInSpecifiedUnits+1).toFixed(3)}ex`;
|
||||
|
||||
@@ -89,8 +89,8 @@ Plugin settings are grouped into the following sections:
|
||||
- **Basic settings**: such as default folders to use.
|
||||
- **Saving**: compression and autosave timer.
|
||||
- **Filename**: configure the automatically created Excalidraw filename.
|
||||
- **Display**: settings that effect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings).
|
||||
- **Links and transclusions**: Settings that effect how links and embedded items behave on the Excalidraw canvas.
|
||||
- **Display**: settings that affect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings).
|
||||
- **Links and transclusions**: Settings that affect how links and embedded items behave on the Excalidraw canvas.
|
||||
- **Markdown-embed settings**: These settings control how markdown documents from your Vault embedded into Excalidraw drawings will behave.
|
||||
- **Embed & Export**: Settings that control how Excalidraw images are displayed when embedding them into markdown documents.
|
||||
- **Auto-export Settings**: You can configure Excalidraw to create a PNG or SVG copy of your drawing each time it gets saved.
|
||||
|
||||
@@ -21,7 +21,7 @@ The script will convert your drawing into a slideshow presentation.
|
||||
|
||||
```javascript
|
||||
*/
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.1.7")) {
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.8.0")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
@@ -46,6 +46,8 @@ const TRANSITION_DELAY = 1000; //maximum time for transition between slides in m
|
||||
const FRAME_SLEEP = 1; //milliseconds
|
||||
const EDIT_ZOOMOUT = 0.7; //70% of original slide zoom, set to a value between 1 and 0
|
||||
const FADE_LEVEL = 0.1; //opacity of the slideshow controls after fade delay (value between 0 and 1)
|
||||
const PRINT_SLIDE_WIDTH = 1920;
|
||||
const PRINT_SLIDE_HEIGHT = 1080;
|
||||
//using outerHTML because the SVG object returned by Obsidin is in the main workspace window
|
||||
//but excalidraw might be open in a popout window which has a different document object
|
||||
const SVG_COG = ea.obsidian.getIcon("lucide-settings").outerHTML;
|
||||
@@ -57,6 +59,7 @@ const SVG_MAXIMIZE = ea.obsidian.getIcon("lucide-maximize").outerHTML;
|
||||
const SVG_MINIMIZE = ea.obsidian.getIcon("lucide-minimize").outerHTML;
|
||||
const SVG_LASER_ON = ea.obsidian.getIcon("lucide-hand").outerHTML;
|
||||
const SVG_LASER_OFF = ea.obsidian.getIcon("lucide-wand").outerHTML;
|
||||
const SVG_PRINTER = ea.obsidian.getIcon("lucide-printer").outerHTML;
|
||||
|
||||
//-------------------------------
|
||||
//utility & convenience functions
|
||||
@@ -202,7 +205,7 @@ const gotoFullscreen = async () => {
|
||||
}
|
||||
await waitForExcalidrawResize();
|
||||
const layerUIWrapper = contentEl.querySelector(".layer-ui__wrapper");
|
||||
if(!layerUIWrapper.hasClass("excalidraw-hidden")) layerUIWrapper.addClass("excalidraw-hidden");
|
||||
if(!layerUIWrapper?.hasClass("excalidraw-hidden")) layerUIWrapper.addClass("excalidraw-hidden");
|
||||
if(toggleFullscreenButton) toggleFullscreenButton.innerHTML = SVG_MINIMIZE;
|
||||
resetControlPanelElPosition();
|
||||
isFullscreen = true;
|
||||
@@ -271,8 +274,8 @@ if(presentationPathType==="line") {
|
||||
//-----------------------------
|
||||
// scroll-to-location functions
|
||||
//-----------------------------
|
||||
const getNavigationRect = ({ x1, y1, x2, y2 }) => {
|
||||
const { width, height } = excalidrawAPI.getAppState();
|
||||
const getNavigationRect = ({ x1, y1, x2, y2, printDimensions }) => {
|
||||
const { width, height } = printDimensions ? printDimensions : excalidrawAPI.getAppState();
|
||||
const ratioX = width / Math.abs(x1 - x2);
|
||||
const ratioY = height / Math.abs(y1 - y2);
|
||||
let ratio = Math.min(Math.max(ratioX, ratioY), 30);
|
||||
@@ -534,6 +537,20 @@ const createPresentationNavigationPanel = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
if(ea.DEVICE.isDesktop) {
|
||||
el.createEl("button",{
|
||||
attr: {
|
||||
style: `
|
||||
margin-right: calc(var(--default-button-size)*0.25);`,
|
||||
title: `Print to PDF\nClick to print slides at ${PRINT_SLIDE_WIDTH}x${
|
||||
PRINT_SLIDE_HEIGHT}\nHold SHIFT to print the presentation as displayed`
|
||||
//${!presentationPathLineEl ? "\nHold ALT/OPT to clip frames":""}`
|
||||
}
|
||||
}, button => {
|
||||
button.innerHTML = SVG_PRINTER;
|
||||
button.onclick = (e) => printToPDF(e);
|
||||
});
|
||||
}
|
||||
el.createEl("button",{
|
||||
attr: {
|
||||
style: `
|
||||
@@ -542,7 +559,7 @@ const createPresentationNavigationPanel = () => {
|
||||
}
|
||||
}, button => {
|
||||
button.innerHTML = SVG_FINISH;
|
||||
button.onclick = () => exitPresentation()
|
||||
button.onclick = () => exitPresentation();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -741,6 +758,96 @@ const exitPresentation = async (openForEdit = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// Print to PDF
|
||||
//--------------------------
|
||||
let notice;
|
||||
let noticeEl;
|
||||
function setSingleNotice(message) {
|
||||
if(noticeEl?.parentElement) {
|
||||
notice.setMessage(message);
|
||||
return;
|
||||
}
|
||||
notice = new Notice(message, 0);
|
||||
noticeEl = notice.containerEl ?? notice.noticeEl;
|
||||
}
|
||||
|
||||
function hideSingleNotice() {
|
||||
if(noticeEl?.parentElement) {
|
||||
notice.hide();
|
||||
}
|
||||
}
|
||||
|
||||
const translateToZero = ({ top, left, bottom, right }, padding) => {
|
||||
const {topX, topY, width, height} = ea.getBoundingBox(ea.getViewElements());
|
||||
const newTop = top - (topY - padding);
|
||||
const newLeft = left - (topX - padding);
|
||||
const newBottom = bottom - (topY - padding);
|
||||
const newRight = right - (topX - padding);
|
||||
|
||||
return {
|
||||
top: newTop,
|
||||
left: newLeft,
|
||||
bottom: newBottom,
|
||||
right: newRight,
|
||||
};
|
||||
}
|
||||
|
||||
const printToPDF = async (e) => {
|
||||
const slideWidth = e.shiftKey ? excalidrawAPI.getAppState().width : PRINT_SLIDE_WIDTH;
|
||||
const slideHeight = e.shiftKey ? excalidrawAPI.getAppState().height : PRINT_SLIDE_HEIGHT;
|
||||
//const shouldClipFrames = !presentationPathLineEl && e.altKey;
|
||||
const shouldClipFrames = false;
|
||||
//huge padding to ensure the HD window always fits the width
|
||||
//no padding if frames are clipped
|
||||
const padding = shouldClipFrames ? 0 : Math.round(Math.max(slideWidth,slideHeight)/2)+10;
|
||||
const st = ea.getExcalidrawAPI().getAppState();
|
||||
setSingleNotice("Generating image. This can take a longer time depending on the size of the image and speed of your device");
|
||||
const svg = await ea.createViewSVG({
|
||||
withBackground: true,
|
||||
theme: st.theme,
|
||||
frameRendering: { enabled: shouldClipFrames, name: false, outline: false, clip: shouldClipFrames },
|
||||
padding,
|
||||
selectedOnly: false,
|
||||
skipInliningFonts: false,
|
||||
embedScene: false,
|
||||
});
|
||||
const pages = [];
|
||||
for(i=0;i<slides.length;i++) {
|
||||
setSingleNotice(`Generating slide ${i+1}`);
|
||||
const s = slides[i];
|
||||
const { top, left, bottom, right } = translateToZero(
|
||||
getNavigationRect({
|
||||
...s,
|
||||
printDimensions: {width: slideWidth, height: slideHeight}
|
||||
}), padding
|
||||
);
|
||||
//always create the new SVG in the main Obsidian workspace (not the popout window, if present)
|
||||
const host = window.createDiv();
|
||||
host.innerHTML = svg.outerHTML;
|
||||
const clonedSVG = host.firstElementChild;
|
||||
const width = Math.abs(left-right);
|
||||
const height = Math.abs(top-bottom);
|
||||
clonedSVG.setAttribute("viewBox", `${left} ${top} ${width} ${height}`);
|
||||
clonedSVG.setAttribute("width", `${width}`);
|
||||
clonedSVG.setAttribute("height", `${height}`);
|
||||
pages.push(clonedSVG);
|
||||
}
|
||||
const bgColor = ea.getExcalidrawAPI().getAppState().viewBackgroundColor;
|
||||
setSingleNotice("Creating PDF Document");
|
||||
ea.createPDF({
|
||||
SVG: pages,
|
||||
scale: { fitToPage: true },
|
||||
pageProps: {
|
||||
dimensions: { width: slideWidth, height: slideHeight },
|
||||
backgroundColor: bgColor,
|
||||
margin: { left: 0, right: 0, top: 0, bottom: 0 },
|
||||
alignment: "center"
|
||||
},
|
||||
filename: ea.targetView.file.basename + ".pdf",
|
||||
}).then(()=>hideSingleNotice());
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// Start presentation or open presentation settings on double click
|
||||
//--------------------------
|
||||
@@ -768,7 +875,11 @@ const start = async () => {
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
if(window.ExcalidrawSlideshow && (window.ExcalidrawSlideshow.script === utils.scriptFile.path) && (timestamp - window.ExcalidrawSlideshow.timestamp <400) ) {
|
||||
if(
|
||||
window.ExcalidrawSlideshow &&
|
||||
(window.ExcalidrawSlideshow.script === utils.scriptFile.path) &&
|
||||
(timestamp - window.ExcalidrawSlideshow.timestamp <400)
|
||||
) {
|
||||
if(window.ExcalidrawSlideshowStartTimer) {
|
||||
window.clearTimeout(window.ExcalidrawSlideshowStartTimer);
|
||||
delete window.ExcalidrawSlideshowStartTimer;
|
||||
@@ -789,4 +900,4 @@ if(window.ExcalidrawSlideshow && (window.ExcalidrawSlideshow.script === utils.sc
|
||||
window.ExcalidrawSlideshow.slide[ea.targetView.file.path] = 0;
|
||||
|
||||
window.ExcalidrawSlideshowStartTimer = window.setTimeout(start,500);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.7.6-beta-1",
|
||||
"version": "2.8.3",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.7.5",
|
||||
"version": "2.8.3",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
204
package-lock.json
generated
204
package-lock.json
generated
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
"@zsviczian/excalidraw": "0.17.6-26",
|
||||
"@zsviczian/excalidraw": "0.17.6-30",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"es6-promise-pool": "2.5.0",
|
||||
@@ -41,6 +41,7 @@
|
||||
"@excalidraw/prettier-config": "^1.0.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
@@ -77,7 +78,7 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
@@ -90,7 +91,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
|
||||
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.24.7",
|
||||
"picocolors": "^1.0.0"
|
||||
@@ -103,7 +104,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.8.tgz",
|
||||
"integrity": "sha512-c4IM7OTg6k1Q+AJ153e2mc2QVTezTwnb4VzquwcyiEzGnW0Kedv4do/TrkU98qPeC5LNiMt/QXwIjzYXLBpyZg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -112,7 +113,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.8.tgz",
|
||||
"integrity": "sha512-6AWcmZC/MZCO0yKys4uhg5NlxL0ESF3K6IAaoQ+xSXvPyPyxNWRafP+GDbI88Oh68O7QkJgmEtedWPM9U0pZNg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
@@ -142,7 +143,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.8.tgz",
|
||||
"integrity": "sha512-47DG+6F5SzOi0uEvK4wMShmn5yY0mVjVJoWTphdY2B4Rx9wHgjK7Yhtr0ru6nE+sn0v38mzrWOlah0p/YlHHOQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.8",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
@@ -157,7 +158,7 @@
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
@@ -195,7 +196,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz",
|
||||
"integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.24.8",
|
||||
"@babel/helper-validator-option": "^7.24.8",
|
||||
@@ -267,7 +268,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
|
||||
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
@@ -279,7 +280,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
|
||||
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
@@ -292,7 +293,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
|
||||
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
@@ -317,7 +318,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
|
||||
"integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
@@ -330,7 +331,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.8.tgz",
|
||||
"integrity": "sha512-m4vWKVqvkVAWLXfHCCfff2luJj86U+J0/x+0N3ArG/tP0Fq7zky2dYwMbtPmkc/oulkkbjdL3uWzuoBwQ8R00Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.24.7",
|
||||
"@babel/helper-module-imports": "^7.24.7",
|
||||
@@ -404,7 +405,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
|
||||
"integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
@@ -430,7 +431,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
|
||||
"integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
@@ -442,7 +443,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
|
||||
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -451,7 +452,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
|
||||
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -460,7 +461,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
|
||||
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -484,7 +485,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz",
|
||||
"integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/types": "^7.24.8"
|
||||
@@ -497,7 +498,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
|
||||
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.24.7",
|
||||
"chalk": "^2.4.2",
|
||||
@@ -512,7 +513,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz",
|
||||
"integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
@@ -1818,9 +1819,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
|
||||
"integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -1832,7 +1833,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
|
||||
"integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/parser": "^7.24.7",
|
||||
@@ -1846,7 +1847,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz",
|
||||
"integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/generator": "^7.24.8",
|
||||
@@ -1867,7 +1868,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.8.tgz",
|
||||
"integrity": "sha512-SkSBEHwwJRU52QEVZBmMBnE5Ux2/6WU1grdYyOhpbCNxbmJrDuDCphBzKZSO3taf0zztp+qkWlymE5tVL5l0TA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.24.8",
|
||||
"@babel/helper-validator-identifier": "^7.24.7",
|
||||
@@ -2233,7 +2234,7 @@
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
@@ -2247,7 +2248,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@@ -2256,7 +2257,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@@ -2275,13 +2276,13 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
@@ -2775,6 +2776,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-json": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
|
||||
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
|
||||
@@ -3315,9 +3336,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zsviczian/excalidraw": {
|
||||
"version": "0.17.6-26",
|
||||
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-26.tgz",
|
||||
"integrity": "sha512-UAqr7b7cxIbOvK1u0NKqgAs0wB9KYUsVc6Q2J+yviM4ae+wkTXp8qW/V4mdWADJ3lTG5RgXCb9ausIKfSkPNRg==",
|
||||
"version": "0.17.6-30",
|
||||
"resolved": "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.17.6-30.tgz",
|
||||
"integrity": "sha512-yNbq3kj4Ya+m0Y9bJ08/YNmI49vxO4nxq1SS4uhD/Q3hny1ENR+S9+V+BQai20GRH67msB5RWApdwj60yN5IYQ==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@excalidraw/random-username": "1.1.0",
|
||||
@@ -3333,7 +3354,8 @@
|
||||
"fractional-indexing": "3.2.0",
|
||||
"fuzzy": "0.1.3",
|
||||
"image-blob-reduce": "3.0.1",
|
||||
"jotai": "1.13.1",
|
||||
"jotai": "2.11.0",
|
||||
"jotai-scope": "0.7.2",
|
||||
"lodash.throttle": "4.1.1",
|
||||
"nanoid": "3.3.3",
|
||||
"open-color": "1.9.1",
|
||||
@@ -3425,9 +3447,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -3489,7 +3511,7 @@
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
@@ -3632,7 +3654,7 @@
|
||||
"version": "4.23.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz",
|
||||
"integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -3704,7 +3726,7 @@
|
||||
"version": "1.0.30001641",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001641.tgz",
|
||||
"integrity": "sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -3729,7 +3751,7 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
@@ -3800,7 +3822,7 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@@ -3809,7 +3831,7 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/colord": {
|
||||
"version": "2.9.3",
|
||||
@@ -3847,7 +3869,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.37.1",
|
||||
@@ -4776,7 +4798,7 @@
|
||||
"version": "1.4.827",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz",
|
||||
"integrity": "sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/elkjs": {
|
||||
"version": "0.9.3",
|
||||
@@ -4816,7 +4838,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -4825,7 +4847,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
@@ -5375,7 +5397,7 @@
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -5431,7 +5453,7 @@
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -5484,7 +5506,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -5764,62 +5786,34 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-1.13.1.tgz",
|
||||
"integrity": "sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==",
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz",
|
||||
"integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "*",
|
||||
"@babel/template": "*",
|
||||
"jotai-devtools": "*",
|
||||
"jotai-immer": "*",
|
||||
"jotai-optics": "*",
|
||||
"jotai-redux": "*",
|
||||
"jotai-tanstack-query": "*",
|
||||
"jotai-urql": "*",
|
||||
"jotai-valtio": "*",
|
||||
"jotai-xstate": "*",
|
||||
"jotai-zustand": "*",
|
||||
"react": ">=16.8"
|
||||
"@types/react": ">=17.0.0",
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@babel/template": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-devtools": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-immer": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-optics": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-redux": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-tanstack-query": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-urql": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-valtio": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-xstate": {
|
||||
"optional": true
|
||||
},
|
||||
"jotai-zustand": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/jotai-scope": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/jotai-scope/-/jotai-scope-0.7.2.tgz",
|
||||
"integrity": "sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==",
|
||||
"peerDependencies": {
|
||||
"jotai": ">=2.9.2",
|
||||
"react": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -5874,7 +5868,7 @@
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
@@ -6043,7 +6037,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
@@ -6749,7 +6743,7 @@
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/non-layered-tidy-tree-layout": {
|
||||
"version": "2.0.2",
|
||||
@@ -7005,7 +6999,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
|
||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -8271,7 +8265,7 @@
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -8525,7 +8519,7 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
@@ -8636,7 +8630,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -8873,7 +8867,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
||||
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -9234,7 +9228,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
|
||||
@@ -16,15 +16,14 @@
|
||||
"build:mathjax": "cd MathjaxToSVG && npm run build",
|
||||
"build:all": "npm run build:mathjax && npm run build",
|
||||
"dev:mathjax": "cd MathjaxToSVG && npm run dev",
|
||||
"dev:all": "npm run dev:mathjax && npm run dev",
|
||||
"build:lang": "node ./scripts/compressLanguages.js"
|
||||
"dev:all": "npm run dev:mathjax && npm run dev"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/excalidraw": "0.17.6-26",
|
||||
"@zsviczian/excalidraw": "0.17.6-32",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
@@ -59,6 +58,7 @@
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@types/chroma-js": "^2.4.0",
|
||||
"@types/js-beautify": "^1.14.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
|
||||
@@ -11,6 +11,7 @@ import postprocess from '@zsviczian/rollup-plugin-postprocess';
|
||||
import cssnano from 'cssnano';
|
||||
import jsesc from 'jsesc';
|
||||
import { minify } from 'uglify-js';
|
||||
import json from '@rollup/plugin-json';
|
||||
|
||||
// Load environment variables
|
||||
import dotenv from 'dotenv';
|
||||
@@ -130,6 +131,7 @@ const BASE_CONFIG = {
|
||||
|
||||
const getRollupPlugins = (tsconfig, ...plugins) => [
|
||||
typescript2(tsconfig),
|
||||
json(),
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
||||
@@ -145,6 +147,7 @@ const BUILD_CONFIG = {
|
||||
entryFileNames: 'main.js',
|
||||
format: 'cjs',
|
||||
exports: 'default',
|
||||
inlineDynamicImports: true, // Add this line only
|
||||
},
|
||||
plugins: getRollupPlugins(
|
||||
{
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
* drawingFilePath: string; // The full filepath of the Excalidraw file where the image is being used.
|
||||
* }) => string = null;
|
||||
*/
|
||||
//ea.onImageFileNameHook = (data) => {};
|
||||
//ea.onImageFilePathHook = (data) => {};
|
||||
|
||||
/**
|
||||
* If set, this callback is triggered whenever the active canvas color changes
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ERROR_IFRAME_CONVERSION_CANCELED = "iframe conversion canceled";
|
||||
|
||||
declare const excalidrawLib: typeof ExcalidrawLib;
|
||||
|
||||
export const LOCALE = moment.locale();
|
||||
export const LOCALE = localStorage.getItem("language")?.toLowerCase() || "en";
|
||||
export const CJK_FONTS = "CJK Fonts";
|
||||
|
||||
export const obsidianToExcalidrawMap: { [key: string]: string } = {
|
||||
@@ -446,4 +446,4 @@ export const SCRIPTENGINE_ICON = `<g transform="translate(-8,-8)"><path d="M24.3
|
||||
export const DISK_ICON_NAME = "save";
|
||||
export const EXPORT_IMG_ICON = ` <g transform="scale(4.166)" strokeWidth="1.25" fill="none" stroke="currentColor"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M15 8h.01"></path><path d="M12 20h-5a3 3 0 0 1 -3 -3v-10a3 3 0 0 1 3 -3h10a3 3 0 0 1 3 3v5"></path><path d="M4 15l4 -4c.928 -.893 2.072 -.893 3 0l4 4"></path><path d="M14 14l1 -1c.617 -.593 1.328 -.793 2.009 -.598"></path><path d="M19 16v6"></path><path d="M22 19l-3 3l-3 -3"></path></g>`;
|
||||
export const EXPORT_IMG_ICON_NAME = `export-img`;
|
||||
export const EXCALIDRAW_ICON = `<path d="M24 17h121v121H24z" style="fill:none" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)"/><path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01Zm-66.93-65.3c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02Zm78.54-1.18c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:currentColor;fill-rule:nonzero" transform="translate(-26.41 -29.49)"/>`;
|
||||
export const EXCALIDRAW_ICON = `<path d="M24 17h121v121H24z" style="fill:none" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)"/><path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01Zm-66.93-65.3c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02Zm78.54-1.18c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:currentColor;fill-rule:nonzero" transform="translate(-26.41 -29.49)"/>`;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -166,6 +166,7 @@ export class CommandManager {
|
||||
const fileMenuHandlerCreateNew = (menu: Menu, file: TFile) => {
|
||||
menu.addItem((item: MenuItem) => {
|
||||
item
|
||||
.setSection('action-primary')
|
||||
.setTitle(t("CREATE_NEW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.onClick((e) => {createNewAction(e, file)});
|
||||
@@ -756,7 +757,6 @@ export class CommandManager {
|
||||
if (view) {
|
||||
if(!view.exportDialog) {
|
||||
view.exportDialog = new ExportDialog(this.plugin, view,view.file);
|
||||
view.exportDialog.createForm();
|
||||
}
|
||||
view.exportDialog.open();
|
||||
return true;
|
||||
|
||||
@@ -801,15 +801,16 @@ const tmpObsidianWYSIWYG = async (
|
||||
if(Boolean(ctx.frontmatter)) {
|
||||
el.empty();
|
||||
} else {
|
||||
const warningEl = el.querySelector("div>h3[data-heading^='Unable to find section #^");
|
||||
//Obsidian changed this at some point from h3 to h5 and also the text...
|
||||
const warningEl = el.querySelector("div>*[data-heading^='Unable to find ");
|
||||
if(warningEl) {
|
||||
const ref = warningEl.getAttr("data-heading").match(/Unable to find section (#\^(?:group=|area=|frame=|clippedframe=)[^ ]*)/)?.[1];
|
||||
const dataHeading = warningEl.getAttr("data-heading");
|
||||
const ref = warningEl.getAttr("data-heading").match(/Unable to find[^^]+(\^(?:group=|area=|frame=|clippedframe=)[^ ”]+)/)?.[1];
|
||||
if(ref) {
|
||||
attr.fname = file.path + ref;
|
||||
attr.fname = file.path + "#" +ref;
|
||||
areaPreview = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(!isFrontmatterDiv && !areaPreview) {
|
||||
if(el.parentElement === containerEl) containerEl.removeChild(el);
|
||||
|
||||
@@ -42,6 +42,7 @@ import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/
|
||||
import { HotkeyEditor } from "src/shared/Dialogs/HotkeyEditor";
|
||||
import { getExcalidrawViews } from "src/utils/obsidianUtils";
|
||||
import { createSliderWithText } from "src/utils/sliderUtils";
|
||||
import { PDFExportSettingsComponent, PDFExportSettings } from "src/shared/Dialogs/PDFExportSettingsComponent";
|
||||
|
||||
export interface ExcalidrawSettings {
|
||||
folder: string;
|
||||
@@ -172,6 +173,7 @@ export interface ExcalidrawSettings {
|
||||
showNewVersionNotification: boolean;
|
||||
//mathjaxSourceURL: string;
|
||||
latexBoilerplate: string;
|
||||
latexPreambleLocation: string;
|
||||
taskboneEnabled: boolean;
|
||||
taskboneAPIkey: string;
|
||||
pinnedScripts: string[];
|
||||
@@ -218,6 +220,7 @@ export interface ExcalidrawSettings {
|
||||
rank: Rank;
|
||||
modifierKeyOverrides: {modifiers: Modifier[], key: string}[];
|
||||
showSplashscreen: boolean;
|
||||
pdfSettings: PDFExportSettings;
|
||||
}
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
@@ -346,6 +349,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
showNewVersionNotification: true,
|
||||
//mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js",
|
||||
latexBoilerplate: "\\color{blue}",
|
||||
latexPreambleLocation: "preamble.sty",
|
||||
taskboneEnabled: false,
|
||||
taskboneAPIkey: "",
|
||||
pinnedScripts: [],
|
||||
@@ -497,6 +501,15 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
{modifiers: ["Mod"], key:"G"},
|
||||
],
|
||||
showSplashscreen: true,
|
||||
pdfSettings: {
|
||||
pageSize: "A4",
|
||||
pageOrientation: "portrait",
|
||||
fitToPage: 1,
|
||||
paperColor: "white",
|
||||
customPaperColor: "#ffffff",
|
||||
alignment: "center",
|
||||
margin: "normal",
|
||||
},
|
||||
};
|
||||
|
||||
export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
@@ -2118,6 +2131,20 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
detailsEl = exportDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: t("PDF_EXPORT_SETTINGS"),
|
||||
cls: "excalidraw-setting-h4",
|
||||
});
|
||||
|
||||
new PDFExportSettingsComponent(
|
||||
detailsEl,
|
||||
this.plugin.settings.pdfSettings,
|
||||
() => {
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
).render();
|
||||
|
||||
detailsEl = exportDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: t("EXPORT_HEAD"),
|
||||
@@ -2262,9 +2289,6 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
el.innerHTML = t("MD_EMBED_CUSTOMDATA_HEAD_DESC");
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
new EmbeddalbeMDFileCustomDataSettingsComponent(
|
||||
detailsEl,
|
||||
this.plugin.settings.embeddableMarkdownDefaults,
|
||||
@@ -2486,6 +2510,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
this.requestReloadDrawings = true;
|
||||
this.plugin.settings.experimentalEnableFourthFont = value;
|
||||
this.applySettingsUpdate();
|
||||
if(value) {
|
||||
this.plugin.initializeFonts();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -2591,6 +2618,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LATEX_PREAMBLE_NAME"))
|
||||
.setDesc(fragWithHTML(t("LATEX_PREAMBLE_DESC")))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("e.g.: preamble.sty")
|
||||
.setValue(this.plugin.settings.latexPreambleLocation)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.latexPreambleLocation = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("FILETYPE_NAME"))
|
||||
.setDesc(fragWithHTML(t("FILETYPE_DESC")))
|
||||
|
||||
@@ -9,6 +9,7 @@ declare var LZString: any;
|
||||
let locale: Partial<typeof en> | null = null;
|
||||
|
||||
function loadLocale(lang: string): Partial<typeof en> {
|
||||
if(lang === "zh") lang = "zh-cn"; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2247
|
||||
if (Object.keys(PLUGIN_LANGUAGES).includes(lang)) {
|
||||
const decompressed = LZString.decompressFromBase64(PLUGIN_LANGUAGES[lang]);
|
||||
let x = {};
|
||||
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
DUPLICATE_IMAGE: "Duplicate selected image with a different image ID",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "Convert markdown note to Excalidraw Drawing",
|
||||
CONVERT_EXCALIDRAW: "Convert *.excalidraw to *.md files",
|
||||
CREATE_NEW: "Create new drawing",
|
||||
CREATE_NEW: "New drawing",
|
||||
CONVERT_FILE_KEEP_EXT: "*.excalidraw => *.excalidraw.md",
|
||||
CONVERT_FILE_REPLACE_EXT: "*.excalidraw => *.md (Logseq compatibility)",
|
||||
DOWNLOAD_LIBRARY: "Export stencil library as an *.excalidrawlib file",
|
||||
@@ -386,13 +386,14 @@ FILENAME_HEAD: "Filename",
|
||||
"This setting will not affect the display of the drawing when you are in Excalidraw mode or when you embed the drawing into a markdown document or when rendering hover preview.<br><ul>" +
|
||||
"<li>See other related setting for <a href='#"+TAG_PDFEXPORT+"'>PDF Export</a> under 'Embedding and Exporting' further below.</li></ul><br>" +
|
||||
"You must close the active excalidraw/markdown file and reopen it for this change to take effect.",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "Render the file as an image when exporting an Excalidraw file to PDF",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "Render Excalidraw as Image in Obsidian PDF Export",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_DESC:
|
||||
"This setting controls the behavior of Excalidraw when exporting an Excalidraw file to PDF in markdown view mode using Obsidian's <b>Export to PDF</b> feature.<br>" +
|
||||
"<ul><li>When <b>enabled</b> the PDF will show the Excalidraw drawing only;</li>" +
|
||||
"<li>When <b>disabled</b> the PDF will show the markdown side of the document.</li></ul>" +
|
||||
"See the other related setting for <a href='#"+TAG_MDREADINGMODE+"'>Markdown Reading Mode</a> under 'Appearnace and Behavior' further above.<br>" +
|
||||
"⚠️ Note, you must close the active excalidraw/markdown file and reopen for this change to take effect. ⚠️",
|
||||
"This setting controls how Excalidraw files are exported to PDF using Obsidian's built-in <b>Export to PDF</b> feature.<br>" +
|
||||
"<ul><li><b>Enabled:</b> The PDF will include the Excalidraw drawing as an image.</li>" +
|
||||
"<li><b>Disabled:</b> The PDF will include the markdown content as text.</li></ul>" +
|
||||
"Note: This setting does not affect the PDF export feature within Excalidraw itself.<br>" +
|
||||
"See the other related setting for <a href='#"+TAG_MDREADINGMODE+"'>Markdown Reading Mode</a> under 'Appearance and Behavior' further above.<br>" +
|
||||
"⚠️ You must close and reopen the Excalidraw/markdown file for changes to take effect. ⚠️",
|
||||
HOTKEY_OVERRIDE_HEAD: "Hotkey overrides",
|
||||
HOTKEY_OVERRIDE_DESC: `Some of the Excalidraw hotkeys such as <code>${labelCTRL()}+Enter</code> to edit text or <code>${labelCTRL()}+K</code> to create an element link ` +
|
||||
"conflict with Obsidian hotkey settings. The hotkey combinations you add below will override Obsidian's hotkey settings while useing Excalidraw, thus " +
|
||||
@@ -661,6 +662,7 @@ FILENAME_HEAD: "Filename",
|
||||
EXPORT_EMBED_SCENE_DESC:
|
||||
"Embed Excalidraw scene in exported image. Can be overridden at a file level by adding the <code>excalidraw-export-embed-scene: true/false<code> frontmatter key. " +
|
||||
"The setting only takes effect the next time you (re)open drawings.",
|
||||
PDF_EXPORT_SETTINGS: "PDF Export Settings",
|
||||
EXPORT_HEAD: "Auto-export Settings",
|
||||
EXPORT_SYNC_NAME:
|
||||
"Keep the .SVG and/or .PNG filenames in sync with the drawing file",
|
||||
@@ -720,6 +722,8 @@ FILENAME_HEAD: "Filename",
|
||||
"restart Obsidian after closing settings, for this change to take effect.",
|
||||
LATEX_DEFAULT_NAME: "Default LaTeX formula for new equations",
|
||||
LATEX_DEFAULT_DESC: "Leave empty if you don't want a default formula. You can add default formatting here such as <code>\\color{white}</code>.",
|
||||
LATEX_PREAMBLE_NAME: "LaTeX preamble file (CasE SEnSiTivE!)",
|
||||
LATEX_PREAMBLE_DESC: "Full filepath to the preamble file, leave empty for default. If the file doesn't exist this option will be ignored.<br><strong>Important:</strong> Requires obsidian reload after change to take effect!",
|
||||
NONSTANDARD_HEAD: "Non-Excalidraw.com supported features",
|
||||
NONSTANDARD_DESC: `These settings in the "Non-Excalidraw.com Supported Features" section provide customization options beyond the default Excalidraw.com features. These features are not available on excalidraw.com. When exporting the drawing to Excalidraw.com these features will appear different.
|
||||
You can configure the number of custom pens displayed next to the Obsidian Menu on the canvas, allowing you to choose from a range of options. Additionally, you can enable a local font option, which adds a local font to the list of fonts on the element properties panel for text elements. `,
|
||||
@@ -1006,4 +1010,89 @@ FILENAME_HEAD: "Filename",
|
||||
LINK_CLICK_POPOUT: "Open in a popout window",
|
||||
LINK_CLICK_NEW_TAB: "Open in a new tab",
|
||||
LINK_CLICK_MD_PROPS: "Show the Markdown image-properties dialog (only relevant if you have embedded a markdown document as an image)",
|
||||
|
||||
//ExportDialog
|
||||
// Dialog and tabs
|
||||
EXPORTDIALOG_TITLE: "Export Drawing",
|
||||
EXPORTDIALOG_TAB_IMAGE: "Image",
|
||||
EXPORTDIALOG_TAB_PDF: "PDF",
|
||||
// Settings persistence
|
||||
EXPORTDIALOG_SAVE_SETTINGS: "Save image settings to file doc.properties?",
|
||||
EXPORTDIALOG_SAVE_SETTINGS_SAVE: "Save as preset",
|
||||
EXPORTDIALOG_SAVE_SETTINGS_ONETIME: "One-time use",
|
||||
// Image settings
|
||||
EXPORTDIALOG_IMAGE_SETTINGS: "Image",
|
||||
EXPORTDIALOG_IMAGE_DESC: "PNG supports transparency. External files can include Excalidraw scene data.",
|
||||
EXPORTDIALOG_PADDING: "Padding",
|
||||
EXPORTDIALOG_SCALE: "Scale",
|
||||
EXPORTDIALOG_CURRENT_PADDING: "Current padding:",
|
||||
EXPORTDIALOG_SIZE_DESC: "Scale affects output size",
|
||||
EXPORTDIALOG_SCALE_VALUE: "Scale:",
|
||||
EXPORTDIALOG_IMAGE_SIZE: "Size:",
|
||||
// Theme and background
|
||||
EXPORTDIALOG_EXPORT_THEME: "Theme",
|
||||
EXPORTDIALOG_THEME_LIGHT: "Light",
|
||||
EXPORTDIALOG_THEME_DARK: "Dark",
|
||||
EXPORTDIALOG_BACKGROUND: "Background",
|
||||
EXPORTDIALOG_BACKGROUND_TRANSPARENT: "Transparent",
|
||||
EXPORTDIALOG_BACKGROUND_USE_COLOR: "Use scene color",
|
||||
// Selection
|
||||
EXPORTDIALOG_SELECTED_ELEMENTS: "Export",
|
||||
EXPORTDIALOG_SELECTED_ALL: "Entire scene",
|
||||
EXPORTDIALOG_SELECTED_SELECTED: "Selection only",
|
||||
// Export options
|
||||
EXPORTDIALOG_EMBED_SCENE: "Include scene data?",
|
||||
EXPORTDIALOG_EMBED_YES: "Yes",
|
||||
EXPORTDIALOG_EMBED_NO: "No",
|
||||
// PDF settings
|
||||
EXPORTDIALOG_PDF_SETTINGS: "PDF",
|
||||
EXPORTDIALOG_PAGE_SIZE: "Size",
|
||||
EXPORTDIALOG_PAGE_ORIENTATION: "Orientation",
|
||||
EXPORTDIALOG_ORIENTATION_PORTRAIT: "Portrait",
|
||||
EXPORTDIALOG_ORIENTATION_LANDSCAPE: "Landscape",
|
||||
EXPORTDIALOG_PDF_FIT_TO_PAGE: "Page Fitting",
|
||||
EXPORTDIALOG_PDF_FIT_OPTION: "Fit to page",
|
||||
EXPORTDIALOG_PDF_FIT_2_OPTION: "Fit to max 2-pages",
|
||||
EXPORTDIALOG_PDF_FIT_4_OPTION: "Fit to max 4-pages",
|
||||
EXPORTDIALOG_PDF_FIT_6_OPTION: "Fit to max 6-pages",
|
||||
EXPORTDIALOG_PDF_FIT_8_OPTION: "Fit to max 8-pages",
|
||||
EXPORTDIALOG_PDF_FIT_12_OPTION: "Fit to max 12-pages",
|
||||
EXPORTDIALOG_PDF_FIT_16_OPTION: "Fit to max 16-pages",
|
||||
EXPORTDIALOG_PDF_SCALE_OPTION: "Use image scale (may span multiple pages)",
|
||||
EXPORTDIALOG_PDF_PAPER_COLOR: "Paper Color",
|
||||
EXPORTDIALOG_PDF_PAPER_WHITE: "White",
|
||||
EXPORTDIALOG_PDF_PAPER_SCENE: "Use scene color",
|
||||
EXPORTDIALOG_PDF_PAPER_CUSTOM: "Custom color",
|
||||
EXPORTDIALOG_PDF_ALIGNMENT: "Position on Page",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER: "Center",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER_LEFT: "Center Left",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER_RIGHT: "Center Right",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_LEFT: "Top Left",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_CENTER: "Top Center",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT: "Top Right",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_LEFT: "Bottom Left",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_CENTER: "Bottom Center",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_RIGHT: "Bottom Right",
|
||||
EXPORTDIALOG_PDF_MARGIN: "Margin",
|
||||
EXPORTDIALOG_PDF_MARGIN_NONE: "None",
|
||||
EXPORTDIALOG_PDF_MARGIN_TINY: "Small",
|
||||
EXPORTDIALOG_PDF_MARGIN_NORMAL: "Normal",
|
||||
EXPORTDIALOG_SAVE_PDF_SETTINGS: "Save PDF settings",
|
||||
EXPORTDIALOG_SAVE_CONFIRMATION: "PDF config saved to plugin settings as default",
|
||||
// Buttons
|
||||
EXPORTDIALOG_PNGTOFILE : "Export PNG",
|
||||
EXPORTDIALOG_SVGTOFILE : "Export SVG",
|
||||
EXPORTDIALOG_PNGTOVAULT : "PNG to Vault",
|
||||
EXPORTDIALOG_SVGTOVAULT : "SVG to Vault",
|
||||
EXPORTDIALOG_EXCALIDRAW: "Excalidraw",
|
||||
EXPORTDIALOG_PNGTOCLIPBOARD : "PNG to Clipboard",
|
||||
EXPORTDIALOG_SVGTOCLIPBOARD : "SVG to Clipboard",
|
||||
EXPORTDIALOG_PDF: "Export PDF",
|
||||
|
||||
EXPORTDIALOG_PDF_PROGRESS_NOTICE: "Exporting PDF. If this image is large, it may take a while.",
|
||||
EXPORTDIALOG_PDF_PROGRESS_DONE: "Export complete",
|
||||
EXPORTDIALOG_PDF_PROGRESS_ERROR: "Error exporting PDF, check developer console for details",
|
||||
|
||||
//exportUtils.ts
|
||||
PDF_EXPORT_DESKTOP_ONLY: "PDF export is only available on desktop",
|
||||
};
|
||||
|
||||
@@ -386,13 +386,14 @@ FILENAME_HEAD: "文件名",
|
||||
"此设置不会影响您在 Excalidraw 模式下的绘图显示,或者在将绘图嵌入 Markdown 文档时,或在渲染悬停预览时。<br><ul>" +
|
||||
"<li>请参阅下面‘嵌入和导出’部分的 <a href='#"+TAG_PDFEXPORT+"'>PDF 导出</a> 相关设置。</li></ul><br>" +
|
||||
"您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "在将 Excalidraw 文件导出为 PDF 时将文件渲染为图像",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME : "在 Obsidian 中导出为 PDF 格式时将 Excalidraw 渲染为图像" ,
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_DESC:
|
||||
"处于 Markdown 视图模式时,此设置控制 Excalidraw 在使用 Obsidian 的 <b>导出为 PDF</b> 功能时,将 Excalidraw 文件导出为 PDF 的行为。<br>" +
|
||||
"<ul><li>当 <b>启用</b> 时,PDF 将仅显示 Excalidraw 绘图;</li>" +
|
||||
"<li>当 <b>禁用</b> 时,PDF 将显示文档的 Markdown 部分(背景笔记)。</li></ul>" +
|
||||
"请参阅上面‘外观和行为’部分的 <<a href='#"+TAG_MDREADINGMODE+"'>>Markdown 阅读模式</a> 相关设置。" +
|
||||
"⚠️ 注意,您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。⚠️",
|
||||
"此设置控制在使用 Obsidian 内置的<b>导出为 PDF</b>功能,如何将 Excalidraw 文件导出为 PDF。<br>" +
|
||||
"<ul><li><b>启用:</b>PDF 将包含图像格式的 Excalidraw 绘图。</li>" +
|
||||
"<li><b>禁用:</b>PDF 将包含作为文本的 Markdown 内容。</li></ul>" +
|
||||
"注意:此设置不会影响 Excalidraw 本身的 PDF 导出功能。<br>" +
|
||||
"请参阅上方“外观和行为”部分中与<a href='#" + TAG_MDREADINGMODE + "'>Markdown 阅读模式</a>相关的其他设置。<br>" +
|
||||
"⚠️ 您必须关闭并重新打开 Excalidraw/Markdown 文件,设置更改才会生效。⚠️",
|
||||
HOTKEY_OVERRIDE_HEAD: "热键覆盖",
|
||||
HOTKEY_OVERRIDE_DESC: `一些 Excalidraw 的热键,例如 ${labelCTRL()}+Enter 用于编辑文本,或 ${labelCTRL()}+K 用于创建元素链接。` +
|
||||
"与 Obsidian 的热键设置发生冲突。您在下面添加的热键组合将在使用 Excalidraw 时覆盖 Obsidian 的热键设置," +
|
||||
@@ -661,6 +662,7 @@ FILENAME_HEAD: "文件名",
|
||||
EXPORT_EMBED_SCENE_DESC:
|
||||
"在导出的图像中嵌入 Excalidraw 场景。可以通过在文件级别添加 <code>excalidraw-export-embed-scene: true/false</code> frontmatter 元数据键来覆盖此设置。" +
|
||||
"此设置仅在您下次(重新)打开绘图时生效。",
|
||||
PDF_EXPORT_SETTINGS : "PDF 导出设置",
|
||||
EXPORT_HEAD: "导出设置",
|
||||
EXPORT_SYNC_NAME:
|
||||
"保持 SVG/PNG 文件名与绘图文件同步",
|
||||
@@ -1006,4 +1008,86 @@ FILENAME_HEAD: "文件名",
|
||||
LINK_CLICK_POPOUT : "在弹出窗口中打开" ,
|
||||
LINK_CLICK_NEW_TAB : "在新标签页中打开" ,
|
||||
LINK_CLICK_MD_PROPS : "显示 Markdown 图片属性对话框(仅在嵌入 Markdown 文档为图片时适用)" ,
|
||||
|
||||
// 导出对话框
|
||||
// 对话框和标签页
|
||||
EXPORTDIALOG_TITLE : "导出图形",
|
||||
EXPORTDIALOG_TAB_IMAGE : "图像",
|
||||
EXPORTDIALOG_TAB_PDF : "PDF",
|
||||
// 设置持久化
|
||||
EXPORTDIALOG_SAVE_SETTINGS : "将图像设置保存到文件 doc.properties 吗?",
|
||||
EXPORTDIALOG_SAVE_SETTINGS_SAVE : "保存为预设",
|
||||
EXPORTDIALOG_SAVE_SETTINGS_ONETIME : "仅本次使用",
|
||||
// 图像设置
|
||||
EXPORTDIALOG_IMAGE_SETTINGS : "图像",
|
||||
EXPORTDIALOG_IMAGE_DESC : "PNG 支持透明。外部文件可以包含 Excalidraw 场景数据。",
|
||||
EXPORTDIALOG_PADDING : "边距",
|
||||
EXPORTDIALOG_SCALE : "缩放",
|
||||
EXPORTDIALOG_CURRENT_PADDING : "当前边距:",
|
||||
EXPORTDIALOG_SIZE_DESC : "缩放会影响输出大小",
|
||||
EXPORTDIALOG_SCALE_VALUE : "缩放:",
|
||||
EXPORTDIALOG_IMAGE_SIZE : "大小:",
|
||||
// 主题和背景
|
||||
EXPORTDIALOG_EXPORT_THEME : "主题",
|
||||
EXPORTDIALOG_THEME_LIGHT : "浅色",
|
||||
EXPORTDIALOG_THEME_DARK : "深色",
|
||||
EXPORTDIALOG_BACKGROUND : "背景",
|
||||
EXPORTDIALOG_BACKGROUND_TRANSPARENT : "透明",
|
||||
EXPORTDIALOG_BACKGROUND_USE_COLOR : "使用场景颜色",
|
||||
// 选择
|
||||
EXPORTDIALOG_SELECTED_ELEMENTS : "导出",
|
||||
EXPORTDIALOG_SELECTED_ALL : "整个场景",
|
||||
EXPORTDIALOG_SELECTED_SELECTED : "仅选中部分",
|
||||
// 导出选项
|
||||
EXPORTDIALOG_EMBED_SCENE : "包含场景数据吗?",
|
||||
EXPORTDIALOG_EMBED_YES : "是",
|
||||
EXPORTDIALOG_EMBED_NO : "否",
|
||||
// PDF 设置
|
||||
EXPORTDIALOG_PDF_SETTINGS : "PDF",
|
||||
EXPORTDIALOG_PAGE_SIZE : "页面大小",
|
||||
EXPORTDIALOG_PAGE_ORIENTATION : "方向",
|
||||
EXPORTDIALOG_ORIENTATION_PORTRAIT : "纵向",
|
||||
EXPORTDIALOG_ORIENTATION_LANDSCAPE : "横向",
|
||||
EXPORTDIALOG_PDF_DPI : "图像质量 [DPI]",
|
||||
EXPORTDIALOG_PDF_FIT_TO_PAGE : "页面适配",
|
||||
EXPORTDIALOG_PDF_FIT_OPTION : "适配页面",
|
||||
EXPORTDIALOG_PDF_FIT_2_OPTION : "适配至 2 页" ,
|
||||
EXPORTDIALOG_PDF_FIT_4_OPTION : "适配至 4 页" ,
|
||||
EXPORTDIALOG_PDF_FIT_6_OPTION : "适配至 6 页" ,
|
||||
EXPORTDIALOG_PDF_FIT_8_OPTION : "适配至 8 页" ,
|
||||
EXPORTDIALOG_PDF_FIT_12_OPTION : "适配至 12 页" ,
|
||||
EXPORTDIALOG_PDF_FIT_16_OPTION : "适配至 16 页" ,
|
||||
EXPORTDIALOG_PDF_SCALE_OPTION : "使用图像缩放(可能跨多页)",
|
||||
EXPORTDIALOG_PDF_PAPER_COLOR : "纸张颜色",
|
||||
EXPORTDIALOG_PDF_PAPER_WHITE : "白色",
|
||||
EXPORTDIALOG_PDF_PAPER_SCENE : "使用场景颜色",
|
||||
EXPORTDIALOG_PDF_PAPER_CUSTOM : "自定义颜色",
|
||||
EXPORTDIALOG_PDF_ALIGNMENT : "页面位置",
|
||||
EXPORTDIALOG_PDF_ALIGN_CENTER : "居中",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_LEFT : "左上角",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_CENTER : "顶部居中",
|
||||
EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT : "右上角",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_LEFT : "左下角",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_CENTER : "底部居中",
|
||||
EXPORTDIALOG_PDF_ALIGN_BOTTOM_RIGHT : "右下角",
|
||||
EXPORTDIALOG_PDF_MARGIN : "边距",
|
||||
EXPORTDIALOG_PDF_MARGIN_NONE : "无",
|
||||
EXPORTDIALOG_PDF_MARGIN_TINY : "小",
|
||||
EXPORTDIALOG_PDF_MARGIN_NORMAL : "正常",
|
||||
EXPORTDIALOG_SAVE_PDF_SETTINGS : "保存 PDF 设置",
|
||||
EXPORTDIALOG_SAVE_CONFIRMATION : "PDF 配置已保存为插件默认设置",
|
||||
// 按钮
|
||||
EXPORTDIALOG_PNGTOFILE : "导出 PNG 文件",
|
||||
EXPORTDIALOG_SVGTOFILE : "导出 SVG 文件",
|
||||
EXPORTDIALOG_PNGTOVAULT : "PNG 保存到 Vault",
|
||||
EXPORTDIALOG_SVGTOVAULT : "SVG 保存到 Vault",
|
||||
EXPORTDIALOG_EXCALIDRAW : "Excalidraw",
|
||||
EXPORTDIALOG_PNGTOCLIPBOARD : "PNG 复制到剪贴板",
|
||||
EXPORTDIALOG_SVGTOCLIPBOARD : "SVG 复制到剪贴板",
|
||||
EXPORTDIALOG_PDF : "导出 PDF 文件",
|
||||
EXPORTDIALOG_PDFTOVAULT : "PDF 保存到 Vault",
|
||||
|
||||
EXPORTDIALOG_PDF_PROGRESS_NOTICE : "正在导出页面" ,
|
||||
EXPORTDIALOG_PDF_PROGRESS_IMAGE : "的图像" ,
|
||||
EXPORTDIALOG_PDF_PROGRESS_DONE : "导出完成" ,
|
||||
};
|
||||
@@ -1,11 +1,16 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { Modal, Setting, TFile } from "obsidian";
|
||||
import { Modal, Notice, Setting, TFile, ButtonComponent } from "obsidian";
|
||||
import { getEA } from "src/core";
|
||||
import { DEVICE } from "src/constants/constants";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { fragWithHTML, getExportPadding, getExportTheme, getPNGScale, getWithBackground, shouldEmbedScene } from "src/utils/utils";
|
||||
import { PageOrientation, PageSize, PDFPageAlignment, PDFPageMarginString, exportSVGToClipboard } from "src/utils/exportUtils";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { PDFExportSettings, PDFExportSettingsComponent } from "./PDFExportSettingsComponent";
|
||||
|
||||
|
||||
|
||||
export class ExportDialog extends Modal {
|
||||
private ea: ExcalidrawAutomate;
|
||||
@@ -27,6 +32,17 @@ export class ExportDialog extends Modal {
|
||||
public embedScene: boolean;
|
||||
public exportSelectedOnly: boolean;
|
||||
public saveToVault: boolean;
|
||||
public pageSize: PageSize = "A4";
|
||||
public pageOrientation: PageOrientation = "portrait";
|
||||
private activeTab: "image" | "pdf" = "image";
|
||||
private contentContainer: HTMLDivElement;
|
||||
private buttonContainerRow1: HTMLDivElement;
|
||||
private buttonContainerRow2: HTMLDivElement;
|
||||
public fitToPage: number = 1;
|
||||
public paperColor: "white" | "scene" | "custom" = "white";
|
||||
public customPaperColor: string = "#ffffff";
|
||||
public alignment: PDFPageAlignment = "center";
|
||||
public margin: PDFPageMarginString = "normal";
|
||||
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
@@ -44,7 +60,17 @@ export class ExportDialog extends Modal {
|
||||
this.exportSelectedOnly = false;
|
||||
this.saveToVault = true;
|
||||
this.transparent = !getWithBackground(this.plugin, this.file);
|
||||
|
||||
this.pageSize = plugin.settings.pdfSettings.pageSize;
|
||||
this.pageOrientation = plugin.settings.pdfSettings.pageOrientation;
|
||||
this.fitToPage = plugin.settings.pdfSettings.fitToPage;
|
||||
this.paperColor = plugin.settings.pdfSettings.paperColor;
|
||||
this.customPaperColor = plugin.settings.pdfSettings.customPaperColor;
|
||||
this.alignment = plugin.settings.pdfSettings.alignment;
|
||||
this.margin = plugin.settings.pdfSettings.margin;
|
||||
|
||||
this.saveSettings = false;
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@@ -62,7 +88,7 @@ export class ExportDialog extends Modal {
|
||||
|
||||
onOpen(): void {
|
||||
this.containerEl.classList.add("excalidraw-release");
|
||||
this.titleEl.setText(`Export Image`);
|
||||
this.titleEl.setText(t("EXPORTDIALOG_TITLE"));
|
||||
this.hasSelectedElements = this.view.getViewSelectedElements().length > 0;
|
||||
//@ts-ignore
|
||||
this.selectedOnlySetting.setVisibility(this.hasSelectedElements);
|
||||
@@ -72,29 +98,101 @@ export class ExportDialog extends Modal {
|
||||
this.dirty = this.saveSettings;
|
||||
}
|
||||
|
||||
async createForm() {
|
||||
let scaleSetting:Setting;
|
||||
let paddingSetting: Setting;
|
||||
createForm() {
|
||||
if(DEVICE.isDesktop) {
|
||||
// Create tab container
|
||||
const tabContainer = this.contentEl.createDiv("nav-buttons-container");
|
||||
const imageTab = tabContainer.createEl("button", {
|
||||
text: t("EXPORTDIALOG_TAB_IMAGE"),
|
||||
cls: `nav-button ${this.activeTab === "image" ? "is-active" : ""}`
|
||||
});
|
||||
|
||||
this.contentEl.createEl("h1",{text: "Image settings"});
|
||||
this.contentEl.createEl("p",{text: "Transparency only affects PNGs. Excalidraw files can only be exported outside the Vault. PNGs copied to clipboard may not include the scene."})
|
||||
|
||||
const pdfTab = tabContainer.createEl("button", {
|
||||
text: t("EXPORTDIALOG_TAB_PDF"),
|
||||
cls: `nav-button ${this.activeTab === "pdf" ? "is-active" : ""}`
|
||||
});
|
||||
|
||||
// Tab click handlers
|
||||
imageTab.onclick = () => {
|
||||
this.activeTab = "image";
|
||||
imageTab.addClass("is-active");
|
||||
pdfTab.removeClass("is-active");
|
||||
this.renderContent();
|
||||
};
|
||||
|
||||
pdfTab.onclick = () => {
|
||||
this.activeTab = "pdf";
|
||||
pdfTab.addClass("is-active");
|
||||
imageTab.removeClass("is-active");
|
||||
this.renderContent();
|
||||
};
|
||||
}
|
||||
|
||||
// Create content container
|
||||
this.contentContainer = this.contentEl.createDiv();
|
||||
this.buttonContainerRow1 = this.contentEl.createDiv({cls: "excalidraw-export-buttons-div"});
|
||||
this.buttonContainerRow2 = this.contentEl.createDiv({cls: "excalidraw-export-buttons-div"});
|
||||
this.buttonContainerRow2.style.marginTop = "10px";
|
||||
|
||||
this.renderContent();
|
||||
}
|
||||
|
||||
private createSaveSettingsDropdown() {
|
||||
new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_SAVE_SETTINGS"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("save", t("EXPORTDIALOG_SAVE_SETTINGS_SAVE"))
|
||||
.addOption("one-time", t("EXPORTDIALOG_SAVE_SETTINGS_ONETIME"))
|
||||
.setValue(this.saveSettings ? "save" : "one-time")
|
||||
.onChange(value => {
|
||||
this.saveSettings = value === "save";
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private renderContent() {
|
||||
this.contentContainer.empty();
|
||||
this.buttonContainerRow1.empty();
|
||||
this.buttonContainerRow2.empty();
|
||||
|
||||
if (this.activeTab === "image") {
|
||||
this.createImageSettings();
|
||||
this.createExportSettings();
|
||||
this.createImageButtons();
|
||||
} else {
|
||||
this.createImageSettings();
|
||||
this.createPDFSettings();
|
||||
this.createPDFButton();
|
||||
}
|
||||
}
|
||||
|
||||
private createImageSettings() {
|
||||
let scaleSetting:Setting;
|
||||
let paddingSetting: Setting;
|
||||
|
||||
this.contentContainer.createEl("h1",{text: t("EXPORTDIALOG_IMAGE_SETTINGS")});
|
||||
this.contentContainer.createEl("p",{text: t("EXPORTDIALOG_IMAGE_DESC")})
|
||||
|
||||
this.createSaveSettingsDropdown();
|
||||
|
||||
const size = ():DocumentFragment => {
|
||||
const width = Math.round(this.scale*this.boundingBox.width + this.padding*2);
|
||||
const height = Math.round(this.scale*this.boundingBox.height + this.padding*2);
|
||||
return fragWithHTML(`The lager the scale, the larger the image.<br>Scale: <b>${this.scale}</b><br>Image size: <b>${width}x${height}</b>`);
|
||||
return fragWithHTML(`${t("EXPORTDIALOG_SIZE_DESC")}<br>${t("EXPORTDIALOG_SCALE_VALUE")} <b>${this.scale}</b><br>${t("EXPORTDIALOG_IMAGE_SIZE")} <b>${width}x${height}</b>`);
|
||||
}
|
||||
|
||||
const padding = ():DocumentFragment => {
|
||||
return fragWithHTML(`Current image padding is <b>${this.padding}</b>`);
|
||||
return fragWithHTML(`${t("EXPORTDIALOG_CURRENT_PADDING")} <b>${this.padding}</b>`);
|
||||
}
|
||||
|
||||
paddingSetting = new Setting(this.contentEl)
|
||||
.setName("Image padding")
|
||||
paddingSetting = new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_PADDING"))
|
||||
.setDesc(padding())
|
||||
.addSlider(slider => {
|
||||
slider
|
||||
.setLimits(0,50,1)
|
||||
.setLimits(0,100,1)
|
||||
.setValue(this.padding)
|
||||
.onChange(value => {
|
||||
this.padding = value;
|
||||
@@ -103,12 +201,12 @@ export class ExportDialog extends Modal {
|
||||
})
|
||||
})
|
||||
|
||||
scaleSetting = new Setting(this.contentEl)
|
||||
.setName("PNG Scale")
|
||||
scaleSetting = new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_SCALE"))
|
||||
.setDesc(size())
|
||||
.addSlider(slider =>
|
||||
slider
|
||||
.setLimits(0.5,5,0.5)
|
||||
.setLimits(0.2,7,0.1)
|
||||
.setValue(this.scale)
|
||||
.onChange(value => {
|
||||
this.scale = value;
|
||||
@@ -116,109 +214,198 @@ export class ExportDialog extends Modal {
|
||||
})
|
||||
)
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName("Export theme")
|
||||
new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_EXPORT_THEME"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("light","Light")
|
||||
.addOption("dark","Dark")
|
||||
.addOption("light", t("EXPORTDIALOG_THEME_LIGHT"))
|
||||
.addOption("dark", t("EXPORTDIALOG_THEME_DARK"))
|
||||
.setValue(this.theme)
|
||||
.onChange(value => {
|
||||
this.theme = value;
|
||||
})
|
||||
)
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName("Background color")
|
||||
new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_BACKGROUND"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("transparent","Transparent")
|
||||
.addOption("with-color","Use scene background color")
|
||||
.addOption("transparent", t("EXPORTDIALOG_BACKGROUND_TRANSPARENT"))
|
||||
.addOption("with-color", t("EXPORTDIALOG_BACKGROUND_USE_COLOR"))
|
||||
.setValue(this.transparent?"transparent":"with-color")
|
||||
.onChange(value => {
|
||||
this.transparent = value === "transparent";
|
||||
})
|
||||
)
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName("Save or one-time settings?")
|
||||
|
||||
this.selectedOnlySetting = new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_SELECTED_ELEMENTS"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("save","Save these settings as the preset for this image")
|
||||
.addOption("one-time","These are one-time settings")
|
||||
.setValue(this.saveSettings?"save":"one-time")
|
||||
.addOption("all", t("EXPORTDIALOG_SELECTED_ALL"))
|
||||
.addOption("selected", t("EXPORTDIALOG_SELECTED_SELECTED"))
|
||||
.setValue(this.exportSelectedOnly?"selected":"all")
|
||||
.onChange(value => {
|
||||
this.saveSettings = value === "save";
|
||||
this.exportSelectedOnly = value === "selected";
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.contentEl.createEl("h1",{text:"Export settings"});
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName("Embed the Excalidraw scene in the exported file?")
|
||||
private createExportSettings() {
|
||||
new Setting(this.contentContainer)
|
||||
.setName(t("EXPORTDIALOG_EMBED_SCENE"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("embed","Embed scene")
|
||||
.addOption("no-embed","Do not embed scene")
|
||||
.addOption("embed",t("EXPORTDIALOG_EMBED_YES"))
|
||||
.addOption("no-embed",t("EXPORTDIALOG_EMBED_NO"))
|
||||
.setValue(this.embedScene?"embed":"no-embed")
|
||||
.onChange(value => {
|
||||
this.embedScene = value === "embed";
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private createPDFSettings() {
|
||||
if (!DEVICE.isDesktop) return;
|
||||
|
||||
this.contentContainer.createEl("h1", { text: t("EXPORTDIALOG_PDF_SETTINGS") });
|
||||
|
||||
const pdfSettings: PDFExportSettings = {
|
||||
pageSize: this.pageSize,
|
||||
pageOrientation: this.pageOrientation,
|
||||
fitToPage: this.fitToPage,
|
||||
paperColor: this.paperColor,
|
||||
customPaperColor: this.customPaperColor,
|
||||
alignment: this.alignment,
|
||||
margin: this.margin,
|
||||
};
|
||||
|
||||
new PDFExportSettingsComponent(
|
||||
this.contentContainer,
|
||||
pdfSettings,
|
||||
() => {
|
||||
this.pageSize = pdfSettings.pageSize;
|
||||
this.pageOrientation = pdfSettings.pageOrientation;
|
||||
this.fitToPage = pdfSettings.fitToPage;
|
||||
this.paperColor = pdfSettings.paperColor;
|
||||
this.customPaperColor = pdfSettings.customPaperColor;
|
||||
this.alignment = pdfSettings.alignment;
|
||||
this.margin = pdfSettings.margin;
|
||||
}
|
||||
).render();
|
||||
}
|
||||
|
||||
private createImageButtons() {
|
||||
if(DEVICE.isDesktop) {
|
||||
new Setting(this.contentEl)
|
||||
.setName("Where to save the image?")
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("vault","Save image to your Vault")
|
||||
.addOption("outside","Export image outside your Vault")
|
||||
.setValue(this.saveToVault?"vault":"outside")
|
||||
.onChange(value => {
|
||||
this.saveToVault = value === "vault";
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
this.selectedOnlySetting = new Setting(this.contentEl)
|
||||
.setName("Export entire scene or just selected elements?")
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOption("all","Export entire scene")
|
||||
.addOption("selected","Export selected elements")
|
||||
.setValue(this.exportSelectedOnly?"selected":"all")
|
||||
.onChange(value => {
|
||||
this.exportSelectedOnly = value === "selected";
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
const div = this.contentEl.createDiv({cls: "excalidraw-prompt-buttons-div"});
|
||||
const bPNG = div.createEl("button", { text: "PNG to File", cls: "excalidraw-prompt-button"});
|
||||
bPNG.onclick = () => {
|
||||
this.saveToVault
|
||||
? this.view.savePNG(this.view.getScene(this.hasSelectedElements && this.exportSelectedOnly))
|
||||
: this.view.exportPNG(this.embedScene,this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
const bSVG = div.createEl("button", { text: "SVG to File", cls: "excalidraw-prompt-button" });
|
||||
bSVG.onclick = () => {
|
||||
this.saveToVault
|
||||
? this.view.saveSVG(this.view.getScene(this.hasSelectedElements && this.exportSelectedOnly))
|
||||
: this.view.exportSVG(this.embedScene,this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
const bExcalidraw = div.createEl("button", { text: "Excalidraw", cls: "excalidraw-prompt-button" });
|
||||
bExcalidraw.onclick = () => {
|
||||
this.view.exportExcalidraw(this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
if(DEVICE.isDesktop) {
|
||||
const bPNGClipboard = div.createEl("button", { text: "PNG to Clipboard", cls: "excalidraw-prompt-button" });
|
||||
bPNGClipboard.onclick = () => {
|
||||
this.view.exportPNGToClipboard(this.embedScene, this.hasSelectedElements && this.exportSelectedOnly);
|
||||
const bPNG = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PNGTOFILE"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bPNG.onclick = () => {
|
||||
this.view.exportPNG(this.embedScene, this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
const bPNGVault = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PNGTOVAULT"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bPNGVault.onclick = () => {
|
||||
this.view.savePNG(this.view.getScene(this.hasSelectedElements && this.exportSelectedOnly));
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bPNGClipboard = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PNGTOCLIPBOARD"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bPNGClipboard.onclick = async () => {
|
||||
this.view.exportPNGToClipboard(this.embedScene, this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
|
||||
if(DEVICE.isDesktop) {
|
||||
const bExcalidraw = this.buttonContainerRow2.createEl("button", {
|
||||
text: t("EXPORTDIALOG_EXCALIDRAW"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bExcalidraw.onclick = () => {
|
||||
this.view.exportExcalidraw();
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bSVG = this.buttonContainerRow2.createEl("button", {
|
||||
text: t("EXPORTDIALOG_SVGTOFILE"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bSVG.onclick = () => {
|
||||
this.view.exportSVG(this.embedScene, this.hasSelectedElements && this.exportSelectedOnly);
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
const bSVGVault = this.buttonContainerRow2.createEl("button", {
|
||||
text: t("EXPORTDIALOG_SVGTOVAULT"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bSVGVault.onclick = () => {
|
||||
this.view.saveSVG(this.view.getScene(this.hasSelectedElements && this.exportSelectedOnly));
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bSVGClipboard = this.buttonContainerRow2.createEl("button", {
|
||||
text: t("EXPORTDIALOG_SVGTOCLIPBOARD"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bSVGClipboard.onclick = async () => {
|
||||
const svg = await this.view.getSVG(this.embedScene, this.hasSelectedElements && this.exportSelectedOnly);
|
||||
exportSVGToClipboard(svg);
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
private createPDFButton() {
|
||||
const bSavePDFSettings = this.buttonContainerRow1.createEl("button",
|
||||
{ text: t("EXPORTDIALOG_SAVE_PDF_SETTINGS"), cls: "excalidraw-export-button" }
|
||||
);
|
||||
bSavePDFSettings.onclick = async () => {
|
||||
//in case sync loaded a new version of settings in the mean time
|
||||
await this.plugin.loadSettings();
|
||||
this.plugin.settings.pdfSettings = {
|
||||
pageSize: this.pageSize,
|
||||
pageOrientation: this.pageOrientation,
|
||||
fitToPage: this.fitToPage,
|
||||
paperColor: this.paperColor,
|
||||
customPaperColor: this.customPaperColor,
|
||||
alignment: this.alignment,
|
||||
margin: this.margin,
|
||||
};
|
||||
await this.plugin.saveSettings();
|
||||
new Notice(t("EXPORTDIALOG_SAVE_CONFIRMATION"));
|
||||
};
|
||||
|
||||
if (!DEVICE.isDesktop) return;
|
||||
const bPDFExport = this.buttonContainerRow1.createEl("button", {
|
||||
text: t("EXPORTDIALOG_PDF"),
|
||||
cls: "excalidraw-export-button"
|
||||
});
|
||||
bPDFExport.onclick = () => {
|
||||
this.view.exportPDF(
|
||||
this.hasSelectedElements && this.exportSelectedOnly,
|
||||
this.pageSize,
|
||||
this.pageOrientation
|
||||
);
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
public getPaperColor(): string {
|
||||
switch (this.paperColor) {
|
||||
case "white": return this.theme === "light" ? "#ffffff" : "#000000";
|
||||
case "scene": return this.api.getAppState().viewBackgroundColor;
|
||||
case "custom": return this.customPaperColor;
|
||||
default: return "#ffffff";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,116 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
||||
`,
|
||||
"2.8.3":`
|
||||
## Fixed
|
||||
- Chinese translation not available since 2.8.0. [#2247](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2247)
|
||||
- Since the most recent Samsung Android update, adding images from the gallery returns an Unsupported Image Type error. [#2245](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2245)
|
||||
- Duplicating/removing frame while children selected [#9079](https://github.com/excalidraw/excalidraw/pull/9079)
|
||||
`,
|
||||
"2.8.2":`
|
||||
## New
|
||||
- Moved "Create new drawing" option up in the context menu [#2243](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2243)
|
||||
|
||||
## Fixed
|
||||
- In rare cases drawing content gets overwritten with another drawing [#2152](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2152)
|
||||
- "Wrap selection in frame" sets dark mode to light mode [#2240](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2240)
|
||||
- Multiple bug fixes from Excalidraw.com
|
||||
- Elbow arrows within boxes [#9077](https://github.com/excalidraw/excalidraw/issues/9077)
|
||||
- Elbow arrow orthogonality [#9073](https://github.com/excalidraw/excalidraw/pull/9073)
|
||||
- Improve library sidebar performance [#9060](https://github.com/excalidraw/excalidraw/pull/9060)
|
||||
- Opacity slider now displays numerical value [#9009](https://github.com/excalidraw/excalidraw/pull/9009)
|
||||
- Resize a frame and its children together when box selecting the frame and its children together [#9031](https://github.com/excalidraw/excalidraw/pull/9031)
|
||||
- Excalidraw screen flickering in dark mode [#9057](https://github.com/excalidraw/excalidraw/pull/9057)
|
||||
`,
|
||||
"2.8.1":`
|
||||
## Fixed
|
||||
- Unable to open Excalidraw files after the 2.8.0 update. [#2235](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2235)
|
||||
`,
|
||||
"2.8.0":`
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/tWi5xTUTz7E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## New
|
||||
- Updated "Export Image" dialog
|
||||
- 🚀 PDF Export option including tiling of images over multiple pages. Only available on desktop :(
|
||||
- SVG to clipboard
|
||||
- More granular setting for padding and scale
|
||||
- Slideshow script can now print slides to PDF (update script from script store)
|
||||
- Set local graph to show the links in the embeddable when it is activated/deactivated [#2200](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2200)
|
||||
|
||||
## Fixed
|
||||
- Fixed several LaTeX issues. 🙏 @Sintuz [#1631](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1631), [#2195](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2195), [#1842](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1842)
|
||||
- Fixed support for *.jfif and *.avif images [#2212](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2212)
|
||||
- PDF++ selection is not correctly showing after embedded into a drawing (for some specific files) [#2213](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2213)
|
||||
- iOS 18 can't upload image and library [#2182](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2182)
|
||||
- Image block references are broken in hover previews [#2218](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2218)
|
||||
- ⚠️ Note there is a known issue in Obsidian 1.8.2 ⚠️ affecting preview windows in Excalidraw. I received confirmation that this will be fixed in 1.8.3. For now, if hover previews are important to you, you can downgrade to Obsidian 1.8.1 [#2228](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2225)
|
||||
- Mobile elements panel and context menu are not scrollable [#2216](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2216)
|
||||
- "Local Font" menu disappears when opening a drawing in an Obsidian popout-window [#2205](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2205)
|
||||
|
||||
## Updates from Excalidraw.com
|
||||
- Pressing delete on a frame will only delete the children [#9011](https://github.com/excalidraw/excalidraw/pull/9011)
|
||||
- New crowfoot arrowheads and a new arrowhead picker [#8942](https://github.com/excalidraw/excalidraw/pull/8942)
|
||||
- Fixed some of the arrow binding issues [#9010](https://github.com/excalidraw/excalidraw/pull/9010), [#2209](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2209)
|
||||
- New context menu action: "Wrap selection in frame" [#9005](https://github.com/excalidraw/excalidraw/pull/9005)
|
||||
- Elbow arrow segment fixing and positioning [#8952](https://github.com/excalidraw/excalidraw/pull/8952)
|
||||
- When drag creating a new frame, do not add a partial group to it. When wrapping a selected partial group in a frame however, do add it to the wrapping frame. But such that it should be separated from the previous containing group. [#9014](https://github.com/excalidraw/excalidraw/pull/9014)
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
- New hook: ${String.fromCharCode(96)}onImageFileNameHook${String.fromCharCode(96)}. When set, this callback is triggered when a image is being saved in Excalidraw.
|
||||
- PDF export functions, paving the way for slideshow to export slides to PDF
|
||||
${String.fromCharCode(96,96,96)}ts
|
||||
/**
|
||||
* Returns the dimensions of a standard page size in pixels.
|
||||
*/
|
||||
function getPagePDFDimensions(
|
||||
pageSize: PageSize,
|
||||
orientation: PageOrientation
|
||||
): PageDimensions;
|
||||
|
||||
/**
|
||||
* Creates a PDF from the provided SVG elements with specified scaling and page properties.
|
||||
*/
|
||||
function createPDF(props: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale?: PDFExportScale;
|
||||
pageProps?: PDFPageProperties;
|
||||
filename: string;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates an SVG representation of the current view.
|
||||
*/
|
||||
function createViewSVG(props : {
|
||||
withBackground?: boolean;
|
||||
theme?: "light" | "dark";
|
||||
frameRendering?: FrameRenderingOptions;
|
||||
padding?: number;
|
||||
selectedOnly?: boolean;
|
||||
skipInliningFonts?: boolean;
|
||||
embedScene?: boolean;
|
||||
}): Promise<SVGSVGElement>;
|
||||
|
||||
/**
|
||||
* If set, this callback is triggered when a image is being saved in Excalidraw.
|
||||
* You can use this callback to customize the naming and path of pasted images to avoid
|
||||
* default names like "Pasted image 123147170.png" being saved in the attachments folder,
|
||||
* and instead use more meaningful names based on the Excalidraw file or other criteria,
|
||||
* plus save the image in a different folder.
|
||||
*
|
||||
* If the function returns null or undefined, the normal Excalidraw operation will continue
|
||||
* with the excalidraw generated name and default path.
|
||||
* If a filepath is returned, that will be used. Include the full Vault filepath and filename
|
||||
* with the file extension.
|
||||
* The currentImageName is the name of the image generated by excalidraw or provided during paste.
|
||||
*/
|
||||
function onImageFilePathHook: (data: {
|
||||
currentImageName: string;
|
||||
drawingFilePath: string;
|
||||
}) => string = null;
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"2.7.5":`
|
||||
## Fixed
|
||||
- PDF export scenario described in [#2184](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2184)
|
||||
@@ -171,24 +281,5 @@ ${String.fromCharCode(96,96,96)}
|
||||
- Hover-Editor compatibility resolved [2041](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2041)
|
||||
- ${String.fromCharCode(96)}ExcalidrawAutomate.create() ${String.fromCharCode(96)} will now correctly include the markdown text in templates above Excalidraw Data and below YAML front matter. This also fixes the same issue with the **Deconstruct Selected Element script**.
|
||||
|
||||
`,
|
||||
"2.6.7":`
|
||||
Hoping to finally move on to 2.7.0... but still have one last bug to fix in 2.6.x!
|
||||
|
||||
## Fixed
|
||||
I misread a line in the Excalidraw package code... ended up breaking image loading in 2.6.6. The icon library script didn't work right, and updating nested drawings caused all images in the scene to be dropped from memory. This led to image-placeholders in exports and broke copy-paste to Excalidraw.com and between drawings. I am surprised no one reported it! 😳
|
||||
`,
|
||||
"2.6.6":`
|
||||
## Fixed
|
||||
- Images and LaTeX formulas did not update in the scene when the source was changed until the Excalidraw drawing was closed and reopened. [#2105](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2105)
|
||||
`,
|
||||
"2.6.5":`
|
||||
## Fixed
|
||||
- Text sizing issue in the drawing that is first loaded after Obsidian restarts [#2086](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2086)
|
||||
- Excalidraw didn't load if there was a file in the Excalidraw folder with a name that starts the same way as the Scripts folder name. [#2095](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2095)
|
||||
- **OVERSIZED EXCALIDRAW TOOLBAR**: Added a new setting under "Excalidraw Appearance and Behavior > Theme and Styling" called "Limit Obsidian Font Size to Editor Text." This setting is off by default. When enabled, it restricts Obsidian's custom font size adjustments to editor text only, preventing unintended scaling of Excalidraw UI elements and other themes that rely on the default interface font size. Feel free to experiment with this setting to improve Excalidraw UI consistency. However, because this change affects the broader Obsidian UI, it's recommended to turn it off if any layout issues arise. [#2087](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2087)`,
|
||||
"2.6.4":`
|
||||
## Fixed
|
||||
- Error saving when cropping images embedded from a URL (not from a file in the Vault) [#2096](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2096)
|
||||
`,
|
||||
};
|
||||
|
||||
148
src/shared/Dialogs/PDFExportSettingsComponent.ts
Normal file
148
src/shared/Dialogs/PDFExportSettingsComponent.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { Setting } from "obsidian";
|
||||
import { PageOrientation, PageSize, PDFPageAlignment, PDFPageMarginString, STANDARD_PAGE_SIZES } from "src/utils/exportUtils";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
export interface PDFExportSettings {
|
||||
pageSize: PageSize;
|
||||
pageOrientation: PageOrientation;
|
||||
fitToPage: number;
|
||||
paperColor: "white" | "scene" | "custom";
|
||||
customPaperColor: string;
|
||||
alignment: PDFPageAlignment;
|
||||
margin: PDFPageMarginString;
|
||||
}
|
||||
|
||||
export class PDFExportSettingsComponent {
|
||||
constructor(
|
||||
private contentEl: HTMLElement,
|
||||
private settings: PDFExportSettings,
|
||||
private update?: Function,
|
||||
) {
|
||||
if (!update) this.update = () => {};
|
||||
}
|
||||
|
||||
render() {
|
||||
const pageSizeOptions: Record<string, string> = Object.keys(STANDARD_PAGE_SIZES)
|
||||
.reduce((acc, key) => ({
|
||||
...acc,
|
||||
[key]: key
|
||||
}), {});
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PAGE_SIZE"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions(pageSizeOptions)
|
||||
.setValue(this.settings.pageSize)
|
||||
.onChange(value => {
|
||||
this.settings.pageSize = value as PageSize;
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PAGE_ORIENTATION"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"portrait": t("EXPORTDIALOG_ORIENTATION_PORTRAIT"),
|
||||
"landscape": t("EXPORTDIALOG_ORIENTATION_LANDSCAPE")
|
||||
})
|
||||
.setValue(this.settings.pageOrientation)
|
||||
.onChange(value => {
|
||||
this.settings.pageOrientation = value as PageOrientation;
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_FIT_TO_PAGE"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"scale": t("EXPORTDIALOG_PDF_SCALE_OPTION"),
|
||||
"fit": t("EXPORTDIALOG_PDF_FIT_OPTION"),
|
||||
"fit-2": t("EXPORTDIALOG_PDF_FIT_2_OPTION"),
|
||||
"fit-4": t("EXPORTDIALOG_PDF_FIT_4_OPTION"),
|
||||
"fit-6": t("EXPORTDIALOG_PDF_FIT_6_OPTION"),
|
||||
"fit-8": t("EXPORTDIALOG_PDF_FIT_8_OPTION"),
|
||||
"fit-12": t("EXPORTDIALOG_PDF_FIT_12_OPTION"),
|
||||
"fit-16": t("EXPORTDIALOG_PDF_FIT_16_OPTION")
|
||||
})
|
||||
.setValue(this.settings.fitToPage === 1 ? "fit" :
|
||||
(typeof this.settings.fitToPage === "number" ? `fit-${this.settings.fitToPage}` : "scale"))
|
||||
.onChange(value => {
|
||||
this.settings.fitToPage = value === "scale" ? 0 :
|
||||
(value === "fit" ? 1 : parseInt(value.split("-")[1]));
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_MARGIN"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"none": t("EXPORTDIALOG_PDF_MARGIN_NONE"),
|
||||
"tiny": t("EXPORTDIALOG_PDF_MARGIN_TINY"),
|
||||
"normal": t("EXPORTDIALOG_PDF_MARGIN_NORMAL")
|
||||
})
|
||||
.setValue(this.settings.margin)
|
||||
.onChange(value => {
|
||||
this.settings.margin = value as PDFPageMarginString;
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
const paperColorSetting = new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_PAPER_COLOR"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"white": t("EXPORTDIALOG_PDF_PAPER_WHITE"),
|
||||
"scene": t("EXPORTDIALOG_PDF_PAPER_SCENE"),
|
||||
"custom": t("EXPORTDIALOG_PDF_PAPER_CUSTOM")
|
||||
})
|
||||
.setValue(this.settings.paperColor)
|
||||
.onChange(value => {
|
||||
this.settings.paperColor = value as typeof this.settings.paperColor;
|
||||
colorInput.style.display = (value === "custom") ? "block" : "none";
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
|
||||
const colorInput = paperColorSetting.controlEl.createEl("input", {
|
||||
type: "color",
|
||||
value: this.settings.customPaperColor
|
||||
});
|
||||
colorInput.style.width = "50px";
|
||||
colorInput.style.marginLeft = "10px";
|
||||
colorInput.style.display = this.settings.paperColor === "custom" ? "block" : "none";
|
||||
colorInput.addEventListener("change", (e) => {
|
||||
this.settings.customPaperColor = (e.target as HTMLInputElement).value;
|
||||
this.update();
|
||||
});
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t("EXPORTDIALOG_PDF_ALIGNMENT"))
|
||||
.addDropdown(dropdown =>
|
||||
dropdown
|
||||
.addOptions({
|
||||
"center": t("EXPORTDIALOG_PDF_ALIGN_CENTER"),
|
||||
"center-left": t("EXPORTDIALOG_PDF_ALIGN_CENTER_LEFT"),
|
||||
"center-right": t("EXPORTDIALOG_PDF_ALIGN_CENTER_RIGHT"),
|
||||
"top-left": t("EXPORTDIALOG_PDF_ALIGN_TOP_LEFT"),
|
||||
"top-center": t("EXPORTDIALOG_PDF_ALIGN_TOP_CENTER"),
|
||||
"top-right": t("EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT"),
|
||||
"bottom-left": t("EXPORTDIALOG_PDF_ALIGN_BOTTOM_LEFT"),
|
||||
"bottom-center": t("EXPORTDIALOG_PDF_ALIGN_BOTTOM_CENTER"),
|
||||
"bottom-right": t("EXPORTDIALOG_PDF_ALIGN_BOTTOM_RIGHT")
|
||||
})
|
||||
.setValue(this.settings.alignment)
|
||||
.onChange(value => {
|
||||
this.settings.alignment = value as PDFPageAlignment;
|
||||
this.update();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -224,6 +224,87 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "createPDF",
|
||||
code: "async createPDF({SVG: SVGSVGElement[], scale?: PDFExportScale, pageProps?: PDFPageProperties, filename: string}): Promise<void>",
|
||||
desc: "Creates a PDF from the provided SVG elements with specified scaling and page properties.\n" +
|
||||
"\n" +
|
||||
"@param {Object} params - The parameters for creating the PDF.\n" +
|
||||
"@param {SVGSVGElement[]} params.SVG - An array of SVG elements to be included in the PDF. If multiple SVGs are provided, each will be added to a new page.\n" +
|
||||
"@param {PDFExportScale} [params.scale={ fitToPage: true, zoom: 1 }] - The scaling options for the SVG elements.\n" +
|
||||
"@param {PDFPageProperties} [params.pageProps] - The properties for the PDF pages.\n" +
|
||||
"@param {string} params.filename - The name of the PDF file to be created.\n" +
|
||||
"@returns {Promise<ArrayBuffer>} - A promise that resolves to an ArrayBuffer containing the PDF data.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PDFExportScale\n" +
|
||||
"@property {boolean} fitToPage - Whether to fit the SVG to the page.\n" +
|
||||
"@property {number} [zoom=1] - The zoom level for the SVG. Used only if fitToPage is false. If the SVG does not fit the page, it will be tiled over multiple pages.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PDFPageProperties\n" +
|
||||
"@property {{width: number, height: number}} [dimensions] - The dimensions of the PDF pages in pixels. Use getPageDimensions to get standard page sizes.\n" +
|
||||
"@property {string} [backgroundColor] - The background color of the PDF pages.\n" +
|
||||
"@property {PDFMargin} margin - The margins of the PDF pages in pixels.\n" +
|
||||
"@property {PDFPageAlignment} alignment - The alignment of the SVG on the PDF pages.",
|
||||
after: "({\n" +
|
||||
" SVG: [svgElement1, svgElement2],\n" +
|
||||
" scale: { fitToPage: true },\n" +
|
||||
" pageProps: {\n" +
|
||||
" dimensions: { width: 595.28, height: 841.89 },\n" +
|
||||
" backgroundColor: \"#ffffff\",\n" +
|
||||
" margin: { left: 20, right: 20, top: 20, bottom: 20 },\n" +
|
||||
" alignment: \"center\"\n" +
|
||||
" filename: \"myPDF.pdf\"\n" +
|
||||
" }\n" +
|
||||
"});",
|
||||
},
|
||||
{
|
||||
field: "createViewSVG",
|
||||
code: "async createViewSVG({withBackground?: boolean, theme?: 'light' | 'dark', frameRendering?: FrameRenderingOptions, padding?: number, selectedOnly?: boolean, skipInliningFonts?: boolean, embedScene?: boolean}): Promise<SVGSVGElement>",
|
||||
desc: "Creates an SVG representation of the current view with specified options.\n" +
|
||||
"\n" +
|
||||
"@param {Object} options - The options for creating the SVG.\n" +
|
||||
"@param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.\n" +
|
||||
"@param {\"light\" | \"dark\"} [options.theme] - The theme to use for the SVG.\n" +
|
||||
"@param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.\n" +
|
||||
"@param {number} [options.padding] - The padding to apply around the SVG.\n" +
|
||||
"@param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.\n" +
|
||||
"@param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.\n" +
|
||||
"@param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.\n" +
|
||||
"@returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} FrameRenderingOptions\n" +
|
||||
"@property {boolean} enabled - Whether frame rendering is enabled.\n" +
|
||||
"@property {boolean} name - Whether to include the name in the frame rendering.\n" +
|
||||
"@property {boolean} outline - Whether to include the outline in the frame rendering.\n" +
|
||||
"@property {boolean} clip - Whether to clip the frame rendering.\n",
|
||||
after: "({\n" +
|
||||
" withBackground: true,\n" +
|
||||
" theme: 'light',\n" +
|
||||
" frameRendering: { enabled: true, name: true, outline: true, clip: true },\n" +
|
||||
" padding: 10,\n" +
|
||||
" selectedOnly: false,\n" +
|
||||
" skipInliningFonts: false,\n" +
|
||||
" embedScene: false,\n" +
|
||||
"});",
|
||||
},
|
||||
{
|
||||
field: "getPagePDFDimensions",
|
||||
code: "getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions",
|
||||
desc: "Returns the dimensions of a standard page size in pixels.\n" +
|
||||
"\n" +
|
||||
"@param {PageSize} pageSize - The standard page size. Possible values are \"A0\", \"A1\", \"A2\", \"A3\", \"A4\", \"A5\", \"Letter\", \"Legal\", \"Tabloid\".\n" +
|
||||
"@param {PageOrientation} orientation - The orientation of the page. Possible values are \"portrait\" and \"landscape\".\n" +
|
||||
"@returns {PageDimensions} - An object containing the width and height of the page in pixels.\n" +
|
||||
"\n" +
|
||||
"@typedef {Object} PageDimensions\n" +
|
||||
"@property {number} width - The width of the page in pixels.\n" +
|
||||
"@property {number} height - The height of the page in pixels.\n" +
|
||||
"\n" +
|
||||
"@typedef {\"A0\" | \"A1\" | \"A2\" | \"A3\" | \"A4\" | \"A5\" | \"Letter\" | \"Legal\" | \"Tabloid\"} PageSize\n" +
|
||||
"\n" +
|
||||
"@typedef {\"portrait\" | \"landscape\"} PageOrientation",
|
||||
after: "(\"A4\", \"portrait\");",
|
||||
},
|
||||
{
|
||||
field: "createPNG",
|
||||
code: "async createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,padding?: number): Promise<any>;",
|
||||
@@ -500,6 +581,21 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "If set Excalidraw will call this function onDrop events.\nA return of true will stop the default onDrop processing in Excalidraw.\n\ndraggable is the Obsidian draggable object\nfiles is the array of dropped files\nexcalidrawFile is the file receiving the drop event\nview is the excalidraw view receiving the drop.\npointerPosition is the pointer position on canvas at the time of drop.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "onImageFilePathHook",
|
||||
code: `onImageFilePathHook: (data: {currentImageName: string; drawingFilePath: string;}): string;`,
|
||||
desc: "If set, this callback is triggered when an image is being saved in Excalidraw.\n"
|
||||
+ "You can use this callback to customize the naming and path of pasted images to avoid\n"
|
||||
+ 'default names like "Pasted image 123147170.png" being saved in the attachments folder,\n'
|
||||
+ "and instead use more meaningful names based on the Excalidraw file or other criteria,\n"
|
||||
+ "plus save the image in a different folder.\n\n"
|
||||
+ "If the function returns null or undefined, the normal Excalidraw operation will continue\n"
|
||||
+ "with the excalidraw generated name and default path.\n"
|
||||
+ "If a filepath is returned, that will be used. Include the full Vault filepath and filename\n"
|
||||
+ "with the file extension.\n"
|
||||
+ "The currentImageName is the name of the image generated by excalidraw or provided during paste.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "mostRecentMarkdownSVG",
|
||||
code: "mostRecentMarkdownSVG: SVGSVGElement;",
|
||||
|
||||
@@ -704,7 +704,7 @@ export class EmbeddedFilesLoader {
|
||||
}
|
||||
if (!excalidrawData.getEquation(id).isLoaded) {
|
||||
const latex = equation.latex;
|
||||
const data = await tex2dataURL(latex, 4, this.plugin.app);
|
||||
const data = await tex2dataURL(latex, 4, this.plugin);
|
||||
if (data) {
|
||||
const fileData = {
|
||||
mimeType: data.mimeType,
|
||||
|
||||
@@ -42,6 +42,8 @@ import {
|
||||
wrapTextAtCharLength,
|
||||
arrayToMap,
|
||||
addAppendUpdateCustomData,
|
||||
getSVG,
|
||||
getWithBackground,
|
||||
} from "src/utils/utils";
|
||||
import { getAttachmentsFolderAndFilePath, getExcalidrawViews, getLeaf, getNewOrAdjacentLeaf, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "src/utils/obsidianUtils";
|
||||
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
@@ -83,6 +85,8 @@ import { ExcalidrawLib } from "../types/excalidrawLib";
|
||||
import { GlobalPoint } from "@zsviczian/excalidraw/types/math/types";
|
||||
import { AddImageOptions, ImageInfo, SVGColorInfo } from "src/types/excalidrawAutomateTypes";
|
||||
import { _measureText, cloneElement, createPNG, createSVG, errorMessage, filterColorMap, getEmbeddedFileForImageElment, getFontFamily, getLineBox, getTemplate, isColorStringTransparent, isSVGColorInfo, mergeColorMapIntoSVGColorInfo, normalizeLinePoints, repositionElementsToCursor, svgColorInfoToColorMap, updateOrAddSVGColorInfo, verifyMinimumPluginVersion } from "src/utils/excalidrawAutomateUtils";
|
||||
import { exportToPDF, getMarginValue, getPageDimensions, PageDimensions, PageOrientation, PageSize, PDFExportScale, PDFPageProperties } from "src/utils/exportUtils";
|
||||
import { FrameRenderingOptions } from "src/types/utilTypes";
|
||||
|
||||
extendPlugins([
|
||||
HarmonyPlugin,
|
||||
@@ -884,7 +888,7 @@ export class ExcalidrawAutomate {
|
||||
Object.keys(this.imagesDict).forEach((key: FileId)=> {
|
||||
const item = this.imagesDict[key];
|
||||
if(item.latex) {
|
||||
outString += `${key}: $$${item.latex}$$\n\n`;
|
||||
outString += `${key}: $$${item.latex.trim()}$$\n\n`;
|
||||
} else {
|
||||
if(item.file) {
|
||||
if(item.file instanceof TFile) {
|
||||
@@ -930,6 +934,140 @@ export class ExcalidrawAutomate {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the dimensions of a standard page size in pixels.
|
||||
*
|
||||
* @param {PageSize} pageSize - The standard page size. Possible values are "A0", "A1", "A2", "A3", "A4", "A5", "Letter", "Legal", "Tabloid".
|
||||
* @param {PageOrientation} orientation - The orientation of the page. Possible values are "portrait" and "landscape".
|
||||
* @returns {PageDimensions} - An object containing the width and height of the page in pixels.
|
||||
*
|
||||
* @typedef {Object} PageDimensions
|
||||
* @property {number} width - The width of the page in pixels.
|
||||
* @property {number} height - The height of the page in pixels.
|
||||
*
|
||||
* @example
|
||||
* const dimensions = getPageDimensions("A4", "portrait");
|
||||
* console.log(dimensions); // { width: 794.56, height: 1122.56 }
|
||||
*/
|
||||
getPagePDFDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions {
|
||||
return getPageDimensions(pageSize, orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PDF from the provided SVG elements with specified scaling and page properties.
|
||||
*
|
||||
* @param {Object} params - The parameters for creating the PDF.
|
||||
* @param {SVGSVGElement[]} params.SVG - An array of SVG elements to be included in the PDF.
|
||||
* @param {PDFExportScale} [params.scale={ fitToPage: 1, zoom: 1 }] - The scaling options for the SVG elements.
|
||||
* @param {PDFPageProperties} [params.pageProps] - The properties for the PDF pages.
|
||||
* @returns {Promise<ArrayBuffer>} - A promise that resolves to an ArrayBuffer containing the PDF data.
|
||||
*
|
||||
* @example
|
||||
* const pdfData = await createToPDF({
|
||||
* SVG: [svgElement1, svgElement2],
|
||||
* scale: { fitToPage: 1 },
|
||||
* pageProps: {
|
||||
* dimensions: { width: 794.56, height: 1122.56 },
|
||||
* backgroundColor: "#ffffff",
|
||||
* margin: { left: 20, right: 20, top: 20, bottom: 20 },
|
||||
* alignment: "center",
|
||||
* }
|
||||
* filename: "example.pdf",
|
||||
* });
|
||||
*/
|
||||
async createPDF({
|
||||
SVG,
|
||||
scale = { fitToPage: 1, zoom: 1 },
|
||||
pageProps,
|
||||
filename,
|
||||
}: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale?: PDFExportScale;
|
||||
pageProps?: PDFPageProperties;
|
||||
filename: string;
|
||||
}): Promise<void> {
|
||||
if(!pageProps) {
|
||||
pageProps = {
|
||||
alignment: this.plugin.settings.pdfSettings.alignment,
|
||||
margin: getMarginValue(this.plugin.settings.pdfSettings.margin),
|
||||
};
|
||||
}
|
||||
|
||||
if(!pageProps.dimensions) {
|
||||
pageProps.dimensions = getPageDimensions(
|
||||
this.plugin.settings.pdfSettings.pageSize,
|
||||
this.plugin.settings.pdfSettings.pageOrientation
|
||||
)
|
||||
}
|
||||
if(!pageProps.backgroundColor) {
|
||||
pageProps.backgroundColor = "#ffffff";
|
||||
}
|
||||
|
||||
await exportToPDF({SVG, scale, pageProps, filename});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SVG representation of the current view.
|
||||
*
|
||||
* @param {Object} options - The options for creating the SVG.
|
||||
* @param {boolean} [options.withBackground=true] - Whether to include the background in the SVG.
|
||||
* @param {"light" | "dark"} [options.theme] - The theme to use for the SVG.
|
||||
* @param {FrameRenderingOptions} [options.frameRendering={enabled: true, name: true, outline: true, clip: true}] - The frame rendering options.
|
||||
* @param {number} [options.padding] - The padding to apply around the SVG.
|
||||
* @param {boolean} [options.selectedOnly=false] - Whether to include only the selected elements in the SVG.
|
||||
* @param {boolean} [options.skipInliningFonts=false] - Whether to skip inlining fonts in the SVG.
|
||||
* @param {boolean} [options.embedScene=false] - Whether to embed the scene in the SVG.
|
||||
* @returns {Promise<SVGSVGElement>} A promise that resolves to the SVG element.
|
||||
*/
|
||||
async createViewSVG({
|
||||
withBackground = true,
|
||||
theme,
|
||||
frameRendering = {enabled: true, name: true, outline: true, clip: true},
|
||||
padding,
|
||||
selectedOnly = false,
|
||||
skipInliningFonts = false,
|
||||
embedScene = false,
|
||||
} : {
|
||||
withBackground?: boolean,
|
||||
theme?: "light" | "dark",
|
||||
frameRendering?: FrameRenderingOptions,
|
||||
padding?: number,
|
||||
selectedOnly?: boolean,
|
||||
skipInliningFonts?: boolean,
|
||||
embedScene?: boolean,
|
||||
}): Promise<SVGSVGElement> {
|
||||
if(!this.targetView || !this.targetView.file || !this.targetView._loaded) {
|
||||
console.log("No view loaded");
|
||||
return;
|
||||
}
|
||||
const view = this.targetView;
|
||||
const scene = this.targetView.getScene(selectedOnly);
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: view.getViewExportWithBackground(withBackground),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, view.file),
|
||||
skipInliningFonts,
|
||||
frameRendering,
|
||||
};
|
||||
|
||||
return await getSVG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: view.getViewExportTheme(theme),
|
||||
exportEmbedScene: view.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
view.getViewExportPadding(padding),
|
||||
view.file,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SVG image from the ExcalidrawAutomate elements and the template provided.
|
||||
* @param {string} [templatePath] - The template path to use for the SVG.
|
||||
@@ -1776,7 +1914,7 @@ export class ExcalidrawAutomate {
|
||||
*/
|
||||
async addLaTex(topX: number, topY: number, tex: string): Promise<string> {
|
||||
const id = nanoid();
|
||||
const image = await tex2dataURL(tex, 4, this.plugin.app);
|
||||
const image = await tex2dataURL(tex, 4, this.plugin);
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
@@ -1818,7 +1956,7 @@ export class ExcalidrawAutomate {
|
||||
created: number;
|
||||
size: { height: number; width: number };
|
||||
}> {
|
||||
return await tex2dataURL(tex,scale, this.plugin.app);
|
||||
return await tex2dataURL(tex,scale, this.plugin);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,10 +52,11 @@ import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "..
|
||||
import { DEBUGGING, debug } from "../utils/debugHelper";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
|
||||
import { updateElementIdsInScene } from "../utils/excalidrawSceneUtils";
|
||||
import { getNewUniqueFilepath, splitFolderAndFilename } from "../utils/fileUtils";
|
||||
import { checkAndCreateFolder, getNewUniqueFilepath, splitFolderAndFilename } from "../utils/fileUtils";
|
||||
import { t } from "../lang/helpers";
|
||||
import { displayFontMessage } from "../utils/excalidrawViewUtils";
|
||||
import { getPDFRect } from "../utils/PDFUtils";
|
||||
import { create } from "domain";
|
||||
|
||||
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
|
||||
|
||||
@@ -1461,7 +1462,7 @@ export class ExcalidrawData {
|
||||
: "";
|
||||
if (this.equations.size > 0) {
|
||||
for (const key of this.equations.keys()) {
|
||||
outString += `${key}: $$${this.equations.get(key).latex}$$\n\n`;
|
||||
outString += `${key}: $$${this.equations.get(key).latex.trim()}$$\n\n`;
|
||||
}
|
||||
}
|
||||
if (this.files.size > 0) {
|
||||
@@ -1558,6 +1559,7 @@ export class ExcalidrawData {
|
||||
let filepath:string;
|
||||
if(hookFilepath) {
|
||||
const {folderpath, filename} = splitFolderAndFilename(hookFilepath);
|
||||
await checkAndCreateFolder(folderpath);
|
||||
filepath = getNewUniqueFilepath(this.app.vault,filename,folderpath);
|
||||
} else {
|
||||
const x = await getAttachmentsFolderAndFilePath(this.app, this.file.path, fname);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DataURL } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import ExcalidrawView from "../view/ExcalidrawView";
|
||||
import { FileData, MimeType } from "./EmbeddedFileLoader";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { App } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
|
||||
declare const loadMathjaxToSVG: Function;
|
||||
let mathjaxLoaded = false;
|
||||
@@ -52,7 +52,7 @@ export const updateEquation = async (
|
||||
export async function tex2dataURL(
|
||||
tex: string,
|
||||
scale: number = 4,
|
||||
app: App,
|
||||
plugin: ExcalidrawPlugin,
|
||||
): Promise<{
|
||||
mimeType: MimeType;
|
||||
fileId: FileId;
|
||||
@@ -61,7 +61,7 @@ export async function tex2dataURL(
|
||||
size: { height: number; width: number };
|
||||
}> {
|
||||
await loadMathJax();
|
||||
return tex2dataURLExternal(tex, scale, app);
|
||||
return tex2dataURLExternal(tex, scale, plugin);
|
||||
}
|
||||
|
||||
export const clearMathJaxVariables = () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ function createWorkerBlob(jsCode:string) {
|
||||
const workerCode = `
|
||||
var LZString=function(){var r=String.fromCharCode,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",e={};function t(r,o){if(!e[r]){e[r]={};for(var n=0;n<r.length;n++)e[r][r.charAt(n)]=n}return e[r][o]}var i={compressToBase64:function(r){if(null==r)return"";var n=i._compress(r,6,function(r){return o.charAt(r)});switch(n.length%4){default:case 0:return n;case 1:return n+"===";case 2:return n+"==";case 3:return n+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(n){return t(o,r.charAt(n))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(r){return null==r?"":""==r?null:i._decompress(r.length,16384,function(o){return r.charCodeAt(o)-32})},compressToUint8Array:function(r){for(var o=i.compress(r),n=new Uint8Array(2*o.length),e=0,t=o.length;e<t;e++){var s=o.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null==o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;e<t;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(r){return null==r?"":i._compress(r,6,function(r){return n.charAt(r)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(o){return t(n,r.charAt(o))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(r,o,n){if(null==r)return"";var e,t,i,s={},u={},a="",p="",c="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<r.length;i+=1)if(a=r.charAt(i),Object.prototype.hasOwnProperty.call(s,a)||(s[a]=f++,u[a]=!0),p=c+a,Object.prototype.hasOwnProperty.call(s,p))c=p;else{if(Object.prototype.hasOwnProperty.call(u,c)){if(c.charCodeAt(0)<256){for(e=0;e<h;e++)m<<=1,v==o-1?(v=0,d.push(n(m)),m=0):v++;for(t=c.charCodeAt(0),e=0;e<8;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;e<h;e++)m=m<<1|t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=c.charCodeAt(0),e=0;e<16;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}0==--l&&(l=Math.pow(2,h),h++),delete u[c]}else for(t=s[c],e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;0==--l&&(l=Math.pow(2,h),h++),s[p]=f++,c=String(a)}if(""!==c){if(Object.prototype.hasOwnProperty.call(u,c)){if(c.charCodeAt(0)<256){for(e=0;e<h;e++)m<<=1,v==o-1?(v=0,d.push(n(m)),m=0):v++;for(t=c.charCodeAt(0),e=0;e<8;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;e<h;e++)m=m<<1|t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=c.charCodeAt(0),e=0;e<16;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}0==--l&&(l=Math.pow(2,h),h++),delete u[c]}else for(t=s[c],e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;0==--l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;e<h;e++)m=m<<1|1&t,v==o-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==o-1){d.push(n(m));break}v++}return d.join("")},decompress:function(r){return null==r?"":""==r?null:i._decompress(r.length,32768,function(o){return r.charCodeAt(o)})},_decompress:function(o,n,e){var t,i,s,u,a,p,c,l=[],f=4,h=4,d=3,m="",v=[],g={val:e(0),position:n,index:1};for(t=0;t<3;t+=1)l[t]=t;for(s=0,a=Math.pow(2,2),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;switch(s){case 0:for(s=0,a=Math.pow(2,8),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;c=r(s);break;case 1:for(s=0,a=Math.pow(2,16),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;c=r(s);break;case 2:return""}for(l[3]=c,i=c,v.push(c);;){if(g.index>o)return"";for(s=0,a=Math.pow(2,d),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;switch(c=s){case 0:for(s=0,a=Math.pow(2,8),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;l[h++]=r(s),c=h-1,f--;break;case 1:for(s=0,a=Math.pow(2,16),p=1;p!=a;)u=g.val&g.position,g.position>>=1,0==g.position&&(g.position=n,g.val=e(g.index++)),s|=(u>0?1:0)*p,p<<=1;l[h++]=r(s),c=h-1,f--;break;case 2:return v.join("")}if(0==f&&(f=Math.pow(2,d),d++),l[c])m=l[c];else{if(c!==h)return null;m=i+i.charAt(0)}v.push(m),l[h++]=i+m.charAt(0),i=m,0==--f&&(f=Math.pow(2,d),d++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString});
|
||||
self.onmessage = function(e) {
|
||||
const { data, action } = e.data;
|
||||
const { data, action, messageId } = e.data;
|
||||
try {
|
||||
switch (action) {
|
||||
case 'compress':
|
||||
@@ -22,7 +22,7 @@ self.onmessage = function(e) {
|
||||
for (let i = 0; i < compressed.length; i += chunkSize) {
|
||||
result += compressed.slice(i, i + chunkSize) + '\\n\\n';
|
||||
}
|
||||
self.postMessage({ compressed: result.trim() });
|
||||
self.postMessage({ messageId, compressed: result.trim() });
|
||||
break;
|
||||
case 'decompress':
|
||||
if (!data) throw new Error("No input string provided for decompression.");
|
||||
@@ -35,14 +35,13 @@ self.onmessage = function(e) {
|
||||
}
|
||||
}
|
||||
const decompressed = LZString.decompressFromBase64(cleanedData);
|
||||
self.postMessage({ decompressed });
|
||||
self.postMessage({ messageId, decompressed });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown action.");
|
||||
}
|
||||
} catch (error) {
|
||||
// Post the error message back to the main thread
|
||||
self.postMessage({ error: error.message });
|
||||
self.postMessage({ messageId, error: error.message });
|
||||
}
|
||||
};
|
||||
`;
|
||||
@@ -55,27 +54,48 @@ export function initCompressionWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runCompressionWorker(data:string, action: 'compress' | 'decompress'): Promise<string> {
|
||||
let messageCounter = 0;
|
||||
const pendingOperations = new Map<number, {
|
||||
resolve: (value: string) => void,
|
||||
reject: (reason: Error) => void
|
||||
}>();
|
||||
|
||||
export async function runCompressionWorker(data: string, action: 'compress' | 'decompress'): Promise<string> {
|
||||
const messageId = messageCounter++;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
worker.onmessage = function(e) {
|
||||
const { compressed, decompressed, error } = e.data;
|
||||
|
||||
if (error) {
|
||||
reject(new Error(error));
|
||||
} else if (compressed || decompressed) {
|
||||
resolve(compressed || decompressed);
|
||||
} else {
|
||||
reject(new Error('Unexpected response from worker'));
|
||||
}
|
||||
};
|
||||
// Store the promise handlers
|
||||
pendingOperations.set(messageId, { resolve, reject });
|
||||
|
||||
// Set up the worker's error handler
|
||||
worker.onerror = function(error) {
|
||||
reject(new Error(error.message));
|
||||
};
|
||||
// Set up worker message handler if not already set
|
||||
if (!worker.onmessage) {
|
||||
worker.onmessage = function(e) {
|
||||
const { messageId, compressed, decompressed, error } = e.data;
|
||||
const handlers = pendingOperations.get(messageId);
|
||||
|
||||
if (handlers) {
|
||||
if (error) {
|
||||
handlers.reject(new Error(error));
|
||||
} else if (compressed || decompressed) {
|
||||
handlers.resolve(compressed || decompressed);
|
||||
} else {
|
||||
handlers.reject(new Error('Unexpected response from worker'));
|
||||
}
|
||||
pendingOperations.delete(messageId);
|
||||
}
|
||||
};
|
||||
|
||||
// Post the message to the worker
|
||||
worker.postMessage({ data, action });
|
||||
worker.onerror = function(error) {
|
||||
// Handle any pending operations in case of worker error
|
||||
for (const [id, handlers] of pendingOperations) {
|
||||
handlers.reject(new Error(error.message));
|
||||
pendingOperations.delete(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Post message with ID
|
||||
worker.postMessage({ messageId, data, action });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,11 @@ export enum PreviewImageType {
|
||||
PNG = "PNG",
|
||||
SVGIMG = "SVGIMG",
|
||||
SVG = "SVG"
|
||||
}
|
||||
|
||||
export interface FrameRenderingOptions {
|
||||
enabled: boolean;
|
||||
name: boolean;
|
||||
outline: boolean;
|
||||
clip: boolean;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NonDeletedExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
|
||||
import { getParentOfClass } from "./obsidianUtils";
|
||||
import { App, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { App, Notice, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { getLinkParts } from "./utils";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
|
||||
@@ -62,8 +62,12 @@ export function setFileToLocalGraph(app: App, file: TFile) {
|
||||
app.workspace.iterateAllLeaves((l) => {
|
||||
if (l.view?.getViewType() === "localgraph") lgv = l.view;
|
||||
});
|
||||
if (lgv) {
|
||||
//@ts-ignore
|
||||
lgv.loadFile(file);
|
||||
try {
|
||||
if (lgv) {
|
||||
//@ts-ignore
|
||||
lgv.loadFile(file);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
507
src/utils/exportUtils.ts
Normal file
507
src/utils/exportUtils.ts
Normal file
@@ -0,0 +1,507 @@
|
||||
import { Notice } from 'obsidian';
|
||||
import { DEVICE } from 'src/constants/constants';
|
||||
import { t } from 'src/lang/helpers';
|
||||
|
||||
const DPI = 96;
|
||||
|
||||
export type PDFPageAlignment =
|
||||
| "center"
|
||||
| "top-left"
|
||||
| "top-center"
|
||||
| "top-right"
|
||||
| "bottom-left"
|
||||
| "bottom-center"
|
||||
| "bottom-right"
|
||||
| "center-left"
|
||||
| "center-right";
|
||||
export type PDFPageMarginString = "none" | "tiny" | "normal";
|
||||
|
||||
export interface PDFExportScale {
|
||||
fitToPage: number; // 0 means use zoom, >1 means fit to that many pages exactly
|
||||
zoom?: number;
|
||||
}
|
||||
|
||||
export interface PDFMargin {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
export interface PDFPageProperties {
|
||||
dimensions?: {width: number; height: number};
|
||||
backgroundColor?: string;
|
||||
margin: PDFMargin;
|
||||
alignment: PDFPageAlignment;
|
||||
}
|
||||
|
||||
export interface PageDimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export type PageOrientation = "portrait" | "landscape";
|
||||
|
||||
// All dimensions in pixels (pt)
|
||||
export const STANDARD_PAGE_SIZES = {
|
||||
A0: { width: 3179.52, height: 4494.96 }, // 33.11 × 46.81 inches
|
||||
A1: { width: 2245.76, height: 3179.52 }, // 23.39 × 33.11 inches
|
||||
A2: { width: 1587.76, height: 2245.76 }, // 16.54 × 23.39 inches
|
||||
A3: { width: 1122.56, height: 1587.76 }, // 11.69 × 16.54 inches
|
||||
A4: { width: 794.56, height: 1122.56 }, // 8.27 × 11.69 inches
|
||||
A5: { width: 559.37, height: 794.56 }, // 5.83 × 8.27 inches
|
||||
A6: { width: 397.28, height: 559.37 }, // 4.13 × 5.83 inches
|
||||
Legal: { width: 816, height: 1344 }, // 8.5 × 14 inches
|
||||
Letter: { width: 816, height: 1056 }, // 8.5 × 11 inches
|
||||
Tabloid: { width: 1056, height: 1632 }, // 11 × 17 inches
|
||||
Ledger: { width: 1632, height: 1056 } // 17 × 11 inches
|
||||
} as const;
|
||||
|
||||
export type PageSize = keyof typeof STANDARD_PAGE_SIZES;
|
||||
|
||||
//margins are in pixels
|
||||
export function getMarginValue(margin:PDFPageMarginString): PDFMargin {
|
||||
switch(margin) {
|
||||
case "none": return { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
case "tiny": return { left: 10, right: 10, top: 10, bottom: 10 };
|
||||
case "normal": return { left: 60, right: 60, top: 60, bottom: 60 };
|
||||
default: return { left: 60, right: 60, top: 60, bottom: 60 };
|
||||
}
|
||||
}
|
||||
|
||||
export function getPageDimensions(pageSize: PageSize, orientation: PageOrientation): PageDimensions {
|
||||
const dimensions = STANDARD_PAGE_SIZES[pageSize];
|
||||
return orientation === "portrait"
|
||||
? { width: dimensions.width, height: dimensions.height }
|
||||
: { width: dimensions.height, height: dimensions.width };
|
||||
}
|
||||
|
||||
// Electron IPC interfaces
|
||||
interface PrintToPDFOptions {
|
||||
includeName: boolean;
|
||||
pageSize: string | { width: number; height: number };
|
||||
landscape: boolean;
|
||||
margins: { top: number; left: number; right: number; bottom: number };
|
||||
scaleFactor: number;
|
||||
scale: number;
|
||||
open: boolean;
|
||||
filepath: string;
|
||||
}
|
||||
|
||||
interface SaveDialogOptions {
|
||||
defaultPath: string;
|
||||
filters: { name: string; extensions: string[] }[];
|
||||
properties: string[];
|
||||
}
|
||||
|
||||
interface SaveDialogReturnValue {
|
||||
canceled: boolean;
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
interface ElectronAPI {
|
||||
ipcRenderer: {
|
||||
send(channel: string, ...args: any[]): void;
|
||||
once(channel: string, func: (...args: any[]) => void): void;
|
||||
};
|
||||
remote: {
|
||||
dialog: {
|
||||
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
function getPageSizePixels(pageSize: PageSize | PageDimensions, landscape = false): PageDimensions {
|
||||
if (typeof pageSize === "object") return pageSize;
|
||||
|
||||
const pageDimensions = STANDARD_PAGE_SIZES[pageSize];
|
||||
if (!pageDimensions) {
|
||||
throw new Error(`Unsupported page size: ${pageSize}`);
|
||||
}
|
||||
|
||||
return landscape
|
||||
? { width: pageDimensions.height, height: pageDimensions.width }
|
||||
: { width: pageDimensions.width, height: pageDimensions.height };
|
||||
}
|
||||
|
||||
function getPageSize(pageSize: PageSize | PageDimensions): string | { width: number; height: number } {
|
||||
if (typeof pageSize === "string") return pageSize;
|
||||
|
||||
if (!pageSize || typeof pageSize !== "object" ||
|
||||
typeof pageSize.width !== "number" || typeof pageSize.height !== "number") {
|
||||
throw new Error("Invalid page dimensions");
|
||||
}
|
||||
|
||||
return {
|
||||
width: (pageSize.width / DPI),
|
||||
height: (pageSize.height / DPI)
|
||||
};
|
||||
}
|
||||
|
||||
async function getSavePath(defaultPath: string): Promise<string | undefined> {
|
||||
const result = await window.electron.remote.dialog.showSaveDialog({
|
||||
defaultPath,
|
||||
filters: [
|
||||
{ name: "PDF Files", extensions: ["pdf"] },
|
||||
{ name: "All Files", extensions: ["*"] }
|
||||
],
|
||||
properties: ["showOverwriteConfirmation"]
|
||||
});
|
||||
return result.filePath;
|
||||
}
|
||||
|
||||
async function printPdf(
|
||||
elementToPrint: HTMLElement,
|
||||
pdfPath: string,
|
||||
bgColor: string,
|
||||
pageSize: PageSize | PageDimensions,
|
||||
isLandscape: boolean,
|
||||
margins: { top: number; left: number; right: number; bottom: number }
|
||||
): Promise<void> {
|
||||
const styleTag = document.createElement('style');
|
||||
styleTag.textContent = `
|
||||
@media print {
|
||||
.print {
|
||||
background-color: ${bgColor} !important;
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
flex-direction: column !important;
|
||||
page-break-before: always;
|
||||
margin: 0px !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
const printDiv = document.body.createDiv('print');
|
||||
printDiv.style.top = "0";
|
||||
printDiv.style.left = "0";
|
||||
printDiv.style.display = "flex";
|
||||
printDiv.appendChild(elementToPrint);
|
||||
|
||||
const options: PrintToPDFOptions = {
|
||||
includeName: false,
|
||||
pageSize: getPageSize(pageSize),
|
||||
landscape: isLandscape,
|
||||
margins,
|
||||
scaleFactor: 100,
|
||||
scale: 1,
|
||||
open: true,
|
||||
filepath: pdfPath,
|
||||
};
|
||||
|
||||
try {
|
||||
await new Promise<void>((resolve) => {
|
||||
window.electron.ipcRenderer.once('print-to-pdf', resolve);
|
||||
window.electron.ipcRenderer.send('print-to-pdf', options);
|
||||
});
|
||||
} finally {
|
||||
printDiv.remove();
|
||||
styleTag.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function calculateDimensions(
|
||||
svg: SVGSVGElement,
|
||||
svgWidth: number,
|
||||
svgHeight: number,
|
||||
pageDim: PageDimensions,
|
||||
margin: PDFMargin,
|
||||
scale: PDFExportScale,
|
||||
alignment: PDFPageAlignment
|
||||
): {
|
||||
tiles: {
|
||||
viewBox: string,
|
||||
width: number,
|
||||
height: number,
|
||||
x: number,
|
||||
y: number
|
||||
}[],
|
||||
pages: number
|
||||
} {
|
||||
const viewBox = svg.getAttribute('viewBox')?.split(' ').map(Number) || [0, 0, svgWidth, svgHeight];
|
||||
const [viewBoxX, viewBoxY] = viewBox;
|
||||
|
||||
const availableWidth = pageDim.width - margin.left - margin.right;
|
||||
const availableHeight = pageDim.height - margin.top - margin.bottom;
|
||||
|
||||
// If fitToPage is specified, find optimal zoom using binary search
|
||||
if (scale.fitToPage > 0) {
|
||||
let low = 0;
|
||||
let high = 100;
|
||||
let bestZoom = 1;
|
||||
const tolerance = 0.000001;
|
||||
|
||||
while (high - low > tolerance) {
|
||||
const mid = (low + high) / 2;
|
||||
const scaledWidth = svgWidth * mid;
|
||||
const scaledHeight = svgHeight * mid;
|
||||
const pages = Math.ceil(scaledWidth / availableWidth) *
|
||||
Math.ceil(scaledHeight / availableHeight);
|
||||
|
||||
if (pages > scale.fitToPage) {
|
||||
high = mid;
|
||||
} else {
|
||||
bestZoom = mid;
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
scale.zoom = Math.round(bestZoom * 0.99999 * 1000000) / 1000000;
|
||||
}
|
||||
|
||||
const finalWidth = svgWidth * (scale.zoom || 1);
|
||||
const finalHeight = svgHeight * (scale.zoom || 1);
|
||||
|
||||
if (finalWidth <= availableWidth && finalHeight <= availableHeight) {
|
||||
// Content fits on one page
|
||||
const position = calculatePosition(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
pageDim.width,
|
||||
pageDim.height,
|
||||
margin,
|
||||
alignment
|
||||
);
|
||||
|
||||
return {
|
||||
tiles: [{
|
||||
viewBox: `${viewBoxX} ${viewBoxY} ${svgWidth} ${svgHeight}`,
|
||||
width: finalWidth,
|
||||
height: finalHeight,
|
||||
x: position.x,
|
||||
y: position.y
|
||||
}],
|
||||
pages: 1
|
||||
};
|
||||
}
|
||||
|
||||
// Content needs to be tiled
|
||||
const cols = Math.ceil(finalWidth / availableWidth);
|
||||
const rows = Math.ceil(finalHeight / availableHeight);
|
||||
|
||||
// Calculate total available space considering all margins
|
||||
const totalAvailableWidth = cols * availableWidth;
|
||||
const totalAvailableHeight = rows * availableHeight;
|
||||
|
||||
// Calculate global position in the total available space
|
||||
const globalPosition = calculatePosition(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
totalAvailableWidth,
|
||||
totalAvailableHeight,
|
||||
{
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0
|
||||
},
|
||||
alignment
|
||||
);
|
||||
|
||||
const tiles = [];
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
// Calculate where this tile intersects with the image in global space
|
||||
const tileGlobalX = col * availableWidth;
|
||||
const tileGlobalY = row * availableHeight;
|
||||
|
||||
// Calculate the intersection of the tile with the image
|
||||
const intersectX = Math.max(tileGlobalX, globalPosition.x);
|
||||
const intersectY = Math.max(tileGlobalY, globalPosition.y);
|
||||
const intersectRight = Math.min(tileGlobalX + availableWidth, globalPosition.x + finalWidth);
|
||||
const intersectBottom = Math.min(tileGlobalY + availableHeight, globalPosition.y + finalHeight);
|
||||
|
||||
// Calculate actual tile dimensions
|
||||
const scaledTileWidth = Math.max(0, intersectRight - intersectX);
|
||||
const scaledTileHeight = Math.max(0, intersectBottom - intersectY);
|
||||
|
||||
if (scaledTileWidth <= 0 || scaledTileHeight <= 0) continue;
|
||||
|
||||
// Calculate viewBox coordinates
|
||||
const tileX = (intersectX - globalPosition.x) / (scale.zoom || 1);
|
||||
const tileY = (intersectY - globalPosition.y) / (scale.zoom || 1);
|
||||
const tileWidth = scaledTileWidth / (scale.zoom || 1);
|
||||
const tileHeight = scaledTileHeight / (scale.zoom || 1);
|
||||
|
||||
let x = margin.left;
|
||||
let y = margin.top;
|
||||
|
||||
// Handle horizontal positioning
|
||||
const widthRatio = scaledTileWidth / availableWidth;
|
||||
if (widthRatio >= 0.99 && widthRatio <= 1.01) {
|
||||
x = margin.left;
|
||||
} else {
|
||||
if (alignment === "center" || alignment === "top-center" || alignment === "bottom-center") {
|
||||
if(col === 0) {
|
||||
x = margin.left + (availableWidth - scaledTileWidth);
|
||||
} else {
|
||||
x = margin.left;
|
||||
}
|
||||
} else if (alignment.endsWith('right')) {
|
||||
x = pageDim.width - margin.right - scaledTileWidth;
|
||||
} else {
|
||||
x = margin.left;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle vertical positioning
|
||||
const heightRatio = scaledTileHeight / availableHeight;
|
||||
if (heightRatio >= 0.99 && heightRatio <= 1.01) {
|
||||
y = margin.top;
|
||||
} else {
|
||||
if (alignment === "center" || alignment === "center-left" || alignment === "center-right") {
|
||||
if(row === 0) {
|
||||
y = margin.top + (availableHeight - scaledTileHeight);
|
||||
} else {
|
||||
y = margin.top;
|
||||
}
|
||||
} else if (alignment.startsWith('bottom')) {
|
||||
y = pageDim.height - margin.bottom - scaledTileHeight;
|
||||
} else {
|
||||
y = margin.top;
|
||||
}
|
||||
}
|
||||
|
||||
tiles.push({
|
||||
viewBox: `${tileX + viewBoxX} ${tileY + viewBoxY} ${tileWidth} ${tileHeight}`,
|
||||
width: scaledTileWidth,
|
||||
height: scaledTileHeight,
|
||||
x: x,
|
||||
y: y
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { tiles, pages: tiles.length };
|
||||
}
|
||||
|
||||
function calculatePosition(
|
||||
svgWidth: number,
|
||||
svgHeight: number,
|
||||
pageWidth: number,
|
||||
pageHeight: number,
|
||||
margin: PDFMargin,
|
||||
alignment: PDFPageAlignment,
|
||||
): {x: number, y: number} {
|
||||
const availableWidth = pageWidth - margin.left - margin.right;
|
||||
const availableHeight = pageHeight - margin.top - margin.bottom;
|
||||
|
||||
let x = margin.left;
|
||||
let y = margin.top;
|
||||
|
||||
// Handle horizontal alignment
|
||||
if (alignment === "center" || alignment === "top-center" || alignment === "bottom-center") {
|
||||
x = margin.left + (availableWidth - svgWidth) / 2;
|
||||
} else if (alignment.endsWith('right')) {
|
||||
x = margin.left + availableWidth - svgWidth;
|
||||
}
|
||||
|
||||
// Handle vertical alignment
|
||||
if (alignment === "center" || alignment === "center-left" || alignment === "center-right") {
|
||||
y = margin.top + (availableHeight - svgHeight) / 2;
|
||||
} else if (alignment.startsWith('bottom')) {
|
||||
y = margin.top + availableHeight - svgHeight;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
export async function exportToPDF({
|
||||
SVG,
|
||||
scale = { fitToPage: 1, zoom: 1 },
|
||||
pageProps,
|
||||
filename
|
||||
}: {
|
||||
SVG: SVGSVGElement[];
|
||||
scale: PDFExportScale;
|
||||
pageProps: PDFPageProperties;
|
||||
filename: string;
|
||||
}): Promise<void> {
|
||||
if (!DEVICE.isDesktop) {
|
||||
new Notice(t("PDF_EXPORT_DESKTOP_ONLY"));
|
||||
return;
|
||||
}
|
||||
|
||||
const savePath = await getSavePath(filename);
|
||||
if (!savePath) return;
|
||||
|
||||
const {width, height} = getPageSizePixels(pageProps.dimensions, false);
|
||||
const allPagesDiv = createDiv();
|
||||
allPagesDiv.style.width = "100%";
|
||||
allPagesDiv.style.height = "fit-content";
|
||||
|
||||
let j = 0;
|
||||
for (const svg of SVG) {
|
||||
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
|
||||
const svgHeight = parseFloat(svg.getAttribute('height') || '0');
|
||||
|
||||
const {tiles} = calculateDimensions(
|
||||
svg,
|
||||
svgWidth,
|
||||
svgHeight,
|
||||
pageProps.dimensions,
|
||||
pageProps.margin,
|
||||
scale,
|
||||
pageProps.alignment
|
||||
);
|
||||
|
||||
let i = 0;
|
||||
for (const tile of tiles) {
|
||||
const pageDiv = createDiv();
|
||||
pageDiv.style.width = `${width}px`;
|
||||
pageDiv.style.height = `${height}px`;
|
||||
pageDiv.style.display = "flex";
|
||||
pageDiv.style.justifyContent = "start";
|
||||
pageDiv.style.alignItems = "left";
|
||||
pageDiv.style.padding = `${pageProps.margin.top}px ${pageProps.margin.right}px ${pageProps.margin.bottom}px ${pageProps.margin.left}px`;
|
||||
|
||||
const clonedSVG = svg.cloneNode(true) as SVGSVGElement;
|
||||
clonedSVG.setAttribute('viewBox', tile.viewBox);
|
||||
clonedSVG.style.width = `${tile.width}px`;
|
||||
clonedSVG.style.height = `${tile.height}px`;
|
||||
clonedSVG.style.position = 'absolute';
|
||||
clonedSVG.style.left = `${tile.x}px`;
|
||||
clonedSVG.style.top = `${tile.y + (i+j)*height}px`;
|
||||
|
||||
pageDiv.appendChild(clonedSVG);
|
||||
allPagesDiv.appendChild(pageDiv);
|
||||
i++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_NOTICE"));
|
||||
try {
|
||||
await printPdf(
|
||||
allPagesDiv,
|
||||
savePath,
|
||||
pageProps.backgroundColor || "#ffffff",
|
||||
pageProps.dimensions,
|
||||
false,
|
||||
{ top: 0, right: 0, bottom: 0, left: 0 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to export to PDF: ", error);
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_ERROR"));
|
||||
}
|
||||
new Notice(t("EXPORTDIALOG_PDF_PROGRESS_DONE"));
|
||||
}
|
||||
|
||||
export async function exportSVGToClipboard(svg: SVGSVGElement) {
|
||||
try {
|
||||
const svgString = svg.outerHTML;
|
||||
await navigator.clipboard.writeText(svgString);
|
||||
} catch (error) {
|
||||
console.error("Failed to copy SVG to clipboard: ", error);
|
||||
}
|
||||
}
|
||||
@@ -290,6 +290,17 @@ export const blobToBase64 = async (blob: Blob): Promise<string> => {
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
export const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer): string => {
|
||||
const bytes = new Uint8Array(arrayBuffer);
|
||||
let binary = '';
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
|
||||
return btoa(binary);
|
||||
};
|
||||
|
||||
export const getPDFDoc = async (f: TFile): Promise<any> => {
|
||||
if(typeof window.pdfjsLib === "undefined") await loadPdfJs();
|
||||
return await window.pdfjsLib.getDocument(EXCALIDRAW_PLUGIN.app.vault.getResourcePath(f)).promise;
|
||||
@@ -482,3 +493,22 @@ export const hasExcalidrawEmbeddedImagesTreeChanged = (sourceFile: TFile, mtime:
|
||||
const fileList = getExcalidrawEmbeddedFilesFiletree(sourceFile, plugin);
|
||||
return fileList.some(f=>f.stat.mtime > mtime);
|
||||
}
|
||||
|
||||
export async function createOrOverwriteFile(app: App, path: string, content: string | ArrayBuffer): Promise<TFile> {
|
||||
const file = app.vault.getAbstractFileByPath(normalizePath(path));
|
||||
if(content instanceof ArrayBuffer) {
|
||||
if(file && file instanceof TFile) {
|
||||
await app.vault.modifyBinary(file, content);
|
||||
return file;
|
||||
} else {
|
||||
return await app.vault.createBinary(path, content);
|
||||
}
|
||||
}
|
||||
|
||||
if (file && file instanceof TFile) {
|
||||
await app.vault.modify(file, content);
|
||||
return file;
|
||||
} else {
|
||||
return await app.vault.create(path, content);
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,7 @@ export async function getSVG (
|
||||
: {},
|
||||
},
|
||||
files: scene.files,
|
||||
exportPadding: exportSettings.frameRendering ? 0 : padding,
|
||||
exportPadding: exportSettings.frameRendering?.enabled ? 0 : padding,
|
||||
exportingFrame: null,
|
||||
renderEmbeddables: true,
|
||||
skipInliningFonts: exportSettings.skipInliningFonts,
|
||||
@@ -365,7 +365,7 @@ export async function getPNG (
|
||||
: {},
|
||||
},
|
||||
files: filterFiles(scene.files),
|
||||
exportPadding: exportSettings.frameRendering ? 0 : padding,
|
||||
exportPadding: exportSettings.frameRendering?.enabled ? 0 : padding,
|
||||
mimeType: "image/png",
|
||||
getDimensions: (width: number, height: number) => ({
|
||||
width: width * scale,
|
||||
|
||||
@@ -76,7 +76,9 @@ import {
|
||||
getExcalidrawMarkdownHeaderSection,
|
||||
} from "../shared/ExcalidrawData";
|
||||
import {
|
||||
arrayBufferToBase64,
|
||||
checkAndCreateFolder,
|
||||
createOrOverwriteFile,
|
||||
download,
|
||||
getDataURLFromURL,
|
||||
getIMGFilename,
|
||||
@@ -149,6 +151,9 @@ import { getPDFCropRect } from "../utils/PDFUtils";
|
||||
import { Position, ViewSemaphores } from "../types/excalidrawViewTypes";
|
||||
import { DropManager } from "./managers/DropManager";
|
||||
import { ImageInfo } from "src/types/excalidrawAutomateTypes";
|
||||
import { exportToPDF, getMarginValue, getPageDimensions, PageOrientation, PageSize } from "src/utils/exportUtils";
|
||||
import { create } from "domain";
|
||||
import { FrameRenderingOptions } from "src/types/utilTypes";
|
||||
|
||||
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
|
||||
const PREVENT_RELOAD_TIMEOUT = 2000;
|
||||
@@ -182,12 +187,7 @@ export interface ExportSettings {
|
||||
withBackground: boolean;
|
||||
withTheme: boolean;
|
||||
isMask: boolean;
|
||||
frameRendering?: { //optional, overrides relevant appState settings for rendering the frame
|
||||
enabled: boolean;
|
||||
name: boolean;
|
||||
outline: boolean;
|
||||
clip: boolean;
|
||||
};
|
||||
frameRendering?: FrameRenderingOptions; //optional, overrides relevant appState settings for rendering the frame
|
||||
skipInliningFonts?: boolean;
|
||||
}
|
||||
|
||||
@@ -471,36 +471,75 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
);
|
||||
}
|
||||
|
||||
public getViewExportTheme(theme?:string):string {
|
||||
if(theme) return theme;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.theme : getExportTheme(this.plugin, this.file, this.excalidrawAPI.getAppState().theme)
|
||||
}
|
||||
|
||||
public getViewExportEmbedScene(embedScene?:boolean):boolean {
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene;
|
||||
}
|
||||
|
||||
public getViewExportPadding(padding?: number): number {
|
||||
if(typeof padding !== "undefined") return padding;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.padding : getExportPadding(this.plugin, this.file)
|
||||
}
|
||||
|
||||
public getViewExportScale(scale?: number): number {
|
||||
if(typeof scale !== "undefined") return scale;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? ed.scale : getPNGScale(this.plugin, this.file);
|
||||
}
|
||||
|
||||
public getViewExportWithBackground(withBackground?:boolean) {
|
||||
if(typeof withBackground !== "undefined") return withBackground;
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
}
|
||||
const ed = this.exportDialog;
|
||||
return ed ? !ed.transparent : getWithBackground(this.plugin, this.file)
|
||||
}
|
||||
|
||||
public async svg(scene: any, theme?:string, embedScene?: boolean, embedFont: boolean = false): Promise<SVGSVGElement> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.svg, "ExcalidrawView.svg", scene, theme, embedScene);
|
||||
const ed = this.exportDialog;
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: ed ? !ed.transparent : getWithBackground(this.plugin, this.file),
|
||||
withBackground: this.getViewExportWithBackground(),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, this.file),
|
||||
skipInliningFonts: !embedFont,
|
||||
};
|
||||
|
||||
if(typeof embedScene === "undefined") {
|
||||
embedScene = shouldEmbedScene(this.plugin, this.file);
|
||||
}
|
||||
|
||||
return await getSVG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: theme ?? (ed ? ed.theme : getExportTheme(this.plugin, this.file, scene.appState.theme)),
|
||||
exportEmbedScene: typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene,
|
||||
theme: this.getViewExportTheme(theme),
|
||||
exportEmbedScene: this.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
ed ? ed.padding : getExportPadding(this.plugin, this.file),
|
||||
this.getViewExportPadding(),
|
||||
this.file,
|
||||
);
|
||||
}
|
||||
@@ -515,19 +554,13 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
}
|
||||
|
||||
const exportImage = async (filepath:string, theme?:string) => {
|
||||
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
|
||||
|
||||
const svg = await this.svg(scene,theme, embedScene, true);
|
||||
if (!svg) {
|
||||
return;
|
||||
}
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2026
|
||||
const svgString = svg.outerHTML;
|
||||
if (file && file instanceof TFile) {
|
||||
await this.app.vault.modify(file, svgString);
|
||||
} else {
|
||||
await this.app.vault.create(filepath, svgString);
|
||||
}
|
||||
await createOrOverwriteFile(this.app, filepath, svgString);
|
||||
}
|
||||
|
||||
if(this.plugin.settings.autoExportLightAndDark) {
|
||||
@@ -555,36 +588,78 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
);
|
||||
}
|
||||
|
||||
public async getSVG(embedScene?: boolean, selectedOnly?: boolean):Promise<SVGSVGElement> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.getSVG, "ExcalidrawView.getSVG", embedScene, selectedOnly);
|
||||
if (!this.excalidrawAPI || !this.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = await this.svg(this.getScene(selectedOnly),undefined,embedScene, true);
|
||||
if (!svg) {
|
||||
return;
|
||||
}
|
||||
return svg;
|
||||
}
|
||||
|
||||
public async exportPDF(
|
||||
selectedOnly?: boolean,
|
||||
pageSize: PageSize = "A4",
|
||||
orientation: PageOrientation = "portrait"
|
||||
): Promise<void> {
|
||||
if (!this.excalidrawAPI || !this.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = await this.svg(
|
||||
this.getScene(selectedOnly),
|
||||
undefined,
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
if (!svg) {
|
||||
return;
|
||||
}
|
||||
|
||||
exportToPDF({
|
||||
SVG: [svg],
|
||||
scale: {
|
||||
zoom: this.exportDialog.scale,
|
||||
fitToPage: this.exportDialog.fitToPage
|
||||
},
|
||||
pageProps: {
|
||||
dimensions: getPageDimensions(pageSize, orientation),
|
||||
backgroundColor: this.exportDialog.getPaperColor(),
|
||||
margin: getMarginValue(this.exportDialog.margin),
|
||||
alignment: this.exportDialog.alignment,
|
||||
},
|
||||
filename: this.file.basename,
|
||||
});
|
||||
}
|
||||
|
||||
public async png(scene: any, theme?:string, embedScene?: boolean): Promise<Blob> {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.png, "ExcalidrawView.png", scene, theme, embedScene);
|
||||
const ed = this.exportDialog;
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: ed ? !ed.transparent : getWithBackground(this.plugin, this.file),
|
||||
withBackground: this.getViewExportWithBackground(),
|
||||
withTheme: true,
|
||||
isMask: isMaskFile(this.plugin, this.file),
|
||||
};
|
||||
|
||||
if(typeof embedScene === "undefined") {
|
||||
embedScene = shouldEmbedScene(this.plugin, this.file);
|
||||
}
|
||||
|
||||
return await getPNG(
|
||||
{
|
||||
...scene,
|
||||
...{
|
||||
appState: {
|
||||
...scene.appState,
|
||||
theme: theme ?? (ed ? ed.theme : getExportTheme(this.plugin, this.file, scene.appState.theme)),
|
||||
exportEmbedScene: typeof embedScene === "undefined"
|
||||
? (ed ? ed.embedScene : false)
|
||||
: embedScene,
|
||||
theme: this.getViewExportTheme(theme),
|
||||
exportEmbedScene: this.getViewExportEmbedScene(embedScene),
|
||||
},
|
||||
},
|
||||
},
|
||||
exportSettings,
|
||||
ed ? ed.padding : getExportPadding(this.plugin, this.file),
|
||||
ed ? ed.scale : getPNGScale(this.plugin, this.file),
|
||||
this.getViewExportPadding(),
|
||||
this.getViewExportScale(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -598,17 +673,11 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
}
|
||||
|
||||
const exportImage = async (filepath:string, theme?:string) => {
|
||||
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
|
||||
|
||||
const png = await this.png(scene, theme, embedScene);
|
||||
if (!png) {
|
||||
return;
|
||||
}
|
||||
if (file && file instanceof TFile) {
|
||||
await this.app.vault.modifyBinary(file, await png.arrayBuffer());
|
||||
} else {
|
||||
await this.app.vault.createBinary(filepath, await png.arrayBuffer());
|
||||
}
|
||||
await createOrOverwriteFile(this.app, filepath, await png.arrayBuffer());
|
||||
}
|
||||
|
||||
if(this.plugin.settings.autoExportLightAndDark) {
|
||||
@@ -1509,6 +1578,9 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
this.packages = this.plugin.getPackage(this.ownerWindow);
|
||||
|
||||
if(DEVICE.isDesktop && !apiMissing) {
|
||||
if(this.ownerWindow !== window) {
|
||||
this.plugin.initializeFonts();
|
||||
}
|
||||
this.destroyers.push(
|
||||
//this.containerEl.onWindowMigrated(this.leaf.rebuildView.bind(this))
|
||||
this.containerEl.onWindowMigrated(async() => {
|
||||
@@ -3050,7 +3122,6 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
}
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
this.exportDialog.createForm();
|
||||
}
|
||||
this.exportDialog.open();
|
||||
})
|
||||
@@ -4847,7 +4918,6 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
||||
private actionOpenExportImageDialog() {
|
||||
if(!this.exportDialog) {
|
||||
this.exportDialog = new ExportDialog(this.plugin, this,this.file);
|
||||
this.exportDialog.createForm();
|
||||
}
|
||||
this.exportDialog.open();
|
||||
}
|
||||
|
||||
69
styles.css
69
styles.css
@@ -80,6 +80,11 @@ button.ToolIcon_type_button[title="Export"] {
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
.excalidraw-export-button {
|
||||
width: 9em;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-buttons-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -87,6 +92,13 @@ button.ToolIcon_type_button[title="Export"] {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.excalidraw-export-buttons-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
li[data-testid] {
|
||||
border: 0 !important;
|
||||
margin: 0 !important;
|
||||
@@ -104,6 +116,7 @@ li[data-testid] {
|
||||
border: 0 !important;
|
||||
box-shadow: 0 !important;
|
||||
background-color: transparent !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
.excalidraw .popover {
|
||||
@@ -324,6 +337,18 @@ label.color-input-container > input {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.excalidraw .App-toolbar-content .dropdown-menu {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.excalidraw .selected-shape-actions .panelColumn,
|
||||
.excalidraw .App-mobile-menu .panelColumn
|
||||
{
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.excalidraw .panelColumn .buttonList {
|
||||
max-width: 13rem;
|
||||
}
|
||||
@@ -365,11 +390,14 @@ div.excalidraw-draginfo {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
/*Arrow Picker Popover Overflow*/
|
||||
/*
|
||||
.excalidraw .selected-shape-actions .Island.App-menu__left,
|
||||
.excalidraw .selected-shape-actions .Island.App-menu__left .panelColumn,
|
||||
.excalidraw .Island .App-mobile-menu,
|
||||
.excalidraw .Island.App-menu__left {
|
||||
/*Arrow Picker Popover Overflow*/
|
||||
.excalidraw .Island .App-mobile-menu .panelColumn {
|
||||
overflow: visible;
|
||||
}
|
||||
}*/
|
||||
|
||||
.excalidraw__embeddable-container .view-header {
|
||||
display: none !important;
|
||||
@@ -672,3 +700,38 @@ textarea.excalidraw-wysiwyg, .excalidraw input {
|
||||
background-color: var(--background-modifier-hover);
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.excalidraw-release .nav-buttons-container {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.excalidraw-release .nav-button {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.excalidraw-release .nav-button.is-active {
|
||||
border-bottom: 2px solid var(--interactive-accent);
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.excalidraw .picker-content {
|
||||
grid-gap: 0.2rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .picker-content button.picker-option{
|
||||
width: 1.85rem !important;
|
||||
height: 1.85rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .picker {
|
||||
padding: 0.2rem !important;
|
||||
}
|
||||
|
||||
.excalidraw .context-menu {
|
||||
height: fit-content;
|
||||
}
|
||||
Reference in New Issue
Block a user