mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
1 Commits
1.2.0-alph
...
1.2.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebcf807501 |
@@ -1,5 +1,9 @@
|
||||
import { App, TFile } from "obsidian";
|
||||
import { nanoid} from "./constants";
|
||||
import {
|
||||
nanoid,
|
||||
FRONTMATTER_KEY_CUSTOM_PREFIX,
|
||||
FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
|
||||
} from "./constants";
|
||||
import { measureText } from "./ExcalidrawAutomate";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { ExcalidrawSettings } from "./settings";
|
||||
@@ -29,7 +33,7 @@ export class ExcalidrawData {
|
||||
private settings:ExcalidrawSettings;
|
||||
private app:App;
|
||||
private showLinkBrackets: boolean;
|
||||
private linkIndicator: string;
|
||||
private linkPrefix: string;
|
||||
private allowParse: boolean = false;
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin) {
|
||||
@@ -44,13 +48,16 @@ export class ExcalidrawData {
|
||||
*/
|
||||
public async loadData(data: string,file: TFile, allowParse:boolean):Promise<boolean> {
|
||||
//console.log("Excalidraw.Data.loadData()",{data:data,allowParse:allowParse,file:file});
|
||||
//I am storing these because if the settings change while a drawing is open parsing will run into errors during save
|
||||
//The drawing will use these values until next drawing is loaded or this drawing is re-loaded
|
||||
this.showLinkBrackets = this.settings.showLinkBrackets;
|
||||
this.linkIndicator = this.settings.linkIndicator;
|
||||
|
||||
this.file = file;
|
||||
this.textElements = new Map<string,{raw:string, parsed:string}>();
|
||||
|
||||
//I am storing these because if the settings change while a drawing is open parsing will run into errors during save
|
||||
//The drawing will use these values until next drawing is loaded or this drawing is re-loaded
|
||||
this.setShowLinkBrackets();
|
||||
this.setLinkPrefix();
|
||||
|
||||
|
||||
|
||||
//Load scene: Read the JSON string after "# Drawing"
|
||||
this.scene = null;
|
||||
@@ -259,7 +266,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
outString += text.substring(position,text.length);
|
||||
if (linkIcon) {
|
||||
outString = this.linkIndicator + outString;
|
||||
outString = this.linkPrefix + outString;
|
||||
}
|
||||
|
||||
return outString;
|
||||
@@ -281,7 +288,7 @@ export class ExcalidrawData {
|
||||
public syncElements(newScene:any):boolean {
|
||||
//console.log("Excalidraw.Data.syncElements()");
|
||||
this.scene = JSON_parse(newScene);
|
||||
const result = this.findNewTextElementsInScene();
|
||||
const result = this.setLinkPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
this.updateTextElementsFromSceneRawOnly();
|
||||
return result;
|
||||
}
|
||||
@@ -289,7 +296,7 @@ export class ExcalidrawData {
|
||||
public async updateScene(newScene:any){
|
||||
//console.log("Excalidraw.Data.updateScene()");
|
||||
this.scene = JSON_parse(newScene);
|
||||
const result = this.findNewTextElementsInScene();
|
||||
const result = this.setLinkPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
await this.updateTextElementsFromScene();
|
||||
if(result) {
|
||||
await this.updateSceneTextElements();
|
||||
@@ -302,4 +309,26 @@ export class ExcalidrawData {
|
||||
return this.textElements.get(id)?.raw;
|
||||
}
|
||||
|
||||
private setLinkPrefix():boolean {
|
||||
const linkPrefix = this.linkPrefix;
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
if (fileCache?.frontmatter && fileCache.frontmatter[FRONTMATTER_KEY_CUSTOM_PREFIX]!=null) {
|
||||
this.linkPrefix=fileCache.frontmatter[FRONTMATTER_KEY_CUSTOM_PREFIX];
|
||||
} else {
|
||||
this.linkPrefix = this.settings.linkPrefix;
|
||||
}
|
||||
return linkPrefix != this.linkPrefix;
|
||||
}
|
||||
|
||||
private setShowLinkBrackets():boolean {
|
||||
const showLinkBrackets = this.showLinkBrackets;
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
if (fileCache?.frontmatter && fileCache.frontmatter[FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS]!=null) {
|
||||
this.showLinkBrackets=fileCache.frontmatter[FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS]!=false;
|
||||
} else {
|
||||
this.showLinkBrackets = this.settings.showLinkBrackets;
|
||||
}
|
||||
return showLinkBrackets != this.showLinkBrackets;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
// get the new file content
|
||||
// if drawing is in Text Element Edit Lock, then everything should be parsed and in sync
|
||||
// if drawing is in Text Element Edit Unlock, then everything is raw and parse a.k.a async is not required.
|
||||
// if drawing is in Text Element Edit Unlock, then everything is raw and parse and so an async function is not required here
|
||||
getViewData () {
|
||||
//console.log("ExcalidrawView.getViewData()");
|
||||
if(this.getScene) {
|
||||
@@ -345,7 +345,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
.setIcon(ICON_NAME)
|
||||
.onClick( async (ev) => {
|
||||
if(!this.getScene || !this.file) return;
|
||||
this.download('data:text/plain;charset=utf-8',encodeURIComponent(this.getScene()), this.file.basename+'.excalidraw');
|
||||
this.download('data:text/plain;charset=utf-8',encodeURIComponent(this.getScene().replaceAll("[","[")), this.file.basename+'.excalidraw');
|
||||
});
|
||||
})
|
||||
.addItem((item) => {
|
||||
|
||||
@@ -5,6 +5,8 @@ export function JSON_parse(x:string):any {return JSON.parse(x.replaceAll("["
|
||||
import {customAlphabet} from "nanoid";
|
||||
export const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',8);
|
||||
export const FRONTMATTER_KEY = "excalidraw-plugin";
|
||||
export const FRONTMATTER_KEY_CUSTOM_PREFIX = "excalidraw-link-prefix";
|
||||
export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets";
|
||||
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
|
||||
export const ICON_NAME = "excalidraw-icon";
|
||||
export const MAX_COLORS = 5;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, FRONTMATTER_KEY_CUSTOM_PREFIX } from "src/constants";
|
||||
|
||||
// English
|
||||
export default {
|
||||
// main.ts
|
||||
@@ -45,7 +47,7 @@ export default {
|
||||
TEMPLATE_NAME: "Excalidraw template file",
|
||||
TEMPLATE_DESC: "Full filepath to the Excalidraw template. " +
|
||||
"E.g.: If your template is in the default Excalidraw folder and it's name is " +
|
||||
"Template, the setting would be: Excalidraw/Template",
|
||||
"Template.excalidraw, the setting would be: Excalidraw/Template.excalidraw",
|
||||
FILENAME_HEAD: "Filenam for drawings",
|
||||
FILENAME_DESC: "<p>The auto-generated filename consists of a prefix and a date. " +
|
||||
"e.g.'Drawing 2021-05-24 12.58.07'.</p>"+
|
||||
@@ -64,9 +66,13 @@ export default {
|
||||
"When Obsidian files change, the matching [[link]] in your drawings will also change. " +
|
||||
"If you don't want text accidentally changing in your drawings use [[links|with aliases]].",
|
||||
LINK_BRACKETS_NAME: "Show [[brackets]] around links",
|
||||
LINK_BRACKETS_DESC: "In preview (locked) mode, when parsing Text Elements, place brackets around links",
|
||||
LINK_INDICATOR_NAME:"Link indicator",
|
||||
LINK_INDICATOR_DESC:"In preview (locked) mode, if the Text Element contains a link, precede the text with these characters.",
|
||||
LINK_BRACKETS_DESC: "In preview (locked) mode, when parsing Text Elements, place brackets around links. " +
|
||||
"You can override this setting for a specific drawing by adding '" + FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS +
|
||||
": true/false' to the file\'s frontmatter.",
|
||||
LINK_PREFIX_NAME:"Link prefix",
|
||||
LINK_PREFIX_DESC:"In preview (locked) mode, if the Text Element contains a link, precede the text with these characters. " +
|
||||
"You can override this setting for a specific drawing by adding \'" + FRONTMATTER_KEY_CUSTOM_PREFIX +
|
||||
': "👉 "\' to the file\'s frontmatter.',
|
||||
LINK_CTRL_CLICK_NAME: "CTRL + CLICK on text to open them as links",
|
||||
LINK_CTRL_CLICK_DESC: "You can turn this feature off if it interferes with default Excalidraw features you want to use. If " +
|
||||
"this is turned off, only the link button in the title bar of the drawing pane will open links.",
|
||||
|
||||
59
src/main.ts
59
src/main.ts
@@ -63,8 +63,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
private openDialog: OpenFileDialog;
|
||||
private activeExcalidrawView: ExcalidrawView = null;
|
||||
public lastActiveExcalidrawFilePath: string = null;
|
||||
private workspaceEventHandlers:Map<string,any> = new Map();
|
||||
private vaultEventHandlers:Map<string,any> = new Map();
|
||||
private hover: {linkText: string, sourcePath: string} = {linkText: null, sourcePath: null};
|
||||
private observer: MutationObserver;
|
||||
|
||||
@@ -82,7 +80,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
await this.loadSettings();
|
||||
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
|
||||
|
||||
await initExcalidrawAutomate(this);
|
||||
|
||||
this.registerView(
|
||||
@@ -92,10 +89,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
this.addMarkdownPostProcessor();
|
||||
this.registerCommands();
|
||||
|
||||
this.registerEventListeners();
|
||||
|
||||
//inspiration taken from kanban: https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
|
||||
//inspiration taken from kanban:
|
||||
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
|
||||
this.registerMonkeyPatches();
|
||||
}
|
||||
|
||||
@@ -207,7 +204,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
* @returns
|
||||
*/
|
||||
const hoverEvent = (e:any) => {
|
||||
//@ts-ignore
|
||||
if(!(e.event.ctrlKey||e.event.metaKey)) return;
|
||||
if(!e.linktext) return;
|
||||
this.hover.linkText = e.linktext;
|
||||
@@ -219,10 +215,11 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
//@ts-ignore
|
||||
this.app.workspace.on('hover-link',hoverEvent);
|
||||
this.workspaceEventHandlers.set('hover-link',hoverEvent);
|
||||
};
|
||||
this.registerEvent(
|
||||
//@ts-ignore
|
||||
this.app.workspace.on('hover-link',hoverEvent)
|
||||
);
|
||||
|
||||
//monitoring for div.popover.hover-popover.file-embed.is-loaded to be added to the DOM tree
|
||||
this.observer = new MutationObserver((m)=>{
|
||||
@@ -283,8 +280,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.app.workspace.on("file-menu", fileMenuHandler)
|
||||
);
|
||||
|
||||
this.workspaceEventHandlers.set("file-menu",fileMenuHandler);
|
||||
|
||||
this.addCommand({
|
||||
id: "excalidraw-open",
|
||||
name: t("OPEN_EXISTING_NEW_PANE"),
|
||||
@@ -641,8 +636,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
});
|
||||
};
|
||||
self.app.vault.on("rename",renameEventHandler);
|
||||
this.vaultEventHandlers.set("rename",renameEventHandler);
|
||||
self.registerEvent(
|
||||
self.app.vault.on("rename",renameEventHandler)
|
||||
);
|
||||
|
||||
const modifyEventHandler = async (file:TFile) => {
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
@@ -653,13 +649,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
});
|
||||
}
|
||||
self.app.vault.on("modify",modifyEventHandler);
|
||||
this.vaultEventHandlers.set("modify",modifyEventHandler);
|
||||
self.registerEvent(
|
||||
self.app.vault.on("modify",modifyEventHandler)
|
||||
)
|
||||
|
||||
//watch file delete and delete corresponding .svg
|
||||
//watch file delete and delete corresponding .svg and .png
|
||||
const deleteEventHandler = async (file:TFile) => {
|
||||
if (!(file instanceof TFile)) return;
|
||||
if (!self.isExcalidrawFile(file)) return;
|
||||
if (!(file instanceof TFile)) return;
|
||||
//@ts-ignore
|
||||
if (file.unsaveCachedData && !file.unsafeCachedData.search(/---\n[\s\S]*excalidraw-plugin:\s*(locked|unlocked)\n[\s\S]*---/gm)==-1) return;
|
||||
|
||||
//close excalidraw view where this file is open
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
@@ -680,8 +678,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
});
|
||||
}
|
||||
}
|
||||
self.app.vault.on("delete",deleteEventHandler);
|
||||
this.vaultEventHandlers.set("delete",deleteEventHandler);
|
||||
self.registerEvent(
|
||||
self.app.vault.on("delete",deleteEventHandler)
|
||||
);
|
||||
|
||||
//save open drawings when user quits the application
|
||||
const quitEventHandler = (tasks: Tasks) => {
|
||||
@@ -690,14 +689,14 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
(leaves[i].view as ExcalidrawView).save();
|
||||
}
|
||||
}
|
||||
self.app.workspace.on("quit",quitEventHandler);
|
||||
this.workspaceEventHandlers.set("quit",quitEventHandler);
|
||||
self.registerEvent(
|
||||
self.app.workspace.on("quit",quitEventHandler)
|
||||
);
|
||||
|
||||
//save Excalidraw leaf and update embeds when switching to another leaf
|
||||
const activeLeafChangeEventHandler = async (leaf:WorkspaceLeaf) => {
|
||||
const activeview:ExcalidrawView = (leaf.view instanceof ExcalidrawView) ? leaf.view as ExcalidrawView : null;
|
||||
if(self.activeExcalidrawView && self.activeExcalidrawView != activeview) {
|
||||
//console.log("ExcalidrawPlugin.activeLeafChangeEventHandler()");
|
||||
await self.activeExcalidrawView.save();
|
||||
self.triggerEmbedUpdates(self.activeExcalidrawView.file?.path);
|
||||
}
|
||||
@@ -706,19 +705,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
self.lastActiveExcalidrawFilePath = self.activeExcalidrawView.file?.path;
|
||||
}
|
||||
};
|
||||
self.app.workspace.on("active-leaf-change",activeLeafChangeEventHandler);
|
||||
this.workspaceEventHandlers.set("active-leaf-change",activeLeafChangeEventHandler);
|
||||
self.registerEvent(
|
||||
self.app.workspace.on("active-leaf-change",activeLeafChangeEventHandler)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
destroyExcalidrawAutomate();
|
||||
for(const key of this.vaultEventHandlers.keys())
|
||||
this.app.vault.off(key,this.vaultEventHandlers.get(key))
|
||||
for(const key of this.workspaceEventHandlers.keys())
|
||||
this.app.workspace.off(key,this.workspaceEventHandlers.get(key));
|
||||
this.observer.disconnect();
|
||||
|
||||
const excalidrawLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
excalidrawLeaves.forEach((leaf) => {
|
||||
this.setMarkdownView(leaf);
|
||||
@@ -778,7 +773,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
private getNextDefaultFilename():string {
|
||||
return this.settings.drawingFilenamePrefix + window.moment().format(this.settings.drawingFilenameDateTime)+'.md';
|
||||
return this.settings.drawingFilenamePrefix + window.moment().format(this.settings.drawingFilenameDateTime)+'.excalidraw.md';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
|
||||
this.inputEl.onkeyup = (e) => {
|
||||
if(e.key=="Enter" && this.action == openDialogAction.openFile) {
|
||||
if (this.containerEl.innerText.includes(EMPTY_MESSAGE)) {
|
||||
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.md', this.onNewPane);
|
||||
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.excalidraw.md', this.onNewPane);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
@@ -62,11 +62,8 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
|
||||
break;
|
||||
case(openDialogAction.insertLink):
|
||||
//TO-DO
|
||||
//change to this.app.metadataCache.fileToLinktext(file: TFile, sourcePath: string, omitMdExtension?: boolean): string;
|
||||
|
||||
//@ts-ignore
|
||||
const filepath = this.app.metadataCache.getLinkpathDest(item.path,this.drawingPath)[0].path;
|
||||
this.addText("[["+(filepath.endsWith(".md")?filepath.substr(0,filepath.length-3):filepath)+"]]"); //.md files don't need the extension
|
||||
const filepath = this.app.metadataCache.fileToLinktext(item,this.drawingPath,true);
|
||||
this.addText("[["+filepath+"]]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface ExcalidrawSettings {
|
||||
drawingFilenameDateTime: string,
|
||||
width: string,
|
||||
showLinkBrackets: boolean,
|
||||
linkIndicator: string,
|
||||
linkPrefix: string,
|
||||
// validLinksOnly: boolean, //valid link as in [[valid Obsidian link]] - how to treat text elements in drawings
|
||||
allowCtrlClick: boolean, //if disabled only the link button in the view header will open links
|
||||
exportWithTheme: boolean,
|
||||
@@ -28,11 +28,11 @@ export interface ExcalidrawSettings {
|
||||
|
||||
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
folder: 'Excalidraw',
|
||||
templateFilePath: 'Excalidraw/Template',
|
||||
templateFilePath: 'Excalidraw/Template.excalidraw',
|
||||
drawingFilenamePrefix: 'Drawing ',
|
||||
drawingFilenameDateTime: 'YYYY-MM-DD HH.mm.ss',
|
||||
width: '400',
|
||||
linkIndicator: ">> ",
|
||||
linkPrefix: ">> ",
|
||||
showLinkBrackets: true,
|
||||
// validLinksOnly: false,
|
||||
allowCtrlClick: true,
|
||||
@@ -146,13 +146,13 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("LINK_INDICATOR_NAME"))
|
||||
.setDesc(t("LINK_INDICATOR_DESC"))
|
||||
.setName(t("LINK_PREFIX_NAME"))
|
||||
.setDesc(t("LINK_PREFIX_DESC"))
|
||||
.addText(text => text
|
||||
.setPlaceholder('>> ')
|
||||
.setValue(this.plugin.settings.linkIndicator)
|
||||
.setValue(this.plugin.settings.linkPrefix)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.linkIndicator = value;
|
||||
this.plugin.settings.linkPrefix = value;
|
||||
await this.plugin.saveSettings();
|
||||
reloadDrawings();
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user