Compare commits

...

7 Commits

Author SHA1 Message Date
zsviczian
99a7e74825 Updated rollup to work with React 19
Resolved mobile tabs not visible (hack) due to Obsidain 18.x changes
2025-03-09 11:29:19 +01:00
zsviczian
304cef4d7d Merge pull request #2223 from dmscode/master
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
Update zh-cn.ts to 1215266
2025-03-08 09:28:03 +01:00
稻米鼠
7dba9b88dc Merge branch 'zsviczian:master' into master 2025-02-05 07:46:30 +08:00
zsviczian
bfb5de1525 2.8.3
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
2025-02-04 21:25:13 +01:00
dmscode
e6fca1a2d0 Update zh-cn.ts to a9cad8c 2025-01-25 08:09:25 +08:00
稻米鼠
a9cad8c9f1 Merge branch 'zsviczian:master' into master 2025-01-25 07:56:55 +08:00
dmscode
01a88a25a2 Update zh-cn.ts to 1215266 2025-01-23 08:33:03 +08:00
24 changed files with 1497 additions and 1444 deletions

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.8.2",
"version": "2.8.3",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.8.2",
"version": "2.8.3",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

2680
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,15 +21,15 @@
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.6-31",
"@zsviczian/excalidraw": "0.18.0-0",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",
"gl-matrix": "^3.4.3",
"js-yaml": "^4.1.0",
"lucide-react": "^0.263.1",
"lucide-react": "^0.479.0",
"mathjax-full": "^3.2.2",
"monkey-around": "^2.3.0",
"nanoid": "^4.0.2",

View File

@@ -24,6 +24,29 @@ const isProd = (process.env.NODE_ENV === "production");
const isLib = (process.env.NODE_ENV === "lib");
console.log(`Running: ${process.env.NODE_ENV}; isProd: ${isProd}; isLib: ${isLib}`);
// Excalidraw React 19 compatiblity shim
// Create JSX runtime compatibility layer
const jsxRuntimeShim = `
const jsx = (type, props, key) => {
return React.createElement(type, props);
};
const jsxs = (type, props, key) => {
return React.createElement(type, props);
};
const Fragment = React.Fragment;
React.jsx = jsx;
React.jsxs = jsxs;
React.Fragment = Fragment;
React.jsxRuntime = { jsx, jsxs, Fragment };
window.__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime__ = { jsx, jsxs, Fragment };
window.__WEBPACK_EXTERNAL_MODULE_react_jsx_dev_runtime__ = { jsx, jsxs, Fragment, jsxDEV: jsx };
window['react/jsx-runtime'] = { jsx, jsxs, Fragment };
window['react/jsx-dev-runtime'] = { jsx, jsxs, Fragment, jsxDEV: jsx };
`;
const mathjaxtosvg_pkg = isLib ? "" : fs.readFileSync("./MathjaxToSVG/dist/index.js", "utf8");
const LANGUAGES = ['ru', 'zh-cn']; //english is not compressed as it is always loaded by default
@@ -99,7 +122,7 @@ const packageString = isLib
? ""
: ';const INITIAL_TIMESTAMP=Date.now();' + lzstring_pkg +
'\nlet REACT_PACKAGES = `' +
jsesc(react_pkg + reactdom_pkg, { quotes: 'backtick' }) +
jsesc(react_pkg + reactdom_pkg + jsxRuntimeShim, { quotes: 'backtick' }) +
'`;\n' +
'const unpackExcalidraw = () => LZString.decompressFromBase64("' + LZString.compressToBase64(excalidraw_pkg) + '");\n' +
'let {react, reactDOM } = new Function(`${REACT_PACKAGES}; return {react: React, reactDOM: ReactDOM};`)();\n' +

View File

@@ -232,6 +232,39 @@ export const FRONTMATTER_KEYS:{[key:string]: {name: string, type: string, depric
"open-as-markdown": {name: "excalidraw-open-md", type: "checkbox"},
};
export const CaptureUpdateAction = {
/**
* Immediately undoable.
*
* Use for updates which should be captured.
* Should be used for most of the local updates.
*
* These updates will _immediately_ make it to the local undo / redo stacks.
*/
IMMEDIATELY: "IMMEDIATELY",
/**
* Never undoable.
*
* Use for updates which should never be recorded, such as remote updates
* or scene initialization.
*
* These updates will _never_ make it to the local undo / redo stacks.
*/
NEVER: "NEVER",
/**
* Eventually undoable.
*
* Use for updates which should not be captured immediately - likely
* exceptions which are part of some async multi-step process. Otherwise, all
* such updates would end up being captured with the next
* `CaptureUpdateAction.IMMEDIATELY` - triggered either by the next `updateScene`
* or internally by the editor.
*
* These updates will _eventually_ make it to the local undo / redo stacks.
*/
EVENTUALLY: "EVENTUALLY",
} as const;
export const EMBEDDABLE_THEME_FRONTMATTER_VALUES = ["light", "dark", "auto", "dafault"];
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
export const VIEW_TYPE_EXCALIDRAW_LOADING = "excalidraw-loading";

View File

@@ -412,7 +412,7 @@ export default class ExcalidrawPlugin extends Plugin {
try {
this.isReady = true;
switchToExcalidraw(this.app);
await switchToExcalidraw(this.app);
this.switchToExcalidarwAfterLoad();
} catch (e) {
new Notice("Error switching views to Excalidraw", 6000);

View File

@@ -1,7 +1,7 @@
import { WorkspaceLeaf, TFile, Editor, MarkdownView, MarkdownFileInfo, MetadataCache, App, EventRef, Menu, FileView } from "obsidian";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { getLink } from "../../utils/fileUtils";
import { editorInsertText, getExcalidrawViews, getParentOfClass, setExcalidrawView } from "../../utils/obsidianUtils";
import { editorInsertText, getExcalidrawViews, getParentOfClass, isUnwantedLeaf, setExcalidrawView } from "../../utils/obsidianUtils";
import ExcalidrawPlugin from "src/core/main";
import { DEBUGGING, debug } from "src/utils/debugHelper";
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
@@ -164,6 +164,13 @@ export class EventManager {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onActiveLeafChangeHandler,`onActiveLeafChangeEventHandler`, leaf);
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/723
//In Obsidian 1.8.x the active excalidraw leaf is obscured by an empty leaf without a parent
//This hack resolves it
if(this.app.workspace.activeLeaf === leaf && isUnwantedLeaf(leaf)) {
leaf.detach();
return;
}
if (leaf.view && leaf.view.getViewType() === "pdf") {
this.plugin.lastPDFLeafID = leaf.id;
}

View File

@@ -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 = {};

View File

@@ -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",

View File

@@ -236,7 +236,7 @@ export default {
"在这种情况下,创建新绘图时将提示您选择使用哪个模板。<br>" +
"<b>专业提示:</b> 如果您正在使用 Obsidian Templater 插件,您可以将 Templater 代码添加到不同的" +
"Excalidraw 模板中,以自动配置您的绘图",
SCRIPT_FOLDER_NAME: "Excalidraw 自动化脚本的文件夹(大小写敏感",
SCRIPT_FOLDER_NAME: "Excalidraw 自动化脚本的文件夹(區分大小寫",
SCRIPT_FOLDER_DESC:
"此文件夹用于存放 Excalidraw 自动化脚本。" +
"您可以在 Obsidian 命令面板中执行这些脚本," +
@@ -722,6 +722,8 @@ FILENAME_HEAD: "文件名",
"更改此选项后,您可能需要重启 Obsidian 来使其生效。",
LATEX_DEFAULT_NAME: "插入 LaTeX 时的默认表达式",
LATEX_DEFAULT_DESC: "允许留空。允许使用类似 <code>\\color{white}</code> 的格式化表达式。",
LATEX_PREAMBLE_NAME : "LaTeX 前言文件(區分大小寫!)" ,
LATEX_PREAMBLE_DESC : "前言文件的完整路径,留空则使用默认值。如果文件不存在,此选项将被忽略。<br><strong>重要:</strong>更改后需要重新加载 Obsidian 才能生效!" ,
NONSTANDARD_HEAD: "非 Excalidraw.com 官方支持的特性",
NONSTANDARD_DESC: `这些特性不受 Excalidraw.com 官方支持。如果在 Excalidraw.com 导入绘图,这些特性将会发生不可预知的变化。
包括:自定义画笔工具的数量,自定义字体等。`,
@@ -1048,15 +1050,14 @@ 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_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 : "白色",
@@ -1064,6 +1065,8 @@ EXPORTDIALOG_PDF_PAPER_SCENE : "使用场景颜色",
EXPORTDIALOG_PDF_PAPER_CUSTOM : "自定义颜色",
EXPORTDIALOG_PDF_ALIGNMENT : "页面位置",
EXPORTDIALOG_PDF_ALIGN_CENTER : "居中",
EXPORTDIALOG_PDF_ALIGN_CENTER_LEFT : "左对齐居中" ,
EXPORTDIALOG_PDF_ALIGN_CENTER_RIGHT : "右对齐居中" ,
EXPORTDIALOG_PDF_ALIGN_TOP_LEFT : "左上角",
EXPORTDIALOG_PDF_ALIGN_TOP_CENTER : "顶部居中",
EXPORTDIALOG_PDF_ALIGN_TOP_RIGHT : "右上角",
@@ -1085,9 +1088,11 @@ 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_NOTICE : "正在导出 PDF。如果图像较大可能需要一些时间。" ,
EXPORTDIALOG_PDF_PROGRESS_DONE : "导出完成" ,
EXPORTDIALOG_PDF_PROGRESS_ERROR : "导出 PDF 时出错,请检查开发者控制台以获取详细信息" ,
// exportUtils.ts
PDF_EXPORT_DESKTOP_ONLY : "PDF 导出功能仅限桌面端使用" ,
};

View File

@@ -226,7 +226,7 @@ export class EmbeddableSettings extends Modal {
(async() => {
await this.ea.addElementsToView();
//@ts-ignore
this.ea.viewUpdateScene({appState: {}, storeAction: "update"});
this.ea.viewUpdateScene({appState: {}, captureUpdate: CaptureUpdateAction.NEVER});
this.close(); //close should only run once update scene is done
})();
} else {

View File

@@ -1,5 +1,6 @@
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
import { t } from "src/lang/helpers";
import { CaptureUpdateAction } from "src/constants/constants";
export const showFrameSettings = (ea: ExcalidrawAutomate) => {
const {enabled, clip, name, outline} = ea.getExcalidrawAPI().getAppState().frameRendering;
@@ -64,7 +65,7 @@ export const showFrameSettings = (ea: ExcalidrawAutomate) => {
appState: {
frameRendering: settings
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
frameSettingsModal.close();
})

View File

@@ -17,6 +17,12 @@ 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)

View File

@@ -919,9 +919,9 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
},
{
field: "viewUpdateScene",
code: "viewUpdateScene(scene:{elements?:ExcalidrawElement[],appState?: AppState,files?: BinaryFileData,commitToHistory?: boolean,storeAction?: 'capture' | 'none' | 'update'},restore:boolean=false):void",
code: "viewUpdateScene(scene:{elements?:ExcalidrawElement[],appState?: AppState,files?: BinaryFileData,captureUpdate?: 'IMMEDIATELY' | 'NEVER' | 'EVENTUALLY'},restore:boolean=false):void",
desc: "Calls the ExcalidrawAPI updateScene function for the targetView. When restore=true, excalidraw will try to correct errors in the scene such as setting default values to missing element properties. " +
`Note that commitToHistory has been deprecated in Excalidraw and is no longer used. You should use storeAction instead. See ${hyperlink("https://github.com/excalidraw/excalidraw/pull/7898", "ExcalidrawAPI")} documentation for more information.`,
`Use captureUpdate to control undo/redo behavior: 'IMMEDIATELY' for immediate undoable updates (most local changes), 'NEVER' for updates that should never be undoable, or 'EVENTUALLY' for updates that should be undoable as part of an async multi-step process. See the ExcalidrawAPI documentation for more information.`,
after: "",
},
{

View File

@@ -46,7 +46,7 @@ import {
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";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, SceneData } from "@zsviczian/excalidraw/types/excalidraw/types";
import { EmbeddedFile, EmbeddedFilesLoader } from "./EmbeddedFileLoader";
import { tex2dataURL } from "./LaTeX";
import { NewFileActions } from "src/shared/Dialogs/Prompt";
@@ -87,6 +87,7 @@ import { AddImageOptions, ImageInfo, SVGColorInfo } from "src/types/excalidrawAu
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";
import { CaptureUpdateAction } from "src/constants/constants";
extendPlugins([
HarmonyPlugin,
@@ -2237,7 +2238,7 @@ export class ExcalidrawAutomate {
this.targetView.updateScene({
elements: el.filter((e: ExcalidrawElement) => !ids.includes(e.id)),
appState: st,
storeAction: "capture",
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
});
//this.targetView.save();
return true;
@@ -2564,7 +2565,7 @@ export class ExcalidrawAutomate {
appState: {
viewModeEnabled: !isFullscreen,
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
this.targetView.toolsPanelRef?.current?.setExcalidrawViewMode(!isFullscreen);
}
@@ -2586,7 +2587,7 @@ export class ExcalidrawAutomate {
return;
}
const view = this.targetView as ExcalidrawView;
view.updateScene({appState:{viewModeEnabled: enabled}, storeAction: "update"});
view.updateScene({appState:{viewModeEnabled: enabled}, captureUpdate: CaptureUpdateAction.NEVER});
view.toolsPanelRef?.current?.setExcalidrawViewMode(enabled);
}
@@ -2596,8 +2597,9 @@ export class ExcalidrawAutomate {
* @param {ExcalidrawElement[]} [scene.elements] - Array of elements in the scene.
* @param {AppState} [scene.appState] - The app state of the scene.
* @param {BinaryFileData} [scene.files] - The files in the scene.
* @param {boolean} [scene.commitToHistory] - Whether to commit the scene to history.
* @param {"capture" | "none" | "update"} [scene.storeAction] - The store action for the scene.
* @param {boolean} [scene.commitToHistory] - Whether to commit the scene to history. @deprecated Use scene.storageOption instead
* @param {"capture" | "none" | "update"} [scene.storeAction] - The store action for the scene. @deprecated Use scene.storageOption instead
* @param {"IMMEDIATELY" | "NEVER" | "EVENTUALLY"} [scene.captureUpdate] - The capture update action for the scene.
* @param {boolean} [restore=false] - Whether to restore legacy elements in the scene.
*/
viewUpdateScene (
@@ -2607,6 +2609,7 @@ export class ExcalidrawAutomate {
files?: BinaryFileData,
commitToHistory?: boolean,
storeAction?: "capture" | "none" | "update",
captureUpdate?: SceneData["captureUpdate"],
},
restore: boolean = false,
):void {
@@ -2623,6 +2626,7 @@ export class ExcalidrawAutomate {
appState: scene.appState,
files: scene.files,
storeAction: scene.storeAction,
captureUpdate: scene.captureUpdate,
},restore);
}
@@ -3308,7 +3312,7 @@ export class ExcalidrawAutomate {
elements.splice(newZIndex, 0, elements.splice(oldZIndex, 1)[0]);
this.targetView.updateScene({
elements,
storeAction: "capture",
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
});
};

View File

@@ -7,6 +7,7 @@ import { cloneElement } from "./excalidrawAutomateUtils";
import { ExcalidrawFrameElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { addAppendUpdateCustomData } from "./utils";
import { mutateElement } from "src/constants/constants";
import { CaptureUpdateAction } from "src/constants/constants";
export const setDynamicStyle = (
ea: ExcalidrawAutomate,
@@ -17,7 +18,7 @@ export const setDynamicStyle = (
) => {
if(dynamicStyle === "none") {
view.excalidrawContainer?.removeAttribute("style");
setTimeout(()=>view.updateScene({appState:{dynamicStyle: ""}, storeAction: "update"}));
setTimeout(()=>view.updateScene({appState:{dynamicStyle: ""}, captureUpdate: CaptureUpdateAction.NEVER}));
const toolspanel = view.toolsPanelRef?.current?.containerRef?.current;
if(toolspanel) {
let toolsStyle = toolspanel.getAttribute("style");
@@ -178,7 +179,7 @@ export const setDynamicStyle = (
frameColor,
dynamicStyle: styleObject
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
view = null;
ea = null;

View File

@@ -14,6 +14,7 @@ import { nanoid } from "nanoid";
import { t } from "src/lang/helpers";
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
import { EmbeddedFile } from "src/shared/EmbeddedFileLoader";
import { CaptureUpdateAction } from "src/constants/constants";
export async function insertImageToView(
ea: ExcalidrawAutomate,
@@ -264,7 +265,7 @@ export async function addBackOfTheNoteCard(
api.selectElements([el]);
if(activate) {
window.setTimeout(()=>{
api.updateScene({appState: {activeEmbeddable: {element: el, state: "active"}}, storeAction: "update"});
api.updateScene({appState: {activeEmbeddable: {element: el, state: "active"}}, captureUpdate: CaptureUpdateAction.NEVER,});
if(found) view.getEmbeddableLeafElementById(el.id)?.editNode?.();
});
}

View File

@@ -8,7 +8,7 @@ import {
import ExcalidrawPlugin from "../core/main";
import { checkAndCreateFolder, splitFolderAndFilename } from "./fileUtils";
import { linkClickModifierType, ModifierKeys } from "./modifierkeyHelper";
import { EXCALIDRAW_PLUGIN, REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
import { DEVICE, EXCALIDRAW_PLUGIN, REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
import yaml from "js-yaml";
import { debug, DEBUGGING } from "./debugHelper";
import ExcalidrawView from "src/view/ExcalidrawView";
@@ -416,3 +416,12 @@ export async function closeLeafView(leaf: WorkspaceLeaf) {
state: {},
});
}
//In Obsidian 1.8.x the active excalidraw leaf is obscured by an empty leaf without a parent
//In some ways similar to patchMobileView() - though does something completely different, but both mobile leaf related
export function isUnwantedLeaf(leaf:WorkspaceLeaf):boolean {
return !DEVICE.isDesktop &&
leaf.view?.getViewType() === "empty" && leaf.parent && !leaf.parent.parent &&
//@ts-ignore
leaf.parent.type === "split" && leaf.parent.children.length === 1
}

View File

@@ -1,17 +1,19 @@
import { App, FileView, WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_EXCALIDRAW_LOADING } from "src/constants/constants";
import { act } from "react";
import { DEVICE, VIEW_TYPE_EXCALIDRAW_LOADING } from "src/constants/constants";
import ExcalidrawPlugin from "src/core/main";
import { setExcalidrawView } from "src/utils/obsidianUtils";
import { isUnwantedLeaf, setExcalidrawView } from "src/utils/obsidianUtils";
export function switchToExcalidraw(app: App) {
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW_LOADING).filter(l=>l.view instanceof ExcalidrawLoading);
leaves.forEach(l=>(l.view as ExcalidrawLoading).switchToeExcalidraw());
export async function switchToExcalidraw(app: App) {
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW_LOADING);
for(const leaf of leaves) {
await (leaf.view as ExcalidrawLoading)?.switchToeExcalidraw();
}
}
export class ExcalidrawLoading extends FileView {
constructor(leaf: WorkspaceLeaf, private plugin: ExcalidrawPlugin) {
super(leaf);
this.displayLoadingText();
}
public onload() {
@@ -19,8 +21,13 @@ export class ExcalidrawLoading extends FileView {
this.displayLoadingText();
}
public switchToeExcalidraw() {
setExcalidrawView(this.leaf);
public async switchToeExcalidraw() {
const prevLeaf = this.app.workspace.activeLeaf;
await setExcalidrawView(this.leaf);
const activeLeaf = this.app.workspace.activeLeaf;
if(prevLeaf === this.leaf && activeLeaf !== prevLeaf && isUnwantedLeaf(activeLeaf)) {
this.app.workspace.activeLeaf.detach();
}
}
getViewType(): string {

View File

@@ -30,6 +30,7 @@ import {
ExcalidrawImperativeAPI,
Gesture,
LibraryItems,
SceneData,
UIAppState,
} from "@zsviczian/excalidraw/types/excalidraw/types";
import {
@@ -152,8 +153,8 @@ 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";
import { CaptureUpdateAction } from "src/constants/constants";
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
const PREVENT_RELOAD_TIMEOUT = 2000;
@@ -253,7 +254,7 @@ export const addFiles = async (
view.updateScene({
elements: s.scene.elements,
appState: s.scene.appState,
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
for (const f of files) {
@@ -1096,21 +1097,21 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
toggleDisableBinding() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleDisableBinding, "ExcalidrawView.toggleDisableBinding");
const newState = !this.excalidrawAPI.getAppState().invertBindingBehaviour;
this.updateScene({appState: {invertBindingBehaviour:newState}, storeAction: "update"});
this.updateScene({appState: {invertBindingBehaviour:newState}, captureUpdate: CaptureUpdateAction.NEVER,});
new Notice(newState ? t("ARROW_BINDING_INVERSE_MODE") : t("ARROW_BINDING_NORMAL_MODE"));
}
toggleFrameRendering() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleFrameRendering, "ExcalidrawView.toggleFrameRendering");
const frameRenderingSt = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState().frameRendering;
this.updateScene({appState: {frameRendering: {...frameRenderingSt, enabled: !frameRenderingSt.enabled}}, storeAction: "update"});
this.updateScene({appState: {frameRendering: {...frameRenderingSt, enabled: !frameRenderingSt.enabled}}, captureUpdate: CaptureUpdateAction.NEVER,});
new Notice(frameRenderingSt.enabled ? t("FRAME_CLIPPING_ENABLED") : t("FRAME_CLIPPING_DISABLED"));
}
toggleFrameClipping() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleFrameClipping, "ExcalidrawView.toggleFrameClipping");
const frameRenderingSt = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState().frameRendering;
this.updateScene({appState: {frameRendering: {...frameRenderingSt, clip: !frameRenderingSt.clip}}, storeAction: "update"});
this.updateScene({appState: {frameRendering: {...frameRenderingSt, clip: !frameRenderingSt.clip}}, captureUpdate: CaptureUpdateAction.NEVER,});
new Notice(frameRenderingSt.clip ? "Frame Clipping: Enabled" : "Frame Clipping: Disabled");
}
@@ -1379,7 +1380,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
if (this.excalidrawData.hasMermaid(selectedImage.fileId) || getMermaidText(imageElement)) {
if(shouldRenderMermaid) {
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
api.updateScene({appState: {openDialog: { name: "ttd", tab: "mermaid" }}, storeAction: "update"})
api.updateScene({appState: {openDialog: { name: "ttd", tab: "mermaid" }}, captureUpdate: CaptureUpdateAction.NEVER })
}
return;
}
@@ -1806,7 +1807,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
...st,
theme,
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
@@ -1976,6 +1977,11 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
protected async onClose(): Promise<void> {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onClose,`ExcalidrawView.onClose, file:${this.file?.name}`);
//I noticed Obsidian calls this function twice when disabling the plugin
//once from "unregisterView"
//the from "detachLeavesOfType"
if(!this.dropManager) return; //the view is already closed
// This happens when the user right clicks a tab and selects delete
// in this case the onDelete event handler tirggers, but then Obsidian's delete event handler reaches onclose first, and
// when the function is called a second time via on delete an error is thrown.)
@@ -2741,7 +2747,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
{
elements: excalidrawData.elements.concat(deletedElements??[]), //need to preserve deleted elements during autosave if images, links, etc. are updated
files: excalidrawData.files,
storeAction: justloaded ? "update" : "update", //was none, but I think based on a false understanding of none
captureUpdate: CaptureUpdateAction.NEVER,
},
justloaded
);
@@ -2764,7 +2770,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
pinnedScripts: this.plugin.settings.pinnedScripts,
customPens: this.plugin.settings.customPens.slice(0,this.plugin.settings.numberOfCustomPens),
},
storeAction: justloaded ? "update" : "update", //was none, but I think based on a false understanding of none
captureUpdate: CaptureUpdateAction.NEVER,
},
);
if (
@@ -3530,7 +3536,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
this.updateScene(
{
elements,
storeAction: "capture",
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
},
shouldRestoreElements,
);
@@ -3930,7 +3936,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
st = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState();
canvasColor = canvasColor ?? st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor;
}
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}, storeAction: "update"}));
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}, captureUpdate: CaptureUpdateAction.NEVER}));
}
private canvasColorChangeHook(st: AppState) {
@@ -4183,7 +4189,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
this.excalidrawData.deleteTextElement(clone.id);
sceneElements[sceneElements.indexOf(el)] = clone;
}
this.updateScene({elements: sceneElements, storeAction: "update"});
this.updateScene({elements: sceneElements, captureUpdate: CaptureUpdateAction.NEVER});
//then insert images and embeds
//shift text elements down to make space for images and embeds
@@ -4307,7 +4313,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
clone.rawText = WARNING;
elements[elements.indexOf(el[0])] = clone;
this.excalidrawData.setTextElement(clone.id,WARNING,()=>{});
this.updateScene({elements, storeAction: "update"});
this.updateScene({elements, captureUpdate: CaptureUpdateAction.NEVER});
api.history.clear();
}
});
@@ -4331,7 +4337,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
clone.isDeleted = true;
this.excalidrawData.deleteTextElement(clone.id);
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements, storeAction: "update"});
this.updateScene({elements, captureUpdate: CaptureUpdateAction.NEVER});
const ea:ExcalidrawAutomate = getEA(this);
if(IMAGE_TYPES.contains(file.extension)) {
ea.selectElementsInView([await insertImageToView (ea, center, file)]);
@@ -4384,7 +4390,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements, storeAction: "update"});
this.updateScene({elements, captureUpdate: CaptureUpdateAction.NEVER});
if(clone.containerId) this.updateContainerSize(clone.containerId);
this.setDirty(8.1);
}
@@ -5600,7 +5606,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
api.updateScene({
appState: { pinnedScripts: this.plugin.settings.pinnedScripts },
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
@@ -5614,7 +5620,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
appState: {
customPens: this.plugin.settings.customPens.slice(0,this.plugin.settings.numberOfCustomPens),
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
@@ -5626,7 +5632,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
api.updateScene({
appState: { allowPinchZoom: this.plugin.settings.allowPinchZoom },
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
@@ -5638,7 +5644,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
api.updateScene({
appState: { allowWheelZoom: this.plugin.settings.allowWheelZoom },
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
}
@@ -5651,7 +5657,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
const st = api.getAppState();
api.updateScene({
appState: { trayModeEnabled: !st.trayModeEnabled },
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
});
//just in case settings were updated via Obsidian sync
@@ -5899,14 +5905,29 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
appState?: any;
files?: any;
storeAction?: "capture" | "none" | "update"; //https://github.com/excalidraw/excalidraw/pull/7898
captureUpdate?: SceneData["captureUpdate"];
},
shouldRestore: boolean = false,
) {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.updateScene, "ExcalidrawView.updateScene", scene, shouldRestore);
const api = this.excalidrawAPI;
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
if (!api) {
return;
}
if(typeof scene.storeAction === "string") {
switch(scene.storeAction) {
case "capture":
scene.captureUpdate = CaptureUpdateAction.IMMEDIATELY;
break;
case "none":
scene.captureUpdate = CaptureUpdateAction.EVENTUALLY;
break;
default:
scene.captureUpdate = CaptureUpdateAction.NEVER;
}
delete scene.storeAction;
}
const shouldRestoreElements = scene.elements && shouldRestore;
if (shouldRestoreElements) {
scene.elements = restore(scene, null, null).elements;

View File

@@ -15,6 +15,7 @@ import { EmbeddableSettings } from "src/shared/Dialogs/EmbeddableSettings";
import { openExternalLink } from "src/utils/excalidrawViewUtils";
import { getEA } from "src/core";
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
import { CaptureUpdateAction } from "src/constants/constants";
export class EmbeddableMenu {
private menuFadeTimeout: number = 0;
@@ -70,7 +71,7 @@ export class EmbeddableMenu {
};
private async actionMarkdownSelection (file: TFile, isExcalidrawFile: boolean, subpath: string, element: ExcalidrawEmbeddableElement) {
this.view.updateScene({appState: {activeEmbeddable: null}, storeAction: "update"});
this.view.updateScene({appState: {activeEmbeddable: null}, captureUpdate: CaptureUpdateAction.NEVER});
const sections = (await this.view.app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },file))
.blocks.filter((b: any) => b.display && b.node?.type === "heading")
@@ -98,7 +99,7 @@ export class EmbeddableMenu {
private async actionMarkdownBlock (file: TFile, subpath: string, element: ExcalidrawEmbeddableElement) {
if(!file) return;
this.view.updateScene({appState: {activeEmbeddable: null}, storeAction: "update"});
this.view.updateScene({appState: {activeEmbeddable: null}, captureUpdate: CaptureUpdateAction.NEVER});
const paragraphs = (await this.view.app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },file))
.blocks.filter((b: any) => b.display && b.node &&

View File

@@ -12,6 +12,7 @@ import { ICONS, penIcon, stringToSVG } from "../../../constants/actionIcons";
import { UniversalInsertFileModal } from "src/shared/Dialogs/UniversalInsertFileModal";
import { t } from "src/lang/helpers";
import { getExcalidrawViews } from "src/utils/obsidianUtils";
import { CaptureUpdateAction } from "src/constants/constants";
export function setPen (pen: PenStyle, api: any) {
const st = api.getAppState();
@@ -34,7 +35,7 @@ export function setPen (pen: PenStyle, api: any) {
}}
: null,
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER,
})
}
@@ -51,7 +52,7 @@ export function resetStrokeOptions (resetCustomPen:any, api: ExcalidrawImperativ
resetCustomPen: null,
...clearCurrentStrokeOptions ? {currentStrokeOptions: null} : null,
},
storeAction: "update",
captureUpdate: CaptureUpdateAction.NEVER
});
}

View File

@@ -684,6 +684,10 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
return "";
}
if(!this.view.plugin._loaded) {
return null;
}
const downloadedScriptsRoot = `${this.view.plugin.settings.scriptFolderPath}/${SCRIPT_INSTALL_FOLDER}/`;
const filterCondition = (key: string): boolean =>