mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
12 Commits
1.8.3-beta
...
1.8.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90c377f125 | ||
|
|
df85138890 | ||
|
|
654b656a2f | ||
|
|
1d22dcb488 | ||
|
|
a42e907d0c | ||
|
|
353cad21d6 | ||
|
|
2b02f186a6 | ||
|
|
3e12d1e815 | ||
|
|
539bbdf07f | ||
|
|
b993b358fe | ||
|
|
0639ea5969 | ||
|
|
05b7bc6029 |
@@ -308,16 +308,8 @@ function loadELK(doAfterLoaded) {
|
||||
function normalizeSelectedArrows() {
|
||||
let gapValue = 2;
|
||||
|
||||
const selectedIndividualArrows = ea
|
||||
.getMaximumGroups(ea.getViewSelectedElements())
|
||||
.reduce(
|
||||
(result, group) =>
|
||||
group.length === 1 &&
|
||||
(group[0].type === "arrow" || group[0].type === "line")
|
||||
? [...result, group[0]]
|
||||
: result,
|
||||
[]
|
||||
);
|
||||
const selectedIndividualArrows = ea.getMaximumGroups(ea.getViewSelectedElements())
|
||||
.reduce((result, g) => [...result, ...g.filter(el => el.type === 'arrow')], []);
|
||||
|
||||
const allElements = ea.getViewElements();
|
||||
for (const arrow of selectedIndividualArrows) {
|
||||
|
||||
@@ -13,12 +13,49 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
|
||||
|
||||
```javascript
|
||||
*/
|
||||
const selectedCenterConnectPoints = await utils.suggester(
|
||||
['Yes', 'No'],
|
||||
[true, false],
|
||||
"Center connect points?"
|
||||
);
|
||||
const centerConnectPoints = selectedCenterConnectPoints??false;
|
||||
|
||||
const allElements = ea.getViewElements();
|
||||
const elements = ea.getViewSelectedElements();
|
||||
|
||||
const lines = elements.filter((el)=>el.type==="arrow" || el.type==="line");
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.points.length >= 3) {
|
||||
if(centerConnectPoints) {
|
||||
const startBindingEl = allElements.filter(el => el.id === (line.startBinding||{}).elementId)[0];
|
||||
const endBindingEl = allElements.filter(el => el.id === (line.endBinding||{}).elementId)[0];
|
||||
|
||||
if(startBindingEl) {
|
||||
const startPointX = line.x +line.points[0][0];
|
||||
if(startPointX >= startBindingEl.x && startPointX <= startBindingEl.x + startBindingEl.width) {
|
||||
line.points[0][0] = startBindingEl.x + startBindingEl.width / 2 - line.x;
|
||||
}
|
||||
|
||||
const startPointY = line.y +line.points[0][1];
|
||||
if(startPointY >= startBindingEl.y && startPointY <= startBindingEl.y + startBindingEl.height) {
|
||||
line.points[0][1] = startBindingEl.y + startBindingEl.height / 2 - line.y;
|
||||
}
|
||||
}
|
||||
|
||||
if(endBindingEl) {
|
||||
const startPointX = line.x +line.points[line.points.length-1][0];
|
||||
if(startPointX >= endBindingEl.x && startPointX <= endBindingEl.x + endBindingEl.width) {
|
||||
line.points[line.points.length-1][0] = endBindingEl.x + endBindingEl.width / 2 - line.x;
|
||||
}
|
||||
|
||||
const startPointY = line.y +line.points[line.points.length-1][1];
|
||||
if(startPointY >= endBindingEl.y && startPointY <= endBindingEl.y + endBindingEl.height) {
|
||||
line.points[line.points.length-1][1] = endBindingEl.y + endBindingEl.height / 2 - line.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < line.points.length - 2; i++) {
|
||||
var p1;
|
||||
var p3;
|
||||
|
||||
@@ -26,8 +26,7 @@ if(!settings["Gap"]) {
|
||||
let gapValue = settings["Gap"].value;
|
||||
|
||||
const selectedIndividualArrows = ea.getMaximumGroups(ea.getViewSelectedElements())
|
||||
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
|
||||
[...result, group[0]] : result, []);
|
||||
.reduce((result, g) => [...result, ...g.filter(el => el.type === 'arrow')], []);
|
||||
|
||||
const allElements = ea.getViewElements();
|
||||
for(const arrow of selectedIndividualArrows) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" stroke="#000" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g fill="none"><circle cx="12" cy="12" r="10"></circle><path d="M17 12h.01"></path><path d="M12 12h.01"></path><path d="M7 12h.01"></path></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" stroke="#000" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g stroke-width="2" fill="none"><circle cx="12" cy="12" r="10"></circle><path d="M17 12h.01"></path><path d="M12 12h.01"></path><path d="M7 12h.01"></path></g></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 327 B |
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.7.28-beta",
|
||||
"minAppVersion": "0.15.6",
|
||||
"version": "1.8.3-beta",
|
||||
"minAppVersion": "0.16.0",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
"authorUrl": "https://zsolt.blog",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.8.2",
|
||||
"minAppVersion": "0.16.0",
|
||||
"version": "1.8.5",
|
||||
"minAppVersion": "1.0.0",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
"authorUrl": "https://zsolt.blog",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lz-string": "^1.3.34",
|
||||
"@zsviczian/excalidraw": "0.13.0-obsidian-1",
|
||||
"@zsviczian/excalidraw": "0.13.0-obsidian-2",
|
||||
"clsx": "^1.1.1",
|
||||
"lz-string": "^1.4.4",
|
||||
"monkey-around": "^2.3.0",
|
||||
|
||||
@@ -2,13 +2,14 @@ import ExcalidrawPlugin from "./main";
|
||||
import {
|
||||
FillStyle,
|
||||
StrokeStyle,
|
||||
StrokeSharpness,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawBindableElement,
|
||||
FileId,
|
||||
NonDeletedExcalidrawElement,
|
||||
ExcalidrawImageElement,
|
||||
ExcalidrawTextElement,
|
||||
StrokeRoundness,
|
||||
RoundnessType,
|
||||
} from "@zsviczian/excalidraw/types/element/types";
|
||||
import { normalizePath, Notice, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import * as obsidian_module from "obsidian";
|
||||
@@ -61,6 +62,7 @@ import RYBPlugin from "colormaster/plugins/ryb";
|
||||
import CMYKPlugin from "colormaster/plugins/cmyk";
|
||||
import { TInput } from "colormaster/types";
|
||||
import {ConversionResult, svgToExcalidraw} from "./svgToExcalidraw/parser"
|
||||
import { ROUNDNESS } from "./Constants";
|
||||
|
||||
extendPlugins([
|
||||
HarmonyPlugin,
|
||||
@@ -118,7 +120,8 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
|
||||
strokeSharpness?: StrokeRoundness; //defaults to undefined, use strokeRoundess and roundess instead. Only kept for legacy script compatibility type StrokeRoundness = "round" | "sharp"
|
||||
roundness: null | { type: RoundnessType; value?: number };
|
||||
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
|
||||
fontSize: number;
|
||||
textAlign: string; //"left"|"right"|"center"
|
||||
@@ -190,10 +193,12 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
setStrokeSharpness(val: number) {
|
||||
switch (val) {
|
||||
case 0:
|
||||
this.style.strokeSharpness = "round";
|
||||
this.style.roundness = {
|
||||
type: ROUNDNESS.LEGACY
|
||||
}
|
||||
return "round";
|
||||
default:
|
||||
this.style.strokeSharpness = "sharp";
|
||||
this.style.roundness = null; //sharp
|
||||
return "sharp";
|
||||
}
|
||||
};
|
||||
@@ -383,18 +388,17 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
template?.appState?.currentItemFontSize ?? this.style.fontSize,
|
||||
currentItemTextAlign:
|
||||
template?.appState?.currentItemTextAlign ?? this.style.textAlign,
|
||||
currentItemStrokeSharpness:
|
||||
template?.appState?.currentItemStrokeSharpness ??
|
||||
this.style.strokeSharpness,
|
||||
currentItemStartArrowhead:
|
||||
template?.appState?.currentItemStartArrowhead ??
|
||||
this.style.startArrowHead,
|
||||
currentItemEndArrowhead:
|
||||
template?.appState?.currentItemEndArrowhead ??
|
||||
this.style.endArrowHead,
|
||||
currentItemLinearStrokeSharpness:
|
||||
template?.appState?.currentItemLinearStrokeSharpness ??
|
||||
this.style.strokeSharpness,
|
||||
currentItemRoundness: //type StrokeRoundness = "round" | "sharp"
|
||||
template?.appState?.currentItemLinearStrokeSharpness ?? //legacy compatibility
|
||||
template?.appState?.currentItemStrokeSharpness ?? //legacy compatibility
|
||||
template?.appState?.currentItemRoundness ??
|
||||
this.style.roundness ? "round":"sharp",
|
||||
gridSize: template?.appState?.gridSize ?? this.canvas.gridSize,
|
||||
colorPalette: template?.appState?.colorPalette ?? this.colorPalette,
|
||||
},
|
||||
@@ -584,7 +588,11 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
strokeStyle: this.style.strokeStyle,
|
||||
roughness: this.style.roughness,
|
||||
opacity: this.style.opacity,
|
||||
strokeSharpness: this.style.strokeSharpness,
|
||||
roundness: this.style.strokeSharpness
|
||||
? (this.style.strokeSharpness === "round"
|
||||
? {type: ROUNDNESS.LEGACY}
|
||||
: null)
|
||||
: this.style.roundness,
|
||||
seed: Math.floor(Math.random() * 100000),
|
||||
version: 1,
|
||||
versionNonce: Math.floor(Math.random() * 1000000000),
|
||||
@@ -1186,7 +1194,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
strokeStyle: "solid",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
strokeSharpness: "sharp",
|
||||
roundness: null,
|
||||
fontFamily: 1,
|
||||
fontSize: 20,
|
||||
textAlign: "left",
|
||||
|
||||
@@ -615,6 +615,7 @@ export class ExcalidrawData {
|
||||
newText: string,
|
||||
newOriginalText: string,
|
||||
forceUpdate: boolean = false,
|
||||
containerType?: string,
|
||||
) {
|
||||
if (forceUpdate || newText != sceneTextElement.text) {
|
||||
const measure = _measureText(
|
||||
@@ -625,7 +626,7 @@ export class ExcalidrawData {
|
||||
sceneTextElement.text = newText;
|
||||
sceneTextElement.originalText = newOriginalText;
|
||||
|
||||
if (!sceneTextElement.containerId) {
|
||||
if (!sceneTextElement.containerId || containerType==="arrow") {
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
|
||||
//I leave the setting of text width to excalidraw, when text is in a container
|
||||
//because text width is fixed to the container width
|
||||
@@ -660,6 +661,7 @@ export class ExcalidrawData {
|
||||
) : originalText,
|
||||
originalText,
|
||||
forceupdate,
|
||||
container?.type,
|
||||
); //(await this.getText(te.id))??te.text serves the case when the whole #Text Elements section is deleted by accident
|
||||
}
|
||||
}
|
||||
@@ -1326,7 +1328,9 @@ export class ExcalidrawData {
|
||||
|
||||
public getOpenMode(): { viewModeEnabled: boolean; zenModeEnabled: boolean } {
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
let mode = this.plugin.settings.defaultMode;
|
||||
let mode = this.plugin.settings.defaultMode === "view-mobile"
|
||||
? (this.plugin.device.isPhone ? "view" : "normal")
|
||||
: this.plugin.settings.defaultMode;
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_DEFAULT_MODE] != null
|
||||
|
||||
@@ -2083,7 +2083,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return { id: selectedElement[0].id, text: selectedElement[0].text };
|
||||
} //a text element was selected. Return text
|
||||
|
||||
if (selectedElement[0].type === "image") {
|
||||
if (["image","arrow"].contains(selectedElement[0].type)) {
|
||||
return { id: null, text: null };
|
||||
}
|
||||
|
||||
@@ -2375,14 +2375,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
currentItemFontFamily: st.currentItemFontFamily,
|
||||
currentItemFontSize: st.currentItemFontSize,
|
||||
currentItemTextAlign: st.currentItemTextAlign,
|
||||
currentItemStrokeSharpness: st.currentItemStrokeSharpness,
|
||||
currentItemStartArrowhead: st.currentItemStartArrowhead,
|
||||
currentItemEndArrowhead: st.currentItemEndArrowhead,
|
||||
scrollX: st.scrollX,
|
||||
scrollY: st.scrollY,
|
||||
zoom: st.zoom,
|
||||
currentItemLinearStrokeSharpness:
|
||||
st.currentItemLinearStrokeSharpness,
|
||||
currentItemRoundness: st.currentItemRoundness,
|
||||
gridSize: st.gridSize,
|
||||
colorPalette: st.colorPalette,
|
||||
},
|
||||
@@ -3078,14 +3076,18 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const el = elements.filter((el:ExcalidrawElement)=>el.id === textElement.id);
|
||||
if(el.length === 1) {
|
||||
const clone = cloneElement(el[0]);
|
||||
const containerType = el[0].containerId
|
||||
? api.getSceneElements().filter((e:ExcalidrawElement)=>e.id===el[0].containerId)?.[0]?.type
|
||||
: undefined;
|
||||
this.excalidrawData.updateTextElement(
|
||||
clone,
|
||||
wrappedParsedText,
|
||||
parsedText,
|
||||
true
|
||||
true,
|
||||
containerType
|
||||
);
|
||||
elements[elements.indexOf(el[0])] = clone;
|
||||
await this.updateScene({elements});
|
||||
this.updateScene({elements});
|
||||
if(clone.containerId) this.updateContainerSize(clone.containerId);
|
||||
}
|
||||
|
||||
@@ -3322,11 +3324,11 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const containers = containerId
|
||||
? api
|
||||
.getSceneElements()
|
||||
.filter((el: ExcalidrawElement) => el.id === containerId)
|
||||
.filter((el: ExcalidrawElement) => el.id === containerId && el.type!=="arrow")
|
||||
: api
|
||||
.getSceneElements()
|
||||
.filter((el: ExcalidrawElement) =>
|
||||
el.boundElements?.map((e) => e.type).includes("text"),
|
||||
el.type!=="arrow" && el.boundElements?.map((e) => e.type).includes("text"),
|
||||
);
|
||||
if (containers.length > 0) {
|
||||
if (this.initialContainerSizeUpdate) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
svgToBase64,
|
||||
} from "./utils/Utils";
|
||||
import { isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
import { image } from "html2canvas/dist/types/css/types/image";
|
||||
|
||||
interface imgElementAttributes {
|
||||
file?: TFile;
|
||||
@@ -185,54 +186,77 @@ const getIMG = async (
|
||||
return img;
|
||||
};
|
||||
|
||||
const createImgElement = async (
|
||||
attr: imgElementAttributes,
|
||||
) :Promise<HTMLElement> => {
|
||||
const img = await getIMG(attr);
|
||||
img.setAttribute("fileSource", attr.fname);
|
||||
if (attr.fwidth) {
|
||||
img.setAttribute("w", attr.fwidth);
|
||||
}
|
||||
if (attr.fheight) {
|
||||
img.setAttribute("h", attr.fheight);
|
||||
}
|
||||
|
||||
let timer:NodeJS.Timeout;
|
||||
const clickEvent = (ev:PointerEvent) => {
|
||||
if (
|
||||
ev.target instanceof Element &&
|
||||
ev.target.tagName.toLowerCase() != "img"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const src = img.getAttribute("fileSource");
|
||||
if (src) {
|
||||
const srcParts = src.match(/([^#]*)(.*)/);
|
||||
if(!srcParts) return;
|
||||
plugin.openDrawing(
|
||||
vault.getAbstractFileByPath(srcParts[1]) as TFile,
|
||||
ev[CTRL_OR_CMD]
|
||||
? "new-pane"
|
||||
: (ev.metaKey && !app.isMobile)
|
||||
? "popout-window"
|
||||
: "active-pane",
|
||||
true,
|
||||
srcParts[2],
|
||||
);
|
||||
} //.ctrlKey||ev.metaKey);
|
||||
};
|
||||
img.addEventListener("pointerdown",(ev)=>{
|
||||
if(img?.parentElement?.hasClass("canvas-node-content")) return;
|
||||
timer = setTimeout(()=>clickEvent(ev),500);
|
||||
});
|
||||
img.addEventListener("pointerup",()=>{
|
||||
if(timer) clearTimeout(timer);
|
||||
timer = null;
|
||||
})
|
||||
img.addEventListener("dblclick",clickEvent);
|
||||
img.addEventListener(RERENDER_EVENT, async (e) => {
|
||||
e.stopPropagation();
|
||||
const parent = img.parentElement;
|
||||
const imgMaxWidth = img.style.maxWidth;
|
||||
const imgMaxHeigth = img.style.maxHeight;
|
||||
const fileSource = img.getAttribute("fileSource");
|
||||
const newImg = await createImgElement({
|
||||
fname: fileSource,
|
||||
fwidth: img.getAttribute("w"),
|
||||
fheight: img.getAttribute("h"),
|
||||
style: img.getAttribute("class"),
|
||||
});
|
||||
parent.empty();
|
||||
newImg.style.maxHeight = imgMaxHeigth;
|
||||
newImg.style.maxWidth = imgMaxWidth;
|
||||
newImg.setAttribute("fileSource",fileSource);
|
||||
parent.append(newImg);
|
||||
});
|
||||
return img;
|
||||
}
|
||||
|
||||
const createImageDiv = async (
|
||||
attr: imgElementAttributes,
|
||||
): Promise<HTMLDivElement> => {
|
||||
const img = await getIMG(attr);
|
||||
return createDiv(attr.style, (el) => {
|
||||
el.append(img);
|
||||
el.setAttribute("src", attr.fname);
|
||||
if (attr.fwidth) {
|
||||
el.setAttribute("w", attr.fwidth);
|
||||
}
|
||||
if (attr.fheight) {
|
||||
el.setAttribute("h", attr.fheight);
|
||||
}
|
||||
el.onClickEvent((ev) => {
|
||||
if (
|
||||
ev.target instanceof Element &&
|
||||
ev.target.tagName.toLowerCase() != "img"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const src = el.getAttribute("src");
|
||||
if (src) {
|
||||
const srcParts = src.match(/([^#]*)(.*)/);
|
||||
if(!srcParts) return;
|
||||
plugin.openDrawing(
|
||||
vault.getAbstractFileByPath(srcParts[1]) as TFile,
|
||||
ev[CTRL_OR_CMD]
|
||||
? "new-pane"
|
||||
: (ev.metaKey && !app.isMobile)
|
||||
? "popout-window"
|
||||
: "active-pane",
|
||||
true,
|
||||
srcParts[2],
|
||||
);
|
||||
} //.ctrlKey||ev.metaKey);
|
||||
});
|
||||
el.addEventListener(RERENDER_EVENT, async (e) => {
|
||||
e.stopPropagation();
|
||||
el.empty();
|
||||
const img = await getIMG({
|
||||
fname: el.getAttribute("src"),
|
||||
fwidth: el.getAttribute("w"),
|
||||
fheight: el.getAttribute("h"),
|
||||
style: el.getAttribute("class"),
|
||||
});
|
||||
el.append(img);
|
||||
});
|
||||
});
|
||||
const img = await createImgElement(attr);
|
||||
return createDiv(attr.style, (el) => el.append(img));
|
||||
};
|
||||
|
||||
const processReadingMode = async (
|
||||
@@ -336,101 +360,106 @@ const tmpObsidianWYSIWYG = async (
|
||||
return;
|
||||
}
|
||||
|
||||
//The timeout gives time for Obsidian to attach el to the displayed document
|
||||
//Once the element is attached, I can traverse up the dom tree to find .internal-embed
|
||||
//If internal embed is not found, it means the that the excalidraw.md file
|
||||
//is being rendered in "reading" mode. In that case, the image with the default width
|
||||
//specified in setting should be displayed
|
||||
//if .internal-embed is found, then contents is replaced with the image using the
|
||||
//internal-embed: Excalidraw is embedded into a markdown document
|
||||
//markdown-reading-view: we are processing the markdown reading view of an actual Excalidraw file
|
||||
//markdown-embed: we are processing the hover preview of a markdown file
|
||||
//alt, width, and height attributes of .internal-embed to size and style the image
|
||||
setTimeout(async () => {
|
||||
//wait for el to be attached to the displayed document
|
||||
let counter = 0;
|
||||
while(!el.parentElement && counter++<=50) await sleep(50);
|
||||
if(!el.parentElement) return;
|
||||
|
||||
let internalEmbedDiv: HTMLElement = el;
|
||||
while (
|
||||
!internalEmbedDiv.hasClass("dataview") &&
|
||||
!internalEmbedDiv.hasClass("cm-preview-code-block") &&
|
||||
!internalEmbedDiv.hasClass("cm-embed-block") &&
|
||||
!internalEmbedDiv.hasClass("internal-embed") &&
|
||||
internalEmbedDiv.parentElement
|
||||
) {
|
||||
internalEmbedDiv = internalEmbedDiv.parentElement;
|
||||
}
|
||||
|
||||
if(
|
||||
internalEmbedDiv.hasClass("dataview") ||
|
||||
internalEmbedDiv.hasClass("cm-preview-code-block") ||
|
||||
internalEmbedDiv.hasClass("cm-embed-block")
|
||||
) {
|
||||
return; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/835
|
||||
}
|
||||
|
||||
const attr: imgElementAttributes = {
|
||||
fname: ctx.sourcePath,
|
||||
fheight: "",
|
||||
fwidth: getDefaultWidth(plugin),
|
||||
style: "excalidraw-svg",
|
||||
};
|
||||
|
||||
attr.file = file;
|
||||
//@ts-ignore
|
||||
const containerEl = ctx.containerEl;
|
||||
let internalEmbedDiv: HTMLElement = containerEl;
|
||||
while (
|
||||
!internalEmbedDiv.hasClass("dataview") &&
|
||||
!internalEmbedDiv.hasClass("cm-preview-code-block") &&
|
||||
!internalEmbedDiv.hasClass("cm-embed-block") &&
|
||||
!internalEmbedDiv.hasClass("internal-embed") &&
|
||||
!internalEmbedDiv.hasClass("markdown-reading-view") &&
|
||||
!internalEmbedDiv.hasClass("markdown-embed") &&
|
||||
internalEmbedDiv.parentElement
|
||||
) {
|
||||
internalEmbedDiv = internalEmbedDiv.parentElement;
|
||||
}
|
||||
|
||||
if(
|
||||
internalEmbedDiv.hasClass("dataview") ||
|
||||
internalEmbedDiv.hasClass("cm-preview-code-block") ||
|
||||
internalEmbedDiv.hasClass("cm-embed-block")
|
||||
) {
|
||||
return; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/835
|
||||
}
|
||||
|
||||
if (!internalEmbedDiv.hasClass("internal-embed")) {
|
||||
//We are processing the markdown preview of an actual Excalidraw file
|
||||
//This could be in a hover preview of the file
|
||||
//Or the file could be in markdown mode and the user switched markdown
|
||||
//view of the drawing to reading mode
|
||||
el.empty();
|
||||
const mdPreviewSection = el.parentElement;
|
||||
if(!mdPreviewSection.hasClass("markdown-preview-section")) return;
|
||||
if(mdPreviewSection.hasAttribute("ready")) {
|
||||
mdPreviewSection.removeChild(el);
|
||||
return;
|
||||
}
|
||||
mdPreviewSection.setAttribute("ready","");
|
||||
const imgDiv = await createImageDiv(attr);
|
||||
el.appendChild(imgDiv);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isTextOnlyEmbed(internalEmbedDiv)) {
|
||||
//legacy reference to a block or section as text
|
||||
//should be embedded as legacy text
|
||||
return;
|
||||
}
|
||||
const attr: imgElementAttributes = {
|
||||
fname: ctx.sourcePath,
|
||||
fheight: "",
|
||||
fwidth: getDefaultWidth(plugin),
|
||||
style: "excalidraw-svg",
|
||||
};
|
||||
|
||||
attr.file = file;
|
||||
|
||||
const markdownEmbed = internalEmbedDiv.hasClass("markdown-embed");
|
||||
const markdownReadingView = internalEmbedDiv.hasClass("markdown-reading-view");
|
||||
if (!internalEmbedDiv.hasClass("internal-embed") &&
|
||||
( markdownEmbed || markdownReadingView)
|
||||
) {
|
||||
//We are processing the markdown preview of an actual Excalidraw file
|
||||
//the excalidraw file in markdown preview mode
|
||||
const isFrontmatterDiv = Boolean(el.querySelector(".frontmatter"));
|
||||
el.empty();
|
||||
|
||||
if(internalEmbedDiv.hasAttribute("ready")) {
|
||||
if(!isFrontmatterDiv) {
|
||||
containerEl.removeChild(el);
|
||||
return;
|
||||
}
|
||||
internalEmbedDiv.setAttribute("ready","");
|
||||
|
||||
internalEmbedDiv.empty();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
const imgDiv = await createImageDiv(attr);
|
||||
if(markdownEmbed) {
|
||||
internalEmbedDiv.addClass("markdown-embed");
|
||||
if(imgDiv.firstChild instanceof HTMLElement) {
|
||||
imgDiv.firstChild.style.maxHeight = "100%";
|
||||
imgDiv.firstChild.style.maxWidth = null;
|
||||
internalEmbedDiv.appendChild(imgDiv.firstChild);
|
||||
return;
|
||||
}
|
||||
}
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
return;
|
||||
}
|
||||
|
||||
//timer to avoid the image flickering when the user is typing
|
||||
let timer: NodeJS.Timeout = null;
|
||||
const observer = new MutationObserver((m) => {
|
||||
if (!["alt", "width", "height"].contains(m[0]?.attributeName)) {
|
||||
return;
|
||||
}
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(async () => {
|
||||
timer = null;
|
||||
internalEmbedDiv.empty();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
}, 500);
|
||||
});
|
||||
observer.observe(internalEmbedDiv, {
|
||||
attributes: true, //configure it to listen to attribute changes
|
||||
});
|
||||
if(isTextOnlyEmbed(internalEmbedDiv)) {
|
||||
//legacy reference to a block or section as text
|
||||
//should be embedded as legacy text
|
||||
return;
|
||||
}
|
||||
|
||||
el.empty();
|
||||
|
||||
if(internalEmbedDiv.hasAttribute("ready")) {
|
||||
return;
|
||||
}
|
||||
internalEmbedDiv.setAttribute("ready","");
|
||||
|
||||
internalEmbedDiv.empty();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
|
||||
//timer to avoid the image flickering when the user is typing
|
||||
let timer: NodeJS.Timeout = null;
|
||||
const observer = new MutationObserver((m) => {
|
||||
if (!["alt", "width", "height"].contains(m[0]?.attributeName)) {
|
||||
return;
|
||||
}
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(async () => {
|
||||
timer = null;
|
||||
internalEmbedDiv.empty();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
}, 500);
|
||||
});
|
||||
observer.observe(internalEmbedDiv, {
|
||||
attributes: true, //configure it to listen to attribute changes
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ export const nanoid = customAlphabet(
|
||||
export const KEYCODE = {
|
||||
ESC: 27,
|
||||
};
|
||||
export const ROUNDNESS = { //should at one point publish @zsviczian/excalidraw/types/constants
|
||||
LEGACY: 1,
|
||||
PROPORTIONAL_RADIUS: 2,
|
||||
ADAPTIVE_RADIUS: 3,
|
||||
} as const;
|
||||
export const PLUGIN_ID = "obsidian-excalidraw-plugin";
|
||||
export const SCRIPT_INSTALL_CODEBLOCK = "excalidraw-script-install";
|
||||
export const SCRIPT_INSTALL_FOLDER = "Downloaded";
|
||||
|
||||
@@ -17,6 +17,50 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
|
||||
`,
|
||||
"1.8.5":`
|
||||
## New from Excalidraw.com:
|
||||
- Better default radius for rectangles [#5553](https://github.com/excalidraw/excalidraw/pull/5553). Existing drawings will look unchanged, this applies only to new rectangles.
|
||||

|
||||
> [!attention]- ExcalidrawAutomate technical details
|
||||
> - ${String.fromCharCode(96)}strokeSharpness${String.fromCharCode(96)} is now deprecated
|
||||
> - use roundness instead
|
||||
> - ${String.fromCharCode(96)}roundness === null${String.fromCharCode(96)} is legacy ${String.fromCharCode(96)}strokeSharpness = "sharp"${String.fromCharCode(96)}
|
||||
> - ${String.fromCharCode(96)}roundness = { type: RoundnessType; value?: number }${String.fromCharCode(96)}
|
||||
> - type: 1, LEGACY, type:2 PROPORTIONAL_RADIUS, type:3 ADAPTIVE_RADIUS: 3
|
||||
> - value:
|
||||
> - Radius represented as % of element's largest side (width/height).
|
||||
> DEFAULT_PROPORTIONAL_RADIUS = 0.25;
|
||||
> - Fixed radius for the ADAPTIVE_RADIUS algorithm. In pixels.
|
||||
> DEFAULT_ADAPTIVE_RADIUS = 32;
|
||||
|
||||
## New
|
||||
- Improved embedding into Obsidian Canvas
|
||||
- Improved embedding into Markdown documents
|
||||
- Added setting under ${String.fromCharCode(96)}Display/Default mode when opening Excalidraw${String.fromCharCode(96)} to always open the drawing in view mode on Mobile, but in normal mode on desktop. [#939](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/939)
|
||||
|
||||
## Fixed
|
||||
- Zoom reset tooltip appears twice [#942](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/942)
|
||||
- Hid export library from library menu as it does not work due to Obsidian limitations. Use the command palette export library instead.
|
||||
- Arrow with label did not get exported and embedded correctly [#941](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/941)
|
||||

|
||||
`,
|
||||
"1.8.4":`
|
||||
## New from Excalidraw.com
|
||||
- Labels on Arrows!!! [#5723](https://github.com/excalidraw/excalidraw/pull/5723)
|
||||
- To add a label press "Enter" or "Double click" on the arrow
|
||||
- Use "Cmd/Ctrl+double click" to enter the line editor
|
||||
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://user-images.githubusercontent.com/11256141/192515552-6b6ddc06-5de0-4931-abdd-6ac3a804656d.mp4" title="Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## New
|
||||
- **Changed behavior**: In the Obsidian markdown editor clicking an Excalidraw image will not open the image (to avoid accidentally opening the image on a tablet). To open a drawing for editing in Excalidraw double click or long-tap on it. [#920](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/920)
|
||||
|
||||
## Fixed
|
||||
- Text stroke color is not honored when pasting a HEX color string to an Excalidraw canvas open in an Obsidian popout window [#921](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/921)
|
||||
- The new [multi-line >> multi-element paste behavior](https://github.com/excalidraw/excalidraw/pull/5786) introduced in the previous release did not work as expected in Obsidian. Now it does.
|
||||
`,
|
||||
"1.8.2":`
|
||||
Introducing the [Excalidraw Slideshow Script](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Slideshow.md) - available in the script store
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
|
||||
@@ -73,9 +73,9 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "style.strokeSharpness",
|
||||
code: "[string]",
|
||||
desc: "'round' | 'sharp'",
|
||||
field: "style.roundness",
|
||||
code: "[null | { type: RoundnessType; value?: number };]",
|
||||
desc: "set to null for 'sharp', else the stroke will be 'round'<br>type: 1==LEGACY,<br>2==PROPORTIONAL RADIUS,<br>3==ADAPTIVE RADIUS, value: adaptive factor defaults to 32",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "obsidian";
|
||||
//import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
export {ExcalidrawAutomateInterface} from "./types";
|
||||
export type { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
export type { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeRoundness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
export type { Point } from "@zsviczian/excalidraw/types/types";
|
||||
export const getEA = (view?:any): any => {
|
||||
try {
|
||||
|
||||
@@ -2099,8 +2099,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
e.initEvent(RERENDER_EVENT, true, false);
|
||||
ownerDocument
|
||||
.querySelectorAll(
|
||||
`div[class^='excalidraw-svg']${
|
||||
filepath ? `[src='${filepath.replaceAll("'", "\\'")}']` : ""
|
||||
`img[class^='excalidraw-svg']${
|
||||
filepath ? `[fileSource='${filepath.replaceAll("'", "\\'")}']` : ""
|
||||
}`,
|
||||
)
|
||||
.forEach((el) => el.dispatchEvent(e));
|
||||
|
||||
@@ -565,9 +565,10 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.setDesc(fragWithHTML(t("DEFAULT_OPEN_MODE_DESC")))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("normal", "Normal Mode")
|
||||
.addOption("zen", "Zen Mode")
|
||||
.addOption("view", "View Mode")
|
||||
.addOption("normal", "Always in normal-mode")
|
||||
.addOption("zen", "Always in zen-mode")
|
||||
.addOption("view", "Always in view-mode")
|
||||
.addOption("view-mobile", "Usually normal, but view-mode on Phone")
|
||||
.setValue(this.plugin.settings.defaultMode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.defaultMode = value;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomId, randomInteger } from "../utils";
|
||||
|
||||
import { ExcalidrawLinearElement, FillStyle, GroupId, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawLinearElement, FillStyle, GroupId, RoundnessType, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
|
||||
export type Point = [number, number];
|
||||
|
||||
@@ -13,7 +13,7 @@ export type ExcalidrawElementBase = {
|
||||
fillStyle: FillStyle;
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle;
|
||||
strokeSharpness: StrokeSharpness;
|
||||
roundness: null | { type: RoundnessType; value?: number };
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
width: number;
|
||||
@@ -71,7 +71,7 @@ export function createExElement(): ExcalidrawElementBase {
|
||||
fillStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
strokeSharpness: "sharp",
|
||||
roundness: null,
|
||||
roughness: 0,
|
||||
opacity: 100,
|
||||
width: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, FillStyle, GroupId, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, FillStyle, GroupId, RoundnessType, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
|
||||
export type PathCommand = {
|
||||
type: string;
|
||||
@@ -53,7 +53,7 @@ type _ExcalidrawElementBase = Readonly<{
|
||||
fillStyle: FillStyle;
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle;
|
||||
strokeSharpness: StrokeSharpness;
|
||||
roundness: null | { type: RoundnessType; value?: number };
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
width: number;
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import { getTransformMatrix, transformPoints } from "./transform";
|
||||
import { pointsOnPath } from "points-on-path";
|
||||
import { randomId, getWindingOrder } from "./utils";
|
||||
import { ROUNDNESS } from "../Constants";
|
||||
|
||||
const SUPPORTED_TAGS = [
|
||||
"svg",
|
||||
@@ -352,7 +353,7 @@ const walkers = {
|
||||
y: result[13],
|
||||
width: result[0],
|
||||
height: result[5],
|
||||
strokeSharpness: isRound ? "round" : "sharp",
|
||||
roundness: isRound ? {type:ROUNDNESS.LEGACY} : null,
|
||||
};
|
||||
|
||||
scene.elements.push(rect);
|
||||
|
||||
5
src/types.d.ts
vendored
5
src/types.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import { ExcalidrawBindableElement, ExcalidrawElement, ExcalidrawImageElement, FileId, FillStyle, NonDeletedExcalidrawElement, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawBindableElement, ExcalidrawElement, ExcalidrawImageElement, FileId, FillStyle, NonDeletedExcalidrawElement, RoundnessType, StrokeRoundness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { Point } from "@zsviczian/excalidraw/types/types";
|
||||
import { TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
|
||||
@@ -28,7 +28,8 @@ export interface ExcalidrawAutomateInterface {
|
||||
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
|
||||
strokeSharpness?: StrokeRoundness; //defaults to undefined, use strokeRoundess and roundess instead. Only kept for legacy script compatibility type StrokeRoundness = "round" | "sharp"
|
||||
roundness: null | { type: RoundnessType; value?: number };
|
||||
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
|
||||
fontSize: number;
|
||||
textAlign: string; //"left"|"right"|"center"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"1.8.5": "1.0.0",
|
||||
"1.7.13": "0.15.6",
|
||||
"1.7.8": "0.15.5",
|
||||
"1.7.7": "0.15.4",
|
||||
|
||||
@@ -2221,10 +2221,10 @@
|
||||
dependencies:
|
||||
"@zerollup/ts-helpers" "^1.7.18"
|
||||
|
||||
"@zsviczian/excalidraw@0.13.0-obsidian-1":
|
||||
"integrity" "sha512-gHfuEX/qrBa+4kolxEkQ/3W5hGfSLoJSXDpuhb8Mvvyyl148hsuWmhUQGFWcNee73YbuQ0arb3hXqwnMUgK0Ig=="
|
||||
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.13.0-obsidian-1.tgz"
|
||||
"version" "0.13.0-obsidian-1"
|
||||
"@zsviczian/excalidraw@0.13.0-obsidian-2":
|
||||
"integrity" "sha512-gKB+V8EgxFimjaTmRZzBPumh1q7UeG7ri9MbVcBovpeWxnz7q/PROCgGv3tJvHzwZLzts3f+pL66l9pTecXoUA=="
|
||||
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.13.0-obsidian-2.tgz"
|
||||
"version" "0.13.0-obsidian-2"
|
||||
|
||||
"abab@^2.0.3", "abab@^2.0.5":
|
||||
"integrity" "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
|
||||
Reference in New Issue
Block a user