mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
19 Commits
1.3.14
...
1.4.0.prer
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9251d4f1d | ||
|
|
9a57db43f2 | ||
|
|
fed106c811 | ||
|
|
7ebdec7713 | ||
|
|
1917dad8cd | ||
|
|
3bbff7f8d5 | ||
|
|
034927ada0 | ||
|
|
0cccdad13f | ||
|
|
fe7f3f58c5 | ||
|
|
23da271b73 | ||
|
|
59db43c3f0 | ||
|
|
597ee4f70e | ||
|
|
8222d8c146 | ||
|
|
f785d756be | ||
|
|
78fb37b173 | ||
|
|
a17638717f | ||
|
|
70de8ba2f8 | ||
|
|
e8a29a2715 | ||
|
|
7b1f13391c |
@@ -119,6 +119,19 @@ export interface ExcalidrawAutomate extends Window {
|
||||
}
|
||||
):boolean;
|
||||
addElementsToView (repositionToCursor:boolean, save:boolean):Promise<boolean>;
|
||||
onDropHook (data: {
|
||||
ea: ExcalidrawAutomate,
|
||||
event: React.DragEvent<HTMLDivElement>,
|
||||
draggable: any, //Obsidian draggable object
|
||||
type: "file"|"text"|"unknown",
|
||||
payload: {
|
||||
files: TFile[], //TFile[] array of dropped files
|
||||
text: string, //string
|
||||
},
|
||||
excalidrawFile: TFile, //the file receiving the drop event
|
||||
view: ExcalidrawView, //the excalidraw view receiving the drop
|
||||
pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop
|
||||
}):boolean;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -184,4 +184,38 @@ Adds elements created with ExcalidrawAutomate to the target ExcalidrawView.
|
||||
|
||||
`save` default is false
|
||||
- true: the drawing will be saved after the elements were added.
|
||||
- false: the drawing will be saved at the next autosave cycle. Use false when adding multiple elements one after the other. Else, best to use true, to minimize risk of data loss.
|
||||
- false: the drawing will be saved at the next autosave cycle. Use false when adding multiple elements one after the other. Else, best to use true, to minimize risk of data loss.
|
||||
|
||||
### onDropHook
|
||||
```typescript
|
||||
onDropHook (data: {
|
||||
ea: ExcalidrawAutomate,
|
||||
event: React.DragEvent<HTMLDivElement>,
|
||||
draggable: any, //Obsidian draggable object
|
||||
type: "file"|"text"|"unknown",
|
||||
payload: {
|
||||
files: TFile[], //TFile[] array of dropped files
|
||||
text: string, //string
|
||||
},
|
||||
excalidrawFile: TFile, //the file receiving the drop event
|
||||
view: ExcalidrawView, //the excalidraw view receiving the drop
|
||||
pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop
|
||||
}):boolean;
|
||||
```
|
||||
|
||||
Callback function triggered when an draggable item is dropped on Excalidraw.
|
||||
The function should return a boolean value. True if the drop was handled by the hook and futher native processing should be stopped, and false if Excalidraw should continue with the processing of the drop.
|
||||
type of drop can be one of:
|
||||
- "file" if a file from Obsidian file explorer is dropped onto Excalidraw. In this case payload.files will contain the list of files dropped.
|
||||
- "text" if a link (e.g. url, or wiki link) or other text is dropped. In this case payload.text will contain the received string
|
||||
- "unknown" if Excalidraw plugin does not recognize the type of dropped object. In this case you can use React.DragEvent to analysed the dropped object.
|
||||
|
||||
Use Templater startup templates or similar to set the Hook function.
|
||||
|
||||
```typescript
|
||||
ea = ExcalidrawAutomate;
|
||||
ea.onDropHook = (data) => {
|
||||
console.log(data);
|
||||
return false;
|
||||
}
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.3.13",
|
||||
"version": "1.3.20",
|
||||
"minAppVersion": "0.12.0",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
20
package.json
20
package.json
@@ -11,7 +11,7 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.9.0-obsidian-8",
|
||||
"@zsviczian/excalidraw": "0.10.0-obsidian-1",
|
||||
"monkey-around": "^2.2.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
@@ -19,22 +19,22 @@
|
||||
"roughjs": "4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^15.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-replace": "^2.4.2",
|
||||
"@rollup/plugin-typescript": "^8.2.1",
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/react-dom": "^17.0.8",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"cross-env": "^7.0.3",
|
||||
"nanoid": "^3.1.23",
|
||||
"obsidian": "^0.12.16",
|
||||
"rollup": "^2.52.3",
|
||||
"rollup-plugin-visualizer": "^5.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"typescript": "^4.3.4"
|
||||
"rollup-plugin-visualizer": "^5.5.2",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,19 @@ export interface ExcalidrawAutomate extends Window {
|
||||
}
|
||||
):boolean;
|
||||
addElementsToView (repositionToCursor:boolean, save:boolean):Promise<boolean>;
|
||||
onDropHook (data: {
|
||||
ea: ExcalidrawAutomate,
|
||||
event: React.DragEvent<HTMLDivElement>,
|
||||
draggable: any, //Obsidian draggable object
|
||||
type: "file"|"text"|"unknown",
|
||||
payload: {
|
||||
files: TFile[], //TFile[] array of dropped files
|
||||
text: string, //string
|
||||
},
|
||||
excalidrawFile: TFile, //the file receiving the drop event
|
||||
view: ExcalidrawView, //the excalidraw view receiving the drop
|
||||
pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop
|
||||
}):boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -313,7 +326,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
currentItemLinearStrokeSharpness: template? template.appState.currentItemLinearStrokeSharpness : this.style.strokeSharpness,
|
||||
gridSize: template ? template.appState.gridSize : this.canvas.gridSize
|
||||
}
|
||||
}))
|
||||
},null,"\t"))
|
||||
);
|
||||
},
|
||||
async createSVG(templatePath?:string):Promise<SVGSVGElement> {
|
||||
@@ -671,7 +684,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
|
||||
const elements = this.getElements();
|
||||
return await this.targetView.addElements(elements,repositionToCursor,save);
|
||||
},
|
||||
|
||||
onDropHook:null,
|
||||
};
|
||||
await initFonts();
|
||||
}
|
||||
|
||||
@@ -24,8 +24,11 @@ declare module "obsidian" {
|
||||
|
||||
export const REGEX_LINK = {
|
||||
//![[link|alias]] [alias](link){num}
|
||||
//12 3 4 5 6 7 8
|
||||
EXPR: /(!)?(\[\[([^|\]]+)\|?(.+)?]]|\[(.*)\]\((.*)\))(\{(\d+)\})?/g,
|
||||
// 1 2 3 4 5 6 7 8 9
|
||||
EXPR: /(!)?(\[\[([^|\]]+)\|?([^\]]+)?]]|\[([^\]]*)]\(([^)]*)\))(\{(\d+)\})?/g, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
|
||||
getRes: (text:string):IterableIterator<RegExpMatchArray> => {
|
||||
return text.matchAll(REGEX_LINK.EXPR);
|
||||
},
|
||||
isTransclusion: (parts: IteratorResult<RegExpMatchArray, any>):boolean => {
|
||||
return parts.value[1] ? true:false;
|
||||
},
|
||||
@@ -48,7 +51,7 @@ export const REGEX_LINK = {
|
||||
|
||||
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
|
||||
|
||||
const DRAWING_REG = /\n%%\n# Drawing\n(```json\n)(.*)\n```%%/gm;
|
||||
const DRAWING_REG = /\n%%\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
|
||||
const DRAWING_REG_FALLBACK = /\n# Drawing\n(```json\n)?(.*)(```)?(%%)?/gm;
|
||||
export function getJSON(data:string):[string,number] {
|
||||
let res = data.matchAll(DRAWING_REG);
|
||||
@@ -136,6 +139,9 @@ export class ExcalidrawData {
|
||||
}
|
||||
position += data.match(/((^%%\n)?# Text Elements\n)/m)[0].length
|
||||
|
||||
data = data.substring(position);
|
||||
position = 0;
|
||||
|
||||
//iterating through all the text elements in .md
|
||||
//Text elements always contain the raw value
|
||||
const BLOCKREF_LEN:number = " ^12345678\n\n".length;
|
||||
@@ -143,7 +149,12 @@ export class ExcalidrawData {
|
||||
let parts;
|
||||
while(!(parts = res.next()).done) {
|
||||
const text = data.substring(position,parts.value.index);
|
||||
this.textElements.set(parts.value[1],{raw: text, parsed: await this.parse(text)});
|
||||
const id:string = parts.value[1];
|
||||
this.textElements.set(id,{raw: text, parsed: await this.parse(text)});
|
||||
//this will set the rawText field of text elements imported from files before 1.3.14, and from other instances of Excalidraw
|
||||
const textEl = this.scene.elements.filter((el:any)=>el.id===id)[0];
|
||||
if(textEl && (!textEl.rawText || textEl.rawText === "")) textEl.rawText = text;
|
||||
|
||||
position = parts.value.index + BLOCKREF_LEN;
|
||||
}
|
||||
|
||||
@@ -241,8 +252,9 @@ export class ExcalidrawData {
|
||||
dirty = true;
|
||||
} else if(!this.textElements.has(id)) {
|
||||
dirty = true;
|
||||
this.textElements.set(id,{raw: te.text, parsed: null});
|
||||
this.parseasync(id,te.text);
|
||||
const raw = (te.rawText && te.rawText!==""?te.rawText:te.text); //this is for compatibility with drawings created before the rawText change on ExcalidrawTextElement
|
||||
this.textElements.set(id,{raw: raw, parsed: null});
|
||||
this.parseasync(id,raw);
|
||||
}
|
||||
}
|
||||
if(dirty) { //reload scene json in case it has changed
|
||||
@@ -263,13 +275,9 @@ export class ExcalidrawData {
|
||||
if(el.length==0) {
|
||||
this.textElements.delete(key); //if no longer in the scene, delete the text element
|
||||
} else {
|
||||
if(!this.textElements.has(key)) {
|
||||
const text = await this.getText(key);
|
||||
if(text != el[0].text) {
|
||||
this.textElements.set(key,{raw: el[0].text,parsed: await this.parse(el[0].text)});
|
||||
} else {
|
||||
const text = await this.getText(key);
|
||||
if(text != el[0].text) {
|
||||
this.textElements.set(key,{raw: el[0].text,parsed: await this.parse(el[0].text)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,14 +294,24 @@ export class ExcalidrawData {
|
||||
(this.showLinkBrackets ? "]]" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param text
|
||||
* @returns [string,number] - the transcluded text, and the line number for the location of the text
|
||||
*/
|
||||
public async getTransclusion (text:string):Promise<[string,number]> {
|
||||
//file-name#^blockref
|
||||
//1 2 3
|
||||
const REG_FILE_BLOCKREF = /(.*)#(\^)?(.*)/g;
|
||||
const parts=text.matchAll(REG_FILE_BLOCKREF).next();
|
||||
if(parts.done || !parts.value[1] || !parts.value[3]) return [text,0]; //filename and/or blockref not found
|
||||
const file = this.app.metadataCache.getFirstLinkpathDest(parts.value[1],this.file.path);
|
||||
if(!parts.done && !parts.value[1]) return [text,0]; //filename not found
|
||||
const filename = parts.done ? text : parts.value[1];
|
||||
const file = this.app.metadataCache.getFirstLinkpathDest(filename,this.file.path);
|
||||
if(!file || !(file instanceof TFile)) return [text,0];
|
||||
const contents = await this.app.vault.cachedRead(file);
|
||||
if(parts.done) { //no blockreference
|
||||
return([contents.substr(0,this.plugin.settings.pageTransclusionCharLimit),0]);
|
||||
}
|
||||
const isParagraphRef = parts.value[2] ? true : false; //does the reference contain a ^ character?
|
||||
const id = parts.value[3]; //the block ID or heading text
|
||||
const blocks = (await this.app.metadataCache.blockCache.getForFile({isCancelled: ()=>false},file)).blocks.filter((block:any)=>block.node.type!="comment");
|
||||
@@ -335,7 +353,7 @@ export class ExcalidrawData {
|
||||
private async parse(text:string):Promise<string>{
|
||||
let outString = "";
|
||||
let position = 0;
|
||||
const res = text.matchAll(REGEX_LINK.EXPR);
|
||||
const res = REGEX_LINK.getRes(text);
|
||||
let linkIcon = false;
|
||||
let urlIcon = false;
|
||||
let parts;
|
||||
@@ -375,7 +393,7 @@ export class ExcalidrawData {
|
||||
*/
|
||||
private quickParse(text:string):string {
|
||||
const hasTransclusion = (text:string):boolean => {
|
||||
const res = text.matchAll(REGEX_LINK.EXPR);
|
||||
const res = REGEX_LINK.getRes(text);
|
||||
let parts;
|
||||
while(!(parts=res.next()).done) {
|
||||
if (REGEX_LINK.isTransclusion(parts)) return true;
|
||||
@@ -386,7 +404,7 @@ export class ExcalidrawData {
|
||||
|
||||
let outString = "";
|
||||
let position = 0;
|
||||
const res = text.matchAll(REGEX_LINK.EXPR);
|
||||
const res = REGEX_LINK.getRes(text);
|
||||
let linkIcon = false;
|
||||
let urlIcon = false;
|
||||
let parts;
|
||||
@@ -420,23 +438,23 @@ export class ExcalidrawData {
|
||||
for(const key of this.textElements.keys()){
|
||||
outString += this.textElements.get(key).raw+' ^'+key+'\n\n';
|
||||
}
|
||||
return outString + this.plugin.getMarkdownDrawingSection(JSON.stringify(this.scene));
|
||||
return outString + this.plugin.getMarkdownDrawingSection(JSON.stringify(this.scene,null,"\t"));
|
||||
}
|
||||
|
||||
public async syncElements(newScene:any):Promise<boolean> {
|
||||
//console.log("Excalidraw.Data.syncElements()");
|
||||
this.scene = newScene;//JSON_parse(newScene);
|
||||
const result = this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
const result = this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets();
|
||||
await this.updateTextElementsFromScene();
|
||||
return result;
|
||||
return result || this.findNewTextElementsInScene();
|
||||
}
|
||||
|
||||
public async updateScene(newScene:any){
|
||||
//console.log("Excalidraw.Data.updateScene()");
|
||||
this.scene = JSON_parse(newScene);
|
||||
const result = this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets() || this.findNewTextElementsInScene();
|
||||
const result = this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets();
|
||||
await this.updateTextElementsFromScene();
|
||||
if(result) {
|
||||
if(result || this.findNewTextElementsInScene()) {
|
||||
await this.updateSceneTextElements();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -34,8 +34,9 @@ import ExcalidrawPlugin from './main';
|
||||
import {estimateBounds, ExcalidrawAutomate, repositionElementsToCursor} from './ExcalidrawAutomate';
|
||||
import { t } from "./lang/helpers";
|
||||
import { ExcalidrawData, REG_LINKINDEX_HYPERLINK, REGEX_LINK } from "./ExcalidrawData";
|
||||
import { checkAndCreateFolder, download, getNewUniqueFilepath, splitFolderAndFilename, viewportCoordsToSceneCoords } from "./Utils";
|
||||
import { checkAndCreateFolder, download, getNewOrAdjacentLeaf, getNewUniqueFilepath, rotatedDimensions, splitFolderAndFilename, viewportCoordsToSceneCoords } from "./Utils";
|
||||
import { Prompt } from "./Prompt";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
|
||||
declare let window: ExcalidrawAutomate;
|
||||
|
||||
@@ -78,7 +79,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
private ctrlKeyDown = false;
|
||||
private shiftKeyDown = false;
|
||||
private altKeyDown = false;
|
||||
private mouseEvent:any = null;
|
||||
|
||||
id: string = (this.leaf as any).id;
|
||||
|
||||
@@ -95,8 +95,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
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));
|
||||
else this.app.vault.create(filepath,JSON.stringify(scene));
|
||||
if(file && file instanceof TFile) this.app.vault.modify(file,JSON.stringify(scene,null,"\t"));
|
||||
else this.app.vault.create(filepath,JSON.stringify(scene,null,"\t"));
|
||||
}
|
||||
|
||||
public saveSVG(scene?: any) {
|
||||
@@ -196,7 +196,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
|
||||
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
|
||||
}
|
||||
return JSON.stringify(scene);
|
||||
return JSON.stringify(scene,null,"\t");
|
||||
}
|
||||
return this.data;
|
||||
}
|
||||
@@ -209,12 +209,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
new Notice(t("LINK_BUTTON_CLICK_NO_TEXT"),20000);
|
||||
return;
|
||||
}
|
||||
text = text.replaceAll("\n",""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
|
||||
if(text.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
window.open(text,"_blank");
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = text.matchAll(REGEX_LINK.EXPR).next();
|
||||
const parts = REGEX_LINK.getRes(text).next();
|
||||
if(!parts.value) {
|
||||
const tags = text.matchAll(/#([\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/ug).next();
|
||||
if(!tags.value || tags.value.length<2) {
|
||||
@@ -226,7 +227,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//@ts-ignore
|
||||
search[0].view.setQuery("tag:"+tags.value[1]);
|
||||
this.app.workspace.revealLeaf(search[0]);
|
||||
//if(this.gotoFullscreen.style.display=="none") this.toggleFullscreen();
|
||||
if(document.fullscreenElement === this.contentEl) {
|
||||
document.exitFullscreen();
|
||||
this.zoomToFit();
|
||||
@@ -234,7 +234,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return;
|
||||
}
|
||||
|
||||
text = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6];
|
||||
text = REGEX_LINK.getLink(parts);
|
||||
|
||||
if(text.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
window.open(text,"_blank");
|
||||
@@ -263,17 +263,20 @@ export default class ExcalidrawView extends TextFileView {
|
||||
document.exitFullscreen();
|
||||
this.zoomToFit();
|
||||
}
|
||||
if(lineNum) {
|
||||
const leaf = ev.shiftKey ? view.app.workspace.createLeafBySplit(view.leaf) : view.leaf;
|
||||
leaf.openFile(file,{eState: {line: lineNum-1}});
|
||||
return;
|
||||
}
|
||||
view.app.workspace.openLinkText(text,view.file.path,ev.shiftKey);
|
||||
const leaf = ev.shiftKey ? getNewOrAdjacentLeaf(this.plugin,view.leaf) : view.leaf;
|
||||
view.app.workspace.setActiveLeaf(leaf);
|
||||
leaf.view.app.workspace.openLinkText(text,view.file.path);
|
||||
} catch (e) {
|
||||
new Notice(e,4000);
|
||||
}
|
||||
}
|
||||
|
||||
onResize() {
|
||||
if(!this.plugin.settings.zoomToFitOnResize) return;
|
||||
if(!this.excalidrawRef) return;
|
||||
this.zoomToFit(false);
|
||||
}
|
||||
|
||||
onload() {
|
||||
//console.log("ExcalidrawView.onload()");
|
||||
this.addAction(DISK_ICON_NAME,t("FORCE_SAVE"),async (ev)=> {
|
||||
@@ -483,12 +486,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const folderpath = splitFolderAndFilename(this.file.path).folderpath;
|
||||
await checkAndCreateFolder(this.app.vault,folderpath); //create folder if it does not exist
|
||||
const fname = getNewUniqueFilepath(this.app.vault,filename,folderpath);
|
||||
this.app.vault.create(fname,JSON.stringify(this.getScene()));
|
||||
this.app.vault.create(fname,JSON.stringify(this.getScene(),null,"\t"));
|
||||
new Notice("Exported to " + fname,6000);
|
||||
});
|
||||
return;
|
||||
}
|
||||
download('data:text/plain;charset=utf-8',encodeURIComponent(JSON.stringify(this.getScene())), this.file.basename+'.excalidraw');
|
||||
download('data:text/plain;charset=utf-8',encodeURIComponent(JSON.stringify(this.getScene(),null,"\t")), this.file.basename+'.excalidraw');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@@ -703,12 +706,28 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const getTextElementAtPointer = (pointer:any) => {
|
||||
const elements = this.excalidrawRef.current.getSceneElements()
|
||||
.filter((e:ExcalidrawElement)=>{
|
||||
return e.type == "text"
|
||||
&& e.x<=pointer.x && (e.x+e.width)>=pointer.x
|
||||
&& e.y<=pointer.y && (e.y+e.height)>=pointer.y;
|
||||
if (e.type !== "text") return false;
|
||||
const [x,y,w,h] = rotatedDimensions(e);
|
||||
return x<=pointer.x && x+w>=pointer.x
|
||||
&& y<=pointer.y && y+h>=pointer.y;
|
||||
});
|
||||
if(elements.length==0) return null;
|
||||
return {id:elements[0].id,text:elements[0].text};
|
||||
if(elements.length===1) return {id:elements[0].id,text:elements[0].text};
|
||||
//if more than 1 text elements are at the location, look for one that has a link
|
||||
const elementsWithLinks = elements.filter((e:ExcalidrawTextElement)=> {
|
||||
const text:string = (this.textMode == TextMode.parsed)
|
||||
? this.excalidrawData.getRawText(e.id)
|
||||
: e.text;
|
||||
if(!text) return false;
|
||||
if(text.match(REG_LINKINDEX_HYPERLINK)) return true;
|
||||
const parts = REGEX_LINK.getRes(text).next();
|
||||
if(!parts.value) return false;
|
||||
return true;
|
||||
});
|
||||
//if there are no text elements with links, return the first element without a link
|
||||
if(elementsWithLinks.length==0) return {id:elements[0].id,text:elements[0].text};
|
||||
//if there are still multiple text elements with links on top of each other, return the first
|
||||
return {id:elementsWithLinks[0].id,text:elementsWithLinks[0].text};
|
||||
}
|
||||
|
||||
let hoverPoint = {x:0,y:0};
|
||||
@@ -740,6 +759,18 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (transfer.types?.includes('text/html') || transfer.types?.includes('text/plain')) return 'copy';
|
||||
}
|
||||
|
||||
let viewModeEnabled = false;
|
||||
const handleLinkClick = () => {
|
||||
selectedTextElement = getTextElementAtPointer(currentPosition);
|
||||
if(selectedTextElement) {
|
||||
const event = new MouseEvent("click", {ctrlKey: true, shiftKey: this.shiftKeyDown, altKey:this.altKeyDown});
|
||||
this.handleLinkClick(this,event);
|
||||
selectedTextElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
let mouseEvent:any = null;
|
||||
|
||||
const excalidrawDiv = React.createElement(
|
||||
"div",
|
||||
{
|
||||
@@ -748,6 +779,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
key: "abc",
|
||||
tabIndex: 0,
|
||||
onKeyDown: (e:any) => {
|
||||
//@ts-ignore
|
||||
if(e.target === excalidrawDiv.ref.current) return; //event should originate from the canvas
|
||||
if(document.fullscreenEnabled && document.fullscreenElement == this.contentEl && e.keyCode==27) {
|
||||
document.exitFullscreen();
|
||||
this.zoomToFit();
|
||||
@@ -767,7 +800,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(!text) return;
|
||||
if(text.match(REG_LINKINDEX_HYPERLINK)) return;
|
||||
|
||||
const parts = text.matchAll(REGEX_LINK.EXPR).next();
|
||||
const parts = REGEX_LINK.getRes(text).next();
|
||||
if(!parts.value) return;
|
||||
let linktext = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6];
|
||||
|
||||
@@ -777,7 +810,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.plugin.hover.sourcePath = this.file.path;
|
||||
hoverPreviewTarget = this.contentEl; //e.target;
|
||||
this.app.workspace.trigger('hover-link', {
|
||||
event: this.mouseEvent,
|
||||
event: mouseEvent,
|
||||
source: VIEW_TYPE_EXCALIDRAW,
|
||||
hoverParent: hoverPreviewTarget,
|
||||
targetEl: hoverPreviewTarget,
|
||||
@@ -808,7 +841,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
},
|
||||
onMouseMove: (e:MouseEvent) => {
|
||||
//@ts-ignore
|
||||
this.mouseEvent = e.nativeEvent;
|
||||
mouseEvent = e.nativeEvent;
|
||||
},
|
||||
onMouseOver: (e:MouseEvent) => {
|
||||
clearHoverPreview();
|
||||
@@ -842,17 +875,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
onPointerUpdate: (p:any) => {
|
||||
currentPosition = p.pointer;
|
||||
if(hoverPreviewTarget && (Math.abs(hoverPoint.x-p.pointer.x)>50 || Math.abs(hoverPoint.y-p.pointer.y)>50)) clearHoverPreview();
|
||||
if(!this.excalidrawRef.current.getAppState().viewModeEnabled) return;
|
||||
const handleLinkClick = () => {
|
||||
selectedTextElement = getTextElementAtPointer(p.pointer);
|
||||
if(selectedTextElement) {
|
||||
const event = new MouseEvent("click", {ctrlKey: true, shiftKey: this.shiftKeyDown, altKey:this.altKeyDown});
|
||||
this.handleLinkClick(this,event);
|
||||
selectedTextElement = null;
|
||||
}
|
||||
}
|
||||
if(!viewModeEnabled) return;
|
||||
|
||||
const buttonDown = !blockOnMouseButtonDown && p.button=="down";
|
||||
const buttonDown = !blockOnMouseButtonDown && p.button === "down";
|
||||
if(buttonDown) {
|
||||
blockOnMouseButtonDown = true;
|
||||
|
||||
@@ -870,11 +895,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
timestamp = now;
|
||||
return;
|
||||
}
|
||||
if (p.button=="up") {
|
||||
if (p.button === "up") {
|
||||
blockOnMouseButtonDown=false;
|
||||
}
|
||||
},
|
||||
onChange: (et:ExcalidrawElement[],st:AppState) => {
|
||||
viewModeEnabled = st.viewModeEnabled;
|
||||
if(this.justLoaded) {
|
||||
this.justLoaded = false;
|
||||
this.zoomToFit(false);
|
||||
@@ -897,32 +923,70 @@ export default class ExcalidrawView extends TextFileView {
|
||||
await this.plugin.saveSettings();
|
||||
})();
|
||||
},
|
||||
/*onPaste: (data: ClipboardData, event: ClipboardEvent | null) => {
|
||||
console.log(data,event);
|
||||
return false;
|
||||
},*/
|
||||
onPaste: (data: ClipboardData, event: ClipboardEvent | null) => {
|
||||
if(data.elements) {
|
||||
const self = this;
|
||||
setTimeout(()=>self.save(false),300);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onDrop: (event: React.DragEvent<HTMLDivElement>):boolean => {
|
||||
const st: AppState = excalidrawRef.current.getAppState();
|
||||
currentPosition = viewportCoordsToSceneCoords({ clientX: event.clientX, clientY: event.clientY },st);
|
||||
|
||||
const draggable = (this.app as any).dragManager.draggable;
|
||||
const onDropHook = (type:"file"|"text"|"unknown", files:TFile[], text:string):boolean => {
|
||||
if (window.ExcalidrawAutomate.onDropHook) {
|
||||
try {
|
||||
return window.ExcalidrawAutomate.onDropHook({
|
||||
//@ts-ignore
|
||||
ea: window.ExcalidrawAutomate, //the Excalidraw Automate object
|
||||
event: event, //React.DragEvent<HTMLDivElement>
|
||||
draggable: draggable, //Obsidian draggable object
|
||||
type: type, //"file"|"text"
|
||||
payload: {
|
||||
files: files, //TFile[] array of dropped files
|
||||
text: text, //string
|
||||
},
|
||||
excalidrawFile: this.file, //the file receiving the drop event
|
||||
view: this, //the excalidraw view receiving the drop
|
||||
pointerPosition: currentPosition //the pointer position on canvas at the time of drop
|
||||
});
|
||||
} catch (e) {
|
||||
new Notice("on drop hook error. See console log for details");
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch(draggable?.type) {
|
||||
case "file":
|
||||
this.addText(`[[${this.app.metadataCache.fileToLinktext(draggable.file,this.file.path,true)}]]`);
|
||||
if (!onDropHook("file",[draggable.file],null)) {
|
||||
this.addText(`[[${this.app.metadataCache.fileToLinktext(draggable.file,this.file.path,true)}]]`);
|
||||
}
|
||||
return false;
|
||||
case "files":
|
||||
for(const f of draggable.files) {
|
||||
this.addText(`[[${this.app.metadataCache.fileToLinktext(f,this.file.path,true)}]]`);
|
||||
currentPosition.y+=st.currentItemFontSize*2;
|
||||
if (!onDropHook("file",draggable.files,null)) {
|
||||
for(const f of draggable.files) {
|
||||
this.addText(`[[${this.app.metadataCache.fileToLinktext(f,this.file.path,true)}]]`);
|
||||
currentPosition.y+=st.currentItemFontSize*2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event.dataTransfer.types.includes("text/plain")) {
|
||||
const text:string = event.dataTransfer.getData("text");
|
||||
if(!text) return true;
|
||||
this.addText(text.replace(/(!\[\[.*#[^\]]*\]\])/g,"$1{40}"));
|
||||
if (!onDropHook("text",null,text)) {
|
||||
this.addText(text.replace(/(!\[\[.*#[^\]]*\]\])/g,"$1{40}"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if(onDropHook("unknown",null,null)) return false;
|
||||
return true;
|
||||
},
|
||||
onBeforeTextEdit: (textElement: ExcalidrawTextElement) => {
|
||||
@@ -930,8 +994,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
clearInterval(this.autosaveTimer);
|
||||
this.autosaveTimer = null;
|
||||
}
|
||||
if(this.textMode==TextMode.parsed) return this.excalidrawData.getRawText(textElement.id);
|
||||
return null;
|
||||
//if(this.textMode==TextMode.parsed) {
|
||||
const raw = this.excalidrawData.getRawText(textElement.id);
|
||||
if(!raw) return textElement.rawText;
|
||||
return raw;
|
||||
/*}
|
||||
return null;*/
|
||||
},
|
||||
onBeforeTextSubmit: (textElement: ExcalidrawTextElement, text:string, isDeleted:boolean) => {
|
||||
if(isDeleted) {
|
||||
@@ -942,7 +1010,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
//If the parsed text is different than the raw text, and if View is in TextMode.parsed
|
||||
//Then I need to clear the undo history to avoid overwriting raw text with parsed text and losing links
|
||||
if(text!=textElement.text) { //the user made changes to the text
|
||||
if(text!=textElement.text || !this.excalidrawData.getRawText(textElement.id)) { //the user made changes to the text or the text is missing from Excalidraw Data (recently copy/pasted)
|
||||
//setTextElement will attempt a quick parse (without processing transclusions)
|
||||
const parseResult = this.excalidrawData.setTextElement(textElement.id, text,async ()=>{
|
||||
await this.save(false);
|
||||
@@ -982,9 +1050,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const fullscreen = (document.fullscreenElement==this.contentEl);
|
||||
const elements = current.getSceneElements();
|
||||
if(delay) { //time for the DOM to render, I am sure there is a more elegant solution
|
||||
setTimeout(() => current.zoomToFit(elements,10,fullscreen?0:0.05),100);
|
||||
setTimeout(() => current.zoomToFit(elements,2,fullscreen?0:0.05),100);
|
||||
} else {
|
||||
current.zoomToFit(elements,10,fullscreen?0:0.05);
|
||||
current.zoomToFit(elements,2,fullscreen?0:0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export class MigrationPrompt extends Modal {
|
||||
|
||||
createForm(): void {
|
||||
const div = this.contentEl.createDiv();
|
||||
div.addClass("excalidarw-prompt-div");
|
||||
div.addClass("excalidraw-prompt-div");
|
||||
div.style.maxWidth = "600px";
|
||||
div.createEl('p',{text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more."});
|
||||
div.createEl('p',{text: ""} , (el) => {
|
||||
|
||||
56
src/Utils.ts
56
src/Utils.ts
@@ -1,6 +1,14 @@
|
||||
import { normalizePath, TAbstractFile, TFolder, Vault } from "obsidian";
|
||||
import { normalizePath, TAbstractFile, TFolder, Vault, WorkspaceLeaf } from "obsidian";
|
||||
import { Random } from "roughjs/bin/math";
|
||||
import { Zoom } from "@zsviczian/excalidraw/types/types";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
|
||||
declare module "obsidian" {
|
||||
interface Workspace {
|
||||
getAdjacentLeafInDirection(leaf: WorkspaceLeaf, direction: string): WorkspaceLeaf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
|
||||
@@ -102,6 +110,38 @@ export function wrapText(text:string, lineLen:number, forceWrap:boolean=false):s
|
||||
return outstring.replace(/\n$/, '');
|
||||
}
|
||||
|
||||
const rotate = (
|
||||
pointX: number,
|
||||
pointY: number,
|
||||
centerX: number,
|
||||
centerY: number,
|
||||
angle: number,
|
||||
): [number, number] =>
|
||||
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
||||
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
||||
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
||||
[
|
||||
(pointX - centerX) * Math.cos(angle) - (pointY - centerY) * Math.sin(angle) + centerX,
|
||||
(pointX - centerX) * Math.sin(angle) + (pointY - centerY) * Math.cos(angle) + centerY,
|
||||
];
|
||||
|
||||
export const rotatedDimensions = (
|
||||
element: ExcalidrawElement
|
||||
): [number, number, number, number] => {
|
||||
if(element.angle===0) [element.x,element.y,element.width,element.height];
|
||||
const centerX = element.x+element.width/2;
|
||||
const centerY = element.y+element.height/2;
|
||||
const [left,top] = rotate(element.x,element.y,centerX,centerY,element.angle);
|
||||
const [right,bottom] = rotate(element.x+element.width,element.y+element.height,centerX,centerY,element.angle);
|
||||
return [
|
||||
left<right ? left : right,
|
||||
top<bottom ? top : bottom,
|
||||
Math.abs(left-right),
|
||||
Math.abs(top-bottom)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
export const viewportCoordsToSceneCoords = (
|
||||
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||
{
|
||||
@@ -122,4 +162,16 @@ export const viewportCoordsToSceneCoords = (
|
||||
const x = (clientX - zoom.translation.x - offsetLeft) * invScale - scrollX;
|
||||
const y = (clientY - zoom.translation.y - offsetTop) * invScale - scrollY;
|
||||
return { x, y };
|
||||
};
|
||||
};
|
||||
|
||||
export const getNewOrAdjacentLeaf = (plugin: ExcalidrawPlugin, leaf: WorkspaceLeaf):WorkspaceLeaf => {
|
||||
if(plugin.settings.openInAdjacentPane) {
|
||||
let leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "right");
|
||||
if(!leafToUse){leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "left");}
|
||||
if(!leafToUse){leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "bottom");}
|
||||
if(!leafToUse){leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "top");}
|
||||
if(!leafToUse){leafToUse = plugin.app.workspace.createLeafBySplit(leaf);}
|
||||
return leafToUse;
|
||||
}
|
||||
return plugin.app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
@@ -13,7 +13,8 @@ export const MAX_COLORS = 5;
|
||||
export const COLOR_FREQ = 6;
|
||||
export const RERENDER_EVENT = "excalidraw-embed-rerender";
|
||||
export const BLANK_DRAWING = '{"type":"excalidraw","version":2,"source":"https://excalidraw.com","elements":[],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff"}}';
|
||||
export const FRONTMATTER = ["---","",`${FRONTMATTER_KEY}: unlocked`,"","---", "==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==", "",""].join("\n");
|
||||
export const DARK_BLANK_DRAWING = '{"type":"excalidraw","version":2,"source":"https://excalidraw.com","elements":[],"appState":{"theme":"dark","gridSize":null,"viewBackgroundColor":"#ffffff"}}';
|
||||
export const FRONTMATTER = ["---","",`${FRONTMATTER_KEY}: parsed`,"","---", "==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==", "",""].join("\n");
|
||||
export const EMPTY_MESSAGE = "Hit enter to create a new drawing";
|
||||
export const TEXT_DISPLAY_PARSED_ICON_NAME = "quote-glyph";
|
||||
export const TEXT_DISPLAY_RAW_ICON_NAME = "presentation";
|
||||
|
||||
@@ -1,154 +1,168 @@
|
||||
import { FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, FRONTMATTER_KEY_CUSTOM_PREFIX, FRONTMATTER_KEY_CUSTOM_URL_PREFIX } from "src/constants";
|
||||
|
||||
// English
|
||||
export default {
|
||||
// main.ts
|
||||
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
|
||||
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
|
||||
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)",
|
||||
DOWNLOAD_LIBRARY: "Export stencil library as an *.excalidrawlib file",
|
||||
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) 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 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 RAW/PREVIEW",
|
||||
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 an .Excalidraw file",
|
||||
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)",
|
||||
RAW: "Change to PREVIEW mode (only effects text-elements with links or transclusions)",
|
||||
PARSED: "Change to RAW mode (only effects text-elements with links or transclusions)",
|
||||
NOFILE: "Excalidraw (no file)",
|
||||
COMPATIBILITY_MODE: "*.excalidraw file opened in compatibility mode. Convert to new format for full plugin functionality.",
|
||||
CONVERT_FILE: "Convert to new format",
|
||||
|
||||
//settings.ts
|
||||
FOLDER_NAME: "Excalidraw folder",
|
||||
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 and it's name is " +
|
||||
"Template.md, the setting would be: Excalidraw/Template.md (or just Excalidraw/Template - you may ommit the .md file extension" +
|
||||
"If you are using Excalidraw in compatibility mode, then your template must be a legacy excalidraw file as well " +
|
||||
"such as 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 and transclusion",
|
||||
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 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 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.',
|
||||
URL_PREFIX_NAME:"URL prefix",
|
||||
URL_PREFIX_DESC:"In PREVIEW mode, if the Text Element contains a URL link, precede the text with these characters. " +
|
||||
"You can override this setting for a specific drawing by adding \'" + FRONTMATTER_KEY_CUSTOM_URL_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.",
|
||||
TRANSCLUSION_WRAP_NAME: "Overflow wrap behavior of transcluded text",
|
||||
TRANSCLUSION_WRAP_DESC: "Number specifies the character count where the text should be wrapped. " +
|
||||
"Set the text wrapping behavior of transcluded text. Turn this ON to force-wrap " +
|
||||
"text (i.e. no overflow), or OFF to soft-warp text (at the nearest whitespace).",
|
||||
EMBED_HEAD: "Embed & Export",
|
||||
EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview",
|
||||
EMBED_PREVIEW_SVG_DESC: "The default is to display drawings as SVG images in the markdown preview. Turning this feature off, the markdown preview will display the drawing as an embedded PNG image.",
|
||||
EMBED_WIDTH_NAME: "Default width of embedded (transcluded) image",
|
||||
EMBED_WIDTH_DESC: "Only relevant if embed type is excalidraw. Has no effect on PNG and SVG embeds. The default width of an embedded drawing. You can specify a custom " +
|
||||
"width when embedding an image using the ![[drawing.excalidraw|100]] or " +
|
||||
"[[drawing.excalidraw|100x100]] format.",
|
||||
EMBED_TYPE_NAME: "Type of file to insert into the document",
|
||||
EMBED_TYPE_DESC: "When you embed an image into a document using the command palette this setting will specify if Excalidraw should embed the original excalidraw file "+
|
||||
"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 correspondign 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.",
|
||||
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",
|
||||
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_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_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",
|
||||
COMPATIBILITY_MODE_NAME: "New drawings as legacy files",
|
||||
COMPATIBILITY_MODE_DESC: "By enabling this feature drawings you create with the ribbon icon, the command palette actions, "+
|
||||
"and the file explorer are going to be all legacy *.excalidraw files. This setting will also turn off the reminder message " +
|
||||
"when you open a legacy file for editing.",
|
||||
EXPERIMENTAL_HEAD: "Experimental features",
|
||||
EXPERIMENTAL_DESC: "These setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
|
||||
FILETYPE_NAME: "Display type (✏️) for excalidraw.md files in File Explorer",
|
||||
FILETYPE_DESC: "Excalidraw files will receive an indicator using the emojii or text defined in the next setting.",
|
||||
FILETAG_NAME: "Set the type indicator for excalidraw.md files",
|
||||
FILETAG_DESC: "The text or emojii to display as type indicator.",
|
||||
INSERT_EMOJI: "Insert an emoji",
|
||||
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "Select a file then press enter.",
|
||||
NO_MATCH: "No file matches your query.",
|
||||
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 drawing then press Enter.",
|
||||
SELECT_TO_EMBED: "Select the drawing to insert into active document.",
|
||||
};
|
||||
import { FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, FRONTMATTER_KEY_CUSTOM_PREFIX, FRONTMATTER_KEY_CUSTOM_URL_PREFIX } from "src/constants";
|
||||
|
||||
// English
|
||||
export default {
|
||||
// main.ts
|
||||
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
|
||||
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
|
||||
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)",
|
||||
DOWNLOAD_LIBRARY: "Export stencil library as an *.excalidrawlib file",
|
||||
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) 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 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 RAW/PREVIEW",
|
||||
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 an .Excalidraw file",
|
||||
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)",
|
||||
RAW: "Change to PREVIEW mode (only effects text-elements with links or transclusions)",
|
||||
PARSED: "Change to RAW mode (only effects text-elements with links or transclusions)",
|
||||
NOFILE: "Excalidraw (no file)",
|
||||
COMPATIBILITY_MODE: "*.excalidraw file opened in compatibility mode. Convert to new format for full plugin functionality.",
|
||||
CONVERT_FILE: "Convert to new format",
|
||||
|
||||
//settings.ts
|
||||
FOLDER_NAME: "Excalidraw folder",
|
||||
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 and it's name is " +
|
||||
"Template.md, the setting would be: Excalidraw/Template.md (or just Excalidraw/Template - you may ommit the .md file extension" +
|
||||
"If you are using Excalidraw in compatibility mode, then your template must be a legacy excalidraw file as well " +
|
||||
"such as 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",
|
||||
DISPLAY_HEAD: "Display",
|
||||
MATCH_THEME_NAME: "New drawing to match Obsidian theme",
|
||||
MATCH_THEME_DESC: "If theme is dark, new drawing will be created in dark mode. This does not apply when you use a template for new drawings. " +
|
||||
"Also this will not effect when you open an existing drawing. Those will follow the theme of the template/drawing respectively.",
|
||||
ZOOM_TO_FIT_NAME: "Zoom to fit on view resize",
|
||||
ZOOM_TO_FIT_DESC: "Zoom to fit drawing when the pane is resized",
|
||||
LINKS_HEAD: "Links and transclusion",
|
||||
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]].",
|
||||
ADJACENT_PANE_NAME: "Open in adjacent pane",
|
||||
ADJACENT_PANE_DESC: "When CTRL+SHIFT clicking a link in Excalidraw by default the plugin will open the link in a new pane. " +
|
||||
"Turning this setting on, Excalidraw will first look for an existing adjacent pane, and try to open the link there. " +
|
||||
"Excalidraw will first look too the right, then to the left, then down, then up. If no pane is found, Excalidraw will open " +
|
||||
"a new pane.",
|
||||
LINK_BRACKETS_NAME: "Show [[brackets]] around links",
|
||||
LINK_BRACKETS_DESC: "In PREVIEW 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 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.',
|
||||
URL_PREFIX_NAME:"URL prefix",
|
||||
URL_PREFIX_DESC:"In PREVIEW mode, if the Text Element contains a URL link, precede the text with these characters. " +
|
||||
"You can override this setting for a specific drawing by adding \'" + FRONTMATTER_KEY_CUSTOM_URL_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.",
|
||||
TRANSCLUSION_WRAP_NAME: "Overflow wrap behavior of transcluded text",
|
||||
TRANSCLUSION_WRAP_DESC: "Number specifies the character count where the text should be wrapped. " +
|
||||
"Set the text wrapping behavior of transcluded text. Turn this ON to force-wrap " +
|
||||
"text (i.e. no overflow), or OFF to soft-warp text (at the nearest whitespace).",
|
||||
PAGE_TRANSCLUSION_CHARCOUNT_NAME: "Page transclusion max char count",
|
||||
PAGE_TRANSCLUSION_CHARCOUNT_DESC: "The maximum number of characters to display from the page when transcluding an entire page with the "+
|
||||
"![[markdown page]] format.",
|
||||
EMBED_HEAD: "Embed & Export",
|
||||
EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview",
|
||||
EMBED_PREVIEW_SVG_DESC: "The default is to display drawings as SVG images in the markdown preview. Turning this feature off, the markdown preview will display the drawing as an embedded PNG image.",
|
||||
EMBED_WIDTH_NAME: "Default width of embedded (transcluded) image",
|
||||
EMBED_WIDTH_DESC: "Only relevant if embed type is excalidraw. Has no effect on PNG and SVG embeds. The default width of an embedded drawing. You can specify a custom " +
|
||||
"width when embedding an image using the ![[drawing.excalidraw|100]] or " +
|
||||
"[[drawing.excalidraw|100x100]] format.",
|
||||
EMBED_TYPE_NAME: "Type of file to insert into the document",
|
||||
EMBED_TYPE_DESC: "When you embed an image into a document using the command palette this setting will specify if Excalidraw should embed the original excalidraw file "+
|
||||
"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 correspondign 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.",
|
||||
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",
|
||||
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_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_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",
|
||||
COMPATIBILITY_MODE_NAME: "New drawings as legacy files",
|
||||
COMPATIBILITY_MODE_DESC: "By enabling this feature drawings you create with the ribbon icon, the command palette actions, "+
|
||||
"and the file explorer are going to be all legacy *.excalidraw files. This setting will also turn off the reminder message " +
|
||||
"when you open a legacy file for editing.",
|
||||
EXPERIMENTAL_HEAD: "Experimental features",
|
||||
EXPERIMENTAL_DESC: "These setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
|
||||
FILETYPE_NAME: "Display type (✏️) for excalidraw.md files in File Explorer",
|
||||
FILETYPE_DESC: "Excalidraw files will receive an indicator using the emojii or text defined in the next setting.",
|
||||
FILETAG_NAME: "Set the type indicator for excalidraw.md files",
|
||||
FILETAG_DESC: "The text or emojii to display as type indicator.",
|
||||
INSERT_EMOJI: "Insert an emoji",
|
||||
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "Select a file then press enter.",
|
||||
NO_MATCH: "No file matches your query.",
|
||||
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 drawing then press Enter.",
|
||||
SELECT_TO_EMBED: "Select the drawing to insert into active document.",
|
||||
};
|
||||
13
src/main.ts
13
src/main.ts
@@ -31,7 +31,8 @@ import {
|
||||
FRONTMATTER_KEY,
|
||||
FRONTMATTER,
|
||||
JSON_parse,
|
||||
nanoid
|
||||
nanoid,
|
||||
DARK_BLANK_DRAWING
|
||||
} from "./constants";
|
||||
import ExcalidrawView, {ExportSettings, TextMode} from "./ExcalidrawView";
|
||||
import {getJSON} from "./ExcalidrawData";
|
||||
@@ -112,6 +113,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//inspiration taken from kanban:
|
||||
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
|
||||
this.registerMonkeyPatches();
|
||||
new Notice("Excalidraw was updated. Files opened with this version will not open with the older version. Please update plugin on all your devices.\n\nI will remove this message with next update.",8000);
|
||||
if(this.settings.loadCount<1) this.migrationNotice();
|
||||
const electron:string = process.versions.electron;
|
||||
if(electron.startsWith("8.")) {
|
||||
@@ -1101,16 +1103,17 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
if (this.settings.compatibilityMode) {
|
||||
return BLANK_DRAWING;
|
||||
return this.settings.matchTheme && document.body.classList.contains("theme-dark") ? DARK_BLANK_DRAWING : BLANK_DRAWING;
|
||||
}
|
||||
return FRONTMATTER + '\n' + this.getMarkdownDrawingSection(BLANK_DRAWING);
|
||||
const blank = this.settings.matchTheme && document.body.classList.contains("theme-dark") ? DARK_BLANK_DRAWING : BLANK_DRAWING;
|
||||
return FRONTMATTER + '\n' + this.getMarkdownDrawingSection(blank);
|
||||
}
|
||||
|
||||
public getMarkdownDrawingSection(jsonString: string) {
|
||||
return '%%\n# Drawing\n'
|
||||
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96)+'json\n'
|
||||
+ jsonString + '\n'
|
||||
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96) + '%%';
|
||||
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96) + '\n%%';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1135,7 +1138,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
outString += te.text+' ^'+id+'\n\n';
|
||||
}
|
||||
return outString + this.getMarkdownDrawingSection(data);
|
||||
return outString + this.getMarkdownDrawingSection(JSON.stringify(JSON_parse(data),null,"\t"));
|
||||
}
|
||||
|
||||
public async createDrawing(filename: string, onNewPane: boolean, foldername?: string, initData?:string):Promise<string> {
|
||||
|
||||
@@ -17,11 +17,15 @@ export interface ExcalidrawSettings {
|
||||
drawingFilenameDateTime: string,
|
||||
displaySVGInPreview: boolean,
|
||||
width: string,
|
||||
matchTheme: boolean,
|
||||
zoomToFitOnResize: boolean,
|
||||
openInAdjacentPane: boolean,
|
||||
showLinkBrackets: boolean,
|
||||
linkPrefix: string,
|
||||
urlPrefix: string,
|
||||
allowCtrlClick: boolean, //if disabled only the link button in the view header will open links
|
||||
forceWrap: boolean,
|
||||
pageTransclusionCharLimit: number,
|
||||
pngExportScale: number,
|
||||
exportWithTheme: boolean,
|
||||
exportWithBackground: boolean,
|
||||
@@ -47,11 +51,15 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
drawingFilenameDateTime: 'YYYY-MM-DD HH.mm.ss',
|
||||
displaySVGInPreview: true,
|
||||
width: '400',
|
||||
matchTheme: false,
|
||||
zoomToFitOnResize: true,
|
||||
linkPrefix: "📍",
|
||||
urlPrefix: "🌐",
|
||||
openInAdjacentPane: false,
|
||||
showLinkBrackets: true,
|
||||
allowCtrlClick: true,
|
||||
forceWrap: false,
|
||||
pageTransclusionCharLimit: 200,
|
||||
pngExportScale: 1,
|
||||
exportWithTheme: true,
|
||||
exportWithBackground: true,
|
||||
@@ -184,10 +192,42 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
this.applySettingsUpdate();
|
||||
}));
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("DISPLAY_HEAD")});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("MATCH_THEME_NAME"))
|
||||
.setDesc(t("MATCH_THEME_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.matchTheme)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.matchTheme = value;
|
||||
this.applySettingsUpdate();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("ZOOM_TO_FIT_NAME"))
|
||||
.setDesc(t("ZOOM_TO_FIT_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.zoomToFitOnResize)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.zoomToFitOnResize = value;
|
||||
this.applySettingsUpdate();
|
||||
}));
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("LINKS_HEAD")});
|
||||
this.containerEl.createEl('p',{
|
||||
text: t("LINKS_DESC")});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("ADJACENT_PANE_NAME"))
|
||||
.setDesc(t("ADJACENT_PANE_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.openInAdjacentPane)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.openInAdjacentPane = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("LINK_BRACKETS_NAME"))
|
||||
.setDesc(t("LINK_BRACKETS_DESC"))
|
||||
@@ -242,6 +282,29 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}));
|
||||
s.descEl.innerHTML="<code>![[doc#^ref]]{number}</code> "+t("TRANSCLUSION_WRAP_DESC");
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("PAGE_TRANSCLUSION_CHARCOUNT_NAME"))
|
||||
.setDesc(t("PAGE_TRANSCLUSION_CHARCOUNT_DESC"))
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter a number')
|
||||
.setValue(this.plugin.settings.pageTransclusionCharLimit.toString())
|
||||
.onChange(async (value) => {
|
||||
const intVal = parseInt(value);
|
||||
if(isNaN(intVal) && value!=="") {
|
||||
text.setValue(this.plugin.settings.pageTransclusionCharLimit.toString());
|
||||
return;
|
||||
}
|
||||
this.requestEmbedUpdate = true;
|
||||
if(value === "") {
|
||||
this.plugin.settings.pageTransclusionCharLimit = 10;
|
||||
this.applySettingsUpdate(true);
|
||||
return;
|
||||
}
|
||||
this.plugin.settings.pageTransclusionCharLimit = intVal;
|
||||
text.setValue(this.plugin.settings.pageTransclusionCharLimit.toString());
|
||||
this.applySettingsUpdate(true);
|
||||
}));
|
||||
|
||||
this.containerEl.createEl('h1', {text: t("EMBED_HEAD")});
|
||||
|
||||
|
||||
@@ -267,6 +330,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
this.applySettingsUpdate();
|
||||
this.requestEmbedUpdate = true;
|
||||
}));
|
||||
|
||||
let dropdown: DropdownComponent;
|
||||
|
||||
new Setting(containerEl)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"1.3.13": "0.11.13"
|
||||
"1.3.20": "0.11.13"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user