mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
1.6.14
This commit is contained in:
@@ -5,12 +5,36 @@ The script allows you to change the shape of selected Rectangles, Diamonds and E
|
||||
|
||||
```javascript
|
||||
*/
|
||||
const shapesDispaly=["○ ellipse","□ rectangle","◇ diamond"];
|
||||
const shapes=["ellipse","rectangle","diamond"];
|
||||
elements = ea.getViewSelectedElements().filter(el=>shapes.contains(el.type));
|
||||
newShape = await utils.suggester(shapesDispaly, shapes);
|
||||
if(!newShape) return;
|
||||
const boxShapesDispaly=["○ ellipse","□ rectangle","◇ diamond"];
|
||||
const boxShapes=["ellipse","rectangle","diamond"];
|
||||
const lineShapesDispaly=["- line","⭢ arrow"];
|
||||
const lineShapes=["line","arrow"];
|
||||
|
||||
let editedElements = [];
|
||||
|
||||
let elements = ea.getViewSelectedElements().filter(el=>boxShapes.contains(el.type));
|
||||
if (elements.length>0) {
|
||||
newShape = await utils.suggester(boxShapesDispaly, boxShapes, "Change shape of 'box' type elements in selection");
|
||||
if(newShape) {
|
||||
editedElements = elements;
|
||||
elements.forEach(el=>el.type = newShape);
|
||||
}
|
||||
}
|
||||
|
||||
elements = ea.getViewSelectedElements().filter(el=>lineShapes.contains(el.type));
|
||||
if (elements.length>0) {
|
||||
newShape = await utils.suggester(lineShapesDispaly, lineShapes, "Change shape of 'line' type elements in selection");
|
||||
if(newShape) {
|
||||
editedElements = editedElements.concat(elements);
|
||||
elements.forEach((el)=>{
|
||||
el.type = newShape;
|
||||
if(newShape === "arrow") {
|
||||
el.endArrowhead = "triangle";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ea.copyViewElementsToEAforEditing(editedElements);
|
||||
|
||||
elements.forEach(el=>el.type = newShape);
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
ea.addElementsToView(false,false);
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.6.13",
|
||||
"version": "1.6.14",
|
||||
"minAppVersion": "0.12.16",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -83,28 +83,58 @@ export const REGEX_LINK = {
|
||||
},
|
||||
};
|
||||
|
||||
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
|
||||
|
||||
//added \n at and of DRAWING_REG: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/357
|
||||
const DRAWING_REG = /\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```\n/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
|
||||
const DRAWING_REG_FALLBACK = /\n# Drawing\n(```json\n)?(.*)(```)?(%%)?/gm;
|
||||
const DRAWING_COMPRESSED_REG = /\n# Drawing\n[^`]*(```compressed\-json\n)([\s\S]*?)```\n/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
|
||||
const DRAWING_COMPRESSED_REG_FALLBACK = /\n# Drawing\n(```compressed\-json\n)?(.*)(```)?(%%)?/gm;
|
||||
const DRAWING_COMPRESSED_REG = /(\n# Drawing\n[^`]*(?:```compressed\-json\n))([\s\S]*?)(```\n)/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
|
||||
const DRAWING_COMPRESSED_REG_FALLBACK = /(\n# Drawing\n(?:```compressed\-json\n)?)(.*)((```)?(%%)?)/gm;
|
||||
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
|
||||
|
||||
const isCompressedMD = (data:string):boolean => {
|
||||
return data.match(/```compressed\-json\n/gm) !== null;
|
||||
}
|
||||
|
||||
const getDecompressedScene = (data:string):[string,IteratorResult<RegExpMatchArray, any>] => {
|
||||
let res = data.matchAll(DRAWING_COMPRESSED_REG);
|
||||
|
||||
//In case the user adds a text element with the contents "# Drawing\n"
|
||||
let parts;
|
||||
parts = res.next();
|
||||
if (parts.done) {
|
||||
//did not find a match
|
||||
res = data.matchAll(DRAWING_COMPRESSED_REG_FALLBACK);
|
||||
parts = res.next();
|
||||
}
|
||||
if (parts.value && parts.value.length > 1) {
|
||||
return [decompress(parts.value[2]),parts];
|
||||
}
|
||||
return [null,parts];
|
||||
}
|
||||
|
||||
export const changeThemeOfExcalidrawMD = (data:string):string => {
|
||||
const compressed = isCompressedMD(data);
|
||||
let scene = compressed ? getDecompressedScene(data)[0] : data;
|
||||
if(!scene) return data;
|
||||
if(isObsidianThemeDark) {
|
||||
if((scene.match(/"theme"\s*:\s*"light"\s*,/g)||[]).length === 1) {
|
||||
scene = scene.replace(/"theme"\s*:\s*"light"\s*,/,`"theme": "dark",`);
|
||||
}
|
||||
} else {
|
||||
if((scene.match(/"theme"\s*:\s*"dark"\s*,/g)||[]).length === 1) {
|
||||
scene = scene.replace(/"theme"\s*:\s*"dark"\s*,/,`"theme": "light",`);
|
||||
}
|
||||
}
|
||||
if(compressed) {
|
||||
return data.replace(DRAWING_COMPRESSED_REG,`$1${compress(scene)}$3`);
|
||||
}
|
||||
return scene;
|
||||
}
|
||||
|
||||
export function getJSON(data: string): { scene: string; pos: number } {
|
||||
let res;
|
||||
if(data.match(/```compressed\-json\n/gm)) {
|
||||
res = data.matchAll(DRAWING_COMPRESSED_REG);
|
||||
|
||||
//In case the user adds a text element with the contents "# Drawing\n"
|
||||
let parts;
|
||||
parts = res.next();
|
||||
if (parts.done) {
|
||||
//did not find a match
|
||||
res = data.matchAll(DRAWING_COMPRESSED_REG_FALLBACK);
|
||||
parts = res.next();
|
||||
}
|
||||
if (parts.value && parts.value.length > 1) {
|
||||
const result = decompress(parts.value[2]);
|
||||
if(isCompressedMD(data)) {
|
||||
const [result,parts] = getDecompressedScene(data);
|
||||
if(result) {
|
||||
return {
|
||||
scene: result.substring(0, result.lastIndexOf("}") + 1),
|
||||
pos: parts.value.index,
|
||||
|
||||
@@ -146,6 +146,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
public excalidrawAPI: any = null;
|
||||
public excalidrawWrapperRef: React.MutableRefObject<any> = null;
|
||||
private justLoaded: boolean = false;
|
||||
private preventAutozoomOnLoad: boolean = false;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private dirty: string = null;
|
||||
public autosaveTimer: any = null;
|
||||
@@ -693,7 +694,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.textIsParsed_Element.hide();
|
||||
}
|
||||
if (reload) {
|
||||
await this.save(false);
|
||||
await this.save(false,true);
|
||||
this.updateContainerSize();
|
||||
this.excalidrawAPI.history.clear(); //to avoid undo replacing links with parsed text
|
||||
}
|
||||
@@ -758,6 +759,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const loadOnModifyTrigger = (file && file === this.file);
|
||||
if (loadOnModifyTrigger) {
|
||||
this.data = await this.app.vault.cachedRead(file);
|
||||
this.preventAutozoomOnLoad = true;
|
||||
}
|
||||
if (fullreload) {
|
||||
await this.excalidrawData.loadData(this.data, this.file, this.textMode);
|
||||
@@ -1837,7 +1839,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
viewModeEnabled = st.viewModeEnabled;
|
||||
if (this.justLoaded) {
|
||||
this.justLoaded = false;
|
||||
this.zoomToFit(false);
|
||||
if(!this.preventAutozoomOnLoad) this.zoomToFit(false);
|
||||
this.preventAutozoomOnLoad = false;
|
||||
this.previousSceneVersion = getSceneVersion(et);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export default {
|
||||
"Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!})",
|
||||
ENTER_LATEX: "Enter a valid LaTeX expression",
|
||||
TRAY_MODE: "Toggle property-panel tray-mode",
|
||||
SEARCH: "Search for text in drawing",
|
||||
|
||||
//ExcalidrawView.ts
|
||||
INSTALL_SCRIPT_BUTTON: "Install or update Excalidraw Scripts",
|
||||
@@ -256,6 +257,8 @@ export default {
|
||||
"or a PNG or an SVG copy. You need to enable auto-export PNG / SVG (see below under Export Settings) for those image types to be available in the dropdown. For drawings that do not have a " +
|
||||
"a corresponding PNG or SVG readily available the command palette action will insert a broken link. You need to open the original drawing and initiate export manually. " +
|
||||
"This option will not autogenerate PNG/SVG files, but will simply reference the already existing files.",
|
||||
EMBED_WIKILINK_NAME: "Embed SVG or PNG as Wiki link",
|
||||
EMBED_WIKILINK_DESC: "Toggle ON: Excalidraw will embed a [[wiki link]]. Toggle OFF: Excalidraw will embed a [markdown](link).",
|
||||
EXPORT_PNG_SCALE_NAME: "PNG export image scale",
|
||||
EXPORT_PNG_SCALE_DESC: "The size-scale of the exported PNG image",
|
||||
EXPORT_BACKGROUND_NAME: "Export image with background",
|
||||
|
||||
121
src/main.ts
121
src/main.ts
@@ -15,6 +15,8 @@ import {
|
||||
loadMathJax,
|
||||
Scope,
|
||||
request,
|
||||
MetadataCache,
|
||||
FrontMatterCache,
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -42,7 +44,7 @@ import {
|
||||
VIRGIL_DATAURL,
|
||||
} from "./constants";
|
||||
import ExcalidrawView, { TextMode } from "./ExcalidrawView";
|
||||
import { getMarkdownDrawingSection } from "./ExcalidrawData";
|
||||
import { changeThemeOfExcalidrawMD, getMarkdownDrawingSection } from "./ExcalidrawData";
|
||||
import {
|
||||
ExcalidrawSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
@@ -97,6 +99,7 @@ declare module "obsidian" {
|
||||
}
|
||||
|
||||
export default class ExcalidrawPlugin extends Plugin {
|
||||
private excalidrawFiles: Set<TFile> = new Set<TFile>();
|
||||
public excalidrawFileModes: { [file: string]: string } = {};
|
||||
private _loaded: boolean = false;
|
||||
public settings: ExcalidrawSettings;
|
||||
@@ -757,6 +760,55 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "search-text",
|
||||
name: t("SEARCH"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ===
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
(async ()=>{
|
||||
const ea = this.ea;
|
||||
ea.reset();
|
||||
ea.setView(view);
|
||||
const elements = ea.getViewElements().filter(el=>el.type==="text");
|
||||
if(elements.length === 0) return;
|
||||
let text = await ScriptEngine.inputPrompt(this.app,"Search for","use quotation marks for exact match","");
|
||||
if(!text) return;
|
||||
const res = text.matchAll(/"(.*?)"/g)
|
||||
let query:string[] = [];
|
||||
let parts;
|
||||
while (!(parts = res.next()).done) {
|
||||
query.push(parts.value[1]);
|
||||
}
|
||||
text = text.replaceAll(/"(.*?)"/g, "");
|
||||
query = query.concat(text.split(" ").filter(s=>s.length!==0));
|
||||
const match = elements
|
||||
.filter((el:any)=>query
|
||||
.some(q=>el
|
||||
.rawText
|
||||
.toLowerCase()
|
||||
.replaceAll("\n"," ")
|
||||
.match(q.toLowerCase())
|
||||
));
|
||||
if(match.length === 0) {
|
||||
new Notice("I could not find a matching text element");
|
||||
return;
|
||||
}
|
||||
ea.selectElementsInView(match);
|
||||
ea.getExcalidrawAPI().zoomToFit(match,this.settings.zoomToFitMaxLevel,0.05);
|
||||
})();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "export-png",
|
||||
name: t("EXPORT_PNG"),
|
||||
@@ -1253,14 +1305,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (!(file instanceof TFile)) {
|
||||
return;
|
||||
}
|
||||
const isExcalidarwFile =
|
||||
//@ts-ignore
|
||||
(file.unsafeCachedData &&
|
||||
//@ts-ignore
|
||||
file.unsafeCachedData.search(
|
||||
/---[\r\n]+[\s\S]*excalidraw-plugin:\s*\w+[\r\n]+[\s\S]*---/gm,
|
||||
) > -1) ||
|
||||
file.extension == "excalidraw";
|
||||
|
||||
const isExcalidarwFile = this.excalidrawFiles.has(file);
|
||||
this.updateFileCache(file,undefined,true);
|
||||
if (!isExcalidarwFile) {
|
||||
return;
|
||||
}
|
||||
@@ -1373,9 +1420,36 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
activeLeafChangeEventHandler,
|
||||
),
|
||||
);
|
||||
|
||||
const metaCache:MetadataCache = self.app.metadataCache;
|
||||
//@ts-ignore
|
||||
metaCache.getCachedFiles().forEach((filename:string) => {
|
||||
const fm = metaCache.getCache(filename)?.frontmatter;
|
||||
if ((fm && Object.keys(fm).contains(FRONTMATTER_KEY)) ||
|
||||
filename.match(/\.excalidraw$/)
|
||||
) {
|
||||
self.updateFileCache(
|
||||
self.app.vault.getAbstractFileByPath(filename) as TFile, fm
|
||||
);
|
||||
}
|
||||
});
|
||||
this.registerEvent(metaCache.on("changed", (file, data, cache) => this.updateFileCache(file, cache?.frontmatter)));
|
||||
});
|
||||
}
|
||||
|
||||
updateFileCache(file: TFile, frontmatter?: FrontMatterCache, deleted: boolean = false) {
|
||||
if(frontmatter) {
|
||||
const isExcalidrawFile = Object.keys(frontmatter).contains(FRONTMATTER_KEY);
|
||||
this.excalidrawFiles.add(file);
|
||||
return;
|
||||
}
|
||||
if(!deleted && file.extension==="excalidraw") {
|
||||
this.excalidrawFiles.add(file);
|
||||
return;
|
||||
}
|
||||
this.excalidrawFiles.delete(file);
|
||||
}
|
||||
|
||||
onunload() {
|
||||
window.removeEventListener("keydown", this.onKeyDown, false);
|
||||
window.removeEventListener("keyup", this.onKeyUp, false);
|
||||
@@ -1413,7 +1487,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
)
|
||||
const editor = activeView.editor;
|
||||
if (this.settings.embedType === "excalidraw") {
|
||||
editor.replaceSelection(`![[${data}]]`);
|
||||
editor.replaceSelection(this.settings.embedWikiLink
|
||||
? `![[${data}]]` : `})`);
|
||||
editor.focus();
|
||||
return;
|
||||
}
|
||||
@@ -1425,11 +1500,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
file.path,"."+this.settings.embedType.toLowerCase()
|
||||
);
|
||||
|
||||
await this.app.vault.create(filepath, "");
|
||||
//await sleep(200);
|
||||
|
||||
const imgFile = this.app.vault.getAbstractFileByPath(filepath);
|
||||
if(!imgFile) {
|
||||
await this.app.vault.create(filepath, "");
|
||||
await sleep(200);
|
||||
}
|
||||
editor.replaceSelection(
|
||||
`![[${filename}]]\n%%[[${data}|🖋 Edit in Excalidraw]]%%`,
|
||||
this.settings.embedWikiLink
|
||||
? `![[${filename}]]\n%%[[${data}|🖋 Edit in Excalidraw]]%%`
|
||||
: `})\n%%[🖋 Edit in Excalidraw](${encodeURI(data)})%%`,
|
||||
);
|
||||
editor.focus();
|
||||
}
|
||||
@@ -1515,18 +1594,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
) {
|
||||
let data = await this.app.vault.read(template);
|
||||
if (data) {
|
||||
if(this.settings.matchTheme) {
|
||||
if(isObsidianThemeDark) {
|
||||
if((data.match(/"theme"\s*:\s*"light"\s*,/g)||[]).length === 1) {
|
||||
data = data.replace(/"theme"\s*:\s*"light"\s*,/,`"theme": "dark",`);
|
||||
}
|
||||
} else {
|
||||
if((data.match(/"theme"\s*:\s*"dark"\s*,/g)||[]).length === 1) {
|
||||
data = data.replace(/"theme"\s*:\s*"dark"\s*,/,`"theme": "light",`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return this.settings.matchTheme ? changeThemeOfExcalidrawMD(data) : data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1634,4 +1702,5 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const fileCache = f ? this.app.metadataCache.getFileCache(f) : null;
|
||||
return !!fileCache?.frontmatter && !!fileCache.frontmatter[FRONTMATTER_KEY];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ export interface ExcalidrawSettings {
|
||||
autoexportPNG: boolean;
|
||||
autoexportExcalidraw: boolean;
|
||||
embedType: "excalidraw" | "PNG" | "SVG";
|
||||
embedWikiLink: boolean,
|
||||
syncExcalidraw: boolean;
|
||||
compatibilityMode: boolean;
|
||||
experimentalFileType: boolean;
|
||||
@@ -114,6 +115,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
autoexportPNG: false,
|
||||
autoexportExcalidraw: false,
|
||||
embedType: "excalidraw",
|
||||
embedWikiLink: true,
|
||||
syncExcalidraw: false,
|
||||
experimentalFileType: false,
|
||||
experimentalFileTag: "✏️",
|
||||
@@ -789,6 +791,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("EMBED_WIKILINK_NAME"))
|
||||
.setDesc(fragWithHTML(t("EMBED_WIKILINK_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.embedWikiLink)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.embedWikiLink = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
let scaleText: HTMLDivElement;
|
||||
|
||||
new Setting(containerEl)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"1.6.13": "0.12.16",
|
||||
"1.6.14": "0.12.16",
|
||||
"1.4.2": "0.11.13"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user