mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
9 Commits
1.2.0-alph
...
1.2.0-rc-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc342189b | ||
|
|
454c68b4b9 | ||
|
|
09889d7ed3 | ||
|
|
096efc45d7 | ||
|
|
55291d8c27 | ||
|
|
e81787ee4b | ||
|
|
ebcf807501 | ||
|
|
bd155eced3 | ||
|
|
2b7d0d5dc2 |
@@ -1,6 +1,5 @@
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import {
|
||||
ExcalidrawElement,
|
||||
FillStyle,
|
||||
StrokeStyle,
|
||||
StrokeSharpness,
|
||||
@@ -12,7 +11,12 @@ import {
|
||||
} from "obsidian"
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import { getJSON } from "./ExcalidrawData";
|
||||
import { CASCADIA_FONT, FRONTMATTER, nanoid, VIRGIL_FONT } from "./constants";
|
||||
import {
|
||||
FRONTMATTER,
|
||||
nanoid,
|
||||
JSON_stringify,
|
||||
JSON_parse
|
||||
} from "./constants";
|
||||
|
||||
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
|
||||
|
||||
@@ -159,7 +163,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
elements.push(this.elementsDict[this.elementIds[i]]);
|
||||
}
|
||||
navigator.clipboard.writeText(
|
||||
JSON.stringify({
|
||||
JSON_stringify({
|
||||
"type":"excalidraw/clipboard",
|
||||
"elements": elements,
|
||||
}));
|
||||
@@ -171,11 +175,11 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
elements.push(this.elementsDict[this.elementIds[i]]);
|
||||
}
|
||||
plugin.createDrawing(
|
||||
params?.filename ? params.filename + '.excalidraw' : this.plugin.getNextDefaultFilename(),
|
||||
params?.filename ? params.filename + '.excalidraw.md' : this.plugin.getNextDefaultFilename(),
|
||||
params?.onNewPane ? params.onNewPane : false,
|
||||
params?.foldername ? params.foldername : this.plugin.settings.folder,
|
||||
FRONTMATTER + exportSceneToMD(
|
||||
JSON.stringify({
|
||||
JSON_stringify({
|
||||
type: "excalidraw",
|
||||
version: 2,
|
||||
source: "https://excalidraw.com",
|
||||
@@ -208,7 +212,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
elements.push(this.elementsDict[this.elementIds[i]]);
|
||||
}
|
||||
return ExcalidrawView.getSVG(
|
||||
JSON.stringify({
|
||||
{//createDrawing
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
@@ -217,7 +221,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
"theme": template ? template.appState.theme : this.canvas.theme,
|
||||
"viewBackgroundColor": template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor
|
||||
}
|
||||
}),
|
||||
},//),
|
||||
{
|
||||
withBackground: plugin.settings.exportWithBackground,
|
||||
withTheme: plugin.settings.exportWithTheme
|
||||
@@ -231,7 +235,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
elements.push(this.elementsDict[this.elementIds[i]]);
|
||||
}
|
||||
return ExcalidrawView.getPNG(
|
||||
JSON.stringify({
|
||||
{ //JSON_stringify(
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
@@ -240,7 +244,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
"theme": template ? template.appState.theme : this.canvas.theme,
|
||||
"viewBackgroundColor": template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor
|
||||
}
|
||||
}),
|
||||
},//),
|
||||
{
|
||||
withBackground: plugin.settings.exportWithBackground,
|
||||
withTheme: plugin.settings.exportWithTheme
|
||||
@@ -440,7 +444,6 @@ async function initFonts () {
|
||||
for (let i=1;i<=3;i++) {
|
||||
await (document as any).fonts.load('20px ' + getFontFamily(i));
|
||||
}
|
||||
console.log("Fonts Ready");
|
||||
}
|
||||
|
||||
export function measureText (newText:string, fontSize:number, fontFamily:number) {
|
||||
@@ -477,7 +480,7 @@ async function getTemplate(fileWithPath: string):Promise<{elements: any,appState
|
||||
const file = vault.getAbstractFileByPath(normalizePath(fileWithPath));
|
||||
if(file && file instanceof TFile) {
|
||||
const data = await vault.read(file);
|
||||
const excalidrawData = JSON.parse(getJSON(data));
|
||||
const excalidrawData = JSON_parse(getJSON(data));
|
||||
return {
|
||||
elements: excalidrawData.elements,
|
||||
appState: excalidrawData.appState,
|
||||
@@ -496,7 +499,7 @@ async function getTemplate(fileWithPath: string):Promise<{elements: any,appState
|
||||
*/
|
||||
export function exportSceneToMD(data:string): string {
|
||||
if(!data) return "";
|
||||
const excalidrawData = JSON.parse(data);
|
||||
const excalidrawData = JSON_parse(data);
|
||||
const textElements = excalidrawData.elements?.filter((el:any)=> el.type=="text")
|
||||
let outString = '# Text Elements\n';
|
||||
let id:string;
|
||||
@@ -511,5 +514,5 @@ async function getTemplate(fileWithPath: string):Promise<{elements: any,appState
|
||||
}
|
||||
outString += te.text+' ^'+id+'\n\n';
|
||||
}
|
||||
return outString + '# Drawing\n'+ data;
|
||||
return outString + '# Drawing\n'+ data.replaceAll("[","[");
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
import { App, TFile } from "obsidian";
|
||||
import { nanoid} from "./constants";
|
||||
import { App, normalizePath, TFile } from "obsidian";
|
||||
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";
|
||||
import {
|
||||
JSON_stringify,
|
||||
JSON_parse
|
||||
} from "./constants";
|
||||
|
||||
//![[link|alias]]
|
||||
//1 2 3 4 5 6
|
||||
@@ -25,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) {
|
||||
@@ -40,20 +48,31 @@ 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;
|
||||
if (this.settings.syncExcalidraw) {
|
||||
const excalfile = file.path.substring(0,file.path.lastIndexOf('.md')) + '.excalidraw';
|
||||
const f = this.app.vault.getAbstractFileByPath(excalfile);
|
||||
if(f && f instanceof TFile && f.stat.mtime>file.stat.mtime) { //the .excalidraw file is newer then the .md file
|
||||
const d = await this.app.vault.read(f);
|
||||
this.scene = JSON.parse(d);
|
||||
}
|
||||
}
|
||||
|
||||
//Load scene: Read the JSON string after "# Drawing"
|
||||
let parts = data.matchAll(/\n# Drawing\n(.*)/gm).next();
|
||||
if(!(parts.value && parts.value.length>1)) return false; //JSON not found or invalid
|
||||
this.scene = JSON.parse(parts.value[1]);
|
||||
|
||||
if(!this.scene) { //scene was not loaded from .excalidraw
|
||||
this.scene = JSON_parse(parts.value[1]);
|
||||
}
|
||||
//Trim data to remove the JSON string
|
||||
data = data.substring(0,parts.value.index);
|
||||
|
||||
@@ -127,7 +146,7 @@ export class ExcalidrawData {
|
||||
//get scene text elements
|
||||
const texts = this.scene.elements?.filter((el:any)=> el.type=="text")
|
||||
|
||||
let jsonString = JSON.stringify(this.scene);
|
||||
let jsonString = JSON_stringify(this.scene);
|
||||
|
||||
let dirty:boolean = false; //to keep track if the json has changed
|
||||
let id:string; //will be used to hold the new 8 char long ID for textelements that don't yet appear under # Text Elements
|
||||
@@ -148,7 +167,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
}
|
||||
if(dirty) { //reload scene json in case it has changed
|
||||
this.scene = JSON.parse(jsonString);
|
||||
this.scene = JSON_parse(jsonString);
|
||||
}
|
||||
|
||||
return dirty;
|
||||
@@ -219,11 +238,11 @@ export class ExcalidrawData {
|
||||
//1 2
|
||||
const REG_FILE_BLOCKREF = /(.*)#\^(.*)/g;
|
||||
const parts=text.matchAll(REG_FILE_BLOCKREF).next();
|
||||
if(!parts.value[1] || !parts.value[2]) return text; //filename and/or blockref not found
|
||||
if(parts.done || !parts.value[1] || !parts.value[2]) return text; //filename and/or blockref not found
|
||||
const file = this.app.metadataCache.getFirstLinkpathDest(parts.value[1],this.file.path);
|
||||
const contents = await this.app.vault.cachedRead(file);
|
||||
//get transcluded line and take the part before ^blockref
|
||||
const REG_TRANSCLUDE = new RegExp("(.*)\\s\\^" + parts.value[2] + "\\n");
|
||||
const REG_TRANSCLUDE = new RegExp("(.*)\\s\\^" + parts.value[2]);
|
||||
const res = contents.match(REG_TRANSCLUDE);
|
||||
if(res) return res[1];
|
||||
return text;//if blockref not found in file, return the input string
|
||||
@@ -255,7 +274,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
outString += text.substring(position,text.length);
|
||||
if (linkIcon) {
|
||||
outString = this.linkIndicator + outString;
|
||||
outString = this.linkPrefix + outString;
|
||||
}
|
||||
|
||||
return outString;
|
||||
@@ -271,21 +290,21 @@ export class ExcalidrawData {
|
||||
for(const key of this.textElements.keys()){
|
||||
outString += this.textElements.get(key).raw+' ^'+key+'\n\n';
|
||||
}
|
||||
return outString + '# Drawing\n' + JSON.stringify(this.scene);
|
||||
return outString + '# Drawing\n' + JSON_stringify(this.scene);
|
||||
}
|
||||
|
||||
public syncElements(newScene:any):boolean {
|
||||
//console.log("Excalidraw.Data.syncElements()");
|
||||
this.scene = JSON.parse(newScene);
|
||||
const result = this.findNewTextElementsInScene();
|
||||
this.scene = newScene;//JSON_parse(newScene);
|
||||
const result = this.setLinkPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
this.updateTextElementsFromSceneRawOnly();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async updateScene(newScene:any){
|
||||
//console.log("Excalidraw.Data.updateScene()");
|
||||
this.scene = JSON.parse(newScene);
|
||||
const result = this.findNewTextElementsInScene();
|
||||
this.scene = JSON_parse(newScene);
|
||||
const result = this.setLinkPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
await this.updateTextElementsFromScene();
|
||||
if(result) {
|
||||
await this.updateSceneTextElements();
|
||||
@@ -298,4 +317,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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
FRONTMATTER_KEY,
|
||||
UNLOCK_ICON_NAME,
|
||||
LOCK_ICON_NAME,
|
||||
|
||||
JSON_stringify,
|
||||
JSON_parse
|
||||
} from './constants';
|
||||
import ExcalidrawPlugin from './main';
|
||||
import {ExcalidrawAutomate} from './ExcalidrawAutomate';
|
||||
@@ -59,7 +60,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
private justLoaded: boolean = false;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private dirty: boolean = false;
|
||||
private autosaveTimer: any = null;
|
||||
public autosaveTimer: any = null;
|
||||
public isTextLocked:boolean = false;
|
||||
private lockedElement:HTMLElement;
|
||||
private unlockedElement:HTMLElement;
|
||||
@@ -73,10 +74,21 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.excalidrawData = new ExcalidrawData(plugin);
|
||||
}
|
||||
|
||||
public async saveSVG(data?: string) {
|
||||
if(!data) {
|
||||
public saveExcalidraw(scene?: any){
|
||||
if(!scene) {
|
||||
if (!this.getScene) return false;
|
||||
data = this.getScene();
|
||||
scene = this.getScene();
|
||||
}
|
||||
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.md')) + '.excalidraw';
|
||||
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
|
||||
if(file && file instanceof TFile) this.app.vault.modify(file,JSON.stringify(scene));//data.replaceAll("[","["));
|
||||
else this.app.vault.create(filepath,JSON.stringify(scene));//.replaceAll("[","["));
|
||||
}
|
||||
|
||||
public async saveSVG(scene?: any) {
|
||||
if(!scene) {
|
||||
if (!this.getScene) return false;
|
||||
scene = this.getScene();
|
||||
}
|
||||
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.md')) + '.svg';
|
||||
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
|
||||
@@ -84,7 +96,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
withBackground: this.plugin.settings.exportWithBackground,
|
||||
withTheme: this.plugin.settings.exportWithTheme
|
||||
}
|
||||
const svg = ExcalidrawView.getSVG(data,exportSettings);
|
||||
const svg = ExcalidrawView.getSVG(scene,exportSettings);
|
||||
if(!svg) return;
|
||||
const svgString = ExcalidrawView.embedFontsInSVG(svg).outerHTML;
|
||||
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
|
||||
@@ -102,16 +114,16 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return svg;
|
||||
}
|
||||
|
||||
public async savePNG(data?: string) {
|
||||
if(!data) {
|
||||
public async savePNG(scene?: any) {
|
||||
if(!scene) {
|
||||
if (!this.getScene) return false;
|
||||
data = this.getScene();
|
||||
scene = this.getScene();
|
||||
}
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: this.plugin.settings.exportWithBackground,
|
||||
withTheme: this.plugin.settings.exportWithTheme
|
||||
}
|
||||
const png = await ExcalidrawView.getPNG(data,exportSettings);
|
||||
const png = await ExcalidrawView.getPNG(scene,exportSettings);
|
||||
if(!png) return;
|
||||
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.md')) + '.png';
|
||||
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
|
||||
@@ -126,19 +138,23 @@ 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) {
|
||||
const scene = this.getScene();
|
||||
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
|
||||
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
|
||||
if(this.excalidrawData.syncElements(scene)) {
|
||||
|
||||
if(this.excalidrawData.syncElements(this.getScene())) {
|
||||
this.loadDrawing(false);
|
||||
}
|
||||
let trimLocation = this.data.search("# Text Elements\n");
|
||||
if(trimLocation == -1) trimLocation = this.data.search("# Drawing\n");
|
||||
if(trimLocation == -1) return this.data;
|
||||
|
||||
const scene = this.excalidrawData.scene;
|
||||
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
|
||||
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
|
||||
if(this.plugin.settings.autoexportExcalidraw) this.saveExcalidraw(scene);
|
||||
|
||||
const header = this.data.substring(0,trimLocation)
|
||||
.replace(/excalidraw-plugin:\s.*\n/,FRONTMATTER_KEY+": " + (this.isTextLocked ? "locked\n" : "unlocked\n"));
|
||||
return header + this.excalidrawData.generateMD();
|
||||
@@ -239,16 +255,19 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
private setupAutosaveTimer() {
|
||||
public setupAutosaveTimer() {
|
||||
const timer = async () => {
|
||||
//console.log("ExcalidrawView.autosaveTimer(), dirty", this.dirty);
|
||||
if(this.dirty) {
|
||||
console.log("autosave",Date.now());
|
||||
this.dirty = false;
|
||||
if(this.excalidrawRef) await this.save();
|
||||
this.plugin.triggerEmbedUpdates();
|
||||
}
|
||||
}
|
||||
this.autosaveTimer = setInterval(timer,30000);
|
||||
if(this.plugin.settings.autosave) {
|
||||
this.autosaveTimer = setInterval(timer,30000);
|
||||
}
|
||||
}
|
||||
|
||||
//save current drawing when user closes workspace leaf
|
||||
@@ -280,6 +299,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
async setViewData (data: string, clear: boolean) {
|
||||
this.app.workspace.onLayoutReady(async ()=>{
|
||||
//console.log("ExcalidrawView.setViewData()");
|
||||
this.plugin.settings.drawingOpenCount++;
|
||||
this.plugin.saveSettings();
|
||||
this.lock(data.search("excalidraw-plugin: locked\n")>-1,false);
|
||||
if(!(await this.excalidrawData.loadData(data, this.file,this.isTextLocked))) return;
|
||||
if(clear) this.clear();
|
||||
@@ -309,6 +330,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
//Compatibility mode with .excalidraw files
|
||||
/* canAcceptExtension(extension: string) {
|
||||
return extension == "excalidraw";
|
||||
}*/
|
||||
|
||||
// gets the title of the document
|
||||
getDisplayText() {
|
||||
@@ -344,7 +369,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(JSON.stringify(this.getScene())), this.file.basename+'.excalidraw');
|
||||
});
|
||||
})
|
||||
.addItem((item) => {
|
||||
@@ -397,7 +422,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
async getLibrary() {
|
||||
const data = JSON.parse(this.plugin.settings.library);
|
||||
const data = JSON_parse(this.plugin.settings.library);
|
||||
return data?.library ? data.library : [];
|
||||
}
|
||||
|
||||
@@ -495,7 +520,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
const el: ExcalidrawElement[] = excalidrawRef.current.getSceneElements();
|
||||
const st: AppState = excalidrawRef.current.getAppState();
|
||||
return JSON.stringify({
|
||||
return { //JSON_stringify(
|
||||
type: "excalidraw",
|
||||
version: 2,
|
||||
source: "https://excalidraw.com",
|
||||
@@ -519,7 +544,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
currentItemLinearStrokeSharpness: st.currentItemLinearStrokeSharpness,
|
||||
gridSize: st.gridSize,
|
||||
}
|
||||
});
|
||||
};//);
|
||||
};
|
||||
|
||||
this.refresh = () => {
|
||||
@@ -542,13 +567,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(this.isTextLocked && (e.target instanceof HTMLCanvasElement) && this.getSelectedText(true)) { //text element is selected
|
||||
const now = (new Date()).getTime();
|
||||
if(now-timestamp < 600) { //double click
|
||||
var event = new MouseEvent('dblclick', {
|
||||
let event = new MouseEvent('dblclick', {
|
||||
'view': window,
|
||||
'bubbles': true,
|
||||
'cancelable': true,
|
||||
});
|
||||
e.target.dispatchEvent(event);
|
||||
new Notice(t("UNLOCK_TO_EDIT"))
|
||||
new Notice(t("UNLOCK_TO_EDIT"));
|
||||
timestamp = now;
|
||||
return;
|
||||
}
|
||||
@@ -608,15 +633,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
ReactDOM.render(reactElement,(this as any).contentEl);
|
||||
}
|
||||
|
||||
public static getSVG(data:string, exportSettings:ExportSettings):SVGSVGElement {
|
||||
public static getSVG(scene:any, exportSettings:ExportSettings):SVGSVGElement {
|
||||
try {
|
||||
const excalidrawData = JSON.parse(data);
|
||||
return exportToSvg({
|
||||
elements: excalidrawData.elements,
|
||||
elements: scene.elements,
|
||||
appState: {
|
||||
exportBackground: exportSettings.withBackground,
|
||||
exportWithDarkMode: exportSettings.withTheme ? (excalidrawData.appState?.theme=="light" ? false : true) : false,
|
||||
... excalidrawData.appState,},
|
||||
exportWithDarkMode: exportSettings.withTheme ? (scene.appState?.theme=="light" ? false : true) : false,
|
||||
... scene.appState,},
|
||||
exportPadding:10,
|
||||
metadata: "Generated by Excalidraw-Obsidian plugin",
|
||||
});
|
||||
@@ -625,15 +649,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
public static async getPNG(data:string, exportSettings:ExportSettings) {
|
||||
public static async getPNG(scene:any, exportSettings:ExportSettings) {
|
||||
try {
|
||||
const excalidrawData = JSON.parse(data);
|
||||
return await Excalidraw.exportToBlob({
|
||||
elements: excalidrawData.elements,
|
||||
elements: scene.elements,
|
||||
appState: {
|
||||
exportBackground: exportSettings.withBackground,
|
||||
exportWithDarkMode: exportSettings.withTheme ? (excalidrawData.appState?.theme=="light" ? false : true) : false,
|
||||
... excalidrawData.appState,},
|
||||
exportWithDarkMode: exportSettings.withTheme ? (scene.appState?.theme=="light" ? false : true) : false,
|
||||
... scene.appState,},
|
||||
mimeType: "image/png",
|
||||
exportWithDarkMode: "true",
|
||||
metadata: "Generated by Excalidraw-Obsidian plugin",
|
||||
|
||||
53
src/MigrationPrompt.ts
Normal file
53
src/MigrationPrompt.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { App, Modal } from "obsidian";
|
||||
import { t } from "./lang/helpers";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
|
||||
export class MigrationPrompt extends Modal {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
|
||||
constructor(app: App, plugin:ExcalidrawPlugin) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.titleEl.setText("Welcome to Excalidraw 1.2");
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
const div = this.contentEl.createDiv();
|
||||
div.addClass("excalidarw-prompt-div");
|
||||
div.style.maxWidth = "600px";
|
||||
div.createEl('p',{text: "This version comes with many new features and possibilities. Please read the description in Community Plugins to find out more."});
|
||||
div.createEl('p',{text: ""} , (el) => {
|
||||
el.innerHTML = "<b>⚠ ATTENTION</b>: Drawings you've created with version 1.1.x need to be converted, they WILL NOT WORK out of the box. "+
|
||||
"During conversion your old *.excalidraw files will be replaced with new *.excalidraw.md files.";
|
||||
});
|
||||
div.createEl('p',{text: ""}, (el) => {//files manually follow one of two options:
|
||||
el.innerHTML = "To convert your drawings you have the following options:<br><ul>" +
|
||||
"<li>Click <code>CONVERT</code> to convert all of your *.excalidraw files now, or if you prefer to make a backup first, then click <code>CANCEL</code>.</li>" +
|
||||
"<li>Using the Command Palette select <code>Excalidraw: Convert *.excalidraw files to *.excalidraw.md files</code></li>" +
|
||||
"<li>Right click an *.excalidraw file in File Explorer and select one of the following to convert files individually: <ul>"+
|
||||
"<li><code>*.excalidraw => *.excalidraw.md</code></li>"+
|
||||
"<li><code>*.excalidraw => *.md (Logseq compatibility)</code>. This option will retain the original *.excalidraw file next to the new Obsidian format. " +
|
||||
"Make sure you also enable <code>Compatibility features</code> in Settings for a full solution.</li></ul></li></ul>";
|
||||
});
|
||||
div.createEl('p',{text: "This message will only appear maximum 3 times in case you have *.excalidraw files in your Vault."});
|
||||
const bConvert = div.createEl('button', {text: "CONVERT FILES"});
|
||||
bConvert.onclick = (ev)=>{
|
||||
this.plugin.convertExcalidrawToMD();
|
||||
this.close();
|
||||
};
|
||||
const bCancel = div.createEl('button', {text: "CANCEL"});
|
||||
bCancel.onclick = (ev)=>{
|
||||
this.close();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
//This is to avoid brackets littering graph view with links
|
||||
export function JSON_stringify(x:any):string {return JSON.stringify(x).replaceAll("[","[");}
|
||||
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,77 +1,89 @@
|
||||
// English
|
||||
import { FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, FRONTMATTER_KEY_CUSTOM_PREFIX } from "src/constants";
|
||||
|
||||
// English
|
||||
export default {
|
||||
// main.ts
|
||||
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
|
||||
TOGGLE_MODE: "Toggle between Excalidraw and markdown mode",
|
||||
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
|
||||
MIGRATE_TO_2: "MIGRATE to version 2: convert .excalidraw files to .md files",
|
||||
CREATE_NEW : "New Excalidraw",
|
||||
CONVERT_EXCALIDRAW: "Convert *.excalidraw to *.md files",
|
||||
CREATE_NEW : "New Excalidraw drawing",
|
||||
CONVERT_FILE_KEEP_EXT: "*.excalidraw => *.excalidraw.md",
|
||||
CONVERT_FILE_REPLACE_EXT: "*.excalidraw => *.md (Logseq compatibility)",
|
||||
OPEN_EXISTING_NEW_PANE: "Open an existing drawing - IN A NEW PANE",
|
||||
OPEN_EXISTING_ACTIVE_PANE: "Open an existing drawing - IN THE CURRENT ACTIVE PANE",
|
||||
TRANSCLUDE: "Transclude (embed) an Excalidraw drawing",
|
||||
TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited Excalidraw drawing",
|
||||
TRANSCLUDE: "Transclude (embed) a drawing",
|
||||
TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited drawing",
|
||||
NEW_IN_NEW_PANE: "Create a new drawing - IN A NEW PANE",
|
||||
NEW_IN_ACTIVE_PANE: "Create a new drawing - IN THE CURRENT ACTIVE PANE",
|
||||
NEW_IN_NEW_PANE_EMBED: "Create a new drawing - IN A NEW PANE - and embed in current document",
|
||||
NEW_IN_ACTIVE_PANE_EMBED: "Create a new drawing - IN THE CURRENT ACTIVE PANE - and embed in current document",
|
||||
EXPORT_SVG: "Export SVG. Save it next to the current file",
|
||||
EXPORT_PNG: "Export PNG. Save it next to the current file",
|
||||
TOGGLE_LOCK: "Toggle text element edit lock/unlock",
|
||||
NEW_IN_NEW_PANE_EMBED: "Create a new drawing - IN A NEW PANE - and embed into active document",
|
||||
NEW_IN_ACTIVE_PANE_EMBED: "Create a new drawing - IN THE CURRENT ACTIVE PANE - and embed into active document",
|
||||
EXPORT_SVG: "Save as SVG next to the current file",
|
||||
EXPORT_PNG: "Save as PNG next to the current file",
|
||||
TOGGLE_LOCK: "Toggle Text Element edit LOCK/UNLOCK",
|
||||
INSERT_LINK: "Insert link to file",
|
||||
INSERT_LATEX: "Insert LaTeX-symbol (e.g. $\\theta$)",
|
||||
ENTER_LATEX: "Enter a valid LaTeX expression",
|
||||
|
||||
|
||||
//ExcalidrawView.ts
|
||||
OPEN_AS_MD: "Open as markdown",
|
||||
SAVE_AS_PNG: "Save as PNG into Vault (CTRL/META+click to export)",
|
||||
SAVE_AS_SVG: "Save as SVG into Vault (CTRL/META+click to export)",
|
||||
OPEN_LINK: "Open selected text as link\n(SHIFT+click to open in a new pane)",
|
||||
EXPORT_EXCALIDRAW: "Export to .Excalidraw file",
|
||||
UNLOCK_TO_EDIT: "Unlock text elements to edit",
|
||||
LINK_BUTTON_CLICK_NO_TEXT: 'Select a text element containing an internal or external link.\n'+
|
||||
'SHIFT CLICK this button to open link in a new pane.\n'+
|
||||
'CTRL/META CLICK on the Text Element on the canvas also works!',
|
||||
TEXT_ELEMENT_EMPTY: "Text element is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
|
||||
OPEN_AS_MD: "Open as Markdown",
|
||||
SAVE_AS_PNG: "Save as PNG into Vault (CTRL/META+CLICK to export)",
|
||||
SAVE_AS_SVG: "Save as SVG into Vault (CTRL/META+CLICK to export)",
|
||||
OPEN_LINK: "Open selected text as link\n(SHIFT+CLICK to open in a new pane)",
|
||||
EXPORT_EXCALIDRAW: "Export to an .Excalidraw file",
|
||||
UNLOCK_TO_EDIT: "UNLOCK Text Elements to edit",
|
||||
LINK_BUTTON_CLICK_NO_TEXT: 'Select a Text Element containing an internal or external link.\n'+
|
||||
'SHIFT CLICK this button to open the link in a new pane.\n'+
|
||||
'CTRL/META CLICK the Text Element on the canvas has the same effect!',
|
||||
TEXT_ELEMENT_EMPTY: "Text Element is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
|
||||
FILENAME_INVALID_CHARS: 'File name cannot contain any of the following characters: * " \\ < > : | ?',
|
||||
FILE_DOES_NOT_EXIST: "File does not exist. Hold down ALT (or ALT+SHIFT) and click link button to create a new file.",
|
||||
FORCE_SAVE: "Force-save to update transclusions in adjacent panes\n(Please note, that autosave is always on)",
|
||||
LOCK: "Text Elements are unlocked. Click to lock.",
|
||||
UNLOCK: "Text Elements are locked. Click to unlock.",
|
||||
FILE_DOES_NOT_EXIST: "File does not exist. Hold down ALT (or ALT+SHIFT) and CLICK link button to create a new file.",
|
||||
FORCE_SAVE: "Force-save to update transclusions in adjacent panes.\n(Please note, that autosave is always on)",
|
||||
LOCK: "Text Elements are unlocked. Click to LOCK.",
|
||||
UNLOCK: "Text Elements are locked. Click to UNLOCK.",
|
||||
NOFILE: "Excalidraw (no file)",
|
||||
|
||||
//settings.ts
|
||||
FOLDER_NAME: "Excalidraw folder",
|
||||
FOLDER_DESC: "Default location for your drawings. If empty, drawings will be created in the Vault root.",
|
||||
FOLDER_DESC: "Default location for new drawings. If empty, drawings will be created in the Vault root.",
|
||||
TEMPLATE_NAME: "Excalidraw template file",
|
||||
TEMPLATE_DESC: "Full filepath to the Excalidraw template. " +
|
||||
"E.g.: If your template is in the default Excalidraw folder, the setting would be: Excalidraw/Template",
|
||||
FILENAME_HEAD: "New drawing filename",
|
||||
FILENAME_DESC: '<p>The auto-generated filename consists of a prefix and a date. ' +
|
||||
'e.g."Drawing 2021-05-24 12.58.07".</p>'+
|
||||
'<p>Click this link for the <a href="https://momentjs.com/docs/#/displaying/format/">'+
|
||||
'date and time format reference</a>.</p>',
|
||||
"E.g.: If your template is in the default Excalidraw folder and it's name is " +
|
||||
"Template.excalidraw, the setting would be: Excalidraw/Template.excalidraw",
|
||||
AUTOSAVE_NAME: "Autosave",
|
||||
AUTOSAVE_DESC: "Automatically save the active drawing every 30 seconds. Save normally happens when you close Excalidraw or Obsidian, or move "+
|
||||
"focus to another pane. In rare cases autosave may slightly disrupt your drawing flow. I created this feature with mobile " +
|
||||
"phones in mind (I only have experience with Android), where 'swiping out Obsidian to close it' led to some data loss, and because " +
|
||||
"I wasn't able to force save on application termination on mobiles. If you use Excalidraw on a desktop this is likely not needed.",
|
||||
FILENAME_HEAD: "Filename",
|
||||
FILENAME_DESC: "<p>The auto-generated filename consists of a prefix and a date. " +
|
||||
"e.g.'Drawing 2021-05-24 12.58.07'.</p>"+
|
||||
"<p>Click this link for the <a href='https://momentjs.com/docs/#/displaying/format/'>"+
|
||||
"date and time format reference</a>.</p>",
|
||||
FILENAME_SAMPLE: "The current file format is: <b>",
|
||||
FILENAME_PREFIX_NAME: "Filename prefix",
|
||||
FILENAME_PREFIX_DESC: "The first part of the filename",
|
||||
FILENAME_DATE_NAME: "Filename date",
|
||||
FILENAME_DATE_DESC: "The second part of the filename",
|
||||
LINKS_HEAD: "Links in drawings",
|
||||
LINKS_DESC: 'CTRL/META + CLICK on Text Elements to open them as links. ' +
|
||||
'If the selected text has more than one [[valid Obsidian links]], only the first will be opened. ' +
|
||||
'If the text starts as a valid web link (i.e. https:// or http://), then ' +
|
||||
'the plugin will try to open it in a browser. ' +
|
||||
'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 [[bracket]] around links",
|
||||
LINK_BRACKETS_DESC: "When parsing text elements, place brackets around links",
|
||||
LINK_INDICATOR_NAME:"Link indicator",
|
||||
LINK_INDICATOR_DESC:"If text element contains a link, precede the text with these characters in preview.",
|
||||
LINKS_HEAD: "Links",
|
||||
LINKS_DESC: "CTRL/META + CLICK on Text Elements to open them as links. " +
|
||||
"If the selected text has more than one [[valid Obsidian links]], only the first will be opened. " +
|
||||
"If the text starts as a valid web link (i.e. https:// or http://), then " +
|
||||
"the plugin will open it in a browser. " +
|
||||
"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. " +
|
||||
"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.',
|
||||
EMBED_HEAD: "Embedded image settings",
|
||||
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.",
|
||||
EMBED_HEAD: "Embed & Export",
|
||||
EMBED_WIDTH_NAME: "Default width of embedded (transcluded) image",
|
||||
EMBED_WIDTH_DESC: "The default width of an embedded drawing. You can specify a custom " +
|
||||
"width when embedding an image using the ![[drawing.excalidraw|100]] or " +
|
||||
@@ -79,24 +91,31 @@ export default {
|
||||
EXPORT_BACKGROUND_NAME: "Export image with background",
|
||||
EXPORT_BACKGROUND_DESC: "If turned off, the exported image will be transparent.",
|
||||
EXPORT_THEME_NAME: "Export image with theme",
|
||||
EXPORT_THEME_DESC: 'Export the image matching the dark/light theme of your drawing. If turned off, ' +
|
||||
'drawings created in drak mode will appear as they would in light mode.',
|
||||
EXPORT_SVG_NAME: "Auto-export SVG",
|
||||
EXPORT_SVG_DESC: 'Automatically create an SVG export of your drawing matching the title of your file. ' +
|
||||
'The plugin will save the .SVG file in the same folder as the drawing. '+
|
||||
'Embed the .svg file into your documents instead of excalidraw to making you embeds platform independent. ' +
|
||||
'While the auto export switch is on, this file will get updated every time you edit the excalidraw drawing with the matching name.',
|
||||
EXPORT_PNG_NAME: "Auto-export PNG",
|
||||
EXPORT_PNG_DESC: "Same as the auto-export SVG, but for PNG.",
|
||||
EXPORT_THEME_DESC: "Export the image matching the dark/light theme of your drawing. If turned off, " +
|
||||
"drawings created in drak mode will appear as they would in light mode.",
|
||||
EXPORT_HEAD: "Export Settings",
|
||||
EXPORT_SYNC_NAME:"Keep the .SVG and/or .PNG filenames in sync with the drawing file",
|
||||
EXPORT_SYNC_DESC:'When turned on, the plugin will automaticaly update the filename of the .SVG and/or .PNG files when the drawing in the same folder (and same name) is renamed. ' +
|
||||
'The plugin will also automatically delete the .SVG and/or .PNG files when the drawing in the same folder (and same name) is deleted. ',
|
||||
EXPORT_SYNC_DESC:"When turned on, the plugin will automaticaly update the filename of the .SVG and/or .PNG files when the drawing in the same folder (and same name) is renamed. " +
|
||||
"The plugin will also automatically delete the .SVG and/or .PNG files when the drawing in the same folder (and same name) is deleted. ",
|
||||
EXPORT_SVG_NAME: "Auto-export SVG",
|
||||
EXPORT_SVG_DESC: "Automatically create an SVG export of your drawing matching the title of your file. " +
|
||||
"The plugin will save the *.SVG file in the same folder as the drawing. "+
|
||||
"Embed the .svg file into your documents instead of excalidraw making you embeds platform independent. " +
|
||||
"While the auto-export switch is on, this file will get updated every time you edit the excalidraw drawing with the matching name.",
|
||||
EXPORT_PNG_NAME: "Auto-export PNG",
|
||||
EXPORT_PNG_DESC: "Same as the auto-export SVG, but for *.PNG",
|
||||
COMPATIBILITY_HEAD: "Compatibility features",
|
||||
EXPORT_EXCALIDRAW_NAME: "Auto-export Excalidraw",
|
||||
EXPORT_EXCALIDRAW_DESC: "Same as the auto-export SVG, but for *.Excalidraw",
|
||||
SYNC_EXCALIDRAW_NAME: "Sync *.excalidraw with *.md version of the same drawing",
|
||||
SYNC_EXCALIDRAW_DESC: "If the modified date of the *.excalidraw file is more recent than the modified date of the *.md file " +
|
||||
"then update the drawing in the .md file based on the .excalidraw file",
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "Select a file then hit enter.",
|
||||
SELECT_FILE: "Select a file then press enter.",
|
||||
NO_MATCH: "No file matches your query.",
|
||||
SELECT_FILE_TO_LINK: "Select file to inster link for.",
|
||||
SELECT_FILE_TO_LINK: "Select the file you want to insert the link for.",
|
||||
TYPE_FILENAME: "Type name of drawing to select.",
|
||||
SELECT_FILE_OR_TYPE_NEW: "Select existing drawing or type name of a new then hit enter.",
|
||||
SELECT_TO_EMBED: "Select drawing to insert into document.",
|
||||
SELECT_FILE_OR_TYPE_NEW: "Select existing drawing or type name of a new drawing then press Enter.",
|
||||
SELECT_TO_EMBED: "Select the drawing to insert into active document.",
|
||||
};
|
||||
|
||||
207
src/main.ts
207
src/main.ts
@@ -15,6 +15,7 @@ import {
|
||||
Tasks,
|
||||
MarkdownRenderer,
|
||||
ViewState,
|
||||
Notice,
|
||||
} from "obsidian";
|
||||
|
||||
import {
|
||||
@@ -34,7 +35,8 @@ import {
|
||||
LOCK_ICON,
|
||||
LOCK_ICON_NAME,
|
||||
UNLOCK_ICON_NAME,
|
||||
UNLOCK_ICON
|
||||
UNLOCK_ICON,
|
||||
JSON_parse
|
||||
} from "./constants";
|
||||
import ExcalidrawView, {ExportSettings} from "./ExcalidrawView";
|
||||
import {getJSON} from "./ExcalidrawData";
|
||||
@@ -55,6 +57,7 @@ import {
|
||||
import { Prompt } from "./Prompt";
|
||||
import { around } from "monkey-around";
|
||||
import { t } from "./lang/helpers";
|
||||
import { MigrationPrompt } from "./MigrationPrompt";
|
||||
|
||||
export default class ExcalidrawPlugin extends Plugin {
|
||||
public excalidrawFileModes: { [file: string]: string } = {};
|
||||
@@ -63,8 +66,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 +83,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
await this.loadSettings();
|
||||
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
|
||||
|
||||
await initExcalidrawAutomate(this);
|
||||
|
||||
this.registerView(
|
||||
@@ -92,13 +92,26 @@ 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();
|
||||
if(this.settings.loadCount<3) this.migrationNotice();
|
||||
}
|
||||
|
||||
private migrationNotice(){
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
self.settings.loadCount++;
|
||||
self.saveSettings();
|
||||
const files = this.app.vault.getFiles().filter((f)=>f.extension=="excalidraw");
|
||||
if(files.length>0) {
|
||||
const prompt = new MigrationPrompt(self.app, self);
|
||||
prompt.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Displays a transcluded .excalidraw image in markdown preview mode
|
||||
*/
|
||||
@@ -127,7 +140,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
withBackground: this.settings.exportWithBackground,
|
||||
withTheme: this.settings.exportWithTheme
|
||||
}
|
||||
let svg = ExcalidrawView.getSVG(getJSON(content),exportSettings);
|
||||
let svg = ExcalidrawView.getSVG(JSON_parse(getJSON(content)),exportSettings);
|
||||
if(!svg) return null;
|
||||
svg = ExcalidrawView.embedFontsInSVG(svg);
|
||||
const img = createEl("img");
|
||||
@@ -154,7 +167,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
attr.fname = drawing.getAttribute("src");
|
||||
file = this.app.metadataCache.getFirstLinkpathDest(attr.fname, ctx.sourcePath);
|
||||
if(file && file instanceof TFile && this.isExcalidrawFile(file)) {
|
||||
attr.fwidth = drawing.getAttribute("width");
|
||||
attr.fwidth = drawing.getAttribute("width") ? drawing.getAttribute("width") : this.settings.width;
|
||||
attr.fheight = drawing.getAttribute("height");
|
||||
alt = drawing.getAttribute("alt");
|
||||
if(alt == attr.fname) alt = ""; //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
|
||||
@@ -175,8 +188,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
div = createDiv(attr.style, (el)=>{
|
||||
el.append(img);
|
||||
el.setAttribute("src",file.path);
|
||||
el.setAttribute("w",attr.fwidth);
|
||||
el.setAttribute("h",attr.fheight);
|
||||
if(attr.fwidth) el.setAttribute("w",attr.fwidth);
|
||||
if(attr.fheight) el.setAttribute("h",attr.fheight);
|
||||
el.onClickEvent((ev)=>{
|
||||
if(ev.target instanceof Element && ev.target.tagName.toLowerCase() != "img") return;
|
||||
let src = el.getAttribute("src");
|
||||
@@ -207,7 +220,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 +231,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)=>{
|
||||
@@ -267,23 +280,53 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.createDrawing(this.getNextDefaultFilename(), e.ctrlKey||e.metaKey);
|
||||
});
|
||||
|
||||
const fileMenuHandler = (menu: Menu, file: TFile) => {
|
||||
if (file instanceof TFolder) {
|
||||
menu.addItem((item: MenuItem) => {
|
||||
item.setTitle(t("CREATE_NEW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.onClick(evt => {
|
||||
this.createDrawing(this.getNextDefaultFilename(),false,file.path);
|
||||
})
|
||||
});
|
||||
}
|
||||
const fileMenuHandlerCreateNew = (menu: Menu, file: TFile) => {
|
||||
menu.addItem((item: MenuItem) => {
|
||||
item.setTitle(t("CREATE_NEW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.onClick(evt => {
|
||||
let folderpath = file.path;
|
||||
if(file instanceof TFile) {
|
||||
folderpath = normalizePath(file.path.substr(0,file.path.lastIndexOf(file.name)));
|
||||
}
|
||||
this.createDrawing(this.getNextDefaultFilename(),false,folderpath);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-menu", fileMenuHandler)
|
||||
this.app.workspace.on("file-menu", fileMenuHandlerCreateNew)
|
||||
);
|
||||
|
||||
this.workspaceEventHandlers.set("file-menu",fileMenuHandler);
|
||||
const fileMenuHandlerConvertKeepExtension = (menu: Menu, file: TFile) => {
|
||||
if(file instanceof TFile && file.extension == "excalidraw") {
|
||||
menu.addItem((item: MenuItem) => {
|
||||
item.setTitle(t("CONVERT_FILE_KEEP_EXT"))
|
||||
.onClick(evt => {
|
||||
this.convertSingleExcalidrawToMD(file,false,false);
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-menu", fileMenuHandlerConvertKeepExtension)
|
||||
);
|
||||
|
||||
const fileMenuHandlerConvertReplaceExtension = (menu: Menu, file: TFile) => {
|
||||
if(file instanceof TFile && file.extension == "excalidraw") {
|
||||
menu.addItem((item: MenuItem) => {
|
||||
item.setTitle(t("CONVERT_FILE_REPLACE_EXT"))
|
||||
.onClick(evt => {
|
||||
this.convertSingleExcalidrawToMD(file,true,true);
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-menu", fileMenuHandlerConvertReplaceExtension)
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: "excalidraw-open",
|
||||
@@ -409,7 +452,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
this.addCommand({
|
||||
id: "toggle-lock",
|
||||
hotkeys: [{modifiers:["Ctrl" || "Meta"], key:"e"}],
|
||||
hotkeys: [{modifiers:["Ctrl" || "Meta","Shift"], key:"e"}],
|
||||
name: t("TOGGLE_LOCK"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
@@ -427,7 +470,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
this.addCommand({
|
||||
id: "insert-link",
|
||||
hotkeys: [{modifiers:["Ctrl" || "Meta"], key:"k"}],
|
||||
hotkeys: [{modifiers:["Ctrl" || "Meta","Shift"], key:"k"}],
|
||||
name: t("INSERT_LINK"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
@@ -515,23 +558,37 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
/*1.2 migration command */
|
||||
this.addCommand({
|
||||
id: "migrate-to-1.2.x",
|
||||
name: t("MIGRATE_TO_2"),
|
||||
callback: async () => {
|
||||
const files = this.app.vault.getFiles().filter((f)=>f.extension=="excalidraw");
|
||||
for (const file of files) {
|
||||
const data = await this.app.vault.read(file);
|
||||
const fname = this.getNewUniqueFilepath(file.name+'.md',normalizePath(file.path.substr(0,file.path.lastIndexOf(file.name))));
|
||||
console.log(fname);
|
||||
await this.app.vault.create(fname,FRONTMATTER + exportSceneToMD(data));
|
||||
this.app.vault.delete(file);
|
||||
id: "convert-excalidraw",
|
||||
name: t("CONVERT_EXCALIDRAW"),
|
||||
checkCallback: (checking) => {
|
||||
if (checking) {
|
||||
const files = this.app.vault.getFiles().filter((f)=>f.extension=="excalidraw");
|
||||
return files.length>0;
|
||||
}
|
||||
this.convertExcalidrawToMD()
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async convertSingleExcalidrawToMD(file: TFile, replaceExtension:boolean = false, keepOriginal:boolean = false) {
|
||||
const data = await this.app.vault.read(file);
|
||||
const filename = file.name.substr(0,file.name.lastIndexOf(".excalidraw")) + (replaceExtension ? ".md" : ".excalidraw.md");
|
||||
const fname = this.getNewUniqueFilepath(filename,normalizePath(file.path.substr(0,file.path.lastIndexOf(file.name))));
|
||||
console.log(fname);
|
||||
await this.app.vault.create(fname,FRONTMATTER + exportSceneToMD(data));
|
||||
if (!keepOriginal) this.app.vault.delete(file);
|
||||
}
|
||||
|
||||
public async convertExcalidrawToMD(replaceExtension:boolean = false, keepOriginal:boolean = false) {
|
||||
const files = this.app.vault.getFiles().filter((f)=>f.extension=="excalidraw");
|
||||
for (const file of files) {
|
||||
this.convertSingleExcalidrawToMD(file,replaceExtension,keepOriginal);
|
||||
}
|
||||
new Notice("Converted " + files.length + " files.")
|
||||
}
|
||||
|
||||
private registerMonkeyPatches() {
|
||||
const self = this;
|
||||
|
||||
@@ -632,7 +689,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if(!(file instanceof TFile)) return;
|
||||
if (!self.isExcalidrawFile(file)) return;
|
||||
if (!self.settings.keepInSync) return;
|
||||
['.svg','.png'].forEach(async (ext:string)=>{
|
||||
['.svg','.png','.excalidraw'].forEach(async (ext:string)=>{
|
||||
const oldIMGpath = oldPath.substring(0,oldPath.lastIndexOf('.md')) + ext;
|
||||
const imgFile = self.app.vault.getAbstractFileByPath(normalizePath(oldIMGpath));
|
||||
if(imgFile && imgFile instanceof TFile) {
|
||||
@@ -641,25 +698,31 @@ 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);
|
||||
leaves.forEach((leaf:WorkspaceLeaf)=> {
|
||||
const excalidrawView = (leaf.view as ExcalidrawView);
|
||||
if(excalidrawView.file && excalidrawView.file.path == file.path) {
|
||||
excalidrawView.reload(true,file);
|
||||
if(excalidrawView.file
|
||||
&& (excalidrawView.file.path == file.path
|
||||
|| (file.extension=="excalidraw"
|
||||
&& file.path.substring(0,file.path.lastIndexOf('.excalidraw'))+'.md' == excalidrawView.file.path))) {
|
||||
excalidrawView.reload(true,excalidrawView.file);
|
||||
}
|
||||
});
|
||||
}
|
||||
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);
|
||||
@@ -671,7 +734,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
//delete PNG and SVG files as well
|
||||
if (self.settings.keepInSync) {
|
||||
['.svg','.png'].forEach(async (ext:string) => {
|
||||
['.svg','.png','.excalidraw'].forEach(async (ext:string) => {
|
||||
const imgPath = file.path.substring(0,file.path.lastIndexOf('.md')) + ext;
|
||||
const imgFile = self.app.vault.getAbstractFileByPath(normalizePath(imgPath));
|
||||
if(imgFile && imgFile instanceof TFile) {
|
||||
@@ -680,8 +743,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,35 +754,34 @@ 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);
|
||||
const activeExcalidrawView = self.activeExcalidrawView;
|
||||
const newActiveview:ExcalidrawView = (leaf.view instanceof ExcalidrawView) ? leaf.view as ExcalidrawView : null;
|
||||
if(activeExcalidrawView && activeExcalidrawView != newActiveview) {
|
||||
await activeExcalidrawView.save(false);
|
||||
if(activeExcalidrawView.file) {
|
||||
self.triggerEmbedUpdates(activeExcalidrawView.file.path);
|
||||
}
|
||||
}
|
||||
self.activeExcalidrawView = activeview;
|
||||
if(self.activeExcalidrawView) {
|
||||
self.lastActiveExcalidrawFilePath = self.activeExcalidrawView.file?.path;
|
||||
self.activeExcalidrawView = newActiveview;
|
||||
if(newActiveview) {
|
||||
self.lastActiveExcalidrawFilePath = newActiveview.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,12 +841,12 @@ 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';
|
||||
}
|
||||
|
||||
|
||||
private async getBlankDrawing():Promise<string> {
|
||||
const template = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.templateFilePath));
|
||||
const template = this.app.metadataCache.getFirstLinkpathDest(normalizePath(this.settings.templateFilePath),"");
|
||||
if(template && template instanceof TFile) {
|
||||
const data = await this.app.vault.read(template);
|
||||
if (data) return data;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
100
src/settings.ts
100
src/settings.ts
@@ -15,33 +15,41 @@ export interface ExcalidrawSettings {
|
||||
drawingFilenameDateTime: string,
|
||||
width: string,
|
||||
showLinkBrackets: boolean,
|
||||
linkIndicator: string,
|
||||
// validLinksOnly: boolean, //valid link as in [[valid Obsidian link]] - how to treat text elements in drawings
|
||||
linkPrefix: string,
|
||||
autosave: boolean;
|
||||
allowCtrlClick: boolean, //if disabled only the link button in the view header will open links
|
||||
exportWithTheme: boolean,
|
||||
exportWithBackground: boolean,
|
||||
keepInSync: boolean,
|
||||
autoexportSVG: boolean,
|
||||
autoexportPNG: boolean,
|
||||
keepInSync: boolean,
|
||||
autoexportExcalidraw: boolean,
|
||||
syncExcalidraw: boolean,
|
||||
library: string,
|
||||
loadCount: number, //version 1.2 migration counter
|
||||
drawingOpenCount: number,
|
||||
}
|
||||
|
||||
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,
|
||||
autosave: false,
|
||||
allowCtrlClick: true,
|
||||
exportWithTheme: true,
|
||||
exportWithBackground: true,
|
||||
keepInSync: false,
|
||||
autoexportSVG: false,
|
||||
autoexportPNG: false,
|
||||
keepInSync: false,
|
||||
autoexportExcalidraw: false,
|
||||
syncExcalidraw: false,
|
||||
library: `{"type":"excalidrawlib","version":1,"library":[]}`,
|
||||
loadCount: 0,
|
||||
drawingOpenCount: 0,
|
||||
}
|
||||
|
||||
export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
@@ -78,6 +86,28 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("AUTOSAVE_NAME"))
|
||||
.setDesc(t("AUTOSAVE_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.autosave)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.autosave = value;
|
||||
await this.plugin.saveSettings();
|
||||
const exs = this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for(const v of exs) {
|
||||
if(v.view instanceof ExcalidrawView) {
|
||||
if(v.view.autosaveTimer) {
|
||||
clearInterval(v.view.autosaveTimer)
|
||||
v.view.autosaveTimer = null;
|
||||
}
|
||||
if(value) {
|
||||
v.view.setupAutosaveTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("FILENAME_HEAD")});
|
||||
containerEl.createDiv('',(el) => {
|
||||
el.innerHTML = t("FILENAME_DESC");
|
||||
@@ -146,13 +176,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();
|
||||
}));
|
||||
@@ -202,7 +232,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.triggerEmbedUpdates();
|
||||
}));
|
||||
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("EXPORT_HEAD")});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("EXPORT_SYNC_NAME"))
|
||||
.setDesc(t("EXPORT_SYNC_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.keepInSync)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.keepInSync = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("EXPORT_SVG_NAME"))
|
||||
.setDesc(t("EXPORT_SVG_DESC"))
|
||||
@@ -213,26 +255,38 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("EXPORT_PNG_NAME"))
|
||||
.setDesc(t("EXPORT_PNG_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.autoexportPNG)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.autoexportPNG = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.autoexportPNG)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.autoexportPNG = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("COMPATIBILITY_HEAD")});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("EXPORT_SYNC_NAME"))
|
||||
.setDesc(t("EXPORT_SYNC_DESC"))
|
||||
.setName(t("EXPORT_EXCALIDRAW_NAME"))
|
||||
.setDesc(t("EXPORT_EXCALIDRAW_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.keepInSync)
|
||||
.setValue(this.plugin.settings.autoexportExcalidraw)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.keepInSync = value;
|
||||
this.plugin.settings.autoexportExcalidraw = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("SYNC_EXCALIDRAW_NAME"))
|
||||
.setDesc(t("SYNC_EXCALIDRAW_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.syncExcalidraw)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.syncExcalidraw = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
13
styles.css
13
styles.css
@@ -51,6 +51,7 @@ button.ToolIcon_type_button[title="Export"] {
|
||||
|
||||
.excalidraw-prompt-div {
|
||||
display: flex;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-form {
|
||||
@@ -60,4 +61,14 @@ button.ToolIcon_type_button[title="Export"] {
|
||||
|
||||
.excalidraw-prompt-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: "Virgil";
|
||||
src: url("https://excalidraw.com/Virgil.woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Cascadia";
|
||||
src: url("https://excalidraw.com/Cascadia.woff2");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user