This commit is contained in:
Zsolt Viczian
2021-09-25 19:56:46 +02:00
parent a27da5f5f5
commit aafd9f17f8
10 changed files with 302 additions and 122 deletions

View File

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

View File

@@ -231,7 +231,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
async toClipboard(templatePath?:string) {
const template = templatePath ? (await getTemplate(templatePath)) : null;
let elements = template ? template.elements : [];
elements.concat(this.getElements());
elements = elements.concat(this.getElements());
navigator.clipboard.writeText(
JSON.stringify({
"type":"excalidraw/clipboard",
@@ -337,7 +337,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
async createPNG(templatePath?:string, scale:number=1) {
const template = templatePath ? (await getTemplate(templatePath)) : null;
let elements = template ? template.elements : [];
elements.concat(this.getElements());
elements = elements.concat(this.getElements());
return ExcalidrawView.getPNG(
{
"type": "excalidraw",
@@ -748,7 +748,7 @@ async function getTemplate(fileWithPath: string):Promise<{elements: any,appState
let trimLocation = data.search("# Text Elements\n");
if(trimLocation == -1) trimLocation = data.search("# Drawing\n");
const excalidrawData = JSON_parse(getJSON(data));
const excalidrawData = JSON_parse(getJSON(data)[0]);
return {
elements: excalidrawData.elements,
appState: excalidrawData.appState,

View File

@@ -22,9 +22,6 @@ declare module "obsidian" {
}
}
const DRAWING_REG = /[\r\n]# Drawing[\r\n](```json[\r\n])?(.*)(```)?(%%)?/gm;
export const REGEX_LINK = {
//![[link|alias]] [alias](link){num}
//12 3 4 5 6 7 8
@@ -51,14 +48,23 @@ export const REGEX_LINK = {
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
export function getJSON(data:string):string {
const res = data.matchAll(DRAWING_REG);
const parts = res.next();
const DRAWING_REG = /\n%%\n# Drawing\n(```json\n)(.*)\n```%%/gm;
const DRAWING_REG_FALLBACK = /\n# Drawing\n(```json\n)?(.*)(```)?(%%)?/gm;
export function getJSON(data:string):[string,number] {
let res = data.matchAll(DRAWING_REG);
//In case the user adds a text element with the contents "# Drawing\n"
let parts;
parts = res.next();
if(parts.done) { //did not find a match
res = data.matchAll(DRAWING_REG_FALLBACK);
parts = res.next();
}
if(parts.value && parts.value.length>1) {
const result = parts.value[2];
return result.substr(0,result.lastIndexOf("}")+1); //this is a workaround in case sync merges two files together and one version is still an old version without the ```codeblock
return [result.substr(0,result.lastIndexOf("}")+1),parts.value.index]; //this is a workaround in case sync merges two files together and one version is still an old version without the ```codeblock
}
return data;
return [data,parts.value.index];
}
export class ExcalidrawData {
@@ -71,6 +77,7 @@ export class ExcalidrawData {
private urlPrefix: string;
private textMode: TextMode = TextMode.raw;
private plugin: ExcalidrawPlugin;
public loaded: boolean = false;
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
@@ -83,7 +90,7 @@ export class ExcalidrawData {
* @returns {boolean} - true if file was loaded, false if there was an error
*/
public async loadData(data: string,file: TFile, textMode:TextMode):Promise<boolean> {
this.loaded = false;
this.file = file;
this.textElements = new Map<string,{raw:string, parsed:string}>();
@@ -109,27 +116,31 @@ export class ExcalidrawData {
}
//Load scene: Read the JSON string after "# Drawing"
let parts = data.matchAll(DRAWING_REG).next();
if(!(parts.value && parts.value.length>1)) return false; //JSON not found or invalid
if(!this.scene) { //scene was not loaded from .excalidraw
const scene = parts.value[2];
this.scene = JSON_parse(scene.substr(0,scene.lastIndexOf("}")+1)); //this is a workaround to address when files are mereged by sync and one version is still an old markdown without the codeblock ```
//using JSON_parse for legacy compatibiltiy. In an earlier version Excalidraw JSON was not enclosed in a codeblock
const [scene,pos] = getJSON(data);
if (pos === -1) {
return false; //JSON not found
}
//Trim data to remove the JSON string
data = data.substring(0,parts.value.index);
if (!this.scene) {
this.scene = JSON_parse(scene); //this is a workaround to address when files are mereged by sync and one version is still an old markdown without the codeblock ```
}
data = data.substring(0,pos);
//The Markdown # Text Elements take priority over the JSON text elements. Assuming the scenario in which the link was updated due to filename changes
//The .excalidraw JSON is modified to reflect the MD in case of difference
//Read the text elements into the textElements Map
let position = data.search("# Text Elements");
if(position==-1) return true; //Text Elements header does not exist
position += "# Text Elements\n".length;
let position = data.search(/(^%%\n)?# Text Elements\n/m);
if(position==-1) {
await this.setTextMode(textMode,false);
this.loaded = true;
return true; //Text Elements header does not exist
}
position += data.match(/((^%%\n)?# Text Elements\n)/m)[0].length
//iterating through all the text elements in .md
//Text elements always contain the raw value
const BLOCKREF_LEN:number = " ^12345678\n\n".length;
const res = data.matchAll(/\s\^(.{8})[\r\n]/g);
const res = data.matchAll(/\s\^(.{8})[\n]+/g);
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)});
@@ -140,6 +151,7 @@ export class ExcalidrawData {
//e.g. if the entire text elements section was deleted.
this.findNewTextElementsInScene();
await this.setTextMode(textMode,true);
this.loaded = true;
return true;
}

View File

@@ -99,23 +99,25 @@ export default class ExcalidrawView extends TextFileView {
else this.app.vault.create(filepath,JSON.stringify(scene));
}
public async saveSVG(scene?: any) {
public saveSVG(scene?: any) {
if(!scene) {
if (!this.getScene) return false;
scene = this.getScene();
}
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf(this.compatibilityMode ? '.excalidraw':'.md')) + '.svg';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const svg = await ExcalidrawView.getSVG(scene,exportSettings);
if(!svg) return;
let serializer =new XMLSerializer();
const svgString = serializer.serializeToString(ExcalidrawView.embedFontsInSVG(svg));
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
else await this.app.vault.create(filepath,svgString);
(async () => {
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const svg = await ExcalidrawView.getSVG(scene,exportSettings);
if(!svg) return;
let serializer =new XMLSerializer();
const svgString = serializer.serializeToString(ExcalidrawView.embedFontsInSVG(svg));
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
else await this.app.vault.create(filepath,svgString);
})();
}
public static embedFontsInSVG(svg:SVGSVGElement):SVGSVGElement {
@@ -129,21 +131,25 @@ export default class ExcalidrawView extends TextFileView {
return svg;
}
public async savePNG(scene?: any) {
public savePNG(scene?: any) {
if(!scene) {
if (!this.getScene) return false;
scene = this.getScene();
}
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const png = await ExcalidrawView.getPNG(scene,exportSettings,this.plugin.settings.pngExportScale);
if(!png) return;
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf(this.compatibilityMode ? '.excalidraw':'.md')) + '.png';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
if(file && file instanceof TFile) await this.app.vault.modifyBinary(file,await png.arrayBuffer());
else await this.app.vault.createBinary(filepath,await png.arrayBuffer());
(async () => {
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const png = await ExcalidrawView.getPNG(scene,exportSettings,this.plugin.settings.pngExportScale);
if(!png) return;
if(file && file instanceof TFile) await this.app.vault.modifyBinary(file,await png.arrayBuffer());
else await this.app.vault.createBinary(filepath,await png.arrayBuffer());
})();
}
async save(preventReload:boolean=true) {
@@ -167,8 +173,9 @@ export default class ExcalidrawView extends TextFileView {
getViewData () {
//console.log("ExcalidrawView.getViewData()");
if(!this.getScene) return this.data;
if(!this.excalidrawData.loaded) return this.data;
if(!this.compatibilityMode) {
let trimLocation = this.data.search("# Text Elements\n");
let trimLocation = this.data.search(/(^%%\n)?# Text Elements\n/m);
if(trimLocation == -1) trimLocation = this.data.search(/(%%\n)?# Drawing\n/);
if(trimLocation == -1) return this.data;
@@ -352,12 +359,15 @@ export default class ExcalidrawView extends TextFileView {
// clear the view content
clear() {
if(!this.excalidrawRef) return;
this.excalidrawRef.current.resetScene();
this.excalidrawRef.current.history.clear();
}
async setViewData (data: string, clear: boolean = false) {
if(clear) this.clear();
data = this.data = data.replaceAll("\r\n","\n").replaceAll("\r","\n");
this.app.workspace.onLayoutReady(async ()=>{
//console.log("ExcalidrawView.setViewData()");
this.dirty = null;
this.compatibilityMode = this.file.extension == "excalidraw";
await this.plugin.loadSettings();
@@ -372,9 +382,19 @@ export default class ExcalidrawView extends TextFileView {
} else {
const parsed = data.search("excalidraw-plugin: parsed\n")>-1 || data.search("excalidraw-plugin: locked\n")>-1; //locked for backward compatibility
this.changeTextMode(parsed ? TextMode.parsed : TextMode.raw,false);
if(!(await this.excalidrawData.loadData(data, this.file,this.textMode))) return;
try {
if(!(await this.excalidrawData.loadData(data, this.file,this.textMode))) return;
} catch(e) {
new Notice( "Error loading drawing:\n"
+ e.message
+ ((e.message === "Cannot read property 'index' of undefined")
? "\n'# Drawing' section is likely missing"
: "")
+ "\nTry manually fixing the file or restoring an earlier version from sync history" ,8000);
this.setMarkdownView();
return;
}
}
if(clear) this.clear();
await this.loadDrawing(true)
});
}
@@ -385,14 +405,10 @@ export default class ExcalidrawView extends TextFileView {
*/
private async loadDrawing(justloaded:boolean) {
const excalidrawData = this.excalidrawData.scene;
this.justLoaded = justloaded;
if(this.excalidrawRef) {
const viewModeEnabled = this.excalidrawRef.current.getAppState().viewModeEnabled;
const zenModeEnabled = this.excalidrawRef.current.getAppState().zenModeEnabled;
if(justloaded) {
this.excalidrawRef.current.resetScene();
this.excalidrawRef.current.history.clear();
this.justLoaded = justloaded; //reset screen will clear justLoaded, so need to set it here
}
this.excalidrawRef.current.updateScene({
elements: excalidrawData.elements,
appState: {
@@ -406,7 +422,6 @@ export default class ExcalidrawView extends TextFileView {
this.excalidrawWrapperRef.current.focus();
}
} else {
this.justLoaded = justloaded;
this.instantiateExcalidraw({
elements: excalidrawData.elements,
appState: excalidrawData.appState,
@@ -436,6 +451,11 @@ export default class ExcalidrawView extends TextFileView {
return ICON_NAME;
}
setMarkdownView() {
this.plugin.excalidrawFileModes[this.id || this.file.path] = "markdown";
this.plugin.setMarkdownView(this.leaf);
}
onMoreOptionsMenu(menu: Menu) {
// Add a menu item to force the board to markdown view
if(!this.compatibilityMode) {
@@ -445,8 +465,7 @@ export default class ExcalidrawView extends TextFileView {
.setTitle(t("OPEN_AS_MD"))
.setIcon("document")
.onClick(async () => {
this.plugin.excalidrawFileModes[this.id || this.file.path] = "markdown";
this.plugin.setMarkdownView(this.leaf);
this.setMarkdownView();
});
})
.addItem((item) => {

43
src/InsertLinkDialog.ts Normal file
View File

@@ -0,0 +1,43 @@
import {
App,
FuzzySuggestModal,
TFile
} from "obsidian";
import {t} from './lang/helpers'
export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
public app: App;
private addText: Function;
private drawingPath: string;
constructor(app: App) {
super(app);
this.app = app;
this.limit = 20;
this.setInstructions([{
command: t("SELECT_FILE"),
purpose: "",
}]);
this.setPlaceholder(t("SELECT_FILE_TO_LINK"));
this.emptyStateText = t("NO_MATCH");
}
getItems(): TFile[] {
return this.app.vault.getFiles();
}
getItemText(item: TFile): string {
return item.path;
}
onChooseItem(item: TFile, _evt: MouseEvent | KeyboardEvent): void {
const filepath = this.app.metadataCache.fileToLinktext(item,this.drawingPath,true);
this.addText("[["+filepath+"]]");
}
public start(drawingPath:string, addText: Function) {
this.addText = addText;
this.drawingPath = drawingPath;
this.open();
}
}

View File

@@ -51,7 +51,7 @@ export default {
TEMPLATE_NAME: "Excalidraw template file",
TEMPLATE_DESC: "Full filepath to the Excalidraw template. " +
"E.g.: If your template is in the default Excalidraw folder and it's name is " +
"Template.md, the setting would be: Excalidraw/Template.md " +
"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",
@@ -99,9 +99,14 @@ export default {
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: "The default width of an embedded drawing. You can specify a custom " +
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",

View File

@@ -46,6 +46,9 @@ import {
openDialogAction,
OpenFileDialog
} from "./openDrawing";
import {
InsertLinkDialog
} from "./InsertLinkDialog";
import {
initExcalidrawAutomate,
destroyExcalidrawAutomate
@@ -55,6 +58,7 @@ import { around } from "monkey-around";
import { t } from "./lang/helpers";
import { MigrationPrompt } from "./MigrationPrompt";
import { checkAndCreateFolder, download, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, splitFolderAndFilename } from "./Utils";
import { tsExpressionWithTypeArguments } from "@babel/types";
declare module "obsidian" {
interface App {
@@ -74,6 +78,7 @@ export default class ExcalidrawPlugin extends Plugin {
public settings: ExcalidrawSettings;
//public stencilLibrary: any = null;
private openDialog: OpenFileDialog;
private insertLinkDialog: InsertLinkDialog;
private activeExcalidrawView: ExcalidrawView = null;
public lastActiveExcalidrawFilePath: string = null;
public hover: {linkText: string, sourcePath: string} = {linkText: null, sourcePath: null};
@@ -117,6 +122,43 @@ export default class ExcalidrawPlugin extends Plugin {
new Notice(`You are running an older version of the electron Browser (${electron}). If Excalidraw does not start up, please reinstall Obsidian with the latest installer and try again.`,10000);
}
this.switchToExcalidarwAfterLoad()
//This is a once off cleanup process to remediate incorrectly placed comment %% before # Text Elements
if(this.settings.patchCommentBlock) {
const self = this;
console.log(window.moment().format("HH:mm:ss") + ": Excalidraw will patch drawings in 5 minutes");
setTimeout(async ()=>{
await self.loadSettings();
if (!self.settings.patchCommentBlock) {
console.log(window.moment().format("HH:mm:ss") + ": Excalidraw patching aborted because synched data.json is already patched");
return;
}
console.log(window.moment().format("HH:mm:ss") + ": Excalidraw is starting the patching process");
let i = 0;
const excalidrawFiles = this.app.vault.getFiles();
for (const f of (excalidrawFiles || []).filter((f:TFile) => self.isExcalidrawFile(f))) {
if ( (f.extension !== "excalidraw") //legacy files do not need to be touched
&& (self.app.workspace.getActiveFile() !== f)) { //file is currently being edited
let drawing = await self.app.vault.read(f);
const orig_drawing = drawing;
drawing = drawing.replaceAll("\r\n","\n").replaceAll("\r","\n"); //Win, Mac, Linux compatibility
drawing = drawing.replace("\n%%\n# Text Elements\n","\n# Text Elements\n");
if (drawing.search("\n%%\n# Drawing\n") === -1) {
const [json,pos] = getJSON(drawing);
drawing = drawing.substr(0,pos)+"\n%%\n# Drawing\n```json\n"+json+"\n```%%";
};
if (drawing !== orig_drawing) {
i++;
console.log("Excalidraw patched: " + f.path);
await self.app.vault.modify(f,drawing);
}
}
}
self.settings.patchCommentBlock = false;
self.saveSettings();
console.log(window.moment().format("HH:mm:ss") + ": Excalidraw patched in total " + i + " files");
},300000) //5 minutes
}
}
private switchToExcalidarwAfterLoad() {
@@ -191,12 +233,12 @@ export default class ExcalidrawPlugin extends Plugin {
if(width>=800) scale = 2;
if(width>=1600) scale = 3;
if(width>=2400) scale = 4;
const png = await ExcalidrawView.getPNG(JSON_parse(getJSON(content)),exportSettings, scale);
const png = await ExcalidrawView.getPNG(JSON_parse(getJSON(content)[0]),exportSettings, scale);
if(!png) return null;
img.src = URL.createObjectURL(png);
return img;
}
let svg = await ExcalidrawView.getSVG(JSON_parse(getJSON(content)),exportSettings);
let svg = await ExcalidrawView.getSVG(JSON_parse(getJSON(content)[0]),exportSettings);
if(!svg) return null;
svg = ExcalidrawView.embedFontsInSVG(svg);
svg.removeAttribute('width');
@@ -401,6 +443,7 @@ export default class ExcalidrawPlugin extends Plugin {
private registerCommands() {
this.openDialog = new OpenFileDialog(this.app, this);
this.insertLinkDialog = new InsertLinkDialog(this.app);
this.addRibbonIcon(ICON_NAME, t("CREATE_NEW"), async (e) => {
this.createDrawing(this.getNextDefaultFilename(), e.ctrlKey||e.metaKey);
@@ -645,7 +688,7 @@ export default class ExcalidrawPlugin extends Plugin {
} else {
const view = this.app.workspace.activeLeaf.view;
if (view instanceof ExcalidrawView) {
this.openDialog.insertLink(view.file.path,view.addText);
this.insertLinkDialog.start(view.file.path,view.addText);
return true;
}
else return false;
@@ -904,7 +947,7 @@ export default class ExcalidrawPlugin extends Plugin {
const deleteEventHandler = async (file:TFile) => {
if (!(file instanceof TFile)) return;
//@ts-ignore
const isExcalidarwFile = (file.unsafeCachedData && file.unsafeCachedData.search(/---[\r\n][\s\S]*excalidraw-plugin:\s*(locked|unlocked)[\r\n][\s\S]*---/gm)>-1)
const isExcalidarwFile = (file.unsafeCachedData && file.unsafeCachedData.search(/---[\r\n]+[\s\S]*excalidraw-plugin:\s*\w+[\r\n]+[\s\S]*---/gm)>-1)
|| (file.extension=="excalidraw");
if(!isExcalidarwFile) return;
@@ -980,7 +1023,18 @@ export default class ExcalidrawPlugin extends Plugin {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if(activeView) {
const editor = activeView.editor;
editor.replaceSelection("![["+data+"]]");
switch (this.settings.embedType) {
case "excalidraw":
editor.replaceSelection("![["+data+"]]");
break;
case "PNG":
editor.replaceSelection("![["+data.substr(0,data.lastIndexOf("."))+".png]] ([["+data+"|*]])");
break;
case "SVG":
editor.replaceSelection("![["+data.substr(0,data.lastIndexOf("."))+".svg]] ([["+data+"|*]])");
break;
}
editor.focus();
}

View File

@@ -12,7 +12,6 @@ import {t} from './lang/helpers'
export enum openDialogAction {
openFile,
insertLinkToDrawing,
insertLink
}
export class OpenFileDialog extends FuzzySuggestModal<TFile> {
@@ -20,8 +19,6 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
private plugin: ExcalidrawPlugin;
private action: openDialogAction;
private onNewPane: boolean;
private addText: Function;
private drawingPath: string;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
@@ -29,6 +26,11 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
this.action = openDialogAction.openFile;
this.plugin = plugin;
this.onNewPane = false;
this.limit = 20;
this.setInstructions([{
command: t("TYPE_FILENAME"),
purpose: "",
}]);
this.inputEl.onkeyup = (e) => {
if(e.key=="Enter" && this.action == openDialogAction.openFile) {
@@ -42,10 +44,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
getItems(): TFile[] {
const excalidrawFiles = this.app.vault.getFiles();
return (excalidrawFiles || []).filter((f:TFile) => {
if (this.action == openDialogAction.insertLink) return true;
return this.plugin.isExcalidrawFile(f);
});
return (excalidrawFiles || []).filter((f:TFile) => this.plugin.isExcalidrawFile(f));
}
getItemText(item: TFile): string {
@@ -60,32 +59,10 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
case(openDialogAction.insertLinkToDrawing):
this.plugin.embedDrawing(item.path);
break;
case(openDialogAction.insertLink):
//TO-DO
const filepath = this.app.metadataCache.fileToLinktext(item,this.drawingPath,true);
this.addText("[["+filepath+"]]");
break;
}
}
public insertLink(drawingPath:string, addText: Function) {
this.action = openDialogAction.insertLink;
this.addText = addText;
this.drawingPath = drawingPath;
this.setInstructions([{
command: t("SELECT_FILE"),
purpose: "",
}]);
this.emptyStateText = t("NO_MATCH");
this.setPlaceholder(t("SELECT_FILE_TO_LINK"));
this.open();
}
public start(action:openDialogAction, onNewPane: boolean): void {
this.setInstructions([{
command: t("TYPE_FILENAME"),
purpose: "",
}]);
this.action = action;
this.onNewPane = onNewPane;
switch(action) {

View File

@@ -1,5 +1,6 @@
import {
App,
DropdownComponent,
PluginSettingTab,
Setting,
TFile
@@ -28,6 +29,7 @@ export interface ExcalidrawSettings {
autoexportSVG: boolean,
autoexportPNG: boolean,
autoexportExcalidraw: boolean,
embedType: "excalidraw"|"PNG"|"SVG",
syncExcalidraw: boolean,
compatibilityMode: boolean,
experimentalFileType: boolean,
@@ -35,6 +37,7 @@ export interface ExcalidrawSettings {
loadCount: number, //version 1.2 migration counter
drawingOpenCount: number,
library: string,
patchCommentBlock: boolean, //1.3.12
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
@@ -56,6 +59,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
autoexportSVG: false,
autoexportPNG: false,
autoexportExcalidraw: false,
embedType: "excalidraw",
syncExcalidraw: false,
experimentalFileType: false,
experimentalFileTag: "✏️",
@@ -63,18 +67,29 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
loadCount: 0,
drawingOpenCount: 0,
library: `{"type":"excalidrawlib","version":1,"library":[]}`,
patchCommentBlock: true,
}
export class ExcalidrawSettingTab extends PluginSettingTab {
plugin: ExcalidrawPlugin;
private requestEmbedUpdate:boolean = false;
private requestReloadDrawings:boolean = false;
private applyDebounceTimer: number = 0;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app, plugin);
this.plugin = plugin;
}
applySettingsUpdate(requestReloadDrawings:boolean = false) {
clearTimeout(this.applyDebounceTimer);
const plugin = this.plugin;
this.applyDebounceTimer = window.setTimeout(() => {
plugin.saveSettings();
}, 200);
if(requestReloadDrawings) this.requestReloadDrawings = true;
}
async hide() {
if(this.requestReloadDrawings) {
const exs = this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
@@ -114,7 +129,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.folder)
.onChange(async (value) => {
this.plugin.settings.folder = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
new Setting(containerEl)
@@ -125,7 +140,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.templateFilePath)
.onChange(async (value) => {
this.plugin.settings.templateFilePath = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
this.containerEl.createEl('h1', {text: t("FILENAME_HEAD")});
@@ -153,7 +168,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.plugin.settings.drawingFilenamePrefix = value.replaceAll(/[<>:"/\\|?*]/g,'_');
text.setValue(this.plugin.settings.drawingFilenamePrefix);
filenameEl.innerHTML = getFilenameSample();
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
new Setting(containerEl)
@@ -166,7 +181,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.plugin.settings.drawingFilenameDateTime = value.replaceAll(/[<>:"/\\|?*]/g,'_');
text.setValue(this.plugin.settings.drawingFilenameDateTime);
filenameEl.innerHTML = getFilenameSample();
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
this.containerEl.createEl('h1', {text: t("LINKS_HEAD")});
@@ -180,8 +195,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.showLinkBrackets)
.onChange(async (value) => {
this.plugin.settings.showLinkBrackets = value;
await this.plugin.saveSettings();
this.requestReloadDrawings = true;
this.applySettingsUpdate(true);
}));
new Setting(containerEl)
@@ -193,8 +207,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.onChange((value) => {
console.log(value);
this.plugin.settings.linkPrefix = value;
this.plugin.saveSettings();
this.requestReloadDrawings = true;
this.applySettingsUpdate(true);
}));
new Setting(containerEl)
@@ -205,8 +218,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.urlPrefix)
.onChange(async (value) => {
this.plugin.settings.urlPrefix = value;
await this.plugin.saveSettings();
this.requestReloadDrawings = true;
this.applySettingsUpdate(true);
}));
new Setting(containerEl)
@@ -216,7 +228,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.allowCtrlClick)
.onChange(async (value) => {
this.plugin.settings.allowCtrlClick = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
const s = new Setting(containerEl)
@@ -226,8 +238,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.forceWrap)
.onChange(async (value) => {
this.plugin.settings.forceWrap = value;
await this.plugin.saveSettings();
this.requestReloadDrawings = true;
this.applySettingsUpdate(true);
}));
s.descEl.innerHTML="<code>![[doc#^ref]]{number}</code> "+t("TRANSCLUSION_WRAP_DESC");
@@ -241,7 +252,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.displaySVGInPreview)
.onChange(async (value) => {
this.plugin.settings.displaySVGInPreview = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
@@ -253,9 +264,41 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.width)
.onChange(async (value) => {
this.plugin.settings.width = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
this.requestEmbedUpdate = true;
}));
}));
let dropdown: DropdownComponent;
new Setting(containerEl)
.setName(t("EMBED_TYPE_NAME"))
.setDesc(t("EMBED_TYPE_DESC"))
.addDropdown(async (d:DropdownComponent) => {
dropdown = d;
dropdown.addOption("excalidraw","excalidraw")
if(this.plugin.settings.autoexportPNG) {
dropdown.addOption("PNG","PNG");
} else {
if(this.plugin.settings.embedType === "PNG") {
this.plugin.settings.embedType = "excalidraw";
this.applySettingsUpdate();
}
}
if(this.plugin.settings.autoexportSVG) {
dropdown.addOption("SVG","SVG");
} else {
if(this.plugin.settings.embedType === "SVG") {
this.plugin.settings.embedType = "excalidraw";
this.applySettingsUpdate();
}
}
dropdown
.setValue(this.plugin.settings.embedType)
.onChange(async (value)=>{
//@ts-ignore
this.plugin.settings.embedType = value;
this.applySettingsUpdate();
});
});
let scaleText:HTMLDivElement;
@@ -268,7 +311,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.onChange(async (value)=> {
scaleText.innerText = " " + value.toString();
this.plugin.settings.pngExportScale = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}))
.settingEl.createDiv('',(el)=>{
scaleText = el;
@@ -284,7 +327,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.exportWithBackground)
.onChange(async (value) => {
this.plugin.settings.exportWithBackground = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
this.requestEmbedUpdate = true;
}));
@@ -295,7 +338,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.exportWithTheme)
.onChange(async (value) => {
this.plugin.settings.exportWithTheme = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
this.requestEmbedUpdate = true;
}));
@@ -308,17 +351,35 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.keepInSync)
.onChange(async (value) => {
this.plugin.settings.keepInSync = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
const removeDropdownOption = (opt: string) => {
let i=0;
for(i=0;i<dropdown.selectEl.options.length;i++) {
if((dropdown.selectEl.item(i) as HTMLOptionElement).label===opt) {
dropdown.selectEl.item(i).remove();
}
}
}
new Setting(containerEl)
.setName(t("EXPORT_SVG_NAME"))
.setDesc(t("EXPORT_SVG_DESC"))
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoexportSVG)
.onChange(async (value) => {
if(value) {
dropdown.addOption("SVG","SVG");
} else {
if (this.plugin.settings.embedType === "SVG") {
dropdown.setValue("excalidraw");
this.plugin.settings.embedType = "excalidraw";
}
removeDropdownOption("SVG");
}
this.plugin.settings.autoexportSVG = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
@@ -328,8 +389,17 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoexportPNG)
.onChange(async (value) => {
if(value) {
dropdown.addOption("PNG","PNG");
} else {
if (this.plugin.settings.embedType === "PNG") {
dropdown.setValue("excalidraw");
this.plugin.settings.embedType = "excalidraw";
}
removeDropdownOption("PNG");
}
this.plugin.settings.autoexportPNG = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
this.containerEl.createEl('h1', {text: t("COMPATIBILITY_HEAD")});
@@ -341,7 +411,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.compatibilityMode)
.onChange(async (value) => {
this.plugin.settings.compatibilityMode = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
new Setting(containerEl)
@@ -351,7 +421,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.autoexportExcalidraw)
.onChange(async (value) => {
this.plugin.settings.autoexportExcalidraw = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
new Setting(containerEl)
@@ -361,7 +431,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.syncExcalidraw)
.onChange(async (value) => {
this.plugin.settings.syncExcalidraw = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
this.containerEl.createEl('h1', {text: t("EXPERIMENTAL_HEAD")});
@@ -375,7 +445,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.experimentalFileType = value;
this.plugin.experimentalFileTypeDisplayToggle(value);
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
new Setting(containerEl)
@@ -386,7 +456,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.experimentalFileTag)
.onChange(async (value) => {
this.plugin.settings.experimentalFileTag = value;
await this.plugin.saveSettings();
this.applySettingsUpdate();
}));
}
}

View File

@@ -1,3 +1,3 @@
{
"1.3.11": "0.11.13"
"1.3.12": "0.11.13"
}