import {
  TFile,
  App,
  MarkdownView,
  normalizePath,
  Menu,
  MenuItem,
  Notice,
  Command,
  EventRef,
  FileView,
} from "obsidian";
import {
  VIEW_TYPE_EXCALIDRAW,
  ICON_NAME,
  IMAGE_TYPES,
  DEVICE,
  sceneCoordsToViewportCoords,
  fileid,
} from "../../constants/constants";
import ExcalidrawView, { TextMode } from "../../view/ExcalidrawView";
import {
  REGEX_LINK,
} from "../../shared/ExcalidrawData";
import { ExcalidrawSettings } from "../settings";
import { openDialogAction, OpenFileDialog } from "../../shared/Dialogs/OpenDrawing";
import { InsertLinkDialog } from "../../shared/Dialogs/InsertLinkDialog";
import { InsertCommandDialog } from "../../shared/Dialogs/InsertCommandDialog";
import { InsertImageDialog } from "../../shared/Dialogs/InsertImageDialog";
import { ImportSVGDialog } from "../../shared/Dialogs/ImportSVGDialog";
import { InsertMDDialog } from "../../shared/Dialogs/InsertMDDialog";
import { ExcalidrawAutomate } from "../../shared/ExcalidrawAutomate";
import { insertLaTeXToView, search } from "src/utils/excalidrawAutomateUtils";
import { templatePromt } from "../../shared/Dialogs/Prompt";
import { t } from "../../lang/helpers";
import {
  createOrOverwriteFile,
  getAliasWithSize,
  getAnnotationFileNameAndFolder,
  getCropFileNameAndFolder,
  getDrawingFilename,
  getEmbedFilename,
  getIMGFilename,
  getLink,
  getListOfTemplateFiles,
  getURLImageExtension,
} from "../../utils/fileUtils";
import {
  setLeftHandedMode,
  sleep,
  decompress,
  getImageSize,
} from "../../utils/utils";
import { extractSVGPNGFileName, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, isObsidianThemeDark, mergeMarkdownFiles, setExcalidrawView } from "../../utils/obsidianUtils";
import { ExcalidrawElement, ExcalidrawEmbeddableElement, ExcalidrawImageElement, ExcalidrawTextElement, FileId } from "@zsviczian/excalidraw/types/element/src/types";
import { ReleaseNotes } from "../../shared/Dialogs/ReleaseNotes";
import { ScriptInstallPrompt } from "../../shared/Dialogs/ScriptInstallPrompt";
import Taskbone from "../../shared/OCR/Taskbone";
import { emulateCTRLClickForLinks, linkClickModifierType, PaneTarget } from "../../utils/modifierkeyHelper";
import { InsertPDFModal } from "../../shared/Dialogs/InsertPDFModal";
import { ExportDialog } from "../../shared/Dialogs/ExportDialog";
import { UniversalInsertFileModal } from "../../shared/Dialogs/UniversalInsertFileModal";
import { PublishOutOfDateFilesDialog } from "../../shared/Dialogs/PublishOutOfDateFiles";
import { EmbeddableSettings } from "../../shared/Dialogs/EmbeddableSettings";
import { processLinkText } from "../../utils/customEmbeddableUtils";
import { getEA } from "src/core";
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
import { Mutable } from "@zsviczian/excalidraw/types/common/src/utility-types";
import { carveOutImage, carveOutPDF, createImageCropperFile } from "../../utils/carveout";
import { showFrameSettings } from "../../shared/Dialogs/FrameSettings";
import { insertImageToView } from "../../utils/excalidrawViewUtils";
import ExcalidrawPlugin from "src/core/main";

declare const PLUGIN_VERSION:string;

export class CommandManager {
  private app: App;
  private plugin: ExcalidrawPlugin;
  private openDialog: OpenFileDialog;
  public insertLinkDialog: InsertLinkDialog;
  public insertCommandDialog: InsertCommandDialog;
  public insertImageDialog: InsertImageDialog;
  public importSVGDialog: ImportSVGDialog;
  public insertMDDialog: InsertMDDialog;
  public taskbone: Taskbone;
  public forceSaveCommand:Command;

  get settings(): ExcalidrawSettings {
    return this.plugin.settings;
  }

  get ea(): ExcalidrawAutomate {
    return this.plugin.ea;
  }

  private isExcalidrawFile(file: TFile): boolean {
    return this.plugin.isExcalidrawFile(file);
  }
  
  constructor(plugin: ExcalidrawPlugin) {
    this.app = plugin.app;
    this.plugin = plugin;
  }

  public initialize() {
    try {
      this.taskbone = new Taskbone(this.plugin);
      this.registerCommands();
    } catch (e) {
      new Notice("Error registering commands", 6000);
      console.error("Error registering commands", e);
    }
    this.plugin.logStartupEvent("Commands registered");
  }

  destroy() {
    this.openDialog.destroy();
    this.openDialog = null;
    this.insertLinkDialog.destroy();
    this.insertLinkDialog = null;
    this.insertCommandDialog.destroy();
    this.insertCommandDialog = null;
    this.importSVGDialog.destroy();
    this.importSVGDialog = null;
    this.insertImageDialog.destroy();
    this.insertImageDialog = null;
    this.insertMDDialog.destroy();
    this.insertMDDialog = null;
    if (this.taskbone) {
      this.taskbone.destroy();
      this.taskbone = null;
    }
    this.forceSaveCommand = null;
  }

  private registerEvent(event: EventRef): void {
    this.plugin.registerEvent(event);
  }

  private addCommand(command: Command): Command {
    return this.plugin.addCommand(command);
  }

  private registerCommands() {
    this.openDialog = new OpenFileDialog(this.app, this.plugin);
    this.insertLinkDialog = new InsertLinkDialog(this.plugin);
    this.insertCommandDialog = new InsertCommandDialog(this.app);
    this.insertImageDialog = new InsertImageDialog(this.plugin);
    this.importSVGDialog = new ImportSVGDialog(this.plugin);
    this.insertMDDialog = new InsertMDDialog(this.plugin);

    const createNewAction = (e: MouseEvent | KeyboardEvent, file: TFile) => {
      let folderpath = file.path;
      if (file instanceof TFile) {
        folderpath = normalizePath(
          file.path.substr(0, file.path.lastIndexOf(file.name)),
        );
      }
      this.plugin.createAndOpenDrawing(
        getDrawingFilename(this.settings),
        linkClickModifierType(emulateCTRLClickForLinks(e)),
        folderpath,
      );
    }

    const fileMenuHandlerCreateNew = (menu: Menu, file: TFile) => {
      menu.addItem((item: MenuItem) => {
        item
          .setSection('action-primary')
          .setTitle(t("CREATE_NEW"))
          .setIcon(ICON_NAME)
          .onClick((e) => {createNewAction(e, file)});
      });
    };

    this.registerEvent(
      this.app.workspace.on("file-menu", fileMenuHandlerCreateNew),
    );

    const fileMenuHandlerConvertKeepExtension = (menu: Menu, file: TFile) => {
      if (file instanceof TFile && file.extension == "excalidraw") {
        menu.addItem((item: MenuItem) => {
          item.setTitle(t("CONVERT_FILE_KEEP_EXT")).onClick(() => {
            this.plugin.convertSingleExcalidrawToMD(file, false, false);
          });
        });
      }
    };

    this.registerEvent(
      this.app.workspace.on("file-menu", fileMenuHandlerConvertKeepExtension),
    );

    const fileMenuHandlerConvertReplaceExtension = (
      menu: Menu,
      file: TFile,
    ) => {
      if (file instanceof TFile && file.extension == "excalidraw") {
        menu.addItem((item: MenuItem) => {
          item.setTitle(t("CONVERT_FILE_REPLACE_EXT")).onClick(() => {
            this.plugin.convertSingleExcalidrawToMD(file, true, true);
          });
        });
      }
    };

    this.registerEvent(
      this.app.workspace.on(
        "file-menu",
        fileMenuHandlerConvertReplaceExtension,
      ),
    );

    this.addCommand({
      id: "excalidraw-convert-image-from-url-to-local-file",
      name: t("CONVERT_URL_TO_FILE"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        const img = view.getSingleSelectedImage();
        if(!img || !img.embeddedFile?.isHyperLink) return false;
        if(checking) return true;
        view.convertImageElWithURLToLocalFile(img);
      },
    });

    this.addCommand({
      id: "excalidraw-unzip-file",
      name: t("UNZIP_CURRENT_FILE"),
      checkCallback: (checking: boolean) => {
        const activeFile = this.app.workspace.getActiveFile();
        if (!activeFile) {
          return false;
        }
        const fileIsExcalidraw = this.isExcalidrawFile(activeFile);
        if (!fileIsExcalidraw) {
          return false;
        }

        const excalidrawView = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (excalidrawView) {
          return false;
        }

        if (checking) {
          return true;
        }

        (async () => {
          const data = await this.app.vault.read(activeFile);
          const parts = data.split("\n## Drawing\n```compressed-json\n");
          if(parts.length!==2) return;
          const header = parts[0] + "\n## Drawing\n```json\n";
          const compressed = parts[1].split("\n```\n%%");
          if(compressed.length!==2) return;
          const decompressed = decompress(compressed[0]);
          if(!decompressed) {
            new Notice("The compressed string is corrupted. Unable to decompress data.");
            return;
          }
          await createOrOverwriteFile(this.app, activeFile.path,header + decompressed + "\n```\n%%" + compressed[1]);
        })();

      }
    })

    this.addCommand({
      id: "excalidraw-publish-svg-check",
      name: t("PUBLISH_SVG_CHECK"),
      checkCallback: (checking: boolean) => {
        const publish = this.app.internalPlugins.plugins["publish"].instance;
        if (!publish) {
          return false;
        }
        if (checking) {
          return true;
        }
        (new PublishOutOfDateFilesDialog(this.plugin)).open();
      }
    })

    this.addCommand({
      id: "excalidraw-embeddable-poroperties",
      name: t("EMBEDDABLE_PROPERTIES"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el=>el.type==="embeddable") as ExcalidrawEmbeddableElement[];
        if(els.length !== 1) {
          if(checking) return false;
          new Notice("Select a single embeddable element and try again");
          return false;
        }
        if(checking) return true;
        const getFile = (el:ExcalidrawEmbeddableElement):TFile => {
          const res = REGEX_LINK.getRes(el.link).next();
          if(!res || (!res.value && res.done)) {
            return null;
          }
          const link = REGEX_LINK.getLink(res);
          const { file } = processLinkText(link, view);
          return file;
        }
        new EmbeddableSettings(view.plugin,view,getFile(els[0]),els[0]).open();
      }
    })

    this.addCommand({
      id: "excalidraw-embeddables-relative-scale",
      name: t("EMBEDDABLE_RELATIVE_ZOOM"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el=>el.type==="embeddable") as ExcalidrawEmbeddableElement[];
        if(els.length === 0) {
          if(checking) return false;
          new Notice("Select at least one embeddable element and try again");
          return false;
        }
        if(checking) return true;
        const ea = getEA(view) as ExcalidrawAutomate;
        const api = ea.getExcalidrawAPI() as ExcalidrawImperativeAPI;
        ea.copyViewElementsToEAforEditing(els);
        const scale = 1/api.getAppState().zoom.value;
        ea.getElements().forEach((el: Mutable<ExcalidrawEmbeddableElement>)=>{
          el.scale = [scale,scale];
        })
        ea.addElementsToView().then(()=>ea.destroy());
      }
    })

    this.addCommand({
      id: "open-image-excalidraw-source",
      name: t("OPEN_IMAGE_SOURCE"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(MarkdownView);
        if(!view) return false;
        if(view.leaf !== this.app.workspace.activeLeaf) return false;
        const editor = view.editor;
        if(!editor) return false;
        const cursor = editor.getCursor();
        const line = editor.getLine(cursor.line);
        const fname = extractSVGPNGFileName(line);
        if(!fname) return false;
        const imgFile = this.app.metadataCache.getFirstLinkpathDest(fname, view.file.path);
        if(!imgFile) return false;
        const excalidrawFname = getIMGFilename(imgFile.path, "md");
        let excalidrawFile = this.app.metadataCache.getFirstLinkpathDest(excalidrawFname, view.file.path);
        if(!excalidrawFile) {
          if(excalidrawFname.endsWith(".dark.md")) {
            excalidrawFile = this.app.metadataCache.getFirstLinkpathDest(excalidrawFname.replace(/\.dark\.md$/,".md"), view.file.path);
          }
          if(excalidrawFname.endsWith(".light.md")) {
            excalidrawFile = this.app.metadataCache.getFirstLinkpathDest(excalidrawFname.replace(/\.light\.md$/,".md"), view.file.path);
          }
          //handles the case if the png or svg is not in the same folder as the excalidraw file
          if(!excalidrawFile) {
            const basename = imgFile.basename.replace(/(?:\.dark|\.light)$/,"");
            const candidates = this.app.vault.getMarkdownFiles().filter(f=>f.basename === basename);
            if(candidates.length === 1) {
              excalidrawFile = candidates[0];
            } else {
              return false;
            }
          }
        }
        if(checking) return true;
        this.plugin.openDrawing(excalidrawFile, "new-tab", true);
      }
    });

    this.addCommand({
      id: "excalidraw-disable-autosave",
      name: t("TEMPORARY_DISABLE_AUTOSAVE"),
      checkCallback: (checking) => {
        if(!this.settings.autosave) return false; //already disabled
        if(checking) return true;
        this.settings.autosave = false;
        return true;
      }
    })

    this.addCommand({
      id: "excalidraw-enable-autosave",
      name: t("TEMPORARY_ENABLE_AUTOSAVE"),
      checkCallback: (checking) => {
        if(this.settings.autosave) return false; //already enabled
        if(checking) return true;
        this.settings.autosave = true;
        return true;
      }
    })    

    this.addCommand({
      id: "excalidraw-download-lib",
      name: t("DOWNLOAD_LIBRARY"),
      callback: ()=>this.plugin.exportLibrary(),
    });

    this.addCommand({
      id: "excalidraw-open",
      name: t("OPEN_EXISTING_NEW_PANE"),
      callback: () => {
        this.openDialog.start(openDialogAction.openFile, true);
      },
    });

    this.addCommand({
      id: "excalidraw-open-on-current",
      name: t("OPEN_EXISTING_ACTIVE_PANE"),
      callback: () => {
        this.openDialog.start(openDialogAction.openFile, false);
      },
    });

    this.addCommand({
      id: "excalidraw-insert-transclusion",
      name: t("TRANSCLUDE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView)) 
        }
        this.openDialog.start(openDialogAction.insertLinkToDrawing, false);
        return true;
      },
    });

    this.addCommand({
      id: "excalidraw-insert-last-active-transclusion",
      name: t("TRANSCLUDE_MOST_RECENT"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(MarkdownView)) &&
            this.plugin.lastActiveExcalidrawFilePath !== null
          );
        }
        const file = this.app.vault.getAbstractFileByPath(
          this.plugin.lastActiveExcalidrawFilePath,
        );
        if (!(file instanceof TFile)) {
          return false;
        }
        this.plugin.embedDrawing(file);
        return true;
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate",
      name: t("NEW_IN_NEW_PANE"),
      callback: () => {
        this.plugin.createAndOpenDrawing(getDrawingFilename(this.settings), "new-pane");
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-newtab",
      name: t("NEW_IN_NEW_TAB"),
      callback: () => {
        this.plugin.createAndOpenDrawing(getDrawingFilename(this.settings), "new-tab");
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-on-current",
      name: t("NEW_IN_ACTIVE_PANE"),
      callback: () => {
        this.plugin.createAndOpenDrawing(getDrawingFilename(this.settings), "active-pane");
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-popout",
      name: t("NEW_IN_POPOUT_WINDOW"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return !DEVICE.isMobile;
        }
        this.plugin.createAndOpenDrawing(getDrawingFilename(this.settings), "popout-window");
      },
    });

    const insertDrawingToDoc = async (
      location: PaneTarget
    ) => {
      const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
      if (!activeView) {
        return;
      }
      const filename = getEmbedFilename(
        activeView.file.basename,
        this.settings,
      );
      const folder = this.settings.embedUseExcalidrawFolder
        ? null
        : (
            await getAttachmentsFolderAndFilePath(
              this.app,
              activeView.file.path,
              filename,
            )
          ).folder;
      const file = await this.plugin.createDrawing(filename, folder);
      await this.plugin.embedDrawing(file);
      this.plugin.openDrawing(file, location, true, undefined, true);
    };

    this.addCommand({
      id: "excalidraw-autocreate-and-embed",
      name: t("NEW_IN_NEW_PANE_EMBED"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
        }
        insertDrawingToDoc("new-pane");
        return true;
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-and-embed-new-tab",
      name: t("NEW_IN_NEW_TAB_EMBED"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
        }
        insertDrawingToDoc("new-tab");
        return true;
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-and-embed-on-current",
      name: t("NEW_IN_ACTIVE_PANE_EMBED"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
        }
        insertDrawingToDoc("active-pane");
        return true;
      },
    });

    this.addCommand({
      id: "excalidraw-autocreate-and-embed-popout",
      name: t("NEW_IN_POPOUT_WINDOW_EMBED"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return !DEVICE.isMobile && Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
        }
        insertDrawingToDoc("popout-window");
        return true;
      },
    });    

    this.addCommand({
      id: "run-ocr",
      name: t("RUN_OCR"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (checking) {
          return (
            Boolean(view)
          );
        }
        if (view) {
          if(!this.settings.taskboneEnabled) {
            new Notice("Taskbone OCR is not enabled. Please go to plugins settings to enable it.",4000);
            return true;
          }
          this.taskbone.getTextForView(view, {forceReScan: false});
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "rerun-ocr",
      name: t("RERUN_OCR"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (checking) {
          return (
            Boolean(view)
          );
        }
        if (view) {
          if(!this.settings.taskboneEnabled) {
            new Notice("Taskbone OCR is not enabled. Please go to plugins settings to enable it.",4000);
            return true;
          }
          this.taskbone.getTextForView(view, {forceReScan: true});
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "run-ocr-selectedelements",
      name: t("RUN_OCR_ELEMENTS"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (checking) {
          return (
            Boolean(view)
          );
        }
        if (view) {
          if(!this.settings.taskboneEnabled) {
            new Notice("Taskbone OCR is not enabled. Please go to plugins settings to enable it.",4000);
            return true;
          }
          this.taskbone.getTextForView(view, {forceReScan: false, selectedElementsOnly: true, addToFrontmatter: false});
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "search-text",
      name: t("SEARCH"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          search(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "fullscreen",
      name: t("TOGGLE_FULLSCREEN"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          if (view.isFullscreen()) {
            view.exitFullscreen();
          } else {
            view.gotoFullscreen();
          }
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "disable-binding",
      name: t("TOGGLE_DISABLEBINDING"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.toggleDisableBinding();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "disable-framerendering",
      name: t("TOGGLE_FRAME_RENDERING"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.toggleFrameRendering();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "frame-settings",
      name: t("FRAME_SETTINGS_TITLE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          showFrameSettings(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "copy-link-to-drawing",
      name: t("COPY_DRAWING_LINK"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          navigator.clipboard.writeText(`![[${view.file.path}]]`);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "disable-frameclipping",
      name: t("TOGGLE_FRAME_CLIPPING"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.toggleFrameClipping();
          return true;
        }
        return false;
      },
    });


    this.addCommand({
      id: "export-image",
      name: t("EXPORT_IMAGE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          if(!view.exportDialog) {
            view.exportDialog = new ExportDialog(this.plugin, view,view.file);
          }
          view.exportDialog.open();
          return true;
        }
        return false;
      },
    });

    this.forceSaveCommand = this.addCommand({
      id: "save",
      hotkeys: [{modifiers: ["Ctrl"], key:"s"}], //See also Poposcope
      name: t("FORCE_SAVE"),
      checkCallback: (checking:boolean) => this.plugin.forceSaveActiveView(checking),
    })

    //removing raw mode. Not required. I never use it. Raw mode can still be enabled
    //via document properties. Only showing command palette action if raw mode is enabled
    this.addCommand({
      id: "toggle-lock",
      name: t("TOGGLE_LOCK"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (checking) {
          if (view) {
            return !view.compatibilityMode && view.textMode === TextMode.raw;
          }
          return false;
        }
        if (view && !view.compatibilityMode) {
          view.changeTextMode(
            view.textMode === TextMode.parsed ? TextMode.raw : TextMode.parsed,
          );
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "scriptengine-store",
      name: t("INSTALL_SCRIPT_BUTTON"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          );
        }
        new ScriptInstallPrompt(this.plugin).open();
        return true;
      },
    });

    this.addCommand({
      id: "delete-file",
      name: t("DELETE_FILE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.ea.reset();
          this.ea.setView(view);
          const el = this.ea.getViewSelectedElement();
          if (el.type !== "image") {
            new Notice(
              "Please select an image or embedded markdown document",
              4000,
            );
            return true;
          }
          const file = this.ea.getViewFileForImageElement(el);
          if (!file) {
            new Notice(
              "Please select an image or embedded markdown document",
              4000,
            );
            return true;
          }
          this.app.vault.delete(file);
          this.ea.deleteViewElements([el]);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "convert-text2MD",
      name: t("CONVERT_TO_MARKDOWN"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView)
        if(!view) return false;
        const selectedTextElements = view.getViewSelectedElements().filter(el=>el.type === "text");
        if(selectedTextElements.length !==1 ) return false;
        const selectedTextElement = selectedTextElements[0] as ExcalidrawTextElement;
        const containerElement = (view.getViewElements() as ExcalidrawElement[]).find(el=>el.id === selectedTextElement.containerId);
        if(containerElement && containerElement.type === "arrow") return false;
        if(checking) return true;
        view.convertTextElementToMarkdown(selectedTextElement, containerElement);
      }
    })

    this.addCommand({
      id: "insert-link",
      hotkeys: [{ modifiers: ["Mod", "Shift"], key: "k" }],
      name: t("INSERT_LINK"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.insertLinkDialog.start(view.file.path, (markdownlink: string, path:string, alias:string) => view.addLink(markdownlink, path, alias));
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-command",
      name: t("INSERT_COMMAND"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.insertCommandDialog.start((text: string, fontFamily?: 1 | 2 | 3 | 4, save?: boolean) => view.addText(text, fontFamily, save));
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-link-to-element",
      name: t("INSERT_LINK_TO_ELEMENT_NORMAL"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.copyLinkToSelectedElementToClipboard("");
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-link-to-element-group",
      name: t("INSERT_LINK_TO_ELEMENT_GROUP"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.copyLinkToSelectedElementToClipboard("group=");
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-link-to-element-frame",
      name: t("INSERT_LINK_TO_ELEMENT_FRAME"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.copyLinkToSelectedElementToClipboard("frame=");
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-link-to-element-frame-clipped",
      name: t("INSERT_LINK_TO_ELEMENT_FRAME_CLIPPED"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.copyLinkToSelectedElementToClipboard("clippedframe=");
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-link-to-element-area",
      name: t("INSERT_LINK_TO_ELEMENT_AREA"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.copyLinkToSelectedElementToClipboard("area=");
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "toggle-lefthanded-mode",
      name: t("TOGGLE_LEFTHANDED_MODE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          if(this.app.workspace.getActiveViewOfType(ExcalidrawView)) {
            const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
            const api = view?.excalidrawAPI;
            if(!api) return false;
            const st = api.getAppState();
            if(!st.trayModeEnabled) return false;
            return true;
          }
          return false;
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        (async()=>{
          const isLeftHanded = this.settings.isLeftHanded;
          await this.plugin.loadSettings({applyLefthandedMode: false});
          this.settings.isLeftHanded = !isLeftHanded;
          this.plugin.saveSettings();
          //not clear why I need to do this. If I don't double apply the stylesheet changes 
          //then the style won't be applied in the popout windows
          setLeftHandedMode(!isLeftHanded);
          setTimeout(()=>setLeftHandedMode(!isLeftHanded));
        })()
        return true;
      },
    });

    this.addCommand({
      id: "flip-image",
      name: t("FLIP_IMAGE"),
      checkCallback: (checking:boolean) => {
        if (!DEVICE.isDesktop) return;
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view
          .getViewSelectedElements()
          .filter(el=>{
            if(el.type==="image") {
              const ef = view.excalidrawData.getFile(el.fileId);
              if(!ef) {
                return false;
              }
              return this.isExcalidrawFile(ef.file);
            }
            return false;
          });
        if(els.length !== 1) {
          return false;
        }
        if(checking) return true;
        const el = els[0] as ExcalidrawImageElement;
        let ef = view.excalidrawData.getFile(el.fileId);
        this.plugin.forceToOpenInMarkdownFilepath = ef.file?.path;
        const appState = view.excalidrawAPI.getAppState();
        const {x:centerX,y:centerY} = sceneCoordsToViewportCoords({sceneX:el.x+el.width/2,sceneY:el.y+el.height/2},appState);
        const {width, height} = {width:600, height:600};
        const {x,y} = {
          x:Math.max(0,centerX - width/2 + view.ownerWindow.screenX),
          y:Math.max(0,centerY - height/2 + view.ownerWindow.screenY),
        }
        const focusOnFileTab = this.settings.focusOnFileTab;
        //override focusOnFileTab for popout windows
        if(DEVICE.isDesktop) {
          this.settings.focusOnFileTab = false;
        }
        this.plugin.openDrawing(ef.file, DEVICE.isMobile ? "new-tab":"popout-window", true, undefined, false, {x,y,width,height});
        this.settings.focusOnFileTab = focusOnFileTab;
      }
    })

    this.addCommand({
      id: "duplicate-image",
      name: t("DUPLICATE_IMAGE"),
      checkCallback: (checking:boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el=>el.type==="image");
        if(els.length !== 1) {
          if(checking) return false;
          new Notice("Select a single image element and try again");
          return false;
        }
        const el = els[0] as ExcalidrawImageElement;
        const ef = view.excalidrawData.getFile(el.fileId);
        if(!ef?.file) return false;
        if(checking) return true;
        (async()=>{
          const ea = getEA(view) as ExcalidrawAutomate;
          const isAnchored = Boolean(el.customData?.isAnchored); 
          const imgId = await ea.addImage(el.x+el.width/5, el.y+el.height/5, ef.file,!isAnchored, isAnchored);
          const img = ea.getElement(imgId) as Mutable<ExcalidrawImageElement>;
          img.width = el.width;
          img.height = el.height;
          if(el.crop) img.crop = {...el.crop};
          const newFileId = fileid() as FileId;
          ea.imagesDict[newFileId] = ea.imagesDict[img.fileId];
          ea.imagesDict[newFileId].id = newFileId;
          delete ea.imagesDict[img.fileId]
          img.fileId = newFileId;
          await ea.addElementsToView(false, false, true);
          ea.selectElementsInView([imgId]);
          ea.destroy();
        })();
      }
    })

    this.addCommand({
      id: "reset-image-to-100",
      name: t("RESET_IMG_TO_100"),
      checkCallback: (checking:boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el=>el.type==="image");
        if(els.length !== 1) {
          if(checking) return false;
          new Notice("Select a single image element and try again");
          return false;
        }
        if(checking) return true;
        
        (async () => {
          const el = els[0] as ExcalidrawImageElement;
          let ef = view.excalidrawData.getFile(el.fileId);
          if(!ef) {
            await view.forceSave();
            let ef = view.excalidrawData.getFile(el.fileId);
            new Notice("Select a single image element and try again");
            return false;
          }

          const ea = new ExcalidrawAutomate(this.plugin,view);
          const size = await ea.getOriginalImageSize(el);
          if(size) {
            ea.copyViewElementsToEAforEditing(els);
            const eaEl = ea.getElement(el.id) as Mutable<ExcalidrawImageElement>;
            if(eaEl.crop) {
              eaEl.width = eaEl.crop.width;
              eaEl.height = eaEl.crop.height;
            } else {
              eaEl.width = size.width; eaEl.height = size.height;
            }
            await ea.addElementsToView(false,false,false);
          }
          ea.destroy();
        })()
      }
    })

    this.addCommand({
      id: "reset-image-ar",
      name: t("RESET_IMG_ASPECT_RATIO"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (!view) return false;
        if (!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el => el.type === "image");
        if (els.length !== 1) {
          if (checking) return false;
          new Notice("Select a single image element and try again");
          return false;
        }
        if (checking) return true;
    
        (async () => {
          const el = els[0] as ExcalidrawImageElement;
          let ef = view.excalidrawData.getFile(el.fileId);
          if (!ef) {
            await view.forceSave();
            let ef = view.excalidrawData.getFile(el.fileId);
            new Notice("Select a single image element and try again");
            return false;
          }
    
          const ea = new ExcalidrawAutomate(this.plugin, view);
          if (await ea.resetImageAspectRatio(el)) {
            await ea.addElementsToView(false, false, false);
          }
          ea.destroy();
        })();
      }
    });
    
    this.addCommand({
      id: "open-link-props",
      name: t("OPEN_LINK_PROPS"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (!view) return false;
        if (!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el => el.type === "image");
        if (els.length !== 1) {
          if (checking) return false;
          new Notice("Select a single image element and try again");
          return false;
        }
        if (checking) return true;

        const el = els[0] as ExcalidrawImageElement;
        let ef = view.excalidrawData.getFile(el.fileId);
        let eq = view.excalidrawData.getEquation(el.fileId);
        if (!ef && !eq) {
          view.forceSave();
          new Notice("Please try again.");
          return false;
        }

        if(ef) {
          view.openEmbeddedLinkEditor(el.id);
        }
        if(eq) {
          view.openLaTeXEditor(el.id);
        }
      }
    });

    this.addCommand({
      id: "convert-card-to-file",
      name: t("CONVERT_CARD_TO_FILE"),
      checkCallback: (checking:boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!view) return false;
        if(!view.excalidrawAPI) return false;
        const els = view.getViewSelectedElements().filter(el=>el.type==="embeddable");
        if(els.length !== 1) {
          if(checking) return false;
          new Notice("Select a single back-of-the-note card and try again");
          return false;
        }
        const embeddableData = view.getEmbeddableLeafElementById(els[0].id);
        const child = embeddableData?.node?.child;
        if(!child || (child.file !== view.file)) {
          if(checking) return false;
          new Notice("The selected embeddable is not a back-of-the-note card.");
          return false;
        }
        if(checking) return true;
        view.moveBackOfTheNoteCardToFile();
      }
    })

    this.addCommand({
      id: "insert-active-pdfpage",
      name: t("INSERT_ACTIVE_PDF_PAGE_AS_IMAGE"),
      checkCallback: (checking:boolean) => {
        const excalidrawView = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!excalidrawView) return false;
        const embeddables = excalidrawView.getViewSelectedElements().filter(el=>el.type==="embeddable");
        if(embeddables.length !== 1) {
          if(checking) return false;
          new Notice("Select a single PDF embeddable and try again");
          return false;
        }
        const isPDF = excalidrawView.getEmbeddableLeafElementById(embeddables[0].id)?.leaf?.view?.getViewType() === "pdf"
        if(!isPDF) return false;
        const page = getActivePDFPageNumberFromPDFView(excalidrawView.getEmbeddableLeafElementById(embeddables[0].id)?.leaf?.view);
        if(!page) return false;
        if(checking) return true;

        const embeddableEl = embeddables[0] as ExcalidrawEmbeddableElement;
        const ea = new ExcalidrawAutomate(this.plugin,excalidrawView);
        const view = excalidrawView.getEmbeddableLeafElementById(embeddableEl.id)?.leaf?.view;
        const pdfFile: TFile = view && (view instanceof FileView) ? view.file : undefined;
        (async () => {
          const imgID = await ea.addImage(embeddableEl.x + embeddableEl.width + 10, embeddableEl.y, `${pdfFile?.path}#page=${page}`, false, false);
          const imgEl = ea.getElement(imgID) as Mutable<ExcalidrawImageElement>;
          const imageAspectRatio = imgEl.width / imgEl.height;
          if(imageAspectRatio > 1) {
            imgEl.width = embeddableEl.width;
            imgEl.height = embeddableEl.width / imageAspectRatio;
          } else {
            imgEl.height = embeddableEl.height;
            imgEl.width = embeddableEl.height * imageAspectRatio;
          }
          ea.addElementsToView(false, true, true);
        })()
      }
    })

    this.addCommand({
      id: "crop-image",
      name: t("CROP_IMAGE"),
      checkCallback: (checking:boolean) => {
        const excalidrawView = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
        const canvasView:any = this.app.workspace.activeLeaf?.view;
        const isCanvas = canvasView && canvasView.getViewType() === "canvas";
        if(!excalidrawView && !markdownView && !isCanvas) return false;

        if(excalidrawView) {
          if(!excalidrawView.excalidrawAPI) return false;
          const embeddables = excalidrawView.getViewSelectedElements().filter(el=>el.type==="embeddable");
          const imageEls = excalidrawView.getViewSelectedElements().filter(el=>el.type==="image");
          const isPDF = (imageEls.length === 0 && embeddables.length === 1 && excalidrawView.getEmbeddableLeafElementById(embeddables[0].id)?.node?.child?.file?.extension === "pdf")
          const isImage = (imageEls.length === 1 && embeddables.length === 0)

          if(!isPDF && !isImage) {
            if(checking) return false;
            new Notice("Select a single image element or single PDF embeddable and try again");
            return false;
          }

          const page = isPDF ? getActivePDFPageNumberFromPDFView(excalidrawView.getEmbeddableLeafElementById(embeddables[0].id)?.node?.child) : undefined;
          if(isPDF && !page) {
            return false;
          }

          if(checking) return true;

          if(isPDF) {
            const embeddableEl = embeddables[0] as ExcalidrawEmbeddableElement;
            const ea = new ExcalidrawAutomate(this.plugin,excalidrawView);
            const view = excalidrawView.getEmbeddableLeafElementById(embeddableEl.id)?.node?.child;
            const pdfFile: TFile = view?.file;
            carveOutPDF(ea, embeddableEl, `${pdfFile?.path}#page=${page}`, pdfFile);
            return;
          }

          const imageEl = imageEls[0] as ExcalidrawImageElement;
          (async () => {
            let ef = excalidrawView.excalidrawData.getFile(imageEl.fileId);

            if(!ef) {
              await excalidrawView.save();
              await sleep(500);
              ef = excalidrawView.excalidrawData.getFile(imageEl.fileId);
              if(!ef) {             
                new Notice("Select a single image element and try again");
                return false;
              }
            }
            const ea = new ExcalidrawAutomate(this.plugin,excalidrawView);
            carveOutImage(ea, imageEl);
          })();
        }

        const carveout = async (isFile: boolean, sourceFile: TFile, imageFile: TFile, imageURL: string, replacer: Function, ref?: string) => {
          const ea = getEA() as ExcalidrawAutomate;
          const imageID = await ea.addImage(
            0, 0,
            isFile
              ? ((isFile && imageFile.extension === "pdf" && ref) ? `${imageFile.path}#${ref}` : imageFile)
              : imageURL,
            false, false
          );
          if(!imageID) {
            new Notice(`Can't load image\n\n${imageURL}`);
            return;
          }
          
          let fnBase = "";
          let imageLink = "";
          if(isFile) {
            fnBase = imageFile.basename;
            imageLink = ref
              ? `[[${imageFile.path}#${ref}]]`
              : `[[${imageFile.path}]]`;
          } else {
            imageLink = imageURL;
            const imagename = imageURL.match(/^.*\/([^?]*)\??.*$/)?.[1];
            fnBase = imagename.substring(0,imagename.lastIndexOf("."));
          }
          
          const {folderpath, filename} = await getCropFileNameAndFolder(this.plugin,sourceFile.path,fnBase)
          const newFile = await createImageCropperFile(ea,imageID,imageLink,folderpath,filename);
          ea.destroy();
          if(!newFile) return;
          const link = this.app.metadataCache.fileToLinktext(newFile,sourceFile.path, true);
          replacer(link, newFile);
        }

        if(isCanvas) {
          const selectedNodes:any = [];
          canvasView.canvas.nodes.forEach((node:any) => {
            if(node.nodeEl.hasClass("is-focused")) selectedNodes.push(node);
          })
          if(selectedNodes.length !== 1) return false;
          const node = selectedNodes[0];
          let extension = "";
          let isExcalidraw = false;
          if(node.file) {
            extension = node.file.extension;
            isExcalidraw = this.isExcalidrawFile(node.file);
          }
          if(node.url) {
            extension = getURLImageExtension(node.url);
          }
          const page = extension === "pdf" ? getActivePDFPageNumberFromPDFView(node?.child) : undefined;
          if(!page && !IMAGE_TYPES.contains(extension) && !isExcalidraw) return false;
          if(checking) return true;

          const replacer = (link:string, file: TFile) => {
            if(node.file) {
              (node.file.extension === "pdf")
                ? node.canvas.createFileNode({pos:{x:node.x + node.width + 10,y: node.y}, file})
                : node.setFile(file);
            }
            if(node.url) {
              node.canvas.createFileNode({pos:{x:node.x + 20,y: node.y+20}, file});
            }
          }
          carveout(Boolean(node.file), canvasView.file, node.file, node.url, replacer, page ? `page=${page}` : undefined);
        }

        if (markdownView) {
          const editor = markdownView.editor;
          const cursor = editor.getCursor();
          const line = editor.getLine(cursor.line);
          const parts = REGEX_LINK.getResList(line);
          if(parts.length === 0) return false;
          let imgpath = REGEX_LINK.getLink(parts[0]);
          const isWikilink = REGEX_LINK.isWikiLink(parts[0]);
          let alias = REGEX_LINK.getAliasOrLink(parts[0]);
          if(alias === imgpath) alias = null;
          imgpath = decodeURI(imgpath);
          const imagePathParts = imgpath.split("#");
          const hasRef = imagePathParts.length === 2;
          const imageFile = this.app.metadataCache.getFirstLinkpathDest(
            hasRef ? imagePathParts[0] : imgpath,
            markdownView.file.path
          );
          const isFile = (imageFile && imageFile instanceof TFile);
          const isExcalidraw = isFile ? this.isExcalidrawFile(imageFile) : false;
          let imagepath = isFile ? imageFile.path : "";
          let extension = isFile ? imageFile.extension : "";
          if(imgpath.match(/^https?|file/)) {
            imagepath = imgpath;
            extension = getURLImageExtension(imgpath);
          }
          if(imagepath === "") return false;
          if(extension !== "pdf" && !IMAGE_TYPES.contains(extension) && !isExcalidraw) return false;
          if(checking) return true;
          const ref = imagePathParts[1];
          const replacer = (link:string) => {
            const lineparts = line.split(parts[0].value[0]) 
            const pdfLink = isFile && ref 
              ? "\n" + getLink(this.plugin ,{
                  embed: false,
                  alias: alias ?? `${imageFile.basename}, ${ref.replace("="," ")}`,
                  path:`${imageFile.path}#${ref}`
                }, isWikilink) 
              : "";
            editor.setLine(cursor.line,lineparts[0] + getLink(this.plugin ,{embed: true, path:link, alias}, isWikilink) + pdfLink + lineparts[1]);
          }
          carveout(isFile, markdownView.file, imageFile, imagepath, replacer, ref);
        }
      }
    })

    this.addCommand({
      id: "annotate-image",
      name: t("ANNOTATE_IMAGE"),
      checkCallback: (checking:boolean) => {
        const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
        const canvasView:any = this.app.workspace.activeLeaf?.view;
        const isCanvas = canvasView && canvasView.getViewType() === "canvas";
        if(!markdownView && !isCanvas) return false;

        const carveout = async (isFile: boolean, sourceFile: TFile, imageFile: TFile, imageURL: string, replacer: Function, ref?: string) => {
          const ea = getEA() as ExcalidrawAutomate;
          const imageID = await ea.addImage(
            0, 0,
            isFile
              ? ((isFile && imageFile.extension === "pdf" && ref) ? `${imageFile.path}#${ref}` : imageFile)
              : imageURL,
            false, false
          );
          if(!imageID) {
            new Notice(`Can't load image\n\n${imageURL}`);
            ea.destroy();
            return;
          }
          const el = ea.getElement(imageID) as Mutable<ExcalidrawImageElement>;
          el.locked = true;
          const size = this.settings.annotatePreserveSize
            ? await getImageSize(ea.imagesDict[el.fileId].dataURL)
            : null;
          let fnBase = "";
          let imageLink = "";
          if(isFile) {
            fnBase = imageFile.basename;
            imageLink = ref
              ? `[[${imageFile.path}#${ref}]]`
              : `[[${imageFile.path}]]`;
          } else {
            imageLink = imageURL;
            const imagename = imageURL.match(/^.*\/([^?]*)\??.*$/)?.[1];
            fnBase = imagename.substring(0,imagename.lastIndexOf("."));
          }
          
          let template:TFile;
          const templates = getListOfTemplateFiles(this.plugin);
          if(templates) {
            template = await templatePromt(templates, this.app);
          }

          const {folderpath, filename} = await getAnnotationFileNameAndFolder(this.plugin,sourceFile.path,fnBase)
          const newPath = await ea.create ({
            templatePath: template?.path,
            filename,
            foldername: folderpath,
            onNewPane: true,
            frontmatterKeys: {
              ...this.settings.matchTheme ? {"excalidraw-export-dark": isObsidianThemeDark()} : {},
              ...(imageFile.extension === "pdf") ? {"cssclasses": "excalidraw-cropped-pdfpage"} : {},
            }
          });
          ea.destroy();

          //wait for file to be created/indexed by Obsidian
          let newFile = this.app.vault.getAbstractFileByPath(newPath);
          let counter = 0;
          while((!newFile || !this.isExcalidrawFile(newFile as TFile)) && counter < 50) {
            await sleep(100);
            newFile = this.app.vault.getAbstractFileByPath(newPath);
            counter++;
          }
          //console.log({counter, file});
          if(!newFile || !(newFile instanceof TFile)) {
            new Notice("File not found. NewExcalidraw Drawing is taking too long to create. Please try again.");
            return;
          }

          if(!newFile) return;
          const link = this.app.metadataCache.fileToLinktext(newFile,sourceFile.path, true);
          replacer(link, newFile, size ? `${size.width}` : null);
        }

        if(isCanvas) {
          const selectedNodes:any = [];
          canvasView.canvas.nodes.forEach((node:any) => {
            if(node.nodeEl.hasClass("is-focused")) selectedNodes.push(node);
          })
          if(selectedNodes.length !== 1) return false;
          const node = selectedNodes[0];
          let extension = "";
          let isExcalidraw = false;
          if(node.file) {
            extension = node.file.extension;
            isExcalidraw = this.isExcalidrawFile(node.file);
          }
          if(node.url) {
            extension = getURLImageExtension(node.url);
          }
          const page = extension === "pdf" ? getActivePDFPageNumberFromPDFView(node?.child) : undefined;
          if(!page && !IMAGE_TYPES.contains(extension) && !isExcalidraw) return false;
          if(checking) return true;

          const replacer = (link:string, file: TFile) => {
            if(node.file) {
              (node.file.extension === "pdf")
                ? node.canvas.createFileNode({pos:{x:node.x + node.width + 10,y: node.y}, file})
                : node.setFile(file);
            }
            if(node.url) {
              node.canvas.createFileNode({pos:{x:node.x + 20,y: node.y+20}, file});
            }
          }
          carveout(Boolean(node.file), canvasView.file, node.file, node.url, replacer, page ? `page=${page}` : undefined);
        }

        if (markdownView) {
          const editor = markdownView.editor;
          const cursor = editor.getCursor();
          const line = editor.getLine(cursor.line);
          const parts = REGEX_LINK.getResList(line);
          if(parts.length === 0) return false;
          let imgpath = REGEX_LINK.getLink(parts[0]);
          const isWikilink = REGEX_LINK.isWikiLink(parts[0]);
          let alias = REGEX_LINK.getAliasOrLink(parts[0]);
          if(alias === imgpath) alias = null;
          imgpath = decodeURI(imgpath);
          const imagePathParts = imgpath.split("#");
          const hasRef = imagePathParts.length === 2;
          const imageFile = this.app.metadataCache.getFirstLinkpathDest(
            hasRef ? imagePathParts[0] : imgpath,
            markdownView.file.path
          );
          const isFile = (imageFile && imageFile instanceof TFile);
          const isExcalidraw = isFile ? this.isExcalidrawFile(imageFile) : false;
          let imagepath = isFile ? imageFile.path : "";
          let extension = isFile ? imageFile.extension : "";
          if(imgpath.match(/^https?|file/)) {
            imagepath = imgpath;
            extension = getURLImageExtension(imgpath);
          }
          if(imagepath === "") return false;
          if(extension !== "pdf" && !IMAGE_TYPES.contains(extension) && !isExcalidraw) return false;
          if(checking) return true;
          const ref = imagePathParts[1];
          const replacer = (link:string, _:TFile, size:string) => {
            const lineparts = line.split(parts[0].value[0]) 
            const pdfLink = isFile && ref 
              ? "\n" + getLink(this.plugin ,{
                  embed: false,
                  alias: getAliasWithSize(alias ?? `${imageFile.basename}, ${ref.replace("="," ")}`,size),
                  path:`${imageFile.path}#${ref}`
                }, isWikilink) 
              : "";
            editor.setLine(
              cursor.line,
              lineparts[0] + getLink(this.plugin ,{embed: true, path:link, alias: getAliasWithSize(alias,size)}, isWikilink) + pdfLink + lineparts[1]
            );
          }
          carveout(isFile, markdownView.file, imageFile, imagepath, replacer, ref);
        }
      }
    })

    this.addCommand({
      id: "insert-image",
      name: t("INSERT_IMAGE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.insertImageDialog.start(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "import-svg",
      name: t("IMPORT_SVG"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.importSVGDialog.start(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "release-notes",
      name: t("READ_RELEASE_NOTES"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        new ReleaseNotes(this.app, this.plugin, PLUGIN_VERSION).open();
        return true;
      },
    });

    this.addCommand({
      id: "tray-mode",
      name: t("TRAY_MODE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
          if (!view || !view.excalidrawAPI) {
            return false;
          }
          const st = view.excalidrawAPI.getAppState();
          if (st.zenModeEnabled || st.viewModeEnabled) {
            return false;
          }
          return true;
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view && view.excalidrawAPI) {
          view.toggleTrayMode();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-md",
      name: t("INSERT_MD"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          this.insertMDDialog.start(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-pdf",
      name: t("INSERT_PDF"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          const insertPDFModal = new InsertPDFModal(this.plugin, view);
          insertPDFModal.open();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-pdf",
      name: t("INSERT_LAST_ACTIVE_PDF_PAGE_AS_IMAGE"),
      checkCallback: (checking: boolean) => {
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if(!Boolean(view)) return false;
        const PDFLink = this.plugin.getLastActivePDFPageLink(view.file);
        if(!PDFLink) return false;
        if(checking) return true;
        (async()=>{
          const ea = getEA(view) as ExcalidrawAutomate;
          const id = await insertImageToView(
            ea,
            view.currentPosition,
            PDFLink,
            undefined,
            undefined,
            true,
          );
          ea.selectElementsInView([id]);
          ea.destroy()
        })();
      },
    });

    this.addCommand({
      id: "universal-add-file",
      name: t("UNIVERSAL_ADD_FILE"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          const insertFileModal = new UniversalInsertFileModal(this.plugin, view);
          insertFileModal.open();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "universal-card",
      name: t("INSERT_CARD"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          view.insertBackOfTheNoteCard();
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "insert-LaTeX-symbol",
      name: t("INSERT_LATEX"),
      checkCallback: (checking: boolean) => {
        if (checking) {
          return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
        }
        const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
        if (view) {
          insertLaTeXToView(view);
          return true;
        }
        return false;
      },
    });

    this.addCommand({
      id: "toggle-excalidraw-view",
      name: t("TOGGLE_MODE"),
      checkCallback: (checking) => {
        const activeFile = this.app.workspace.getActiveFile();
        if (!activeFile) {
          return false;
        }
        const fileIsExcalidraw = this.isExcalidrawFile(activeFile);

        if (checking) {
          if (
            Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
          ) {
            return !(this.app.workspace.getActiveViewOfType(ExcalidrawView))
              .compatibilityMode;
          }
          return fileIsExcalidraw;
        }

        const excalidrawView = this.app.workspace.getActiveViewOfType(ExcalidrawView)
        if (excalidrawView) {
          excalidrawView.openAsMarkdown();
          return;
        }

        const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView)
        if (markdownView && fileIsExcalidraw) {
          (async()=>{
            await markdownView.save();
            const activeLeaf = markdownView.leaf;
            this.plugin.excalidrawFileModes[(activeLeaf as any).id || activeFile.path] =
              VIEW_TYPE_EXCALIDRAW;
            setExcalidrawView(activeLeaf);
          })()
          return;
        }
      },
    });

    this.addCommand({
      id: "convert-to-excalidraw",
      name: t("CONVERT_NOTE_TO_EXCALIDRAW"),
      checkCallback: (checking) => {
        const activeFile = this.app.workspace.getActiveFile();
        const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);

        if (!activeFile || !activeView) {
          return false;
        }

        if(this.isExcalidrawFile(activeFile)) {
          return false;
        }

        if(checking) {
          return true;
        }

        (async () => {
          await activeView.save();
          const template = await this.plugin.getBlankDrawing();
          const target = await this.app.vault.read(activeFile);
          const mergedTarget = mergeMarkdownFiles(template, target);
          await createOrOverwriteFile(this.app, activeFile.path, mergedTarget);
          setExcalidrawView(activeView.leaf);
        })();
      },
    });

    this.addCommand({
      id: "convert-excalidraw",
      name: t("CONVERT_EXCALIDRAW"),
      checkCallback: (checking) => {
        if (checking) {
          const files = this.app.vault
            .getFiles()
            .filter((f) => f.extension == "excalidraw");
          return files.length > 0;
        }
        this.plugin.convertExcalidrawToMD();
        return true;
      },
    });
  }
}