mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48466f624d | ||
|
|
fc4fd685ba | ||
|
|
7449df6ac6 | ||
|
|
1390333c4c | ||
|
|
a9f545a1b2 | ||
|
|
f291c15bbc | ||
|
|
18821e1a67 | ||
|
|
5dd65d691c | ||
|
|
8f96dbc21d | ||
|
|
f71623f8a1 | ||
|
|
b380420cac | ||
|
|
21cccd4475 | ||
|
|
06475aea78 |
@@ -4,7 +4,9 @@ The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/),
|
||||
|
||||
## Video Walkthrough
|
||||
|
||||
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
|
||||
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
|
||||
<a href="https://youtu.be/QKnQgSjJVuc" target="_blank"><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/thumbnail-getting-started.jpg" width="300"/></a>
|
||||
|
||||
|
||||
<details><summary>10 Part (slightly outdated) Video Walkthrough</summary>
|
||||
<a href="https://youtu.be/sY4FoflGaiM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160304-7f211180-e17c-11eb-8363-c52723de1ffd.jpg" width="100" style="vertical-align: middle;"/> 1 Getting Started</a><br>
|
||||
|
||||
772
docs/API/ExcalidrawAutomate.d.ts
vendored
Normal file
772
docs/API/ExcalidrawAutomate.d.ts
vendored
Normal file
@@ -0,0 +1,772 @@
|
||||
/// <reference types="react" />
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { FillStyle, StrokeStyle, ExcalidrawElement, ExcalidrawBindableElement, FileId, NonDeletedExcalidrawElement, ExcalidrawImageElement, StrokeRoundness, RoundnessType } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { Editor, OpenViewState, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import * as obsidian_module from "obsidian";
|
||||
import ExcalidrawView, { ExportSettings } from "src/ExcalidrawView";
|
||||
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/types";
|
||||
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
|
||||
import { ConnectionPoint, DeviceType } from "src/types";
|
||||
import { ColorMaster } from "colormaster";
|
||||
import { TInput } from "colormaster/types";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
export declare class ExcalidrawAutomate {
|
||||
/**
|
||||
* Utility function that returns the Obsidian Module object.
|
||||
*/
|
||||
get obsidian(): typeof obsidian_module;
|
||||
get DEVICE(): DeviceType;
|
||||
getAttachmentFilepath(filename: string): Promise<string>;
|
||||
/**
|
||||
* Prompts the user with a dialog to select new file action.
|
||||
* - create markdown file
|
||||
* - create excalidraw file
|
||||
* - cancel action
|
||||
* The new file will be relative to this.targetView.file.path, unless parentFile is provided.
|
||||
* If shouldOpenNewFile is true, the new file will be opened in a workspace leaf.
|
||||
* targetPane control which leaf will be used for the new file.
|
||||
* Returns the TFile for the new file or null if the user cancelled the action.
|
||||
* @param newFileNameOrPath
|
||||
* @param shouldOpenNewFile
|
||||
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
|
||||
* @param parentFile
|
||||
* @returns
|
||||
*/
|
||||
newFilePrompt(newFileNameOrPath: string, shouldOpenNewFile: boolean, targetPane?: PaneTarget, parentFile?: TFile): Promise<TFile | null>;
|
||||
/**
|
||||
* Generates a new Obsidian Leaf following Excalidraw plugin settings such as open in Main Workspace or not, open in adjacent pane if avaialble, etc.
|
||||
* @param origo // the currently active leaf, the origin of the new leaf
|
||||
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
|
||||
* @returns
|
||||
*/
|
||||
getLeaf(origo: WorkspaceLeaf, targetPane?: PaneTarget): WorkspaceLeaf;
|
||||
/**
|
||||
* Returns the editor or leaf.view of the currently active embedded obsidian file.
|
||||
* If view is not provided, ea.targetView is used.
|
||||
* If the embedded file is a markdown document the function will return
|
||||
* {file:TFile, editor:Editor} otherwise it will return {view:any}. You can check view type with view.getViewType();
|
||||
* @param view
|
||||
* @returns
|
||||
*/
|
||||
getActiveEmbeddableViewOrEditor(view?: ExcalidrawView): {
|
||||
view: any;
|
||||
} | {
|
||||
file: TFile;
|
||||
editor: Editor;
|
||||
} | null;
|
||||
plugin: ExcalidrawPlugin;
|
||||
elementsDict: {
|
||||
[key: string]: any;
|
||||
};
|
||||
imagesDict: {
|
||||
[key: FileId]: any;
|
||||
};
|
||||
mostRecentMarkdownSVG: SVGSVGElement;
|
||||
style: {
|
||||
strokeColor: string;
|
||||
backgroundColor: string;
|
||||
angle: number;
|
||||
fillStyle: FillStyle;
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle;
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness?: StrokeRoundness;
|
||||
roundness: null | {
|
||||
type: RoundnessType;
|
||||
value?: number;
|
||||
};
|
||||
fontFamily: number;
|
||||
fontSize: number;
|
||||
textAlign: string;
|
||||
verticalAlign: string;
|
||||
startArrowHead: string;
|
||||
endArrowHead: string;
|
||||
};
|
||||
canvas: {
|
||||
theme: string;
|
||||
viewBackgroundColor: string;
|
||||
gridSize: number;
|
||||
};
|
||||
colorPalette: {};
|
||||
constructor(plugin: ExcalidrawPlugin, view?: ExcalidrawView);
|
||||
/**
|
||||
*
|
||||
* @returns the last recorded pointer position on the Excalidraw canvas
|
||||
*/
|
||||
getViewLastPointerPosition(): {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
getAPI(view?: ExcalidrawView): ExcalidrawAutomate;
|
||||
/**
|
||||
* @param val //0:"hachure", 1:"cross-hatch" 2:"solid"
|
||||
* @returns
|
||||
*/
|
||||
setFillStyle(val: number): "hachure" | "cross-hatch" | "solid";
|
||||
/**
|
||||
* @param val //0:"solid", 1:"dashed", 2:"dotted"
|
||||
* @returns
|
||||
*/
|
||||
setStrokeStyle(val: number): "solid" | "dashed" | "dotted";
|
||||
/**
|
||||
* @param val //0:"round", 1:"sharp"
|
||||
* @returns
|
||||
*/
|
||||
setStrokeSharpness(val: number): "round" | "sharp";
|
||||
/**
|
||||
* @param val //1: Virgil, 2:Helvetica, 3:Cascadia
|
||||
* @returns
|
||||
*/
|
||||
setFontFamily(val: number): "Virgil, Segoe UI Emoji" | "Helvetica, Segoe UI Emoji" | "Cascadia, Segoe UI Emoji" | "LocalFont";
|
||||
/**
|
||||
* @param val //0:"light", 1:"dark"
|
||||
* @returns
|
||||
*/
|
||||
setTheme(val: number): "light" | "dark";
|
||||
/**
|
||||
* @param objectIds
|
||||
* @returns
|
||||
*/
|
||||
addToGroup(objectIds: string[]): string;
|
||||
/**
|
||||
* @param templatePath
|
||||
*/
|
||||
toClipboard(templatePath?: string): Promise<void>;
|
||||
/**
|
||||
* @param file: TFile
|
||||
* @returns ExcalidrawScene
|
||||
*/
|
||||
getSceneFromFile(file: TFile): Promise<{
|
||||
elements: ExcalidrawElement[];
|
||||
appState: AppState;
|
||||
}>;
|
||||
/**
|
||||
* get all elements from ExcalidrawAutomate elementsDict
|
||||
* @returns elements from elemenetsDict
|
||||
*/
|
||||
getElements(): ExcalidrawElement[];
|
||||
/**
|
||||
* get single element from ExcalidrawAutomate elementsDict
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
getElement(id: string): ExcalidrawElement;
|
||||
/**
|
||||
* create a drawing and save it to filename
|
||||
* @param params
|
||||
* filename: if null, default filename as defined in Excalidraw settings
|
||||
* foldername: if null, default folder as defined in Excalidraw settings
|
||||
* @returns
|
||||
*/
|
||||
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;
|
||||
"excalidraw-export-transparent"?: boolean;
|
||||
"excalidraw-export-dark"?: boolean;
|
||||
"excalidraw-export-padding"?: number;
|
||||
"excalidraw-export-pngscale"?: number;
|
||||
"excalidraw-default-mode"?: "view" | "zen";
|
||||
"excalidraw-onload-script"?: string;
|
||||
"excalidraw-linkbutton-opacity"?: number;
|
||||
"excalidraw-autoexport"?: boolean;
|
||||
};
|
||||
plaintext?: string;
|
||||
}): Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param templatePath
|
||||
* @param embedFont
|
||||
* @param exportSettings use ExcalidrawAutomate.getExportSettings(boolean,boolean)
|
||||
* @param loader use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
|
||||
* @param theme
|
||||
* @returns
|
||||
*/
|
||||
createSVG(templatePath?: string, embedFont?: boolean, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string, padding?: number): Promise<SVGSVGElement>;
|
||||
/**
|
||||
*
|
||||
* @param templatePath
|
||||
* @param scale
|
||||
* @param exportSettings use ExcalidrawAutomate.getExportSettings(boolean,boolean)
|
||||
* @param loader use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
|
||||
* @param theme
|
||||
* @returns
|
||||
*/
|
||||
createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string, padding?: number): Promise<any>;
|
||||
/**
|
||||
*
|
||||
* @param text
|
||||
* @param lineLen
|
||||
* @returns
|
||||
*/
|
||||
wrapText(text: string, lineLen: number): string;
|
||||
private boxedElement;
|
||||
addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addRect(topX: number, topY: number, width: number, height: number): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addDiamond(topX: number, topY: number, width: number, height: number): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addEllipse(topX: number, topY: number, width: number, height: number): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addBlob(topX: number, topY: number, width: number, height: number): string;
|
||||
/**
|
||||
* Refresh the size of a text element to fit its contents
|
||||
* @param id - the id of the text element
|
||||
*/
|
||||
refreshTextElementSize(id: string): void;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param text
|
||||
* @param formatting
|
||||
* box: if !null, text will be boxed
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
addText(topX: number, topY: number, text: string, formatting?: {
|
||||
wrapAt?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
textAlign?: "left" | "center" | "right";
|
||||
box?: boolean | "box" | "blob" | "ellipse" | "diamond";
|
||||
boxPadding?: number;
|
||||
boxStrokeColor?: string;
|
||||
textVerticalAlign?: "top" | "middle" | "bottom";
|
||||
}, id?: string): string;
|
||||
/**
|
||||
*
|
||||
* @param points
|
||||
* @returns
|
||||
*/
|
||||
addLine(points: [[x: number, y: number]]): string;
|
||||
/**
|
||||
*
|
||||
* @param points
|
||||
* @param formatting
|
||||
* @returns
|
||||
*/
|
||||
addArrow(points: [x: number, y: number][], formatting?: {
|
||||
startArrowHead?: string;
|
||||
endArrowHead?: string;
|
||||
startObjectId?: string;
|
||||
endObjectId?: string;
|
||||
}): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param imageFile
|
||||
* @returns
|
||||
*/
|
||||
addImage(topX: number, topY: number, imageFile: TFile | string, scale?: boolean, //default is true which will scale the image to MAX_IMAGE_SIZE, false will insert image at 100% of its size
|
||||
anchor?: boolean): Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param tex
|
||||
* @returns
|
||||
*/
|
||||
addLaTex(topX: number, topY: number, tex: string): Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param objectA
|
||||
* @param connectionA type ConnectionPoint = "top" | "bottom" | "left" | "right" | null
|
||||
* @param objectB
|
||||
* @param connectionB when passed null, Excalidraw will automatically decide
|
||||
* @param formatting
|
||||
* numberOfPoints: points on the line. Default is 0 ie. line will only have a start and end point
|
||||
* startArrowHead: "triangle"|"dot"|"arrow"|"bar"|null
|
||||
* endArrowHead: "triangle"|"dot"|"arrow"|"bar"|null
|
||||
* padding:
|
||||
* @returns
|
||||
*/
|
||||
connectObjects(objectA: string, connectionA: ConnectionPoint | null, objectB: string, connectionB: ConnectionPoint | null, formatting?: {
|
||||
numberOfPoints?: number;
|
||||
startArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
|
||||
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
|
||||
padding?: number;
|
||||
}): string;
|
||||
/**
|
||||
* Adds a text label to a line or arrow. Currently only works with a straight (2 point - start & end - line)
|
||||
* @param lineId id of the line or arrow object in elementsDict
|
||||
* @param label the label text
|
||||
* @returns undefined (if unsuccessful) or the id of the new text element
|
||||
*/
|
||||
addLabelToLine(lineId: string, label: string): string;
|
||||
/**
|
||||
* clear elementsDict and imagesDict only
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* clear() + reset all style values to default
|
||||
*/
|
||||
reset(): void;
|
||||
/**
|
||||
* returns true if MD file is an Excalidraw file
|
||||
* @param f
|
||||
* @returns
|
||||
*/
|
||||
isExcalidrawFile(f: TFile): boolean;
|
||||
targetView: ExcalidrawView;
|
||||
/**
|
||||
* sets the target view for EA. All the view operations and the access to Excalidraw API will be performend on this view
|
||||
* if view is null or undefined, the function will first try setView("active"), then setView("first").
|
||||
* @param view
|
||||
* @returns targetView
|
||||
*/
|
||||
setView(view?: ExcalidrawView | "first" | "active"): ExcalidrawView;
|
||||
/**
|
||||
*
|
||||
* @returns https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
|
||||
*/
|
||||
getExcalidrawAPI(): any;
|
||||
/**
|
||||
* get elements in View
|
||||
* @returns
|
||||
*/
|
||||
getViewElements(): ExcalidrawElement[];
|
||||
/**
|
||||
*
|
||||
* @param elToDelete
|
||||
* @returns
|
||||
*/
|
||||
deleteViewElements(elToDelete: ExcalidrawElement[]): boolean;
|
||||
/**
|
||||
* get the selected element in the view, if more are selected, get the first
|
||||
* @returns
|
||||
*/
|
||||
getViewSelectedElement(): any;
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
getViewSelectedElements(): any[];
|
||||
/**
|
||||
*
|
||||
* @param el
|
||||
* @returns TFile file handle for the image element
|
||||
*/
|
||||
getViewFileForImageElement(el: ExcalidrawElement): TFile | null;
|
||||
/**
|
||||
* copies elements from view to elementsDict for editing
|
||||
* @param elements
|
||||
*/
|
||||
copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void;
|
||||
/**
|
||||
*
|
||||
* @param forceViewMode
|
||||
* @returns
|
||||
*/
|
||||
viewToggleFullScreen(forceViewMode?: boolean): void;
|
||||
setViewModeEnabled(enabled: boolean): void;
|
||||
/**
|
||||
* This function gives you a more hands on access to Excalidraw.
|
||||
* @param scene - The scene you want to load to Excalidraw
|
||||
* @param restore - Use this if the scene includes legacy excalidraw file elements that need to be converted to the latest excalidraw data format (not a typical usecase)
|
||||
* @returns
|
||||
*/
|
||||
viewUpdateScene(scene: {
|
||||
elements?: ExcalidrawElement[];
|
||||
appState?: AppState;
|
||||
files?: BinaryFileData;
|
||||
commitToHistory?: boolean;
|
||||
}, restore?: boolean): void;
|
||||
/**
|
||||
* connect an object to the selected element in the view
|
||||
* @param objectA ID of the element
|
||||
* @param connectionA
|
||||
* @param connectionB
|
||||
* @param formatting
|
||||
* @returns
|
||||
*/
|
||||
connectObjectWithViewSelectedElement(objectA: string, connectionA: ConnectionPoint | null, connectionB: ConnectionPoint | null, formatting?: {
|
||||
numberOfPoints?: number;
|
||||
startArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
|
||||
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
|
||||
padding?: number;
|
||||
}): boolean;
|
||||
/**
|
||||
* zoom tarteView to fit elements provided as input
|
||||
* elements === [] will zoom to fit the entire scene
|
||||
* selectElements toggles whether the elements should be in a selected state at the end of the operation
|
||||
* @param selectElements
|
||||
* @param elements
|
||||
*/
|
||||
viewZoomToElements(selectElements: boolean, elements: ExcalidrawElement[]): void;
|
||||
/**
|
||||
* Adds elements from elementsDict to the current view
|
||||
* @param repositionToCursor default is false
|
||||
* @param save default is true
|
||||
* @param newElementsOnTop 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
|
||||
* Note that elements copied to the view with copyViewElementsToEAforEditing retain their
|
||||
* position in the stack of elements in the view even if modified using EA
|
||||
* default is false, i.e. the new elements get to the bottom of the stack
|
||||
* @param shouldRestoreElements - restore elements - auto-corrects broken, incomplete or old elements included in the update
|
||||
* @returns
|
||||
*/
|
||||
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean, shouldRestoreElements?: boolean): Promise<boolean>;
|
||||
/**
|
||||
* Register instance of EA to use for hooks with TargetView
|
||||
* By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks.
|
||||
* Using this event you can set a different instance of Excalidraw Automate for hooks
|
||||
* @returns true if successful
|
||||
*/
|
||||
registerThisAsViewEA(): boolean;
|
||||
/**
|
||||
* Sets the targetView EA to window.ExcalidrawAutomate
|
||||
* @returns true if successful
|
||||
*/
|
||||
deregisterThisAsViewEA(): boolean;
|
||||
/**
|
||||
* If set, this callback is triggered when the user closes an Excalidraw view.
|
||||
*/
|
||||
onViewUnloadHook: (view: ExcalidrawView) => void;
|
||||
/**
|
||||
* If set, this callback is triggered, when the user changes the view mode.
|
||||
* You can use this callback in case you want to do something additional when the user switches to view mode and back.
|
||||
*/
|
||||
onViewModeChangeHook: (isViewModeEnabled: boolean, view: ExcalidrawView, ea: ExcalidrawAutomate) => void;
|
||||
/**
|
||||
* If set, this callback is triggered, when the user hovers a link in the scene.
|
||||
* You can use this callback in case you want to do something additional when the onLinkHover event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onLinkHover action you must return false, it will stop the native excalidraw onLinkHover management flow.
|
||||
*/
|
||||
onLinkHoverHook: (element: NonDeletedExcalidrawElement, linkText: string, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
|
||||
/**
|
||||
* If set, this callback is triggered, when the user clicks a link in the scene.
|
||||
* You can use this callback in case you want to do something additional when the onLinkClick event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onLinkClick action you must return false, it will stop the native excalidraw onLinkClick management flow.
|
||||
*/
|
||||
onLinkClickHook: (element: ExcalidrawElement, linkText: string, event: MouseEvent, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
|
||||
/**
|
||||
* If set, this callback is triggered, when Excalidraw receives an onDrop event.
|
||||
* You can use this callback in case you want to do something additional when the onDrop event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onDrop action you must return false, it will stop the native excalidraw onDrop management flow.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* If set, this callback is triggered, when Excalidraw receives an onPaste event.
|
||||
* You can use this callback in case you want to do something additional when the
|
||||
* onPaste event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onPaste action you must return false,
|
||||
* it will stop the native excalidraw onPaste management flow.
|
||||
*/
|
||||
onPasteHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
payload: ClipboardData;
|
||||
event: ClipboardEvent;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
pointerPosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
}) => boolean;
|
||||
/**
|
||||
* if set, this callback is triggered, when an Excalidraw file is opened
|
||||
* You can use this callback in case you want to do something additional when the file is opened.
|
||||
* This will run before the file level script defined in the `excalidraw-onload-script` frontmatter.
|
||||
*/
|
||||
onFileOpenHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
}) => Promise<void>;
|
||||
/**
|
||||
* if set, this callback is triggered, when an Excalidraw file is created
|
||||
* see also: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1124
|
||||
*/
|
||||
onFileCreateHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
}) => Promise<void>;
|
||||
/**
|
||||
* If set, this callback is triggered whenever the active canvas color changes
|
||||
*/
|
||||
onCanvasColorChangeHook: (ea: ExcalidrawAutomate, view: ExcalidrawView, //the excalidraw view
|
||||
color: string) => void;
|
||||
/**
|
||||
* utility function to generate EmbeddedFilesLoader object
|
||||
* @param isDark
|
||||
* @returns
|
||||
*/
|
||||
getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader;
|
||||
/**
|
||||
* utility function to generate ExportSettings object
|
||||
* @param withBackground
|
||||
* @param withTheme
|
||||
* @returns
|
||||
*/
|
||||
getExportSettings(withBackground: boolean, withTheme: boolean): ExportSettings;
|
||||
/**
|
||||
* get bounding box of elements
|
||||
* bounding box is the box encapsulating all of the elements completely
|
||||
* @param elements
|
||||
* @returns
|
||||
*/
|
||||
getBoundingBox(elements: ExcalidrawElement[]): {
|
||||
topX: number;
|
||||
topY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* elements grouped by the highest level groups
|
||||
* @param elements
|
||||
* @returns
|
||||
*/
|
||||
getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];
|
||||
/**
|
||||
* 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
|
||||
* @param elements
|
||||
* @returns
|
||||
*/
|
||||
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
|
||||
/**
|
||||
* @param element
|
||||
* @param a
|
||||
* @param b
|
||||
* @param gap
|
||||
* @returns 2 or 0 intersection points between line going through `a` and `b`
|
||||
* and the `element`, in ascending order of distance from `a`.
|
||||
*/
|
||||
intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number): Point[];
|
||||
/**
|
||||
* Gets the groupId for the group that contains all the elements, or null if such a group does not exist
|
||||
* @param elements
|
||||
* @returns null or the groupId
|
||||
*/
|
||||
getCommonGroupForElements(elements: ExcalidrawElement[]): string;
|
||||
/**
|
||||
* Gets all the elements from elements[] that share one or more groupIds with element.
|
||||
* @param element
|
||||
* @param elements - typically all the non-deleted elements in the scene
|
||||
* @returns
|
||||
*/
|
||||
getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
|
||||
/**
|
||||
* Gets all the elements from elements[] that are contained in the frame.
|
||||
* @param element
|
||||
* @param elements - typically all the non-deleted elements in the scene
|
||||
* @returns
|
||||
*/
|
||||
getElementsInFrame(frameElement: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
|
||||
/**
|
||||
* See OCR plugin for example on how to use scriptSettings
|
||||
* Set by the ScriptEngine
|
||||
*/
|
||||
activeScript: string;
|
||||
/**
|
||||
*
|
||||
* @returns script settings. Saves settings in plugin settings, under the activeScript key
|
||||
*/
|
||||
getScriptSettings(): {};
|
||||
/**
|
||||
* sets script settings.
|
||||
* @param settings
|
||||
* @returns
|
||||
*/
|
||||
setScriptSettings(settings: any): Promise<void>;
|
||||
/**
|
||||
* Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
|
||||
* @param file
|
||||
* @param openState - if not provided {active: true} will be used
|
||||
* @returns
|
||||
*/
|
||||
openFileInNewOrAdjacentLeaf(file: TFile, openState?: OpenViewState): WorkspaceLeaf;
|
||||
/**
|
||||
* measure text size based on current style settings
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
measureText(text: string): {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* Returns the size of the image element at 100% (i.e. the original size)
|
||||
* @param imageElement an image element from the active scene on targetView
|
||||
*/
|
||||
getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
/**
|
||||
* verifyMinimumPluginVersion returns true if plugin version is >= than required
|
||||
* recommended use:
|
||||
* if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
|
||||
* @param requiredVersion
|
||||
* @returns
|
||||
*/
|
||||
verifyMinimumPluginVersion(requiredVersion: string): boolean;
|
||||
/**
|
||||
* Check if view is instance of ExcalidrawView
|
||||
* @param view
|
||||
* @returns
|
||||
*/
|
||||
isExcalidrawView(view: any): boolean;
|
||||
/**
|
||||
* sets selection in view
|
||||
* @param elements
|
||||
* @returns
|
||||
*/
|
||||
selectElementsInView(elements: ExcalidrawElement[] | string[]): void;
|
||||
/**
|
||||
* @returns an 8 character long random id
|
||||
*/
|
||||
generateElementId(): string;
|
||||
/**
|
||||
* @param element
|
||||
* @returns a clone of the element with a new id
|
||||
*/
|
||||
cloneElement(element: ExcalidrawElement): ExcalidrawElement;
|
||||
/**
|
||||
* Moves the element to a specific position in the z-index
|
||||
*/
|
||||
moveViewElementToZIndex(elementId: number, newZIndex: number): void;
|
||||
/**
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
hexStringToRgb(color: string): number[];
|
||||
/**
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
rgbToHexString(color: number[]): string;
|
||||
/**
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
hslToRgb(color: number[]): number[];
|
||||
/**
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
rgbToHsl(color: number[]): number[];
|
||||
/**
|
||||
*
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
colorNameToHex(color: string): string;
|
||||
/**
|
||||
* https://github.com/lbragile/ColorMaster
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
getCM(color: TInput): ColorMaster;
|
||||
importSVG(svgString: string): boolean;
|
||||
}
|
||||
export declare function initExcalidrawAutomate(plugin: ExcalidrawPlugin): Promise<ExcalidrawAutomate>;
|
||||
export declare function destroyExcalidrawAutomate(): void;
|
||||
export declare function _measureText(newText: string, fontSize: number, fontFamily: number, lineHeight: number): {
|
||||
w: number;
|
||||
h: number;
|
||||
baseline: number;
|
||||
};
|
||||
export declare const generatePlaceholderDataURL: (width: number, height: number) => DataURL;
|
||||
export declare function createPNG(templatePath: string, scale: number, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any): Promise<Blob>;
|
||||
export declare function createSVG(templatePath: string, embedFont: boolean, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any, convertMarkdownLinksToObsidianURLs?: boolean): Promise<SVGSVGElement>;
|
||||
export declare function estimateBounds(elements: ExcalidrawElement[]): [number, number, number, number];
|
||||
export declare function repositionElementsToCursor(elements: ExcalidrawElement[], newPosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
}, center: boolean, api: ExcalidrawImperativeAPI): ExcalidrawElement[];
|
||||
export declare const insertLaTeXToView: (view: ExcalidrawView) => void;
|
||||
export declare const search: (view: ExcalidrawView) => Promise<void>;
|
||||
/**
|
||||
*
|
||||
* @param elements
|
||||
* @param query
|
||||
* @param exactMatch - when searching for section header exactMatch should be set to true
|
||||
* @returns the elements matching the query
|
||||
*/
|
||||
export declare const getTextElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
|
||||
/**
|
||||
*
|
||||
* @param elements
|
||||
* @param query
|
||||
* @param exactMatch - when searching for section header exactMatch should be set to true
|
||||
* @returns the elements matching the query
|
||||
*/
|
||||
export declare const getFrameElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
|
||||
export declare const cloneElement: (el: ExcalidrawElement) => any;
|
||||
export declare const verifyMinimumPluginVersion: (requiredVersion: string) => boolean;
|
||||
@@ -2,36 +2,110 @@
|
||||
## Attributes and functions overview
|
||||
Here's the interface implemented by ExcalidrawAutomate:
|
||||
|
||||
```typescript
|
||||
export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
plugin: ExcalidrawPlugin;
|
||||
targetView: ExcalidrawView = null; //the view currently edited
|
||||
elementsDict: {[key:string]:any}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
|
||||
imagesDict: {[key: FileId]: any}; //the images files including DataURL, indexed by fileId
|
||||
mostRecentMarkdownSVG:SVGSVGElement = null; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes
|
||||
style: {
|
||||
strokeColor: string; //https://www.w3schools.com/colors/default.asp
|
||||
backgroundColor: string;
|
||||
angle: number; //radian
|
||||
fillStyle: FillStyle; //type FillStyle = "hachure" | "cross-hatch" | "solid"
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
|
||||
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
|
||||
fontSize: number;
|
||||
textAlign: string; //"left"|"right"|"center"
|
||||
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
|
||||
startArrowHead: string; //"triangle"|"dot"|"arrow"|"bar"|null
|
||||
endArrowHead: string;
|
||||
};
|
||||
canvas: {
|
||||
theme: string; //"dark"|"light"
|
||||
viewBackgroundColor: string;
|
||||
gridSize: number;
|
||||
};
|
||||
You can find the source file here: [ExcalidrawAutomate.d.ts](ExcalidrawAutomate.d.ts).
|
||||
|
||||
```javascript
|
||||
/// <reference types="react" />
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { FillStyle, StrokeStyle, ExcalidrawElement, ExcalidrawBindableElement, FileId, NonDeletedExcalidrawElement, ExcalidrawImageElement, StrokeRoundness, RoundnessType } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { Editor, OpenViewState, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import * as obsidian_module from "obsidian";
|
||||
import ExcalidrawView, { ExportSettings } from "src/ExcalidrawView";
|
||||
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/types";
|
||||
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
|
||||
import { ConnectionPoint, DeviceType } from "src/types";
|
||||
import { ColorMaster } from "colormaster";
|
||||
import { TInput } from "colormaster/types";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
export declare class ExcalidrawAutomate {
|
||||
/**
|
||||
* Utility function that returns the Obsidian Module object.
|
||||
*/
|
||||
get obsidian(): typeof obsidian_module;
|
||||
get DEVICE(): DeviceType;
|
||||
getAttachmentFilepath(filename: string): Promise<string>;
|
||||
/**
|
||||
* Prompts the user with a dialog to select new file action.
|
||||
* - create markdown file
|
||||
* - create excalidraw file
|
||||
* - cancel action
|
||||
* The new file will be relative to this.targetView.file.path, unless parentFile is provided.
|
||||
* If shouldOpenNewFile is true, the new file will be opened in a workspace leaf.
|
||||
* targetPane control which leaf will be used for the new file.
|
||||
* Returns the TFile for the new file or null if the user cancelled the action.
|
||||
* @param newFileNameOrPath
|
||||
* @param shouldOpenNewFile
|
||||
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
|
||||
* @param parentFile
|
||||
* @returns
|
||||
*/
|
||||
newFilePrompt(newFileNameOrPath: string, shouldOpenNewFile: boolean, targetPane?: PaneTarget, parentFile?: TFile): Promise<TFile | null>;
|
||||
/**
|
||||
* Generates a new Obsidian Leaf following Excalidraw plugin settings such as open in Main Workspace or not, open in adjacent pane if avaialble, etc.
|
||||
* @param origo // the currently active leaf, the origin of the new leaf
|
||||
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
|
||||
* @returns
|
||||
*/
|
||||
getLeaf(origo: WorkspaceLeaf, targetPane?: PaneTarget): WorkspaceLeaf;
|
||||
/**
|
||||
* Returns the editor or leaf.view of the currently active embedded obsidian file.
|
||||
* If view is not provided, ea.targetView is used.
|
||||
* If the embedded file is a markdown document the function will return
|
||||
* {file:TFile, editor:Editor} otherwise it will return {view:any}. You can check view type with view.getViewType();
|
||||
* @param view
|
||||
* @returns
|
||||
*/
|
||||
getActiveEmbeddableViewOrEditor(view?: ExcalidrawView): {
|
||||
view: any;
|
||||
} | {
|
||||
file: TFile;
|
||||
editor: Editor;
|
||||
} | null;
|
||||
plugin: ExcalidrawPlugin;
|
||||
elementsDict: {
|
||||
[key: string]: any;
|
||||
};
|
||||
imagesDict: {
|
||||
[key: FileId]: any;
|
||||
};
|
||||
mostRecentMarkdownSVG: SVGSVGElement;
|
||||
style: {
|
||||
strokeColor: string;
|
||||
backgroundColor: string;
|
||||
angle: number;
|
||||
fillStyle: FillStyle;
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle;
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness?: StrokeRoundness;
|
||||
roundness: null | {
|
||||
type: RoundnessType;
|
||||
value?: number;
|
||||
};
|
||||
fontFamily: number;
|
||||
fontSize: number;
|
||||
textAlign: string;
|
||||
verticalAlign: string;
|
||||
startArrowHead: string;
|
||||
endArrowHead: string;
|
||||
};
|
||||
canvas: {
|
||||
theme: string;
|
||||
viewBackgroundColor: string;
|
||||
gridSize: number;
|
||||
};
|
||||
colorPalette: {};
|
||||
constructor(plugin: ExcalidrawPlugin, view?: ExcalidrawView);
|
||||
/**
|
||||
*
|
||||
* @returns the last recorded pointer position on the Excalidraw canvas
|
||||
*/
|
||||
getViewLastPointerPosition(): {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
@@ -71,6 +145,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @param templatePath
|
||||
*/
|
||||
toClipboard(templatePath?: string): Promise<void>;
|
||||
/**
|
||||
* @param file: TFile
|
||||
* @returns ExcalidrawScene
|
||||
*/
|
||||
getSceneFromFile(file: TFile): Promise<{
|
||||
elements: ExcalidrawElement[];
|
||||
appState: AppState;
|
||||
}>;
|
||||
/**
|
||||
* get all elements from ExcalidrawAutomate elementsDict
|
||||
* @returns elements from elemenetsDict
|
||||
@@ -101,10 +183,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
"excalidraw-url-prefix"?: string;
|
||||
"excalidraw-export-transparent"?: boolean;
|
||||
"excalidraw-export-dark"?: boolean;
|
||||
"excalidraw-export-svgpadding"?: number;
|
||||
"excalidraw-export-padding"?: number;
|
||||
"excalidraw-export-pngscale"?: number;
|
||||
"excalidraw-default-mode"?: "view" | "zen";
|
||||
"excalidraw-onload-script"?: string;
|
||||
"excalidraw-linkbutton-opacity"?: number;
|
||||
"excalidraw-autoexport"?: boolean;
|
||||
};
|
||||
plaintext?: string;
|
||||
}): Promise<string>;
|
||||
/**
|
||||
*
|
||||
@@ -134,6 +220,16 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
*/
|
||||
wrapText(text: string, lineLen: number): string;
|
||||
private boxedElement;
|
||||
addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
* @param topY
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns
|
||||
*/
|
||||
addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
@@ -170,6 +266,11 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
addBlob(topX: number, topY: number, width: number, height: number): string;
|
||||
/**
|
||||
* Refresh the size of a text element to fit its contents
|
||||
* @param id - the id of the text element
|
||||
*/
|
||||
refreshTextElementSize(id: string): void;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
@@ -184,9 +285,11 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
wrapAt?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
textAlign?: string;
|
||||
textAlign?: "left" | "center" | "right";
|
||||
box?: boolean | "box" | "blob" | "ellipse" | "diamond";
|
||||
boxPadding?: number;
|
||||
boxStrokeColor?: string;
|
||||
textVerticalAlign?: "top" | "middle" | "bottom";
|
||||
}, id?: string): string;
|
||||
/**
|
||||
*
|
||||
@@ -213,7 +316,8 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @param imageFile
|
||||
* @returns
|
||||
*/
|
||||
addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;
|
||||
addImage(topX: number, topY: number, imageFile: TFile | string, scale?: boolean, //default is true which will scale the image to MAX_IMAGE_SIZE, false will insert image at 100% of its size
|
||||
anchor?: boolean): Promise<string>;
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
@@ -262,12 +366,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
isExcalidrawFile(f: TFile): boolean;
|
||||
targetView: ExcalidrawView;
|
||||
/**
|
||||
*
|
||||
* sets the target view for EA. All the view operations and the access to Excalidraw API will be performend on this view
|
||||
* if view is null or undefined, the function will first try setView("active"), then setView("first").
|
||||
* @param view
|
||||
* @returns
|
||||
* @returns targetView
|
||||
*/
|
||||
setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;
|
||||
setView(view?: ExcalidrawView | "first" | "active"): ExcalidrawView;
|
||||
/**
|
||||
*
|
||||
* @returns https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
|
||||
@@ -311,6 +417,19 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
viewToggleFullScreen(forceViewMode?: boolean): void;
|
||||
setViewModeEnabled(enabled: boolean): void;
|
||||
/**
|
||||
* This function gives you a more hands on access to Excalidraw.
|
||||
* @param scene - The scene you want to load to Excalidraw
|
||||
* @param restore - Use this if the scene includes legacy excalidraw file elements that need to be converted to the latest excalidraw data format (not a typical usecase)
|
||||
* @returns
|
||||
*/
|
||||
viewUpdateScene(scene: {
|
||||
elements?: ExcalidrawElement[];
|
||||
appState?: AppState;
|
||||
files?: BinaryFileData;
|
||||
commitToHistory?: boolean;
|
||||
}, restore?: boolean): void;
|
||||
/**
|
||||
* connect an object to the selected element in the view
|
||||
* @param objectA ID of the element
|
||||
@@ -325,6 +444,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
|
||||
padding?: number;
|
||||
}): boolean;
|
||||
/**
|
||||
* zoom tarteView to fit elements provided as input
|
||||
* elements === [] will zoom to fit the entire scene
|
||||
* selectElements toggles whether the elements should be in a selected state at the end of the operation
|
||||
* @param selectElements
|
||||
* @param elements
|
||||
*/
|
||||
viewZoomToElements(selectElements: boolean, elements: ExcalidrawElement[]): void;
|
||||
/**
|
||||
* Adds elements from elementsDict to the current view
|
||||
* @param repositionToCursor default is false
|
||||
@@ -334,9 +461,10 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* Note that elements copied to the view with copyViewElementsToEAforEditing retain their
|
||||
* position in the stack of elements in the view even if modified using EA
|
||||
* default is false, i.e. the new elements get to the bottom of the stack
|
||||
* @param shouldRestoreElements - restore elements - auto-corrects broken, incomplete or old elements included in the update
|
||||
* @returns
|
||||
*/
|
||||
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean): Promise<boolean>;
|
||||
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean, shouldRestoreElements?: boolean): Promise<boolean>;
|
||||
/**
|
||||
* Register instance of EA to use for hooks with TargetView
|
||||
* By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks.
|
||||
@@ -362,7 +490,7 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* If set, this callback is triggered, when the user hovers a link in the scene.
|
||||
* You can use this callback in case you want to do something additional when the onLinkHover event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onLinkHover action you must return true, it will stop the native excalidraw onLinkHover management flow.
|
||||
* In case you want to prevent the excalidraw onLinkHover action you must return false, it will stop the native excalidraw onLinkHover management flow.
|
||||
*/
|
||||
onLinkHoverHook: (element: NonDeletedExcalidrawElement, linkText: string, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
|
||||
/**
|
||||
@@ -394,6 +522,49 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
y: number;
|
||||
};
|
||||
}) => boolean;
|
||||
/**
|
||||
* If set, this callback is triggered, when Excalidraw receives an onPaste event.
|
||||
* You can use this callback in case you want to do something additional when the
|
||||
* onPaste event occurs.
|
||||
* This callback must return a boolean value.
|
||||
* In case you want to prevent the excalidraw onPaste action you must return false,
|
||||
* it will stop the native excalidraw onPaste management flow.
|
||||
*/
|
||||
onPasteHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
payload: ClipboardData;
|
||||
event: ClipboardEvent;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
pointerPosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
}) => boolean;
|
||||
/**
|
||||
* if set, this callback is triggered, when an Excalidraw file is opened
|
||||
* You can use this callback in case you want to do something additional when the file is opened.
|
||||
* This will run before the file level script defined in the `excalidraw-onload-script` frontmatter.
|
||||
*/
|
||||
onFileOpenHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
}) => Promise<void>;
|
||||
/**
|
||||
* if set, this callback is triggered, when an Excalidraw file is created
|
||||
* see also: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1124
|
||||
*/
|
||||
onFileCreateHook: (data: {
|
||||
ea: ExcalidrawAutomate;
|
||||
excalidrawFile: TFile;
|
||||
view: ExcalidrawView;
|
||||
}) => Promise<void>;
|
||||
/**
|
||||
* If set, this callback is triggered whenever the active canvas color changes
|
||||
*/
|
||||
onCanvasColorChangeHook: (ea: ExcalidrawAutomate, view: ExcalidrawView, //the excalidraw view
|
||||
color: string) => void;
|
||||
/**
|
||||
* utility function to generate EmbeddedFilesLoader object
|
||||
* @param isDark
|
||||
@@ -431,6 +602,15 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
|
||||
/**
|
||||
* @param element
|
||||
* @param a
|
||||
* @param b
|
||||
* @param gap
|
||||
* @returns 2 or 0 intersection points between line going through `a` and `b`
|
||||
* and the `element`, in ascending order of distance from `a`.
|
||||
*/
|
||||
intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number): Point[];
|
||||
/**
|
||||
* Gets the groupId for the group that contains all the elements, or null if such a group does not exist
|
||||
* @param elements
|
||||
@@ -445,14 +625,12 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
*/
|
||||
getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
|
||||
/**
|
||||
* Gets all the elements from elements[] that are contained in the frame.
|
||||
* @param element
|
||||
* @param a
|
||||
* @param b
|
||||
* @param gap
|
||||
* @returns 2 or 0 intersection points between line going through `a` and `b`
|
||||
* and the `element`, in ascending order of distance from `a`.
|
||||
* @param elements - typically all the non-deleted elements in the scene
|
||||
* @returns
|
||||
*/
|
||||
intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number): Point[];
|
||||
getElementsInFrame(frameElement: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
|
||||
/**
|
||||
* See OCR plugin for example on how to use scriptSettings
|
||||
* Set by the ScriptEngine
|
||||
@@ -472,9 +650,10 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
/**
|
||||
* Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
|
||||
* @param file
|
||||
* @param openState - if not provided {active: true} will be used
|
||||
* @returns
|
||||
*/
|
||||
openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf;
|
||||
openFileInNewOrAdjacentLeaf(file: TFile, openState?: OpenViewState): WorkspaceLeaf;
|
||||
/**
|
||||
* measure text size based on current style settings
|
||||
* @param text
|
||||
@@ -484,6 +663,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* Returns the size of the image element at 100% (i.e. the original size)
|
||||
* @param imageElement an image element from the active scene on targetView
|
||||
*/
|
||||
getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
/**
|
||||
* verifyMinimumPluginVersion returns true if plugin version is >= than required
|
||||
* recommended use:
|
||||
@@ -503,7 +690,7 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @param elements
|
||||
* @returns
|
||||
*/
|
||||
selectElementsInView(elements: ExcalidrawElement[]): void;
|
||||
selectElementsInView(elements: ExcalidrawElement[] | string[]): void;
|
||||
/**
|
||||
* @returns an 8 character long random id
|
||||
*/
|
||||
@@ -518,25 +705,25 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
*/
|
||||
moveViewElementToZIndex(elementId: number, newZIndex: number): void;
|
||||
/**
|
||||
*
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
hexStringToRgb(color: string): number[];
|
||||
/**
|
||||
*
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
rgbToHexString(color: number[]): string;
|
||||
/**
|
||||
*
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
hslToRgb(color: number[]): number[];
|
||||
/**
|
||||
*
|
||||
* Depricated. Use getCM / ColorMaster instead
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
@@ -547,5 +734,47 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
colorNameToHex(color: string): string;
|
||||
}```
|
||||
|
||||
/**
|
||||
* https://github.com/lbragile/ColorMaster
|
||||
* @param color
|
||||
* @returns
|
||||
*/
|
||||
getCM(color: TInput): ColorMaster;
|
||||
importSVG(svgString: string): boolean;
|
||||
}
|
||||
export declare function initExcalidrawAutomate(plugin: ExcalidrawPlugin): Promise<ExcalidrawAutomate>;
|
||||
export declare function destroyExcalidrawAutomate(): void;
|
||||
export declare function _measureText(newText: string, fontSize: number, fontFamily: number, lineHeight: number): {
|
||||
w: number;
|
||||
h: number;
|
||||
baseline: number;
|
||||
};
|
||||
export declare const generatePlaceholderDataURL: (width: number, height: number) => DataURL;
|
||||
export declare function createPNG(templatePath: string, scale: number, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any): Promise<Blob>;
|
||||
export declare function createSVG(templatePath: string, embedFont: boolean, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any, convertMarkdownLinksToObsidianURLs?: boolean): Promise<SVGSVGElement>;
|
||||
export declare function estimateBounds(elements: ExcalidrawElement[]): [number, number, number, number];
|
||||
export declare function repositionElementsToCursor(elements: ExcalidrawElement[], newPosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
}, center: boolean, api: ExcalidrawImperativeAPI): ExcalidrawElement[];
|
||||
export declare const insertLaTeXToView: (view: ExcalidrawView) => void;
|
||||
export declare const search: (view: ExcalidrawView) => Promise<void>;
|
||||
/**
|
||||
*
|
||||
* @param elements
|
||||
* @param query
|
||||
* @param exactMatch - when searching for section header exactMatch should be set to true
|
||||
* @returns the elements matching the query
|
||||
*/
|
||||
export declare const getTextElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
|
||||
/**
|
||||
*
|
||||
* @param elements
|
||||
* @param query
|
||||
* @param exactMatch - when searching for section header exactMatch should be set to true
|
||||
* @returns the elements matching the query
|
||||
*/
|
||||
export declare const getFrameElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
|
||||
export declare const cloneElement: (el: ExcalidrawElement) => any;
|
||||
export declare const verifyMinimumPluginVersion: (requiredVersion: string) => boolean;
|
||||
```
|
||||
BIN
images/thumbnail-getting-started.jpg
Normal file
BIN
images/thumbnail-getting-started.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.9.19.2",
|
||||
"version": "1.9.29",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.9.24",
|
||||
"version": "1.9.28",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.16.1-obsidian-4",
|
||||
"@zsviczian/excalidraw": "0.16.1-obsidian-8",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"colormaster": "^1.2.1",
|
||||
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
svgToBase64,
|
||||
} from "./utils/Utils";
|
||||
import { ValueOf } from "./types";
|
||||
import { has } from "./svgToExcalidraw/attributes";
|
||||
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
|
||||
import { mermaidToExcalidraw } from "src/constants";
|
||||
|
||||
@@ -106,8 +105,12 @@ const replaceSVGColors = (svg: SVGSVGElement | string, colorMap: ColorMap | null
|
||||
for (const [oldColor, newColor] of Object.entries(colorMap)) {
|
||||
const fillRegex = new RegExp(`fill="${oldColor}"`, 'gi');
|
||||
svg = svg.replaceAll(fillRegex, `fill="${newColor}"`);
|
||||
const fillStyleRegex = new RegExp(`fill:${oldColor}`, 'gi');
|
||||
svg = svg.replaceAll(fillStyleRegex, `fill:${newColor}`);
|
||||
const strokeRegex = new RegExp(`stroke="${oldColor}"`, 'gi');
|
||||
svg = svg.replaceAll(strokeRegex, `stroke="${newColor}"`);
|
||||
const strokeStyleRegex = new RegExp(`stroke:${oldColor}`, 'gi');
|
||||
svg = svg.replaceAll(strokeStyleRegex, `stroke:${newColor}`);
|
||||
}
|
||||
return svg;
|
||||
}
|
||||
@@ -594,7 +597,7 @@ export class EmbeddedFilesLoader {
|
||||
continue;
|
||||
}
|
||||
const data = getMermaidText(element);
|
||||
const result = await mermaidToExcalidraw(data, {fontSize: 20});
|
||||
const result = await mermaidToExcalidraw(data, {fontSize: 20}, true);
|
||||
if(!result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import { getAttachmentsFolderAndFilePath, getLeaf, getNewOrAdjacentLeaf, isObsid
|
||||
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/types";
|
||||
import { EmbeddedFile, EmbeddedFilesLoader, FileData } from "src/EmbeddedFileLoader";
|
||||
import { tex2dataURL } from "src/LaTeX";
|
||||
import { NewFileActions, Prompt } from "src/dialogs/Prompt";
|
||||
import { GenericInputPrompt, NewFileActions, Prompt } from "src/dialogs/Prompt";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ScriptEngine } from "src/Scripts";
|
||||
import { ConnectionPoint, DeviceType } from "src/types";
|
||||
@@ -79,6 +79,7 @@ import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { emulateKeysForLinkClick, KeyEvent, PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/utility-types";
|
||||
import PolyBool from "polybooljs";
|
||||
import { compressToBase64, decompressFromBase64 } from "lz-string";
|
||||
|
||||
extendPlugins([
|
||||
HarmonyPlugin,
|
||||
@@ -108,6 +109,10 @@ export class ExcalidrawAutomate {
|
||||
return obsidian_module;
|
||||
};
|
||||
|
||||
get LASERPOINTER() {
|
||||
return this.plugin.settings.laserSettings;
|
||||
}
|
||||
|
||||
get DEVICE():DeviceType {
|
||||
return DEVICE;
|
||||
}
|
||||
@@ -121,6 +126,14 @@ export class ExcalidrawAutomate {
|
||||
return getNewUniqueFilepath(app.vault, filename, folderAndPath.folder);
|
||||
}
|
||||
|
||||
public compressToBase64(str:string):string {
|
||||
return compressToBase64(str);
|
||||
}
|
||||
|
||||
public decompressFromBase64(str:string):string {
|
||||
return decompressFromBase64(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user with a dialog to select new file action.
|
||||
* - create markdown file
|
||||
@@ -147,14 +160,14 @@ export class ExcalidrawAutomate {
|
||||
return null;
|
||||
}
|
||||
const modifierKeys = emulateKeysForLinkClick(targetPane);
|
||||
const newFilePrompt = new NewFileActions(
|
||||
this.plugin,
|
||||
newFileNameOrPath,
|
||||
modifierKeys,
|
||||
this.targetView,
|
||||
shouldOpenNewFile,
|
||||
parentFile
|
||||
)
|
||||
const newFilePrompt = new NewFileActions({
|
||||
plugin: this.plugin,
|
||||
path: newFileNameOrPath,
|
||||
keys: modifierKeys,
|
||||
view: this.targetView,
|
||||
openNewFile: shouldOpenNewFile,
|
||||
parentFile: parentFile
|
||||
})
|
||||
newFilePrompt.open();
|
||||
return await newFilePrompt.waitForClose;
|
||||
}
|
||||
@@ -1543,11 +1556,7 @@ export class ExcalidrawAutomate {
|
||||
errorMessage("targetView not set", "getViewElements()");
|
||||
return [];
|
||||
}
|
||||
const api = this.targetView.excalidrawAPI;
|
||||
if (!api) {
|
||||
return [];
|
||||
}
|
||||
return api.getSceneElements();
|
||||
return this.targetView.getViewElements();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2590,12 +2599,12 @@ export async function createPNG(
|
||||
);
|
||||
}
|
||||
|
||||
const updateElementLinksToObsidianLinks = ({elements, hostFile}:{
|
||||
export const updateElementLinksToObsidianLinks = ({elements, hostFile}:{
|
||||
elements: ExcalidrawElement[];
|
||||
hostFile: TFile;
|
||||
}): ExcalidrawElement[] => {
|
||||
return elements.map((el)=>{
|
||||
if(el.type!=="embeddable" && el.link && el.link.startsWith("[")) {
|
||||
if(el.link && el.link.startsWith("[")) {
|
||||
const partsArray = REGEX_LINK.getResList(el.link)[0];
|
||||
if(!partsArray?.value) return el;
|
||||
let linkText = REGEX_LINK.getLink(partsArray);
|
||||
@@ -2683,6 +2692,7 @@ export async function createSVG(
|
||||
withTheme,
|
||||
},
|
||||
padding,
|
||||
null,
|
||||
);
|
||||
|
||||
if (withTheme && theme === "dark") addFilterToForeignObjects(svg);
|
||||
@@ -2791,13 +2801,16 @@ function errorMessage(message: string, source: string) {
|
||||
export const insertLaTeXToView = (view: ExcalidrawView) => {
|
||||
const app = view.plugin.app;
|
||||
const ea = view.plugin.ea;
|
||||
const prompt = new Prompt(
|
||||
GenericInputPrompt.Prompt(
|
||||
view,
|
||||
view.plugin,
|
||||
app,
|
||||
t("ENTER_LATEX"),
|
||||
view.plugin.settings.latexBoilerplate,
|
||||
"\\color{red}\\oint_S {E_n dA = \\frac{1}{{\\varepsilon _0 }}} Q_{inside}",
|
||||
);
|
||||
prompt.openAndGetValue(async (formula: string) => {
|
||||
view.plugin.settings.latexBoilerplate,
|
||||
undefined,
|
||||
3
|
||||
).then(async (formula: string) => {
|
||||
if (!formula) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -293,8 +293,18 @@ export class ExcalidrawData {
|
||||
|
||||
if (el.boundElements) {
|
||||
const map = new Map<string, string>();
|
||||
let alreadyHasText:boolean = false;
|
||||
el.boundElements.forEach((item: { id: string; type: string }) => {
|
||||
map.set(item.id, item.type);
|
||||
if(item.type === "text") {
|
||||
if(!alreadyHasText) {
|
||||
map.set(item.id, item.type);
|
||||
alreadyHasText = true;
|
||||
} else {
|
||||
elements.find((el:ExcalidrawElement)=>el.id===item.id).containerId = null;
|
||||
}
|
||||
} else {
|
||||
map.set(item.id, item.type);
|
||||
}
|
||||
});
|
||||
const boundElements = Array.from(map, ([id, type]) => ({ id, type }));
|
||||
if (boundElements.length !== el.boundElements.length) {
|
||||
@@ -616,7 +626,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
|
||||
//Load Equations
|
||||
const REG_FILEID_EQUATION = /([\w\d]*):\s*\$\$(.*)(\$\$\s*\n)/gm;
|
||||
const REG_FILEID_EQUATION = /([\w\d]*):\s*\$\$([\s\S]*?)(\$\$\s*\n)/gm;
|
||||
res = data.matchAll(REG_FILEID_EQUATION);
|
||||
while (!(parts = res.next()).done) {
|
||||
this.setEquation(parts.value[1] as FileId, {
|
||||
@@ -1139,7 +1149,7 @@ export class ExcalidrawData {
|
||||
} else {
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/829
|
||||
const path = ef.file
|
||||
? ef.linkParts.original.replace(PATHREG,app.metadataCache.fileToLinktext(ef.file,this.file.path))
|
||||
? ef.linkParts.original.replace(PATHREG,this.app.metadataCache.fileToLinktext(ef.file,this.file.path))
|
||||
: ef.linkParts.original;
|
||||
const colorMap = ef.colorMap ? " " + JSON.stringify(ef.colorMap) : "";
|
||||
outString += `${key}: [[${path}]]${colorMap}\n`;
|
||||
|
||||
1
src/ExcalidrawLib.d.ts
vendored
1
src/ExcalidrawLib.d.ts
vendored
@@ -129,6 +129,7 @@ declare namespace ExcalidrawLib {
|
||||
function mermaidToExcalidraw(
|
||||
mermaidDefinition: string,
|
||||
opts: {fontSize: number},
|
||||
forceSVG?: boolean,
|
||||
): Promise<{
|
||||
elements: ExcalidrawElement[],
|
||||
files:any
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,11 +19,13 @@ import {
|
||||
getWithBackground,
|
||||
hasExportTheme,
|
||||
convertSVGStringToElement,
|
||||
getFileCSSClasses,
|
||||
} from "./utils/Utils";
|
||||
import { getParentOfClass, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
import { linkClickModifierType } from "./utils/ModifierkeyHelper";
|
||||
import { ImageKey, imageCache } from "./utils/ImageCache";
|
||||
import { FILENAMEPARTS, PreviewImageType } from "./utils/UtilTypes";
|
||||
import { css } from "chroma-js";
|
||||
|
||||
interface imgElementAttributes {
|
||||
file?: TFile;
|
||||
@@ -399,6 +401,8 @@ const createImgElement = async (
|
||||
newImg.setAttribute("fileSource",fileSource);
|
||||
parent.append(newImg);
|
||||
});
|
||||
const cssClasses = getFileCSSClasses(plugin, attr.file);
|
||||
cssClasses.forEach((cssClass) => imgOrDiv.addClass(cssClass));
|
||||
return imgOrDiv;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import ExcalidrawPlugin from "./main";
|
||||
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./dialogs/Prompt";
|
||||
import { getIMGFilename } from "./utils/FileUtils";
|
||||
import { splitFolderAndFilename } from "./utils/FileUtils";
|
||||
import { getEA } from "src";
|
||||
|
||||
export type ScriptIconMap = {
|
||||
[key: string]: { name: string; group: string; svgString: string };
|
||||
@@ -199,27 +200,26 @@ export class ScriptEngine {
|
||||
|
||||
const commandId = `${PLUGIN_ID}:${basename}`;
|
||||
// @ts-ignore
|
||||
if (!app.commands.commands[commandId]) {
|
||||
if (!this.plugin.app.commands.commands[commandId]) {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
delete app.commands.commands[commandId];
|
||||
delete this.plugin.app.commands.commands[commandId];
|
||||
}
|
||||
|
||||
async executeScript(view: ExcalidrawView, script: string, title: string, file: TFile) {
|
||||
if (!view || !script || !title) {
|
||||
return;
|
||||
}
|
||||
this.plugin.ea.reset();
|
||||
this.plugin.ea.setView(view);
|
||||
this.plugin.ea.activeScript = title;
|
||||
const ea = getEA(view);
|
||||
ea.activeScript = title;
|
||||
|
||||
//https://stackoverflow.com/questions/45381204/get-asyncfunction-constructor-in-typescript changed tsconfig to es2017
|
||||
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
|
||||
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
|
||||
let result = null;
|
||||
//try {
|
||||
result = await new AsyncFunction("ea", "utils", script)(this.plugin.ea, {
|
||||
result = await new AsyncFunction("ea", "utils", script)(ea, {
|
||||
inputPrompt: (
|
||||
header: string,
|
||||
placeholder?: string,
|
||||
@@ -233,7 +233,7 @@ export class ScriptEngine {
|
||||
ScriptEngine.inputPrompt(
|
||||
view,
|
||||
this.plugin,
|
||||
app,
|
||||
this.plugin.app,
|
||||
header,
|
||||
placeholder,
|
||||
value,
|
||||
@@ -262,7 +262,7 @@ export class ScriptEngine {
|
||||
new Notice(t("SCRIPT_EXECUTION_ERROR"), 4000);
|
||||
errorlog({ script: this.plugin.ea.activeScript, error: e });
|
||||
}*/
|
||||
this.plugin.ea.activeScript = null;
|
||||
ea.activeScript = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
11
src/constMathJaxSource.ts
Normal file
11
src/constMathJaxSource.ts
Normal file
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { DeviceType } from "./types";
|
||||
import { ExcalidrawLib } from "./ExcalidrawLib";
|
||||
import { moment } from "obsidian";
|
||||
//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;
|
||||
|
||||
@@ -8,6 +9,8 @@ export const ERROR_IFRAME_CONVERSION_CANCELED = "iframe conversion canceled";
|
||||
|
||||
declare const excalidrawLib: typeof ExcalidrawLib;
|
||||
|
||||
export const LOCALE = moment.locale();
|
||||
|
||||
export const obsidianToExcalidrawMap: { [key: string]: string } = {
|
||||
'en': 'en-US',
|
||||
'af': 'af-ZA', // Assuming South Africa for Afrikaans
|
||||
|
||||
@@ -45,7 +45,7 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
|
||||
true,
|
||||
);
|
||||
}
|
||||
this.addText(`[[${filepath + (item.alias ? `|${item.alias}` : "")}]]`);
|
||||
this.addText(`[[${filepath + (item.alias ? `|${item.alias}` : "")}]]`, filepath, item.alias);
|
||||
}
|
||||
|
||||
public start(drawingPath: string, addText: Function) {
|
||||
|
||||
@@ -11,7 +11,10 @@ import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/types";
|
||||
export class InsertPDFModal extends Modal {
|
||||
private borderBox: boolean = true;
|
||||
private gapSize:number = 20;
|
||||
private groupPages: boolean = false;
|
||||
private direction: "down" | "right" = "right";
|
||||
private numColumns: number = 1;
|
||||
private numRows: number = 1;
|
||||
private lockAfterImport: boolean = true;
|
||||
private pagesToImport:number[] = [];
|
||||
private pageDimensions: {width: number, height: number} = {width: 0, height: 0};
|
||||
@@ -21,7 +24,6 @@ export class InsertPDFModal extends Modal {
|
||||
private pdfFile: TFile;
|
||||
private dirty: boolean = false;
|
||||
|
||||
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
private view: ExcalidrawView,
|
||||
@@ -47,7 +49,10 @@ export class InsertPDFModal extends Modal {
|
||||
this.plugin.settings.pdfImportScale = this.importScale;
|
||||
this.plugin.settings.pdfBorderBox = this.borderBox;
|
||||
this.plugin.settings.pdfGapSize = this.gapSize;
|
||||
this.plugin.settings.pdfGroupPages = this.groupPages;
|
||||
this.plugin.settings.pdfNumColumns = this.numColumns;
|
||||
this.plugin.settings.pdfNumRows = this.numRows;
|
||||
this.plugin.settings.pdfDirection = this.direction;
|
||||
this.plugin.settings.pdfLockAfterImport = this.lockAfterImport;
|
||||
this.plugin.saveSettings();
|
||||
}
|
||||
@@ -111,7 +116,10 @@ export class InsertPDFModal extends Modal {
|
||||
await this.plugin.loadSettings();
|
||||
this.borderBox = this.plugin.settings.pdfBorderBox;
|
||||
this.gapSize = this.plugin.settings.pdfGapSize;
|
||||
this.groupPages = this.plugin.settings.pdfGroupPages;
|
||||
this.numColumns = this.plugin.settings.pdfNumColumns;
|
||||
this.numRows = this.plugin.settings.pdfNumRows;
|
||||
this.direction = this.plugin.settings.pdfDirection;
|
||||
this.lockAfterImport = this.plugin.settings.pdfLockAfterImport;
|
||||
this.importScale = this.plugin.settings.pdfImportScale;
|
||||
|
||||
@@ -211,7 +219,18 @@ export class InsertPDFModal extends Modal {
|
||||
this.borderBox = value;
|
||||
this.dirty = true;
|
||||
}))
|
||||
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Group pages")
|
||||
.setDesc("This will group all pages into a single group. This is recommended if you are locking the pages after import, because the group will be easier to unlock later rather than unlocking one by one.")
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.groupPages)
|
||||
.onChange((value) => {
|
||||
this.groupPages = value
|
||||
this.dirty = true;
|
||||
}))
|
||||
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Lock pages on canvas after import")
|
||||
.addToggle(toggle => toggle
|
||||
@@ -221,8 +240,38 @@ export class InsertPDFModal extends Modal {
|
||||
this.dirty = true;
|
||||
}))
|
||||
|
||||
let columnsText: HTMLDivElement;
|
||||
let numColumnsSetting: Setting;
|
||||
let numRowsSetting: Setting;
|
||||
const colRowVisibility = () => {
|
||||
switch(this.direction) {
|
||||
case "down":
|
||||
numColumnsSetting.settingEl.style.display = "none";
|
||||
numRowsSetting.settingEl.style.display = "";
|
||||
break;
|
||||
case "right":
|
||||
numColumnsSetting.settingEl.style.display = "";
|
||||
numRowsSetting.settingEl.style.display = "none";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Import direction")
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOptions({
|
||||
"down": "Top > Down",
|
||||
"right": "Left > Right"
|
||||
})
|
||||
.setValue(this.direction)
|
||||
.onChange(value => {
|
||||
this.direction = value as "down" | "right";
|
||||
colRowVisibility();
|
||||
this.dirty = true;
|
||||
}))
|
||||
|
||||
let columnsText: HTMLDivElement;
|
||||
numColumnsSetting = new Setting(ce);
|
||||
numColumnsSetting
|
||||
.setName("Number of columns")
|
||||
.addSlider(slider => slider
|
||||
.setLimits(1, 100, 1)
|
||||
@@ -239,6 +288,26 @@ export class InsertPDFModal extends Modal {
|
||||
el.innerText = ` ${this.numColumns.toString()}`;
|
||||
});
|
||||
|
||||
let rowsText: HTMLDivElement;
|
||||
numRowsSetting = new Setting(ce);
|
||||
numRowsSetting
|
||||
.setName("Number of rows")
|
||||
.addSlider(slider => slider
|
||||
.setLimits(1, 100, 1)
|
||||
.setValue(this.numRows)
|
||||
.onChange(value => {
|
||||
this.numRows = value;
|
||||
rowsText.innerText = ` ${value.toString()}`;
|
||||
this.dirty = true;
|
||||
}))
|
||||
.settingEl.createDiv("", (el) => {
|
||||
rowsText = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.numRows.toString()}`;
|
||||
});
|
||||
colRowVisibility();
|
||||
|
||||
let gapSizeText: HTMLDivElement;
|
||||
new Setting(ce)
|
||||
.setName("Size of gap between pages")
|
||||
@@ -256,7 +325,7 @@ export class InsertPDFModal extends Modal {
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.gapSize.toString()}`;
|
||||
});
|
||||
|
||||
|
||||
const importSizeSetting = new Setting(ce)
|
||||
.setName("Imported page size")
|
||||
.setDesc(`${this.pageDimensions.width*this.importScale} x ${this.pageDimensions.height*this.importScale}`)
|
||||
@@ -303,6 +372,7 @@ export class InsertPDFModal extends Modal {
|
||||
topX,
|
||||
topY,
|
||||
this.pdfFile.path + `#page=${page}`,
|
||||
false,
|
||||
false);
|
||||
const imgEl = ea.getElement(imageID) as any;
|
||||
imgEl.width = imgWidth;
|
||||
@@ -310,9 +380,21 @@ export class InsertPDFModal extends Modal {
|
||||
if(this.lockAfterImport) imgEl.locked = true;
|
||||
|
||||
ea.addToGroup([boxID,imageID]);
|
||||
|
||||
column = (column + 1) % this.numColumns;
|
||||
if(column === 0) row++;
|
||||
|
||||
switch(this.direction) {
|
||||
case "right":
|
||||
column = (column + 1) % this.numColumns;
|
||||
if(column === 0) row++;
|
||||
break;
|
||||
case "down":
|
||||
row = (row + 1) % this.numRows;
|
||||
if(row === 0) column++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(this.groupPages) {
|
||||
const ids = ea.getElements().map(el => el.id);
|
||||
ea.addToGroup(ids);
|
||||
}
|
||||
await ea.addElementsToView(true,true,false);
|
||||
const api = ea.getExcalidrawAPI() as ExcalidrawImperativeAPI;
|
||||
|
||||
@@ -17,6 +17,65 @@ 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.28":`
|
||||
## Fixed & Improved
|
||||
- Fixed an issue where the toolbar lost focus, requiring two clicks. This caused a problem when the hand tool was activated from ExcalidrawAutomate script when opening a drawing, causing buttons to stop working. [#1344](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1344)
|
||||
- Resolved a caching issue affecting image area-links and group-links, making them work inconsistently. For more details, refer to the discussion on [Discord](https://discord.com/channels/1026825302900494357/1169311900308361318).
|
||||
- Improved frame colors with Dynamic Coloring.
|
||||
- Added support for multiline LaTeX formulas. [#1403](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1403)
|
||||
- Fixed the issue of Chinese characters overlapping in MathJax. [#1406](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1406)
|
||||
|
||||
## New
|
||||
- Added support for Mermaid to Excalidraw **Sequence Diagrams**.
|
||||
- If an image contains an element link, clicking on the image will now open the link chooser, allowing you to decide whether to open the image or follow the element link.
|
||||
- When hovering over an image that also has an element link, the hover preview will display the contents of the link.
|
||||
- You can now choose to **import PDFs** in columns instead of rows. Additionally, you have the option to group all pages after import, which will improve the unlocking experience if you also lock pages on import.
|
||||
- Introduced configuration options for the **Laser Tool**, including pointer color, decay length, and time. ([#1408](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1408), [#1220](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1220))
|
||||
|
||||

|
||||
`,
|
||||
"1.9.27": `
|
||||
## New
|
||||
- Restructured plugin settings, added additional comments and relevant videos
|
||||
- Added setting to change PDF to Image resolution/scale. This has an effect when embedding PDF pages to Excalidraw. A lower value will result in less-sharp pages, but better overall performance. Also, larger pages (higher scale value) were not accepted by Excalidraw.com when copying from Obsidian due to the 2MB image file limit. Find the "PDF to Image" setting under "Embedding Excalidraw into your Notes and Exporting" setting. [#1393](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1393)
|
||||
|
||||
## Fixed
|
||||
- When multiple Excalidraw Scripts were executed parallel a race condition occurred causing scripts to override each other
|
||||
- I implemented a partial fix to "text detaching from figures when dragging them" [#1400](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1400)
|
||||
- Regression: extra thin stroke removed with 1.9.26 [#1399](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1399)`,
|
||||
"1.9.26":`
|
||||
## Fixes and improvements from Excalidraw.com
|
||||
- Freedraw shape selection issue, when fill-pattern is not solid [#7193](https://github.com/excalidraw/excalidraw/pull/7193)
|
||||
- Actions panel UX improvement [#6850](https://github.com/excalidraw/excalidraw/pull/6850)
|
||||
|
||||
## Fixed in plugin
|
||||
- After inserting PDF pages as image the size of inserted images were incorrectly anchored preventing resizing of pages. The fix does not solve the issue with already imported pages, but pages you import in the future will not be anchored.
|
||||
- Mobile toolbar flashes up on tab change on desktop
|
||||
- Toolbar buttons are active on the first click after opening a drawing. This addresses the "hand" issue raised here: [#1344](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1344)
|
||||
`,
|
||||
"1.9.25":`
|
||||
## Fixed
|
||||
- Fixed issues with creating Markdown or Excalidraw files for non-existing documents [#1385](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1385)
|
||||
- Resolved a bug where changing the section/block filter after duplicating a markdown embeddable now works correctly on the first attempt [#1387](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1387)
|
||||
|
||||
## New
|
||||
- Easily create a markdown file and embed it as an embedded frame with a single click when clicking a link pointing to a non-existent file.
|
||||

|
||||
- Offline LaTeX support. The MathJax package is now included in the plugin, eliminating the need for an internet connection. [#1383](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1383), [#936](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/936), [#1289](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1289)
|
||||
|
||||
## Minor Updates from excalidraw.com
|
||||
- Improved the laser pointer in dark mode.
|
||||
- Removed bound arrows from frames.
|
||||
- Enhanced fill rendering.
|
||||
- Maintained the z-order of elements added to frames.
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
- Introduced two LZString functions in ExcalidrawAutomate:
|
||||
${String.fromCharCode(96,96,96)}typescript
|
||||
compressToBase64(str:string):string;
|
||||
decompressFromBase64(str:string):string;
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"1.9.24":`
|
||||
## Fixed
|
||||
- Resolved some hidden Image and Backup Cache initialization errors.
|
||||
|
||||
@@ -11,11 +11,14 @@ import {
|
||||
} from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { sleep } from "../utils/Utils";
|
||||
import { escapeRegExp, sleep } from "../utils/Utils";
|
||||
import { getLeaf } from "../utils/ObsidianUtils";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "src/utils/FileUtils";
|
||||
import { KeyEvent, isCTRL } from "src/utils/ModifierkeyHelper";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ExcalidrawElement, getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { MAX_IMAGE_SIZE } from "src/constants";
|
||||
|
||||
export type ButtonDefinition = { caption: string; tooltip?:string; action: Function };
|
||||
|
||||
@@ -461,22 +464,44 @@ export class NewFileActions extends Modal {
|
||||
private resolvePromise: (file: TFile|null) => void;
|
||||
private rejectPromise: (reason?: any) => void;
|
||||
private newFile: TFile = null;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private path: string;
|
||||
private keys: KeyEvent;
|
||||
private view: ExcalidrawView;
|
||||
private openNewFile: boolean;
|
||||
private parentFile: TFile;
|
||||
private sourceElement: ExcalidrawElement;
|
||||
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
private path: string,
|
||||
private keys: KeyEvent,
|
||||
private view: ExcalidrawView,
|
||||
private openNewFile: boolean = true,
|
||||
private parentFile?: TFile,
|
||||
) {
|
||||
constructor({
|
||||
plugin,
|
||||
path,
|
||||
keys,
|
||||
view,
|
||||
openNewFile = true,
|
||||
parentFile,
|
||||
sourceElement,
|
||||
}: {
|
||||
plugin: ExcalidrawPlugin;
|
||||
path: string;
|
||||
keys: KeyEvent;
|
||||
view: ExcalidrawView;
|
||||
openNewFile?: boolean;
|
||||
parentFile?: TFile;
|
||||
sourceElement?: ExcalidrawElement;
|
||||
}) {
|
||||
super(plugin.app);
|
||||
this.plugin = plugin;
|
||||
this.path = path;
|
||||
this.keys = keys;
|
||||
this.view = view;
|
||||
this.openNewFile = openNewFile;
|
||||
this.sourceElement = sourceElement;
|
||||
if(!parentFile) this.parentFile = view.file;
|
||||
this.waitForClose = new Promise<TFile|null>((resolve, reject) => {
|
||||
this.resolvePromise = resolve;
|
||||
this.rejectPromise = reject;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.createForm();
|
||||
@@ -528,7 +553,7 @@ export class NewFileActions extends Modal {
|
||||
|
||||
const createFile = async (data: string): Promise<TFile> => {
|
||||
if (!this.path.includes("/")) {
|
||||
const re = new RegExp(`${this.parentFile.name}$`, "g");
|
||||
const re = new RegExp(`${escapeRegExp(this.parentFile.name)}$`, "g");
|
||||
this.path = this.parentFile.path.replace(re, this.path);
|
||||
}
|
||||
if (!this.path.match(/\.md$/)) {
|
||||
@@ -540,7 +565,31 @@ export class NewFileActions extends Modal {
|
||||
return f;
|
||||
};
|
||||
|
||||
const bMd = el.createEl("button", { text: t("PROMPT_BUTTON_CREATE_MARKDOWN") });
|
||||
if(this.sourceElement) {
|
||||
const bEmbedMd = el.createEl("button", {
|
||||
text: t("PROMPT_BUTTON_EMBED_MARKDOWN"),
|
||||
attr: {"aria-label": t("PROMPT_BUTTON_EMBED_MARKDOWN_ARIA")},
|
||||
});
|
||||
bEmbedMd.onclick = async () => {
|
||||
if (!checks) {
|
||||
return;
|
||||
}
|
||||
const f = await createFile("");
|
||||
if(f) {
|
||||
const ea:ExcalidrawAutomate = getEA(this.view);
|
||||
ea.copyViewElementsToEAforEditing([this.sourceElement]);
|
||||
ea.getElement(this.sourceElement.id).isDeleted = true;
|
||||
ea.addEmbeddable(this.sourceElement.x, this.sourceElement.y,MAX_IMAGE_SIZE, MAX_IMAGE_SIZE, undefined,f);
|
||||
ea.addElementsToView();
|
||||
}
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
const bMd = el.createEl("button", {
|
||||
text: t("PROMPT_BUTTON_CREATE_MARKDOWN"),
|
||||
attr: {"aria-label": t("PROMPT_BUTTON_CREATE_MARKDOWN_ARIA")},
|
||||
});
|
||||
bMd.onclick = async () => {
|
||||
if (!checks) {
|
||||
return;
|
||||
@@ -550,7 +599,10 @@ export class NewFileActions extends Modal {
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bEx = el.createEl("button", { text: t("PROMPT_BUTTON_CREATE_EXCALIDRAW") });
|
||||
const bEx = el.createEl("button", {
|
||||
text: t("PROMPT_BUTTON_CREATE_EXCALIDRAW"),
|
||||
attr: {"aria-label": t("PROMPT_BUTTON_CREATE_EXCALIDRAW_ARIA")},
|
||||
});
|
||||
bEx.onclick = async () => {
|
||||
if (!checks) {
|
||||
return;
|
||||
|
||||
125
src/dialogs/PublishOutOfDateFiles.ts
Normal file
125
src/dialogs/PublishOutOfDateFiles.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { Modal, Setting, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getIMGFilename } from "src/utils/FileUtils";
|
||||
import { addIframe } from "src/utils/Utils";
|
||||
|
||||
const haveLinkedFilesChanged = (depth: number, mtime: number, path: string, sourceList: Set<string>, plugin: ExcalidrawPlugin):boolean => {
|
||||
if(depth++ > 5) return false;
|
||||
sourceList.add(path);
|
||||
const links = plugin.app.metadataCache.resolvedLinks[path];
|
||||
if(!links) return false;
|
||||
for(const link of Object.keys(links)) {
|
||||
if(sourceList.has(link)) continue;
|
||||
const file = plugin.app.vault.getAbstractFileByPath(link);
|
||||
if(!file || !(file instanceof TFile)) continue;
|
||||
console.log(path, {mtimeLinked: file.stat.mtime, mtimeSource: mtime, path: file.path});
|
||||
if(file.stat.mtime > mtime) return true;
|
||||
if(plugin.isExcalidrawFile(file)) {
|
||||
if(haveLinkedFilesChanged(depth, mtime, file.path, sourceList, plugin)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const listOfOutOfSyncSVGExports = async(plugin: ExcalidrawPlugin, recursive: boolean):Promise<TFile[]> => {
|
||||
const app = plugin.app;
|
||||
|
||||
const publish = app.internalPlugins.plugins["publish"].instance;
|
||||
if(!publish) return;
|
||||
const list = await app.internalPlugins.plugins["publish"].instance.apiList();
|
||||
if(!list || !list.files) return;
|
||||
const outOfSyncFiles = new Set<TFile>();
|
||||
list.files.filter((f:any)=>f.path.endsWith(".svg")).forEach((f:any)=>{
|
||||
const maybeExcalidraFilePath = getIMGFilename(f.path,"md");
|
||||
const svgFile = app.vault.getAbstractFileByPath(f.path);
|
||||
const excalidrawFile = app.vault.getAbstractFileByPath(maybeExcalidraFilePath);
|
||||
if(!excalidrawFile || !svgFile || !(excalidrawFile instanceof TFile) || !(svgFile instanceof TFile)) return;
|
||||
console.log(excalidrawFile, {mtimeEx: excalidrawFile.stat.mtime, mtimeSVG: svgFile.stat.mtime});
|
||||
if(excalidrawFile.stat.mtime <= svgFile.stat.mtime) {
|
||||
if(!recursive) return;
|
||||
if(!haveLinkedFilesChanged(0, excalidrawFile.stat.mtime, excalidrawFile.path, new Set<string>(), plugin)) return;
|
||||
}
|
||||
outOfSyncFiles.add(excalidrawFile);
|
||||
});
|
||||
return Array.from(outOfSyncFiles);
|
||||
}
|
||||
|
||||
export class PublishOutOfDateFilesDialog extends Modal {
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
async onClose() {}
|
||||
|
||||
onOpen() {
|
||||
this.containerEl.classList.add("excalidraw-release");
|
||||
this.titleEl.setText(`Out of Date SVG Files`);
|
||||
this.createForm(false);
|
||||
}
|
||||
|
||||
async createForm(recursive: boolean) {
|
||||
const detailsEl = this.contentEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: "Video about Obsidian Publish support",
|
||||
});
|
||||
addIframe(detailsEl, "OX5_UYjXEvc");
|
||||
|
||||
const p = this.contentEl.createEl("p",{text: "Collecting data..."});
|
||||
const files = await listOfOutOfSyncSVGExports(this.plugin, recursive);
|
||||
|
||||
if(!files || files.length === 0) {
|
||||
p.innerText = "No out of date files found.";
|
||||
const div = this.contentEl.createDiv({cls: "excalidraw-prompt-buttons-div"});
|
||||
const bClose = div.createEl("button", { text: "Close", cls: "excalidraw-prompt-button"});
|
||||
bClose.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
if(!recursive) {
|
||||
const bRecursive = div.createEl("button", { text: "Check Recursive", cls: "excalidraw-prompt-button"});
|
||||
bRecursive.onclick = () => {
|
||||
this.contentEl.empty();
|
||||
this.createForm(true);
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const filesMap = new Map<TFile,boolean>();
|
||||
p.innerText = "Select files to open.";
|
||||
files.forEach((f:TFile) => {
|
||||
filesMap.set(f,true);
|
||||
new Setting(this.contentEl)
|
||||
.setName(f.path)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(true)
|
||||
.onChange(value => {
|
||||
filesMap.set(f,value);
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
const div = this.contentEl.createDiv({cls: "excalidraw-prompt-buttons-div"});
|
||||
const bClose = div.createEl("button", { text: "Close", cls: "excalidraw-prompt-button"});
|
||||
bClose.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
if(!recursive) {
|
||||
const bRecursive = div.createEl("button", { text: "Check Recursive", cls: "excalidraw-prompt-button"});
|
||||
bRecursive.onclick = () => {
|
||||
this.contentEl.empty();
|
||||
this.createForm(true);
|
||||
};
|
||||
}
|
||||
const bOpen = div.createEl("button", { text: "Open Selected", cls: "excalidraw-prompt-button" });
|
||||
bOpen.onclick = () => {
|
||||
filesMap.forEach((value:boolean,key:TFile) => {
|
||||
if(value) {
|
||||
this.plugin.openDrawing(key,"new-tab",true);
|
||||
}
|
||||
});
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MarkdownRenderer, Modal, Notice, request } from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { errorlog, log } from "../utils/Utils";
|
||||
import { errorlog, escapeRegExp, log } from "../utils/Utils";
|
||||
|
||||
const URL =
|
||||
"https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index-new.md";
|
||||
@@ -134,7 +134,7 @@ export class ScriptInstallPrompt extends Modal {
|
||||
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
const regex = new RegExp(searchTerm, 'gi');
|
||||
const regex = new RegExp(escapeRegExp(searchTerm), 'gi');
|
||||
|
||||
// Iterate over all matches in the text node
|
||||
while ((match = regex.exec(nodeContent)) !== null) {
|
||||
|
||||
@@ -249,7 +249,7 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
{
|
||||
field: "addEmbeddable",
|
||||
code: "addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;",
|
||||
desc: "Adds an iframe to the drawing. If url is not null then the iframe will be loaded from the url. The url maybe a markdown link to an note in the Vault or a weblink. If url is null then the iframe will be loaded from the file. Both the url and the file may not be null.",
|
||||
desc: "Adds an iframe/webview (depending on content and platform) to the drawing. If url is not null then the iframe/webview will be loaded from the url. The url maybe a markdown link to an note in the Vault or a weblink. If url is null then the iframe/webview will be loaded from the file. Both the url and the file may not be null.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
@@ -540,6 +540,18 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "Zoom tarteView to fit elements provided as input. elements === [] will zoom to fit the entire scene. SelectElements toggles whether the elements should be in a selected state at the end of the operation.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "compressToBase64",
|
||||
code: "compressToBase64(str: string):string",
|
||||
desc: "Compresses String to a Base64 string using LZString",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "decompressFromBase64",
|
||||
code: "decompressFromBase64(str: string):string",
|
||||
desc: "Decompresses a base 64 compressed string using LZString",
|
||||
after: "",
|
||||
},
|
||||
];
|
||||
|
||||
export const EXCALIDRAW_SCRIPTENGINE_INFO: SuggesterInfo[] = [
|
||||
|
||||
@@ -158,7 +158,7 @@ export class UniversalInsertFileModal extends Modal {
|
||||
new Setting(ce)
|
||||
.addButton(button => {
|
||||
button
|
||||
.setButtonText("as iFrame")
|
||||
.setButtonText("as Embeddable")
|
||||
.onClick(async () => {
|
||||
const path = app.metadataCache.fileToLinktext(
|
||||
file,
|
||||
|
||||
@@ -25,6 +25,7 @@ import ru from "./locale/ru";
|
||||
import tr from "./locale/tr";
|
||||
import zhCN from "./locale/zh-cn";
|
||||
import zhTW from "./locale/zh-tw";
|
||||
import { LOCALE } from "src/constants";
|
||||
|
||||
const localeMap: { [k: string]: Partial<typeof en> } = {
|
||||
ar,
|
||||
@@ -52,16 +53,9 @@ const localeMap: { [k: string]: Partial<typeof en> } = {
|
||||
"zh-tw": zhTW,
|
||||
};
|
||||
|
||||
const locale = localeMap[moment.locale()];
|
||||
const locale = localeMap[LOCALE];
|
||||
|
||||
export function t(str: keyof typeof en): string {
|
||||
if (!locale) {
|
||||
errorlog({
|
||||
where: "helpers.t",
|
||||
message: "Error: Excalidraw locale not found",
|
||||
locale: moment.locale(),
|
||||
});
|
||||
}
|
||||
|
||||
return (locale && locale[str]) || en[str];
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/Modifierke
|
||||
// English
|
||||
export default {
|
||||
// main.ts
|
||||
PUBLISH_SVG_CHECK: "Obsidian Publish: Find SVGs that are out of date",
|
||||
INSTALL_SCRIPT: "Install the script",
|
||||
UPDATE_SCRIPT: "Update available - Click to install",
|
||||
CHECKING_SCRIPT:
|
||||
@@ -62,7 +63,7 @@ export default {
|
||||
IMPORT_SVG: "Import an SVG file as Excalidraw strokes (limited SVG support, TEXT currently not supported)",
|
||||
INSERT_MD: "Insert markdown file from vault",
|
||||
INSERT_PDF: "Insert PDF file from vault",
|
||||
UNIVERSAL_ADD_FILE: "Insert ANY file from your Vault to the active drawing",
|
||||
UNIVERSAL_ADD_FILE: "Insert ANY file",
|
||||
INSERT_LATEX:
|
||||
`Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!}). ${labelALT()}+CLICK to watch a help video.`,
|
||||
ENTER_LATEX: "Enter a valid LaTeX expression",
|
||||
@@ -100,6 +101,7 @@ export default {
|
||||
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",
|
||||
CONVERT_TO_MARKDOWN: "Convert to file...",
|
||||
|
||||
//settings.ts
|
||||
RELEASE_NOTES_NAME: "Display Release Notes after update",
|
||||
@@ -111,6 +113,8 @@ export default {
|
||||
"<b><u>Toggle ON:</u></b> Show a notification when a new version of the plugin is available.<br>" +
|
||||
"<b><u>Toggle OFF:</u></b> Silent mode. You need to check for plugin updates in Community Plugins.",
|
||||
|
||||
BASIC_HEAD: "Basic",
|
||||
BASIC_DESC: `In the "Basic" settings, you can configure options such as displaying release notes after updates, receiving plugin update notifications, setting the default location for new drawings, specifying the Excalidraw folder for embedding drawings into active documents, defining an Excalidraw template file, and designating an Excalidraw Automate script folder for managing automation scripts.`,
|
||||
FOLDER_NAME: "Excalidraw folder",
|
||||
FOLDER_DESC:
|
||||
"Default location for new drawings. If empty, drawings will be created in the Vault root.",
|
||||
@@ -134,6 +138,7 @@ export default {
|
||||
"hotkeys to your favorite scripts just like to any other Obsidian command. " +
|
||||
"The folder may not be the root folder of your Vault. ",
|
||||
SAVING_HEAD: "Saving",
|
||||
SAVING_DESC: "In the 'Saving' section of Excalidraw Settings, you can configure how your drawings are saved. This includes options for compressing Excalidraw JSON in Markdown, setting autosave intervals for both desktop and mobile, defining filename formats, and choosing whether to use the .excalidraw.md or .md file extension. ",
|
||||
COMPRESS_NAME: "Compress Excalidraw JSON in Markdown",
|
||||
COMPRESS_DESC:
|
||||
"By enabling this feature Excalidraw will store the drawing JSON in a Base64 compressed " +
|
||||
@@ -181,7 +186,8 @@ FILENAME_HEAD: "Filename",
|
||||
FILENAME_EXCALIDRAW_EXTENSION_DESC:
|
||||
"This setting does not apply if you use Excalidraw in compatibility mode, " +
|
||||
"i.e. you are not using Excalidraw markdown files.<br><b><u>Toggle ON:</u></b> filename ends with .excalidraw.md<br><b><u>Toggle OFF:</u></b> filename ends with .md",
|
||||
DISPLAY_HEAD: "Display",
|
||||
DISPLAY_HEAD: "Excalidraw appearance and behavior",
|
||||
DISPLAY_DESC: "In the 'appearance and behavior' section of Excalidraw Settings, you can fine-tune how Excalidraw appears and behaves. This includes options for dynamic styling, left-handed mode, matching Excalidraw and Obsidian themes, default modes, and more.",
|
||||
DYNAMICSTYLE_NAME: "Dynamic styling",
|
||||
DYNAMICSTYLE_DESC:
|
||||
"Change Excalidraw UI colors to match the canvas color",
|
||||
@@ -214,7 +220,8 @@ FILENAME_HEAD: "Filename",
|
||||
DEFAULT_PEN_MODE_NAME: "Pen mode",
|
||||
DEFAULT_PEN_MODE_DESC:
|
||||
"Should pen mode be automatically enabled when opening Excalidraw?",
|
||||
|
||||
THEME_HEAD: "Theme and styling",
|
||||
ZOOM_HEAD: "Zoom",
|
||||
DEFAULT_PINCHZOOM_NAME: "Allow pinch zoom in pen mode",
|
||||
DEFAULT_PINCHZOOM_DESC:
|
||||
"Pinch zoom in pen mode when using the freedraw tool is disabled by default to prevent unwanted accidental zooming with your palm.<br>" +
|
||||
@@ -233,7 +240,14 @@ FILENAME_HEAD: "Filename",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_NAME: "Zoom to fit max ZOOM level",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_DESC:
|
||||
"Set the maximum level to which zoom to fit will enlarge the drawing. Minimum is 0.5 (50%) and maximum is 10 (1000%).",
|
||||
LINKS_HEAD: "Links and transclusion",
|
||||
LASER_HEAD: "Laser pointer",
|
||||
LASER_COLOR: "Laser pointer color",
|
||||
LASER_DECAY_TIME_NAME: "Laser pointer decay time",
|
||||
LASER_DECAY_TIME_DESC: "Laser pointer decay time in milliseconds. Default is 1000 (i.e. 1 second).",
|
||||
LASER_DECAY_LENGTH_NAME: "Laser pointer decay length.",
|
||||
LASER_DECAY_LENGTH_DESC: "Laser pointer decay length in line points. Default is 50.",
|
||||
LINKS_HEAD: "Links, transclusion and TODOs",
|
||||
LINKS_HEAD_DESC: "In the 'Links, transclusion and TODOs' section of Excalidraw Settings, you can configure how Excalidraw handles links, transclusions, and TODO items. This includes options for opening links, managing panes, displaying links with brackets, customizing link prefixes, handling TODO items, and more. ",
|
||||
LINKS_DESC:
|
||||
`${labelCTRL()}+CLICK on <code>[[Text Elements]]</code> to open them as links. ` +
|
||||
"If the selected text has more than one <code>[[valid Obsidian links]]</code>, only the first will be opened. " +
|
||||
@@ -243,13 +257,13 @@ FILENAME_HEAD: "Filename",
|
||||
"If you don't want text accidentally changing in your drawings use <code>[[links|with aliases]]</code>.",
|
||||
ADJACENT_PANE_NAME: "Reuse adjacent pane",
|
||||
ADJACENT_PANE_DESC:
|
||||
`When ${labelCTRL()}+${labelSHIFT()} clicking a link in Excalidraw, by default the plugin will open the link in a new pane. ` +
|
||||
`When ${labelCTRL()}+${labelALT()} clicking a link in Excalidraw, by default the plugin will open the link in a new pane. ` +
|
||||
"Turning this setting on, Excalidraw will first look for an existing pane, and try to open the link there. " +
|
||||
"Excalidraw will look for the other workspace pane based on your focus/navigation history, i.e. the workpane that was active before you " +
|
||||
"activated Excalidraw.",
|
||||
MAINWORKSPACE_PANE_NAME: "Open in main workspace",
|
||||
MAINWORKSPACE_PANE_DESC:
|
||||
`When ${labelCTRL()}+${labelSHIFT()} clicking a link in Excalidraw, by default the plugin will open the link in a new pane in the current active window. ` +
|
||||
`When ${labelCTRL()}+${labelALT()} clicking a link in Excalidraw, by default the plugin will open the link in a new pane in the current active window. ` +
|
||||
"Turning this setting on, Excalidraw will open the link in an existing or new pane in the main workspace. ",
|
||||
LINK_BRACKETS_NAME: "Show <code>[[brackets]]</code> around links",
|
||||
LINK_BRACKETS_DESC: `${
|
||||
@@ -267,7 +281,7 @@ FILENAME_HEAD: "Filename",
|
||||
"You can override this setting for a specific drawing by adding <code>"
|
||||
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "</code> to the file's frontmatter.`,
|
||||
PARSE_TODO_NAME: "Parse todo",
|
||||
PARSE_TODO_DESC: "Convert '- [ ] ' and '- [x] ' to checkpox and tick in the box.",
|
||||
PARSE_TODO_DESC: "Convert '- [ ] ' and '- [x] ' to checkbox and tick in the box.",
|
||||
TODO_NAME: "Open TODO icon",
|
||||
TODO_DESC: "Icon to use for open TODO items",
|
||||
DONE_NAME: "Completed TODO icon",
|
||||
@@ -305,11 +319,12 @@ FILENAME_HEAD: "Filename",
|
||||
GET_URL_TITLE_NAME: "Use iframely to resolve page title",
|
||||
GET_URL_TITLE_DESC:
|
||||
"Use the <code>http://iframely.server.crestify.com/iframely?url=</code> to get title of page when dropping a link into Excalidraw",
|
||||
MD_HEAD: "Markdown-embed settings",
|
||||
MD_HEAD_DESC:
|
||||
`You can transclude formatted markdown documents into drawings as images ${labelSHIFT()} drop from the file explorer or using ` +
|
||||
"the command palette action.",
|
||||
|
||||
PDF_TO_IMAGE: "PDF to Image",
|
||||
PDF_TO_IMAGE_SCALE_NAME: "PDF to Image conversion scale",
|
||||
PDF_TO_IMAGE_SCALE_DESC: "Sets the resolution of the image that is generated from the PDF page. Higher resolution will result in bigger images in memory and consequently a higher load on your system (slower performance), but sharper imagee. " +
|
||||
"Additionally, if you want to copy PDF pages (as images) to Excalidraw.com, the bigger image size may result in exceeding the 2MB limit on Excalidraw.com.",
|
||||
MD_HEAD: "Embed markdown into Excalidraw as image",
|
||||
MD_HEAD_DESC: `In the "Embed markdown as image settings," you can configure various options for how markdown documents are embedded as images within Excalidraw. These settings allow you to control the default width and maximum height of embedded markdown files, choose the font typeface, font color, and border color for embedded markdown content. Additionally, you can specify a custom CSS file to style the embedded markdown content. Note you can also embed markdown documents as interactive frames. The color setting of frames is under the Display Settings section.`,
|
||||
MD_TRANSCLUDE_WIDTH_NAME: "Default width of a transcluded markdown document",
|
||||
MD_TRANSCLUDE_WIDTH_DESC:
|
||||
"The width of the markdown page. This affects the word wrapping when transcluding longer paragraphs, and the width of " +
|
||||
@@ -345,8 +360,11 @@ FILENAME_HEAD: "Filename",
|
||||
"Setting the font-family in the css is has limitations. By default only your operating system's standard fonts are available (see README for details). " +
|
||||
"You can add one custom font beyond that using the setting above. " +
|
||||
'You can override this css setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-css: css_file_in_vault|css-snippet".',
|
||||
EMBED_HEAD: "Embed & Export",
|
||||
EMBED_HEAD: "Embedding Excalidraw into your Notes and Exporting",
|
||||
EMBED_DESC: `In the "Embed & Export" settings, you can configure how images and Excalidraw drawings are embedded and exported within your documents. Key settings include choosing the image type for markdown preview (such as Native SVG or PNG), specifying the type of file to insert into the document (original Excalidraw, PNG, or SVG), and managing image caching for embedding in markdown. You can also control image sizing, whether to embed drawings using wiki links or markdown links, and adjust settings related to image themes, background colors, and Obsidian integration.
|
||||
Additionally, there are settings for auto-export, which automatically generates SVG and/or PNG files to match the title of your Excalidraw drawings, keeping them in sync with file renames and deletions.`,
|
||||
EMBED_CACHING: "Image caching",
|
||||
EXPORT_SUBHEAD: "Export Settings",
|
||||
EMBED_SIZING: "Image sizing",
|
||||
EMBED_THEME_BACKGROUND: "Image theme and background color",
|
||||
EMBED_IMAGE_CACHE_NAME: "Cache images for embedding in markdown",
|
||||
@@ -417,13 +435,14 @@ FILENAME_HEAD: "Filename",
|
||||
"Embed the .svg file into your documents instead of Excalidraw making you embeds platform independent. " +
|
||||
"While the auto-export switch is on, this file will get updated every time you edit the Excalidraw drawing with the matching name. " +
|
||||
"You can override this setting on a file level by adding the <code>excalidraw-autoexport</code> frontmatter key. Valid values for this key are " +
|
||||
"<code>none</code>,<code>both</code>,<code>svg</code>, and <code>png</code>",
|
||||
"<code>none</code>,<code>both</code>,<code>svg</code>, and <code>png</code>.",
|
||||
EXPORT_PNG_NAME: "Auto-export PNG",
|
||||
EXPORT_PNG_DESC: "Same as the auto-export SVG, but for *.PNG",
|
||||
EXPORT_BOTH_DARK_AND_LIGHT_NAME: "Export both dark- and light-themed image",
|
||||
EXPORT_BOTH_DARK_AND_LIGHT_DESC: "When enabled, Excalidraw will export two files instead of one: filename.dark.png, filename.light.png and/or filename.dark.svg and filename.light.svg<br>"+
|
||||
"Double files will be exported both if auto-export SVG or PNG (or both) are enabled, as well as when clicking export on a single image.",
|
||||
COMPATIBILITY_HEAD: "Compatibility features",
|
||||
COMPATIBILITY_DESC: "You should only enable these features if you have a strong reason for wanting to work with excalidraw.com files instead of markdown files. Many of the plugin features are not supported on legacy files. Typical usecase would be if you use set your vault up on top of a Visual Studio Code project folder and you have .excalidraw drawings you want to access from Visual Studio Code as well. Another usecase might be using Excalidraw in Logseq and Obsidian in parallel.",
|
||||
EXPORT_EXCALIDRAW_NAME: "Auto-export Excalidraw",
|
||||
EXPORT_EXCALIDRAW_DESC: "Same as the auto-export SVG, but for *.Excalidraw",
|
||||
SYNC_EXCALIDRAW_NAME:
|
||||
@@ -433,6 +452,7 @@ FILENAME_HEAD: "Filename",
|
||||
"then update the drawing in the .md file based on the .excalidraw file",
|
||||
COMPATIBILITY_MODE_NAME: "New drawings as legacy files",
|
||||
COMPATIBILITY_MODE_DESC:
|
||||
"⚠️ Enable this only if you know what you are doing. In 99.9% of the cases you DO NOT want this on. " +
|
||||
"By enabling this feature drawings you create with the ribbon icon, the command palette actions, " +
|
||||
"and the file explorer are going to be all legacy *.excalidraw files. This setting will also turn off the reminder message " +
|
||||
"when you open a legacy file for editing.",
|
||||
@@ -443,12 +463,13 @@ FILENAME_HEAD: "Filename",
|
||||
LATEX_DEFAULT_NAME: "Default LaTeX formual for new equations",
|
||||
LATEX_DEFAULT_DESC: "Leave empty if you don't want a default formula. You can add default formatting here such as <code>\\color{white}</code>.",
|
||||
NONSTANDARD_HEAD: "Non-Excalidraw.com supported features",
|
||||
NONSTANDARD_DESC: "These features are not available on excalidraw.com. When exporting the drawing to Excalidraw.com these features will appear different.",
|
||||
NONSTANDARD_DESC: `These settings in the "Non-Excalidraw.com Supported Features" section provide customization options beyond the default Excalidraw.com features. These features are not available on excalidraw.com. When exporting the drawing to Excalidraw.com these features will appear different.
|
||||
You can configure the number of custom pens displayed next to the Obsidian Menu on the canvas, allowing you to choose from a range of options. Additionally, you can enable a fourth font option, which adds a fourth font button to the properties panel for text elements. `,
|
||||
CUSTOM_PEN_HEAD: "Custom pens",
|
||||
CUSTOM_PEN_NAME: "Number of custom pens",
|
||||
CUSTOM_PEN_DESC: "You will see these pens next to the Obsidian Menu on the canvas. You can customize the pens on the canvas by long-pressing the pen button.",
|
||||
EXPERIMENTAL_HEAD: "Experimental features",
|
||||
EXPERIMENTAL_DESC:
|
||||
"Some of these setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
|
||||
EXPERIMENTAL_HEAD: "Miscellaneous features",
|
||||
EXPERIMENTAL_DESC: `These miscellaneous features in Excalidraw include options for setting default LaTeX formulas for new equations, enabling a Field Suggester for autocompletion, displaying type indicators for Excalidraw files, enabling immersive image embedding in live preview editing mode, and experimenting with Taskbone Optical Character Recognition for text extraction from images and drawings. Users can also enter a Taskbone API key for extended usage of the OCR service.`,
|
||||
FIELD_SUGGESTER_NAME: "Enable Field Suggester",
|
||||
FIELD_SUGGESTER_DESC:
|
||||
"Field Suggester borrowed from Breadcrumbs and Templater plugins. The Field Suggester will show an autocomplete menu " +
|
||||
@@ -464,6 +485,7 @@ FILENAME_HEAD: "Filename",
|
||||
"Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " +
|
||||
"The setting will not affect the currently open documents. You need close the open documents and re-open them for the change " +
|
||||
"to take effect.",
|
||||
CUSTOM_FONT_HEAD: "Fourth font",
|
||||
ENABLE_FOURTH_FONT_NAME: "Enable fourth font option",
|
||||
ENABLE_FOURTH_FONT_DESC:
|
||||
"By turning this on, you will see a fourth font button on the properties panel for text elements. " +
|
||||
@@ -475,6 +497,7 @@ FILENAME_HEAD: "Filename",
|
||||
"Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
|
||||
"If no file is selected, Excalidraw will use the Virgil font by default.",
|
||||
SCRIPT_SETTINGS_HEAD: "Settings for installed Scripts",
|
||||
SCRIPT_SETTINGS_DESC: "Some of the Excalidraw Automate Scripts include settings. Settings are organized by script. Settings will only become visible in this list after you have executed the newly downloaded script once.",
|
||||
TASKBONE_HEAD: "Taskbone Optical Character Recogntion",
|
||||
TASKBONE_DESC: "This is an experimental integration of optical character recognition into Excalidraw. Please note, that taskbone is an independent external service not provided by Excalidraw, nor the Excalidraw-Obsidian plugin project. " +
|
||||
"The OCR service will grab legible text from freedraw lines and embedded pictures on your canvas and place the recognized text in the frontmatter of your drawing as well as onto clipboard. " +
|
||||
@@ -524,7 +547,7 @@ FILENAME_HEAD: "Filename",
|
||||
TOGGLE_DISABLEBINDING: "Toggle to invert default binding behavior",
|
||||
TOGGLE_FRAME_RENDERING: "Toggle frame rendering",
|
||||
TOGGLE_FRAME_CLIPPING: "Toggle frame clipping",
|
||||
OPEN_LINK_CLICK: "Navigate to selected element link",
|
||||
OPEN_LINK_CLICK: "Open Link",
|
||||
OPEN_LINK_PROPS: "Open markdown-embed properties or open link in new window",
|
||||
|
||||
//IFrameActionsMenu.tsx
|
||||
@@ -541,8 +564,12 @@ FILENAME_HEAD: "Filename",
|
||||
PROMPT_ERROR_DRAWING_CLOSED: "Unknown error. It seems as if your drawing was closed or the drawing file is missing",
|
||||
PROMPT_TITLE_NEW_FILE: "New File",
|
||||
PROMPT_TITLE_CONFIRMATION: "Confirmation",
|
||||
PROMPT_BUTTON_CREATE_EXCALIDRAW: "Create Excalidraw",
|
||||
PROMPT_BUTTON_CREATE_MARKDOWN: "Create Markdown",
|
||||
PROMPT_BUTTON_CREATE_EXCALIDRAW: "Create EX",
|
||||
PROMPT_BUTTON_CREATE_EXCALIDRAW_ARIA: "Create Excalidraw drawing and open in new tab",
|
||||
PROMPT_BUTTON_CREATE_MARKDOWN: "Create MD",
|
||||
PROMPT_BUTTON_CREATE_MARKDOWN_ARIA: "Create markdown document and open in new tab",
|
||||
PROMPT_BUTTON_EMBED_MARKDOWN: "Embed MD",
|
||||
PROMPT_BUTTON_EMBED_MARKDOWN_ARIA: "Replace selected element with embedded markdown document",
|
||||
PROMPT_BUTTON_NEVERMIND: "Nevermind",
|
||||
PROMPT_BUTTON_OK: "OK",
|
||||
PROMPT_BUTTON_CANCEL: "Cancel",
|
||||
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
IMPORT_SVG: "从 SVG 文件导入图形元素到当前绘图中(暂不支持文本元素)",
|
||||
INSERT_MD: "插入 Markdown 文档(以图像形式嵌入)到当前绘图中",
|
||||
INSERT_PDF: "插入 PDF 文档(以图像形式嵌入)到当前绘图中",
|
||||
UNIVERSAL_ADD_FILE: "插入任意文件(以 iFrame 形式嵌入)到当前绘图中",
|
||||
UNIVERSAL_ADD_FILE: "插入任意文件(以 Embeddable 形式嵌入)到当前绘图中",
|
||||
INSERT_LATEX:
|
||||
`插入 LaTeX 公式到当前绘图。按住 ${labelALT()} 可观看视频演示。`,
|
||||
ENTER_LATEX: "输入 LaTeX 表达式",
|
||||
|
||||
141
src/main.ts
141
src/main.ts
@@ -21,6 +21,7 @@ import {
|
||||
Editor,
|
||||
MarkdownFileInfo,
|
||||
loadMermaid,
|
||||
moment,
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
EXPORT_TYPES,
|
||||
EXPORT_IMG_ICON_NAME,
|
||||
EXPORT_IMG_ICON,
|
||||
LOCALE,
|
||||
} from "./constants";
|
||||
import ExcalidrawView, { TextMode, getTextMode } from "./ExcalidrawView";
|
||||
import {
|
||||
@@ -90,7 +92,7 @@ import {
|
||||
} from "./utils/Utils";
|
||||
import { getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
//import { OneOffs } from "./OneOffs";
|
||||
import { ExcalidrawElement, ExcalidrawImageElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawElement, ExcalidrawImageElement, ExcalidrawTextElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ScriptEngine } from "./Scripts";
|
||||
import {
|
||||
hoverEvent,
|
||||
@@ -112,6 +114,9 @@ import { ExportDialog } from "./dialogs/ExportDialog";
|
||||
import { UniversalInsertFileModal } from "./dialogs/UniversalInsertFileModal";
|
||||
import { imageCache } from "./utils/ImageCache";
|
||||
import { StylesManager } from "./utils/StylesManager";
|
||||
import { MATHJAX_SOURCE_LZCOMPRESSED } from "./constMathJaxSource";
|
||||
import { getEA } from "src";
|
||||
import { PublishOutOfDateFilesDialog } from "./dialogs/PublishOutOfDateFiles";
|
||||
|
||||
declare const EXCALIDRAW_PACKAGES:string;
|
||||
declare const react:any;
|
||||
@@ -151,7 +156,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public equationsMaster: Map<FileId, string> = null; //fileId, formula
|
||||
public mermaidsMaster: Map<FileId, string> = null; //fileId, mermaidText
|
||||
public mathjax: any = null;
|
||||
private mathjaxDiv: HTMLDivElement = null;
|
||||
public mathjaxLoaderFinished: boolean = false;
|
||||
public scriptEngine: ScriptEngine;
|
||||
public fourthFontDef: string = VIRGIL_FONT;
|
||||
@@ -171,6 +175,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.mermaidsMaster = new Map<FileId, string>();
|
||||
}
|
||||
|
||||
get locale() {
|
||||
return LOCALE;
|
||||
}
|
||||
|
||||
public getPackage(win:Window):Packages {
|
||||
if(win===window) {
|
||||
return {react, reactDOM, excalidrawLib};
|
||||
@@ -287,11 +295,91 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
private removeMathJax() {
|
||||
if("ExcalidrawMathJax" in window) {
|
||||
delete window.ExcalidrawMathJax;
|
||||
}
|
||||
const scriptElement = document.getElementById("ExcalidrawMathJax");
|
||||
if(scriptElement) {
|
||||
scriptElement.parentNode.removeChild(scriptElement);
|
||||
}
|
||||
}
|
||||
|
||||
public loadMathJax() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
//loading Obsidian MathJax as fallback
|
||||
await loadMathJax();
|
||||
//@ts-ignore
|
||||
const backup = window.MathJax;
|
||||
try {
|
||||
this.removeMathJax();
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("id","ExcalidrawMathJax");
|
||||
script.type = "text/javascript";
|
||||
script.onload = () => {
|
||||
//@ts-ignore
|
||||
window.ExcalidrawMathJax.startup.pagePromise.then(async () => {
|
||||
|
||||
// Set the 'all' package to load all MathJax modules
|
||||
const mathJaxConfig = {
|
||||
tex: {
|
||||
packages: { '[+]': ['all'] }, //this is required because during runtime loading fails due to the renaming of the package
|
||||
// Add any other configurations you need here
|
||||
},
|
||||
};
|
||||
|
||||
// Set the MathJax configuration
|
||||
//@ts-ignore
|
||||
window.ExcalidrawMathJax = {
|
||||
//@ts-ignore
|
||||
...window.ExcalidrawMathJax,
|
||||
options: {
|
||||
//@ts-ignore
|
||||
...window.ExcalidrawMathJax.options,
|
||||
...mathJaxConfig,
|
||||
},
|
||||
};
|
||||
|
||||
//https://github.com/xldenis/obsidian-latex/blob/master/main.ts
|
||||
const file = this.app.vault.getAbstractFileByPath("preamble.sty");
|
||||
const preamble: string =
|
||||
file && file instanceof TFile
|
||||
? await this.app.vault.read(file)
|
||||
: null;
|
||||
try {
|
||||
if (preamble) {
|
||||
//@ts-ignore
|
||||
await window.ExcalidrawMathJax.tex2svg(preamble);
|
||||
}
|
||||
} catch (e) {
|
||||
errorlog({
|
||||
where: self.loadMathJax,
|
||||
description: "Unexpected error while loading preamble.sty",
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
//@ts-ignore
|
||||
self.mathjax = window.ExcalidrawMathJax;
|
||||
self.mathjaxLoaderFinished = true;
|
||||
});
|
||||
};
|
||||
script.src = "data:text/javascript;base64," + decompressFromBase64(MATHJAX_SOURCE_LZCOMPRESSED); //self.settings.mathjaxSourceURL; // "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-svg.js";
|
||||
//script.src = MATHJAX_DATAURL;
|
||||
document.head.appendChild(script);
|
||||
} catch {
|
||||
new Notice("Excalidraw: Error initializing LaTeX support");
|
||||
self.mathjaxLoaderFinished = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* public loadMathJax() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
//loading Obsidian MathJax as fallback
|
||||
await loadMathJax();
|
||||
//@ts-ignore
|
||||
try {
|
||||
if(self.mathjaxDiv) {
|
||||
document.body.removeChild(self.mathjaxDiv);
|
||||
@@ -313,10 +401,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//@ts-ignore
|
||||
win.MathJax.startup.pagePromise.then(async () => {
|
||||
//https://github.com/xldenis/obsidian-latex/blob/master/main.ts
|
||||
const file = app.vault.getAbstractFileByPath("preamble.sty");
|
||||
const file = this.app.vault.getAbstractFileByPath("preamble.sty");
|
||||
const preamble: string =
|
||||
file && file instanceof TFile
|
||||
? await app.vault.read(file)
|
||||
? await this.app.vault.read(file)
|
||||
: null;
|
||||
try {
|
||||
if (preamble) {
|
||||
@@ -335,7 +423,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
self.mathjaxLoaderFinished = true;
|
||||
});
|
||||
};
|
||||
script.src = self.settings.mathjaxSourceURL; // "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js";
|
||||
script.src = "data:text/javascript;base64," + decompressFromBase64(MATHJAX_SOURCE_LZCOMPRESSED); //self.settings.mathjaxSourceURL; // "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-svg.js";
|
||||
//script.src = MATHJAX_DATAURL;
|
||||
doc.head.appendChild(script);
|
||||
} catch {
|
||||
@@ -343,7 +431,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
self.mathjaxLoaderFinished = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
private switchToExcalidarwAfterLoad() {
|
||||
const self = this;
|
||||
@@ -750,6 +838,21 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
),
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: "excalidraw-publish-svg-check",
|
||||
name: t("PUBLISH_SVG_CHECK"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
const publish = app.internalPlugins.plugins["publish"].instance;
|
||||
if (!publish) {
|
||||
return false;
|
||||
}
|
||||
if (checking) {
|
||||
return true;
|
||||
}
|
||||
(new PublishOutOfDateFilesDialog(this)).open();
|
||||
}
|
||||
})
|
||||
|
||||
this.addCommand({
|
||||
id: "excalidraw-disable-autosave",
|
||||
name: t("TEMPORARY_DISABLE_AUTOSAVE"),
|
||||
@@ -1155,6 +1258,22 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "convert-text2MD",
|
||||
name: t("CONVERT_TO_MARKDOWN"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView)
|
||||
if(!view) return false;
|
||||
const selectedTextElements = view.getViewSelectedElements().filter(el=>el.type === "text");
|
||||
if(selectedTextElements.length !==1 ) return false;
|
||||
const selectedTextElement = selectedTextElements[0] as ExcalidrawTextElement;
|
||||
const containerElement = (view.getViewElements() as ExcalidrawElement[]).find(el=>el.id === selectedTextElement.containerId);
|
||||
if(containerElement && containerElement.type === "arrow") return false;
|
||||
if(checking) return true;
|
||||
view.convertTextElementToMarkdown(selectedTextElement, containerElement);
|
||||
}
|
||||
})
|
||||
|
||||
this.addCommand({
|
||||
id: "insert-link",
|
||||
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "k" }],
|
||||
@@ -1165,7 +1284,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
this.insertLinkDialog.start(view.file.path, view.addText);
|
||||
this.insertLinkDialog.start(view.file.path, view.addLink);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1863,7 +1982,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
//close excalidraw view where this file is open
|
||||
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (let i = 0; i < leaves.length; i++) {
|
||||
if ((leaves[i].view as ExcalidrawView).file.path == file.path) {
|
||||
await leaves[i].setViewState({
|
||||
@@ -2190,9 +2309,11 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
excalidrawLeaves.forEach((leaf) => {
|
||||
this.setMarkdownView(leaf);
|
||||
});
|
||||
if (this.mathjaxDiv) {
|
||||
|
||||
this.removeMathJax();
|
||||
/* if (this.mathjaxDiv) {
|
||||
document.body.removeChild(this.mathjaxDiv);
|
||||
}
|
||||
}*/
|
||||
|
||||
Object.values(this.packageMap).forEach((p:Packages)=>{
|
||||
delete p.excalidrawLib;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TFile } from "obsidian";
|
||||
import * as React from "react";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { ExcalidrawEmbeddableElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawElement, ExcalidrawEmbeddableElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { AppState, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/types";
|
||||
import { ActionButton } from "./ActionButton";
|
||||
import { ICONS } from "./ActionIcons";
|
||||
@@ -23,6 +23,9 @@ export class EmbeddableMenu {
|
||||
private updateElement = (subpath: string, element: ExcalidrawEmbeddableElement, file: TFile) => {
|
||||
if(!element) return;
|
||||
const view = this.view;
|
||||
const app = view.app;
|
||||
element = view.excalidrawAPI.getSceneElements().find((e:ExcalidrawElement) => e.id === element.id);
|
||||
if(!element) return;
|
||||
const path = app.metadataCache.fileToLinktext(
|
||||
file,
|
||||
view.file.path,
|
||||
@@ -52,6 +55,7 @@ export class EmbeddableMenu {
|
||||
|
||||
renderButtons(appState: AppState) {
|
||||
const view = this.view;
|
||||
const app = view.app;
|
||||
const api = view?.excalidrawAPI as ExcalidrawImperativeAPI;
|
||||
if(!api) return null;
|
||||
if(!view.file) return null;
|
||||
|
||||
872
src/settings.ts
872
src/settings.ts
File diff suppressed because it is too large
Load Diff
1
src/types.d.ts
vendored
1
src/types.d.ts
vendored
@@ -32,6 +32,7 @@ declare global {
|
||||
|
||||
declare module "obsidian" {
|
||||
interface App {
|
||||
internalPlugins: any;
|
||||
isMobile(): boolean;
|
||||
getObsidianUrl(file:TFile): string;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const setDynamicStyle = (
|
||||
const darker = "#101010";
|
||||
const lighter = "#f0f0f0";
|
||||
const step = 10;
|
||||
const mixRatio = 0.8;
|
||||
const mixRatio = 0.9;
|
||||
|
||||
const invertColor = (c:string) => {
|
||||
const cm = ea.getCM(c);
|
||||
@@ -43,68 +43,91 @@ export const setDynamicStyle = (
|
||||
: invertColor(color);
|
||||
|
||||
const bgLightness = cmBG().lightness;
|
||||
const isDark = cmBG().isDark();
|
||||
|
||||
const isDark = cmBG().darkerBy(step).isDark();
|
||||
const isGray = dynamicStyle === "gray";
|
||||
|
||||
//@ts-ignore
|
||||
const accentColorString = app.getAccentColor();
|
||||
const accent = () => ea.getCM(accentColorString);
|
||||
const accentColorString = view.app.getAccentColor();
|
||||
const accent = () => isGray
|
||||
? ea.getCM(accentColorString)
|
||||
: ea.getCM(accentColorString).mix({color:cmBG(),ratio:0.2});
|
||||
|
||||
const cmBlack = () => ea.getCM("#000000").lightnessTo(bgLightness);
|
||||
|
||||
const isGray = dynamicStyle === "gray";
|
||||
|
||||
const gray1 = isGray
|
||||
? isDark ? cmBlack().lighterBy(15) : cmBlack().darkerBy(15)
|
||||
: isDark ? cmBG().lighterBy(15).mix({color:cmBlack(),ratio:0.6}) : cmBG().darkerBy(15).mix({color:cmBlack(),ratio:0.6});
|
||||
? isDark ? cmBlack().lighterBy(10) : cmBlack().darkerBy(10)
|
||||
: isDark ? cmBG().lighterBy(10).mix({color:cmBlack(),ratio:0.5}) : cmBG().darkerBy(10).mix({color:cmBlack(),ratio:0.5});
|
||||
const gray2 = isGray
|
||||
? isDark ? cmBlack().lighterBy(5) : cmBlack().darkerBy(5)
|
||||
: isDark ? cmBG().lighterBy(5).mix({color:cmBlack(),ratio:0.6}) : cmBG().darkerBy(5).mix({color:cmBlack(),ratio:0.6});
|
||||
? isDark ? cmBlack().lighterBy(4) : cmBlack().darkerBy(4)
|
||||
: isDark ? cmBG().lighterBy(4).mix({color:cmBlack(),ratio:0.5}) : cmBG().darkerBy(4).mix({color:cmBlack(),ratio:0.5});
|
||||
const text = cmBG().mix({color:isDark?lighter:darker, ratio:mixRatio});
|
||||
|
||||
const str = (cm: ColorMaster) => cm.stringHEX({alpha:false});
|
||||
const style = `--color-primary: ${str(accent())};` +
|
||||
`--color-primary-darker: ${str(accent().darkerBy(step))};` +
|
||||
`--color-primary-darkest: ${str(accent().darkerBy(step))};` +
|
||||
`--button-gray-1: ${str(gray1)};` +
|
||||
`--button-gray-2: ${str(gray2)};` +
|
||||
`--input-border-color: ${str(gray1)};` +
|
||||
`--input-bg-color: ${str(gray2)};` +
|
||||
`--input-label-color: ${str(text)};` +
|
||||
`--island-bg-color: ${gray2.alphaTo(0.93).stringHEX()};` +
|
||||
`--popup-secondary-bg-color: ${gray2.alphaTo(0.93).stringHEX()};` +
|
||||
`--icon-fill-color: ${str(text)};` +
|
||||
`--text-primary-color: ${str(text)};` +
|
||||
`--overlay-bg-color: ${gray2.alphaTo(0.6).stringHEX()};` +
|
||||
`--popup-bg-color: ${str(gray1)};` +
|
||||
`--color-gray-100: ${str(text)};` +
|
||||
`--color-gray-40: ${str(text)};` +
|
||||
`--color-gray-30: ${str(gray1)};` +
|
||||
`--color-gray-80: ${str(gray1)};` +
|
||||
`--sidebar-border-color: ${str(gray1)};` +
|
||||
`--color-primary-light: ${str(accent().lighterBy(step))};` +
|
||||
`--button-hover-bg: ${str(gray1)};` +
|
||||
`--sidebar-bg-color: ${gray2.alphaTo(0.93).stringHEX()};` +
|
||||
`--sidebar-shadow: ${str(gray1)};` +
|
||||
`--popup-text-color: ${str(text)};` +
|
||||
`--code-normal: ${str(text)};` +
|
||||
`--code-background: ${str(gray2)};` +
|
||||
`--h1-color: ${str(text)};` +
|
||||
`--h2-color: ${str(text)};` +
|
||||
`--h3-color: ${str(text)};` +
|
||||
`--h4-color: ${str(text)};` +
|
||||
`color: ${str(text)};` +
|
||||
`--select-highlight-color: ${str(gray1)};`;
|
||||
const styleObject:{[x: string]: string;} = {
|
||||
[`--color-primary`]: str(accent()),
|
||||
[`--color-surface-low`]: str(gray1),
|
||||
[`--color-surface-mid`]: str(gray1),
|
||||
[`--color-surface-lowest`]: str(gray2),
|
||||
[`--color-surface-high`]: str(gray1.lighterBy(step)),
|
||||
[`--color-on-primary-container`]: str(!isDark?accent().darkerBy(15):accent().lighterBy(15)),
|
||||
[`--color-surface-primary-container`]: str(isDark?accent().darkerBy(step):accent().lighterBy(step)),
|
||||
//[`--color-primary-darker`]: str(accent().darkerBy(step)),
|
||||
//[`--color-primary-darkest`]: str(accent().darkerBy(step)),
|
||||
[`--button-gray-1`]: str(gray1),
|
||||
[`--button-gray-2`]: str(gray2),
|
||||
[`--input-border-color`]: str(gray1),
|
||||
[`--input-bg-color`]: str(gray2),
|
||||
[`--input-label-color`]: str(text),
|
||||
[`--island-bg-color`]: gray2.alphaTo(0.93).stringHEX(),
|
||||
[`--popup-secondary-bg-color`]: gray2.alphaTo(0.93).stringHEX(),
|
||||
[`--icon-fill-color`]: str(text),
|
||||
[`--text-primary-color`]: str(text),
|
||||
[`--overlay-bg-color`]: gray2.alphaTo(0.6).stringHEX(),
|
||||
[`--popup-bg-color`]: str(gray1),
|
||||
[`--color-on-surface`]: str(text),
|
||||
//[`--color-gray-100`]: str(text),
|
||||
[`--color-gray-40`]: str(text), //frame
|
||||
[`--color-gray-50`]: str(text), //frame
|
||||
[`--color-surface-highlight`]: str(gray1),
|
||||
//[`--color-gray-30`]: str(gray1),
|
||||
[`--color-gray-80`]: str(isDark?text.lighterBy(15):text.darkerBy(15)), //frame
|
||||
[`--sidebar-border-color`]: str(gray1),
|
||||
[`--color-primary-light`]: str(accent().lighterBy(step)),
|
||||
[`--button-hover-bg`]: str(gray1),
|
||||
[`--sidebar-bg-color`]: gray2.alphaTo(0.93).stringHEX(),
|
||||
[`--sidebar-shadow`]: str(gray1),
|
||||
[`--popup-text-color`]: str(text),
|
||||
[`--code-normal`]: str(text),
|
||||
[`--code-background`]: str(gray2),
|
||||
[`--h1-color`]: str(text),
|
||||
[`--h2-color`]: str(text),
|
||||
[`--h3-color`]: str(text),
|
||||
[`--h4-color`]: str(text),
|
||||
[`color`]: str(text),
|
||||
[`--select-highlight-color`]: str(gray1),
|
||||
};
|
||||
|
||||
view.excalidrawContainer?.setAttribute(
|
||||
"style",
|
||||
style
|
||||
)
|
||||
const styleString = Object.keys(styleObject)
|
||||
.map((property) => `${property}: ${styleObject[property]}`)
|
||||
.join("; ");
|
||||
|
||||
setTimeout(()=>view.updateScene({appState:{dynamicStyle: style}}));
|
||||
/*view.excalidrawContainer?.setAttribute(
|
||||
"style",
|
||||
styleString
|
||||
)*/
|
||||
|
||||
setTimeout(()=>view.updateScene({appState:{
|
||||
frameColor: {
|
||||
stroke: isDark?str(gray2.lighterBy(15)):str(gray2.darkerBy(15)),
|
||||
fill: str((isDark?gray2.lighterBy(30):gray2.darkerBy(30)).alphaTo(0.2)),
|
||||
},
|
||||
dynamicStyle: styleObject
|
||||
}}));
|
||||
const toolspanel = view.toolsPanelRef?.current?.containerRef?.current;
|
||||
if(toolspanel) {
|
||||
let toolsStyle = toolspanel.getAttribute("style");
|
||||
toolsStyle = toolsStyle.replace(/\-\-color\-primary.*/,"");
|
||||
toolspanel.setAttribute("style",toolsStyle+style);
|
||||
toolspanel.setAttribute("style",toolsStyle+styleString);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { App, Notice, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { convertSVGStringToElement } from "./Utils";
|
||||
import { PreviewImageType } from "./UtilTypes";
|
||||
import { FILENAMEPARTS, PreviewImageType } from "./UtilTypes";
|
||||
|
||||
//@ts-ignore
|
||||
const DB_NAME = "Excalidraw " + app.appId;
|
||||
@@ -19,10 +19,11 @@ export type ImageKey = {
|
||||
isDark: boolean;
|
||||
previewImageType: PreviewImageType;
|
||||
scale: number;
|
||||
};
|
||||
} & FILENAMEPARTS;
|
||||
|
||||
const getKey = (key: ImageKey): string =>
|
||||
`${key.filepath}#${key.blockref}#${key.sectionref}#${key.isDark ? 1 : 0}#${
|
||||
`${key.filepath}#${key.blockref??""}#${key.sectionref??""}#${key.isDark ? 1 : 0}#${
|
||||
key.hasGroupref}#${key.hasArearef}#${key.hasFrameref}#${key.hasSectionref}#${
|
||||
key.previewImageType === PreviewImageType.SVGIMG
|
||||
? 1
|
||||
: key.previewImageType === PreviewImageType.PNG
|
||||
@@ -149,10 +150,11 @@ class ImageCache {
|
||||
const cursor = (event.target as IDBRequest<IDBCursorWithValue | null>).result;
|
||||
if(cursor) {
|
||||
const key = cursor.key as string;
|
||||
const isLegacyKey = key.replaceAll(/[^#]/g,"").length < 9; // introduced hasGroupref, etc. in 1.9.28
|
||||
const filepath = key.split("#")[0];
|
||||
const fileExists = files.some((f: TFile) => f.path === filepath);
|
||||
const file = fileExists ? files.find((f: TFile) => f.path === filepath) : null;
|
||||
if (!file || (file && file.stat.mtime > cursor.value.mtime) || (!cursor.value.blob && !cursor.value.svg)) {
|
||||
if (isLegacyKey || !file || (file && file.stat.mtime > cursor.value.mtime) || (!cursor.value.blob && !cursor.value.svg)) {
|
||||
deletePromises.push(
|
||||
new Promise<void>((innerResolve, innerReject) => {
|
||||
const deleteRequest = store.delete(cursor.primaryKey);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {
|
||||
App,
|
||||
Notice,
|
||||
parseFrontMatterEntry,
|
||||
request,
|
||||
requestUrl,
|
||||
TFile,
|
||||
@@ -30,6 +31,8 @@ import ExcalidrawScene from "src/svgToExcalidraw/elements/ExcalidrawScene";
|
||||
import { FILENAMEPARTS } from "./UtilTypes";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/utility-types";
|
||||
import { cleanBlockRef, cleanSectionHeading } from "./ObsidianUtils";
|
||||
import { updateElementLinksToObsidianLinks } from "src/ExcalidrawAutomate";
|
||||
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
@@ -257,6 +260,7 @@ export const getSVG = async (
|
||||
scene: any,
|
||||
exportSettings: ExportSettings,
|
||||
padding: number,
|
||||
srcFile: TFile|null, //if set, will replace markdown links with obsidian links
|
||||
): Promise<SVGSVGElement> => {
|
||||
let elements:ExcalidrawElement[] = scene.elements;
|
||||
if(elements.some(el => el.type === "embeddable")) {
|
||||
@@ -267,8 +271,13 @@ export const getSVG = async (
|
||||
}
|
||||
|
||||
try {
|
||||
return await exportToSvg({
|
||||
elements,
|
||||
const svg = await exportToSvg({
|
||||
elements: srcFile
|
||||
? updateElementLinksToObsidianLinks({
|
||||
elements,
|
||||
hostFile: srcFile,
|
||||
})
|
||||
: elements,
|
||||
appState: {
|
||||
exportBackground: exportSettings.withBackground,
|
||||
exportWithDarkMode: exportSettings.withTheme
|
||||
@@ -279,6 +288,10 @@ export const getSVG = async (
|
||||
files: scene.files,
|
||||
exportPadding: padding,
|
||||
});
|
||||
if(svg) {
|
||||
svg.addClass("excalidraw-svg");
|
||||
}
|
||||
return svg;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
@@ -595,6 +608,21 @@ export const getExportPadding = (
|
||||
return plugin.settings.exportPaddingSVG;
|
||||
};
|
||||
|
||||
export const getFileCSSClasses = (
|
||||
plugin: ExcalidrawPlugin,
|
||||
file: TFile,
|
||||
): string[] => {
|
||||
if (file) {
|
||||
const fileCache = plugin.app.metadataCache.getFileCache(file);
|
||||
if(!fileCache?.frontmatter) return [];
|
||||
const x = parseFrontMatterEntry(fileCache.frontmatter, "cssclasses");
|
||||
if (Array.isArray(x)) return x
|
||||
if (typeof x === "string") return Array.from(new Set(x.split(/[, ]+/).filter(Boolean)));
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export const getPNGScale = (plugin: ExcalidrawPlugin, file: TFile): number => {
|
||||
if (file) {
|
||||
const fileCache = plugin.app.metadataCache.getFileCache(file);
|
||||
@@ -764,4 +792,20 @@ export const convertSVGStringToElement = (svg: string): SVGSVGElement => {
|
||||
return firstChild;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export const escapeRegExp = (str:string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
|
||||
export const addIframe = (containerEl: HTMLElement, link:string, startAt?: number) => {
|
||||
const wrapper = containerEl.createDiv({cls: "excalidraw-videoWrapper settings"})
|
||||
wrapper.createEl("iframe", {
|
||||
attr: {
|
||||
allowfullscreen: true,
|
||||
allow: "encrypted-media;picture-in-picture",
|
||||
frameborder: "0",
|
||||
title: "YouTube video player",
|
||||
src: "https://www.youtube.com/embed/" + link + (startAt ? "?start=" + startAt : ""),
|
||||
sandbox: "allow-forms allow-presentation allow-same-origin allow-scripts allow-modals",
|
||||
},
|
||||
});
|
||||
}
|
||||
56
styles.css
56
styles.css
@@ -183,15 +183,24 @@ li[data-testid] {
|
||||
}
|
||||
|
||||
.excalidraw-videoWrapper {
|
||||
max-width:600px
|
||||
max-width:600px;
|
||||
}
|
||||
.excalidraw-videoWrapper div {
|
||||
.excalidraw-videoWrapper.settings {
|
||||
max-width:340px;
|
||||
}
|
||||
|
||||
.excalidraw-videoWrapper div{
|
||||
position: relative;
|
||||
padding-bottom: 56.25%;
|
||||
height: 0;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.excalidraw-videoWrapper.settings iframe {
|
||||
position: relative;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.excalidraw-videoWrapper iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -423,4 +432,47 @@ div.excalidraw-draginfo {
|
||||
max-height: initial;
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
summary.excalidraw-setting-h1 {
|
||||
font-variant: var(--h1-variant);
|
||||
letter-spacing: -0.015em;
|
||||
line-height: var(--h1-line-height);
|
||||
font-size: var(--h1-size);
|
||||
color: var(--h1-color);
|
||||
font-weight: var(--h1-weight);
|
||||
font-style: var(--h1-style);
|
||||
font-family: var(--h1-font);
|
||||
/*margin-block-start: var(--p-spacing);*/
|
||||
margin-block-end: var(--p-spacing);
|
||||
}
|
||||
|
||||
summary.excalidraw-setting-h3 {
|
||||
font-variant: var(--h3-variant);
|
||||
letter-spacing: -0.015em;
|
||||
line-height: var(--h3-line-height);
|
||||
font-size: var(--h3-size);
|
||||
color: var(--h3-color);
|
||||
font-weight: var(--h3-weight);
|
||||
font-style: var(--h3-style);
|
||||
font-family: var(--h3-font);
|
||||
margin-block-start: var(--p-spacing);
|
||||
margin-block-end: var(--p-spacing);
|
||||
}
|
||||
|
||||
summary.excalidraw-setting-h4 {
|
||||
font-variant: var(--h4-variant);
|
||||
letter-spacing: -0.015em;
|
||||
line-height: var(--h4-line-height);
|
||||
font-size: var(--h4-size);
|
||||
color: var(--h4-color);
|
||||
font-weight: var(--h4-weight);
|
||||
font-style: var(--h4-style);
|
||||
font-family: var(--h4-font);
|
||||
margin-block-start: var(--p-spacing);
|
||||
margin-block-end: var(--p-spacing);
|
||||
}
|
||||
|
||||
hr.excalidraw-setting-hr {
|
||||
margin: 1rem 0rem 0rem 0rem;
|
||||
}
|
||||
Reference in New Issue
Block a user