feat: dark theme export background
This commit is contained in:
parent
baa133cbb7
commit
787f5d68cf
@ -25,7 +25,7 @@ import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import { Theme } from "../element/types";
|
||||
|
||||
import "../components/ToolIcon.scss";
|
||||
import Select from "../components/Select";
|
||||
import Select, { convertToSelectItems } from "../components/Select";
|
||||
|
||||
export const actionChangeProjectName = register({
|
||||
name: "changeProjectName",
|
||||
@ -119,21 +119,27 @@ export const actionChangeFancyBackgroundImageUrl = register({
|
||||
trackEvent: { category: "export", action: "toggleBackgroundImage" },
|
||||
perform: (_elements, appState, value) => {
|
||||
return {
|
||||
appState: { ...appState, fancyBackgroundImageUrl: value },
|
||||
appState: { ...appState, fancyBackgroundImageKey: value },
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ updateData }) => (
|
||||
<Select
|
||||
items={FANCY_BACKGROUND_IMAGES}
|
||||
ariaLabel={t("imageExportDialog.label.backgroundImage")}
|
||||
placeholder={t("imageExportDialog.label.backgroundImage")}
|
||||
value={DEFAULT_FANCY_BACKGROUND_IMAGE}
|
||||
onChange={(value) => {
|
||||
updateData(value);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
PanelComponent: ({ updateData }) => {
|
||||
const items = convertToSelectItems(
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
(item) => item.label,
|
||||
);
|
||||
return (
|
||||
<Select
|
||||
items={items}
|
||||
ariaLabel={t("imageExportDialog.label.backgroundImage")}
|
||||
placeholder={t("imageExportDialog.label.backgroundImage")}
|
||||
value={DEFAULT_FANCY_BACKGROUND_IMAGE}
|
||||
onChange={(value) => {
|
||||
updateData(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const actionChangeExportEmbedScene = register({
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_TEXT_ALIGN,
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
EXPORT_SCALES,
|
||||
THEME,
|
||||
} from "./constants";
|
||||
@ -101,8 +100,7 @@ export const getDefaultAppState = (): Omit<
|
||||
pendingImageElementId: null,
|
||||
showHyperlinkPopup: false,
|
||||
selectedLinearElement: null,
|
||||
fancyBackgroundImageUrl:
|
||||
FANCY_BACKGROUND_IMAGES[DEFAULT_FANCY_BACKGROUND_IMAGE].path,
|
||||
fancyBackgroundImageKey: DEFAULT_FANCY_BACKGROUND_IMAGE,
|
||||
};
|
||||
};
|
||||
|
||||
@ -210,7 +208,7 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
pendingImageElementId: { browser: false, export: false, server: false },
|
||||
showHyperlinkPopup: { browser: false, export: false, server: false },
|
||||
selectedLinearElement: { browser: true, export: false, server: false },
|
||||
fancyBackgroundImageUrl: { browser: false, export: false, server: false },
|
||||
fancyBackgroundImageKey: { browser: false, export: false, server: false },
|
||||
});
|
||||
|
||||
const _clearAppStateForStorage = <
|
||||
|
@ -38,7 +38,7 @@ import { Tooltip } from "./Tooltip";
|
||||
import "./ImageExportDialog.scss";
|
||||
import { useAppProps } from "./App";
|
||||
import { FilledButton } from "./FilledButton";
|
||||
import Select from "./Select";
|
||||
import Select, { convertToSelectItems } from "./Select";
|
||||
|
||||
const supportsContextFilters =
|
||||
"filter" in document.createElement("canvas").getContext("2d")!;
|
||||
@ -69,6 +69,11 @@ function isBackgroundImageKey(
|
||||
return key in FANCY_BACKGROUND_IMAGES;
|
||||
}
|
||||
|
||||
const backgroundSelectItems = convertToSelectItems(
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
(item) => item.label,
|
||||
);
|
||||
|
||||
const ImageExportModal = ({
|
||||
appState,
|
||||
elements,
|
||||
@ -138,7 +143,7 @@ const ImageExportModal = ({
|
||||
}, [
|
||||
appState,
|
||||
appState.exportBackground,
|
||||
appState.fancyBackgroundImageUrl,
|
||||
appState.fancyBackgroundImageKey,
|
||||
files,
|
||||
exportedElements,
|
||||
]);
|
||||
@ -150,7 +155,9 @@ const ImageExportModal = ({
|
||||
<div
|
||||
className={clsx("ImageExportModal__preview__canvas", {
|
||||
"ImageExportModal__preview__canvas--img-bcg":
|
||||
appState.exportBackground && appState.fancyBackgroundImageUrl,
|
||||
appState.exportBackground &&
|
||||
appState.fancyBackgroundImageKey &&
|
||||
appState.fancyBackgroundImageKey !== "solid",
|
||||
})}
|
||||
ref={previewRef}
|
||||
>
|
||||
@ -199,7 +206,7 @@ const ImageExportModal = ({
|
||||
>
|
||||
{exportWithBackground && (
|
||||
<Select
|
||||
items={FANCY_BACKGROUND_IMAGES}
|
||||
items={backgroundSelectItems}
|
||||
ariaLabel={t("imageExportDialog.label.backgroundImage")}
|
||||
placeholder={t("imageExportDialog.label.backgroundImage")}
|
||||
value={exportBackgroundImage}
|
||||
@ -209,7 +216,7 @@ const ImageExportModal = ({
|
||||
actionManager.executeAction(
|
||||
actionChangeFancyBackgroundImageUrl,
|
||||
"ui",
|
||||
FANCY_BACKGROUND_IMAGES[value].path,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
@ -4,23 +4,39 @@ import * as RadixSelect from "@radix-ui/react-select";
|
||||
import "./Select.scss";
|
||||
import { tablerChevronDownIcon, tablerChevronUpIcon } from "./icons";
|
||||
|
||||
type SelectItems = Record<string, { path: string | null; label: string }>;
|
||||
type SelectItems<T extends string> = Record<T, string>;
|
||||
|
||||
export type SelectProps = {
|
||||
items: SelectItems;
|
||||
value: keyof SelectItems;
|
||||
onChange: (value: keyof SelectItems) => void;
|
||||
export type SelectProps<T extends string> = {
|
||||
items: SelectItems<T>;
|
||||
value: T;
|
||||
onChange: (value: T) => void;
|
||||
placeholder?: string;
|
||||
ariaLabel?: string;
|
||||
};
|
||||
|
||||
const Select = ({
|
||||
type ConverterFunction<T> = (
|
||||
items: Record<string, T>,
|
||||
getLabel: (item: T) => string,
|
||||
) => SelectItems<string>;
|
||||
|
||||
export const convertToSelectItems: ConverterFunction<any> = (
|
||||
items,
|
||||
getLabel,
|
||||
) => {
|
||||
const result: SelectItems<string> = {};
|
||||
for (const key in items) {
|
||||
result[key] = getLabel(items[key]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const Select = <T extends string>({
|
||||
items,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
ariaLabel,
|
||||
}: SelectProps) => (
|
||||
}: SelectProps<T>) => (
|
||||
<RadixSelect.Root value={value} onValueChange={onChange}>
|
||||
<RadixSelect.Trigger
|
||||
className="Select__trigger"
|
||||
@ -41,11 +57,13 @@ const Select = ({
|
||||
</RadixSelect.ScrollUpButton>
|
||||
|
||||
<RadixSelect.Viewport className="Select__viewport">
|
||||
{Object.entries(items).map(([itemValue, itemLabel]) => (
|
||||
<SelectItem value={itemValue} key={itemValue}>
|
||||
{itemLabel.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
{(Object.entries(items) as [T, string][]).map(
|
||||
([itemValue, itemLabel]) => (
|
||||
<SelectItem value={itemValue} key={itemValue}>
|
||||
{itemLabel}
|
||||
</SelectItem>
|
||||
),
|
||||
)}
|
||||
</RadixSelect.Viewport>
|
||||
|
||||
<RadixSelect.ScrollDownButton className="Select__scroll-button">
|
||||
|
@ -188,6 +188,13 @@ export const ACTIVE_THRESHOLD = 3_000;
|
||||
|
||||
export const THEME_FILTER = cssVariables.themeFilter;
|
||||
|
||||
// using a stronger invert (100% vs our regular 93%) and saturate
|
||||
// as a temp hack to make images in dark theme look closer to original
|
||||
// color scheme (it's still not quite there and the colors look slightly
|
||||
// desatured, alas...)
|
||||
export const IMAGE_INVERT_FILTER =
|
||||
"invert(100%) hue-rotate(180deg) saturate(1.25)";
|
||||
|
||||
export const URL_QUERY_KEYS = {
|
||||
addLibrary: "addLibrary",
|
||||
} as const;
|
||||
@ -320,16 +327,33 @@ export const DEFAULT_SIDEBAR = {
|
||||
export const LIBRARY_DISABLED_TYPES = new Set(["embeddable", "image"] as const);
|
||||
|
||||
export const FANCY_BACKGROUND_IMAGES = {
|
||||
solid: { path: null, label: "solid color" },
|
||||
bubbles: { path: "/backgrounds/bubbles.svg" as DataURL, label: "bubbles" },
|
||||
solid: { light: null, dark: null, label: "solid color" },
|
||||
bubbles: {
|
||||
light: "/backgrounds/bubbles.svg" as DataURL,
|
||||
dark: "/backgrounds/bubbles_dark.svg" as DataURL,
|
||||
label: "bubbles",
|
||||
},
|
||||
bubbles2: {
|
||||
path: "/backgrounds/bubbles2.svg" as DataURL,
|
||||
light: "/backgrounds/bubbles2.svg" as DataURL,
|
||||
dark: "/backgrounds/bubbles2_dark.svg" as DataURL,
|
||||
label: "bubbles 2",
|
||||
},
|
||||
bricks: { path: "/backgrounds/bricks.svg" as DataURL, label: "bricks" },
|
||||
lines: { path: "/backgrounds/lines.svg" as DataURL, label: "lines" },
|
||||
lines2: { path: "/backgrounds/lines2.svg" as DataURL, label: "lines 2" },
|
||||
bricks: {
|
||||
light: "/backgrounds/bricks.svg" as DataURL,
|
||||
dark: "/backgrounds/bricks_dark.svg" as DataURL,
|
||||
label: "bricks",
|
||||
},
|
||||
lines: {
|
||||
light: "/backgrounds/lines.svg" as DataURL,
|
||||
dark: "/backgrounds/lines_dark.svg" as DataURL,
|
||||
label: "lines",
|
||||
},
|
||||
lines2: {
|
||||
light: "/backgrounds/lines2.svg" as DataURL,
|
||||
dark: "/backgrounds/lines2_dark.svg" as DataURL,
|
||||
label: "lines 2",
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_FANCY_BACKGROUND_IMAGE: keyof typeof FANCY_BACKGROUND_IMAGES =
|
||||
"solid" as const;
|
||||
"bubbles" as const;
|
||||
|
@ -47,7 +47,7 @@ export const exportCanvas = async (
|
||||
exportPadding,
|
||||
exportScale: appState.exportScale,
|
||||
exportEmbedScene: appState.exportEmbedScene && type === "svg",
|
||||
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl,
|
||||
fancyBackgroundImageKey: appState.fancyBackgroundImageKey,
|
||||
},
|
||||
files,
|
||||
);
|
||||
|
@ -124,9 +124,9 @@ export const normalizeSVG = async (SVGString: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const loadSVGElement = (filePath: string) => {
|
||||
export const loadSVGElement = (dataURL: DataURL) => {
|
||||
return new Promise<SVGSVGElement>((resolve, reject) => {
|
||||
fetch(filePath)
|
||||
fetch(dataURL)
|
||||
.then((response) => response.text())
|
||||
.then((svgString) => {
|
||||
const parser = new DOMParser();
|
||||
|
@ -34,6 +34,7 @@ import { getDefaultAppState } from "../appState";
|
||||
import {
|
||||
BOUND_TEXT_PADDING,
|
||||
FRAME_STYLE,
|
||||
IMAGE_INVERT_FILTER,
|
||||
MAX_DECIMALS_FOR_SVG_EXPORT,
|
||||
MIME_TYPES,
|
||||
SVG_NS,
|
||||
@ -56,12 +57,6 @@ import { getContainingFrame } from "../frame";
|
||||
import { normalizeLink, toValidURL } from "../data/url";
|
||||
import { ShapeCache } from "../scene/ShapeCache";
|
||||
|
||||
// using a stronger invert (100% vs our regular 93%) and saturate
|
||||
// as a temp hack to make images in dark theme look closer to original
|
||||
// color scheme (it's still not quite there and the colors look slightly
|
||||
// desatured, alas...)
|
||||
const IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
||||
|
||||
const defaultAppState = getDefaultAppState();
|
||||
|
||||
const isPendingImageElement = (
|
||||
|
@ -3,9 +3,10 @@ import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
||||
import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
|
||||
import { distance, isOnlyExportingSingleFrame } from "../utils";
|
||||
import { AppState, BinaryFiles, DataURL } from "../types";
|
||||
import { AppState, BinaryFiles } from "../types";
|
||||
import {
|
||||
DEFAULT_EXPORT_PADDING,
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
FANCY_BG_BORDER_RADIUS,
|
||||
FANCY_BG_PADDING,
|
||||
SVG_NS,
|
||||
@ -51,7 +52,8 @@ export const exportToCanvas = async (
|
||||
) => {
|
||||
const exportWithFancyBackground =
|
||||
exportBackground &&
|
||||
!!appState.fancyBackgroundImageUrl &&
|
||||
appState.fancyBackgroundImageKey &&
|
||||
appState.fancyBackgroundImageKey !== "solid" &&
|
||||
elements.length > 0;
|
||||
const padding = !exportWithFancyBackground
|
||||
? exportPadding
|
||||
@ -75,7 +77,7 @@ export const exportToCanvas = async (
|
||||
|
||||
const renderConfig = {
|
||||
viewBackgroundColor:
|
||||
exportBackground && !appState.fancyBackgroundImageUrl
|
||||
exportBackground && !exportWithFancyBackground
|
||||
? viewBackgroundColor
|
||||
: null,
|
||||
scrollX: -minX + (onlyExportingSingleFrame ? 0 : padding),
|
||||
@ -92,15 +94,19 @@ export const exportToCanvas = async (
|
||||
renderSelection: false,
|
||||
renderGrid: false,
|
||||
isExporting: true,
|
||||
exportBackgroundImage: appState.fancyBackgroundImageUrl,
|
||||
exportBackgroundImage: appState.fancyBackgroundImageKey,
|
||||
};
|
||||
|
||||
if (exportWithFancyBackground) {
|
||||
if (
|
||||
exportWithFancyBackground &&
|
||||
appState.fancyBackgroundImageKey !== "solid"
|
||||
) {
|
||||
await applyFancyBackgroundOnCanvas({
|
||||
canvas,
|
||||
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl!,
|
||||
fancyBackgroundImageKey: appState.fancyBackgroundImageKey,
|
||||
backgroundColor: viewBackgroundColor,
|
||||
exportScale: appState.exportScale,
|
||||
theme: renderConfig.theme,
|
||||
});
|
||||
}
|
||||
|
||||
@ -139,7 +145,7 @@ export const exportToSvg = async (
|
||||
exportWithDarkMode?: boolean;
|
||||
exportEmbedScene?: boolean;
|
||||
renderFrame?: boolean;
|
||||
fancyBackgroundImageUrl: DataURL | null;
|
||||
fancyBackgroundImageKey?: keyof typeof FANCY_BACKGROUND_IMAGES;
|
||||
},
|
||||
files: BinaryFiles | null,
|
||||
opts?: {
|
||||
@ -157,8 +163,9 @@ export const exportToSvg = async (
|
||||
|
||||
const exportWithFancyBackground =
|
||||
exportBackground &&
|
||||
!!appState.fancyBackgroundImageUrl &&
|
||||
elements.length > 0;
|
||||
elements.length > 0 &&
|
||||
appState.fancyBackgroundImageKey &&
|
||||
appState.fancyBackgroundImageKey !== "solid";
|
||||
|
||||
const padding = !exportWithFancyBackground
|
||||
? exportPadding
|
||||
@ -191,7 +198,8 @@ export const exportToSvg = async (
|
||||
svgRoot.setAttribute("filter", THEME_FILTER);
|
||||
}
|
||||
|
||||
let assetPath = "https://excalidraw.com/";
|
||||
// let assetPath = "https://excalidraw.com/";
|
||||
let assetPath = "http://localhost:3000/";
|
||||
// Asset path needs to be determined only when using package
|
||||
if (import.meta.env.VITE_IS_EXCALIDRAW_NPM_PACKAGE) {
|
||||
assetPath =
|
||||
@ -258,14 +266,17 @@ export const exportToSvg = async (
|
||||
|
||||
// render background rect
|
||||
if (appState.exportBackground && viewBackgroundColor) {
|
||||
if (appState.fancyBackgroundImageUrl) {
|
||||
if (
|
||||
appState.fancyBackgroundImageKey &&
|
||||
appState.fancyBackgroundImageKey !== "solid"
|
||||
) {
|
||||
await applyFancyBackgroundOnSvg({
|
||||
svgRoot,
|
||||
fancyBackgroundImageUrl:
|
||||
`${appState.fancyBackgroundImageUrl}` as DataURL,
|
||||
fancyBackgroundImageKey: `${appState.fancyBackgroundImageKey}`,
|
||||
backgroundColor: viewBackgroundColor,
|
||||
dimensions: { w: width, h: height },
|
||||
exportScale,
|
||||
theme: appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT,
|
||||
});
|
||||
} else {
|
||||
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
|
||||
|
@ -1,7 +1,15 @@
|
||||
import { FANCY_BG_BORDER_RADIUS, FANCY_BG_PADDING, SVG_NS } from "../constants";
|
||||
import {
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
FANCY_BG_BORDER_RADIUS,
|
||||
FANCY_BG_PADDING,
|
||||
IMAGE_INVERT_FILTER,
|
||||
SVG_NS,
|
||||
THEME,
|
||||
THEME_FILTER,
|
||||
} from "../constants";
|
||||
import { loadHTMLImageElement, loadSVGElement } from "../element/image";
|
||||
import { roundRect } from "../renderer/roundRect";
|
||||
import { AppState, DataURL } from "../types";
|
||||
import { AppState } from "../types";
|
||||
|
||||
type Dimensions = { w: number; h: number };
|
||||
|
||||
@ -62,6 +70,7 @@ const addContentBackground = (
|
||||
normalizedDimensions: Dimensions,
|
||||
contentBackgroundColor: string,
|
||||
exportScale: AppState["exportScale"],
|
||||
theme: AppState["theme"],
|
||||
) => {
|
||||
const shadows = [
|
||||
{
|
||||
@ -113,6 +122,9 @@ const addContentBackground = (
|
||||
}
|
||||
|
||||
if (index === shadows.length - 1) {
|
||||
if (theme === THEME.DARK) {
|
||||
context.filter = THEME_FILTER;
|
||||
}
|
||||
context.fillStyle = contentBackgroundColor;
|
||||
context.fill();
|
||||
}
|
||||
@ -123,17 +135,25 @@ const addContentBackground = (
|
||||
|
||||
export const applyFancyBackgroundOnCanvas = async ({
|
||||
canvas,
|
||||
fancyBackgroundImageUrl,
|
||||
fancyBackgroundImageKey,
|
||||
backgroundColor,
|
||||
exportScale,
|
||||
theme,
|
||||
}: {
|
||||
canvas: HTMLCanvasElement;
|
||||
fancyBackgroundImageUrl: DataURL;
|
||||
fancyBackgroundImageKey: Exclude<
|
||||
keyof typeof FANCY_BACKGROUND_IMAGES,
|
||||
"solid"
|
||||
>;
|
||||
backgroundColor: string;
|
||||
exportScale: AppState["exportScale"];
|
||||
theme: AppState["theme"];
|
||||
}) => {
|
||||
const context = canvas.getContext("2d")!;
|
||||
|
||||
const fancyBackgroundImageUrl =
|
||||
FANCY_BACKGROUND_IMAGES[fancyBackgroundImageKey][theme];
|
||||
|
||||
const fancyBackgroundImage = await loadHTMLImageElement(
|
||||
fancyBackgroundImageUrl,
|
||||
);
|
||||
@ -142,31 +162,45 @@ export const applyFancyBackgroundOnCanvas = async ({
|
||||
|
||||
addImageBackground(context, canvasDimensions, fancyBackgroundImage);
|
||||
|
||||
addContentBackground(context, canvasDimensions, backgroundColor, exportScale);
|
||||
addContentBackground(
|
||||
context,
|
||||
canvasDimensions,
|
||||
backgroundColor,
|
||||
exportScale,
|
||||
theme,
|
||||
);
|
||||
};
|
||||
|
||||
export const applyFancyBackgroundOnSvg = async ({
|
||||
svgRoot,
|
||||
fancyBackgroundImageUrl,
|
||||
fancyBackgroundImageKey,
|
||||
backgroundColor,
|
||||
dimensions,
|
||||
exportScale,
|
||||
theme,
|
||||
}: {
|
||||
svgRoot: SVGSVGElement;
|
||||
fancyBackgroundImageUrl: DataURL;
|
||||
fancyBackgroundImageKey: Exclude<
|
||||
keyof typeof FANCY_BACKGROUND_IMAGES,
|
||||
"solid"
|
||||
>;
|
||||
backgroundColor: string;
|
||||
dimensions: Dimensions;
|
||||
exportScale: AppState["exportScale"];
|
||||
theme: AppState["theme"];
|
||||
}) => {
|
||||
const fancyBackgroundImage = await loadSVGElement(
|
||||
`${fancyBackgroundImageUrl}`,
|
||||
);
|
||||
const fancyBackgroundImageUrl =
|
||||
FANCY_BACKGROUND_IMAGES[fancyBackgroundImageKey][theme];
|
||||
const fancyBackgroundImage = await loadSVGElement(fancyBackgroundImageUrl);
|
||||
|
||||
fancyBackgroundImage.setAttribute("x", "0");
|
||||
fancyBackgroundImage.setAttribute("y", "0");
|
||||
fancyBackgroundImage.setAttribute("width", `${dimensions.w}`);
|
||||
fancyBackgroundImage.setAttribute("height", `${dimensions.h}`);
|
||||
fancyBackgroundImage.setAttribute("preserveAspectRatio", "none");
|
||||
if (theme === THEME.DARK) {
|
||||
fancyBackgroundImage.setAttribute("filter", IMAGE_INVERT_FILTER);
|
||||
}
|
||||
|
||||
svgRoot.appendChild(fancyBackgroundImage);
|
||||
|
||||
|
@ -32,7 +32,11 @@ import { isOverScrollBars } from "./scene";
|
||||
import { MaybeTransformHandleType } from "./element/transformHandles";
|
||||
import Library from "./data/library";
|
||||
import type { FileSystemHandle } from "./data/filesystem";
|
||||
import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants";
|
||||
import type {
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
IMAGE_MIME_TYPES,
|
||||
MIME_TYPES,
|
||||
} from "./constants";
|
||||
import { ContextMenuItems } from "./components/ContextMenu";
|
||||
import { Merge, ForwardRef, ValueOf } from "./utility-types";
|
||||
|
||||
@ -287,7 +291,7 @@ export type AppState = {
|
||||
pendingImageElementId: ExcalidrawImageElement["id"] | null;
|
||||
showHyperlinkPopup: false | "info" | "editor";
|
||||
selectedLinearElement: LinearElementEditor | null;
|
||||
fancyBackgroundImageUrl: DataURL | null;
|
||||
fancyBackgroundImageKey: keyof typeof FANCY_BACKGROUND_IMAGES;
|
||||
};
|
||||
|
||||
export type UIAppState = Omit<
|
||||
|
Loading…
x
Reference in New Issue
Block a user