mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71f1072822 | ||
|
|
8029c5d4cd | ||
|
|
962c6a71f7 | ||
|
|
3e0a6e839f | ||
|
|
7aa416766c | ||
|
|
3dae930201 | ||
|
|
379c2d0e52 | ||
|
|
d9a1113f25 | ||
|
|
d0d5677c81 | ||
|
|
a364c0fe45 | ||
|
|
bf9a917af3 |
@@ -13,7 +13,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|
||||
|[](https://youtu.be/VRZVujfVab0)|[](https://youtu.be/D1iBYo1_jjc)|[](https://www.youtube.com/watch?v=_c_0zpBJ4Xc&)|
|
||||
|[](https://youtu.be/r08wk-58DPk)|[](https://youtu.be/tsecSfnTMow)|[](https://youtu.be/K6qZkTz8GHs)|
|
||||
|[](https://youtu.be/hePJcObHIso)|[](https://youtu.be/NOuddK6xrr8)|[](https://youtu.be/lzYdOQ6z8F0)|
|
||||
|[](https://youtu.be/eKFmrSQhFA4)|||
|
||||
|[](https://youtu.be/eKFmrSQhFA4)|[](https://youtu.be/qbPIAZguJeo)||
|
||||
|
||||
|
||||
# Key features
|
||||
|
||||
@@ -24,7 +24,8 @@ if(!settings["Point density"]) {
|
||||
ea.setScriptSettings(settings);
|
||||
}
|
||||
|
||||
const setSize = parseInt(settings["Point density"].value[0]);
|
||||
const scale = settings["Point density"].value;
|
||||
const setSize = parseInt(scale.substring(0,scale.indexOf(":")));
|
||||
|
||||
const elements = ea.getViewSelectedElements().filter(el=>el.type==="freedraw");
|
||||
if(elements.length === 0) {
|
||||
@@ -59,4 +60,5 @@ elements.forEach((el)=>{
|
||||
});
|
||||
|
||||
ea.deleteViewElements(elements);
|
||||
ea.addElementsToView(false,true,true);
|
||||
await ea.addElementsToView(false,true,true);
|
||||
ea.selectElementsInView(ea.getElements());
|
||||
@@ -42,8 +42,8 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|
||||
|[Normalize Selected Arrows](Normalize%20Selected%20Arrows.md)|This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.||[@1-2-3](https://github.com/1-2-3)|
|
||||
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Reverse arrows](Reverse%20arrows.md)|Reverse the direction of **arrows** within the scope of selected elements.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Select Elements of Type](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%Type.md)|Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set background color of unclosed line object by adding a shadow clone](Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md)|Use this script to set the background color of unclosed line objects by creating a clone of the line object. The script will setting the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Select Elements of Type](Select%20Elements%20of%20Type.md)|Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set background color of unclosed line object by adding a shadow clone](Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md)|Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set Dimensions](Set%20Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set Font Family](Set%20Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set Grid](Set%20Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|
||||
@@ -21,23 +21,26 @@ if(elements.length === 0) {
|
||||
|
||||
typeSet = new Set();
|
||||
elements.forEach(el=>typeSet.add(el.type));
|
||||
|
||||
const elementType = await utils.suggester(
|
||||
Array.from(typeSet).map((item) => {
|
||||
switch(item) {
|
||||
case "line": return "— line";
|
||||
case "ellipse": return "○ ellipse";
|
||||
case "rectangle": return "□ rectangle";
|
||||
case "diamond": return "◇ diamond";
|
||||
case "arrow": return "→ arrow";
|
||||
case "freedraw": return "✎ freedraw";
|
||||
case "image": return "🖼 image";
|
||||
case "text": return "A text";
|
||||
default: return item;
|
||||
}
|
||||
}),
|
||||
Array.from(typeSet)
|
||||
);
|
||||
let elementType = Array.from(typeSet)[0];
|
||||
|
||||
if(typeSet.size > 1) {
|
||||
elementType = await utils.suggester(
|
||||
Array.from(typeSet).map((item) => {
|
||||
switch(item) {
|
||||
case "line": return "— line";
|
||||
case "ellipse": return "○ ellipse";
|
||||
case "rectangle": return "□ rectangle";
|
||||
case "diamond": return "◇ diamond";
|
||||
case "arrow": return "→ arrow";
|
||||
case "freedraw": return "✎ freedraw";
|
||||
case "image": return "🖼 image";
|
||||
case "text": return "A text";
|
||||
default: return item;
|
||||
}
|
||||
}),
|
||||
Array.from(typeSet)
|
||||
);
|
||||
}
|
||||
|
||||
if(!elementType) return;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||

|
||||
|
||||
Use this script to set the background color of unclosed line objects by creating a clone of the line object. The script will setting the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.
|
||||
Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.
|
||||
|
||||
```javascript
|
||||
*/
|
||||
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) {
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.26")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ if(elements.length === 0) {
|
||||
}
|
||||
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
elementsToMove = [];
|
||||
|
||||
elements.forEach((el)=>{
|
||||
const newEl = ea.cloneElement(el);
|
||||
@@ -64,6 +65,16 @@ elements.forEach((el)=>{
|
||||
]);
|
||||
newEl.points.push([0,0]);
|
||||
if(shouldGroup) ea.addToGroup([el.id,newEl.id]);
|
||||
elementsToMove.push({fillId: newEl.id, shapeId: el.id});
|
||||
});
|
||||
|
||||
ea.addElementsToView();
|
||||
await ea.addElementsToView();
|
||||
elementsToMove.forEach((x)=>{
|
||||
const viewElements = ea.getViewElements();
|
||||
ea.moveViewElementToZIndex(
|
||||
x.fillId,
|
||||
viewElements.indexOf(viewElements.filter(el=>el.id === x.shapeId)[0])-1
|
||||
)
|
||||
});
|
||||
|
||||
ea.selectElementsInView(ea.getElements());
|
||||
@@ -235,15 +235,15 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
|
||||
## Select Elements of Type
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%Type.md
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%20Type.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Select%20Elements%20of%Type.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg'></td></tr></table>
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Select%20Elements%20of%20Type.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg'></td></tr></table>
|
||||
|
||||
## Set background color of unclosed line object by adding a shadow clone
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Use this script to set the background color of unclosed line objects by creating a clone of the line object. The script will setting the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-background-color-of-unclosed-line.jpg'></td></tr></table>
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-background-color-of-unclosed-line.jpg'></td></tr></table>
|
||||
|
||||
## Set Dimensions
|
||||
```excalidraw-script-install
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.5.24",
|
||||
"version": "1.5.28",
|
||||
"minAppVersion": "0.12.16",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.10.0-obsidian-37",
|
||||
"@zsviczian/excalidraw": "0.10.0-obsidian-39",
|
||||
"monkey-around": "^2.3.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
@@ -31,6 +31,7 @@
|
||||
"@types/js-beautify": "^1.13.3",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"html2canvas": "^1.4.0",
|
||||
"nanoid": "^3.1.31",
|
||||
|
||||
@@ -233,6 +233,7 @@ export interface ExcalidrawAutomate {
|
||||
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
|
||||
generateElementId(): string; //returns an 8 character long random id
|
||||
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id
|
||||
moveViewElementToZIndex(elementId:number, newZIndex:number): void; //Moves the element to a specific position in the z-index
|
||||
}
|
||||
|
||||
declare let window: any;
|
||||
@@ -1307,17 +1308,14 @@ export async function initExcalidrawAutomate(
|
||||
},
|
||||
selectElementsInView(elements: ExcalidrawElement[]):void {
|
||||
if (!this.targetView || !this.targetView?._loaded) {
|
||||
errorMessage("targetView not set", "getViewSelectedElements()");
|
||||
errorMessage("targetView not set", "selectElementsInView()");
|
||||
return;
|
||||
}
|
||||
if (!elements || elements.length===0) {
|
||||
return;
|
||||
}
|
||||
const API = this.getExcalidrawAPI();
|
||||
let st = API.getAppState();
|
||||
st.selectedElementIds = {};
|
||||
elements.forEach(el=>st.selectedElementIds[el.id] = true);
|
||||
API.updateScene({appState: st});
|
||||
API.selectElements(elements);
|
||||
},
|
||||
generateElementId(): string {
|
||||
return nanoid();
|
||||
@@ -1326,7 +1324,35 @@ export async function initExcalidrawAutomate(
|
||||
const newEl = JSON.parse(JSON.stringify(element));
|
||||
newEl.id = nanoid();
|
||||
return newEl;
|
||||
}
|
||||
},
|
||||
moveViewElementToZIndex(elementId:number, newZIndex:number): void {
|
||||
if (!this.targetView || !this.targetView?._loaded) {
|
||||
errorMessage("targetView not set", "moveViewElementToZIndex()");
|
||||
return;
|
||||
}
|
||||
const API = this.getExcalidrawAPI();
|
||||
const elements = this.getViewElements();
|
||||
const elementToMove = elements.filter((el:any)=>el.id===elementId);
|
||||
if (elementToMove.length === 0) {
|
||||
errorMessage(`Element (id: ${elementId}) not found`, "moveViewElementToZIndex");
|
||||
return;
|
||||
}
|
||||
if (newZIndex >= elements.length) {
|
||||
API.bringToFront(elementToMove);
|
||||
return;
|
||||
}
|
||||
if (newZIndex < 0) {
|
||||
API.sendToBack(elementToMove);
|
||||
return;
|
||||
}
|
||||
|
||||
const oldZIndex = elements.indexOf(elementToMove[0]);
|
||||
elements.splice(newZIndex, 0, elements.splice(oldZIndex, 1)[0]);
|
||||
API.updateScene({
|
||||
elements: elements,
|
||||
commitToHistory: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
await initFonts();
|
||||
return window.ExcalidrawAutomate;
|
||||
|
||||
@@ -62,7 +62,7 @@ import {
|
||||
svgToBase64,
|
||||
viewportCoordsToSceneCoords,
|
||||
} from "./Utils";
|
||||
import { Prompt } from "./Prompt";
|
||||
import { NewFileActions, Prompt } from "./Prompt";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { updateEquation } from "./LaTeX";
|
||||
import {
|
||||
@@ -464,10 +464,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
linkText,
|
||||
view.file.path,
|
||||
);
|
||||
if (!ev.altKey && !file) {
|
||||
new Notice(t("FILE_DOES_NOT_EXIST"), 4000);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const selectedImage = this.getSelectedImageElement();
|
||||
if (selectedImage?.id) {
|
||||
@@ -523,10 +519,11 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
linkText = this.excalidrawData.getFile(selectedImage.fileId).file
|
||||
.path;
|
||||
file = this.excalidrawData.getFile(selectedImage.fileId).file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!linkText) {
|
||||
new Notice(t("LINK_BUTTON_CLICK_NO_TEXT"), 20000);
|
||||
return;
|
||||
@@ -536,15 +533,15 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (ev.shiftKey && this.isFullscreen()) {
|
||||
this.exitFullscreen();
|
||||
}
|
||||
if (!file) {
|
||||
(new NewFileActions(this.plugin,linkText,ev.shiftKey,view)).open();
|
||||
return;
|
||||
}
|
||||
const leaf = ev.shiftKey
|
||||
? getNewOrAdjacentLeaf(this.plugin, view.leaf)
|
||||
: view.leaf;
|
||||
view.app.workspace.setActiveLeaf(leaf);
|
||||
if (file) {
|
||||
leaf.openFile(file, { eState: { line: lineNum - 1 } }); //if file exists open file and jump to reference
|
||||
} else {
|
||||
leaf.view.app.workspace.openLinkText(linkText, view.file.path);
|
||||
}
|
||||
leaf.openFile(file, { eState: { line: lineNum - 1 } }); //if file exists open file and jump to reference
|
||||
view.app.workspace.setActiveLeaf(leaf,true,true);
|
||||
} catch (e) {
|
||||
new Notice(e, 4000);
|
||||
}
|
||||
|
||||
460
src/FolderSuggester.ts
Normal file
460
src/FolderSuggester.ts
Normal file
@@ -0,0 +1,460 @@
|
||||
import {
|
||||
FuzzyMatch,
|
||||
TFile,
|
||||
BlockCache,
|
||||
HeadingCache,
|
||||
CachedMetadata,
|
||||
TextComponent,
|
||||
App,
|
||||
TFolder,
|
||||
FuzzySuggestModal,
|
||||
SuggestModal,
|
||||
Scope
|
||||
} from "obsidian";
|
||||
import { t } from "./lang/helpers";
|
||||
import { createPopper, Instance as PopperInstance } from "@popperjs/core";
|
||||
|
||||
class Suggester<T> {
|
||||
owner: SuggestModal<T>;
|
||||
items: T[];
|
||||
suggestions: HTMLDivElement[];
|
||||
selectedItem: number;
|
||||
containerEl: HTMLElement;
|
||||
constructor(
|
||||
owner: SuggestModal<T>,
|
||||
containerEl: HTMLElement,
|
||||
scope: Scope
|
||||
) {
|
||||
this.containerEl = containerEl;
|
||||
this.owner = owner;
|
||||
containerEl.on(
|
||||
"click",
|
||||
".suggestion-item",
|
||||
this.onSuggestionClick.bind(this)
|
||||
);
|
||||
containerEl.on(
|
||||
"mousemove",
|
||||
".suggestion-item",
|
||||
this.onSuggestionMouseover.bind(this)
|
||||
);
|
||||
|
||||
scope.register([], "ArrowUp", () => {
|
||||
this.setSelectedItem(this.selectedItem - 1, true);
|
||||
return false;
|
||||
});
|
||||
|
||||
scope.register([], "ArrowDown", () => {
|
||||
this.setSelectedItem(this.selectedItem + 1, true);
|
||||
return false;
|
||||
});
|
||||
|
||||
scope.register([], "Enter", (evt) => {
|
||||
this.useSelectedItem(evt);
|
||||
return false;
|
||||
});
|
||||
|
||||
scope.register([], "Tab", (evt) => {
|
||||
this.chooseSuggestion(evt);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
chooseSuggestion(evt: KeyboardEvent) {
|
||||
if (!this.items || !this.items.length) return;
|
||||
const currentValue = this.items[this.selectedItem];
|
||||
if (currentValue) {
|
||||
this.owner.onChooseSuggestion(currentValue, evt);
|
||||
}
|
||||
}
|
||||
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
|
||||
event.preventDefault();
|
||||
if (!this.suggestions || !this.suggestions.length) return;
|
||||
|
||||
const item = this.suggestions.indexOf(el);
|
||||
this.setSelectedItem(item, false);
|
||||
this.useSelectedItem(event);
|
||||
}
|
||||
|
||||
onSuggestionMouseover(event: MouseEvent, el: HTMLDivElement): void {
|
||||
if (!this.suggestions || !this.suggestions.length) return;
|
||||
const item = this.suggestions.indexOf(el);
|
||||
this.setSelectedItem(item, false);
|
||||
}
|
||||
empty() {
|
||||
this.containerEl.empty();
|
||||
}
|
||||
setSuggestions(items: T[]) {
|
||||
this.containerEl.empty();
|
||||
const els: HTMLDivElement[] = [];
|
||||
|
||||
items.forEach((item) => {
|
||||
const suggestionEl = this.containerEl.createDiv("suggestion-item");
|
||||
this.owner.renderSuggestion(item, suggestionEl);
|
||||
els.push(suggestionEl);
|
||||
});
|
||||
this.items = items;
|
||||
this.suggestions = els;
|
||||
this.setSelectedItem(0, false);
|
||||
}
|
||||
useSelectedItem(event: MouseEvent | KeyboardEvent) {
|
||||
if (!this.items || !this.items.length) return;
|
||||
const currentValue = this.items[this.selectedItem];
|
||||
if (currentValue) {
|
||||
this.owner.selectSuggestion(currentValue, event);
|
||||
}
|
||||
}
|
||||
wrap(value: number, size: number): number {
|
||||
return ((value % size) + size) % size;
|
||||
}
|
||||
setSelectedItem(index: number, scroll: boolean) {
|
||||
const nIndex = this.wrap(index, this.suggestions.length);
|
||||
const prev = this.suggestions[this.selectedItem];
|
||||
const next = this.suggestions[nIndex];
|
||||
|
||||
if (prev) prev.removeClass("is-selected");
|
||||
if (next) next.addClass("is-selected");
|
||||
|
||||
this.selectedItem = nIndex;
|
||||
|
||||
if (scroll) {
|
||||
next.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class SuggestionModal<T> extends FuzzySuggestModal<T> {
|
||||
items: T[] = [];
|
||||
suggestions: HTMLDivElement[];
|
||||
popper: PopperInstance;
|
||||
//@ts-ignore
|
||||
scope: Scope = new Scope(this.app.scope);
|
||||
suggester: Suggester<FuzzyMatch<T>>;
|
||||
suggestEl: HTMLDivElement;
|
||||
promptEl: HTMLDivElement;
|
||||
emptyStateText: string = "No match found";
|
||||
limit: number = 100;
|
||||
shouldNotOpen: boolean;
|
||||
constructor(app: App, inputEl: HTMLInputElement, items: T[]) {
|
||||
super(app);
|
||||
this.inputEl = inputEl;
|
||||
this.items = items;
|
||||
|
||||
this.suggestEl = createDiv("suggestion-container");
|
||||
|
||||
this.contentEl = this.suggestEl.createDiv("suggestion");
|
||||
|
||||
this.suggester = new Suggester(this, this.contentEl, this.scope);
|
||||
|
||||
this.scope.register([], "Escape", this.onEscape.bind(this));
|
||||
|
||||
this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
|
||||
this.inputEl.addEventListener("focus", this.onFocus.bind(this));
|
||||
this.inputEl.addEventListener("blur", this.close.bind(this));
|
||||
this.suggestEl.on(
|
||||
"mousedown",
|
||||
".suggestion-container",
|
||||
(event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
}
|
||||
);
|
||||
}
|
||||
empty() {
|
||||
this.suggester.empty();
|
||||
}
|
||||
onInputChanged(): void {
|
||||
if (this.shouldNotOpen) return;
|
||||
const inputStr = this.modifyInput(this.inputEl.value);
|
||||
const suggestions = this.getSuggestions(inputStr);
|
||||
if (suggestions.length > 0) {
|
||||
this.suggester.setSuggestions(suggestions.slice(0, this.limit));
|
||||
} else {
|
||||
this.onNoSuggestion();
|
||||
}
|
||||
this.open();
|
||||
}
|
||||
onFocus(): void {
|
||||
this.shouldNotOpen = false;
|
||||
this.onInputChanged();
|
||||
}
|
||||
modifyInput(input: string): string {
|
||||
return input;
|
||||
}
|
||||
onNoSuggestion() {
|
||||
this.empty();
|
||||
this.renderSuggestion(
|
||||
null,
|
||||
this.contentEl.createDiv("suggestion-item")
|
||||
);
|
||||
}
|
||||
open(): void {
|
||||
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
|
||||
this.app.keymap.pushScope(this.scope);
|
||||
|
||||
document.body.appendChild(this.suggestEl);
|
||||
this.popper = createPopper(this.inputEl, this.suggestEl, {
|
||||
placement: "bottom-start",
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, 10]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "flip",
|
||||
options: {
|
||||
fallbackPlacements: ["top"]
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
onEscape(): void {
|
||||
this.close();
|
||||
this.shouldNotOpen = true;
|
||||
}
|
||||
close(): void {
|
||||
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
|
||||
this.app.keymap.popScope(this.scope);
|
||||
|
||||
this.suggester.setSuggestions([]);
|
||||
if (this.popper) {
|
||||
this.popper.destroy();
|
||||
}
|
||||
|
||||
this.suggestEl.detach();
|
||||
}
|
||||
createPrompt(prompts: HTMLSpanElement[]) {
|
||||
if (!this.promptEl)
|
||||
this.promptEl = this.suggestEl.createDiv("prompt-instructions");
|
||||
let prompt = this.promptEl.createDiv("prompt-instruction");
|
||||
for (let p of prompts) {
|
||||
prompt.appendChild(p);
|
||||
}
|
||||
}
|
||||
abstract onChooseItem(item: T, evt: MouseEvent | KeyboardEvent): void;
|
||||
abstract getItemText(arg: T): string;
|
||||
abstract getItems(): T[];
|
||||
}
|
||||
|
||||
export class PathSuggestionModal extends SuggestionModal<
|
||||
TFile | BlockCache | HeadingCache
|
||||
> {
|
||||
file: TFile;
|
||||
files: TFile[];
|
||||
text: TextComponent;
|
||||
cache: CachedMetadata;
|
||||
constructor(app: App, input: TextComponent, items: TFile[]) {
|
||||
super(app, input.inputEl, items);
|
||||
this.files = [...items];
|
||||
this.text = input;
|
||||
//this.getFile();
|
||||
|
||||
|
||||
this.inputEl.addEventListener("input", this.getFile.bind(this));
|
||||
}
|
||||
|
||||
getFile() {
|
||||
const v = this.inputEl.value,
|
||||
file = this.app.metadataCache.getFirstLinkpathDest(
|
||||
v.split(/[\^#]/).shift() || "",
|
||||
""
|
||||
);
|
||||
if (file == this.file) return;
|
||||
this.file = file;
|
||||
if (this.file)
|
||||
this.cache = this.app.metadataCache.getFileCache(this.file);
|
||||
this.onInputChanged();
|
||||
}
|
||||
getItemText(item: TFile | HeadingCache | BlockCache) {
|
||||
if (item instanceof TFile) return item.path;
|
||||
if (Object.prototype.hasOwnProperty.call(item, "heading")) {
|
||||
return (<HeadingCache>item).heading;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(item, "id")) {
|
||||
return (<BlockCache>item).id;
|
||||
}
|
||||
}
|
||||
onChooseItem(item: TFile | HeadingCache | BlockCache) {
|
||||
if (item instanceof TFile) {
|
||||
this.text.setValue(item.basename);
|
||||
this.file = item;
|
||||
this.cache = this.app.metadataCache.getFileCache(this.file);
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
|
||||
this.text.setValue(
|
||||
this.file.basename + "#" + (<HeadingCache>item).heading
|
||||
);
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
|
||||
this.text.setValue(
|
||||
this.file.basename + "^" + (<BlockCache>item).id
|
||||
);
|
||||
}
|
||||
}
|
||||
selectSuggestion({ item }: FuzzyMatch<TFile | BlockCache | HeadingCache>) {
|
||||
let link: string;
|
||||
if (item instanceof TFile) {
|
||||
link = item.basename;
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
|
||||
link = this.file.basename + "#" + (<HeadingCache>item).heading;
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
|
||||
link = this.file.basename + "^" + (<BlockCache>item).id;
|
||||
}
|
||||
|
||||
this.text.setValue(link);
|
||||
this.onClose();
|
||||
|
||||
this.close();
|
||||
}
|
||||
renderSuggestion(
|
||||
result: FuzzyMatch<TFile | BlockCache | HeadingCache>,
|
||||
el: HTMLElement
|
||||
) {
|
||||
let { item, match: matches } = result || {};
|
||||
let content = el.createDiv({
|
||||
cls: "suggestion-content"
|
||||
});
|
||||
if (!item) {
|
||||
content.setText(this.emptyStateText);
|
||||
content.parentElement.addClass("is-selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (item instanceof TFile) {
|
||||
let pathLength = item.path.length - item.name.length;
|
||||
const matchElements = matches.matches.map((m) => {
|
||||
return createSpan("suggestion-highlight");
|
||||
});
|
||||
for (
|
||||
let i = pathLength;
|
||||
i < item.path.length - item.extension.length - 1;
|
||||
i++
|
||||
) {
|
||||
let match = matches.matches.find((m) => m[0] === i);
|
||||
if (match) {
|
||||
let element = matchElements[matches.matches.indexOf(match)];
|
||||
content.appendChild(element);
|
||||
element.appendText(item.path.substring(match[0], match[1]));
|
||||
|
||||
i += match[1] - match[0] - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
content.appendText(item.path[i]);
|
||||
}
|
||||
el.createDiv({
|
||||
cls: "suggestion-note",
|
||||
text: item.path
|
||||
});
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
|
||||
content.setText((<HeadingCache>item).heading);
|
||||
content.prepend(
|
||||
createSpan({
|
||||
cls: "suggestion-flair",
|
||||
text: `H${(<HeadingCache>item).level}`
|
||||
})
|
||||
);
|
||||
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
|
||||
content.setText((<BlockCache>item).id);
|
||||
}
|
||||
}
|
||||
get headings() {
|
||||
if (!this.file) return [];
|
||||
if (!this.cache) {
|
||||
this.cache = this.app.metadataCache.getFileCache(this.file);
|
||||
}
|
||||
return this.cache.headings || [];
|
||||
}
|
||||
get blocks() {
|
||||
if (!this.file) return [];
|
||||
if (!this.cache) {
|
||||
this.cache = this.app.metadataCache.getFileCache(this.file);
|
||||
}
|
||||
return Object.values(this.cache.blocks || {}) || [];
|
||||
}
|
||||
getItems() {
|
||||
const v = this.inputEl.value;
|
||||
if (/#/.test(v)) {
|
||||
this.modifyInput = (i) => i.split(/#/).pop();
|
||||
return this.headings;
|
||||
} else if (/\^/.test(v)) {
|
||||
this.modifyInput = (i) => i.split(/\^/).pop();
|
||||
return this.blocks;
|
||||
}
|
||||
return this.files;
|
||||
}
|
||||
}
|
||||
|
||||
export class FolderSuggestionModal extends SuggestionModal<TFolder> {
|
||||
text: TextComponent;
|
||||
cache: CachedMetadata;
|
||||
folders: TFolder[];
|
||||
folder: TFolder;
|
||||
constructor(app: App, input: TextComponent, items: TFolder[]) {
|
||||
super(app, input.inputEl, items);
|
||||
this.folders = [...items];
|
||||
this.text = input;
|
||||
|
||||
this.inputEl.addEventListener("input", () => this.getFolder());
|
||||
}
|
||||
getFolder() {
|
||||
const v = this.inputEl.value,
|
||||
folder = this.app.vault.getAbstractFileByPath(v);
|
||||
if (folder == this.folder) return;
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
this.folder = folder;
|
||||
|
||||
this.onInputChanged();
|
||||
}
|
||||
getItemText(item: TFolder) {
|
||||
return item.path;
|
||||
}
|
||||
onChooseItem(item: TFolder) {
|
||||
this.text.setValue(item.path);
|
||||
this.folder = item;
|
||||
}
|
||||
selectSuggestion({ item }: FuzzyMatch<TFolder>) {
|
||||
let link = item.path;
|
||||
|
||||
this.text.setValue(link);
|
||||
this.onClose();
|
||||
|
||||
this.close();
|
||||
}
|
||||
renderSuggestion(result: FuzzyMatch<TFolder>, el: HTMLElement) {
|
||||
let { item, match: matches } = result || {};
|
||||
let content = el.createDiv({
|
||||
cls: "suggestion-content"
|
||||
});
|
||||
if (!item) {
|
||||
content.setText(this.emptyStateText);
|
||||
content.parentElement.addClass("is-selected");
|
||||
return;
|
||||
}
|
||||
|
||||
let pathLength = item.path.length - item.name.length;
|
||||
const matchElements = matches.matches.map((m) => {
|
||||
return createSpan("suggestion-highlight");
|
||||
});
|
||||
for (let i = pathLength; i < item.path.length; i++) {
|
||||
let match = matches.matches.find((m) => m[0] === i);
|
||||
if (match) {
|
||||
let element = matchElements[matches.matches.indexOf(match)];
|
||||
content.appendChild(element);
|
||||
element.appendText(item.path.substring(match[0], match[1]));
|
||||
|
||||
i += match[1] - match[0] - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
content.appendText(item.path[i]);
|
||||
}
|
||||
el.createDiv({
|
||||
cls: "suggestion-note",
|
||||
text: item.path
|
||||
});
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return this.folders;
|
||||
}
|
||||
}
|
||||
132
src/Prompt.ts
132
src/Prompt.ts
@@ -6,7 +6,11 @@ import {
|
||||
FuzzyMatch,
|
||||
FuzzySuggestModal,
|
||||
Instruction,
|
||||
TFile,
|
||||
} from "obsidian";
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { getNewOrAdjacentLeaf } from "./Utils";
|
||||
|
||||
export class Prompt extends Modal {
|
||||
private promptEl: HTMLInputElement;
|
||||
@@ -281,3 +285,131 @@ export class GenericSuggester extends FuzzySuggestModal<any> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MigrationPrompt extends Modal {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
|
||||
constructor(app: App, plugin: ExcalidrawPlugin) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.titleEl.setText("Welcome to Excalidraw 1.2");
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
const div = this.contentEl.createDiv();
|
||||
// div.addClass("excalidraw-prompt-div");
|
||||
// div.style.maxWidth = "600px";
|
||||
div.createEl("p", {
|
||||
text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more.",
|
||||
});
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
el.innerHTML =
|
||||
"Drawings you've created with version 1.1.x need to be converted to take advantage of the new features. You can also continue to use them in compatibility mode. " +
|
||||
"During conversion your old *.excalidraw files will be replaced with new *.excalidraw.md files.";
|
||||
});
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
//files manually follow one of two options:
|
||||
el.innerHTML =
|
||||
"To convert your drawings you have the following options:<br><ul>" +
|
||||
"<li>Click <code>CONVERT FILES</code> now to convert all of your *.excalidraw files, or if you prefer to make a backup first, then click <code>CANCEL</code>.</li>" +
|
||||
"<li>In the Command Palette select <code>Excalidraw: Convert *.excalidraw files to *.excalidraw.md files</code></li>" +
|
||||
"<li>Right click an <code>*.excalidraw</code> file in File Explorer and select one of the following options to convert files one by one: <ul>" +
|
||||
"<li><code>*.excalidraw => *.excalidraw.md</code></li>" +
|
||||
"<li><code>*.excalidraw => *.md (Logseq compatibility)</code>. This option will retain the original *.excalidraw file next to the new Obsidian format. " +
|
||||
"Make sure you also enable <code>Compatibility features</code> in Settings for a full solution.</li></ul></li>" +
|
||||
"<li>Open a drawing in compatibility mode and select <code>Convert to new format</code> from the <code>Options Menu</code></li></ul>";
|
||||
});
|
||||
div.createEl("p", {
|
||||
text: "This message will only appear maximum 3 times in case you have *.excalidraw files in your Vault.",
|
||||
});
|
||||
const bConvert = div.createEl("button", { text: "CONVERT FILES" });
|
||||
bConvert.onclick = () => {
|
||||
this.plugin.convertExcalidrawToMD();
|
||||
this.close();
|
||||
};
|
||||
const bCancel = div.createEl("button", { text: "CANCEL" });
|
||||
bCancel.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class NewFileActions extends Modal {
|
||||
constructor (
|
||||
private plugin: ExcalidrawPlugin,
|
||||
private path: string,
|
||||
private newPane: boolean,
|
||||
private view: ExcalidrawView,
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
}
|
||||
|
||||
openFile(file: TFile): void {
|
||||
if(!file) return;
|
||||
const leaf = this.newPane
|
||||
? getNewOrAdjacentLeaf(this.plugin, this.view.leaf)
|
||||
: this.view.leaf;
|
||||
leaf.openFile(file);
|
||||
this.app.workspace.setActiveLeaf(leaf, true, true);
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
this.titleEl.setText("New File");
|
||||
|
||||
this.contentEl.createDiv({
|
||||
cls: "excalidraw-prompt-center",
|
||||
text: "File does not exist. Do you want to create it?"
|
||||
});
|
||||
this.contentEl.createDiv({
|
||||
cls: "excalidraw-prompt-center filepath",
|
||||
text: this.path
|
||||
});
|
||||
|
||||
this.contentEl.createDiv({cls: "excalidraw-prompt-center"}, (el) => {
|
||||
//files manually follow one of two options:
|
||||
el.style.textAlign = "right";
|
||||
|
||||
const bMd = el.createEl("button", { text: "Create Markdown" });
|
||||
bMd.onclick = async () => {
|
||||
//@ts-ignore
|
||||
const f = await this.app.fileManager.createNewMarkdownFileFromLinktext(this.path,this.viewFile);
|
||||
this.openFile(f);
|
||||
this.close();
|
||||
};
|
||||
|
||||
|
||||
const bEx = el.createEl("button", { text: "Create Excalidraw" });
|
||||
bEx.onclick = async () => {
|
||||
//@ts-ignore
|
||||
const f = await this.app.fileManager.createNewMarkdownFileFromLinktext(this.path,this.viewFile)
|
||||
if(!f) return;
|
||||
await this.app.vault.modify(f,await this.plugin.getBlankDrawing());
|
||||
await new Promise(r => setTimeout(r, 200)); //wait for metadata cache to update, so file opens as excalidraw
|
||||
this.openFile(f);
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bCancel = el.createEl("button", {
|
||||
text: "Never Mind",
|
||||
});
|
||||
bCancel.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ type SuggestorInfo = {
|
||||
field: string,
|
||||
code: string,
|
||||
desc: string,
|
||||
after: string,
|
||||
alt: boolean
|
||||
after: string
|
||||
}
|
||||
|
||||
export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [
|
||||
@@ -14,504 +13,438 @@ export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [
|
||||
code: null,
|
||||
desc: "The ExcalidrawPlugin object",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "elementsDict",
|
||||
code: null,
|
||||
desc: "The {} dictionary object, contains the ExcalidrawElements currently edited in Automate indexed by el.id",
|
||||
after: '[""]',
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "imagesDict",
|
||||
code: null,
|
||||
desc: "the images files including DataURL, indexed by fileId",
|
||||
after: '[""]',
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.strokeColor",
|
||||
code: "[string]",
|
||||
desc: "A valid css color. See <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.backgroundColor",
|
||||
code: "[string]",
|
||||
desc: "A valid css color. See <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.angle",
|
||||
code: "[number]",
|
||||
desc: "Rotation of the object in radian",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.fillStyle",
|
||||
code: "[string]",
|
||||
desc: "'hachure' | 'cross-hatch' | 'solid'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.strokeWidth",
|
||||
code: "[number]",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.strokeStyle",
|
||||
code: "[string]",
|
||||
desc: "'solid' | 'dashed' | 'dotted'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.roughness",
|
||||
code: "[number]",
|
||||
desc: "0:Architect\n1:Artist\n2:Cartoonist",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.opacity",
|
||||
code: "[number]",
|
||||
desc: "100: Fully opaque\n0: Fully transparent",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.strokeSharpness",
|
||||
code: "[string]",
|
||||
desc: "'round' | 'sharp'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.fontFamily",
|
||||
code: "[number]",
|
||||
desc: "1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.fontSize",
|
||||
code: "[number]",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.textAlign",
|
||||
code: "[string]",
|
||||
desc: "'left' | 'right' | 'center'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.verticalAlign",
|
||||
code: "[string]",
|
||||
desc: "For future use, has no effect currently; 'top' | 'bottom' | 'middle'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.startArrowHead",
|
||||
code: "[string]",
|
||||
desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "style.endArrowHead",
|
||||
code: "[string]",
|
||||
desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "canvas.theme",
|
||||
code: "[string]",
|
||||
desc: "'dark' | 'light'",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "canvas.viewBackgroundColor",
|
||||
code: "[string]",
|
||||
desc: "A valid css color.\nSee <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "canvas.gridSize",
|
||||
code: "[number]",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addToGroup",
|
||||
code: "addToGroup(objectIds: []): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "toCliboard",
|
||||
code: "toClipboard(templatePath?: string): void;",
|
||||
desc: "Copies current elements using template to clipboard, ready to be pasted into an excalidraw canvas",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getElements",
|
||||
code: "getElements(): ExcalidrawElement[];",
|
||||
desc: "Get all elements from ExcalidrawAutomate elementsDict",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getElement",
|
||||
code: "getElement(id: string): ExcalidrawElement;",
|
||||
desc: "Get single element from ExcalidrawAutomate elementsDict",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "create",
|
||||
code: 'create(params?: {filename?: string, foldername?: string, templatePath?: string, onNewPane?: boolean, frontmatterKeys?: { "excalidraw-plugin"?: "raw" | "parsed", "excalidraw-link-prefix"?: string, "excalidraw-link-brackets"?: boolean, "excalidraw-url-prefix"?: string,},}): Promise<string>;',
|
||||
desc: 'Create a drawing and save it to filename.\nIf filename is null: default filename as defined in Excalidraw settings.\nIf folder is null: default folder as defined in Excalidraw settings\n',
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "createSVG",
|
||||
code: "createSVG(templatePath?: string, embedFont?: boolean, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise<SVGSVGElement>;",
|
||||
desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "createPNG",
|
||||
code: "createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise<any>;",
|
||||
desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "wrapText",
|
||||
code: "wrapText(text: string, lineLen: number): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addRect",
|
||||
code: "addRect(topX: number, topY: number, width: number, height: number): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addDiamond",
|
||||
code: "addDiamond(topX: number, topY: number, width: number, height: number): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addEllipse",
|
||||
code: "addEllipse(topX: number, topY: number, width: number, height: number): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addBlob",
|
||||
code: "addBlob(topX: number, topY: number, width: number, height: number): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addText",
|
||||
code: 'addText(topX: number, topY: number, text: string, formatting?: {wrapAt?: number; width?: number; height?: number; textAlign?: string; box?: boolean | "box" | "blob" | "ellipse" | "diamond"; boxPadding?: number;}, id?: string,): string;',
|
||||
desc: 'If box is !null, then text will be boxed\nThe function returns the id of the TextElement. If the text element is boxed i.e. it is a sticky note, then the id of the container object',
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addLine",
|
||||
code: "addLine(points: [[x: number, y: number]]): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addArrow",
|
||||
code: "addArrow(points: [[x: number, y: number]], formatting?: { startArrowHead?: string; endArrowHead?: string; startObjectId?: string; endObjectId?: string;},): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addImage",
|
||||
code: "addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addLaTex",
|
||||
code: "addLaTex(topX: number, topY: number, tex: string): Promise<string>;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "connectObjects",
|
||||
code: 'connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): void;',
|
||||
desc: 'type ConnectionPoint = "top" | "bottom" | "left" | "right" | null\nWhen null is passed as ConnectionPoint then Excalidraw will automatically decide\nnumberOfPoints is the number of points on the line. Default is 0 i.e. line will only have a start and end point.\nArrowHead: "triangle"|"dot"|"arrow"|"bar"|null',
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "clear",
|
||||
code: "clear(): void;",
|
||||
desc: "Clears elementsDict and imagesDict only",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "reset",
|
||||
code: "reset(): void;",
|
||||
desc: "clear() + reset all style values to default",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "isExcalidrawFile",
|
||||
code: "isExcalidrawFile(f: TFile): boolean;",
|
||||
desc: "Returns true if MD file is an Excalidraw file",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "targetView",
|
||||
code: "targetView: ExcalidrawView;",
|
||||
desc: "The Obsidian view currently edited",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "setView",
|
||||
code: 'setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;',
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getExcalidrawAPI",
|
||||
code: "getExcalidrawAPI(): any;",
|
||||
desc: "<a onclick='window.open(\"https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref\")'>Excalidraw API</a>",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getViewElements",
|
||||
code: "getViewElements(): ExcalidrawElement[];",
|
||||
desc: "Get elements in View",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "deleteViewElements",
|
||||
code: "deleteViewElements(el: ExcalidrawElement[]): boolean;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getViewSelectedElement",
|
||||
code: "getViewSelectedElement(): ExcalidrawElement;",
|
||||
desc: "Get the selected element in the view, if more are selected, get the first",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getViewSelectedElements",
|
||||
code: "getViewSelectedElements(): ExcalidrawElement[];",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getViewFileForImageElement",
|
||||
code: "getViewFileForImageElement(el: ExcalidrawElement): TFile | null;",
|
||||
desc: "Returns the TFile file handle for the image element",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "copyViewElementsToEAforEditing",
|
||||
code: "copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void;",
|
||||
desc: "Copies elements from view to elementsDict for editing",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "viewToggleFullScreen",
|
||||
code: "viewToggleFullScreen(forceViewMode?: boolean): void;",
|
||||
desc: null,
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "connectObjectWithViewSelectedElement",
|
||||
code: "connectObjectWithViewSelectedElement(objectA: string, connectionA: ConnectionPoint, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): boolean;",
|
||||
desc: "Connect an object to the selected element in the view\nSee tooltip for connectObjects for details",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "addElementsToView",
|
||||
code: "addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean,): Promise<boolean>;",
|
||||
desc: "Adds elements from elementsDict to the current view\nrepositionToCursor: default is false\nsave: default is true\nnewElementsOnTop: default is false, i.e. the new elements get to the bottom of the stack\nnewElementsOnTop controls whether elements created with ExcalidrawAutomate are added at the bottom of the stack or the top of the stack of elements already in the view\nNote that elements copied to the view with copyViewElementsToEAforEditing retain their position in the stack of elements in the view even if modified using EA",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "onDropHook",
|
||||
code: 'onDropHook(data: {ea: ExcalidrawAutomate, event: React.DragEvent<HTMLDivElement>, draggable: any, type: "file" | "text" | "unknown", payload: {files: TFile[], text: string,}, excalidrawFile: TFile, view: ExcalidrawView, pointerPosition: { x: number, y: number},}): boolean;',
|
||||
desc: 'If set Excalidraw will call this function onDrop events.\nA return of true will stop the default onDrop processing in Excalidraw.\n\ndraggable is the Obsidian draggable object\nfiles is the array of dropped files\nexcalidrawFile is the file receiving the drop event\nview is the excalidraw view receiving the drop.\npointerPosition is the pointer position on canvas at the time of drop.',
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "mostRecentMarkdownSVG",
|
||||
code: "mostRecentMarkdownSVG: SVGSVGElement;",
|
||||
desc: "Markdown renderer will drop a copy of the most recent SVG here for debugging purposes",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getEmbeddedFilesLoader",
|
||||
code: "getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader;",
|
||||
desc: "Utility function to generate EmbeddedFilesLoader object",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getExportSettings",
|
||||
code: "getExportSettings(withBackground: boolean, withTheme: boolean,): ExportSettings;",
|
||||
desc: "Utility function to generate ExportSettings object",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getBoundingBox",
|
||||
code: "getBoundingBox(elements: ExcalidrawElement[]): {topX: number, topY: number, width: number, height: number,};",
|
||||
desc: "Gets the bounding box of elements. The bounding box is the box encapsulating all of the elements completely.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getMaximumGroups",
|
||||
code: "getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];",
|
||||
desc: "Elements grouped by the highest level groups",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getLargestElement",
|
||||
code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;",
|
||||
desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "intersectElementWithLine",
|
||||
code: "intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number,): Point[];",
|
||||
desc: "If gap is given, the element is inflated by this value.\nReturns 2 or 0 intersection points between line going through `a` and `b` and the `element`, in ascending order of distance from `a`.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getLargestElement",
|
||||
code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;",
|
||||
desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "activeScript",
|
||||
code: "activeScript: string;",
|
||||
desc: "Mandatory to set before calling the get and set ScriptSettings functions. Set automatically by the ScriptEngine\nSee for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "getScriptSettings",
|
||||
code: "getScriptSettings(): {};",
|
||||
desc: "Returns script settings. Saves settings in plugin settings, under the activeScript key. See for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "setScriptSettings",
|
||||
code: "setScriptSettings(settings: any): Promise<void>;",
|
||||
desc: "Sets script settings.\nSee for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "openFileInNewOrAdjacentLeaf",
|
||||
code: "openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf;",
|
||||
desc: "Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "measureText",
|
||||
code: "measureText(text: string): { width: number; height: number };",
|
||||
desc: "Measures text size based on current style settings",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "verifyMinimumPluginVersion",
|
||||
code: 'verifyMinimumPluginVersion(requiredVersion: string): boolean;',
|
||||
desc: 'Returns true if plugin version is >= than required\nrecommended use:\n<code>if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}<code>',
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "selectElementsInView",
|
||||
code: "selectElementsInView(elements: ExcalidrawElement[]):void;",
|
||||
desc: "Elements provided will be set as selected in the targetView.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "generateElementId",
|
||||
code: "generateElementId(): string;",
|
||||
desc: "Returns an 8 character long random id",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "cloneElement",
|
||||
code: "cloneElement(element: ExcalidrawElement): ExcalidrawElement;",
|
||||
desc: "Returns a clone of the element with a new element id",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "moveViewElementToZIndex",
|
||||
code: "moveViewElementToZIndex(elementId:number, newZIndex:number): void;",
|
||||
desc: "Moves the element to a specific position in the z-index",
|
||||
after: "",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -521,14 +454,12 @@ export const EXCALIDRAW_SCRIPTENGINE_INFO:SuggestorInfo[] = [
|
||||
code: "inputPrompt: (header: string, placeholder?: string, value?: string);",
|
||||
desc: "Opens a prompt that asks for an input.\nReturns a string with the input.\nYou need to await the result of inputPrompt.",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: "suggester",
|
||||
code: "suggester: (displayItems: string[], items: any[], hint?: string, instructions?:Instruction[]);",
|
||||
desc: "Opens a suggester. Displays the displayItems and returns the corresponding item from items[]\nYou need to await the result of suggester.\nIf the user cancels (ESC), suggester will return undefined\nHint and instructions are optional\n\n<code>interface Instruction {command: string;purpose: string;}</code>",
|
||||
after: "",
|
||||
alt: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -538,55 +469,47 @@ export const FRONTMATTER_KEYS_INFO:SuggestorInfo[] = [
|
||||
code: null,
|
||||
desc: "Denotes an excalidraw file. If key is not present, the file will not be recognized as an Excalidarw file. Valid values are 'parsed' and 'raw'",
|
||||
after: ": parsed",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_CUSTOM_PREFIX,
|
||||
code: null,
|
||||
desc: "Set custom prefix to denote text element containing a valid internal link. Set to empty string if you do not want to show a prefix",
|
||||
after: ': "📍"',
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
|
||||
code: null,
|
||||
desc: "Set custom prefix to denote text element containing a valid external link. Set to empty string if you do not want to show a prefix",
|
||||
after: ': "🌐"',
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
|
||||
code: null,
|
||||
desc: "Set to true, if you want to display [[square brackets]] around the links in Text Elements",
|
||||
after: ": true",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_DEFAULT_MODE,
|
||||
code: null,
|
||||
desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen",
|
||||
after: ": view",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_FONT,
|
||||
code: null,
|
||||
desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: Virgil|Cascadia|font_file_name.extension",
|
||||
after: ": Virgil",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_FONTCOLOR,
|
||||
code: null,
|
||||
desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: css-color-name|#HEXcolor|any-other-html-standard-format",
|
||||
after: ": SteelBlue",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
field: FRONTMATTER_KEY_MD_STYLE,
|
||||
code: null,
|
||||
desc: 'This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: "css-filename|css snippet"',
|
||||
after: ': ""',
|
||||
alt: true,
|
||||
},
|
||||
];
|
||||
@@ -277,8 +277,9 @@ export const viewportCoordsToSceneCoords = (
|
||||
},
|
||||
) => {
|
||||
const invScale = 1 / zoom.value;
|
||||
const x = (clientX - zoom.translation.x - offsetLeft) * invScale - scrollX;
|
||||
const y = (clientY - zoom.translation.y - offsetTop) * invScale - scrollY;
|
||||
const x = (clientX - offsetLeft) * invScale - scrollX;
|
||||
const y = (clientY - offsetTop) * invScale - scrollY;
|
||||
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
|
||||
@@ -1459,7 +1459,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
);
|
||||
}
|
||||
|
||||
private async getBlankDrawing(): Promise<string> {
|
||||
public async getBlankDrawing(): Promise<string> {
|
||||
const template = this.app.metadataCache.getFirstLinkpathDest(
|
||||
normalizePath(this.settings.templateFilePath),
|
||||
"",
|
||||
|
||||
10
styles.css
10
styles.css
@@ -134,4 +134,14 @@ li[data-testid] {
|
||||
|
||||
.excalidraw-scriptengine-install .modal {
|
||||
max-height:90%;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-center.filepath {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"1.5.24": "0.12.16",
|
||||
"1.5.28": "0.12.16",
|
||||
"1.4.2": "0.11.13"
|
||||
}
|
||||
|
||||
13
yarn.lock
13
yarn.lock
@@ -1355,6 +1355,11 @@
|
||||
"schema-utils" "^3.0.0"
|
||||
"source-map" "^0.7.3"
|
||||
|
||||
"@popperjs/core@^2.11.2":
|
||||
"integrity" "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
|
||||
"resolved" "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz"
|
||||
"version" "2.11.2"
|
||||
|
||||
"@rollup/plugin-babel@^5.2.0", "@rollup/plugin-babel@^5.3.0":
|
||||
"integrity" "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw=="
|
||||
"resolved" "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
|
||||
@@ -2122,10 +2127,10 @@
|
||||
"resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
|
||||
"version" "4.2.2"
|
||||
|
||||
"@zsviczian/excalidraw@0.10.0-obsidian-37":
|
||||
"integrity" "sha512-FssxK/xkDzsltu81aMTLkWVd0Te9EMV7H74K6GINF2M688rTk1Up5DGKIwI1fX8Zl+OobpmEBErctLLsQmb5jQ=="
|
||||
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-37.tgz"
|
||||
"version" "0.10.0-obsidian-37"
|
||||
"@zsviczian/excalidraw@0.10.0-obsidian-39":
|
||||
"integrity" "sha512-8uUby+Wzt1lVGqG6231IKWssd1uL+ERBSqscjjI/15bIDrqdatwJIm4taSHjNjiasOTo8p/OjG/s2Aulkr4uug=="
|
||||
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-39.tgz"
|
||||
"version" "0.10.0-obsidian-39"
|
||||
dependencies:
|
||||
"dotenv" "10.0.0"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user