Compare commits

..
Author SHA1 Message Date
dwelle 0458834681 wip 2024-09-16 10:49:08 +02:00
David LuzarandGitHub 508f16dc04 refactor: rename example App.tsx -> ExampleApp.tsx (#8501) 2024-09-13 16:56:32 +02:00
c1b310c56b fix: Buffer dependency (#8474)
* fix Buffer dependency

* moved to encode.ts

* move base64 parsing out

---------

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
2024-09-12 15:48:47 +02:00
zsviczianandGitHub d4900e8f19 fix: Linear element complete button disabled (#8492) 2024-09-12 14:59:38 +02:00
zsviczianandGitHub caf2db934c fix: aspect ratio of distorted images are not preserved in SVG exports (#8061) 2024-09-12 14:11:08 +02:00
zsviczianandGitHub 60e3801691 fix: WYSIWYG editor padding is not normalized with zoom.value (#8481) 2024-09-12 13:42:39 +02:00
David LuzarandGitHub 80f3b75d42 chore: revert vite 5.4.2 -> 5.0.12 (#8499) 2024-09-12 12:56:45 +02:00
hocinoandGitHub dc812bee19 docs: replace dead link (#8494)
* docs update dead link on main-menu page

* doc: fix dead link
2024-09-12 13:20:18 +05:30
David LuzarandGitHub 01e83cc9a5 fix: default sidebar triggers & behavior (#8498) 2024-09-11 20:19:14 +02:00
David LuzarandGitHub 813f9b702e feat: merge search sidebar back to default sidebar (#8497) 2024-09-11 19:26:01 +02:00
David LuzarandGitHub fd39712ba6 fix: improve canvas search scroll behavior further (#8491) 2024-09-11 18:01:18 +02:00
zsviczianandGitHub b46ca0192b fix: addFiles clears the whole image cache when each file is added - regression from #8471 (#8490)
Update App.tsx
2024-09-11 07:57:41 +02:00
David LuzarandGitHub 72b7c937b1 feat: smarter zooming when scrolling to match & only match on search/switch (#8488) 2024-09-10 21:26:01 +02:00
42 changed files with 640 additions and 588 deletions
@@ -20,7 +20,7 @@ exportToCanvas(&#123;<br/>&nbsp;
getDimensions,<br/>&nbsp;
files,<br/>&nbsp;
exportPadding?: number;<br/>
&#125;: <a href="https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/packages/utils.ts#L21">ExportOpts</a>
&#125;: <a href="https://github.com/excalidraw/excalidraw/blob/master/packages/utils/export.ts#L24">ExportOpts</a>
</pre>
| Name | Type | Default | Description |
@@ -40,7 +40,7 @@ import type {
} from "@excalidraw/excalidraw/dist/excalidraw/element/types";
import type { ImportedLibraryData } from "@excalidraw/excalidraw/dist/excalidraw/data/types";
import "./App.scss";
import "./ExampleApp.scss";
type Comment = {
x: number;
@@ -73,7 +73,7 @@ export interface AppProps {
excalidrawLib: typeof TExcalidraw;
}
export default function App({
export default function ExampleApp({
appTitle,
useCustom,
customArgs,
@@ -1,7 +1,7 @@
"use client";
import * as excalidrawLib from "@excalidraw/excalidraw";
import { Excalidraw } from "@excalidraw/excalidraw";
import App from "../../components/App";
import App from "../../components/ExampleApp";
import "@excalidraw/excalidraw/index.css";
@@ -1,4 +1,4 @@
import App from "../components/App";
import App from "../components/ExampleApp";
import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
+14 -1
View File
@@ -20,6 +20,10 @@ import {
get,
} from "idb-keyval";
import { clearAppStateForLocalStorage } from "../../packages/excalidraw/appState";
import {
CANVAS_SEARCH_TAB,
DEFAULT_SIDEBAR,
} from "../../packages/excalidraw/constants";
import type { LibraryPersistedData } from "../../packages/excalidraw/data/library";
import type { ImportedDataState } from "../../packages/excalidraw/data/types";
import { clearElementsForLocalStorage } from "../../packages/excalidraw/element";
@@ -66,13 +70,22 @@ const saveDataStateToLocalStorage = (
appState: AppState,
) => {
try {
const _appState = clearAppStateForLocalStorage(appState);
if (
_appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
_appState.openSidebar.tab === CANVAS_SEARCH_TAB
) {
_appState.openSidebar = null;
}
localStorage.setItem(
STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS,
JSON.stringify(clearElementsForLocalStorage(elements)),
);
localStorage.setItem(
STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
JSON.stringify(clearAppStateForLocalStorage(appState)),
JSON.stringify(_appState),
);
updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE);
} catch (error: any) {
+1 -1
View File
@@ -36,7 +36,7 @@
"prettier": "2.6.2",
"rewire": "6.0.0",
"typescript": "4.9.4",
"vite": "5.4.2",
"vite": "5.0.12",
"vite-plugin-checker": "0.7.2",
"vite-plugin-ejs": "1.7.0",
"vite-plugin-pwa": "0.17.4",
+58 -51
View File
@@ -24,7 +24,7 @@ import { CODES, KEYS } from "../keys";
import { getNormalizedZoom } from "../scene";
import { centerScrollOn } from "../scene/scroll";
import { getStateForZoom } from "../scene/zoom";
import type { AppState } from "../types";
import type { AppState, Offsets } from "../types";
import { getShortcutKey, updateActiveTool } from "../utils";
import { register } from "./register";
import { Tooltip } from "../components/Tooltip";
@@ -38,7 +38,7 @@ import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
import type { SceneBounds } from "../element/bounds";
import { setCursor } from "../cursor";
import { StoreAction } from "../store";
import { clamp } from "../../math";
import { clamp, roundToStep } from "../../math";
export const actionChangeViewBackgroundColor = register({
name: "changeViewBackgroundColor",
@@ -259,89 +259,85 @@ const zoomValueToFitBoundsOnViewport = (
const adjustedZoomValue =
smallestZoomValue * clamp(viewportZoomFactor, 0.1, 1);
const zoomAdjustedToSteps =
Math.floor(adjustedZoomValue / ZOOM_STEP) * ZOOM_STEP;
return getNormalizedZoom(Math.min(zoomAdjustedToSteps, 1));
return Math.min(adjustedZoomValue, 1);
};
export const zoomToFitBounds = ({
bounds,
appState,
canvasOffsets,
fitToViewport = false,
viewportZoomFactor = 1,
minZoom = -Infinity,
maxZoom = Infinity,
}: {
bounds: SceneBounds;
canvasOffsets?: Offsets;
appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number;
minZoom?: number;
maxZoom?: number;
}) => {
viewportZoomFactor = clamp(viewportZoomFactor, MIN_ZOOM, MAX_ZOOM);
const [x1, y1, x2, y2] = bounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
let newZoomValue;
let scrollX;
let scrollY;
const canvasOffsetLeft = canvasOffsets?.left ?? 0;
const canvasOffsetTop = canvasOffsets?.top ?? 0;
const canvasOffsetRight = canvasOffsets?.right ?? 0;
const canvasOffsetBottom = canvasOffsets?.bottom ?? 0;
const effectiveCanvasWidth =
appState.width - canvasOffsetLeft - canvasOffsetRight;
const effectiveCanvasHeight =
appState.height - canvasOffsetTop - canvasOffsetBottom;
let adjustedZoomValue;
if (fitToViewport) {
const commonBoundsWidth = x2 - x1;
const commonBoundsHeight = y2 - y1;
newZoomValue =
adjustedZoomValue =
Math.min(
appState.width / commonBoundsWidth,
appState.height / commonBoundsHeight,
) * clamp(viewportZoomFactor, 0.1, 1);
newZoomValue = getNormalizedZoom(newZoomValue);
let appStateWidth = appState.width;
if (appState.openSidebar) {
const sidebarDOMElem = document.querySelector(
".sidebar",
) as HTMLElement | null;
const sidebarWidth = sidebarDOMElem?.offsetWidth ?? 0;
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
appStateWidth = !isRTL
? appState.width - sidebarWidth
: appState.width + sidebarWidth;
}
scrollX = (appStateWidth / 2) * (1 / newZoomValue) - centerX;
scrollY = (appState.height / 2) * (1 / newZoomValue) - centerY;
effectiveCanvasWidth / commonBoundsWidth,
effectiveCanvasHeight / commonBoundsHeight,
) * viewportZoomFactor;
} else {
newZoomValue = zoomValueToFitBoundsOnViewport(
adjustedZoomValue = zoomValueToFitBoundsOnViewport(
bounds,
{
width: appState.width,
height: appState.height,
width: effectiveCanvasWidth,
height: effectiveCanvasHeight,
},
viewportZoomFactor,
);
const centerScroll = centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: { value: newZoomValue },
});
scrollX = centerScroll.scrollX;
scrollY = centerScroll.scrollY;
}
const newZoomValue = getNormalizedZoom(
clamp(roundToStep(adjustedZoomValue, ZOOM_STEP, "floor"), minZoom, maxZoom),
);
const centerScroll = centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
offsets: canvasOffsets,
zoom: { value: newZoomValue },
});
return {
appState: {
...appState,
scrollX,
scrollY,
scrollX: centerScroll.scrollX,
scrollY: centerScroll.scrollY,
zoom: { value: newZoomValue },
},
storeAction: StoreAction.NONE,
@@ -349,25 +345,34 @@ export const zoomToFitBounds = ({
};
export const zoomToFit = ({
canvasOffsets,
targetElements,
appState,
fitToViewport,
viewportZoomFactor,
minZoom,
maxZoom,
}: {
canvasOffsets?: Offsets;
targetElements: readonly ExcalidrawElement[];
appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number;
minZoom?: number;
maxZoom?: number;
}) => {
const commonBounds = getCommonBounds(getNonDeletedElements(targetElements));
return zoomToFitBounds({
canvasOffsets,
bounds: commonBounds,
appState,
fitToViewport,
viewportZoomFactor,
minZoom,
maxZoom,
});
};
@@ -388,6 +393,7 @@ export const actionZoomToFitSelectionInViewport = register({
userToFollow: null,
},
fitToViewport: false,
canvasOffsets: app.getEditorUIOffsets(),
});
},
// NOTE shift-2 should have been assigned actionZoomToFitSelection.
@@ -413,7 +419,7 @@ export const actionZoomToFitSelection = register({
userToFollow: null,
},
fitToViewport: true,
viewportZoomFactor: 0.7,
canvasOffsets: app.getEditorUIOffsets(),
});
},
// NOTE this action should use shift-2 per figma, alas
@@ -430,7 +436,7 @@ export const actionZoomToFit = register({
icon: zoomAreaIcon,
viewMode: true,
trackEvent: { category: "canvas" },
perform: (elements, appState) =>
perform: (elements, appState, _, app) =>
zoomToFit({
targetElements: elements,
appState: {
@@ -438,6 +444,7 @@ export const actionZoomToFit = register({
userToFollow: null,
},
fitToViewport: false,
canvasOffsets: app.getEditorUIOffsets(),
}),
keyTest: (event) =>
event.code === CODES.ONE &&
@@ -217,6 +217,7 @@ export const actionFinalize = register({
onClick={updateData}
visible={appState.multiElement != null}
size={data?.size || "medium"}
style={{ pointerEvents: "all" }}
/>
),
});
@@ -3,7 +3,7 @@ import { register } from "./register";
import type { AppState } from "../types";
import { searchIcon } from "../components/icons";
import { StoreAction } from "../store";
import { CLASSES, SEARCH_SIDEBAR } from "../constants";
import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants";
export const actionToggleSearchMenu = register({
name: "searchMenu",
@@ -17,7 +17,10 @@ export const actionToggleSearchMenu = register({
predicate: (appState) => appState.gridModeEnabled,
},
perform(elements, appState, _, app) {
if (appState.openSidebar?.name === SEARCH_SIDEBAR.name) {
if (
appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
appState.openSidebar.tab === CANVAS_SEARCH_TAB
) {
const searchInput =
app.excalidrawContainerValue.container?.querySelector<HTMLInputElement>(
`.${CLASSES.SEARCH_MENU_INPUT_WRAPPER} input`,
@@ -31,13 +34,14 @@ export const actionToggleSearchMenu = register({
}
searchInput?.focus();
searchInput?.select();
return false;
}
return {
appState: {
...appState,
openSidebar: { name: SEARCH_SIDEBAR.name },
openSidebar: { name: DEFAULT_SIDEBAR.name, tab: CANVAS_SEARCH_TAB },
openDialog: null,
},
storeAction: StoreAction.NONE,
+85
View File
@@ -1,5 +1,90 @@
import oc from "open-color";
import type { Merge } from "./utility-types";
import { clamp } from "../math/utils";
import tinycolor from "tinycolor2";
import { degreesToRadians } from "../math/angle";
import type { Degrees } from "../math/types";
function cssHueRotate(
red: number,
green: number,
blue: number,
degrees: Degrees,
): { r: number; g: number; b: number } {
// normalize
const r = red / 255;
const g = green / 255;
const b = blue / 255;
// Convert degrees to radians
const a = degreesToRadians(degrees);
const c = Math.cos(a);
const s = Math.sin(a);
// rotation matrix
const matrix = [
0.213 + c * 0.787 - s * 0.213,
0.715 - c * 0.715 - s * 0.715,
0.072 - c * 0.072 + s * 0.928,
0.213 - c * 0.213 + s * 0.143,
0.715 + c * 0.285 + s * 0.14,
0.072 - c * 0.072 - s * 0.283,
0.213 - c * 0.213 - s * 0.787,
0.715 - c * 0.715 + s * 0.715,
0.072 + c * 0.928 + s * 0.072,
];
// transform
const newR = r * matrix[0] + g * matrix[1] + b * matrix[2];
const newG = r * matrix[3] + g * matrix[4] + b * matrix[5];
const newB = r * matrix[6] + g * matrix[7] + b * matrix[8];
// clamp the values to [0, 1] range and convert back to [0, 255]
return {
r: Math.round(Math.max(0, Math.min(1, newR)) * 255),
g: Math.round(Math.max(0, Math.min(1, newG)) * 255),
b: Math.round(Math.max(0, Math.min(1, newB)) * 255),
};
}
const cssInvert = (
r: number,
g: number,
b: number,
percent: number,
): { r: number; g: number; b: number } => {
const p = clamp(percent, 0, 100) / 100;
// Function to invert a single color component
const invertComponent = (color: number): number => {
// Apply the invert formula
const inverted = color * (1 - p) + (255 - color) * p;
// Round to the nearest integer and clamp to [0, 255]
return Math.round(clamp(inverted, 0, 255));
};
// Calculate the inverted RGB components
const invertedR = invertComponent(r);
const invertedG = invertComponent(g);
const invertedB = invertComponent(b);
return { r: invertedR, g: invertedG, b: invertedB };
};
export const applyDarkModeFilter = (color: string) => {
let tc = tinycolor(color);
const _alpha = tc._a;
// order of operations matters
// (corresponds to "filter: invert(invertPercent) hue-rotate(hueDegrees)" in css)
tc = tinycolor(cssInvert(tc._r, tc._g, tc._b, 93));
tc = tinycolor(cssHueRotate(tc._r, tc._g, tc._b, 180 as Degrees));
tc.setAlpha(_alpha);
return tc.toHex8String();
};
// FIXME can't put to utils.ts rn because of circular dependency
const pick = <R extends Record<string, any>, K extends readonly (keyof R)[]>(
+53 -31
View File
@@ -259,6 +259,7 @@ import type {
ElementsPendingErasure,
GenerateDiagramToCode,
NullableGridSize,
Offsets,
} from "../types";
import {
debounce,
@@ -1699,6 +1700,7 @@ class App extends React.Component<AppProps, AppState> {
elementsPendingErasure: this.elementsPendingErasure,
pendingFlowchartNodes:
this.flowChartCreator.pendingNodes,
theme: this.state.theme,
}}
/>
{this.state.newElement && (
@@ -1719,6 +1721,7 @@ class App extends React.Component<AppProps, AppState> {
elementsPendingErasure:
this.elementsPendingErasure,
pendingFlowchartNodes: null,
theme: this.state.theme,
}}
/>
)}
@@ -2367,9 +2370,10 @@ class App extends React.Component<AppProps, AppState> {
return false;
};
private clearImageShapeCache() {
private clearImageShapeCache(filesMap?: BinaryFiles) {
const files = filesMap ?? this.files;
this.scene.getNonDeletedElements().forEach((element) => {
if (isInitializedImageElement(element) && this.files[element.fileId]) {
if (isInitializedImageElement(element) && files[element.fileId]) {
this.imageCache.delete(element.fileId);
ShapeCache.delete(element);
}
@@ -2693,6 +2697,11 @@ class App extends React.Component<AppProps, AppState> {
activeTool: updateActiveTool(this.state, { type: "selection" }),
});
}
if (prevState.theme !== this.state.theme) {
this.scene
.getElementsIncludingDeleted()
.forEach((element) => ShapeCache.delete(element));
}
if (
this.state.activeTool.type === "eraser" &&
prevState.theme !== this.state.theme
@@ -3231,6 +3240,7 @@ class App extends React.Component<AppProps, AppState> {
if (opts.fitToContent) {
this.scrollToContent(newElements, {
fitToContent: true,
canvasOffsets: this.getEditorUIOffsets(),
});
}
};
@@ -3543,7 +3553,7 @@ class App extends React.Component<AppProps, AppState> {
target:
| ExcalidrawElement
| readonly ExcalidrawElement[] = this.scene.getNonDeletedElements(),
opts?:
opts?: (
| {
fitToContent?: boolean;
fitToViewport?: never;
@@ -3560,7 +3570,12 @@ class App extends React.Component<AppProps, AppState> {
viewportZoomFactor?: number;
animate?: boolean;
duration?: number;
},
}
) & {
minZoom?: number;
maxZoom?: number;
canvasOffsets?: Offsets;
},
) => {
this.cancelInProgressAnimation?.();
@@ -3573,10 +3588,13 @@ class App extends React.Component<AppProps, AppState> {
if (opts?.fitToContent || opts?.fitToViewport) {
const { appState } = zoomToFit({
canvasOffsets: opts.canvasOffsets,
targetElements,
appState: this.state,
fitToViewport: !!opts?.fitToViewport,
viewportZoomFactor: opts?.viewportZoomFactor,
minZoom: opts?.minZoom,
maxZoom: opts?.maxZoom,
});
zoom = appState.zoom;
scrollX = appState.scrollX;
@@ -3690,7 +3708,7 @@ class App extends React.Component<AppProps, AppState> {
this.files = { ...this.files, ...Object.fromEntries(filesMap) };
this.clearImageShapeCache();
this.clearImageShapeCache(Object.fromEntries(filesMap));
this.scene.triggerUpdate();
this.addNewImagesToImageCache();
@@ -3804,40 +3822,42 @@ class App extends React.Component<AppProps, AppState> {
},
);
public getEditorUIOffsets = (): {
top: number;
right: number;
bottom: number;
left: number;
} => {
public getEditorUIOffsets = (): Offsets => {
const toolbarBottom =
this.excalidrawContainerRef?.current
?.querySelector(".App-toolbar")
?.getBoundingClientRect()?.bottom ?? 0;
const sidebarWidth = Math.max(
this.excalidrawContainerRef?.current
?.querySelector(".default-sidebar")
?.getBoundingClientRect()?.width ?? 0,
);
const propertiesPanelWidth = Math.max(
this.excalidrawContainerRef?.current
?.querySelector(".App-menu__left")
?.getBoundingClientRect()?.width ?? 0,
0,
);
const sidebarRect = this.excalidrawContainerRef?.current
?.querySelector(".sidebar")
?.getBoundingClientRect();
const propertiesPanelRect = this.excalidrawContainerRef?.current
?.querySelector(".App-menu__left")
?.getBoundingClientRect();
const PADDING = 16;
return getLanguage().rtl
? {
top: toolbarBottom,
right: propertiesPanelWidth,
bottom: 0,
left: sidebarWidth,
top: toolbarBottom + PADDING,
right:
Math.max(
this.state.width -
(propertiesPanelRect?.left ?? this.state.width),
0,
) + PADDING,
bottom: PADDING,
left: Math.max(sidebarRect?.right ?? 0, 0) + PADDING,
}
: {
top: toolbarBottom,
right: sidebarWidth,
bottom: 0,
left: propertiesPanelWidth,
top: toolbarBottom + PADDING,
right: Math.max(
this.state.width -
(sidebarRect?.left ?? this.state.width) +
PADDING,
0,
),
bottom: PADDING,
left: Math.max(propertiesPanelRect?.right ?? 0, 0) + PADDING,
};
};
@@ -3922,7 +3942,7 @@ class App extends React.Component<AppProps, AppState> {
animate: true,
duration: 300,
fitToContent: true,
viewportZoomFactor: 0.8,
canvasOffsets: this.getEditorUIOffsets(),
});
}
@@ -3978,6 +3998,7 @@ class App extends React.Component<AppProps, AppState> {
this.scrollToContent(nextNode, {
animate: true,
duration: 300,
canvasOffsets: this.getEditorUIOffsets(),
});
}
}
@@ -4410,6 +4431,7 @@ class App extends React.Component<AppProps, AppState> {
this.scrollToContent(firstNode, {
animate: true,
duration: 300,
canvasOffsets: this.getEditorUIOffsets(),
});
}
}
@@ -1,5 +1,9 @@
import clsx from "clsx";
import { DEFAULT_SIDEBAR, LIBRARY_SIDEBAR_TAB } from "../constants";
import {
CANVAS_SEARCH_TAB,
DEFAULT_SIDEBAR,
LIBRARY_SIDEBAR_TAB,
} from "../constants";
import { useTunnels } from "../context/tunnels";
import { useUIAppState } from "../context/ui-appState";
import type { MarkOptional, Merge } from "../utility-types";
@@ -10,7 +14,8 @@ import { LibraryMenu } from "./LibraryMenu";
import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common";
import { Sidebar } from "./Sidebar/Sidebar";
import "../components/dropdownMenu/DropdownMenu.scss";
import { t } from "../i18n";
import { SearchMenu } from "./SearchMenu";
import { LibraryIcon, searchIcon } from "./icons";
const DefaultSidebarTrigger = withInternalFallback(
"DefaultSidebarTrigger",
@@ -32,14 +37,11 @@ const DefaultSidebarTrigger = withInternalFallback(
);
DefaultSidebarTrigger.displayName = "DefaultSidebarTrigger";
const DefaultTabTriggers = ({
children,
...rest
}: { children: React.ReactNode } & React.HTMLAttributes<HTMLDivElement>) => {
const DefaultTabTriggers = ({ children }: { children: React.ReactNode }) => {
const { DefaultSidebarTabTriggersTunnel } = useTunnels();
return (
<DefaultSidebarTabTriggersTunnel.In>
<Sidebar.TabTriggers {...rest}>{children}</Sidebar.TabTriggers>
{children}
</DefaultSidebarTabTriggersTunnel.In>
);
};
@@ -66,16 +68,21 @@ export const DefaultSidebar = Object.assign(
const { DefaultSidebarTabTriggersTunnel } = useTunnels();
const isForceDocked = appState.openSidebar?.tab === CANVAS_SEARCH_TAB;
return (
<Sidebar
{...rest}
name={"default"}
name="default"
key="default"
className={clsx("default-sidebar", className)}
docked={docked ?? appState.defaultSidebarDockedPreference}
docked={
isForceDocked || (docked ?? appState.defaultSidebarDockedPreference)
}
onDock={
// `onDock=false` disables docking.
// if `docked` passed, but no onDock passed, disable manual docking.
onDock === false || (!onDock && docked != null)
isForceDocked || onDock === false || (!onDock && docked != null)
? undefined
: // compose to allow the host app to listen on default behavior
composeEventHandlers(onDock, (docked) => {
@@ -85,26 +92,22 @@ export const DefaultSidebar = Object.assign(
>
<Sidebar.Tabs>
<Sidebar.Header>
{rest.__fallback && (
<div
style={{
color: "var(--color-primary)",
fontSize: "1.2em",
fontWeight: "bold",
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
paddingRight: "1em",
}}
>
{t("toolBar.library")}
</div>
)}
<DefaultSidebarTabTriggersTunnel.Out />
<Sidebar.TabTriggers>
<Sidebar.TabTrigger tab={CANVAS_SEARCH_TAB}>
{searchIcon}
</Sidebar.TabTrigger>
<Sidebar.TabTrigger tab={LIBRARY_SIDEBAR_TAB}>
{LibraryIcon}
</Sidebar.TabTrigger>
<DefaultSidebarTabTriggersTunnel.Out />
</Sidebar.TabTriggers>
</Sidebar.Header>
<Sidebar.Tab tab={LIBRARY_SIDEBAR_TAB}>
<LibraryMenu />
</Sidebar.Tab>
<Sidebar.Tab tab={CANVAS_SEARCH_TAB}>
<SearchMenu />
</Sidebar.Tab>
{children}
</Sidebar.Tabs>
</Sidebar>
@@ -13,7 +13,7 @@ import { isEraserActive } from "../appState";
import "./HintViewer.scss";
import { isNodeInFlowchart } from "../element/flowchart";
import { isGridModeEnabled } from "../snapping";
import { SEARCH_SIDEBAR } from "../constants";
import { CANVAS_SEARCH_TAB, DEFAULT_SIDEBAR } from "../constants";
interface HintViewerProps {
appState: UIAppState;
@@ -32,7 +32,8 @@ const getHints = ({
const multiMode = appState.multiElement !== null;
if (
appState.openSidebar?.name === SEARCH_SIDEBAR.name &&
appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
appState.openSidebar.tab === CANVAS_SEARCH_TAB &&
appState.searchMatches?.length
) {
return t("hints.dismissSearch");
+13 -20
View File
@@ -5,7 +5,6 @@ import {
CLASSES,
DEFAULT_SIDEBAR,
LIBRARY_SIDEBAR_WIDTH,
SEARCH_SIDEBAR,
TOOL_TYPE,
} from "../constants";
import { showSelectedShapeActions } from "../element";
@@ -54,9 +53,6 @@ import { LibraryIcon } from "./icons";
import { UIAppStateContext } from "../context/ui-appState";
import { DefaultSidebar } from "./DefaultSidebar";
import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper";
import "./LayerUI.scss";
import "./Toolbar.scss";
import { mutateElement } from "../element/mutateElement";
import { ShapeCache } from "../scene/ShapeCache";
import Scene from "../scene/Scene";
@@ -64,7 +60,9 @@ import { LaserPointerButton } from "./LaserPointerButton";
import { TTDDialog } from "./TTDDialog/TTDDialog";
import { Stats } from "./Stats";
import { actionToggleStats } from "../actions";
import { SearchSidebar } from "./SearchSidebar";
import "./LayerUI.scss";
import "./Toolbar.scss";
interface LayerUIProps {
actionManager: ActionManager;
@@ -365,21 +363,16 @@ const LayerUI = ({
const renderSidebars = () => {
return (
<>
{appState.openSidebar?.name === SEARCH_SIDEBAR.name && (
<SearchSidebar />
)}
<DefaultSidebar
__fallback
onDock={(docked) => {
trackEvent(
"sidebar",
`toggleDock (${docked ? "dock" : "undock"})`,
`(${device.editor.isMobile ? "mobile" : "desktop"})`,
);
}}
/>
</>
<DefaultSidebar
__fallback
onDock={(docked) => {
trackEvent(
"sidebar",
`toggleDock (${docked ? "dock" : "undock"})`,
`(${device.editor.isMobile ? "mobile" : "desktop"})`,
);
}}
/>
);
};
+135 -85
View File
@@ -20,18 +20,19 @@ import { CLASSES, EVENT } from "../constants";
import { useStable } from "../hooks/useStable";
import "./SearchMenu.scss";
import { round } from "../../math";
const searchKeywordAtom = atom<string>("");
const searchQueryAtom = atom<string>("");
export const searchItemInFocusAtom = atom<number | null>(null);
const SEARCH_DEBOUNCE = 350;
type SearchMatchItem = {
textElement: ExcalidrawTextElement;
keyword: string;
searchQuery: SearchQuery;
index: number;
preview: {
indexInKeyword: number;
indexInSearchQuery: number;
previewText: string;
moreBefore: boolean;
moreAfter: boolean;
@@ -49,19 +50,25 @@ type SearchMatches = {
items: SearchMatchItem[];
};
type SearchQuery = string & { _brand: "SearchQuery" };
export const SearchMenu = () => {
const app = useApp();
const setAppState = useExcalidrawSetAppState();
const searchInputRef = useRef<HTMLInputElement>(null);
const [keyword, setKeyword] = useAtom(searchKeywordAtom, jotaiScope);
const [inputValue, setInputValue] = useAtom(searchQueryAtom, jotaiScope);
const searchQuery = inputValue.trim() as SearchQuery;
const [isSearching, setIsSearching] = useState(false);
const [searchMatches, setSearchMatches] = useState<SearchMatches>({
nonce: null,
items: [],
});
const searchedKeywordRef = useRef<string | null>();
const lastSceneNonceRef = useRef<number | undefined>();
const searchedQueryRef = useRef<SearchQuery | null>(null);
const lastSceneNonceRef = useRef<number | undefined>(undefined);
const [focusIndex, setFocusIndex] = useAtom(
searchItemInFocusAtom,
@@ -70,19 +77,20 @@ export const SearchMenu = () => {
const elementsMap = app.scene.getNonDeletedElementsMap();
useEffect(() => {
const trimmedKeyword = keyword.trim();
if (isSearching) {
return;
}
if (
trimmedKeyword !== searchedKeywordRef.current ||
searchQuery !== searchedQueryRef.current ||
app.scene.getSceneNonce() !== lastSceneNonceRef.current
) {
searchedKeywordRef.current = null;
handleSearch(trimmedKeyword, app, (matchItems, index) => {
searchedQueryRef.current = null;
handleSearch(searchQuery, app, (matchItems, index) => {
setSearchMatches({
nonce: randomInteger(),
items: matchItems,
});
setFocusIndex(index);
searchedKeywordRef.current = trimmedKeyword;
searchedQueryRef.current = searchQuery;
lastSceneNonceRef.current = app.scene.getSceneNonce();
setAppState({
searchMatches: matchItems.map((searchMatch) => ({
@@ -94,7 +102,8 @@ export const SearchMenu = () => {
});
}
}, [
keyword,
isSearching,
searchQuery,
elementsMap,
app,
setAppState,
@@ -128,23 +137,42 @@ export const SearchMenu = () => {
}
};
useEffect(() => {
setAppState((state) => {
return {
searchMatches: state.searchMatches.map((match, index) => {
if (index === focusIndex) {
return { ...match, focus: true };
}
return { ...match, focus: false };
}),
};
});
}, [focusIndex, setAppState]);
useEffect(() => {
if (searchMatches.items.length > 0 && focusIndex !== null) {
const match = searchMatches.items[focusIndex];
if (match) {
const zoomValue = app.state.zoom.value;
const matchAsElement = newTextElement({
text: match.keyword,
text: match.searchQuery,
x: match.textElement.x + (match.matchedLines[0]?.offsetX ?? 0),
y: match.textElement.y + (match.matchedLines[0]?.offsetY ?? 0),
width: match.matchedLines[0]?.width,
height: match.matchedLines[0]?.height,
fontSize: match.textElement.fontSize,
fontFamily: match.textElement.fontFamily,
});
const isTinyText =
app.state.zoom.value *
searchMatches.items[focusIndex].textElement.fontSize <
16;
const FONT_SIZE_LEGIBILITY_THRESHOLD = 14;
const fontSize = match.textElement.fontSize;
const isTextTiny =
fontSize * zoomValue < FONT_SIZE_LEGIBILITY_THRESHOLD;
if (
!isElementCompletelyInViewport(
[matchAsElement],
@@ -160,45 +188,44 @@ export const SearchMenu = () => {
app.scene.getNonDeletedElementsMap(),
app.getEditorUIOffsets(),
) ||
isTinyText
isTextTiny
) {
let zoomOptions: Parameters<AppClassProperties["scrollToContent"]>[1];
if (isTextTiny) {
if (fontSize >= FONT_SIZE_LEGIBILITY_THRESHOLD) {
zoomOptions = { fitToContent: true };
} else {
zoomOptions = {
fitToViewport: true,
// calculate zoom level to make the fontSize ~equal to FONT_SIZE_THRESHOLD, rounded to nearest 10%
maxZoom: round(FONT_SIZE_LEGIBILITY_THRESHOLD / fontSize, 1),
};
}
} else {
zoomOptions = { fitToContent: true };
}
app.scrollToContent(matchAsElement, {
fitToContent: true,
animate: true,
duration: 300,
...zoomOptions,
canvasOffsets: app.getEditorUIOffsets(),
});
}
const nextMatches = searchMatches.items.map((match, index) => {
if (index === focusIndex) {
return {
id: match.textElement.id,
focus: true,
matchedLines: match.matchedLines,
};
}
return {
id: match.textElement.id,
focus: false,
matchedLines: match.matchedLines,
};
});
setAppState({
searchMatches: nextMatches,
});
}
}
}, [app, focusIndex, searchMatches, setAppState]);
}, [focusIndex, searchMatches, app]);
useEffect(() => {
return () => {
setFocusIndex(null);
searchedKeywordRef.current = null;
searchedQueryRef.current = null;
lastSceneNonceRef.current = undefined;
setAppState({
searchMatches: [],
});
setIsSearching(false);
};
}, [setAppState, setFocusIndex]);
@@ -281,12 +308,32 @@ export const SearchMenu = () => {
<div className="layer-ui__search-header">
<TextField
className={CLASSES.SEARCH_MENU_INPUT_WRAPPER}
value={keyword}
value={inputValue}
ref={searchInputRef}
placeholder={t("search.placeholder")}
icon={searchIcon}
onChange={(value) => {
setKeyword(value);
setInputValue(value);
setIsSearching(true);
const searchQuery = value.trim() as SearchQuery;
handleSearch(searchQuery, app, (matchItems, index) => {
setSearchMatches({
nonce: randomInteger(),
items: matchItems,
});
setFocusIndex(index);
searchedQueryRef.current = searchQuery;
lastSceneNonceRef.current = app.scene.getSceneNonce();
setAppState({
searchMatches: matchItems.map((searchMatch) => ({
id: searchMatch.textElement.id,
focus: false,
matchedLines: searchMatch.matchedLines,
})),
});
setIsSearching(false);
});
}}
selectOnRender
/>
@@ -324,8 +371,8 @@ export const SearchMenu = () => {
)}
{searchMatches.items.length === 0 &&
keyword &&
searchedKeywordRef.current && (
searchQuery &&
searchedQueryRef.current && (
<div style={{ margin: "1rem auto" }}>{t("search.noMatch")}</div>
)}
</div>
@@ -334,7 +381,7 @@ export const SearchMenu = () => {
matches={searchMatches}
onItemClick={setFocusIndex}
focusIndex={focusIndex}
trimmedKeyword={keyword.trim()}
searchQuery={searchQuery}
/>
</div>
);
@@ -342,19 +389,19 @@ export const SearchMenu = () => {
const ListItem = (props: {
preview: SearchMatchItem["preview"];
trimmedKeyword: string;
searchQuery: SearchQuery;
highlighted: boolean;
onClick?: () => void;
}) => {
const preview = [
props.preview.moreBefore ? "..." : "",
props.preview.previewText.slice(0, props.preview.indexInKeyword),
props.preview.previewText.slice(0, props.preview.indexInSearchQuery),
props.preview.previewText.slice(
props.preview.indexInKeyword,
props.preview.indexInKeyword + props.trimmedKeyword.length,
props.preview.indexInSearchQuery,
props.preview.indexInSearchQuery + props.searchQuery.length,
),
props.preview.previewText.slice(
props.preview.indexInKeyword + props.trimmedKeyword.length,
props.preview.indexInSearchQuery + props.searchQuery.length,
),
props.preview.moreAfter ? "..." : "",
];
@@ -385,7 +432,7 @@ interface MatchListProps {
matches: SearchMatches;
onItemClick: (index: number) => void;
focusIndex: number | null;
trimmedKeyword: string;
searchQuery: SearchQuery;
}
const MatchListBase = (props: MatchListProps) => {
@@ -394,7 +441,7 @@ const MatchListBase = (props: MatchListProps) => {
{props.matches.items.map((searchMatch, index) => (
<ListItem
key={searchMatch.textElement.id + searchMatch.index}
trimmedKeyword={props.trimmedKeyword}
searchQuery={props.searchQuery}
preview={searchMatch.preview}
highlighted={index === props.focusIndex}
onClick={() => props.onItemClick(index)}
@@ -413,24 +460,27 @@ const areEqual = (prevProps: MatchListProps, nextProps: MatchListProps) => {
const MatchList = memo(MatchListBase, areEqual);
const getMatchPreview = (text: string, index: number, keyword: string) => {
const getMatchPreview = (
text: string,
index: number,
searchQuery: SearchQuery,
) => {
const WORDS_BEFORE = 2;
const WORDS_AFTER = 5;
const substrBeforeKeyword = text.slice(0, index);
const wordsBeforeKeyword = substrBeforeKeyword.split(/\s+/);
// text = "small", keyword = "mall", not complete before
// text = "small", keyword = "smal", complete before
const isKeywordCompleteBefore = substrBeforeKeyword.endsWith(" ");
const substrBeforeQuery = text.slice(0, index);
const wordsBeforeQuery = substrBeforeQuery.split(/\s+/);
// text = "small", query = "mall", not complete before
// text = "small", query = "smal", complete before
const isQueryCompleteBefore = substrBeforeQuery.endsWith(" ");
const startWordIndex =
wordsBeforeKeyword.length -
wordsBeforeQuery.length -
WORDS_BEFORE -
1 -
(isKeywordCompleteBefore ? 0 : 1);
(isQueryCompleteBefore ? 0 : 1);
let wordsBeforeAsString =
wordsBeforeKeyword
.slice(startWordIndex <= 0 ? 0 : startWordIndex)
.join(" ") + (isKeywordCompleteBefore ? " " : "");
wordsBeforeQuery.slice(startWordIndex <= 0 ? 0 : startWordIndex).join(" ") +
(isQueryCompleteBefore ? " " : "");
const MAX_ALLOWED_CHARS = 20;
@@ -439,21 +489,21 @@ const getMatchPreview = (text: string, index: number, keyword: string) => {
? wordsBeforeAsString.slice(-MAX_ALLOWED_CHARS)
: wordsBeforeAsString;
const substrAfterKeyword = text.slice(index + keyword.length);
const wordsAfter = substrAfterKeyword.split(/\s+/);
// text = "small", keyword = "mall", complete after
// text = "small", keyword = "smal", not complete after
const isKeywordCompleteAfter = !substrAfterKeyword.startsWith(" ");
const numberOfWordsToTake = isKeywordCompleteAfter
const substrAfterQuery = text.slice(index + searchQuery.length);
const wordsAfter = substrAfterQuery.split(/\s+/);
// text = "small", query = "mall", complete after
// text = "small", query = "smal", not complete after
const isQueryCompleteAfter = !substrAfterQuery.startsWith(" ");
const numberOfWordsToTake = isQueryCompleteAfter
? WORDS_AFTER + 1
: WORDS_AFTER;
const wordsAfterAsString =
(isKeywordCompleteAfter ? "" : " ") +
(isQueryCompleteAfter ? "" : " ") +
wordsAfter.slice(0, numberOfWordsToTake).join(" ");
return {
indexInKeyword: wordsBeforeAsString.length,
previewText: wordsBeforeAsString + keyword + wordsAfterAsString,
indexInSearchQuery: wordsBeforeAsString.length,
previewText: wordsBeforeAsString + searchQuery + wordsAfterAsString,
moreBefore: startWordIndex > 0,
moreAfter: wordsAfter.length > numberOfWordsToTake,
};
@@ -496,7 +546,7 @@ const normalizeWrappedText = (
const getMatchedLines = (
textElement: ExcalidrawTextElement,
keyword: string,
searchQuery: SearchQuery,
index: number,
) => {
const normalizedText = normalizeWrappedText(
@@ -527,9 +577,9 @@ const getMatchedLines = (
}
let startIndex = index;
let remainingKeyword = textElement.originalText.slice(
let remainingQuery = textElement.originalText.slice(
index,
index + keyword.length,
index + searchQuery.length,
);
const matchedLines: {
offsetX: number;
@@ -539,7 +589,7 @@ const getMatchedLines = (
}[] = [];
for (const lineIndexRange of lineIndexRanges) {
if (remainingKeyword === "") {
if (remainingQuery === "") {
break;
}
@@ -553,8 +603,8 @@ const getMatchedLines = (
startIndex - lineIndexRange.startIndex,
);
const matchedWord = remainingKeyword.slice(0, matchCapacity);
remainingKeyword = remainingKeyword.slice(matchCapacity);
const matchedWord = remainingQuery.slice(0, matchCapacity);
remainingQuery = remainingQuery.slice(matchCapacity);
const offset = measureText(
textToStart,
@@ -613,11 +663,11 @@ const escapeSpecialCharacters = (string: string) => {
const handleSearch = debounce(
(
keyword: string,
searchQuery: SearchQuery,
app: AppClassProperties,
cb: (matchItems: SearchMatchItem[], focusIndex: number | null) => void,
) => {
if (!keyword || keyword === "") {
if (!searchQuery || searchQuery === "") {
cb([], null);
return;
}
@@ -631,20 +681,20 @@ const handleSearch = debounce(
const matchItems: SearchMatchItem[] = [];
const regex = new RegExp(escapeSpecialCharacters(keyword), "gi");
const regex = new RegExp(escapeSpecialCharacters(searchQuery), "gi");
for (const textEl of texts) {
let match = null;
const text = textEl.originalText;
while ((match = regex.exec(text)) !== null) {
const preview = getMatchPreview(text, match.index, keyword);
const matchedLines = getMatchedLines(textEl, keyword, match.index);
const preview = getMatchPreview(text, match.index, searchQuery);
const matchedLines = getMatchedLines(textEl, searchQuery, match.index);
if (matchedLines.length > 0) {
matchItems.push({
textElement: textEl,
keyword,
searchQuery,
preview,
index: match.index,
matchedLines,
@@ -1,29 +0,0 @@
import { SEARCH_SIDEBAR } from "../constants";
import { t } from "../i18n";
import { SearchMenu } from "./SearchMenu";
import { Sidebar } from "./Sidebar/Sidebar";
export const SearchSidebar = () => {
return (
<Sidebar name={SEARCH_SIDEBAR.name} docked>
<Sidebar.Tabs>
<Sidebar.Header>
<div
style={{
color: "var(--color-primary)",
fontSize: "1.2em",
fontWeight: "bold",
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
paddingRight: "1em",
}}
>
{t("search.title")}
</div>
</Sidebar.Header>
<SearchMenu />
</Sidebar.Tabs>
</Sidebar>
);
};
+1 -4
View File
@@ -377,16 +377,13 @@ export const DEFAULT_ELEMENT_PROPS: {
};
export const LIBRARY_SIDEBAR_TAB = "library";
export const CANVAS_SEARCH_TAB = "search";
export const DEFAULT_SIDEBAR = {
name: "default",
defaultTab: LIBRARY_SIDEBAR_TAB,
} as const;
export const SEARCH_SIDEBAR = {
name: "search",
};
export const LIBRARY_DISABLED_TYPES = new Set([
"iframe",
"embeddable",
+1 -1
View File
@@ -124,7 +124,7 @@ body.excalidraw-cursor-resize * {
// recommends surface color of #121212, 93% yields #111111 for #FFF
canvas {
filter: var(--theme-filter);
// filter: var(--theme-filter);
}
}
@@ -189,7 +189,11 @@
}
}
$theme-filter: "invert(93%) hue-rotate(180deg)";
$theme-filter: "invert(93%) hue-rotate(180deg)"; // prod
// $theme-filter: "invert(93%)"; // prod
// $theme-filter: "hue-rotate(180deg)"; // prod
// $theme-filter: "hue-rotate(180deg) invert(93%)";
$right-sidebar-width: "302px";
:export {
+9
View File
@@ -57,6 +57,15 @@ export const base64ToString = async (base64: string, isByteString = false) => {
: byteStringToString(window.atob(base64));
};
export const base64ToArrayBuffer = (base64: string): ArrayBuffer => {
if (typeof Buffer !== "undefined") {
// Node.js environment
return Buffer.from(base64, "base64").buffer;
}
// Browser environment
return byteStringToArrayBuffer(atob(base64));
};
// -----------------------------------------------------------------------------
// text encoding
// -----------------------------------------------------------------------------
@@ -223,7 +223,6 @@ export const newTextElement = (
verticalAlign?: VerticalAlign;
containerId?: ExcalidrawTextContainer["id"] | null;
lineHeight?: ExcalidrawTextElement["lineHeight"];
strokeWidth?: ExcalidrawTextElement["strokeWidth"];
autoResize?: ExcalidrawTextElement["autoResize"];
} & ElementConstructorOpts,
): NonDeleted<ExcalidrawTextElement> => {
+2 -7
View File
@@ -2,7 +2,7 @@ import type { ElementsMap, ExcalidrawElement } from "./types";
import { mutateElement } from "./mutateElement";
import { isFreeDrawElement, isLinearElement } from "./typeChecks";
import { SHIFT_LOCKING_ANGLE } from "../constants";
import type { AppState, Zoom } from "../types";
import type { AppState, Offsets, Zoom } from "../types";
import { getCommonBounds, getElementBounds } from "./bounds";
import { viewportCoordsToSceneCoords } from "../utils";
@@ -67,12 +67,7 @@ export const isElementCompletelyInViewport = (
scrollY: number;
},
elementsMap: ElementsMap,
padding?: Partial<{
top: number;
right: number;
bottom: number;
left: number;
}>,
padding?: Offsets,
) => {
const [x1, y1, x2, y2] = getCommonBounds(elements, elementsMap); // scene coordinates
const topLeftSceneCoords = viewportCoordsToSceneCoords(
+8 -2
View File
@@ -11,7 +11,7 @@ import {
isBoundToContainer,
isTextElement,
} from "./typeChecks";
import { CLASSES, isSafari, POINTER_BUTTON } from "../constants";
import { CLASSES, isSafari, POINTER_BUTTON, THEME } from "../constants";
import type {
ExcalidrawElement,
ExcalidrawLinearElement,
@@ -50,6 +50,7 @@ import {
originalContainerCache,
updateOriginalContainerCache,
} from "./containerCache";
import { applyDarkModeFilter } from "../colors";
const getTransform = (
width: number,
@@ -247,7 +248,7 @@ export const textWysiwyg = ({
// adding left and right padding buffer, so that browser does not cut the glyphs (does not work in Safari)
const padding = !isSafari
? Math.ceil(updatedTextElement.fontSize / 2)
? Math.ceil(updatedTextElement.fontSize / appState.zoom.value / 2)
: 0;
// Make sure text editor height doesn't go beyond viewport
@@ -273,10 +274,15 @@ export const textWysiwyg = ({
textAlign,
verticalAlign,
color: updatedTextElement.strokeColor,
// color:
// appState.theme === THEME.DARK
// ? applyDarkModeFilter(updatedTextElement.strokeColor)
// : updatedTextElement.strokeColor,
opacity: updatedTextElement.opacity / 100,
filter: "var(--theme-filter)",
maxHeight: `${editorMaxHeight}px`,
});
// console.log("...", updatedTextElement.strokeColor);
editable.scrollTop = 0;
// For some reason updating font attribute doesn't set font family
// hence updating font family explicitly for test environment
+6 -5
View File
@@ -1,4 +1,8 @@
import { stringToBase64, toByteString } from "../data/encode";
import {
base64ToArrayBuffer,
stringToBase64,
toByteString,
} from "../data/encode";
import { LOCAL_FONT_PROTOCOL } from "./metadata";
import loadWoff2 from "./wasm/woff2.loader";
import loadHbSubset from "./wasm/hb-subset.loader";
@@ -49,10 +53,7 @@ export class ExcalidrawFont implements Font {
// it's dataurl (server), the font is inlined as base64, no need to fetch
if (url.protocol === "data:") {
const arrayBuffer = Buffer.from(
url.toString().split(",")[1],
"base64",
).buffer;
const arrayBuffer = base64ToArrayBuffer(url.toString().split(",")[1]);
const base64 = await ExcalidrawFont.subsetGlyphsByCodePoints(
arrayBuffer,
+10
View File
@@ -104,3 +104,13 @@ declare namespace jest {
toBeNonNaNNumber(): void;
}
}
declare namespace tinycolor {
interface Instance {
_r: number;
_g: number;
_b: number;
_a: number;
_ok: boolean;
}
}
+1 -1
View File
@@ -167,7 +167,7 @@
"noMatch": "No matches found...",
"singleResult": "result",
"multipleResults": "results",
"placeholder": "Find text..."
"placeholder": "Find text on canvas..."
},
"buttons": {
"clearReset": "Reset the canvas",
+2
View File
@@ -63,6 +63,7 @@
"@radix-ui/react-popover": "1.0.3",
"@radix-ui/react-tabs": "1.0.2",
"@tldraw/vec": "1.7.1",
"@types/tinycolor2": "1.4.6",
"browser-fs-access": "0.29.1",
"canvas-roundrect-polyfill": "0.0.1",
"clsx": "1.1.1",
@@ -84,6 +85,7 @@
"pwacompat": "2.0.17",
"roughjs": "4.6.4",
"sass": "1.51.0",
"tinycolor2": "1.6.0",
"tunnel-rat": "0.1.2"
},
"devDependencies": {
+6 -2
View File
@@ -3,6 +3,7 @@ import type { StaticCanvasAppState, AppState } from "../types";
import type { StaticCanvasRenderConfig } from "../scene/types";
import { THEME, THEME_FILTER } from "../constants";
import { applyDarkModeFilter } from "../colors";
export const fillCircle = (
context: CanvasRenderingContext2D,
@@ -50,7 +51,7 @@ export const bootstrapCanvas = ({
context.scale(scale, scale);
if (isExporting && theme === THEME.DARK) {
context.filter = THEME_FILTER;
// context.filter = THEME_FILTER;
}
// Paint background
@@ -64,7 +65,10 @@ export const bootstrapCanvas = ({
context.clearRect(0, 0, normalizedWidth, normalizedHeight);
}
context.save();
context.fillStyle = viewBackgroundColor;
context.fillStyle =
theme === THEME.DARK
? applyDarkModeFilter(viewBackgroundColor)
: viewBackgroundColor;
context.fillRect(0, 0, normalizedWidth, normalizedHeight);
context.restore();
} else {
+20 -7
View File
@@ -61,6 +61,7 @@ import { ShapeCache } from "../scene/ShapeCache";
import { getVerticalOffset } from "../fonts";
import { isRightAngleRads } from "../../math";
import { getCornerRadius } from "../shapes";
import { applyDarkModeFilter } from "../colors";
// 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
@@ -247,9 +248,9 @@ const generateElementCanvas = (
const rc = rough.canvas(canvas);
// in dark theme, revert the image color filter
if (shouldResetImageFilter(element, renderConfig, appState)) {
context.filter = IMAGE_INVERT_FILTER;
}
// if (shouldResetImageFilter(element, renderConfig, appState)) {
// context.filter = IMAGE_INVERT_FILTER;
// }
drawElementOnCanvas(element, rc, context, renderConfig, appState);
@@ -403,7 +404,10 @@ const drawElementOnCanvas = (
case "freedraw": {
// Draw directly to canvas
context.save();
context.fillStyle = element.strokeColor;
context.fillStyle =
appState.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor;
const path = getFreeDrawPath2D(element) as Path2D;
const fillShape = ShapeCache.get(element);
@@ -412,7 +416,10 @@ const drawElementOnCanvas = (
rc.draw(fillShape);
}
context.fillStyle = element.strokeColor;
context.fillStyle =
appState.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor;
context.fill(path);
context.restore();
@@ -458,7 +465,10 @@ const drawElementOnCanvas = (
context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
context.save();
context.font = getFontString(element);
context.fillStyle = element.strokeColor;
context.fillStyle =
appState.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor;
context.textAlign = element.textAlign as CanvasTextAlign;
// Canvas does not support multiline text by default
@@ -699,7 +709,10 @@ export const renderElement = (
context.fillStyle = "rgba(0, 0, 200, 0.04)";
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
context.strokeStyle = FRAME_STYLE.strokeColor;
context.strokeStyle =
appState.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: FRAME_STYLE.strokeColor;
// TODO change later to only affect AI frames
if (isMagicFrameElement(element)) {
+22 -4
View File
@@ -5,6 +5,7 @@ import {
MAX_DECIMALS_FOR_SVG_EXPORT,
MIME_TYPES,
SVG_NS,
THEME,
} from "../constants";
import { normalizeLink, toValidURL } from "../data/url";
import { getElementAbsoluteCoords } from "../element";
@@ -37,6 +38,7 @@ import { getFontFamilyString, isRTL, isTestEnv } from "../utils";
import { getFreeDrawSvgPath, IMAGE_INVERT_FILTER } from "./renderElement";
import { getVerticalOffset } from "../fonts";
import { getCornerRadius, isPathALoop } from "../shapes";
import { applyDarkModeFilter } from "../colors";
const roughSVGDrawWithPrecision = (
rsvg: RoughSVG,
@@ -139,7 +141,7 @@ const renderElementToSvg = (
case "rectangle":
case "diamond":
case "ellipse": {
const shape = ShapeCache.generateElementShape(element, null);
const shape = ShapeCache.generateElementShape(element, renderConfig);
const node = roughSVGDrawWithPrecision(
rsvg,
shape,
@@ -389,7 +391,12 @@ const renderElementToSvg = (
);
node.setAttribute("stroke", "none");
const path = svgRoot.ownerDocument!.createElementNS(SVG_NS, "path");
path.setAttribute("fill", element.strokeColor);
path.setAttribute(
"fill",
renderConfig.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
);
path.setAttribute("d", getFreeDrawSvgPath(element));
node.appendChild(path);
@@ -421,6 +428,7 @@ const renderElementToSvg = (
image.setAttribute("width", "100%");
image.setAttribute("height", "100%");
image.setAttribute("href", fileData.dataURL);
image.setAttribute("preserveAspectRatio", "none");
symbol.appendChild(image);
@@ -525,7 +533,12 @@ const renderElementToSvg = (
rect.setAttribute("ry", FRAME_STYLE.radius.toString());
rect.setAttribute("fill", "none");
rect.setAttribute("stroke", FRAME_STYLE.strokeColor);
rect.setAttribute(
"stroke",
renderConfig.theme === THEME.DARK
? applyDarkModeFilter(FRAME_STYLE.strokeColor)
: FRAME_STYLE.strokeColor,
);
rect.setAttribute("stroke-width", FRAME_STYLE.strokeWidth.toString());
addToRoot(rect, element);
@@ -576,7 +589,12 @@ const renderElementToSvg = (
text.setAttribute("y", `${i * lineHeightPx + verticalOffset}`);
text.setAttribute("font-family", getFontFamilyString(element));
text.setAttribute("font-size", `${element.fontSize}px`);
text.setAttribute("fill", element.strokeColor);
text.setAttribute(
"fill",
renderConfig.theme === THEME.DARK
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
);
text.setAttribute("text-anchor", textAnchor);
text.setAttribute("style", "white-space: pre;");
text.setAttribute("direction", direction);
+34 -10
View File
@@ -13,7 +13,7 @@ import type {
import { generateFreeDrawShape } from "../renderer/renderElement";
import { isTransparent, assertNever } from "../utils";
import { simplify } from "points-on-curve";
import { ROUGHNESS } from "../constants";
import { ROUGHNESS, THEME } from "../constants";
import {
isElbowArrow,
isEmbeddableElement,
@@ -22,7 +22,7 @@ import {
isLinearElement,
} from "../element/typeChecks";
import { canChangeRoundness } from "./comparisons";
import type { EmbedsValidationStatus } from "../types";
import type { AppState, EmbedsValidationStatus } from "../types";
import {
point,
pointDistance,
@@ -30,6 +30,7 @@ import {
type LocalPoint,
} from "../../math";
import { getCornerRadius, isPathALoop } from "../shapes";
import { applyDarkModeFilter } from "../colors";
const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth];
@@ -61,6 +62,7 @@ function adjustRoughness(element: ExcalidrawElement): number {
export const generateRoughOptions = (
element: ExcalidrawElement,
continuousPath = false,
isDarkMode: boolean = false,
): Options => {
const options: Options = {
seed: element.seed,
@@ -85,7 +87,9 @@ export const generateRoughOptions = (
fillWeight: element.strokeWidth / 2,
hachureGap: element.strokeWidth * 4,
roughness: adjustRoughness(element),
stroke: element.strokeColor,
stroke: isDarkMode
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
preserveVertices:
continuousPath || element.roughness < ROUGHNESS.cartoonist,
};
@@ -99,6 +103,8 @@ export const generateRoughOptions = (
options.fillStyle = element.fillStyle;
options.fill = isTransparent(element.backgroundColor)
? undefined
: isDarkMode
? applyDarkModeFilter(element.backgroundColor)
: element.backgroundColor;
if (element.type === "ellipse") {
options.curveFitting = 1;
@@ -112,6 +118,8 @@ export const generateRoughOptions = (
options.fill =
element.backgroundColor === "transparent"
? undefined
: isDarkMode
? applyDarkModeFilter(element.backgroundColor)
: element.backgroundColor;
}
return options;
@@ -165,6 +173,7 @@ const getArrowheadShapes = (
generator: RoughGenerator,
options: Options,
canvasBackgroundColor: string,
isDarkMode: boolean,
) => {
const arrowheadPoints = getArrowheadPoints(
element,
@@ -192,10 +201,14 @@ const getArrowheadShapes = (
fill:
arrowhead === "circle_outline"
? canvasBackgroundColor
: isDarkMode
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
fillStyle: "solid",
stroke: element.strokeColor,
stroke: isDarkMode
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
roughness: Math.min(0.5, options.roughness || 0),
}),
];
@@ -220,6 +233,8 @@ const getArrowheadShapes = (
fill:
arrowhead === "triangle_outline"
? canvasBackgroundColor
: isDarkMode
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
fillStyle: "solid",
roughness: Math.min(1, options.roughness || 0),
@@ -248,6 +263,8 @@ const getArrowheadShapes = (
fill:
arrowhead === "diamond_outline"
? canvasBackgroundColor
: isDarkMode
? applyDarkModeFilter(element.strokeColor)
: element.strokeColor,
fillStyle: "solid",
roughness: Math.min(1, options.roughness || 0),
@@ -291,12 +308,15 @@ export const _generateElementShape = (
isExporting,
canvasBackgroundColor,
embedsValidationStatus,
theme,
}: {
isExporting: boolean;
canvasBackgroundColor: string;
embedsValidationStatus: EmbedsValidationStatus | null;
theme: AppState["theme"];
},
): Drawable | Drawable[] | null => {
const isDarkMode = theme === THEME.DARK;
switch (element.type) {
case "rectangle":
case "iframe":
@@ -322,6 +342,7 @@ export const _generateElementShape = (
embedsValidationStatus,
),
true,
isDarkMode,
),
);
} else {
@@ -337,6 +358,7 @@ export const _generateElementShape = (
embedsValidationStatus,
),
false,
isDarkMode,
),
);
}
@@ -374,7 +396,7 @@ export const _generateElementShape = (
C ${topX} ${topY}, ${topX} ${topY}, ${topX + verticalRadius} ${
topY + horizontalRadius
}`,
generateRoughOptions(element, true),
generateRoughOptions(element, true, isDarkMode),
);
} else {
shape = generator.polygon(
@@ -384,7 +406,7 @@ export const _generateElementShape = (
[bottomX, bottomY],
[leftX, leftY],
],
generateRoughOptions(element),
generateRoughOptions(element, undefined, isDarkMode),
);
}
return shape;
@@ -395,14 +417,14 @@ export const _generateElementShape = (
element.height / 2,
element.width,
element.height,
generateRoughOptions(element),
generateRoughOptions(element, undefined, isDarkMode),
);
return shape;
}
case "line":
case "arrow": {
let shape: ElementShapes[typeof element.type];
const options = generateRoughOptions(element);
const options = generateRoughOptions(element, undefined, isDarkMode);
// points array can be empty in the beginning, so it is important to add
// initial position to it
@@ -414,7 +436,7 @@ export const _generateElementShape = (
shape = [
generator.path(
generateElbowArrowShape(points, 16),
generateRoughOptions(element, true),
generateRoughOptions(element, true, isDarkMode),
),
];
} else if (!element.roundness) {
@@ -446,6 +468,7 @@ export const _generateElementShape = (
generator,
options,
canvasBackgroundColor,
isDarkMode,
);
shape.push(...shapes);
}
@@ -463,6 +486,7 @@ export const _generateElementShape = (
generator,
options,
canvasBackgroundColor,
isDarkMode,
);
shape.push(...shapes);
}
@@ -477,7 +501,7 @@ export const _generateElementShape = (
// generate rough polygon to fill freedraw shape
const simplifiedPoints = simplify(element.points, 0.75);
shape = generator.curve(simplifiedPoints as [number, number][], {
...generateRoughOptions(element),
...generateRoughOptions(element, undefined, isDarkMode),
stroke: "none",
});
} else {
+3
View File
@@ -9,6 +9,7 @@ import { _generateElementShape } from "./Shape";
import type { ElementShape, ElementShapes } from "./types";
import { COLOR_PALETTE } from "../colors";
import type { AppState, EmbedsValidationStatus } from "../types";
import { THEME } from "..";
export class ShapeCache {
private static rg = new RoughGenerator();
@@ -52,6 +53,7 @@ export class ShapeCache {
isExporting: boolean;
canvasBackgroundColor: AppState["viewBackgroundColor"];
embedsValidationStatus: EmbedsValidationStatus;
theme: AppState["theme"];
} | null,
) => {
// when exporting, always regenerated to guarantee the latest shape
@@ -74,6 +76,7 @@ export class ShapeCache {
isExporting: false,
canvasBackgroundColor: COLOR_PALETTE.white,
embedsValidationStatus: null,
theme: THEME.LIGHT,
},
) as T["type"] extends keyof ElementShapes
? ElementShapes[T["type"]]
+15 -3
View File
@@ -40,6 +40,7 @@ import { syncInvalidIndices } from "../fractionalIndex";
import { renderStaticScene } from "../renderer/staticScene";
import { Fonts } from "../fonts";
import type { Font } from "../fonts/ExcalidrawFont";
import { applyDarkModeFilter } from "../colors";
const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
@@ -214,6 +215,8 @@ export const exportToCanvas = async (
files,
});
const theme = appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT;
renderStaticScene({
canvas,
rc: rough.canvas(canvas),
@@ -233,7 +236,7 @@ export const exportToCanvas = async (
scrollY: -minY + exportPadding,
zoom: defaultAppState.zoom,
shouldCacheIgnoreZoom: false,
theme: appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT,
theme,
},
renderConfig: {
canvasBackgroundColor: viewBackgroundColor,
@@ -244,6 +247,7 @@ export const exportToCanvas = async (
embedsValidationStatus: new Map(),
elementsPendingErasure: new Set(),
pendingFlowchartNodes: null,
theme,
},
});
@@ -330,7 +334,7 @@ export const exportToSvg = async (
svgRoot.setAttribute("width", `${width * exportScale}`);
svgRoot.setAttribute("height", `${height * exportScale}`);
if (exportWithDarkMode) {
svgRoot.setAttribute("filter", THEME_FILTER);
// svgRoot.setAttribute("filter", THEME_FILTER);
}
const offsetX = -minX + exportPadding;
@@ -376,7 +380,12 @@ export const exportToSvg = async (
rect.setAttribute("y", "0");
rect.setAttribute("width", `${width}`);
rect.setAttribute("height", `${height}`);
rect.setAttribute("fill", viewBackgroundColor);
rect.setAttribute(
"fill",
appState.exportWithDarkMode
? applyDarkModeFilter(viewBackgroundColor)
: viewBackgroundColor,
);
svgRoot.appendChild(rect);
}
@@ -384,6 +393,8 @@ export const exportToSvg = async (
const renderEmbeddables = opts?.renderEmbeddables ?? false;
const theme = appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT;
renderSceneToSvg(
elementsForRender,
toBrandedType<RenderableElementsMap>(arrayToMap(elementsForRender)),
@@ -405,6 +416,7 @@ export const exportToSvg = async (
.map((element) => [element.id, true]),
)
: new Map(),
theme,
},
);
+17 -3
View File
@@ -1,4 +1,4 @@
import type { AppState, PointerCoords, Zoom } from "../types";
import type { AppState, Offsets, PointerCoords, Zoom } from "../types";
import type { ExcalidrawElement } from "../element/types";
import {
getCommonBounds,
@@ -31,14 +31,28 @@ export const centerScrollOn = ({
scenePoint,
viewportDimensions,
zoom,
offsets,
}: {
scenePoint: PointerCoords;
viewportDimensions: { height: number; width: number };
zoom: Zoom;
offsets?: Offsets;
}) => {
let scrollX =
(viewportDimensions.width - (offsets?.right ?? 0)) / 2 / zoom.value -
scenePoint.x;
scrollX += (offsets?.left ?? 0) / 2 / zoom.value;
let scrollY =
(viewportDimensions.height - (offsets?.bottom ?? 0)) / 2 / zoom.value -
scenePoint.y;
scrollY += (offsets?.top ?? 0) / 2 / zoom.value;
return {
scrollX: viewportDimensions.width / 2 / zoom.value - scenePoint.x,
scrollY: viewportDimensions.height / 2 / zoom.value - scenePoint.y,
scrollX,
scrollY,
};
};
+2
View File
@@ -35,6 +35,7 @@ export type StaticCanvasRenderConfig = {
embedsValidationStatus: EmbedsValidationStatus;
elementsPendingErasure: ElementsPendingErasure;
pendingFlowchartNodes: PendingExcalidrawElements | null;
theme: AppState["theme"];
};
export type SVGRenderConfig = {
@@ -46,6 +47,7 @@ export type SVGRenderConfig = {
frameRendering: AppState["frameRendering"];
canvasBackgroundColor: AppState["viewBackgroundColor"];
embedsValidationStatus: EmbedsValidationStatus;
theme: AppState["theme"];
};
export type InteractiveCanvasRenderConfig = {
File diff suppressed because one or more lines are too long
+7 -4
View File
@@ -1,7 +1,7 @@
import React from "react";
import { act, render, waitFor } from "./test-utils";
import { Excalidraw } from "../index";
import { CLASSES, SEARCH_SIDEBAR } from "../constants";
import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants";
import { Keyboard } from "./helpers/ui";
import { KEYS } from "../keys";
import { updateTextEditor } from "./queries/dom";
@@ -34,7 +34,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F);
});
expect(h.app.state.openSidebar).not.toBeNull();
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
const searchInput = await querySearchInput();
expect(searchInput.matches(":focus")).toBe(true);
@@ -78,7 +79,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F);
});
expect(h.app.state.openSidebar).not.toBeNull();
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
const searchInput = await querySearchInput();
@@ -122,7 +124,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F);
});
expect(h.app.state.openSidebar).not.toBeNull();
expect(h.app.state.openSidebar?.name).toBe(SEARCH_SIDEBAR.name);
expect(h.app.state.openSidebar?.name).toBe(DEFAULT_SIDEBAR.name);
expect(h.app.state.openSidebar?.tab).toBe(CANVAS_SEARCH_TAB);
const searchInput = await querySearchInput();
+7
View File
@@ -851,3 +851,10 @@ export type GenerateDiagramToCode = (props: {
frame: ExcalidrawMagicFrameElement;
children: readonly ExcalidrawElement[];
}) => MaybePromise<{ html: string }>;
export type Offsets = Partial<{
top: number;
right: number;
bottom: number;
left: number;
}>;
+18 -5
View File
@@ -1,14 +1,27 @@
export const PRECISION = 10e-5;
export function clamp(value: number, min: number, max: number) {
export const clamp = (value: number, min: number, max: number) => {
return Math.min(Math.max(value, min), max);
}
};
export function round(value: number, precision: number) {
export const round = (
value: number,
precision: number,
func: "round" | "floor" | "ceil" = "round",
) => {
const multiplier = Math.pow(10, precision);
return Math.round((value + Number.EPSILON) * multiplier) / multiplier;
}
return Math[func]((value + Number.EPSILON) * multiplier) / multiplier;
};
export const roundToStep = (
value: number,
step: number,
func: "round" | "floor" | "ceil" = "round",
): number => {
const factor = 1 / step;
return Math[func](value * factor) / factor;
};
export const average = (a: number, b: number) => (a + b) / 2;
+1 -1
View File
@@ -8,7 +8,7 @@ export default defineConfig({
// Since hooks are running in stack in v2, which means all hooks run serially whereas
// we need to run them in parallel
sequence: {
hooks: 'parallel',
hooks: "parallel",
},
setupFiles: ["./setupTests.ts"],
globals: true,
+38 -272
View File
@@ -1519,11 +1519,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==
"@esbuild/aix-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
"@esbuild/android-arm64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz#ef31015416dd79398082409b77aaaa2ade4d531a"
@@ -1539,11 +1534,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==
"@esbuild/android-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
"@esbuild/android-arm@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.10.tgz#1c23c7e75473aae9fb323be5d9db225142f47f52"
@@ -1559,11 +1549,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==
"@esbuild/android-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
"@esbuild/android-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.10.tgz#df6a4e6d6eb8da5595cfce16d4e3f6bc24464707"
@@ -1579,11 +1564,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==
"@esbuild/android-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
"@esbuild/darwin-arm64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz#8462a55db07c1b2fad61c8244ce04469ef1043be"
@@ -1599,11 +1579,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==
"@esbuild/darwin-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
"@esbuild/darwin-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz#d1de20bfd41bb75b955ba86a6b1004539e8218c1"
@@ -1619,11 +1594,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
"@esbuild/darwin-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
"@esbuild/freebsd-arm64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz#16904879e34c53a2e039d1284695d2db3e664d57"
@@ -1639,11 +1609,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==
"@esbuild/freebsd-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
"@esbuild/freebsd-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz#8ad9e5ca9786ca3f1ef1411bfd10b08dcd9d4cef"
@@ -1659,11 +1624,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==
"@esbuild/freebsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
"@esbuild/linux-arm64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz#d82cf2c590faece82d28bbf1cfbe36f22ae25bd2"
@@ -1679,11 +1639,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==
"@esbuild/linux-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
"@esbuild/linux-arm@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz#477b8e7c7bcd34369717b04dd9ee6972c84f4029"
@@ -1699,11 +1654,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==
"@esbuild/linux-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
"@esbuild/linux-ia32@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz#d55ff822cf5b0252a57112f86857ff23be6cab0e"
@@ -1719,11 +1669,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==
"@esbuild/linux-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
"@esbuild/linux-loong64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz#a9ad057d7e48d6c9f62ff50f6f208e331c4543c7"
@@ -1739,11 +1684,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==
"@esbuild/linux-loong64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
"@esbuild/linux-mips64el@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz#b011a96924773d60ebab396fbd7a08de66668179"
@@ -1759,11 +1699,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==
"@esbuild/linux-mips64el@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
"@esbuild/linux-ppc64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz#5d8b59929c029811e473f2544790ea11d588d4dd"
@@ -1779,11 +1714,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==
"@esbuild/linux-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
"@esbuild/linux-riscv64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz#292b06978375b271bd8bc0a554e0822957508d22"
@@ -1799,11 +1729,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==
"@esbuild/linux-riscv64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
"@esbuild/linux-s390x@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz#d30af63530f8d4fa96930374c9dd0d62bf59e069"
@@ -1819,11 +1744,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==
"@esbuild/linux-s390x@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
"@esbuild/linux-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz#898c72eeb74d9f2fb43acf316125b475548b75ce"
@@ -1839,11 +1759,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==
"@esbuild/linux-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
"@esbuild/netbsd-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz#fd473a5ae261b43eab6dad4dbd5a3155906e6c91"
@@ -1859,11 +1774,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==
"@esbuild/netbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
"@esbuild/openbsd-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz#96eb8992e526717b5272321eaad3e21f3a608e46"
@@ -1879,11 +1789,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==
"@esbuild/openbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
"@esbuild/sunos-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz#c16ee1c167f903eaaa6acf7372bee42d5a89c9bc"
@@ -1899,11 +1804,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==
"@esbuild/sunos-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
"@esbuild/win32-arm64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz#7e417d1971dbc7e469b4eceb6a5d1d667b5e3dcc"
@@ -1919,11 +1819,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==
"@esbuild/win32-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
"@esbuild/win32-ia32@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz#2b52dfec6cd061ecb36171c13bae554888b439e5"
@@ -1939,11 +1834,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==
"@esbuild/win32-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
"@esbuild/win32-x64@0.19.10":
version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz#bd123a74f243d2f3a1f046447bb9b363ee25d072"
@@ -1959,11 +1849,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
"@esbuild/win32-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -2856,161 +2741,81 @@
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27"
integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==
"@rollup/rollup-android-arm-eabi@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz#0412834dc423d1ff7be4cb1fc13a86a0cd262c11"
integrity sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==
"@rollup/rollup-android-arm64@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203"
integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==
"@rollup/rollup-android-arm64@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz#baf1a014b13654f3b9e835388df9caf8c35389cb"
integrity sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==
"@rollup/rollup-darwin-arm64@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096"
integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==
"@rollup/rollup-darwin-arm64@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz#0a2c364e775acdf1172fe3327662eec7c46e55b1"
integrity sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==
"@rollup/rollup-darwin-x64@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c"
integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==
"@rollup/rollup-darwin-x64@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz#a972db75890dfab8df0da228c28993220a468c42"
integrity sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==
"@rollup/rollup-linux-arm-gnueabihf@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8"
integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==
"@rollup/rollup-linux-arm-gnueabihf@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz#1609d0630ef61109dd19a278353e5176d92e30a1"
integrity sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==
"@rollup/rollup-linux-arm-musleabihf@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549"
integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==
"@rollup/rollup-linux-arm-musleabihf@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz#3c1dca5f160aa2e79e4b20ff6395eab21804f266"
integrity sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==
"@rollup/rollup-linux-arm64-gnu@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577"
integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==
"@rollup/rollup-linux-arm64-gnu@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz#c2fe376e8b04eafb52a286668a8df7c761470ac7"
integrity sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==
"@rollup/rollup-linux-arm64-musl@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c"
integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==
"@rollup/rollup-linux-arm64-musl@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz#e62a4235f01e0f66dbba587c087ca6db8008ec80"
integrity sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==
"@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf"
integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==
"@rollup/rollup-linux-powerpc64le-gnu@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz#24b3457e75ee9ae5b1c198bd39eea53222a74e54"
integrity sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==
"@rollup/rollup-linux-riscv64-gnu@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9"
integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==
"@rollup/rollup-linux-riscv64-gnu@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz#38edfba9620fe2ca8116c97e02bd9f2d606bde09"
integrity sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==
"@rollup/rollup-linux-s390x-gnu@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec"
integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==
"@rollup/rollup-linux-s390x-gnu@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz#a3bfb8bc5f1e802f8c76cff4a4be2e9f9ac36a18"
integrity sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==
"@rollup/rollup-linux-x64-gnu@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942"
integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==
"@rollup/rollup-linux-x64-gnu@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz#0dadf34be9199fcdda44b5985a086326344f30ad"
integrity sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==
"@rollup/rollup-linux-x64-musl@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d"
integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==
"@rollup/rollup-linux-x64-musl@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz#7b7deddce240400eb87f2406a445061b4fed99a8"
integrity sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==
"@rollup/rollup-win32-arm64-msvc@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf"
integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==
"@rollup/rollup-win32-arm64-msvc@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz#a0ca0c5149c2cfb26fab32e6ba3f16996fbdb504"
integrity sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==
"@rollup/rollup-win32-ia32-msvc@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54"
integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==
"@rollup/rollup-win32-ia32-msvc@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz#aae2886beec3024203dbb5569db3a137bc385f8e"
integrity sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==
"@rollup/rollup-win32-x64-msvc@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4"
integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==
"@rollup/rollup-win32-x64-msvc@4.21.2":
version "4.21.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz#e4291e3c1bc637083f87936c333cdbcad22af63b"
integrity sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==
"@rushstack/eslint-patch@^1.1.0":
version "1.10.3"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20"
@@ -3494,6 +3299,11 @@
dependencies:
"@types/jest" "*"
"@types/tinycolor2@1.4.6":
version "1.4.6"
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.6.tgz#670cbc0caf4e58dd61d1e3a6f26386e473087f06"
integrity sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==
"@types/trusted-types@^2.0.2":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
@@ -5999,35 +5809,6 @@ esbuild@^0.20.1:
"@esbuild/win32-ia32" "0.20.2"
"@esbuild/win32-x64" "0.20.2"
esbuild@^0.21.3:
version "0.21.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
optionalDependencies:
"@esbuild/aix-ppc64" "0.21.5"
"@esbuild/android-arm" "0.21.5"
"@esbuild/android-arm64" "0.21.5"
"@esbuild/android-x64" "0.21.5"
"@esbuild/darwin-arm64" "0.21.5"
"@esbuild/darwin-x64" "0.21.5"
"@esbuild/freebsd-arm64" "0.21.5"
"@esbuild/freebsd-x64" "0.21.5"
"@esbuild/linux-arm" "0.21.5"
"@esbuild/linux-arm64" "0.21.5"
"@esbuild/linux-ia32" "0.21.5"
"@esbuild/linux-loong64" "0.21.5"
"@esbuild/linux-mips64el" "0.21.5"
"@esbuild/linux-ppc64" "0.21.5"
"@esbuild/linux-riscv64" "0.21.5"
"@esbuild/linux-s390x" "0.21.5"
"@esbuild/linux-x64" "0.21.5"
"@esbuild/netbsd-x64" "0.21.5"
"@esbuild/openbsd-x64" "0.21.5"
"@esbuild/sunos-x64" "0.21.5"
"@esbuild/win32-arm64" "0.21.5"
"@esbuild/win32-ia32" "0.21.5"
"@esbuild/win32-x64" "0.21.5"
escalade@^3.1.1, escalade@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
@@ -8991,15 +8772,6 @@ postcss@^8.4.32, postcss@^8.4.38, postcss@^8.4.7:
picocolors "^1.0.0"
source-map-js "^1.2.0"
postcss@^8.4.41:
version "8.4.43"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.43.tgz#a5ddf22f4cc38e64c6ae030182b43e539d316419"
integrity sha512-gJAQVYbh5R3gYm33FijzCZj7CHyQ3hWMgJMprLUlIYqCwTeZhBQ19wp0e9mA25BUbEvY5+EXuuaAjqQsrBxQBQ==
dependencies:
nanoid "^3.3.7"
picocolors "^1.0.1"
source-map-js "^1.2.0"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -9475,31 +9247,6 @@ rollup@^4.13.0, rollup@^4.2.0:
"@rollup/rollup-win32-x64-msvc" "4.18.0"
fsevents "~2.3.2"
rollup@^4.20.0:
version "4.21.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.2.tgz#f41f277a448d6264e923dd1ea179f0a926aaf9b7"
integrity sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==
dependencies:
"@types/estree" "1.0.5"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.21.2"
"@rollup/rollup-android-arm64" "4.21.2"
"@rollup/rollup-darwin-arm64" "4.21.2"
"@rollup/rollup-darwin-x64" "4.21.2"
"@rollup/rollup-linux-arm-gnueabihf" "4.21.2"
"@rollup/rollup-linux-arm-musleabihf" "4.21.2"
"@rollup/rollup-linux-arm64-gnu" "4.21.2"
"@rollup/rollup-linux-arm64-musl" "4.21.2"
"@rollup/rollup-linux-powerpc64le-gnu" "4.21.2"
"@rollup/rollup-linux-riscv64-gnu" "4.21.2"
"@rollup/rollup-linux-s390x-gnu" "4.21.2"
"@rollup/rollup-linux-x64-gnu" "4.21.2"
"@rollup/rollup-linux-x64-musl" "4.21.2"
"@rollup/rollup-win32-arm64-msvc" "4.21.2"
"@rollup/rollup-win32-ia32-msvc" "4.21.2"
"@rollup/rollup-win32-x64-msvc" "4.21.2"
fsevents "~2.3.2"
roughjs@4.6.4:
version "4.6.4"
resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.4.tgz#b6f39b44645854a6e0a4a28b078368701eb7f939"
@@ -9925,7 +9672,7 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -9943,6 +9690,15 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -10014,7 +9770,14 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^3.0.0, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@6.0.1, strip-ansi@^3.0.0, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -10218,6 +9981,11 @@ tinybench@^2.8.0:
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
tinycolor2@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
tinypool@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe"
@@ -10695,17 +10463,6 @@ vite@5.0.12:
optionalDependencies:
fsevents "~2.3.3"
vite@5.4.2:
version "5.4.2"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.2.tgz#8acb6ec4bfab823cdfc1cb2d6c53ed311bc4e47e"
integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
dependencies:
esbuild "^0.21.3"
postcss "^8.4.41"
rollup "^4.20.0"
optionalDependencies:
fsevents "~2.3.3"
vite@^5.0.0:
version "5.2.11"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.11.tgz#726ec05555431735853417c3c0bfb36003ca0cbd"
@@ -11262,7 +11019,7 @@ workbox-window@7.1.0, workbox-window@^7.0.0:
"@types/trusted-types" "^2.0.2"
workbox-core "7.1.0"
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -11280,6 +11037,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"