feat: Support LaTeX and AsciiMath via MathJax on stem.excalidraw.com
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import { useEffect } from "react";
|
||||
import { ExcalidrawImperativeAPI } from "../../../../types";
|
||||
|
||||
// Extension authors: provide a extension name here like "myextension"
|
||||
export const EmptyExtension = "empty";
|
||||
|
||||
// Extension authors: provide a hook like `useMyExtension` in `myextension/index`
|
||||
export const useEmptyExtension = (api: ExcalidrawImperativeAPI | null) => {
|
||||
const enabled = emptyExtensionLoadable;
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
}
|
||||
}, [enabled, api]);
|
||||
};
|
||||
|
||||
// Extension authors: Use a variable like `myExtensionLoadable` to determine
|
||||
// whether or not to do anything in each of `useMyExtension` and `testMyExtension`.
|
||||
let emptyExtensionLoadable = false;
|
||||
|
||||
export const getEmptyExtensionLoadable = () => {
|
||||
return emptyExtensionLoadable;
|
||||
};
|
||||
|
||||
export const setEmptyExtensionLoadable = (loadable: boolean) => {
|
||||
emptyExtensionLoadable = loadable;
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare module SREfeature {
|
||||
function custom(locale: string): Promise<string>;
|
||||
export = custom;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Theme } from "../../../../element/types";
|
||||
import { createIcon, iconFillColor } from "../../../../components/icons";
|
||||
|
||||
// We inline font-awesome icons in order to save on js size rather than including the font awesome react library
|
||||
export const mathSubtypeIcon = ({ theme }: { theme: Theme }) =>
|
||||
createIcon(
|
||||
<path
|
||||
fill={iconFillColor(theme)}
|
||||
// fa-square-root-variable-solid
|
||||
d="M289 24.2C292.5 10 305.3 0 320 0H544c17.7 0 32 14.3 32 32s-14.3 32-32 32H345L239 487.8c-3.2 13-14.2 22.6-27.6 24s-26.1-5.5-32.1-17.5L76.2 288H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96c12.1 0 23.2 6.8 28.6 17.7l73.3 146.6L289 24.2zM393.4 233.4c12.5-12.5 32.8-12.5 45.3 0L480 274.7l41.4-41.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L525.3 320l41.4 41.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L480 365.3l-41.4 41.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L434.7 320l-41.4-41.4c-12.5-12.5-12.5-32.8 0-45.3z"
|
||||
/>,
|
||||
{ width: 576, height: 512, mirror: true, strokeWidth: 1.25 },
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
import { useEffect } from "react";
|
||||
import { ExcalidrawImperativeAPI } from "../../../../types";
|
||||
import { addSubtypeMethods } from "../../../../subtypes";
|
||||
import { getMathSubtypeRecord } from "./types";
|
||||
import { prepareMathSubtype } from "./implementation";
|
||||
|
||||
export const MathJaxExtension = "mathjax";
|
||||
|
||||
// Extension authors: provide a hook like `useMyExtension` in `myextension/index`
|
||||
export const useMathJaxExtension = (api: ExcalidrawImperativeAPI | null) => {
|
||||
const enabled = mathJaxExtensionLoadable;
|
||||
useEffect(() => {
|
||||
if (enabled && api) {
|
||||
const prep = api.addSubtype(getMathSubtypeRecord(), prepareMathSubtype);
|
||||
if (prep) {
|
||||
addSubtypeMethods(getMathSubtypeRecord().subtype, prep.methods);
|
||||
}
|
||||
}
|
||||
}, [enabled, api]);
|
||||
};
|
||||
|
||||
// Extension authors: Use a variable like `myExtensionLoadable` to determine
|
||||
// whether or not to do anything in each of `useMyExtension` and `testMyExtension`.
|
||||
let mathJaxExtensionLoadable = false;
|
||||
|
||||
export const getMathJaxExtensionLoadable = () => {
|
||||
return mathJaxExtensionLoadable;
|
||||
};
|
||||
|
||||
export const setMathJaxExtensionLoadable = (loadable: boolean) => {
|
||||
mathJaxExtensionLoadable = loadable;
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"labels": {
|
||||
"changeMathOnly": "Math display",
|
||||
"mathOnlyTrue": "Math only",
|
||||
"mathOnlyFalse": "Mixed text",
|
||||
"resetUseTex": "Reset math input type",
|
||||
"useTexTrueActive": "✔ Standard input",
|
||||
"useTexTrueInactive": "Standard input",
|
||||
"useTexFalseActive": "✔ Simplified input",
|
||||
"useTexFalseInactive": "Simplified input"
|
||||
},
|
||||
"toolBar": {
|
||||
"math": "Math"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { getShortcutKey } from "../../../../utils";
|
||||
import { SubtypeRecord } from "../../../../subtypes";
|
||||
|
||||
// Exports
|
||||
export const getMathSubtypeRecord = () => mathSubtype;
|
||||
|
||||
// Use `getMathSubtype` so we don't have to export this
|
||||
const mathSubtype: SubtypeRecord = {
|
||||
subtype: "math",
|
||||
parents: ["text"],
|
||||
actionNames: ["useTexTrue", "useTexFalse", "resetUseTex", "changeMathOnly"],
|
||||
disabledNames: ["changeFontFamily"],
|
||||
shortcutMap: {
|
||||
resetUseTex: [getShortcutKey("Shift+R")],
|
||||
},
|
||||
alwaysEnabledNames: ["useTexTrue", "useTexFalse"],
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { ExcalidrawImperativeAPI } from "../../../types";
|
||||
import {
|
||||
EmptyExtension,
|
||||
setEmptyExtensionLoadable,
|
||||
useEmptyExtension,
|
||||
} from "./empty";
|
||||
import {
|
||||
MathJaxExtension,
|
||||
setMathJaxExtensionLoadable,
|
||||
useMathJaxExtension,
|
||||
} from "./mathjax";
|
||||
|
||||
// Extension authors: do imports like follows:
|
||||
// ```
|
||||
// import {
|
||||
// MyExtension,
|
||||
// setMyExtensionLoadable,
|
||||
// useMyExtension,
|
||||
// } from "./myExtension";
|
||||
// ```
|
||||
|
||||
// Extension authors: include `MyExtension` in `validExtensions`
|
||||
const validExtensions: readonly string[] = [EmptyExtension, MathJaxExtension];
|
||||
const extensionsUsed: string[] = [];
|
||||
|
||||
// The main invocation hook for use in the UI
|
||||
export const useExtensions = (
|
||||
api: ExcalidrawImperativeAPI | null,
|
||||
extensions?: string[],
|
||||
) => {
|
||||
selectExtensionsToLoad(extensions);
|
||||
useEmptyExtension(api);
|
||||
useMathJaxExtension(api);
|
||||
// Extension authors: add a line here like `useMyExtension();`
|
||||
};
|
||||
|
||||
// This MUST be called before the `useExtension`/`testExtension` calls.
|
||||
const selectExtensionsToLoad = (extensions?: string[]) => {
|
||||
const extensionList: string[] = [];
|
||||
if (extensions === undefined) {
|
||||
extensionList.push(...validExtensions);
|
||||
} else {
|
||||
extensions.forEach(
|
||||
(val) => validExtensions.includes(val) && extensionList.push(val),
|
||||
);
|
||||
}
|
||||
while (extensionsUsed.length > 0) {
|
||||
extensionsUsed.pop();
|
||||
}
|
||||
extensionsUsed.push(...extensionList);
|
||||
setLoadableExtensions();
|
||||
};
|
||||
|
||||
const setLoadableExtensions = () => {
|
||||
setEmptyExtensionLoadable(extensionsUsed.includes(EmptyExtension));
|
||||
setMathJaxExtensionLoadable(extensionsUsed.includes(MathJaxExtension));
|
||||
// Extension authors: add a line here like
|
||||
// `setMyExtensionLoadable(extensionsUsed.includes(MyExtension));`
|
||||
};
|
||||
Reference in New Issue
Block a user