Compare commits

...

7 Commits

Author SHA1 Message Date
zsviczian
3ae890bd86 2.4.0-beta-8 2024-08-19 15:57:23 +02:00
zsviczian
65a4cd4ba5 Merge pull request #1939 from dmscode/master
Update zh-cn.ts 2.4.0-beta-7
2024-08-19 15:38:56 +02:00
dmscode
f63b473bc1 Update zh-cn.ts to the commit "fixed getTemplate, migrade gridSize, gridStep" 2024-08-19 09:42:37 +08:00
稻米鼠
859a5ba03a Merge branch 'zsviczian:master' into master 2024-08-19 09:37:07 +08:00
zsviczian
832b97b179 fixed getTemplate, migrade gridSize, gridStep, 2024-08-18 17:15:03 +02:00
dmscode
e98d688d36 Update zh-cn.ts 2.4.0-beta-7 2024-08-16 15:53:39 +08:00
zsviczian
39318337fe updated set stroke width script 2024-08-16 08:13:55 +02:00
20 changed files with 487 additions and 186 deletions

View File

@@ -19,10 +19,10 @@ if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.25")) {
// -------------------------------
const excalidrawTemplates = ea.getListOfTemplateFiles();
if(typeof window.ExcalidrawDeconstructElements === "undefined") {
window.ExcalidrawDeconstructElements = {
openDeconstructedImage: true,
templatePath: excalidrawTemplates?.[0].path??""
};
window.ExcalidrawDeconstructElements = {
openDeconstructedImage: true,
templatePath: excalidrawTemplates?.[0].path??""
};
}
const splitFolderAndFilename = (filepath) => {
@@ -36,13 +36,13 @@ const splitFolderAndFilename = (filepath) => {
let settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Templates"]) {
settings = {
"Templates" : {
value: "",
settings = {
"Templates" : {
value: "",
description: "Comma-separated list of template filepaths"
}
};
await ea.setScriptSettings(settings);
}
};
await ea.setScriptSettings(settings);
}
if(!settings["Default file name"]) {
@@ -80,31 +80,31 @@ ea.getElements().filter(el=>el.type==="image").forEach(el=>{
const img = ea.targetView.excalidrawData.getFile(el.fileId);
const path = (img?.linkParts?.original)??(img?.file?.path);
if(img && path) {
ea.imagesDict[el.fileId] = {
mimeType: img.mimeType,
id: el.fileId,
dataURL: img.img,
created: img.mtime,
file: path,
hasSVGwithBitmap: img.isSVGwithBitmap,
latex: null,
};
return;
}
const equation = ea.targetView.excalidrawData.getEquation(el.fileId);
eqImg = ea.targetView.getScene()?.files[el.fileId]
if(equation && eqImg) {
ea.imagesDict[el.fileId] = {
mimeType: eqImg.mimeType,
id: el.fileId,
dataURL: eqImg.dataURL,
created: eqImg.created,
file: null,
hasSVGwithBitmap: null,
latex: equation.latex,
};
return;
}
mimeType: img.mimeType,
id: el.fileId,
dataURL: img.img,
created: img.mtime,
file: path,
hasSVGwithBitmap: img.isSVGwithBitmap,
latex: null,
};
return;
}
const equation = ea.targetView.excalidrawData.getEquation(el.fileId);
eqImg = ea.targetView.getScene()?.files[el.fileId]
if(equation && eqImg) {
ea.imagesDict[el.fileId] = {
mimeType: eqImg.mimeType,
id: el.fileId,
dataURL: eqImg.dataURL,
created: eqImg.created,
file: null,
hasSVGwithBitmap: null,
latex: equation.latex,
};
return;
}
});

View File

@@ -9,7 +9,11 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
let width = (ea.getViewSelectedElement().strokeWidth??1).toString();
width = await utils.inputPrompt("Width?","number",width);
width = parseFloat(await utils.inputPrompt("Width?","number",width));
if(isNaN(width)) {
new Notice("Invalid number");
return;
}
const elements=ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>el.strokeWidth=width);

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

After

Width:  |  Height:  |  Size: 861 KiB

View File

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

View File

@@ -19,7 +19,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.1-obsidian-40",
"@zsviczian/excalidraw": "0.17.1-obsidian-41",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"colormaster": "^1.2.1",

View File

@@ -11,7 +11,7 @@ import {
nanoid,
THEME_FILTER,
FRONTMATTER_KEYS,
getFontDefinition,
getCSSFontDefinition,
} from "./constants/constants";
import { createSVG } from "./ExcalidrawAutomate";
import { ExcalidrawData, getTransclusion } from "./ExcalidrawData";
@@ -836,29 +836,29 @@ export class EmbeddedFilesLoader {
}
switch (fontName) {
case "Virgil":
fontDef = await getFontDefinition(1);
fontDef = await getCSSFontDefinition(1);
break;
case "Cascadia":
fontDef = await getFontDefinition(3);
fontDef = await getCSSFontDefinition(3);
break;
case "Assistant":
case "Helvetica":
fontDef = await getFontDefinition(2);
fontDef = await getCSSFontDefinition(2);
break;
case "Excalifont":
fontDef = await getFontDefinition(5);
fontDef = await getCSSFontDefinition(5);
break;
case "Nunito":
fontDef = await getFontDefinition(6);
fontDef = await getCSSFontDefinition(6);
break;
case "Lilita One":
fontDef = await getFontDefinition(7);
fontDef = await getCSSFontDefinition(7);
break;
case "Comic Shanns":
fontDef = await getFontDefinition(8);
fontDef = await getCSSFontDefinition(8);
break;
case "Liberation Sans":
fontDef = await getFontDefinition(9);
fontDef = await getCSSFontDefinition(9);
break;
case "":
fontDef = "";
@@ -941,12 +941,14 @@ export class EmbeddedFilesLoader {
mdDIV.style.display = "block";
mdDIV.style.color = fontColor && fontColor !== "" ? fontColor : "initial";
await MarkdownRenderer.renderMarkdown(text, mdDIV, file.path, plugin);
//await MarkdownRenderer.renderMarkdown(text, mdDIV, file.path, plugin);
await MarkdownRenderer.render(this.plugin.app,text,mdDIV,file.path,this.plugin);
mdDIV
.querySelectorAll(":scope > *[class^='frontmatter']")
.forEach((el) => mdDIV.removeChild(el));
await replaceBlobWithBase64(mdDIV); //because image cache returns a blob
const internalEmbeds = Array.from(mdDIV.querySelectorAll("span[class='internal-embed']"))
for(let i=0;i<internalEmbeds.length;i++) {
const el = internalEmbeds[i];
@@ -1107,3 +1109,19 @@ export const generateIdFromFile = async (file: ArrayBuffer, key?: string): Promi
}
return id;
};
const replaceBlobWithBase64 = async (divElement: HTMLDivElement): Promise<void> => {
const images = divElement.querySelectorAll<HTMLImageElement>('img[src^="blob:app://obsidian.md"]');
for (let img of images) {
const blobUrl = img.src;
try {
const response = await fetch(blobUrl);
const blob = await response.blob();
const base64 = await blobToBase64(blob);
img.src = `data:${blob.type};base64,${base64}`;
} catch (error) {
console.error(`Failed to fetch or convert blob: ${blobUrl}`, error);
}
}
};

View File

@@ -717,6 +717,12 @@ export class ExcalidrawAutomate {
this.style.roundness ? "round":"sharp",
gridSize: template?.appState?.gridSize ?? this.canvas.gridSize,
colorPalette: template?.appState?.colorPalette ?? this.colorPalette,
...template?.appState?.frameRendering
? {frameRendering: template.appState.frameRendering}
: {},
...template?.appState?.objectsSnapModeEnabled
? {objectsSnapModeEnabled: template.appState.objectsSnapModeEnabled}
: {},
},
files: template?.files ?? {},
};
@@ -2560,10 +2566,11 @@ export class ExcalidrawAutomate {
};
/**
* Returns the size of the image element at 100% (i.e. the original size)
* Returns the size of the image element at 100% (i.e. the original size), or undefined if the data URL is not available
* @param imageElement an image element from the active scene on targetView
* @param shouldWaitForImage if true, the function will wait for the image to load before returning the size
*/
async getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{width: number; height: number}> {
async getOriginalImageSize(imageElement: ExcalidrawImageElement, shouldWaitForImage: boolean=false): Promise<{width: number; height: number}> {
//@ts-ignore
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "getOriginalImageSize()");
@@ -2579,10 +2586,59 @@ export class ExcalidrawAutomate {
return null;
}
const isDark = this.getExcalidrawAPI().getAppState().theme === "dark";
const dataURL = ef.getImage(isDark);
let dataURL = ef.getImage(isDark);
if(!dataURL && !shouldWaitForImage) return;
if(!dataURL) {
let watchdog = 0;
while(!dataURL && watchdog < 50) {
await sleep(100);
dataURL = ef.getImage(isDark);
watchdog++;
}
if(!dataURL) return;
}
return await getImageSize(dataURL);
}
/**
* Resets the image to its original aspect ratio.
* If the image is resized then the function returns true.
* If the image element is not in EA (only in the view), then if image is resized, the element is copied to EA for Editing using copyViewElementsToEAforEditing([imgEl]).
* Note you need to run await ea.addElementsToView(false); to add the modified image to the view.
* @param imageElement - the EA image element to be resized
* returns true if image was changed, false if image was not changed
*/
async resetImageAspectRatio(imgEl: ExcalidrawImageElement): Promise<boolean> {
//@ts-ignore
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "resetImageAspectRatio()");
return null;
}
const size = await this.getOriginalImageSize(imgEl, true);
if (size) {
const originalArea = imgEl.width * imgEl.height;
const originalAspectRatio = size.width / size.height;
let newWidth = Math.sqrt(originalArea * originalAspectRatio);
let newHeight = Math.sqrt(originalArea / originalAspectRatio);
const centerX = imgEl.x + imgEl.width / 2;
const centerY = imgEl.y + imgEl.height / 2;
if (newWidth !== imgEl.width || newHeight !== imgEl.height) {
if(!this.getElement(imgEl.id)) {
this.copyViewElementsToEAforEditing([imgEl]);
}
const eaEl = this.getElement(imgEl.id);
eaEl.width = newWidth;
eaEl.height = newHeight;
eaEl.x = centerX - newWidth / 2;
eaEl.y = centerY - newHeight / 2;
return true;
}
}
return false;
}
/**
* verifyMinimumPluginVersion returns true if plugin version is >= than required
* recommended use:
@@ -2953,6 +3009,7 @@ async function getTemplate(
}
excalidrawData.destroy();
const filehead = data.substring(0, trimLocation);
return {
elements: convertMarkdownLinksToObsidianURLs
? updateElementLinksToObsidianLinks({
@@ -2960,7 +3017,7 @@ async function getTemplate(
hostFile: file,
}) : groupElements,
appState: scene.appState,
frontmatter: data.substring(0, trimLocation),
frontmatter: filehead.match(/^---\n.*\n---\n/ms)?.[0] ?? filehead,
files: scene.files,
hasSVGwithBitmap,
};
@@ -3278,7 +3335,7 @@ export const search = async (view: ExcalidrawView) => {
const ea = view.plugin.ea;
ea.reset();
ea.setView(view);
const elements = ea.getViewElements().filter((el) => el.type === "text" || el.type === "frame");
const elements = ea.getViewElements().filter((el) => el.type === "text" || el.type === "frame" || el.link);
if (elements.length === 0) {
return;
}
@@ -3372,6 +3429,32 @@ export const getFrameElementsMatchingQuery = (
}));
}
/**
*
* @param elements
* @param query
* @param exactMatch - when searching for section header exactMatch should be set to true
* @returns the elements matching the query
*/
export const getElementsWithLinkMatchingQuery = (
elements: ExcalidrawElement[],
query: string[],
exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
): ExcalidrawElement[] => {
if (!elements || elements.length === 0 || !query || query.length === 0) {
return [];
}
return elements.filter((el: any) =>
el.link &&
query.some((q) => {
const text = el.link.toLowerCase().trim();
return exactMatch
? (text === q.toLowerCase())
: text.match(q.toLowerCase());
}));
}
export const cloneElement = (el: ExcalidrawElement):any => {
const newEl = JSON.parse(JSON.stringify(el));
newEl.version = el.version + 1;

View File

@@ -68,6 +68,27 @@ export enum AutoexportPreference {
inherit
}
export const REGEX_TAGS = {
// #[\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+
// 1
EXPR: /(#[\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/gu,
getResList: (text: string): IteratorResult<RegExpMatchArray, any>[] => {
const res = text.matchAll(REGEX_TAGS.EXPR);
let parts: IteratorResult<RegExpMatchArray, any>;
const resultList = [];
while (!(parts = res.next()).done) {
resultList.push(parts);
}
return resultList;
},
getTag: (parts: IteratorResult<RegExpMatchArray, any>): string => {
return parts.value[1];
},
isTag: (parts: IteratorResult<RegExpMatchArray, any>): boolean => {
return parts.value[1]?.startsWith("#")
},
};
export const REGEX_LINK = {
//![[link|alias]] [alias](link){num}
// 1 2 3 4 5 67 8 9
@@ -720,6 +741,24 @@ export class ExcalidrawData {
this.scene.appState.theme = isObsidianThemeDark() ? "dark" : "light";
}
//girdSize, gridStep, previousGridSize, gridModeEnabled migration
if(this.scene.appState.hasOwnProperty("previousGridSize")) { //if previousGridSize was present this is legacy data
if(this.scene.appState.gridSize === null) {
this.scene.appState.gridSize = this.scene.appState.previousGridSize;
this.scene.appState.gridModeEnabled = false;
} else {
this.scene.appState.gridModeEnabled = true;
}
delete this.scene.appState.previousGridSize;
}
if(this.scene.appState?.gridColor?.hasOwnProperty("MajorGridFrequency")) { //if this is present, this is legacy data
if(this.scene.appState.gridColor.MajorGridFrequency>1) {
this.scene.gridStep = this.scene.appState.gridColor.MajorGridFrequency;
}
delete this.scene.appState.gridColor.MajorGridFrequency;
}
//once off migration of legacy scenes
if(this.scene?.elements?.some((el:any)=>el.type==="iframe" && !el.customData)) {
const prompt = new ConfirmationPrompt(
@@ -810,7 +849,7 @@ export class ExcalidrawData {
? data.substring(indexOfNewElementLinks + lengthOfNewElementLinks)
: data.substring(indexOfOldElementLinks + lengthOfOldElementLinks);
//Load Embedded files
const RE_ELEMENT_LINKS = /^(.{8}):\s*(\[\[[^\]]*]])$/gm;
const RE_ELEMENT_LINKS = /^(.{8}):\s*(.*)$/gm;
const linksRes = elementLinksData.matchAll(RE_ELEMENT_LINKS);
while (!(parts = linksRes.next()).done) {
elementLinkMap.set(parts.value[1], parts.value[2]);
@@ -1043,7 +1082,7 @@ export class ExcalidrawData {
return (
el.type !== "text" &&
el.link &&
el.link.startsWith("[[") &&
//el.link.startsWith("[[") &&
!this.elementLinks.has(el.id)
);
});
@@ -1134,8 +1173,8 @@ export class ExcalidrawData {
(el: any) =>
el.type !== "text" &&
el.id === key &&
el.link &&
el.link.startsWith("[["),
el.link, //&&
//el.link.startsWith("[["),
);
if (el.length === 0) {
this.elementLinks.delete(key); //if no longer in the scene, delete the text element
@@ -1376,10 +1415,10 @@ export class ExcalidrawData {
const element = this.scene.elements.filter((el:any)=>el.id===key);
let elementString = this.textElements.get(key).raw;
if(element && element.length===1 && element[0].link && element[0].rawText === element[0].originalText) {
if(element[0].link.match(/^\[\[[^\]]*]]$/g)) { //apply this only to markdown links
//if(element[0].link.match(/^\[\[[^\]]*]]$/g)) { //apply this only to markdown links
textElementLinks.set(key, element[0].link);
//elementString = `%%***>>>text element-link:${element[0].link}<<<***%%` + elementString;
}
//}
}
outString += `${elementString} ^${key}\n\n`;
}

View File

@@ -175,6 +175,6 @@ declare namespace ExcalidrawLib {
function registerLocalFont(fontMetrics: FontMetadata, uri: string): void;
function getFontFamilies(): string[];
function registerFontsInCSS(): Promise<void>;
function getFontDefinition(fontFamily: number): Promise<string>;
function getCSSFontDefinition(fontFamily: number): Promise<string>;
}

View File

@@ -60,7 +60,8 @@ import {
ExcalidrawAutomate,
getTextElementsMatchingQuery,
cloneElement,
getFrameElementsMatchingQuery
getFrameElementsMatchingQuery,
getElementsWithLinkMatchingQuery
} from "./ExcalidrawAutomate";
import { t } from "./lang/helpers";
import {
@@ -126,7 +127,7 @@ import { anyModifierKeysPressed, emulateKeysForLinkClick, webbrowserDragModifier
import { setDynamicStyle } from "./utils/DynamicStyling";
import { InsertPDFModal } from "./dialogs/InsertPDFModal";
import { CustomEmbeddable, renderWebView } from "./customEmbeddable";
import { addBackOfTheNoteCard, getExcalidrawFileForwardLinks, getFrameBasedOnFrameNameOrId, getLinkTextFromLink, insertEmbeddableToView, insertImageToView, isTextImageTransclusion, openExternalLink, openTagSearch, parseObsidianLink, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils";
import { addBackOfTheNoteCard, getExcalidrawFileForwardLinks, getFrameBasedOnFrameNameOrId, getLinkTextFromLink, insertEmbeddableToView, insertImageToView, isTextImageTransclusion, openExternalLink, parseObsidianLink, renderContextMenuAction, tmpBruteForceCleanup } from "./utils/ExcalidrawViewUtils";
import { imageCache } from "./utils/ImageCache";
import { CanvasNodeFactory, ObsidianCanvasNode } from "./utils/CanvasNodeFactory";
import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu";
@@ -900,6 +901,90 @@ export default class ExcalidrawView extends TextFileView {
}
}
async openLaTeXEditor(eqId: string) {
const el = this.getViewElements().find((el:ExcalidrawElement)=>el.id === eqId && el.type==="image") as ExcalidrawImageElement;
if(!el) {
return;
}
const fileId = el.fileId;
let equation = this.excalidrawData.getEquation(fileId)?.latex;
if(!equation) {
await this.save(false);
equation = this.excalidrawData.getEquation(fileId)?.latex;
if(!equation) return;
}
GenericInputPrompt.Prompt(this,this.plugin,this.app,t("ENTER_LATEX"),undefined,equation, undefined, 3).then(async (formula: string) => {
if (!formula || formula === equation) {
return;
}
this.excalidrawData.setEquation(fileId, {
latex: formula,
isLoaded: false,
});
await this.save(false);
await updateEquation(
formula,
fileId,
this,
addFiles,
);
this.setDirty(1);
});
}
async openEmbeddedLinkEditor(imgId:string) {
const el = this.getViewElements().find((el:ExcalidrawElement)=>el.id === imgId && el.type==="image") as ExcalidrawImageElement;
if(!el) {
return;
}
const fileId = el.fileId;
const ef = this.excalidrawData.getFile(fileId);
if(!ef) {
return
}
if (!ef.isHyperLink && !ef.isLocalLink && ef.file) {
const handler = async (link:string) => {
if (!link || ef.linkParts.original === link) {
return;
}
ef.resetImage(this.file.path, link);
this.excalidrawData.setFile(fileId, ef);
this.setDirty(2);
await this.save(false);
await sleep(100);
if(!this.plugin.isExcalidrawFile(ef.file) && !link.endsWith("|100%")) {
const ea = getEA(this) as ExcalidrawAutomate;
let imgEl = this.getViewElements().find((x:ExcalidrawElement)=>x.id === el.id) as ExcalidrawImageElement;
if(!imgEl) {
ea.destroy();
return;
}
if(imgEl && await ea.resetImageAspectRatio(imgEl)) {
await ea.addElementsToView(false);
}
ea.destroy();
}
}
GenericInputPrompt.Prompt(
this,
this.plugin,
this.app,
t("MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT_TITLE"),
undefined,
ef.linkParts.original,
[{caption: "✅", action: (x:string)=>{x.replaceAll("\n","").trim()}}],
3,
false,
(container) => container.createEl("p",{text: fragWithHTML(t("MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT"))}),
false
).then(handler.bind(this),()=>{});
return;
}
}
toggleDisableBinding() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleDisableBinding, "ExcalidrawView.toggleDisableBinding");
const newState = !this.excalidrawAPI.getAppState().invertBindingBehaviour;
@@ -1042,6 +1127,10 @@ export default class ExcalidrawView extends TextFileView {
? this.excalidrawData.getRawText(selectedText.id)
: selectedText.text);
if(linkText.startsWith("#")) {
return {linkText, selectedElement: selectedTextElement ?? selectedElement};
}
const maybeObsidianLink = parseObsidianLink(linkText, this.app);
if(typeof maybeObsidianLink === "string") {
linkText = maybeObsidianLink;
@@ -1054,6 +1143,15 @@ export default class ExcalidrawView extends TextFileView {
const container = _getContainerElement(selectedTextElement, {elements: this.excalidrawAPI.getSceneElements()});
if(container) {
linkText = container.link;
if(linkText.startsWith("#")) {
return {linkText, selectedElement: selectedTextElement ?? selectedElement};
}
const maybeObsidianLink = parseObsidianLink(linkText, this.app);
if(typeof maybeObsidianLink === "string") {
linkText = maybeObsidianLink;
}
}
}
if(!linkText || partsArray.length === 0) {
@@ -1108,30 +1206,8 @@ export default class ExcalidrawView extends TextFileView {
if (selectedImage?.id) {
const imageElement = this.getScene().elements.find((el:ExcalidrawElement)=>el.id === selectedImage.id) as ExcalidrawImageElement;
if (this.excalidrawData.hasEquation(selectedImage.fileId)) {
(async () => {
await this.save(false);
selectedImage.fileId = imageElement.fileId;
const equation = this.excalidrawData.getEquation(
selectedImage.fileId,
).latex;
GenericInputPrompt.Prompt(this,this.plugin,this.app,t("ENTER_LATEX"),undefined,equation, undefined, 3).then(async (formula: string) => {
if (!formula || formula === equation) {
return;
}
this.excalidrawData.setEquation(selectedImage.fileId, {
latex: formula,
isLoaded: false,
});
await this.save(false);
await updateEquation(
formula,
selectedImage.fileId,
this,
addFiles,
);
this.setDirty(1);
});
})();
this.updateScene({appState: {contextMenu: null}});
this.openLaTeXEditor(selectedImage.id);
return;
}
if (this.excalidrawData.hasMermaid(selectedImage.fileId) || getMermaidText(imageElement)) {
@@ -1144,38 +1220,13 @@ export default class ExcalidrawView extends TextFileView {
await this.save(false); //in case pasted images haven't been saved yet
if (this.excalidrawData.hasFile(selectedImage.fileId)) {
const ef = this.excalidrawData.getFile(selectedImage.fileId);
if (!ef.isHyperLink && !ef.isLocalLink && linkClickType === "md-properties") {
if (
ef.file.extension === "md" &&
!this.plugin.isExcalidrawFile(ef.file)
) {
const handler = async (link:string) => {
if (!link || ef.linkParts.original === link) {
return;
}
ef.resetImage(this.file.path, link);
this.setDirty(2);
await this.save(false);
await this.loadSceneFiles();
}
GenericInputPrompt.Prompt(
this,
this.plugin,
this.app,
t("MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT_TITLE"),
undefined,
ef.linkParts.original,
[{caption: "✅", action: handler}],
1,
false,
(container) => container.createEl("p",{text: fragWithHTML(t("MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT"))}),
false
).then(handler, () => {});
return;
}
const fileId = selectedImage.fileId;
const ef = this.excalidrawData.getFile(fileId);
if (!ef.isHyperLink && !ef.isLocalLink && ef.file && linkClickType === "md-properties") {
this.updateScene({appState: {contextMenu: null}});
this.openEmbeddedLinkEditor(selectedImage.id);
return;
}
let secondOrderLinks: string = " ";
const backlinks = this.app.metadataCache?.getBacklinksForFile(ef.file)?.data;
@@ -1928,7 +1979,7 @@ export default class ExcalidrawView extends TextFileView {
state.match &&
state.match.content &&
state.match.matches &&
state.match.matches.length === 1 &&
state.match.matches.length >= 1 &&
state.match.matches[0].length === 2
) {
query = [
@@ -2023,7 +2074,7 @@ export default class ExcalidrawView extends TextFileView {
)) {
const cleanQuery = cleanSectionHeading(query[0]);
const sections = await this.getBackOfTheNoteSections();
if(sections.includes(cleanQuery)) {
if(sections.includes(cleanQuery) || this.data.includes(query[0])) {
this.setMarkdownView(state);
return;
}
@@ -2213,13 +2264,13 @@ export default class ExcalidrawView extends TextFileView {
});
}
private getGridColor(bgColor: string, st: AppState):{Bold: string, Regular: string, MajorGridFrequency: number} {
private getGridColor(bgColor: string, st: AppState):{Bold: string, Regular: string} {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.getGridColor, "ExcalidrawView.getGridColor", bgColor, st);
const cm = this.plugin.ea.getCM(bgColor);
const isDark = cm.isDark();
const Regular = (isDark ? cm.lighterBy(7) : cm.darkerBy(7)).stringHEX({alpha: false});
const Bold = (isDark ? cm.lighterBy(14) : cm.darkerBy(14)).stringHEX({alpha: false});
return {Bold, Regular, MajorGridFrequency:st.gridColor.MajorGridFrequency};
return {Bold, Regular};
}
public activeLoader: EmbeddedFilesLoader = null;
@@ -3230,6 +3281,7 @@ export default class ExcalidrawView extends TextFileView {
version: 2,
source: GITHUB_RELEASES+PLUGIN_VERSION,
elements: el,
//see also ExcalidrawAutomate async create(
appState: {
theme: st.theme,
viewBackgroundColor: st.viewBackgroundColor,
@@ -3250,10 +3302,11 @@ export default class ExcalidrawView extends TextFileView {
zoom: st.zoom,
currentItemRoundness: st.currentItemRoundness,
gridSize: st.gridSize,
gridStep: st.gridStep,
gridModeEnabled: st.gridModeEnabled,
gridColor: st.gridColor,
colorPalette: st.colorPalette,
currentStrokeOptions: st.currentStrokeOptions,
previousGridSize: st.previousGridSize,
frameRendering: st.frameRendering,
objectsSnapModeEnabled: st.objectsSnapModeEnabled,
},
@@ -5645,10 +5698,14 @@ export default class ExcalidrawView extends TextFileView {
elements.filter((el: ExcalidrawElement) => el.type === "frame"),
query,
exactMatch
)).concat(getElementsWithLinkMatchingQuery(
elements.filter((el: ExcalidrawElement) => el.link),
query,
exactMatch
));
if (match.length === 0) {
new Notice("I could not find a matching text element");
new Notice(t("NO_SEARCH_RESULT"));
return false;
}
@@ -5673,7 +5730,7 @@ export default class ExcalidrawView extends TextFileView {
const zoomLevel = this.plugin.settings.zoomToFitMaxLevel;
if (selectResult) {
api.selectElements(elements);
api.selectElements(elements, true);
}
api.zoomToFit(elements, zoomLevel, 0.05);
}

View File

@@ -97,7 +97,7 @@ export const {
getFontFamilyString,
getContainerElement,
refreshTextDimensions,
getFontDefinition,
getCSSFontDefinition,
} = excalidrawLib;
export const FONTS_STYLE_ID = "excalidraw-custom-fonts";

View File

@@ -20,7 +20,7 @@ import { t } from "src/lang/helpers";
import { ExcalidrawElement, getEA } from "src";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import { MAX_IMAGE_SIZE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
import { REGEX_LINK } from "src/ExcalidrawData";
import { REGEX_LINK, REGEX_TAGS } from "src/ExcalidrawData";
import { ScriptEngine } from "src/Scripts";
import { openExternalLink, openTagSearch, parseObsidianLink } from "src/utils/ExcalidrawViewUtils";
@@ -211,15 +211,15 @@ export class GenericInputPrompt extends Modal {
}, 30);
}
textComponent.inputEl.addEventListener("keydown", this.keyDownCallback);
textComponent.inputEl.addEventListener('keyup', checkcaret); // Every character written
textComponent.inputEl.addEventListener('pointerup', checkcaret); // Click down
textComponent.inputEl.addEventListener('touchend', checkcaret); // Click down
textComponent.inputEl.addEventListener('input', checkcaret); // Other input events
textComponent.inputEl.addEventListener('paste', checkcaret); // Clipboard actions
textComponent.inputEl.addEventListener('cut', checkcaret);
textComponent.inputEl.addEventListener('select', checkcaret); // Some browsers support this event
textComponent.inputEl.addEventListener('selectionchange', checkcaret);// Some browsers support this event
textComponent.inputEl.addEventListener("keydown", this.keyDownCallback.bind(this));
textComponent.inputEl.addEventListener('keyup', checkcaret.bind(this)); // Every character written
textComponent.inputEl.addEventListener('pointerup', checkcaret.bind(this)); // Click down
textComponent.inputEl.addEventListener('touchend', checkcaret.bind(this)); // Click down
textComponent.inputEl.addEventListener('input', checkcaret.bind(this)); // Other input events
textComponent.inputEl.addEventListener('paste', checkcaret.bind(this)); // Clipboard actions
textComponent.inputEl.addEventListener('cut', checkcaret.bind(this));
textComponent.inputEl.addEventListener('select', checkcaret.bind(this)); // Some browsers support this event
textComponent.inputEl.addEventListener('selectionchange', checkcaret.bind(this));// Some browsers support this event
return textComponent;
}
@@ -272,18 +272,18 @@ export class GenericInputPrompt extends Modal {
this.createButton(
actionButtonContainer,
"✅",
this.submitClickCallback,
this.submitClickCallback.bind(this),
).setCta().buttonEl.style.marginRight = "0";
}
this.createButton(actionButtonContainer, "❌", this.cancelClickCallback, t("PROMPT_BUTTON_CANCEL"));
this.createButton(actionButtonContainer, "❌", this.cancelClickCallback.bind(this), t("PROMPT_BUTTON_CANCEL"));
if(this.displayEditorButtons) {
this.createButton(editorButtonContainer, "⏎", ()=>this.insertStringBtnClickCallback("\n"), t("PROMPT_BUTTON_INSERT_LINE"), "0");
this.createButton(editorButtonContainer, "⌫", this.delBtnClickCallback, "Delete");
this.createButton(editorButtonContainer, "⌫", this.delBtnClickCallback.bind(this), "Delete");
this.createButton(editorButtonContainer, "⎵", ()=>this.insertStringBtnClickCallback(" "), t("PROMPT_BUTTON_INSERT_SPACE"));
if(this.view) {
this.createButton(editorButtonContainer, "🔗", this.linkBtnClickCallback, t("PROMPT_BUTTON_INSERT_LINK"));
this.createButton(editorButtonContainer, "🔗", this.linkBtnClickCallback.bind(this), t("PROMPT_BUTTON_INSERT_LINK"));
}
this.createButton(editorButtonContainer, "🔠", this.uppercaseBtnClickCallback, t("PROMPT_BUTTON_UPPERCASE"));
this.createButton(editorButtonContainer, "🔠", this.uppercaseBtnClickCallback.bind(this), t("PROMPT_BUTTON_UPPERCASE"));
}
}
@@ -342,8 +342,13 @@ export class GenericInputPrompt extends Modal {
this.inputComponent.inputEl.setSelectionRange(this.selectionStart, this.selectionEnd);
}
private submitClickCallback = () => this.submit();
private cancelClickCallback = () => this.cancel();
private submitClickCallback () {
this.submit();
}
private cancelClickCallback () {
this.cancel();
}
private keyDownCallback = (evt: KeyboardEvent) => {
if ((evt.key === "Enter" && this.lines === 1) || (isWinCTRLorMacCMD(evt) && evt.key === "Enter")) {
@@ -668,10 +673,10 @@ export class ConfirmationPrompt extends Modal {
buttonContainer.style.display = "flex";
buttonContainer.style.justifyContent = "flex-end";
const cancelButton = this.createButton(buttonContainer, t("PROMPT_BUTTON_CANCEL"), this.cancelClickCallback);
const cancelButton = this.createButton(buttonContainer, t("PROMPT_BUTTON_CANCEL"), this.cancelClickCallback.bind(this));
cancelButton.buttonEl.style.marginRight = "0.5rem";
const confirmButton = this.createButton(buttonContainer, t("PROMPT_BUTTON_OK"), this.confirmClickCallback);
const confirmButton = this.createButton(buttonContainer, t("PROMPT_BUTTON_OK"), this.confirmClickCallback.bind(this));
confirmButton.buttonEl.style.marginRight = "0";
cancelButton.buttonEl.focus();
@@ -683,12 +688,12 @@ export class ConfirmationPrompt extends Modal {
return button;
}
private cancelClickCallback = () => {
private cancelClickCallback() {
this.didConfirm = false;
this.close();
};
private confirmClickCallback = () => {
private confirmClickCallback() {
this.didConfirm = true;
this.close();
};
@@ -714,18 +719,28 @@ export async function linkPrompt (
view?: ExcalidrawView,
message: string = "Select link to open",
):Promise<[file:TFile, linkText:string, subpath: string]> {
const partsArray = REGEX_LINK.getResList(linkText);
const linksArray = REGEX_LINK.getResList(linkText);
const tagsArray = REGEX_TAGS.getResList(linkText);
let subpath: string = null;
let file: TFile = null;
let parts = partsArray[0];
if (partsArray.length > 1) {
let parts = linksArray[0] ?? tagsArray[0];
const itemsDisplay = [
...linksArray.filter(p=> Boolean(p.value)).map(p => {
const alias = REGEX_LINK.getAliasOrLink(p);
return alias === "100%" ? REGEX_LINK.getLink(p) : alias;
}),
...tagsArray.filter(x=> Boolean(x.value)).map(x => REGEX_TAGS.getTag(x)),
];
const items = [
...linksArray.filter(p=>Boolean(p.value)),
...tagsArray.filter(x=> Boolean(x.value)),
];
if (items.length>1) {
parts = await ScriptEngine.suggester(
app,
partsArray.filter(p=>Boolean(p.value)).map(p => {
const alias = REGEX_LINK.getAliasOrLink(p);
return alias === "100%" ? REGEX_LINK.getLink(p) : alias;
}),
partsArray.filter(p=>Boolean(p.value)),
itemsDisplay,
items,
message,
);
if(!parts) return;
@@ -735,8 +750,8 @@ export async function linkPrompt (
return;
}
if (!parts.value) {
openTagSearch(linkText, app);
if (REGEX_TAGS.isTag(parts)) {
openTagSearch(REGEX_TAGS.getTag(parts), app);
return;
}

View File

@@ -550,8 +550,19 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
},
{
field: "getOriginalImageSize",
code: "async getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{width: number; height: number}>",
desc: "Returns the size of the image element at 100% (i.e. the original size). This is an async function, you need to await the result.",
code: "async getOriginalImageSize(imageElement: ExcalidrawImageElement, shouldWaitForImage: boolean=false): Promise<{width: number; height: number}>",
desc: "Returns the size of the image element at 100% (i.e. the original size) or undefined if the data URL is not available.\n"+
"If shouldWaitForImage is true, the function will wait for the view to load the image before returning the size.\n"+
"This is an async function, you need to await the result.",
after: "",
},
{
field: "resetImageAspectRatio",
code: "async resetImageAspectRatio(imgEl: ExcalidrawImageElement): Promise<boolean>",
desc: "Resets the image to its original aspect ratio.\n" +
"If the image is resized then the function returns true.\n" +
"If the image element is not in EA (only in the view), then if the image is resized, the element is copied to EA for Editing using copyViewElementsToEAforEditing([imgEl]).\n" +
"Note you need to run await ea.addElementsToView(false); to add the modified image to the view.",
after: "",
},
{

View File

@@ -54,7 +54,7 @@ export default {
COPY_ELEMENT_LINK: "Copy [[link]] for selected element(s)",
COPY_DRAWING_LINK: "Copy ![[embed link]] for this drawing",
INSERT_LINK_TO_ELEMENT:
`Copy [[link]] for selected element to clipboard. ${labelCTRL()}+CLICK to copy 'group=' link. ${labelSHIFT()}+CLICK to copy an 'area=' link. ${labelALT()}+CLICK to watch a help video.`,
`Copy [[link]] for selected element to clipboard. ${labelCTRL()}+CLICK to copy 'group=' link. ${labelSHIFT()}+CLICK to copy an 'area=' link.`,
INSERT_LINK_TO_ELEMENT_GROUP:
"Copy 'group=' ![[link]] for selected element to clipboard.",
INSERT_LINK_TO_ELEMENT_AREA:
@@ -80,7 +80,7 @@ export default {
ERROR_TRY_AGAIN: "Please try again.",
PASTE_CODEBLOCK: "Paste code block",
INSERT_LATEX:
`Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!}). ${labelALT()}+CLICK to watch a help video.`,
`Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!}).`,
ENTER_LATEX: "Enter a valid LaTeX expression",
READ_RELEASE_NOTES: "Read latest release notes",
RUN_OCR: "OCR full drawing: Grab text from freedraw + images to clipboard and doc.props",
@@ -93,14 +93,20 @@ export default {
ANNOTATE_IMAGE : "Annotate image in Excalidraw",
INSERT_ACTIVE_PDF_PAGE_AS_IMAGE: "Insert active PDF page as image",
RESET_IMG_TO_100: "Set selected image element size to 100% of original",
RESET_IMG_ASPECT_RATIO: "Reset selected image element aspect ratio",
TEMPORARY_DISABLE_AUTOSAVE: "Disable autosave until next time Obsidian starts (only set this if you know what you are doing)",
TEMPORARY_ENABLE_AUTOSAVE: "Enable autosave",
//ExcalidrawView.ts
NO_SEARCH_RESULT: "Didn't find a matching element in the drawing",
FORCE_SAVE_ABORTED: "Force Save aborted because saving is in progress",
LINKLIST_SECOND_ORDER_LINK: "Second Order Link",
MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT_TITLE: "Customize the link",
MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT: "Do not add [[square brackets]] around the filename!<br>Follow this format when editing your link:<br><mark>filename#^blockref|WIDTHxMAXHEIGHT</mark>",
MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT_TITLE: "Customize the Embedded File link",
MARKDOWN_EMBED_CUSTOMIZE_LINK_PROMPT: "Do not add [[square brackets]] around the filename!<br>" +
"For markdown-page images follow this format when editing your link: <mark>filename#^blockref|WIDTHxMAXHEIGHT</mark><br>" +
"You can anchor Excalidraw images to 100% of their size by adding <code>|100%</code> to the end of the link.<br>" +
"You can change the PDF page by changing <code>#page=1</code> to <code>#page=2</code> etc.<br>" +
"PDF rect crop values are: <code>left, bottom, right, top</code>. Eg.: <code>#rect=0,0,500,500</code><br>",
FRAME_CLIPPING_ENABLED: "Frame Rendering: Enabled",
FRAME_CLIPPING_DISABLED: "Frame Rendering: Disabled",
ARROW_BINDING_INVERSE_MODE: "Inverted Mode: Default arrow binding is now disabled. Use CTRL/CMD to temporarily enable binding when needed.",
@@ -773,7 +779,7 @@ FILENAME_HEAD: "Filename",
TOGGLE_FRAME_RENDERING: "Toggle frame rendering",
TOGGLE_FRAME_CLIPPING: "Toggle frame clipping",
OPEN_LINK_CLICK: "Open Link",
OPEN_LINK_PROPS: "Open markdown-embed properties or open link in new window",
OPEN_LINK_PROPS: "Open the image-link or LaTeX-formula editor",
//IFrameActionsMenu.tsx
NARROW_TO_HEADING: "Narrow to heading...",

View File

@@ -2,6 +2,7 @@ import {
DEVICE,
FRONTMATTER_KEYS,
} from "src/constants/constants";
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
// 简体中文
@@ -336,14 +337,14 @@ FILENAME_HEAD: "文件名",
SHOW_DRAWING_OR_MD_IN_READING_MODE_DESC:
"当您处于 Markdown 阅读模式即查看绘图的背景笔记Excalidraw 绘图是否应该渲染为图像?" +
"此设置不会影响您在 Excalidraw 模式下的绘图显示,或者在将绘图嵌入 Markdown 文档时,或在渲染悬停预览时。<br><ul>" +
"<li>请参阅下面‘嵌入和导出’部分的 <b>PDF 导出</b> 相关设置。</li></ul><br>" +
"<li>请参阅下面‘嵌入和导出’部分的 <a href='#"+TAG_PDFEXPORT+"'>PDF 导出</a> 相关设置。</li></ul><br>" +
"您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。",
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "在将 Excalidraw 文件导出为 PDF 时将文件渲染为图像",
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_DESC:
"处于 Markdown 视图模式时,此设置控制 Excalidraw 在使用 Obsidian 的 <b>导出为 PDF</b> 功能时,将 Excalidraw 文件导出为 PDF 的行为。<br>" +
"<ul><li>当 <b>启用</b> 时PDF 将仅显示 Excalidraw 绘图;</li>" +
"<li>当 <b>禁用</b> 时PDF 将显示文档的 Markdown 部分(背景笔记)。</li></ul>" +
"请参阅上面‘外观和行为’部分的 <b>Markdown 阅读模式</b> 相关设置。" +
"请参阅上面‘外观和行为’部分的 <<a href='#"+TAG_MDREADINGMODE+"'>>Markdown 阅读模式</a> 相关设置。" +
"⚠️ 注意,您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。⚠️",
THEME_HEAD: "主题和样式",
ZOOM_HEAD: "缩放",
@@ -531,7 +532,7 @@ FILENAME_HEAD: "文件名",
EMBED_REUSE_EXPORTED_IMAGE_NAME:
"将之前已导出的图像作为预览图",
EMBED_REUSE_EXPORTED_IMAGE_DESC:
"该选项与自动导出 SVG/PNG 副本选项配合使用。如果嵌入到 Markdown 文档中的绘图文件存在同名的 SVG/PNG 副本,则将其作为预览图,而不再重新生成。<br>" +
"该选项与<a href='#"+TAG_AUTOEXPORT+"'>自动导出 SVG/PNG 副本</a>选项配合使用。如果嵌入到 Markdown 文档中的绘图文件存在同名的 SVG/PNG 副本,则将其作为预览图,而不再重新生成。<br>" +
"该选项能够提高 Markdown 文档的打开速度,尤其是当嵌入到 Markdown 文档中的绘图文件中含有大量图像或 MD-Embed 时。" +
"但是,该选项也可能导致预览图无法立即响应你对绘图文件或者 Obsidian 主题风格的修改。<br>" +
"该选项仅作用于嵌入到 Markdown 文档中的绘图。" +
@@ -562,7 +563,7 @@ FILENAME_HEAD: "文件名",
EMBED_TYPE_NAME: "“嵌入绘图到当前 Markdown 文档中”系列命令的源文件类型",
EMBED_TYPE_DESC:
"在命令面板中执行“嵌入绘图到当前 Markdown 文档中”系列命令时,要嵌入绘图文件本身,还是嵌入其 PNG 或 SVG 副本。<br>" +
"如果您想选择 PNG 或 SVG 副本,需要先开启下方的“自动导出 PNG 副本”或“自动导出 SVG 副本。<br>" +
"如果您想选择 PNG 或 SVG 副本,需要先开启下方的<a href='#"+TAG_AUTOEXPORT+"'>自动导出 PNG / SVG 副本</a>。<br>" +
"如果您选择了 PNG 或 SVG 副本,当副本不存在时,该命令将会插入一条损坏的链接,您需要打开绘图文件并手动导出副本才能修复 —— " +
"也就是说,该选项不会自动帮您生成 PNG/SVG 副本,而只会引用已有的 PNG/SVG 副本。",
EMBED_MARKDOWN_COMMENT_NAME: "将链接作为注释嵌入",
@@ -772,7 +773,7 @@ FILENAME_HEAD: "文件名",
TOGGLE_FRAME_RENDERING: "开启或关闭框架渲染",
TOGGLE_FRAME_CLIPPING: "开启或关闭框架裁剪",
OPEN_LINK_CLICK: "打开所选的图形或文本元素里的链接",
OPEN_LINK_PROPS: "编辑所选 MD-Embed 的内部链接,或者打开所选的图形或文本元素里的链接",
OPEN_LINK_PROPS: "打开 markdown-embed 属性或 LaTeX 编辑器,或在新窗口中打开链接",
//IFrameActionsMenu.tsx
NARROW_TO_HEADING: "缩放至标题",

View File

@@ -1794,12 +1794,80 @@ export default class ExcalidrawPlugin extends Plugin {
const eaEl = ea.getElement(el.id);
//@ts-ignore
eaEl.width = size.width; eaEl.height = size.height;
ea.addElementsToView(false,false,false);
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, 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"),

View File

@@ -365,13 +365,10 @@ export const ICONS = {
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M4 22h14a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v7"/>
<polyline
points="14 2 14 8 20 8"
fill="var(--icon-fill-color)"
/>
<path d="m10 18 3-3-3-3"/>
<path d="M4 18v-1a2 2 0 0 1 2-2h6"/>
<path d="M10 12.5 8 15l2 2.5"/>
<path d="m14 12.5 2 2.5-2 2.5"/>
<path d="M14 2v4a2 2 0 0 0 2 2h4"/>
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/>
</svg>
),
//fa-brands fa-markdown

View File

@@ -2,7 +2,7 @@
import { MAX_IMAGE_SIZE, IMAGE_TYPES, ANIMATED_IMAGE_TYPES, MD_EX_SECTIONS } from "src/constants/constants";
import { App, Notice, TFile, WorkspaceLeaf } from "obsidian";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import { REGEX_LINK, REG_LINKINDEX_HYPERLINK, getExcalidrawMarkdownHeaderSection } from "src/ExcalidrawData";
import { REGEX_LINK, REG_LINKINDEX_HYPERLINK, getExcalidrawMarkdownHeaderSection, REGEX_TAGS } from "src/ExcalidrawData";
import ExcalidrawView from "src/ExcalidrawView";
import { ExcalidrawElement, ExcalidrawFrameElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { getLinkParts } from "./Utils";
@@ -72,24 +72,26 @@ export function getLinkTextFromLink (text: string): string {
return linktext;
}
export function openTagSearch (link:string, app: App, view?: ExcalidrawView) {
const tags = link
.matchAll(/#([\p{Letter}\p{Emoji_Presentation}\p{Number}\/_-]+)/gu)
.next();
if (!tags.value || tags.value.length < 2) {
export function openTagSearch(link: string, app: App, view?: ExcalidrawView) {
const tags = REGEX_TAGS.getResList(link);
if (!tags.length || !tags[0].value || tags[0].value.length < 2) {
return;
}
const search = app.workspace.getLeavesOfType("search");
if (search.length == 0) {
if (search.length === 0) {
return;
}
//@ts-ignore
search[0].view.setQuery(`tag:${tags.value[1]}`);
search[0].view.setQuery(`tag:${tags[0].value[1]}`);
app.workspace.revealLeaf(search[0]);
if (view && view.isFullscreen()) {
view.exitFullscreen();
}
return;
}

View File

@@ -409,4 +409,4 @@ export async function closeLeafView(leaf: WorkspaceLeaf) {
type: "empty",
state: {},
});
}
}