Compare commits

...

13 Commits

Author SHA1 Message Date
zsviczian
9da40944ab 2.2.13 2024-07-29 18:13:13 +02:00
zsviczian
f678203a64 Merge pull request #1908 from dmscode/master
Update zh-cn.ts to 2.2.11
2024-07-29 17:32:14 +02:00
zsviczian
a9572e08e9 Merge branch 'master' into master 2024-07-29 17:31:58 +02:00
dmscode
ee9b042cdf Update zh-cn.ts to 2.2.11 2024-07-28 20:42:41 +08:00
zsviczian
bc138fa78a fixed storeAction, replaced "none" with "update" #1906 2024-07-28 10:06:31 +02:00
zsviczian
67dbe256f7 2.2.12 2024-07-28 06:55:27 +02:00
zsviczian
8b066d46e2 fix rename and skip inline fonts to cache key 2024-07-28 06:24:30 +02:00
zsviczian
1769a65a82 2.2.11 2024-07-27 21:25:23 +02:00
zsviczian
941eb56769 updated ExcaliAI 2024-07-27 07:09:19 +02:00
zsviczian
a317613ef4 Merge pull request #1859 from Saik0s/patch-1
Update ExcaliAI.md to fix incorrect request json for openai api
2024-07-27 07:05:58 +02:00
zsviczian
173571846f 2.2.10-2 2024-07-26 20:11:20 +02:00
zsviczian
ab1078d393 Fourth Font 2.2.10-1 2024-07-25 21:59:42 +02:00
Igor Tarasenko
d3aeedb9f6 Update ExcaliAI.md to fix incorrect request json for openai api 2024-06-28 21:03:58 +02:00
38 changed files with 696 additions and 369 deletions

View File

@@ -6,7 +6,7 @@ import * as obsidian_module from "obsidian";
import ExcalidrawView, { ExportSettings } from "src/ExcalidrawView";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/excalidraw/types";
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
import { ConnectionPoint, DeviceType } from "src/types";
import { ConnectionPoint, DeviceType } from "src/types/types";
import { ColorMaster } from "colormaster";
import { TInput } from "colormaster/types";
import { ClipboardData } from "@zsviczian/excalidraw/types/excalidraw/clipboard";
@@ -123,7 +123,7 @@ export declare class ExcalidrawAutomate {
* @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";
setFontFamily(val: number): "Virgil, Segoe UI Emoji" | "Helvetica, Segoe UI Emoji" | "Cascadia, Segoe UI Emoji" | "Local Font";
/**
* @param val //0:"light", 1:"dark"
* @returns

View File

@@ -427,7 +427,7 @@ const run = async (text) => {
const requestObject = isImageEditRequest
? {
...imageDataURL ? {image: imageDataURL} : {},
...imageDataURL ? {image: {url: imageDataURL}} : {},
...(text && text.trim() !== "") ? {text} : {},
imageGenerationProperties: {
size: imageSize,
@@ -437,7 +437,7 @@ const run = async (text) => {
},
}
: {
...imageDataURL ? {image: imageDataURL} : {},
...imageDataURL ? {image: {url: imageDataURL}} : {},
...(text && text.trim() !== "") ? {text} : {},
systemPrompt: systemPrompt.prompt,
instruction: outputType.instruction,

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -19,7 +19,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.1-obsidian-32",
"@zsviczian/excalidraw": "0.17.1-obsidian-34",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"colormaster": "^1.2.1",
@@ -32,7 +32,9 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"roughjs": "^4.5.2",
"js-yaml": "^4.1.0"
"js-yaml": "^4.1.0",
"opentype.js": "^1.3.4",
"woff2sfnt-sfnt2woff": "^1.0.0"
},
"devDependencies": {
"dotenv": "^16.4.5",
@@ -52,6 +54,7 @@
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@types/js-yaml": "^4.0.9",
"@types/opentype.js": "^1.3.8",
"@zerollup/ts-transform-paths": "^1.7.18",
"cross-env": "^7.0.3",
"cssnano": "^6.0.2",

View File

@@ -107,7 +107,7 @@ const BUILD_CONFIG = {
babel({
presets: [['@babel/preset-env', {
targets: {
esmodules: true,
ios: "15", // ios Compatibility //esmodules: true,
},
}]],
exclude: "node_modules/**",

View File

@@ -4,11 +4,6 @@
import { ExcalidrawElement, FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { BinaryFileData, DataURL } from "@zsviczian/excalidraw/types/excalidraw/types";
import { App, MarkdownRenderer, Notice, TFile } from "obsidian";
import {
ASSISTANT_FONT,
CASCADIA_FONT,
VIRGIL_FONT,
} from "./constants/constFonts";
import {
DEFAULT_MD_EMBED_CSS,
fileid,
@@ -16,6 +11,7 @@ import {
nanoid,
THEME_FILTER,
FRONTMATTER_KEYS,
getFontDefinition,
} from "./constants/constants";
import { createSVG } from "./ExcalidrawAutomate";
import { ExcalidrawData, getTransclusion } from "./ExcalidrawData";
@@ -38,10 +34,9 @@ import {
LinkParts,
svgToBase64,
isMaskFile,
embedFontsInSVG,
getEmbeddedFilenameParts,
} from "./utils/Utils";
import { ValueOf } from "./types";
import { ValueOf } from "./types/types";
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
import { mermaidToExcalidraw } from "src/constants/constants";
import { ImageKey, imageCache } from "./utils/ImageCache";
@@ -363,6 +358,7 @@ export class EmbeddedFilesLoader {
: false,
withTheme: !!forceTheme,
isMask,
skipInliningFonts: false,
};
const hasColorMap = Boolean(inFile instanceof EmbeddedFile ? inFile.colorMap : null);
@@ -370,7 +366,10 @@ export class EmbeddedFilesLoader {
const hasFilenameParts = Boolean((inFile instanceof EmbeddedFile) && inFile.filenameparts);
const filenameParts = hasFilenameParts ? (inFile as EmbeddedFile).filenameparts : null;
const cacheKey:ImageKey = {
...hasFilenameParts? filenameParts : {
...hasFilenameParts? {
...filenameParts,
inlineFonts: !exportSettings.skipInliningFonts,
}: {
filepath: file.path,
hasBlockref: false,
hasGroupref: false,
@@ -379,6 +378,7 @@ export class EmbeddedFilesLoader {
hasFrameref: false,
hasClippedFrameref: false,
hasSectionref: false,
inlineFonts: !exportSettings.skipInliningFonts,
blockref: null,
sectionref: null,
linkpartReference: null,
@@ -445,16 +445,16 @@ export class EmbeddedFilesLoader {
//see svgWithFont below
imageCache.addImageToCache(cacheKey,"", svg);
}
const svgWithFont = embedFontsInSVG(svg, this.plugin);
if(!svgWithFont.hasAttribute("width") && svgWithFont.hasAttribute("viewBox")){
if(!svg.hasAttribute("width") && svg.hasAttribute("viewBox")){
//2024.06.09
//this addresses backward compatibility issues where the cache does not have the width and height attributes
//this should be removed in the future
const vb = svgWithFont.getAttr("viewBox").split(" ");
Boolean(vb[2]) && svgWithFont.setAttribute("width", vb[2]);
Boolean(vb[3]) && svgWithFont.setAttribute("height", vb[3]);
const vb = svg.getAttr("viewBox").split(" ");
Boolean(vb[2]) && svg.setAttribute("width", vb[2]);
Boolean(vb[3]) && svg.setAttribute("height", vb[3]);
}
const dURL = svgToBase64(svgWithFont.outerHTML) as DataURL;
const dURL = svgToBase64(svg.outerHTML) as DataURL;
return {dataURL: dURL as DataURL, hasSVGwithBitmap};
};
@@ -816,13 +816,29 @@ export class EmbeddedFilesLoader {
}
switch (fontName) {
case "Virgil":
fontDef = VIRGIL_FONT;
fontDef = await getFontDefinition(1);
break;
case "Cascadia":
fontDef = CASCADIA_FONT;
fontDef = await getFontDefinition(3);
break;
case "Assistant":
fontDef = ASSISTANT_FONT;
case "Assistant":
case "Helvetica":
fontDef = await getFontDefinition(2);
break;
case "Excalifont":
fontDef = await getFontDefinition(5);
break;
case "Nunito":
fontDef = await getFontDefinition(6);
break;
case "Lilita One":
fontDef = await getFontDefinition(7);
break;
case "Comic Shanns":
fontDef = await getFontDefinition(8);
break;
case "Liberation Sans":
fontDef = await getFontDefinition(9);
break;
case "":
fontDef = "";

View File

@@ -27,7 +27,7 @@ import {
GITHUB_RELEASES,
determineFocusDistance,
getCommonBoundingBox,
getDefaultLineHeight,
getLineHeight,
getMaximumGroups,
intersectElementWithLine,
measureText,
@@ -37,11 +37,11 @@ import {
THEME_FILTER,
mermaidToExcalidraw,
refreshTextDimensions,
getFontFamilyString,
} from "src/constants/constants";
import { blobToBase64, checkAndCreateFolder, getDrawingFilename, getExcalidrawEmbeddedFilesFiletree, getListOfTemplateFiles, getNewUniqueFilepath, hasExcalidrawEmbeddedImagesTreeChanged, } from "src/utils/FileUtils";
import {
//debug,
embedFontsInSVG,
errorlog,
getEmbeddedFilenameParts,
getImageSize,
@@ -61,7 +61,7 @@ import { tex2dataURL } from "src/LaTeX";
import { GenericInputPrompt, NewFileActions } from "src/dialogs/Prompt";
import { t } from "src/lang/helpers";
import { ScriptEngine } from "src/Scripts";
import { ConnectionPoint, DeviceType } from "src/types";
import { ConnectionPoint, DeviceType } from "src/types/types";
import CM, { ColorMaster, extendPlugins } from "colormaster";
import HarmonyPlugin from "colormaster/plugins/harmony";
import MixPlugin from "colormaster/plugins/mix"
@@ -92,6 +92,7 @@ import {
import { EXCALIDRAW_AUTOMATE_INFO, EXCALIDRAW_SCRIPTENGINE_INFO } from "./dialogs/SuggesterInfo";
import { addBackOfTheNoteCard, getFrameBasedOnFrameNameOrId } from "./utils/ExcalidrawViewUtils";
import { log } from "./utils/DebugHelper";
import { ExcalidrawLib } from "./ExcalidrawLib";
extendPlugins([
HarmonyPlugin,
@@ -111,6 +112,7 @@ extendPlugins([
declare const PLUGIN_VERSION:string;
declare var LZString: any;
declare const excalidrawLib: typeof ExcalidrawLib;
const GAP = 4;
@@ -386,7 +388,7 @@ export class ExcalidrawAutomate {
opacity: number;
strokeSharpness?: StrokeRoundness; //defaults to undefined, use strokeRoundess and roundess instead. Only kept for legacy script compatibility type StrokeRoundness = "round" | "sharp"
roundness: null | { type: RoundnessType; value?: number };
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:Local Font
fontSize: number;
textAlign: string; //"left"|"right"|"center"
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
@@ -1191,7 +1193,7 @@ export class ExcalidrawAutomate {
element.text,
element.fontSize,
element.fontFamily,
getDefaultLineHeight(element.fontFamily)
getLineHeight(element.fontFamily)
);
element.width = w;
element.height = h;
@@ -1236,7 +1238,7 @@ export class ExcalidrawAutomate {
text,
this.style.fontSize,
this.style.fontFamily,
getDefaultLineHeight(this.style.fontFamily)
getLineHeight(this.style.fontFamily)
);
const width = formatting?.width ? formatting.width : w;
const height = formatting?.height ? formatting.height : h;
@@ -1294,7 +1296,7 @@ export class ExcalidrawAutomate {
containerId: isContainerBound ? boxId : null,
originalText: isContainerBound ? originalText : text,
rawText: isContainerBound ? originalText : text,
lineHeight: getDefaultLineHeight(this.style.fontFamily),
lineHeight: getLineHeight(this.style.fontFamily),
autoResize: formatting?.box ? true : (formatting?.autoResize ?? true),
};
if (boxId && formatting?.box === "blob") {
@@ -1995,7 +1997,7 @@ export class ExcalidrawAutomate {
appState: {
viewModeEnabled: !isFullscreen,
},
storeAction: "none",
storeAction: "update",
});
this.targetView.toolsPanelRef?.current?.setExcalidrawViewMode(!isFullscreen);
}
@@ -2014,7 +2016,7 @@ export class ExcalidrawAutomate {
return;
}
const view = this.targetView as ExcalidrawView;
view.updateScene({appState:{viewModeEnabled: enabled}});
view.updateScene({appState:{viewModeEnabled: enabled}, storeAction: "update"});
view.toolsPanelRef?.current?.setExcalidrawViewMode(enabled);
}
@@ -2040,7 +2042,7 @@ export class ExcalidrawAutomate {
return;
}
if (!Boolean(scene.storeAction)) {
scene.storeAction = scene.commitToHistory ? "capture" : "none";
scene.storeAction = scene.commitToHistory ? "capture" : "update";
}
this.targetView.updateScene({
@@ -2494,7 +2496,7 @@ export class ExcalidrawAutomate {
text,
this.style.fontSize,
this.style.fontFamily,
getDefaultLineHeight(this.style.fontFamily),
getLineHeight(this.style.fontFamily),
);
return { width: size.w ?? 0, height: size.h ?? 0 };
};
@@ -2756,26 +2758,15 @@ function getLineBox(points: [x: number, y: number][]) {
}
function getFontFamily(id: number) {
switch (id) {
case 1:
return "Virgil, Segoe UI Emoji";
case 2:
return "Helvetica, Segoe UI Emoji";
case 3:
return "Cascadia, Segoe UI Emoji";
case 4:
return "LocalFont";
}
getFontFamilyString({fontFamily:id})
}
export async function initFonts(doc: Document = document) {
for (let i = 1; i <= 3; i++) {
await (doc as any).fonts.load(`20px ${getFontFamily(i)}`);
}
await (doc as any).fonts.load("400 20px Assistant");
await (doc as any).fonts.load("500 20px Assistant");
await (doc as any).fonts.load("600 20px Assistant");
await (doc as any).fonts.load("700 20px Assistant");
export async function initFonts() {
await excalidrawLib.registerFontsInCSS();
/*const fonts = excalidrawLib.getFontFamilies();
for(let i=0;i<fonts.length;i++) {
await (document as any).fonts.load(`20px ${fonts[i]}`);
};*/
}
export function _measureText(
@@ -2790,7 +2781,7 @@ export function _measureText(
}
if (!fontFamily) {
fontFamily = 1;
lineHeight = getDefaultLineHeight(fontFamily);
lineHeight = getLineHeight(fontFamily);
}
const metrics = measureText(
newText,
@@ -3050,6 +3041,9 @@ export async function createSVG(
if (!loader) {
loader = new EmbeddedFilesLoader(plugin);
}
if(typeof exportSettings.skipInliningFonts === "undefined") {
exportSettings.skipInliningFonts = !embedFont;
}
const template = templatePath
? await getTemplate(plugin, templatePath, true, loader, depth, convertMarkdownLinksToObsidianURLs)
: null;
@@ -3120,7 +3114,7 @@ export async function createSVG(
if (template?.hasSVGwithBitmap) {
svg.setAttribute("hasbitmap", "true");
}
return embedFont ? embedFontsInSVG(svg, plugin) : svg;
return svg;
}
function estimateLineBound(points: any): [number, number, number, number] {

View File

@@ -11,7 +11,7 @@ import {
fileid,
DEVICE,
EMBEDDABLE_THEME_FRONTMATTER_VALUES,
getDefaultLineHeight,
getLineHeight,
ERROR_IFRAME_CONVERSION_CANCELED,
JSON_parse,
FRONTMATTER_KEYS,
@@ -549,7 +549,7 @@ export class ExcalidrawData {
}
if (el.type === "text" && !el.hasOwnProperty("lineHeight")) {
el.lineHeight = getDefaultLineHeight(el.fontFamily);
el.lineHeight = getLineHeight(el.fontFamily);
}
if (el.type === "image" && !el.hasOwnProperty("roundness")) {

View File

@@ -2,6 +2,7 @@ import { RestoredDataState } from "@zsviczian/excalidraw/types/excalidraw/data/r
import { ImportedDataState } from "@zsviczian/excalidraw/types/excalidraw/data/types";
import { BoundingBox } from "@zsviczian/excalidraw/types/excalidraw/element/bounds";
import { ElementsMap, ExcalidrawBindableElement, ExcalidrawElement, ExcalidrawFrameElement, ExcalidrawTextContainer, ExcalidrawTextElement, FontFamilyValues, FontString, NonDeleted, NonDeletedExcalidrawElement, Theme } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { FontMetadata } from "@zsviczian/excalidraw/types/excalidraw/fonts/metadata";
import { AppState, BinaryFiles, Point, Zoom } from "@zsviczian/excalidraw/types/excalidraw/types";
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
@@ -46,6 +47,7 @@ declare namespace ExcalidrawLib {
exportPadding?: number;
exportingFrame: ExcalidrawFrameElement | null | undefined;
renderEmbeddables?: boolean;
skipInliningFonts?: boolean;
}): Promise<SVGSVGElement>;
function sceneCoordsToViewportCoords(
@@ -116,8 +118,7 @@ declare namespace ExcalidrawLib {
lineHeight: number,
): { width: number; height: number; };
function getDefaultLineHeight(fontFamily: FontFamilyValues): number;
function getLineHeight (fontFamily: FontFamilyValues):number;
function wrapText(text: string, font: FontString, maxWidth: number): string;
function getFontString({
@@ -128,6 +129,13 @@ declare namespace ExcalidrawLib {
fontFamily: FontFamilyValues;
}): FontString;
function getFontFamilyString ({
fontFamily,
}: {
fontFamily: number;
}): string;
function getBoundTextMaxWidth(container: ExcalidrawElement): number;
function exportToBlob(
@@ -164,4 +172,9 @@ declare namespace ExcalidrawLib {
var TTDDialog: any;
function destroyObsidianUtils(): void;
}
function registerLocalFont(fontMetrics: FontMetadata, uri: string): void;
function getFontFamilies(): string[];
function registerFontsInCSS(): Promise<void>;
function getFontDefinition(fontFamily: number): Promise<string>;
}

View File

@@ -82,7 +82,6 @@ import {
} from "./utils/FileUtils";
import {
checkExcalidrawVersion,
embedFontsInSVG,
errorlog,
getEmbeddedFilenameParts,
getExportTheme,
@@ -120,7 +119,7 @@ import { ObsidianMenu } from "./menu/ObsidianMenu";
import { ToolsPanel } from "./menu/ToolsPanel";
import { ScriptEngine } from "./Scripts";
import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer";
import { ICONS, LogoWrapper, saveIcon } from "./menu/ActionIcons";
import { excalidrawSword, ICONS, LogoWrapper, Rank, saveIcon, SwordColors } from "./menu/ActionIcons";
import { ExportDialog } from "./dialogs/ExportDialog";
import { getEA } from "src"
import { anyModifierKeysPressed, emulateKeysForLinkClick, webbrowserDragModifierType, internalDragModifierType, isWinALTorMacOPT, isWinCTRLorMacCMD, isWinMETAorMacCTRL, isSHIFT, linkClickModifierType, localFileDragModifierType, ModifierKeys, modifierKeyTooltipMessages } from "./utils/ModifierkeyHelper";
@@ -139,8 +138,9 @@ import { CustomMutationObserver, DEBUGGING, debug, log} from "./utils/DebugHelpe
import { extractCodeBlocks, postOpenAI } from "./utils/AIUtils";
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
import { SelectCard } from "./dialogs/SelectCard";
import { Packages } from "./types";
import { Packages } from "./types/types";
import React from "react";
import { StoreAction } from "@zsviczian/excalidraw";
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
const PREVENT_RELOAD_TIMEOUT = 2000;
@@ -180,6 +180,7 @@ export interface ExportSettings {
outline: boolean;
clip: boolean;
};
skipInliningFonts?: boolean;
}
const HIDE = "excalidraw-hidden";
@@ -215,7 +216,7 @@ export const addFiles = async (
view.updateScene({
elements: s.scene.elements,
appState: s.scene.appState,
storeAction: "none",
storeAction: "update",
});
}
for (const f of files) {
@@ -459,7 +460,7 @@ export default class ExcalidrawView extends TextFileView {
);
}
public async svg(scene: any, theme?:string, embedScene?: boolean): Promise<SVGSVGElement> {
public async svg(scene: any, theme?:string, embedScene?: boolean, embedFont: boolean = false): Promise<SVGSVGElement> {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.svg, "ExcalidrawView.svg", scene, theme, embedScene);
const ed = this.exportDialog;
@@ -467,6 +468,7 @@ export default class ExcalidrawView extends TextFileView {
withBackground: ed ? !ed.transparent : getWithBackground(this.plugin, this.file),
withTheme: true,
isMask: isMaskFile(this.plugin, this.file),
skipInliningFonts: !embedFont,
};
if(typeof embedScene === "undefined") {
@@ -504,14 +506,12 @@ export default class ExcalidrawView extends TextFileView {
const exportImage = async (filepath:string, theme?:string) => {
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const svg = await this.svg(scene,theme, embedScene);
const svg = await this.svg(scene,theme, embedScene, true);
if (!svg) {
return;
}
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(
embedFontsInSVG(svg, this.plugin),
);
const svgString = serializer.serializeToString(svg);
if (file && file instanceof TFile) {
await this.app.vault.modify(file, svgString);
} else {
@@ -533,11 +533,10 @@ export default class ExcalidrawView extends TextFileView {
return;
}
let svg = await this.svg(this.getScene(selectedOnly),undefined,embedScene);
const svg = await this.svg(this.getScene(selectedOnly),undefined,embedScene, true);
if (!svg) {
return;
}
svg = embedFontsInSVG(svg, this.plugin);
download(
null,
svgToBase64(svg.outerHTML),
@@ -903,21 +902,21 @@ export default class ExcalidrawView extends TextFileView {
toggleDisableBinding() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleDisableBinding, "ExcalidrawView.toggleDisableBinding");
const newState = !this.excalidrawAPI.getAppState().invertBindingBehaviour;
this.updateScene({appState: {invertBindingBehaviour:newState}});
this.updateScene({appState: {invertBindingBehaviour:newState}, storeAction: "update"});
new Notice(newState ? t("ARROW_BINDING_INVERSE_MODE") : t("ARROW_BINDING_NORMAL_MODE"));
}
toggleFrameRendering() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleFrameRendering, "ExcalidrawView.toggleFrameRendering");
const frameRenderingSt = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState().frameRendering;
this.updateScene({appState: {frameRendering: {...frameRenderingSt, enabled: !frameRenderingSt.enabled}}});
this.updateScene({appState: {frameRendering: {...frameRenderingSt, enabled: !frameRenderingSt.enabled}}, storeAction: "update"});
new Notice(frameRenderingSt.enabled ? t("FRAME_CLIPPING_ENABLED") : t("FRAME_CLIPPING_DISABLED"));
}
toggleFrameClipping() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.toggleFrameClipping, "ExcalidrawView.toggleFrameClipping");
const frameRenderingSt = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState().frameRendering;
this.updateScene({appState: {frameRendering: {...frameRenderingSt, clip: !frameRenderingSt.clip}}});
this.updateScene({appState: {frameRendering: {...frameRenderingSt, clip: !frameRenderingSt.clip}}, storeAction: "update"});
new Notice(frameRenderingSt.clip ? "Frame Clipping: Enabled" : "Frame Clipping: Disabled");
}
@@ -1137,7 +1136,7 @@ export default class ExcalidrawView extends TextFileView {
if (this.excalidrawData.hasMermaid(selectedImage.fileId) || getMermaidText(imageElement)) {
if(shouldRenderMermaid) {
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
api.updateScene({appState: {openDialog: { name: "ttd", tab: "mermaid" }}})
api.updateScene({appState: {openDialog: { name: "ttd", tab: "mermaid" }}, storeAction: "update"})
}
return;
}
@@ -1556,7 +1555,7 @@ export default class ExcalidrawView extends TextFileView {
...st,
theme,
},
storeAction: "none",
storeAction: "update",
});
}
@@ -1903,6 +1902,12 @@ export default class ExcalidrawView extends TextFileView {
return;
}
if (state.rename === "all") {
//@ts-ignore
this.app.fileManager.promptForFileRename(this.file);
return;
}
let query: string[] = null;
if (
@@ -2073,7 +2078,7 @@ export default class ExcalidrawView extends TextFileView {
return;
}
let counter = 0;
while (!this.file && counter++<50) await sleep(50);
while ((!this.file || !this.plugin.fourthFontLoaded) && counter++<50) await sleep(50);
if(!this.file) return;
this.compatibilityMode = this.file.extension === "excalidraw";
await this.plugin.loadSettings();
@@ -2380,7 +2385,7 @@ export default class ExcalidrawView extends TextFileView {
if(this.getSceneVersion(inData.scene.elements) !== this.previousSceneVersion) {
this.setDirty(3);
}
this.updateScene({elements: sceneElements});
this.updateScene({elements: sceneElements, storeAction: "capture"});
if(reloadFiles) this.loadSceneFiles();
} catch(e) {
errorlog({
@@ -2417,13 +2422,13 @@ export default class ExcalidrawView extends TextFileView {
? om.zenModeEnabled
: api.getAppState().zenModeEnabled;
//debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"})
api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont);
//api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont);
this.updateScene(
{
elements: excalidrawData.elements.concat(deletedElements??[]), //need to preserve deleted elements during autosave if images, links, etc. are updated
files: excalidrawData.files,
storeAction: justloaded ? "update" : "none",
storeAction: justloaded ? "update" : "update", //was none, but I think based on a false understanding of none
},
justloaded
);
@@ -2446,7 +2451,7 @@ export default class ExcalidrawView extends TextFileView {
pinnedScripts: this.plugin.settings.pinnedScripts,
customPens: this.plugin.settings.customPens.slice(0,this.plugin.settings.numberOfCustomPens),
},
storeAction: justloaded ? "update" : "none",
storeAction: justloaded ? "update" : "update", //was none, but I think based on a false understanding of none
},
);
if (
@@ -3608,7 +3613,7 @@ export default class ExcalidrawView extends TextFileView {
private canvasColorChangeHook(st: AppState) {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.canvasColorChangeHook, "ExcalidrawView.canvasColorChangeHook", st);
const canvasColor = st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor;
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}}));
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}, storeAction: "update"}));
setDynamicStyle(this.plugin.ea,this,canvasColor,this.plugin.settings.dynamicStyling);
if(this.plugin.ea.onCanvasColorChangeHook) {
try {
@@ -4318,7 +4323,7 @@ export default class ExcalidrawView extends TextFileView {
clone.rawText = WARNING;
elements[elements.indexOf(el[0])] = clone;
this.excalidrawData.setTextElement(clone.id,WARNING,()=>{});
this.updateScene({elements});
this.updateScene({elements, storeAction: "update"});
api.history.clear();
}
});
@@ -4342,7 +4347,7 @@ export default class ExcalidrawView extends TextFileView {
clone.isDeleted = true;
this.excalidrawData.deleteTextElement(clone.id);
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements});
this.updateScene({elements, storeAction: "update"});
const ea:ExcalidrawAutomate = getEA(this);
if(IMAGE_TYPES.contains(file.extension)) {
ea.selectElementsInView([await insertImageToView (ea, center, file)]);
@@ -4395,7 +4400,7 @@ export default class ExcalidrawView extends TextFileView {
}
elements[elements.indexOf(el[0])] = clone;
this.updateScene({elements});
this.updateScene({elements, storeAction: "update"});
if(clone.containerId) this.updateContainerSize(clone.containerId);
this.setDirty(8.1);
}
@@ -4925,7 +4930,7 @@ export default class ExcalidrawView extends TextFileView {
private setExcalidrawAPI (api: ExcalidrawImperativeAPI) {
this.excalidrawAPI = api;
api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont);
//api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont);
window.setTimeout(() => {
this.onAfterLoadScene(true);
this.excalidrawContainer?.focus();
@@ -4999,7 +5004,10 @@ export default class ExcalidrawView extends TextFileView {
private renderWelcomeScreen () {
const React = this.packages.react;
const {WelcomeScreen} = this.packages.excalidrawLib;
const filecount = this.app.vault.getFiles().filter(f=>this.plugin.isExcalidrawFile(f)).length;
const rank = filecount < 200 ? "Bronze" : filecount < 750 ? "Silver" : filecount < 2000 ? "Gold" : "Platinum";
const nextRankDelta = filecount < 200 ? 200 - filecount : filecount < 750 ? 750 - filecount : filecount < 2000 ? 2000 - filecount : 0;
const {decoration, title} = SwordColors[rank as Rank];
return React.createElement(
WelcomeScreen,
{},
@@ -5012,9 +5020,17 @@ export default class ExcalidrawView extends TextFileView {
React.createElement(
LogoWrapper,
{},
ICONS.ExcalidrawSword,
excalidrawSword(rank as Rank),
),
),
React.createElement(
WelcomeScreen.Center.Heading,
{
color: decoration,
message: nextRankDelta > 0 ? `${rank}: ${nextRankDelta} more drawings until the next rank!` : `${rank}: You're at the top. Keep on being legendary!`,
},
title,
),
React.createElement(
WelcomeScreen.Center.Heading,
{},
@@ -5045,9 +5061,9 @@ export default class ExcalidrawView extends TextFileView {
icon: ICONS.Discord,
href: "https://discord.gg/DyfAXFwUHc",
shortcut: null,
"aria-label": "Join the Visual Thinking Discord Server",
"aria-label": "Join the Discord Server",
},
" Join the Visual Thinking Discord Server"
" Join the Discord Server"
),
React.createElement(
WelcomeScreen.Center.MenuItemLink,
@@ -5059,6 +5075,16 @@ export default class ExcalidrawView extends TextFileView {
},
" Follow me on Twitter"
),
React.createElement(
WelcomeScreen.Center.MenuItemLink,
{
icon: ICONS.Learn,
href: "https://visual-thinking-workshop.com",
shortcut: null,
"aria-label": "Learn Visual PKM",
},
" Sign up for the Visual Thinking Workshop"
),
React.createElement(
WelcomeScreen.Center.MenuItemLink,
{
@@ -5409,6 +5435,7 @@ export default class ExcalidrawView extends TextFileView {
renderEmbeddable: this.renderEmbeddable.bind(this),
renderMermaid: shouldRenderMermaid,
obsidianHostPlugin: new WeakRef(this.plugin),
showDeprecatedFonts: true,
},
this.renderCustomActionsMenu(),
this.renderWelcomeScreen(),
@@ -5508,6 +5535,7 @@ export default class ExcalidrawView extends TextFileView {
}
api.updateScene({
appState: { pinnedScripts: this.plugin.settings.pinnedScripts },
storeAction: "update",
});
}
@@ -5519,8 +5547,9 @@ export default class ExcalidrawView extends TextFileView {
}
api.updateScene({
appState: {
customPens: this.plugin.settings.customPens.slice(0,this.plugin.settings.numberOfCustomPens)
customPens: this.plugin.settings.customPens.slice(0,this.plugin.settings.numberOfCustomPens),
},
storeAction: "update",
});
}
@@ -5532,6 +5561,7 @@ export default class ExcalidrawView extends TextFileView {
}
api.updateScene({
appState: { allowPinchZoom: this.plugin.settings.allowPinchZoom },
storeAction: "update",
});
}
@@ -5543,6 +5573,7 @@ export default class ExcalidrawView extends TextFileView {
}
api.updateScene({
appState: { allowWheelZoom: this.plugin.settings.allowWheelZoom },
storeAction: "update",
});
}
@@ -5555,6 +5586,7 @@ export default class ExcalidrawView extends TextFileView {
const st = api.getAppState();
api.updateScene({
appState: { trayModeEnabled: !st.trayModeEnabled },
storeAction: "update",
});
//just in case settings were updated via Obsidian sync

View File

@@ -12,7 +12,6 @@ import { ExportSettings } from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import {getIMGFilename,} from "./utils/FileUtils";
import {
embedFontsInSVG,
getEmbeddedFilenameParts,
getExportTheme,
getQuickImagePreview,
@@ -86,7 +85,14 @@ const _getPNG = async ({imgAttributes,filenameParts,theme,cacheReady,img,file,ex
? 2
: 1;
const cacheKey = {...filenameParts, isDark: theme==="dark", previewImageType: PreviewImageType.PNG, scale, isTransparent: !exportSettings.withBackground};
const cacheKey = {
...filenameParts,
isDark: theme==="dark",
previewImageType: PreviewImageType.PNG,
scale,
isTransparent: !exportSettings.withBackground,
inlineFonts: true, //though for PNG this makes no difference, but the key requires it
};
if(cacheReady) {
const src = await imageCache.getImageFromCache(cacheKey);
@@ -165,7 +171,16 @@ const _getSVGIMG = async ({filenameParts,theme,cacheReady,img,file,exportSetting
exportSettings: ExportSettings,
loader: EmbeddedFilesLoader,
}):Promise<HTMLImageElement> => {
const cacheKey = {...filenameParts, isDark: theme==="dark", previewImageType: PreviewImageType.SVGIMG, scale:1, isTransparent: !exportSettings.withBackground};
exportSettings.skipInliningFonts = false;
const cacheKey = {
...filenameParts,
isDark: theme==="dark",
previewImageType: PreviewImageType.SVGIMG,
scale:1,
isTransparent: !exportSettings.withBackground,
inlineFonts: !exportSettings.skipInliningFonts,
};
if(cacheReady) {
const src = await imageCache.getImageFromCache(cacheKey);
if(src && typeof src === "string") {
@@ -184,7 +199,7 @@ const _getSVGIMG = async ({filenameParts,theme,cacheReady,img,file,exportSetting
}
}
let svg = convertSVGStringToElement((
const svg = convertSVGStringToElement((
await createSVG(
filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref || filenameParts.hasFrameref || filenameParts.hasClippedFrameref
? filenameParts.filepath + filenameParts.linkpartReference
@@ -208,7 +223,6 @@ const _getSVGIMG = async ({filenameParts,theme,cacheReady,img,file,exportSetting
return null;
}
svg = embedFontsInSVG(svg, plugin, false);
//need to remove width and height attributes to support area= embeds
svg.removeAttribute("width");
svg.removeAttribute("height");
@@ -224,13 +238,21 @@ const _getSVGNative = async ({filenameParts,theme,cacheReady,containerElement,fi
exportSettings: ExportSettings,
loader: EmbeddedFilesLoader,
}):Promise<HTMLDivElement> => {
const cacheKey = {...filenameParts, isDark: theme==="dark", previewImageType: PreviewImageType.SVG, scale:1, isTransparent: !exportSettings.withBackground};
exportSettings.skipInliningFonts = false;
const cacheKey = {
...filenameParts,
isDark: theme==="dark",
previewImageType: PreviewImageType.SVG,
scale:1,
isTransparent: !exportSettings.withBackground,
inlineFonts: !exportSettings.skipInliningFonts,
};
let maybeSVG;
if(cacheReady) {
maybeSVG = await imageCache.getImageFromCache(cacheKey);
}
let svg = (maybeSVG && (maybeSVG instanceof SVGSVGElement))
const svg = (maybeSVG && (maybeSVG instanceof SVGSVGElement))
? maybeSVG
: convertSVGStringToElement((await createSVG(
filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref || filenameParts.hasFrameref || filenameParts.hasClippedFrameref
@@ -260,7 +282,7 @@ const _getSVGNative = async ({filenameParts,theme,cacheReady,containerElement,fi
if(!Boolean(maybeSVG)) {
cacheReady && imageCache.addImageToCache(cacheKey,"", svg);
}
svg = embedFontsInSVG(svg, plugin, true);
svg.removeAttribute("width");
svg.removeAttribute("height");
containerElement.append(svg);

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
import { customAlphabet } from "nanoid";
import { DeviceType } from "../types";
import { DeviceType } from "../types/types";
import { ExcalidrawLib } from "../ExcalidrawLib";
import { moment } from "obsidian";
import ExcalidrawPlugin from "src/main";
@@ -85,7 +85,7 @@ export const {
getCommonBoundingBox,
getMaximumGroups,
measureText,
getDefaultLineHeight,
getLineHeight,
wrapText,
getFontString,
getBoundTextMaxWidth,
@@ -94,10 +94,14 @@ export const {
mutateElement,
restore,
mermaidToExcalidraw,
getFontFamilyString,
getContainerElement,
refreshTextDimensions,
getFontDefinition,
} = excalidrawLib;
export const FONTS_STYLE_ID = "excalidraw-custom-fonts";
export function JSON_parse(x: string): any {
return JSON.parse(x.replaceAll("&#91;", "["));
}

View File

@@ -223,6 +223,7 @@ function RenderObsidianView(
} else {
const workspaceLeaf:HTMLDivElement = rootSplit.containerEl.querySelector("div.workspace-leaf");
if(workspaceLeaf) workspaceLeaf.style.borderRadius = "var(--embeddable-radius)";
rootSplit.containerEl.addClass("mod-visible");
containerRef.current.appendChild(rootSplit.containerEl);
setColors(containerRef.current, element, mdProps, canvasColor);
}

View File

@@ -184,7 +184,7 @@ export class EmbeddableSettings extends Modal {
new Notice("File rename failed. A file with this name already exists.\n"+newPath,10000);
} else {
try {
await this.app.vault.rename(this.file,newPath);
await this.app.fileManager.renameFile(this.file,newPath);
el.link = this.element.link.replace(
/(\[\[)([^#\]]*)([^\]]*]])/,`$1${
this.plugin.app.metadataCache.fileToLinktext(
@@ -226,7 +226,7 @@ export class EmbeddableSettings extends Modal {
(async() => {
await this.ea.addElementsToView();
//@ts-ignore
this.ea.viewUpdateScene({appState: {}});
this.ea.viewUpdateScene({appState: {}, storeAction: "update"});
this.close(); //close should only run once update scene is done
})();
} else {

View File

@@ -63,7 +63,8 @@ export const showFrameSettings = (ea: ExcalidrawAutomate) => {
// @ts-ignore
appState: {
frameRendering: settings
}
},
storeAction: "update",
});
frameSettingsModal.close();
})

View File

@@ -17,6 +17,31 @@ 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>
`,
"2.2.13": `
## Fixed
- Could not undo element after pasting [#1906](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1906)
- Links broke after renaming an Excalidraw file using the F2 shortcut [#1907](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1907)
- Unable to open or convert very large ${String.fromCharCode(96)}.excalidraw${String.fromCharCode(96)} file, e.g. BoaPs you can download from [here](https://ko-fi.com/zsolt/shop)
`,
"2.2.12": `
## Fixed
- Rename moved files to root folder [#1905](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1905)
- Fonts not displaying correctly in cached image previews
`,
"2.2.11": `
<img alt="badges" src="https://github.com/user-attachments/assets/7591b523-6bc6-46ff-b552-5c3492139e4c" referrerpolicy="no-referrer" style="width: 100%;">
## New
- Font picker with additional fonts (not yet fully configurable, but that will come in due time) [#8012](https://github.com/excalidraw/excalidraw/pull/8012)
- Introducing Visual Thinking Badges. The more you use Excalidraw the higher your rank will be. Levels are: Bronze, Silver, Gold and Platinum.
## Fixed
- Embedded PDF was not visible on phones [#1904](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1904)
- F2 does not rename files in Excalidraw View [#1900](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1900)
- Wireframe to Code now honors the GPT model settings in plugin settings. [#1901](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1901)
- Updated ExcaliAI to support gpt-4o for vision. [#1859](https://github.com/zsviczian/obsidian-excalidraw-plugin/pull/1859) 🙏@Saik0s
- Minor fixes from excalidraw.com [#8287](https://github.com/excalidraw/excalidraw/pull/8287), [#8285](https://github.com/excalidraw/excalidraw/pull/8285)
`,
"2.2.10": `
<div class="excalidraw-videoWrapper"><div>
<iframe src="https://www.youtube.com/embed/sjZfdqpxqsg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

File diff suppressed because one or more lines are too long

View File

@@ -100,7 +100,7 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
{
field: "style.fontFamily",
code: "[number]",
desc: "1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont",
desc: "1: Virgil, 2:Helvetica, 3:Cascadia, 4:Local Font, 5: Excalifont, 6: Nunito, 7: Lilita One, 8: Comic Shanns, 9: Liberation Sans",
after: "",
},
{
@@ -442,7 +442,19 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
{
field: "getExportSettings",
code: "getExportSettings(withBackground: boolean, withTheme: boolean,): ExportSettings;",
desc: "Utility function to generate ExportSettings object",
desc: "Utility function to generate ExportSettings object\n" +
"export interface ExportSettings {\n" +
" withBackground: boolean;\n" +
" withTheme: boolean;\n" +
" isMask: boolean; //if true elements will be processed as mask, clipping, etc.\n" +
" frameRendering?: { //optional, overrides relevant appState settings for rendering the frame\n" +
" enabled: boolean;\n" +
" name: boolean;\n" +
" outline: boolean;\n" +
" clip: boolean;\n" +
" };\n" +
" skipInliningFonts?: boolean;\n" +
"}",
after: "",
},
{

View File

@@ -230,7 +230,7 @@ export default {
"The default OpenAI API URL. This is a freetext field, so you can enter any valid OpenAI API compatible URL. " +
"Excalidraw will use this URL when posting API requests to OpenAI. I am not doing any error handling on this field, so make sure you enter a valid URL and only change this if you know what you are doing. ",
AI_OPENAI_DEFAULT_IMAGE_API_URL_NAME: "OpenAI Image Generation API URL",
AI_OPENAI_DEFAULT_VISION_MODEL_PLACEHOLDER: "Enter your default AI vision model here. e.g.: gpt-4-vision-preview",
AI_OPENAI_DEFAULT_VISION_MODEL_PLACEHOLDER: "Enter your default AI vision model here. e.g.: gpt-4o",
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",
@@ -647,7 +647,7 @@ FILENAME_HEAD: "Filename",
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 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. `,
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 local font option, which adds a local font to the list of fonts on the element properties panel for text elements. `,
RENDER_TWEAK_HEAD: "Rendering tweaks",
MAX_IMAGE_ZOOM_IN_NAME: "Maximum image zoom in resolution",
MAX_IMAGE_ZOOM_IN_DESC: "To save on memory and because Apple Safari (Obsidian on iOS) has some hard-coded limitations, Excalidraw.com limits the max resolution of images and large objects when zooming in. You can override this limitation using a multiplicator. " +
@@ -697,16 +697,16 @@ FILENAME_HEAD: "Filename",
"Enabling this feature simplifies the use of Excalidraw front matter properties, allowing you to leverage many powerful settings. If you prefer not to load these properties automatically, " +
"you can disable this feature, but you will need to manually remove any unwanted properties from the suggester. " +
"Note that turning on this setting requires restarting the plugin as properties are loaded at startup.",
CUSTOM_FONT_HEAD: "Fourth font",
ENABLE_FOURTH_FONT_NAME: "Enable fourth font option",
CUSTOM_FONT_HEAD: "Local font",
ENABLE_FOURTH_FONT_NAME: "Enable local font option",
ENABLE_FOURTH_FONT_DESC:
"By turning this on, you will see a fourth font button on the properties panel for text elements. " +
"Files that use this fourth font will (partly) lose their platform independence. " +
"By turning this on, you will see a local font in the font list on the properties panel for text elements. " +
"Files that use this local font will (partly) lose their platform independence. " +
"Depending on the custom font set in settings, they will look differently when loaded in another vault, or at a later time. " +
"Also the 4th font will display as system default font on excalidraw.com, or other Excalidraw versions.",
FOURTH_FONT_NAME: "Fourth font file",
FOURTH_FONT_NAME: "Local font file",
FOURTH_FONT_DESC:
"Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
"Select a .ttf, .woff or .woff2 font file from your vault to use as the local 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.",

View File

@@ -230,7 +230,7 @@ export default {
"默认的 OpenAI API URL。请填写有效的 OpenAI API URL。" +
"Excalidraw 会通过该 URL 发送 API 请求给 OpenAI。我没有对此选项做任何错误处理请谨慎修改。",
AI_OPENAI_DEFAULT_IMAGE_API_URL_NAME: "OpenAI 图像生成 API URL",
AI_OPENAI_DEFAULT_VISION_MODEL_PLACEHOLDER: "gpt-4-vision-preview",
AI_OPENAI_DEFAULT_VISION_MODEL_PLACEHOLDER: "输入你的默认 AI 模型名称例如gpt-4o",
SAVING_HEAD: "保存",
SAVING_DESC: "包括:压缩,自动保存的时间间隔,文件的命名格式和扩展名等的设置。",
COMPRESS_NAME: "压缩 Excalidraw JSON",
@@ -646,7 +646,7 @@ FILENAME_HEAD: "文件名",
LATEX_DEFAULT_NAME: "插入 LaTeX 时的默认表达式",
LATEX_DEFAULT_DESC: "允许留空。允许使用类似 <code>\\color{white}</code> 的格式化表达式。",
NONSTANDARD_HEAD: "非 Excalidraw.com 官方支持的特性",
NONSTANDARD_DESC: `这些特性不受 Excalidraw.com 官方支持。如果 Excalidraw.com 格式导出绘图,这些特性将会发生不可预知的变化。
NONSTANDARD_DESC: `这些特性不受 Excalidraw.com 官方支持。如果 Excalidraw.com 导入绘图,这些特性将会发生不可预知的变化。
包括:自定义画笔工具的数量,自定义字体等。`,
RENDER_TWEAK_HEAD: "渲染优化",
MAX_IMAGE_ZOOM_IN_NAME: "最大图像放大倍数",
@@ -697,7 +697,7 @@ FILENAME_HEAD: "文件名",
"启用此功能简化了 Excalidraw 前置属性的使用,使您能够利用许多强大的设置。如果您不希望自动加载这些属性," +
"您可以禁用此功能,但您将需要手动从自动提示中移除任何不需要的属性。" +
"请注意,启用此设置需要重启插件,因为属性是在启动时加载的。",
CUSTOM_FONT_HEAD: "自定义字体",
CUSTOM_FONT_HEAD: "本地字体",
ENABLE_FOURTH_FONT_NAME: "为文本元素启用本地字体",
ENABLE_FOURTH_FONT_DESC:
"开启此项后,文本元素的属性面板里会多出一个本地字体按钮。<br>" +

View File

@@ -43,13 +43,9 @@ import {
IMAGE_TYPES,
setExcalidrawPlugin,
DEVICE,
sceneCoordsToViewportCoords
} from "./constants/constants";
import {
VIRGIL_FONT,
VIRGIL_DATAURL,
sceneCoordsToViewportCoords,
FONTS_STYLE_ID,
} from "./constants/constFonts";
} from "./constants/constants";
import ExcalidrawView, { TextMode, getTextMode } from "./ExcalidrawView";
import {
changeThemeOfExcalidrawMD,
@@ -103,6 +99,7 @@ import {
decompress,
getImageSize,
versionUpdateCheckTimer,
getFontMetrics,
} from "./utils/Utils";
import { editorInsertText, extractSVGPNGFileName, foldExcalidrawSection, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "./utils/ObsidianUtils";
import { ExcalidrawElement, ExcalidrawEmbeddableElement, ExcalidrawImageElement, ExcalidrawTextElement, FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
@@ -116,7 +113,7 @@ import {
import { FieldSuggester } from "./dialogs/FieldSuggester";
import { ReleaseNotes } from "./dialogs/ReleaseNotes";
import { Packages } from "./types";
import { Packages } from "./types/types";
import { PreviewImageType } from "./utils/UtilTypes";
import { ScriptInstallPrompt } from "./dialogs/ScriptInstallPrompt";
import Taskbone from "./ocr/Taskbone";
@@ -138,14 +135,18 @@ import { ExcalidrawConfig } from "./utils/ExcalidrawConfig";
import { EditorHandler } from "./CodeMirrorExtension/EditorHandler";
import { clearMathJaxVariables } from "./LaTeX";
import { showFrameSettings } from "./dialogs/FrameSettings";
import { ExcalidrawLib } from "./ExcalidrawLib";
import { Rank, SwordColors } from "./menu/ActionIcons";
import { RankMessage } from "./dialogs/RankMessage";
declare let EXCALIDRAW_PACKAGES:string;
declare let react:any;
declare let reactDOM:any;
declare let excalidrawLib: any;
declare let excalidrawLib: typeof ExcalidrawLib;
declare let PLUGIN_VERSION:string;
export default class ExcalidrawPlugin extends Plugin {
public fourthFontLoaded: boolean = false;
public excalidrawConfig: ExcalidrawConfig;
public taskbone: Taskbone;
private excalidrawFiles: Set<TFile> = new Set<TFile>();
@@ -178,7 +179,6 @@ export default class ExcalidrawPlugin extends Plugin {
public equationsMaster: Map<FileId, string> = null; //fileId, formula
public mermaidsMaster: Map<FileId, string> = null; //fileId, mermaidText
public scriptEngine: ScriptEngine;
public fourthFontDef: string = VIRGIL_FONT;
private packageMap: Map<Window,Packages> = new Map<Window,Packages>();
public leafChangeTimeout: number = null;
private forceSaveCommand:Command;
@@ -189,6 +189,7 @@ export default class ExcalidrawPlugin extends Plugin {
public forceToOpenInMarkdownFilepath: string = null;
private slob:string;
private ribbonIcon:HTMLElement;
public loadTimestamp:number;
constructor(app: App, manifest: PluginManifest) {
super(app, manifest);
@@ -310,14 +311,23 @@ export default class ExcalidrawPlugin extends Plugin {
}*/
async onload() {
this.loadTimestamp = Date.now();
addIcon(ICON_NAME, EXCALIDRAW_ICON);
addIcon(SCRIPTENGINE_ICON_NAME, SCRIPTENGINE_ICON);
addIcon(EXPORT_IMG_ICON_NAME, EXPORT_IMG_ICON);
await this.loadSettings({reEnableAutosave:true});
const updateSettings = !this.settings.onceOffCompressFlagReset || !this.settings.onceOffGPTVersionReset;
if(!this.settings.onceOffCompressFlagReset) {
this.settings.compress = true;
this.settings.onceOffCompressFlagReset = true;
}
if(!this.settings.onceOffGPTVersionReset) {
if(this.settings.openAIDefaultVisionModel === "gpt-4-vision-preview") {
this.settings.openAIDefaultVisionModel = "gpt-4o";
}
}
if(updateSettings) {
await this.saveSettings();
}
this.excalidrawConfig = new ExcalidrawConfig(this);
@@ -392,26 +402,50 @@ export default class ExcalidrawPlugin extends Plugin {
public initializeFonts() {
this.app.workspace.onLayoutReady(async () => {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.initializeFonts,`ExcalidrawPlugin.initializeFonts > app.workspace.onLayoutReady`);
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.initializeFonts, `ExcalidrawPlugin.initializeFonts > app.workspace.onLayoutReady`);
const font = await getFontDataURL(
this.app,
this.settings.experimantalFourthFont,
"",
"LocalFont",
"Local Font",
);
const fourthFontDataURL =
font.dataURL === "" ? VIRGIL_DATAURL : font.dataURL;
this.fourthFontDef = font.fontDef;
this.getOpenObsidianDocuments().forEach((ownerDocument) => {
this.addFonts([
`@font-face{font-family:'LocalFont';src:url("${fourthFontDataURL}");font-display: swap;`,
],ownerDocument);
})
if(font.dataURL === "") {
this.fourthFontLoaded = true;
return;
}
const fourthFontDataURL = font.dataURL;
const f = this.app.metadataCache.getFirstLinkpathDest(this.settings.experimantalFourthFont, "");
// Call getFontMetrics with the fourthFontDataURL
let fontMetrics = f.extension.startsWith("woff") ? undefined : await getFontMetrics(fourthFontDataURL, "Local Font");
if (!fontMetrics) {
console.log("Font Metrics not found, using default");
fontMetrics = {
unitsPerEm: 1000,
ascender: 750,
descender: -250,
lineHeight: 1.2,
fontName: "Local Font",
}
}
this.packageMap.forEach(({excalidrawLib}) => {
(excalidrawLib as typeof ExcalidrawLib).registerLocalFont({metrics: fontMetrics as any, icon: null}, fourthFontDataURL);
});
// Add fonts to open Obsidian documents
for(const ownerDocument of this.getOpenObsidianDocuments()) {
await this.addFonts([
`@font-face{font-family:'Local Font';src:url("${fourthFontDataURL}");font-display: swap;font-weight: 400;`,
], ownerDocument);
};
if(!this.fourthFontLoaded) setTimeout(()=>{this.fourthFontLoaded = true},100);
});
}
public addFonts(declarations: string[],ownerDocument:Document = document) {
public async addFonts(declarations: string[],ownerDocument:Document = document) {
// replace the old local font <style> element with the one we just created
const newStylesheet = ownerDocument.createElement("style");
newStylesheet.id = FONTS_STYLE_ID;
@@ -421,7 +455,7 @@ export default class ExcalidrawPlugin extends Plugin {
if (oldStylesheet) {
ownerDocument.head.removeChild(oldStylesheet);
}
ownerDocument.fonts.load('20px LocalFont');
await ownerDocument.fonts.load('20px Local Font');
}
public removeFonts() {
@@ -2416,7 +2450,7 @@ export default class ExcalidrawPlugin extends Plugin {
log(fname);
const result = await this.app.vault.create(
fname,
FRONTMATTER + (await this.exportSceneToMD(data)),
FRONTMATTER + (await this.exportSceneToMD(data, false)),
);
if (this.settings.keepInSync) {
EXPORT_TYPES.forEach((ext: string) => {
@@ -3452,7 +3486,7 @@ export default class ExcalidrawPlugin extends Plugin {
* @param {string} data - Excalidraw scene JSON string
* @returns {string} - Text starting with the "# Text Elements" header and followed by each "## id-value" and text
*/
public async exportSceneToMD(data: string): Promise<string> {
public async exportSceneToMD(data: string, compressOverride?: boolean): Promise<string> {
if (!data) {
return "";
}
@@ -3477,7 +3511,9 @@ export default class ExcalidrawPlugin extends Plugin {
outString +
getMarkdownDrawingSection(
JSON.stringify(JSON_parse(data), null, "\t"),
this.settings.compress,
typeof compressOverride === "undefined"
? this.settings.compress
: compressOverride,
)
);
}
@@ -3507,6 +3543,21 @@ export default class ExcalidrawPlugin extends Plugin {
errorlog({file, error: "new drawing not recognized as an excalidraw file", fn: this.createDrawing});
}
if(Date.now() - this.loadTimestamp > 1){//2000) {
const filecount = this.app.vault.getFiles().filter(f=>this.isExcalidrawFile(f)).length;
const rank:Rank = filecount < 200 ? "Bronze" : filecount < 750 ? "Silver" : filecount < 2000 ? "Gold" : "Platinum";
const {grip, decoration, blade} = SwordColors[rank];
if(this.settings.rank !== rank) {
//in case the message was already displayed on another device and it was synced in the mean time
await this.loadSettings();
if(this.settings.rank !== rank) {
this.settings.rank = rank;
await this.saveSettings();
new RankMessage(this.app, this, filecount, rank, decoration, blade, grip).open();
}
}
}
return file;
}

File diff suppressed because one or more lines are too long

View File

@@ -70,7 +70,7 @@ export class EmbeddableMenu {
};
private async actionMarkdownSelection (file: TFile, isExcalidrawFile: boolean, subpath: string, element: ExcalidrawEmbeddableElement) {
this.view.updateScene({appState: {activeEmbeddable: null}});
this.view.updateScene({appState: {activeEmbeddable: null}, storeAction: "update"});
const sections = (await app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },file))
.blocks.filter((b: any) => b.display && b.node?.type === "heading")
@@ -98,7 +98,7 @@ export class EmbeddableMenu {
private async actionMarkdownBlock (file: TFile, subpath: string, element: ExcalidrawEmbeddableElement) {
if(!file) return;
this.view.updateScene({appState: {activeEmbeddable: null}});
this.view.updateScene({appState: {activeEmbeddable: null}, storeAction: "update"});
const paragraphs = (await app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },file))
.blocks.filter((b: any) => b.display && b.node &&

View File

@@ -34,7 +34,8 @@ export function setPen (pen: PenStyle, api: any) {
currentItemRoughness: st.currentItemRoughness,
}}
: null,
}
},
storeAction: "update",
})
}
@@ -50,7 +51,8 @@ export function resetStrokeOptions (resetCustomPen:any, api: ExcalidrawImperativ
}: null,
resetCustomPen: null,
...clearCurrentStrokeOptions ? {currentStrokeOptions: null} : null,
}
},
storeAction: "update",
});
}

View File

@@ -2,6 +2,7 @@ import {
App,
ButtonComponent,
DropdownComponent,
getIcon,
normalizePath,
PluginSettingTab,
Setting,
@@ -13,7 +14,7 @@ import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import type ExcalidrawPlugin from "./main";
import { PenStyle } from "./PenTypes";
import { DynamicStyle } from "./types";
import { DynamicStyle } from "./types/types";
import { PreviewImageType } from "./utils/UtilTypes";
import { setDynamicStyle } from "./utils/DynamicStyling";
import {
@@ -36,6 +37,8 @@ import { ModifierKeySettingsComponent } from "./dialogs/ModifierKeySettings";
import { ANNOTATED_PREFIX, CROPPED_PREFIX } from "./utils/CarveOut";
import { EDITOR_FADEOUT } from "./CodeMirrorExtension/EditorHandler";
import { setDebugging } from "./utils/DebugHelper";
import { link } from "fs";
import { Rank } from "./menu/ActionIcons";
export interface ExcalidrawSettings {
folder: string;
@@ -47,6 +50,7 @@ export interface ExcalidrawSettings {
compress: boolean;
decompressForMDView: boolean;
onceOffCompressFlagReset: boolean; //used to reset compress to true in 2.2.0
onceOffGPTVersionReset: boolean; //used to reset GPT version in 2.2.11
autosave: boolean;
autosaveIntervalDesktop: number;
autosaveIntervalMobile: number;
@@ -196,6 +200,7 @@ export interface ExcalidrawSettings {
longPressDesktop: number;
longPressMobile: number;
isDebugMode: boolean;
rank: Rank;
}
declare const PLUGIN_VERSION:string;
@@ -210,6 +215,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
compress: true,
decompressForMDView: false,
onceOffCompressFlagReset: false,
onceOffGPTVersionReset: false,
autosave: true,
autosaveIntervalDesktop: 15000,
autosaveIntervalMobile: 10000,
@@ -360,7 +366,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
startupScriptPath: "",
openAIAPIToken: "",
openAIDefaultTextModel: "gpt-3.5-turbo-1106",
openAIDefaultVisionModel: "gpt-4-vision-preview",
openAIDefaultVisionModel: "gpt-4o",
openAIDefaultImageGenerationModel: "dall-e-3",
openAIURL: "https://api.openai.com/v1/chat/completions",
openAIImageGenerationURL: "https://api.openai.com/v1/images/generations",
@@ -451,6 +457,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
longPressDesktop: 500,
longPressMobile: 500,
isDebugMode: false,
rank: "Bronze",
};
export class ExcalidrawSettingTab extends PluginSettingTab {
@@ -539,6 +546,46 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
},
});
coffeeImg.height = 45;
const iconLinks = [
{
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"></path><path d="M9 18c-4.51 2-5-2-7-2"></path></svg>`,
href: "https://github.com/zsviczian/obsidian-excalidraw-plugin/issues",
aria: "Report bugs and raise feature requsts on the plugin's GitHub page",
text: "Bugs and Feature Requests",
},
{
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6 0 0 0 0 0 0z"></path><polygon points="10 15 15 12 10 9"></polygon></svg>`,
href: "https://www.youtube.com/@VisualPKM",
aria: "Check out my YouTube channel to learn about Visual Thinking and Excalidraw",
text: "Visual PKM on YouTube",
},
{
icon: `<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" stroke="none" strokeWidth="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 640 512"><path d="M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z"/></svg>`,
href: "https://discord.gg/DyfAXFwUHc",
aria: "Join the Visual Thinking Workshop Discord Server",
text: "Community on Discord",
},
{
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"></path></svg>`,
href: "https://twitter.com/zsviczian",
aria: "Follow me on Twitter",
text: "Follow me on Twitter",
},
{
icon: getIcon("graduation-cap").outerHTML,
href: "https://visual-thinking-workshop.com",
aria: "Learn about Visual PKM, Excalidraw, Obsidian, ExcaliBrain and more",
text: "Join the Visual Thinking Workshop",
}
];
const linksEl = containerEl.createDiv("setting-item-description excalidraw-settings-links-container");
iconLinks.forEach(({ icon, href, aria, text }) => {
linksEl.createEl("a",{href, attr: { "aria-label": aria }}, (a)=> {
a.innerHTML = icon + text;
});
});
// ------------------------------------------------
// Saving

View File

@@ -1,83 +1,83 @@
import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
import { ExcalidrawLib } from "./ExcalidrawLib";
export type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
export type Packages = {
react: any,
reactDOM: any,
excalidrawLib: typeof ExcalidrawLib,
}
export type ValueOf<T> = T[keyof T];
export type DynamicStyle = "none" | "gray" | "colorful";
export type DeviceType = {
isDesktop: boolean,
isPhone: boolean,
isTablet: boolean,
isMobile: boolean,
isLinux: boolean,
isMacOS: boolean,
isWindows: boolean,
isIOS: boolean,
isAndroid: boolean
};
declare global {
interface Window {
ExcalidrawAutomate: ExcalidrawAutomate;
}
}
declare module "obsidian" {
interface App {
internalPlugins: any;
isMobile(): boolean;
getObsidianUrl(file:TFile): string;
metadataTypeManager: {
setType(name:string, type:string): void;
};
}
interface Keymap {
getRootScope(): Scope;
}
interface Scope {
keys: any[];
}
interface Workspace {
on(
name: "hover-link",
callback: (e: MouseEvent) => any,
ctx?: any,
): EventRef;
}
interface DataAdapter {
url: {
pathToFileURL(path: string): URL;
},
basePath: string;
}
interface FoldPosition {
from: number;
to: number;
}
interface FoldInfo {
folds: FoldPosition[];
lines: number;
}
interface MarkdownSubView {
applyFoldInfo(foldInfo: FoldInfo): void;
getFoldInfo(): FoldInfo | null;
}
/*interface Editor {
insertText(data: string): void;
}*/
interface MetadataCache {
getBacklinksForFile(file: TFile): any;
getLinks(): { [id: string]: Array<{ link: string; displayText: string; original: string; position: any }> };
}
import { ExcalidrawAutomate } from "../ExcalidrawAutomate";
import { ExcalidrawLib } from "../ExcalidrawLib";
export type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
export type Packages = {
react: any,
reactDOM: any,
excalidrawLib: typeof ExcalidrawLib,
}
export type ValueOf<T> = T[keyof T];
export type DynamicStyle = "none" | "gray" | "colorful";
export type DeviceType = {
isDesktop: boolean,
isPhone: boolean,
isTablet: boolean,
isMobile: boolean,
isLinux: boolean,
isMacOS: boolean,
isWindows: boolean,
isIOS: boolean,
isAndroid: boolean
};
declare global {
interface Window {
ExcalidrawAutomate: ExcalidrawAutomate;
}
}
declare module "obsidian" {
interface App {
internalPlugins: any;
isMobile(): boolean;
getObsidianUrl(file:TFile): string;
metadataTypeManager: {
setType(name:string, type:string): void;
};
}
interface Keymap {
getRootScope(): Scope;
}
interface Scope {
keys: any[];
}
interface Workspace {
on(
name: "hover-link",
callback: (e: MouseEvent) => any,
ctx?: any,
): EventRef;
}
interface DataAdapter {
url: {
pathToFileURL(path: string): URL;
},
basePath: string;
}
interface FoldPosition {
from: number;
to: number;
}
interface FoldInfo {
folds: FoldPosition[];
lines: number;
}
interface MarkdownSubView {
applyFoldInfo(foldInfo: FoldInfo): void;
getFoldInfo(): FoldInfo | null;
}
/*interface Editor {
insertText(data: string): void;
}*/
interface MetadataCache {
getBacklinksForFile(file: TFile): any;
getLinks(): { [id: string]: Array<{ link: string; displayText: string; original: string; position: any }> };
}
}

View File

@@ -6,7 +6,6 @@ import { Notice } from "obsidian";
import { getEA } from "src";
import { ExcalidrawAutomate, cloneElement } from "src/ExcalidrawAutomate";
import { ExportSettings } from "src/ExcalidrawView";
import { embedFontsInSVG } from "./Utils";
import { nanoid } from "src/constants/constants";
export class CropImage {
@@ -92,7 +91,7 @@ export class CropImage {
isMask: false,
}
const maskSVG = await this.maskEA.createSVG(null,false,exportSettings,null,null,0);
const maskSVG = await this.maskEA.createSVG(null,true,exportSettings,null,null,0);
const defs = maskSVG.querySelector("defs");
const styleEl = maskSVG.querySelector("style");
const style = styleEl ? styleEl.outerHTML : "";
@@ -138,7 +137,7 @@ export class CropImage {
async getCroppedPNG(): Promise<Blob> {
//@ts-ignore
const PLUGIN = app.plugins.plugins["obsidian-excalidraw-plugin"];
const svg = embedFontsInSVG(await this.buildSVG(), PLUGIN);
const svg = await this.buildSVG();
return new Promise((resolve, reject) => {
const svgData = new XMLSerializer().serializeToString(svg);
const canvas = document.createElement('canvas');

View File

@@ -2,7 +2,7 @@ import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/
import { ColorMaster } from "colormaster";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import ExcalidrawView from "src/ExcalidrawView";
import { DynamicStyle } from "src/types";
import { DynamicStyle } from "src/types/types";
import { cloneElement } from "src/ExcalidrawAutomate";
import { ExcalidrawFrameElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { addAppendUpdateCustomData } from "./Utils";
@@ -16,7 +16,7 @@ export const setDynamicStyle = (
) => {
if(dynamicStyle === "none") {
view.excalidrawContainer?.removeAttribute("style");
setTimeout(()=>view.updateScene({appState:{dynamicStyle: ""}}));
setTimeout(()=>view.updateScene({appState:{dynamicStyle: ""}, storeAction: "update"}));
const toolspanel = view.toolsPanelRef?.current?.containerRef?.current;
if(toolspanel) {
let toolsStyle = toolspanel.getAttribute("style");
@@ -167,7 +167,8 @@ export const setDynamicStyle = (
appState:{
frameColor,
dynamicStyle: styleObject
}
},
storeAction: "update",
});
view = null;
ea = null;

View File

@@ -236,8 +236,8 @@ export async function addBackOfTheNoteCard(
const el = ea.getViewElements().find(el=>el.id === id);
api.selectElements([el]);
if(activate) {
setTimeout(()=>{
api.updateScene({appState: {activeEmbeddable: {element: el, state: "active"}}});
window.setTimeout(()=>{
api.updateScene({appState: {activeEmbeddable: {element: el, state: "active"}}, storeAction: "update"});
if(found) view.getEmbeddableLeafElementById(el.id)?.editNode?.();
});
}

View File

@@ -11,6 +11,7 @@ import { getAttachmentsFolderAndFilePath } from "./ObsidianUtils";
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
* @param filepath
* @returns folderpath will be normalized. This means "/" for root folder and no trailing "/" for other folders
*/
type ImageExtension = keyof typeof IMAGE_MIME_TYPES;

View File

@@ -21,11 +21,13 @@ export type ImageKey = {
previewImageType: PreviewImageType;
scale: number;
isTransparent: boolean;
inlineFonts: boolean;
} & FILENAMEPARTS;
const getKey = (key: ImageKey): string =>
`${key.filepath}#${key.blockref??""}#${key.sectionref??""}#${key.isDark ? 1 : 0}#${
key.hasGroupref}#${key.hasArearef}#${key.hasFrameref}#${key.hasClippedFrameref}#${key.hasSectionref}#${
key.hasGroupref}#${key.hasArearef}#${key.hasFrameref}#${key.hasClippedFrameref}#${
key.hasSectionref}#${key.inlineFonts}#${
key.previewImageType === PreviewImageType.SVGIMG
? 1
: key.previewImageType === PreviewImageType.PNG
@@ -172,7 +174,7 @@ class ImageCache {
const cursor = (event.target as IDBRequest<IDBCursorWithValue | null>).result;
if(cursor) {
const key = cursor.key as string;
const isLegacyKey = key.split("#").length-1 < 11; // introduced hasGroupref, etc. in 1.9.28 // introduced hasClippedFrameref in 2.2.10
const isLegacyKey = key.split("#").length-1 < 12; // introduced hasGroupref, etc. in 1.9.28 // introduced hasClippedFrameref in 2.2.10 //introduced inlineFonts 2.2.11
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;

View File

@@ -1,18 +1,12 @@
import {
App,
Notice,
request,
requestUrl,
request,requestUrl,
TFile,
TFolder,
} from "obsidian";
import { Random } from "roughjs/bin/math";
import { BinaryFileData, DataURL} from "@zsviczian/excalidraw/types/excalidraw/types";
import {
ASSISTANT_FONT,
CASCADIA_FONT,
VIRGIL_FONT,
} from "src/constants/constFonts";
import {
exportToSvg,
exportToBlob,
@@ -33,7 +27,7 @@ import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
import { cleanBlockRef, cleanSectionHeading, getFileCSSClasses } from "./ObsidianUtils";
import { updateElementLinksToObsidianLinks } from "src/ExcalidrawAutomate";
import { CropImage } from "./CropImage";
import opentype from 'opentype.js';
declare const PLUGIN_VERSION:string;
declare var LZString: any;
@@ -314,6 +308,7 @@ export async function getSVG (
exportPadding: exportSettings.frameRendering ? 0 : padding,
exportingFrame: null,
renderEmbeddables: true,
skipInliningFonts: exportSettings.skipInliningFonts,
});
}
if(svg) {
@@ -404,34 +399,6 @@ export async function getQuickImagePreview (
}
};
export function embedFontsInSVG(
svg: SVGSVGElement,
plugin: ExcalidrawPlugin,
localOnly: boolean = false,
): SVGSVGElement {
//replace font references with base64 fonts)
const includesVirgil = !localOnly &&
svg.querySelector("text[font-family^='Virgil']") !== null;
const includesCascadia = !localOnly &&
svg.querySelector("text[font-family^='Cascadia']") !== null;
const includesAssistant = !localOnly &&
svg.querySelector("text[font-family^='Assistant']") !== null;
const includesLocalFont =
svg.querySelector("text[font-family^='LocalFont']") !== null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil || includesLocalFont || includesAssistant)) {
let style = defs.querySelector("style");
if (!style) {
style = document.createElement("style");
defs.appendChild(style);
}
style.innerHTML = `${includesVirgil ? (VIRGIL_FONT + "\n") : ""}${
includesCascadia ? (CASCADIA_FONT + "\n") : ""}${
includesAssistant ? (ASSISTANT_FONT + "\n") : ""
}${includesLocalFont ? (plugin.fourthFontDef + "\n") : ""}`;
}
return svg;
};
export async function getImageSize (
src: string,
@@ -924,4 +891,34 @@ export function addIframe (containerEl: HTMLElement, link:string, startAt?: numb
sandbox: "allow-forms allow-presentation allow-same-origin allow-scripts allow-modals",
},
});
}
}
export interface FontMetrics {
unitsPerEm: number;
ascender: number;
descender: number;
lineHeight: number;
fontName: string;
}
export async function getFontMetrics(fontUrl: string, name: string): Promise<FontMetrics | null> {
try {
const font = await opentype.load(fontUrl);
const unitsPerEm = font.unitsPerEm;
const ascender = font.ascender;
const descender = font.descender;
const lineHeight = (ascender - descender) / unitsPerEm;
const fontName = font.names.fontFamily.en ?? name;
return {
unitsPerEm,
ascender,
descender,
lineHeight,
fontName,
};
} catch (error) {
console.error('Error loading font:', error);
return null;
}
}

View File

@@ -594,4 +594,35 @@ root {
textarea.excalidraw-wysiwyg, .excalidraw input {
caret-color: var(--excalidraw-caret-color);
}
.excalidraw-settings-links-container {
display: flex; /* Align SVG and text horizontally */
align-items: center; /* Center SVG and text vertically */
text-decoration: none; /* Remove underline from links */
color: inherit; /* Inherit text color */
text-align: center;
}
.excalidraw-settings-links-container a {
display: flex; /* Align children horizontally */
align-items: center; /* Center items vertically */
text-align: left;
}
.excalidraw-settings-links-container svg {
margin-right: 8px;
height: 30px;
width: 30px;
}
.excalidraw-rank {
text-align: center;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.excalidraw-rank svg {
height: 8rem;
width: 8rem;
}

View File

@@ -18,11 +18,11 @@
"DOM.Iterable"
],
"jsx": "react",
"inlineSourceMap": true
"inlineSourceMap": true,
},
"include": [
"**/*.ts",
"**/*.tsx", "src/Dialogs/OpenDrawing.ts",
"src/types.d.ts"
"src/types/types.d.ts",
]
}

View File

@@ -14,14 +14,14 @@
"dom",
"scripthost",
"es2015",
"esnext",
"ESNext",
"DOM.Iterable"
],
"jsx": "react"
"jsx": "react",
},
"include": [
"**/*.ts",
"**/*.tsx", "src/Dialogs/OpenDrawing.ts",
"src/types.d.ts"
"src/types/types.d.ts",
]
}