Compare commits

..

3 Commits

Author SHA1 Message Date
zsviczian
8b3c61ae24 1.9.12 2023-07-27 10:45:06 +02:00
zsviczian
3f0086359a sword 2023-07-27 10:14:24 +02:00
zsviczian
9a807e4f8a 1.9.11 release message 2023-07-26 15:54:24 +02:00
12 changed files with 292 additions and 58 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

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

View File

@@ -740,7 +740,7 @@ export class ExcalidrawAutomate {
app.metadataCache.fileToLinktext(
file,
this.targetView.file.path,
file.extension === "md",
false, //file.extension === "md", //changed this to false because embedable link navigation in ExcaliBrain
)
}]]` : "",
);

View File

@@ -1346,7 +1346,12 @@ export class ExcalidrawData {
return this.textElements.get(id)?.raw;
}
public getParsedText(id: string): [string, string, string] {
/**
* returns parsed text with the correct line length
* @param id
* @returns
*/
public getParsedText(id: string): [parseResultWrapped: string, parseResultOriginal: string, link: string] {
const t = this.textElements.get(id);
if (!t) {
return [null, null, null];
@@ -1354,12 +1359,28 @@ export class ExcalidrawData {
return [wrap(t.parsed, t.wrapAt), t.parsed, null];
}
/**
* Attempts to quickparse (sycnhronously) the raw text.
*
* If successful:
* - it will set the textElements cache with the parsed result, and
* - return the parsed result as an array of 3 values: [parsedTextWrapped, parsedText, link]
*
* If the text contains a transclusion:
* - it will initiate the async parse, and
* - it will return [null,null,null].
* @param elementID
* @param rawText
* @param rawOriginalText
* @param updateSceneCallback
* @returns [parseResultWrapped: string, parseResultOriginal: string, link: string]
*/
public setTextElement(
elementID: string,
rawText: string,
rawOriginalText: string,
updateScene: Function,
): [string, string, string] {
updateSceneCallback: Function,
): [parseResultWrapped: string, parseResultOriginal: string, link: string] {
const maxLineLen = estimateMaxLineLen(rawText, rawOriginalText);
const [parseResult, link] = this.quickParse(rawOriginalText); //will return the parsed result if raw text does not include transclusion
if (parseResult) {
@@ -1380,7 +1401,7 @@ export class ExcalidrawData {
wrapAt: maxLineLen,
});
if (parsedText) {
updateScene(wrap(parsedText, maxLineLen), parsedText);
updateSceneCallback(wrap(parsedText, maxLineLen), parsedText);
}
});
return [null, null, null];

View File

@@ -113,7 +113,7 @@ import { ObsidianMenu } from "./menu/ObsidianMenu";
import { ToolsPanel } from "./menu/ToolsPanel";
import { ScriptEngine } from "./Scripts";
import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer";
import { ICONS, saveIcon } from "./menu/ActionIcons";
import { ICONS, LogoWrapper, saveIcon } from "./menu/ActionIcons";
import { ExportDialog } from "./dialogs/ExportDialog";
import { getEA } from "src";
import { anyModifierKeysPressed, emulateCTRLClickForLinks, emulateKeysForLinkClick, externalDragModifierType, internalDragModifierType, isALT, isCTRL, isMETA, isSHIFT, linkClickModifierType, mdPropModifier, ModifierKeys } from "./utils/ModifierkeyHelper";
@@ -125,6 +125,7 @@ import { imageCache } from "./utils/ImageCache";
import { CanvasNodeFactory } from "./utils/CanvasNodeFactory";
import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu";
import { useDefaultExcalidrawFrame } from "./utils/CustomEmbeddableUtils";
import { UniversalInsertFileModal } from "./dialogs/UniversalInsertFileModal";
declare const PLUGIN_VERSION:string;
@@ -1081,7 +1082,10 @@ export default class ExcalidrawView extends TextFileView {
console.error(e);
}
await leaf.openFile(file, subpath ? { active: !this.linksAlwaysOpenInANewPane, eState: { subpath } } : undefined); //if file exists open file and jump to reference
await leaf.openFile(file, {
active: !this.linksAlwaysOpenInANewPane,
...subpath ? { eState: { subpath } } : {}
}); //if file exists open file and jump to reference
//view.app.workspace.setActiveLeaf(leaf, true, true); //0.15.4 ExcaliBrain focus issue
} catch (e) {
new Notice(e, 4000);
@@ -3654,8 +3658,31 @@ export default class ExcalidrawView extends TextFileView {
if (!api) {
return [null, null, null];
}
// 1. Set the isEditingText flag to true to prevent autoresize on mobile
// 1500ms is an empirical number, the onscreen keyboard usually disappears in 1-2 seconds
this.semaphores.isEditingText = true;
if(this.isEditingTextResetTimer) {
clearTimeout(this.isEditingTextResetTimer);
}
this.isEditingTextResetTimer = setTimeout(() => {
this.semaphores.isEditingText = false;
this.isEditingTextResetTimer = null;
}, 1500);
// 2. If the text element is deleted, remove it from ExcalidrawData
// parsed textElements cache
if (isDeleted) {
this.excalidrawData.deleteTextElement(textElement.id);
this.setDirty(7);
return [null, null, null];
}
// 3. Check if the user accidently pasted Excalidraw data from the clipboard
// as text. If so, update the parsed link in ExcalidrawData
// textElements cache and update the text element in the scene with a warning.
const FORBIDDEN_TEXT = `{"type":"excalidraw/clipboard","elements":[{"`;
const WARNING = "PASTING EXCALIDRAW ELEMENTS AS A TEXT ELEMENT IS NOT ALLOWED";
const WARNING = t("WARNING_PASTING_ELEMENT_AS_TEXT");
if(text.startsWith(FORBIDDEN_TEXT)) {
setTimeout(()=>{
const elements = this.excalidrawAPI.getSceneElements();
@@ -3671,22 +3698,53 @@ export default class ExcalidrawView extends TextFileView {
});
return [WARNING,WARNING,null];
}
this.semaphores.isEditingText = true;
this.isEditingTextResetTimer = setTimeout(() => {
this.semaphores.isEditingText = false;
this.isEditingTextResetTimer = null;
}, 1500); // to give time for the onscreen keyboard to disappear
if (isDeleted) {
this.excalidrawData.deleteTextElement(textElement.id);
this.setDirty(7);
return [null, null, null];
}
const containerId = textElement.containerId;
//If the parsed text is different than the raw text, and if View is in TextMode.parsed
//Then I need to clear the undo history to avoid overwriting raw text with parsed text and losing links
const REG_TRANSCLUSION = /^!\[\[([^|\]]*)?.*?]]$|^!\[[^\]]*?]\((.*?)\)$/g;
// 4. Check if the text matches the transclusion pattern and if so,
// check if the link in the transclusion can be resolved to a file in the vault
// if the link can be resolved, check if the file is a markdown file but not an
// Excalidraw file. If so, create a timeout to remove the text element from the
// scene and invoke the UniversalInsertFileModal with the file.
const match = originalText.trim().matchAll(REG_TRANSCLUSION).next(); //reset the iterator
if(match?.value?.[0]) {
const link = match.value[1] ?? match.value[2];
const file = app.metadataCache.getFirstLinkpathDest(link, this.file.path);
if(file && file instanceof TFile) {
if (file.extension !== "md" || this.plugin.isExcalidrawFile(file))
{
setTimeout(async ()=>{
const elements = this.excalidrawAPI.getSceneElements();
const el = elements.filter((el:ExcalidrawElement)=>el.id === textElement.id) as ExcalidrawTextElement[];
if(el.length === 1) {
const center = {x: el[0].x, y: el[0].y };
const clone = cloneElement(el[0]);
clone.isDeleted = true;
this.excalidrawData.deleteTextElement(clone.id);
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements});
const ea:ExcalidrawAutomate = getEA(this);
if(IMAGE_TYPES.contains(file.extension)) {
ea.selectElementsInView([await insertImageToView (ea, center, file)]);
} else if(file.extension !== "pdf") {
ea.selectElementsInView([await insertEmbeddableToView (ea, center, file)]);
} else {
const modal = new UniversalInsertFileModal(this.plugin, this);
modal.open(file, center);
}
this.setDirty();
}
});
return [null, null, null];
} else {
new Notice(t("USE_INSERT_FILE_MODAL"),5000);
}
}
}
// 5. Check if the user made changes to the text, or
// the text is missing from ExcalidrawData textElements cache (recently copy/pasted)
if (
text !== textElement.text ||
originalText !== textElement.originalText ||
@@ -3695,37 +3753,48 @@ export default class ExcalidrawView extends TextFileView {
//the user made changes to the text or the text is missing from Excalidraw Data (recently copy/pasted)
//setTextElement will attempt a quick parse (without processing transclusions)
this.setDirty(8);
// setTextElement will invoke this callback function in case quick parse was not possible, the parsed text contains transclusions
// in this case I need to update the scene asynchronously when parsing is complete
const callback = async (wrappedParsedText:string, parsedText:string) => {
//this callback function will only be invoked if quick parse fails, i.e. there is a transclusion in the raw text
if(this.textMode === TextMode.raw) return;
const elements = this.excalidrawAPI.getSceneElements();
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,
containerType
);
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements});
if(clone.containerId) this.updateContainerSize(clone.containerId);
this.setDirty();
}
api.history.clear();
};
const [parseResultWrapped, parseResultOriginal, link] =
this.excalidrawData.setTextElement(
textElement.id,
text,
originalText,
async (wrappedParsedText:string, parsedText:string) => {
//this callback function will only be invoked if quick parse fails, i.e. there is a transclusion in the raw text
if(this.textMode === TextMode.raw) return;
const elements = this.excalidrawAPI.getSceneElements();
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,
containerType
);
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements});
if(clone.containerId) this.updateContainerSize(clone.containerId);
}
api.history.clear();
},
callback,
);
// if quick parse was successful,
// - check if textElement is in a container and update the container size,
// because the parsed text will have a different size than the raw text had
// - depending on the textMode, return the text with markdown markup or the parsed text
// if quick parse was not successful return [null, null, null] to indicate that the no changes were made to the text element
if (parseResultWrapped) {
//there were no transclusions in the raw text, quick parse was successful
if (containerId) {
@@ -3746,6 +3815,7 @@ export default class ExcalidrawView extends TextFileView {
}
return [null, null, null];
}
// even if the text did not change, container sizes might need to be updated
if (containerId) {
this.updateContainerSize(containerId, true);
}
@@ -3782,7 +3852,7 @@ export default class ExcalidrawView extends TextFileView {
}
if (!event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey) {
event = {shiftKey: true, ctrlKey: false, metaKey: false, altKey: false};
event = emulateKeysForLinkClick("new-tab");
}
this.linkClick(
@@ -3790,7 +3860,7 @@ export default class ExcalidrawView extends TextFileView {
null,
null,
{id: element.id, text: link},
emulateCTRLClickForLinks(event)
event,
);
return;
},
@@ -3954,7 +4024,13 @@ export default class ExcalidrawView extends TextFileView {
WelcomeScreen.Center,
{},
React.createElement(
WelcomeScreen.Center.Logo
WelcomeScreen.Center.Logo,
{},
React.createElement(
LogoWrapper,
{},
ICONS.ExcalidrawSword,
),
),
React.createElement(
WelcomeScreen.Center.Heading,

View File

@@ -1,6 +1,5 @@
import { customAlphabet } from "nanoid";
import { DeviceType } from "./types";
import { Platform } from "obsidian";
import { ExcalidrawLib } from "./ExcalidrawLib";
//This is only for backward compatibility because an early version of obsidian included an encoding to avoid fantom links from littering Obsidian graph view
declare const PLUGIN_VERSION:string;

View File

@@ -165,8 +165,7 @@ function RenderObsidianView(
node: null
};
//if subpath is defined, create a canvas node else create a workspace leaf
if(subpath && view.canvasNodeFactory.isInitialized()) {
const setKeepOnTop = () => {
const keepontop = (app.workspace.activeLeaf === view.leaf) && DEVICE.isDesktop;
if (keepontop) {
//@ts-ignore
@@ -179,15 +178,25 @@ function RenderObsidianView(
}, 500);
}
}
}
//if subpath is defined, create a canvas node else create a workspace leaf
if(subpath && view.canvasNodeFactory.isInitialized()) {
setKeepOnTop();
leafRef.current.node = view.canvasNodeFactory.createFileNote(file, subpath, containerRef.current, element.id);
} else {
(async () => {
await leafRef.current.leaf.openFile(file, subpath ? { eState: { subpath }, state: {mode:"preview"} } : undefined);
await leafRef.current.leaf.openFile(file, {
active: false,
state: {mode:"preview"},
...subpath ? { eState: { subpath }}:{},
});
const viewType = leafRef.current.leaf.view?.getViewType();
if(viewType === "canvas") {
leafRef.current.leaf.view.canvas?.setReadonly(true);
}
if ((viewType === "markdown") && view.canvasNodeFactory.isInitialized()) {
setKeepOnTop();
//I haven't found a better way of deciding if an .md file has its own view (e.g., kanban) or not
//This runs only when the file is added, thus should not be a major performance issue
await leafRef.current.leaf.setViewState({state: {file:null}})

View File

@@ -17,7 +17,28 @@ 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.9.12":`
## New
- If you create a Text Element that includes only a transclusion e.g.: ${String.fromCharCode(96)}![[My Image.png]]${String.fromCharCode(96)} then excalidraw will automatically replace the transclusion with the embedded image.
- New Excalidraw splash screen icon contributed by Felix Häberle. 😍
<div class="excalidraw-image-wrapper">
<img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/excalidraw-sword-mini.png'/>
</div>
## Fixed
- Popout windows behaved inconsistently losing focus at the time when a markdown file was embedded. Hopefully, this is now working as intended.
- A number of small fixes that will also improve the ExcaliBrain experience
`,
"1.9.11":`
# New
- I added 2 new command palette actions: 1) to toggle frame clipping and 2) to toggle frame rendering.
# Updated
- I released a minor update to the slideshow script. Frame sequence (Frame 1, 2, 3, ...) will now be displayed in proper order. Frames will be hidden during the presentation (this was there before, but there was a change to excalidraw.com that broke this feature of the slideshow script).
# Fixed:
- Excalidraw Automate error introduced with 1.9.10 - when elements are repositioned to cursor and no ExcalidrawView is active
`,
"1.9.10":`
## New

View File

@@ -13,6 +13,8 @@ import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
export class UniversalInsertFileModal extends Modal {
private center: { x: number, y: number } = { x: 0, y: 0 };
private file: TFile;
constructor(
private plugin: ExcalidrawPlugin,
private view: ExcalidrawView,
@@ -51,6 +53,12 @@ export class UniversalInsertFileModal extends Modal {
private onKeyDown: (evt: KeyboardEvent) => void;
open(file?: TFile, center?: { x: number, y: number }) {
this.file = file;
this.center = center ?? this.center;
super.open();
}
onOpen(): void {
this.containerEl.classList.add("excalidraw-release");
this.titleEl.setText(`Insert File From Vault`);
@@ -66,7 +74,7 @@ export class UniversalInsertFileModal extends Modal {
let actionPDF: ButtonComponent;
let sizeToggleSetting: Setting
let anchorTo100: boolean = false;
let file: TFile;
let file = this.file;
const updateForm = async () => {
const ea = this.plugin.ea;
@@ -240,6 +248,10 @@ export class UniversalInsertFileModal extends Modal {
});
search.inputEl.focus();
if(file) {
search.setValue(file.path);
suggester.close();
}
updateForm();
}

View File

@@ -97,6 +97,8 @@ export default {
CACHE_NOT_READY: "I apologize for the inconvenience, but an error occurred while loading your file.<br><br><mark>Having a little patience can save you a lot of time...</mark><br><br>The plugin has a backup cache, but it appears that you have just started Obsidian. Initializing the Backup Cache may take some time, usually up to a minute or more depending on your device's performance. You will receive a notification in the top right corner when the cache initialization is complete.<br><br>Please press OK to attempt loading the file again and check if the cache has finished initializing. If you see a completely empty file behind this message, I recommend waiting until the backup cache is ready before proceeding. Alternatively, you can choose Cancel to manually correct your file.<br>",
OBSIDIAN_TOOLS_PANEL: "Obsidian Tools Panel",
ERROR_SAVING_IMAGE: "Unknown error occured while fetching the image. It could be that for some reason the image is not available or rejected the fetch request from Obsidian",
WARNING_PASTING_ELEMENT_AS_TEXT: "PASTING EXCALIDRAW ELEMENTS AS A TEXT ELEMENT IS NOT ALLOWED",
USE_INSERT_FILE_MODAL: "Use 'Insert Any File' to embed a markdown note",
//settings.ts
RELEASE_NOTES_NAME: "Display Release Notes after update",

File diff suppressed because one or more lines are too long

View File

@@ -364,4 +364,16 @@ div.excalidraw-draginfo {
position: absolute;
display: block;
z-index: var(--zIndex-layerUI);
}
.excalidraw .welcome-screen-center__logo svg {
width: 5rem !important;
}
.excalidraw-image-wrapper {
text-align: center;
}
.excalidraw-image-wrapper img {
margin: auto;
}