Merge branch 'excalidraw:master' into master

This commit is contained in:
hocino 2024-09-25 19:56:13 +01:00 committed by GitHub
commit a40d5fd697
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 952 additions and 836 deletions

View file

@ -40,7 +40,7 @@ import type {
} from "@excalidraw/excalidraw/dist/excalidraw/element/types"; } from "@excalidraw/excalidraw/dist/excalidraw/element/types";
import type { ImportedLibraryData } from "@excalidraw/excalidraw/dist/excalidraw/data/types"; import type { ImportedLibraryData } from "@excalidraw/excalidraw/dist/excalidraw/data/types";
import "./App.scss"; import "./ExampleApp.scss";
type Comment = { type Comment = {
x: number; x: number;
@ -73,7 +73,7 @@ export interface AppProps {
excalidrawLib: typeof TExcalidraw; excalidrawLib: typeof TExcalidraw;
} }
export default function App({ export default function ExampleApp({
appTitle, appTitle,
useCustom, useCustom,
customArgs, customArgs,

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import * as excalidrawLib from "@excalidraw/excalidraw"; import * as excalidrawLib from "@excalidraw/excalidraw";
import { Excalidraw } from "@excalidraw/excalidraw"; import { Excalidraw } from "@excalidraw/excalidraw";
import App from "../../components/App"; import App from "../../components/ExampleApp";
import "@excalidraw/excalidraw/index.css"; import "@excalidraw/excalidraw/index.css";

View file

@ -1,4 +1,4 @@
import App from "../components/App"; import App from "../components/ExampleApp";
import React, { StrictMode } from "react"; import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";

View file

@ -649,7 +649,12 @@ const ExcalidrawWrapper = () => {
// Render the debug scene if the debug canvas is available // Render the debug scene if the debug canvas is available
if (debugCanvasRef.current && excalidrawAPI) { if (debugCanvasRef.current && excalidrawAPI) {
debugRenderer(debugCanvasRef.current, appState, window.devicePixelRatio); debugRenderer(
debugCanvasRef.current,
appState,
window.devicePixelRatio,
() => forceRefresh((prev) => !prev),
);
} }
}; };

View file

@ -68,12 +68,17 @@ const _debugRenderer = (
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
appState: AppState, appState: AppState,
scale: number, scale: number,
refresh: () => void,
) => { ) => {
const [normalizedWidth, normalizedHeight] = getNormalizedCanvasDimensions( const [normalizedWidth, normalizedHeight] = getNormalizedCanvasDimensions(
canvas, canvas,
scale, scale,
); );
if (appState.height !== canvas.height || appState.width !== canvas.width) {
refresh();
}
const context = bootstrapCanvas({ const context = bootstrapCanvas({
canvas, canvas,
scale, scale,
@ -138,8 +143,13 @@ export const saveDebugState = (debug: { enabled: boolean }) => {
}; };
export const debugRenderer = throttleRAF( export const debugRenderer = throttleRAF(
(canvas: HTMLCanvasElement, appState: AppState, scale: number) => { (
_debugRenderer(canvas, appState, scale); canvas: HTMLCanvasElement,
appState: AppState,
scale: number,
refresh: () => void,
) => {
_debugRenderer(canvas, appState, scale, refresh);
}, },
{ trailing: true }, { trailing: true },
); );

View file

@ -20,7 +20,10 @@ import {
get, get,
} from "idb-keyval"; } from "idb-keyval";
import { clearAppStateForLocalStorage } from "../../packages/excalidraw/appState"; import { clearAppStateForLocalStorage } from "../../packages/excalidraw/appState";
import { SEARCH_SIDEBAR } from "../../packages/excalidraw/constants"; import {
CANVAS_SEARCH_TAB,
DEFAULT_SIDEBAR,
} from "../../packages/excalidraw/constants";
import type { LibraryPersistedData } from "../../packages/excalidraw/data/library"; import type { LibraryPersistedData } from "../../packages/excalidraw/data/library";
import type { ImportedDataState } from "../../packages/excalidraw/data/types"; import type { ImportedDataState } from "../../packages/excalidraw/data/types";
import { clearElementsForLocalStorage } from "../../packages/excalidraw/element"; import { clearElementsForLocalStorage } from "../../packages/excalidraw/element";
@ -69,7 +72,10 @@ const saveDataStateToLocalStorage = (
try { try {
const _appState = clearAppStateForLocalStorage(appState); const _appState = clearAppStateForLocalStorage(appState);
if (_appState.openSidebar?.name === SEARCH_SIDEBAR.name) { if (
_appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
_appState.openSidebar.tab === CANVAS_SEARCH_TAB
) {
_appState.openSidebar = null; _appState.openSidebar = null;
} }

View file

@ -130,15 +130,6 @@
</script> </script>
<% } %> <% } %>
<!-- For Nunito only preload the latin range, which should be good enough for now -->
<link
rel="preload"
href="https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
<!-- Register Assistant as the UI font, before the scene inits --> <!-- Register Assistant as the UI font, before the scene inits -->
<link <link
rel="stylesheet" rel="stylesheet"

View file

@ -48,6 +48,8 @@ export default defineConfig({
}, },
}, },
sourcemap: true, sourcemap: true,
// don't auto-inline small assets (i.e. fonts hosted on CDN)
assetsInlineLimit: 0,
}, },
plugins: [ plugins: [
woff2BrowserPlugin(), woff2BrowserPlugin(),

View file

@ -36,7 +36,7 @@
"prettier": "2.6.2", "prettier": "2.6.2",
"rewire": "6.0.0", "rewire": "6.0.0",
"typescript": "4.9.4", "typescript": "4.9.4",
"vite": "5.4.2", "vite": "5.0.12",
"vite-plugin-checker": "0.7.2", "vite-plugin-checker": "0.7.2",
"vite-plugin-ejs": "1.7.0", "vite-plugin-ejs": "1.7.0",
"vite-plugin-pwa": "0.17.4", "vite-plugin-pwa": "0.17.4",

View file

@ -15,6 +15,8 @@ Please add the latest change on the top under the correct section.
### Features ### Features
- Prefer user defined coordinates and dimensions when creating a frame using [`convertToExcalidrawElements`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton#converttoexcalidrawelements) [#8517](https://github.com/excalidraw/excalidraw/pull/8517)
- `props.initialData` can now be a function that returns `ExcalidrawInitialDataState` or `Promise<ExcalidrawInitialDataState>`. [#8107](https://github.com/excalidraw/excalidraw/pull/8135) - `props.initialData` can now be a function that returns `ExcalidrawInitialDataState` or `Promise<ExcalidrawInitialDataState>`. [#8107](https://github.com/excalidraw/excalidraw/pull/8135)
- Added support for multiplayer undo/redo, by calculating invertible increments and storing them inside the local-only undo/redo stacks. [#7348](https://github.com/excalidraw/excalidraw/pull/7348) - Added support for multiplayer undo/redo, by calculating invertible increments and storing them inside the local-only undo/redo stacks. [#7348](https://github.com/excalidraw/excalidraw/pull/7348)

View file

@ -24,7 +24,7 @@ import { CODES, KEYS } from "../keys";
import { getNormalizedZoom } from "../scene"; import { getNormalizedZoom } from "../scene";
import { centerScrollOn } from "../scene/scroll"; import { centerScrollOn } from "../scene/scroll";
import { getStateForZoom } from "../scene/zoom"; import { getStateForZoom } from "../scene/zoom";
import type { AppState } from "../types"; import type { AppState, Offsets } from "../types";
import { getShortcutKey, updateActiveTool } from "../utils"; import { getShortcutKey, updateActiveTool } from "../utils";
import { register } from "./register"; import { register } from "./register";
import { Tooltip } from "../components/Tooltip"; import { Tooltip } from "../components/Tooltip";
@ -38,7 +38,7 @@ import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
import type { SceneBounds } from "../element/bounds"; import type { SceneBounds } from "../element/bounds";
import { setCursor } from "../cursor"; import { setCursor } from "../cursor";
import { StoreAction } from "../store"; import { StoreAction } from "../store";
import { clamp } from "../../math"; import { clamp, roundToStep } from "../../math";
export const actionChangeViewBackgroundColor = register({ export const actionChangeViewBackgroundColor = register({
name: "changeViewBackgroundColor", name: "changeViewBackgroundColor",
@ -259,89 +259,85 @@ const zoomValueToFitBoundsOnViewport = (
const adjustedZoomValue = const adjustedZoomValue =
smallestZoomValue * clamp(viewportZoomFactor, 0.1, 1); smallestZoomValue * clamp(viewportZoomFactor, 0.1, 1);
const zoomAdjustedToSteps = return Math.min(adjustedZoomValue, 1);
Math.floor(adjustedZoomValue / ZOOM_STEP) * ZOOM_STEP;
return getNormalizedZoom(Math.min(zoomAdjustedToSteps, 1));
}; };
export const zoomToFitBounds = ({ export const zoomToFitBounds = ({
bounds, bounds,
appState, appState,
canvasOffsets,
fitToViewport = false, fitToViewport = false,
viewportZoomFactor = 1, viewportZoomFactor = 1,
minZoom = -Infinity,
maxZoom = Infinity,
}: { }: {
bounds: SceneBounds; bounds: SceneBounds;
canvasOffsets?: Offsets;
appState: Readonly<AppState>; appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */ /** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean; fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */ /** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number; viewportZoomFactor?: number;
minZoom?: number;
maxZoom?: number;
}) => { }) => {
viewportZoomFactor = clamp(viewportZoomFactor, MIN_ZOOM, MAX_ZOOM);
const [x1, y1, x2, y2] = bounds; const [x1, y1, x2, y2] = bounds;
const centerX = (x1 + x2) / 2; const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2; const centerY = (y1 + y2) / 2;
let newZoomValue; const canvasOffsetLeft = canvasOffsets?.left ?? 0;
let scrollX; const canvasOffsetTop = canvasOffsets?.top ?? 0;
let scrollY; 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) { if (fitToViewport) {
const commonBoundsWidth = x2 - x1; const commonBoundsWidth = x2 - x1;
const commonBoundsHeight = y2 - y1; const commonBoundsHeight = y2 - y1;
newZoomValue = adjustedZoomValue =
Math.min( Math.min(
appState.width / commonBoundsWidth, effectiveCanvasWidth / commonBoundsWidth,
appState.height / commonBoundsHeight, effectiveCanvasHeight / commonBoundsHeight,
) * clamp(viewportZoomFactor, 0.1, 1); ) * viewportZoomFactor;
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;
} else { } else {
newZoomValue = zoomValueToFitBoundsOnViewport( adjustedZoomValue = zoomValueToFitBoundsOnViewport(
bounds, bounds,
{ {
width: appState.width, width: effectiveCanvasWidth,
height: appState.height, height: effectiveCanvasHeight,
}, },
viewportZoomFactor, 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 { return {
appState: { appState: {
...appState, ...appState,
scrollX, scrollX: centerScroll.scrollX,
scrollY, scrollY: centerScroll.scrollY,
zoom: { value: newZoomValue }, zoom: { value: newZoomValue },
}, },
storeAction: StoreAction.NONE, storeAction: StoreAction.NONE,
@ -349,25 +345,34 @@ export const zoomToFitBounds = ({
}; };
export const zoomToFit = ({ export const zoomToFit = ({
canvasOffsets,
targetElements, targetElements,
appState, appState,
fitToViewport, fitToViewport,
viewportZoomFactor, viewportZoomFactor,
minZoom,
maxZoom,
}: { }: {
canvasOffsets?: Offsets;
targetElements: readonly ExcalidrawElement[]; targetElements: readonly ExcalidrawElement[];
appState: Readonly<AppState>; appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */ /** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean; fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */ /** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number; viewportZoomFactor?: number;
minZoom?: number;
maxZoom?: number;
}) => { }) => {
const commonBounds = getCommonBounds(getNonDeletedElements(targetElements)); const commonBounds = getCommonBounds(getNonDeletedElements(targetElements));
return zoomToFitBounds({ return zoomToFitBounds({
canvasOffsets,
bounds: commonBounds, bounds: commonBounds,
appState, appState,
fitToViewport, fitToViewport,
viewportZoomFactor, viewportZoomFactor,
minZoom,
maxZoom,
}); });
}; };
@ -388,6 +393,7 @@ export const actionZoomToFitSelectionInViewport = register({
userToFollow: null, userToFollow: null,
}, },
fitToViewport: false, fitToViewport: false,
canvasOffsets: app.getEditorUIOffsets(),
}); });
}, },
// NOTE shift-2 should have been assigned actionZoomToFitSelection. // NOTE shift-2 should have been assigned actionZoomToFitSelection.
@ -413,7 +419,7 @@ export const actionZoomToFitSelection = register({
userToFollow: null, userToFollow: null,
}, },
fitToViewport: true, fitToViewport: true,
viewportZoomFactor: 0.7, canvasOffsets: app.getEditorUIOffsets(),
}); });
}, },
// NOTE this action should use shift-2 per figma, alas // NOTE this action should use shift-2 per figma, alas
@ -430,7 +436,7 @@ export const actionZoomToFit = register({
icon: zoomAreaIcon, icon: zoomAreaIcon,
viewMode: true, viewMode: true,
trackEvent: { category: "canvas" }, trackEvent: { category: "canvas" },
perform: (elements, appState) => perform: (elements, appState, _, app) =>
zoomToFit({ zoomToFit({
targetElements: elements, targetElements: elements,
appState: { appState: {
@ -438,6 +444,7 @@ export const actionZoomToFit = register({
userToFollow: null, userToFollow: null,
}, },
fitToViewport: false, fitToViewport: false,
canvasOffsets: app.getEditorUIOffsets(),
}), }),
keyTest: (event) => keyTest: (event) =>
event.code === CODES.ONE && event.code === CODES.ONE &&

View file

@ -217,6 +217,7 @@ export const actionFinalize = register({
onClick={updateData} onClick={updateData}
visible={appState.multiElement != null} visible={appState.multiElement != null}
size={data?.size || "medium"} size={data?.size || "medium"}
style={{ pointerEvents: "all" }}
/> />
), ),
}); });

View file

@ -0,0 +1,211 @@
import React from "react";
import { Excalidraw } from "../index";
import { render } from "../tests/test-utils";
import { API } from "../tests/helpers/api";
import { point } from "../../math";
import { actionFlipHorizontal, actionFlipVertical } from "./actionFlip";
const { h } = window;
describe("flipping re-centers selection", () => {
it("elbow arrow touches group selection side yet it remains in place after multiple moves", async () => {
const elements = [
API.createElement({
type: "rectangle",
id: "rec1",
x: 100,
y: 100,
width: 100,
height: 100,
boundElements: [{ id: "arr", type: "arrow" }],
}),
API.createElement({
type: "rectangle",
id: "rec2",
x: 220,
y: 250,
width: 100,
height: 100,
boundElements: [{ id: "arr", type: "arrow" }],
}),
API.createElement({
type: "arrow",
id: "arr",
x: 149.9,
y: 95,
width: 156,
height: 239.9,
startBinding: {
elementId: "rec1",
focus: 0,
gap: 5,
fixedPoint: [0.49, -0.05],
},
endBinding: {
elementId: "rec2",
focus: 0,
gap: 5,
fixedPoint: [-0.05, 0.49],
},
startArrowhead: null,
endArrowhead: "arrow",
points: [
point(0, 0),
point(0, -35),
point(-90.9, -35),
point(-90.9, 204.9),
point(65.1, 204.9),
],
elbowed: true,
}),
];
await render(<Excalidraw initialData={{ elements }} />);
API.setSelectedElements(elements);
expect(Object.keys(h.state.selectedElementIds).length).toBe(3);
API.executeAction(actionFlipHorizontal);
API.executeAction(actionFlipHorizontal);
API.executeAction(actionFlipHorizontal);
API.executeAction(actionFlipHorizontal);
const rec1 = h.elements.find((el) => el.id === "rec1");
expect(rec1?.x).toBeCloseTo(100);
expect(rec1?.y).toBeCloseTo(100);
const rec2 = h.elements.find((el) => el.id === "rec2");
expect(rec2?.x).toBeCloseTo(220);
expect(rec2?.y).toBeCloseTo(250);
});
});
describe("flipping arrowheads", () => {
beforeEach(async () => {
await render(<Excalidraw />);
});
it("flipping bound arrow should flip arrowheads only", () => {
const rect = API.createElement({
type: "rectangle",
boundElements: [{ type: "arrow", id: "arrow1" }],
});
const arrow = API.createElement({
type: "arrow",
id: "arrow1",
startArrowhead: "arrow",
endArrowhead: null,
endBinding: {
elementId: rect.id,
focus: 0.5,
gap: 5,
},
});
API.setElements([rect, arrow]);
API.setSelectedElements([arrow]);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe(null);
API.executeAction(actionFlipHorizontal);
expect(API.getElement(arrow).startArrowhead).toBe(null);
expect(API.getElement(arrow).endArrowhead).toBe("arrow");
API.executeAction(actionFlipHorizontal);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe(null);
API.executeAction(actionFlipVertical);
expect(API.getElement(arrow).startArrowhead).toBe(null);
expect(API.getElement(arrow).endArrowhead).toBe("arrow");
});
it("flipping bound arrow should flip arrowheads only 2", () => {
const rect = API.createElement({
type: "rectangle",
boundElements: [{ type: "arrow", id: "arrow1" }],
});
const rect2 = API.createElement({
type: "rectangle",
boundElements: [{ type: "arrow", id: "arrow1" }],
});
const arrow = API.createElement({
type: "arrow",
id: "arrow1",
startArrowhead: "arrow",
endArrowhead: "circle",
startBinding: {
elementId: rect.id,
focus: 0.5,
gap: 5,
},
endBinding: {
elementId: rect2.id,
focus: 0.5,
gap: 5,
},
});
API.setElements([rect, rect2, arrow]);
API.setSelectedElements([arrow]);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe("circle");
API.executeAction(actionFlipHorizontal);
expect(API.getElement(arrow).startArrowhead).toBe("circle");
expect(API.getElement(arrow).endArrowhead).toBe("arrow");
API.executeAction(actionFlipVertical);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe("circle");
});
it("flipping unbound arrow shouldn't flip arrowheads", () => {
const arrow = API.createElement({
type: "arrow",
id: "arrow1",
startArrowhead: "arrow",
endArrowhead: "circle",
});
API.setElements([arrow]);
API.setSelectedElements([arrow]);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe("circle");
API.executeAction(actionFlipHorizontal);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe("circle");
});
it("flipping bound arrow shouldn't flip arrowheads if selected alongside non-arrow eleemnt", () => {
const rect = API.createElement({
type: "rectangle",
boundElements: [{ type: "arrow", id: "arrow1" }],
});
const arrow = API.createElement({
type: "arrow",
id: "arrow1",
startArrowhead: "arrow",
endArrowhead: null,
endBinding: {
elementId: rect.id,
focus: 0.5,
gap: 5,
},
});
API.setElements([rect, arrow]);
API.setSelectedElements([rect, arrow]);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe(null);
API.executeAction(actionFlipHorizontal);
expect(API.getElement(arrow).startArrowhead).toBe("arrow");
expect(API.getElement(arrow).endArrowhead).toBe(null);
});
});

View file

@ -2,6 +2,8 @@ import { register } from "./register";
import { getSelectedElements } from "../scene"; import { getSelectedElements } from "../scene";
import { getNonDeletedElements } from "../element"; import { getNonDeletedElements } from "../element";
import type { import type {
ExcalidrawArrowElement,
ExcalidrawElbowArrowElement,
ExcalidrawElement, ExcalidrawElement,
NonDeleted, NonDeleted,
NonDeletedSceneElementsMap, NonDeletedSceneElementsMap,
@ -18,7 +20,13 @@ import {
import { updateFrameMembershipOfSelectedElements } from "../frame"; import { updateFrameMembershipOfSelectedElements } from "../frame";
import { flipHorizontal, flipVertical } from "../components/icons"; import { flipHorizontal, flipVertical } from "../components/icons";
import { StoreAction } from "../store"; import { StoreAction } from "../store";
import { isLinearElement } from "../element/typeChecks"; import {
isArrowElement,
isElbowArrow,
isLinearElement,
} from "../element/typeChecks";
import { mutateElbowArrow } from "../element/routing";
import { mutateElement, newElementWith } from "../element/mutateElement";
export const actionFlipHorizontal = register({ export const actionFlipHorizontal = register({
name: "flipHorizontal", name: "flipHorizontal",
@ -109,7 +117,23 @@ const flipElements = (
flipDirection: "horizontal" | "vertical", flipDirection: "horizontal" | "vertical",
app: AppClassProperties, app: AppClassProperties,
): ExcalidrawElement[] => { ): ExcalidrawElement[] => {
const { minX, minY, maxX, maxY } = getCommonBoundingBox(selectedElements); if (
selectedElements.every(
(element) =>
isArrowElement(element) && (element.startBinding || element.endBinding),
)
) {
return selectedElements.map((element) => {
const _element = element as ExcalidrawArrowElement;
return newElementWith(_element, {
startArrowhead: _element.endArrowhead,
endArrowhead: _element.startArrowhead,
});
});
}
const { minX, minY, maxX, maxY, midX, midY } =
getCommonBoundingBox(selectedElements);
resizeMultipleElements( resizeMultipleElements(
elementsMap, elementsMap,
@ -131,5 +155,48 @@ const flipElements = (
[], [],
); );
// ---------------------------------------------------------------------------
// flipping arrow elements (and potentially other) makes the selection group
// "move" across the canvas because of how arrows can bump against the "wall"
// of the selection, so we need to center the group back to the original
// position so that repeated flips don't accumulate the offset
const { elbowArrows, otherElements } = selectedElements.reduce(
(
acc: {
elbowArrows: ExcalidrawElbowArrowElement[];
otherElements: ExcalidrawElement[];
},
element,
) =>
isElbowArrow(element)
? { ...acc, elbowArrows: acc.elbowArrows.concat(element) }
: { ...acc, otherElements: acc.otherElements.concat(element) },
{ elbowArrows: [], otherElements: [] },
);
const { midX: newMidX, midY: newMidY } =
getCommonBoundingBox(selectedElements);
const [diffX, diffY] = [midX - newMidX, midY - newMidY];
otherElements.forEach((element) =>
mutateElement(element, {
x: element.x + diffX,
y: element.y + diffY,
}),
);
elbowArrows.forEach((element) =>
mutateElbowArrow(
element,
elementsMap,
element.points,
undefined,
undefined,
{
informMutation: false,
},
),
);
// ---------------------------------------------------------------------------
return selectedElements; return selectedElements;
}; };

View file

@ -1685,19 +1685,6 @@ export const actionChangeArrowType = register({
: {}), : {}),
}, },
); );
} else {
mutateElement(
newElement,
{
startBinding: newElement.startBinding
? { ...newElement.startBinding, fixedPoint: null }
: null,
endBinding: newElement.endBinding
? { ...newElement.endBinding, fixedPoint: null }
: null,
},
false,
);
} }
return newElement; return newElement;

View file

@ -3,7 +3,7 @@ import { register } from "./register";
import type { AppState } from "../types"; import type { AppState } from "../types";
import { searchIcon } from "../components/icons"; import { searchIcon } from "../components/icons";
import { StoreAction } from "../store"; import { StoreAction } from "../store";
import { CLASSES, SEARCH_SIDEBAR } from "../constants"; import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants";
export const actionToggleSearchMenu = register({ export const actionToggleSearchMenu = register({
name: "searchMenu", name: "searchMenu",
@ -17,7 +17,10 @@ export const actionToggleSearchMenu = register({
predicate: (appState) => appState.gridModeEnabled, predicate: (appState) => appState.gridModeEnabled,
}, },
perform(elements, appState, _, app) { 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 = const searchInput =
app.excalidrawContainerValue.container?.querySelector<HTMLInputElement>( app.excalidrawContainerValue.container?.querySelector<HTMLInputElement>(
`.${CLASSES.SEARCH_MENU_INPUT_WRAPPER} input`, `.${CLASSES.SEARCH_MENU_INPUT_WRAPPER} input`,
@ -31,13 +34,14 @@ export const actionToggleSearchMenu = register({
} }
searchInput?.focus(); searchInput?.focus();
searchInput?.select();
return false; return false;
} }
return { return {
appState: { appState: {
...appState, ...appState,
openSidebar: { name: SEARCH_SIDEBAR.name }, openSidebar: { name: DEFAULT_SIDEBAR.name, tab: CANVAS_SEARCH_TAB },
openDialog: null, openDialog: null,
}, },
storeAction: StoreAction.NONE, storeAction: StoreAction.NONE,

View file

@ -185,6 +185,7 @@ import type {
MagicGenerationData, MagicGenerationData,
ExcalidrawNonSelectionElement, ExcalidrawNonSelectionElement,
ExcalidrawArrowElement, ExcalidrawArrowElement,
NonDeletedSceneElementsMap,
} from "../element/types"; } from "../element/types";
import { getCenter, getDistance } from "../gesture"; import { getCenter, getDistance } from "../gesture";
import { import {
@ -259,6 +260,7 @@ import type {
ElementsPendingErasure, ElementsPendingErasure,
GenerateDiagramToCode, GenerateDiagramToCode,
NullableGridSize, NullableGridSize,
Offsets,
} from "../types"; } from "../types";
import { import {
debounce, debounce,
@ -286,6 +288,7 @@ import {
getDateTime, getDateTime,
isShallowEqual, isShallowEqual,
arrayToMap, arrayToMap,
toBrandedType,
} from "../utils"; } from "../utils";
import { import {
createSrcDoc, createSrcDoc,
@ -434,7 +437,7 @@ import { actionTextAutoResize } from "../actions/actionTextAutoResize";
import { getVisibleSceneBounds } from "../element/bounds"; import { getVisibleSceneBounds } from "../element/bounds";
import { isMaybeMermaidDefinition } from "../mermaid"; import { isMaybeMermaidDefinition } from "../mermaid";
import NewElementCanvas from "./canvases/NewElementCanvas"; import NewElementCanvas from "./canvases/NewElementCanvas";
import { mutateElbowArrow } from "../element/routing"; import { mutateElbowArrow, updateElbowArrow } from "../element/routing";
import { import {
FlowChartCreator, FlowChartCreator,
FlowChartNavigator, FlowChartNavigator,
@ -3108,7 +3111,45 @@ class App extends React.Component<AppProps, AppState> {
retainSeed?: boolean; retainSeed?: boolean;
fitToContent?: boolean; fitToContent?: boolean;
}) => { }) => {
const elements = restoreElements(opts.elements, null, undefined); let elements = opts.elements.map((el, _, elements) => {
if (isElbowArrow(el)) {
const startEndElements = [
el.startBinding &&
elements.find((l) => l.id === el.startBinding?.elementId),
el.endBinding &&
elements.find((l) => l.id === el.endBinding?.elementId),
];
const startBinding = startEndElements[0] ? el.startBinding : null;
const endBinding = startEndElements[1] ? el.endBinding : null;
return {
...el,
...updateElbowArrow(
{
...el,
startBinding,
endBinding,
},
toBrandedType<NonDeletedSceneElementsMap>(
new Map(
startEndElements
.filter((x) => x != null)
.map(
(el) =>
[el!.id, el] as [
string,
Ordered<NonDeletedExcalidrawElement>,
],
),
),
),
[el.points[0], el.points[el.points.length - 1]],
),
};
}
return el;
});
elements = restoreElements(elements, null, undefined);
const [minX, minY, maxX, maxY] = getCommonBounds(elements); const [minX, minY, maxX, maxY] = getCommonBounds(elements);
const elementsCenterX = distance(minX, maxX) / 2; const elementsCenterX = distance(minX, maxX) / 2;
@ -3232,6 +3273,7 @@ class App extends React.Component<AppProps, AppState> {
if (opts.fitToContent) { if (opts.fitToContent) {
this.scrollToContent(newElements, { this.scrollToContent(newElements, {
fitToContent: true, fitToContent: true,
canvasOffsets: this.getEditorUIOffsets(),
}); });
} }
}; };
@ -3544,7 +3586,7 @@ class App extends React.Component<AppProps, AppState> {
target: target:
| ExcalidrawElement | ExcalidrawElement
| readonly ExcalidrawElement[] = this.scene.getNonDeletedElements(), | readonly ExcalidrawElement[] = this.scene.getNonDeletedElements(),
opts?: opts?: (
| { | {
fitToContent?: boolean; fitToContent?: boolean;
fitToViewport?: never; fitToViewport?: never;
@ -3561,7 +3603,12 @@ class App extends React.Component<AppProps, AppState> {
viewportZoomFactor?: number; viewportZoomFactor?: number;
animate?: boolean; animate?: boolean;
duration?: number; duration?: number;
}, }
) & {
minZoom?: number;
maxZoom?: number;
canvasOffsets?: Offsets;
},
) => { ) => {
this.cancelInProgressAnimation?.(); this.cancelInProgressAnimation?.();
@ -3574,10 +3621,13 @@ class App extends React.Component<AppProps, AppState> {
if (opts?.fitToContent || opts?.fitToViewport) { if (opts?.fitToContent || opts?.fitToViewport) {
const { appState } = zoomToFit({ const { appState } = zoomToFit({
canvasOffsets: opts.canvasOffsets,
targetElements, targetElements,
appState: this.state, appState: this.state,
fitToViewport: !!opts?.fitToViewport, fitToViewport: !!opts?.fitToViewport,
viewportZoomFactor: opts?.viewportZoomFactor, viewportZoomFactor: opts?.viewportZoomFactor,
minZoom: opts?.minZoom,
maxZoom: opts?.maxZoom,
}); });
zoom = appState.zoom; zoom = appState.zoom;
scrollX = appState.scrollX; scrollX = appState.scrollX;
@ -3805,40 +3855,42 @@ class App extends React.Component<AppProps, AppState> {
}, },
); );
public getEditorUIOffsets = (): { public getEditorUIOffsets = (): Offsets => {
top: number;
right: number;
bottom: number;
left: number;
} => {
const toolbarBottom = const toolbarBottom =
this.excalidrawContainerRef?.current this.excalidrawContainerRef?.current
?.querySelector(".App-toolbar") ?.querySelector(".App-toolbar")
?.getBoundingClientRect()?.bottom ?? 0; ?.getBoundingClientRect()?.bottom ?? 0;
const sidebarWidth = Math.max( const sidebarRect = this.excalidrawContainerRef?.current
this.excalidrawContainerRef?.current ?.querySelector(".sidebar")
?.querySelector(".default-sidebar") ?.getBoundingClientRect();
?.getBoundingClientRect()?.width ?? 0, const propertiesPanelRect = this.excalidrawContainerRef?.current
); ?.querySelector(".App-menu__left")
const propertiesPanelWidth = Math.max( ?.getBoundingClientRect();
this.excalidrawContainerRef?.current
?.querySelector(".App-menu__left") const PADDING = 16;
?.getBoundingClientRect()?.width ?? 0,
0,
);
return getLanguage().rtl return getLanguage().rtl
? { ? {
top: toolbarBottom, top: toolbarBottom + PADDING,
right: propertiesPanelWidth, right:
bottom: 0, Math.max(
left: sidebarWidth, this.state.width -
(propertiesPanelRect?.left ?? this.state.width),
0,
) + PADDING,
bottom: PADDING,
left: Math.max(sidebarRect?.right ?? 0, 0) + PADDING,
} }
: { : {
top: toolbarBottom, top: toolbarBottom + PADDING,
right: sidebarWidth, right: Math.max(
bottom: 0, this.state.width -
left: propertiesPanelWidth, (sidebarRect?.left ?? this.state.width) +
PADDING,
0,
),
bottom: PADDING,
left: Math.max(propertiesPanelRect?.right ?? 0, 0) + PADDING,
}; };
}; };
@ -3923,7 +3975,7 @@ class App extends React.Component<AppProps, AppState> {
animate: true, animate: true,
duration: 300, duration: 300,
fitToContent: true, fitToContent: true,
viewportZoomFactor: 0.8, canvasOffsets: this.getEditorUIOffsets(),
}); });
} }
@ -3979,6 +4031,7 @@ class App extends React.Component<AppProps, AppState> {
this.scrollToContent(nextNode, { this.scrollToContent(nextNode, {
animate: true, animate: true,
duration: 300, duration: 300,
canvasOffsets: this.getEditorUIOffsets(),
}); });
} }
} }
@ -4411,6 +4464,7 @@ class App extends React.Component<AppProps, AppState> {
this.scrollToContent(firstNode, { this.scrollToContent(firstNode, {
animate: true, animate: true,
duration: 300, duration: 300,
canvasOffsets: this.getEditorUIOffsets(),
}); });
} }
} }

View file

@ -1,5 +1,9 @@
import clsx from "clsx"; 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 { useTunnels } from "../context/tunnels";
import { useUIAppState } from "../context/ui-appState"; import { useUIAppState } from "../context/ui-appState";
import type { MarkOptional, Merge } from "../utility-types"; import type { MarkOptional, Merge } from "../utility-types";
@ -10,7 +14,8 @@ import { LibraryMenu } from "./LibraryMenu";
import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common"; import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common";
import { Sidebar } from "./Sidebar/Sidebar"; import { Sidebar } from "./Sidebar/Sidebar";
import "../components/dropdownMenu/DropdownMenu.scss"; import "../components/dropdownMenu/DropdownMenu.scss";
import { t } from "../i18n"; import { SearchMenu } from "./SearchMenu";
import { LibraryIcon, searchIcon } from "./icons";
const DefaultSidebarTrigger = withInternalFallback( const DefaultSidebarTrigger = withInternalFallback(
"DefaultSidebarTrigger", "DefaultSidebarTrigger",
@ -32,14 +37,11 @@ const DefaultSidebarTrigger = withInternalFallback(
); );
DefaultSidebarTrigger.displayName = "DefaultSidebarTrigger"; DefaultSidebarTrigger.displayName = "DefaultSidebarTrigger";
const DefaultTabTriggers = ({ const DefaultTabTriggers = ({ children }: { children: React.ReactNode }) => {
children,
...rest
}: { children: React.ReactNode } & React.HTMLAttributes<HTMLDivElement>) => {
const { DefaultSidebarTabTriggersTunnel } = useTunnels(); const { DefaultSidebarTabTriggersTunnel } = useTunnels();
return ( return (
<DefaultSidebarTabTriggersTunnel.In> <DefaultSidebarTabTriggersTunnel.In>
<Sidebar.TabTriggers {...rest}>{children}</Sidebar.TabTriggers> {children}
</DefaultSidebarTabTriggersTunnel.In> </DefaultSidebarTabTriggersTunnel.In>
); );
}; };
@ -66,16 +68,21 @@ export const DefaultSidebar = Object.assign(
const { DefaultSidebarTabTriggersTunnel } = useTunnels(); const { DefaultSidebarTabTriggersTunnel } = useTunnels();
const isForceDocked = appState.openSidebar?.tab === CANVAS_SEARCH_TAB;
return ( return (
<Sidebar <Sidebar
{...rest} {...rest}
name={"default"} name="default"
key="default"
className={clsx("default-sidebar", className)} className={clsx("default-sidebar", className)}
docked={docked ?? appState.defaultSidebarDockedPreference} docked={
isForceDocked || (docked ?? appState.defaultSidebarDockedPreference)
}
onDock={ onDock={
// `onDock=false` disables docking. // `onDock=false` disables docking.
// if `docked` passed, but no onDock passed, disable manual docking. // if `docked` passed, but no onDock passed, disable manual docking.
onDock === false || (!onDock && docked != null) isForceDocked || onDock === false || (!onDock && docked != null)
? undefined ? undefined
: // compose to allow the host app to listen on default behavior : // compose to allow the host app to listen on default behavior
composeEventHandlers(onDock, (docked) => { composeEventHandlers(onDock, (docked) => {
@ -85,26 +92,22 @@ export const DefaultSidebar = Object.assign(
> >
<Sidebar.Tabs> <Sidebar.Tabs>
<Sidebar.Header> <Sidebar.Header>
{rest.__fallback && ( <Sidebar.TabTriggers>
<div <Sidebar.TabTrigger tab={CANVAS_SEARCH_TAB}>
style={{ {searchIcon}
color: "var(--color-primary)", </Sidebar.TabTrigger>
fontSize: "1.2em", <Sidebar.TabTrigger tab={LIBRARY_SIDEBAR_TAB}>
fontWeight: "bold", {LibraryIcon}
textOverflow: "ellipsis", </Sidebar.TabTrigger>
overflow: "hidden", <DefaultSidebarTabTriggersTunnel.Out />
whiteSpace: "nowrap", </Sidebar.TabTriggers>
paddingRight: "1em",
}}
>
{t("toolBar.library")}
</div>
)}
<DefaultSidebarTabTriggersTunnel.Out />
</Sidebar.Header> </Sidebar.Header>
<Sidebar.Tab tab={LIBRARY_SIDEBAR_TAB}> <Sidebar.Tab tab={LIBRARY_SIDEBAR_TAB}>
<LibraryMenu /> <LibraryMenu />
</Sidebar.Tab> </Sidebar.Tab>
<Sidebar.Tab tab={CANVAS_SEARCH_TAB}>
<SearchMenu />
</Sidebar.Tab>
{children} {children}
</Sidebar.Tabs> </Sidebar.Tabs>
</Sidebar> </Sidebar>

View file

@ -13,7 +13,7 @@ import { isEraserActive } from "../appState";
import "./HintViewer.scss"; import "./HintViewer.scss";
import { isNodeInFlowchart } from "../element/flowchart"; import { isNodeInFlowchart } from "../element/flowchart";
import { isGridModeEnabled } from "../snapping"; import { isGridModeEnabled } from "../snapping";
import { SEARCH_SIDEBAR } from "../constants"; import { CANVAS_SEARCH_TAB, DEFAULT_SIDEBAR } from "../constants";
interface HintViewerProps { interface HintViewerProps {
appState: UIAppState; appState: UIAppState;
@ -32,7 +32,8 @@ const getHints = ({
const multiMode = appState.multiElement !== null; const multiMode = appState.multiElement !== null;
if ( if (
appState.openSidebar?.name === SEARCH_SIDEBAR.name && appState.openSidebar?.name === DEFAULT_SIDEBAR.name &&
appState.openSidebar.tab === CANVAS_SEARCH_TAB &&
appState.searchMatches?.length appState.searchMatches?.length
) { ) {
return t("hints.dismissSearch"); return t("hints.dismissSearch");

View file

@ -5,7 +5,6 @@ import {
CLASSES, CLASSES,
DEFAULT_SIDEBAR, DEFAULT_SIDEBAR,
LIBRARY_SIDEBAR_WIDTH, LIBRARY_SIDEBAR_WIDTH,
SEARCH_SIDEBAR,
TOOL_TYPE, TOOL_TYPE,
} from "../constants"; } from "../constants";
import { showSelectedShapeActions } from "../element"; import { showSelectedShapeActions } from "../element";
@ -54,9 +53,6 @@ import { LibraryIcon } from "./icons";
import { UIAppStateContext } from "../context/ui-appState"; import { UIAppStateContext } from "../context/ui-appState";
import { DefaultSidebar } from "./DefaultSidebar"; import { DefaultSidebar } from "./DefaultSidebar";
import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper"; import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper";
import "./LayerUI.scss";
import "./Toolbar.scss";
import { mutateElement } from "../element/mutateElement"; import { mutateElement } from "../element/mutateElement";
import { ShapeCache } from "../scene/ShapeCache"; import { ShapeCache } from "../scene/ShapeCache";
import Scene from "../scene/Scene"; import Scene from "../scene/Scene";
@ -64,7 +60,9 @@ import { LaserPointerButton } from "./LaserPointerButton";
import { TTDDialog } from "./TTDDialog/TTDDialog"; import { TTDDialog } from "./TTDDialog/TTDDialog";
import { Stats } from "./Stats"; import { Stats } from "./Stats";
import { actionToggleStats } from "../actions"; import { actionToggleStats } from "../actions";
import { SearchSidebar } from "./SearchSidebar";
import "./LayerUI.scss";
import "./Toolbar.scss";
interface LayerUIProps { interface LayerUIProps {
actionManager: ActionManager; actionManager: ActionManager;
@ -365,21 +363,16 @@ const LayerUI = ({
const renderSidebars = () => { const renderSidebars = () => {
return ( return (
<> <DefaultSidebar
{appState.openSidebar?.name === SEARCH_SIDEBAR.name && ( __fallback
<SearchSidebar /> onDock={(docked) => {
)} trackEvent(
<DefaultSidebar "sidebar",
__fallback `toggleDock (${docked ? "dock" : "undock"})`,
onDock={(docked) => { `(${device.editor.isMobile ? "mobile" : "desktop"})`,
trackEvent( );
"sidebar", }}
`toggleDock (${docked ? "dock" : "undock"})`, />
`(${device.editor.isMobile ? "mobile" : "desktop"})`,
);
}}
/>
</>
); );
}; };

View file

@ -20,6 +20,7 @@ import { CLASSES, EVENT } from "../constants";
import { useStable } from "../hooks/useStable"; import { useStable } from "../hooks/useStable";
import "./SearchMenu.scss"; import "./SearchMenu.scss";
import { round } from "../../math";
const searchQueryAtom = atom<string>(""); const searchQueryAtom = atom<string>("");
export const searchItemInFocusAtom = atom<number | null>(null); export const searchItemInFocusAtom = atom<number | null>(null);
@ -154,16 +155,23 @@ export const SearchMenu = () => {
const match = searchMatches.items[focusIndex]; const match = searchMatches.items[focusIndex];
if (match) { if (match) {
const zoomValue = app.state.zoom.value;
const matchAsElement = newTextElement({ const matchAsElement = newTextElement({
text: match.searchQuery, text: match.searchQuery,
x: match.textElement.x + (match.matchedLines[0]?.offsetX ?? 0), x: match.textElement.x + (match.matchedLines[0]?.offsetX ?? 0),
y: match.textElement.y + (match.matchedLines[0]?.offsetY ?? 0), y: match.textElement.y + (match.matchedLines[0]?.offsetY ?? 0),
width: match.matchedLines[0]?.width, width: match.matchedLines[0]?.width,
height: match.matchedLines[0]?.height, height: match.matchedLines[0]?.height,
fontSize: match.textElement.fontSize,
fontFamily: match.textElement.fontFamily,
}); });
const FONT_SIZE_LEGIBILITY_THRESHOLD = 14;
const fontSize = match.textElement.fontSize;
const isTextTiny = const isTextTiny =
match.textElement.fontSize * app.state.zoom.value < 12; fontSize * zoomValue < FONT_SIZE_LEGIBILITY_THRESHOLD;
if ( if (
!isElementCompletelyInViewport( !isElementCompletelyInViewport(
@ -184,9 +192,17 @@ export const SearchMenu = () => {
) { ) {
let zoomOptions: Parameters<AppClassProperties["scrollToContent"]>[1]; let zoomOptions: Parameters<AppClassProperties["scrollToContent"]>[1];
if (isTextTiny && app.state.zoom.value >= 1) { if (isTextTiny) {
zoomOptions = { fitToViewport: true }; if (fontSize >= FONT_SIZE_LEGIBILITY_THRESHOLD) {
} else if (isTextTiny || app.state.zoom.value > 1) { 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 }; zoomOptions = { fitToContent: true };
} }
@ -194,6 +210,7 @@ export const SearchMenu = () => {
animate: true, animate: true,
duration: 300, duration: 300,
...zoomOptions, ...zoomOptions,
canvasOffsets: app.getEditorUIOffsets(),
}); });
} }
} }

View file

@ -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>
);
};

View file

@ -377,16 +377,13 @@ export const DEFAULT_ELEMENT_PROPS: {
}; };
export const LIBRARY_SIDEBAR_TAB = "library"; export const LIBRARY_SIDEBAR_TAB = "library";
export const CANVAS_SEARCH_TAB = "search";
export const DEFAULT_SIDEBAR = { export const DEFAULT_SIDEBAR = {
name: "default", name: "default",
defaultTab: LIBRARY_SIDEBAR_TAB, defaultTab: LIBRARY_SIDEBAR_TAB,
} as const; } as const;
export const SEARCH_SIDEBAR = {
name: "search",
};
export const LIBRARY_DISABLED_TYPES = new Set([ export const LIBRARY_DISABLED_TYPES = new Set([
"iframe", "iframe",
"embeddable", "embeddable",

View file

@ -6,11 +6,11 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"backgroundColor": "#d8f5a2", "backgroundColor": "#d8f5a2",
"boundElements": [ "boundElements": [
{ {
"id": "id45", "id": "id47",
"type": "arrow", "type": "arrow",
}, },
{ {
"id": "id46", "id": "id48",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -47,7 +47,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id46", "id": "id48",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -118,7 +118,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"seed": Any<Number>, "seed": Any<Number>,
"startArrowhead": null, "startArrowhead": null,
"startBinding": { "startBinding": {
"elementId": "id47", "elementId": "id49",
"fixedPoint": null, "fixedPoint": null,
"focus": -0.08139534883720931, "focus": -0.08139534883720931,
"gap": 1, "gap": 1,
@ -200,7 +200,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id45", "id": "id47",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -238,7 +238,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id48", "id": "id50",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -284,7 +284,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id48", "id": "id50",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -329,7 +329,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id49", "id": "id51",
"type": "text", "type": "text",
}, },
], ],
@ -392,7 +392,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"autoResize": true, "autoResize": true,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"containerId": "id48", "containerId": "id50",
"customData": undefined, "customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 5, "fontFamily": 5,
@ -433,7 +433,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id38", "id": "id40",
"type": "text", "type": "text",
}, },
], ],
@ -441,7 +441,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"elbowed": false, "elbowed": false,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "id40", "elementId": "id42",
"fixedPoint": null, "fixedPoint": null,
"focus": 0, "focus": 0,
"gap": 1, "gap": 1,
@ -472,7 +472,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"seed": Any<Number>, "seed": Any<Number>,
"startArrowhead": null, "startArrowhead": null,
"startBinding": { "startBinding": {
"elementId": "id39", "elementId": "id41",
"fixedPoint": null, "fixedPoint": null,
"focus": 0, "focus": 0,
"gap": 1, "gap": 1,
@ -496,7 +496,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"autoResize": true, "autoResize": true,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"containerId": "id37", "containerId": "id39",
"customData": undefined, "customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 5, "fontFamily": 5,
@ -537,7 +537,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id37", "id": "id39",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -574,7 +574,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id37", "id": "id39",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -611,7 +611,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id42", "id": "id44",
"type": "text", "type": "text",
}, },
], ],
@ -619,7 +619,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"elbowed": false, "elbowed": false,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "id44", "elementId": "id46",
"fixedPoint": null, "fixedPoint": null,
"focus": 0, "focus": 0,
"gap": 1, "gap": 1,
@ -650,7 +650,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"seed": Any<Number>, "seed": Any<Number>,
"startArrowhead": null, "startArrowhead": null,
"startBinding": { "startBinding": {
"elementId": "id43", "elementId": "id45",
"fixedPoint": null, "fixedPoint": null,
"focus": 0, "focus": 0,
"gap": 1, "gap": 1,
@ -674,7 +674,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"autoResize": true, "autoResize": true,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"containerId": "id41", "containerId": "id43",
"customData": undefined, "customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 5, "fontFamily": 5,
@ -716,7 +716,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id41", "id": "id43",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -762,7 +762,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id41", "id": "id43",
"type": "arrow", "type": "arrow",
}, },
], ],
@ -1303,7 +1303,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id54", "id": "id56",
"type": "text", "type": "text",
}, },
{ {
@ -1346,7 +1346,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id55", "id": "id57",
"type": "text", "type": "text",
}, },
], ],
@ -1385,7 +1385,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id56", "id": "id58",
"type": "text", "type": "text",
}, },
{ {
@ -1428,7 +1428,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id57", "id": "id59",
"type": "text", "type": "text",
}, },
{ {
@ -1475,7 +1475,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id58", "id": "id60",
"type": "text", "type": "text",
}, },
], ],
@ -1540,7 +1540,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [ "boundElements": [
{ {
"id": "id59", "id": "id61",
"type": "text", "type": "text",
}, },
], ],

View file

@ -57,6 +57,15 @@ export const base64ToString = async (base64: string, isByteString = false) => {
: byteStringToString(window.atob(base64)); : 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 // text encoding
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View file

@ -5,6 +5,7 @@ import type {
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawSelectionElement, ExcalidrawSelectionElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FixedPointBinding,
FontFamilyValues, FontFamilyValues,
OrderedExcalidrawElement, OrderedExcalidrawElement,
PointBinding, PointBinding,
@ -21,6 +22,7 @@ import {
import { import {
isArrowElement, isArrowElement,
isElbowArrow, isElbowArrow,
isFixedPointBinding,
isLinearElement, isLinearElement,
isTextElement, isTextElement,
isUsingAdaptiveRadius, isUsingAdaptiveRadius,
@ -101,8 +103,8 @@ const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
const repairBinding = ( const repairBinding = (
element: ExcalidrawLinearElement, element: ExcalidrawLinearElement,
binding: PointBinding | null, binding: PointBinding | FixedPointBinding | null,
): PointBinding | null => { ): PointBinding | FixedPointBinding | null => {
if (!binding) { if (!binding) {
return null; return null;
} }
@ -110,9 +112,11 @@ const repairBinding = (
return { return {
...binding, ...binding,
focus: binding.focus || 0, focus: binding.focus || 0,
fixedPoint: isElbowArrow(element) ...(isElbowArrow(element) && isFixedPointBinding(binding)
? normalizeFixedPoint(binding.fixedPoint ?? [0, 0]) ? {
: null, fixedPoint: normalizeFixedPoint(binding.fixedPoint ?? [0, 0]),
}
: {}),
}; };
}; };

View file

@ -309,28 +309,32 @@ describe("Test Transform", () => {
}); });
describe("Test Frames", () => { describe("Test Frames", () => {
const elements: ExcalidrawElementSkeleton[] = [
{
type: "rectangle",
x: 10,
y: 10,
strokeWidth: 2,
id: "1",
},
{
type: "diamond",
x: 120,
y: 20,
backgroundColor: "#fff3bf",
strokeWidth: 2,
label: {
text: "HELLO EXCALIDRAW",
strokeColor: "#099268",
fontSize: 30,
},
id: "2",
},
];
it("should transform frames and update frame ids when regenerated", () => { it("should transform frames and update frame ids when regenerated", () => {
const elementsSkeleton: ExcalidrawElementSkeleton[] = [ const elementsSkeleton: ExcalidrawElementSkeleton[] = [
{ ...elements,
type: "rectangle",
x: 10,
y: 10,
strokeWidth: 2,
id: "1",
},
{
type: "diamond",
x: 120,
y: 20,
backgroundColor: "#fff3bf",
strokeWidth: 2,
label: {
text: "HELLO EXCALIDRAW",
strokeColor: "#099268",
fontSize: 30,
},
id: "2",
},
{ {
type: "frame", type: "frame",
children: ["1", "2"], children: ["1", "2"],
@ -352,28 +356,9 @@ describe("Test Transform", () => {
}); });
}); });
it("should consider max of calculated and frame dimensions when provided", () => { it("should consider user defined frame dimensions over calculated when provided", () => {
const elementsSkeleton: ExcalidrawElementSkeleton[] = [ const elementsSkeleton: ExcalidrawElementSkeleton[] = [
{ ...elements,
type: "rectangle",
x: 10,
y: 10,
strokeWidth: 2,
id: "1",
},
{
type: "diamond",
x: 120,
y: 20,
backgroundColor: "#fff3bf",
strokeWidth: 2,
label: {
text: "HELLO EXCALIDRAW",
strokeColor: "#099268",
fontSize: 30,
},
id: "2",
},
{ {
type: "frame", type: "frame",
children: ["1", "2"], children: ["1", "2"],
@ -388,7 +373,27 @@ describe("Test Transform", () => {
); );
const frame = excalidrawElements.find((ele) => ele.type === "frame")!; const frame = excalidrawElements.find((ele) => ele.type === "frame")!;
expect(frame.width).toBe(800); expect(frame.width).toBe(800);
expect(frame.height).toBe(126); expect(frame.height).toBe(100);
});
it("should consider user defined frame coordinates calculated when provided", () => {
const elementsSkeleton: ExcalidrawElementSkeleton[] = [
...elements,
{
type: "frame",
children: ["1", "2"],
name: "My frame",
x: 100,
y: 300,
},
];
const excalidrawElements = convertToExcalidrawElements(
elementsSkeleton,
opts,
);
const frame = excalidrawElements.find((ele) => ele.type === "frame")!;
expect(frame.x).toBe(100);
expect(frame.y).toBe(300);
}); });
}); });

View file

@ -46,6 +46,7 @@ import {
assertNever, assertNever,
cloneJSON, cloneJSON,
getFontString, getFontString,
isDevEnv,
toBrandedType, toBrandedType,
} from "../utils"; } from "../utils";
import { getSizeFromPoints } from "../points"; import { getSizeFromPoints } from "../points";
@ -717,7 +718,7 @@ export const convertToExcalidrawElements = (
} }
// Once all the excalidraw elements are created, we can add frames since we // Once all the excalidraw elements are created, we can add frames since we
// need to calculate coordinates and dimensions of frame which is possibe after all // need to calculate coordinates and dimensions of frame which is possible after all
// frame children are processed. // frame children are processed.
for (const [id, element] of elementsWithIds) { for (const [id, element] of elementsWithIds) {
if (element.type !== "frame" && element.type !== "magicframe") { if (element.type !== "frame" && element.type !== "magicframe") {
@ -764,10 +765,26 @@ export const convertToExcalidrawElements = (
maxX = maxX + PADDING; maxX = maxX + PADDING;
maxY = maxY + PADDING; maxY = maxY + PADDING;
// Take the max of calculated and provided frame dimensions, whichever is higher const frameX = frame?.x || minX;
const width = Math.max(frame?.width, maxX - minX); const frameY = frame?.y || minY;
const height = Math.max(frame?.height, maxY - minY); const frameWidth = frame?.width || maxX - minX;
Object.assign(frame, { x: minX, y: minY, width, height }); const frameHeight = frame?.height || maxY - minY;
Object.assign(frame, {
x: frameX,
y: frameY,
width: frameWidth,
height: frameHeight,
});
if (
isDevEnv() &&
element.children.length &&
(frame?.x || frame?.y || frame?.width || frame?.height)
) {
console.info(
"User provided frame attributes are being considered, if you find this inaccurate, please remove any of the attributes - x, y, width and height so frame coordinates and dimensions are calculated automatically",
);
}
} }
return elementStore.getElements(); return elementStore.getElements();

View file

@ -39,6 +39,7 @@ import {
isBindingElement, isBindingElement,
isBoundToContainer, isBoundToContainer,
isElbowArrow, isElbowArrow,
isFixedPointBinding,
isFrameLikeElement, isFrameLikeElement,
isLinearElement, isLinearElement,
isRectangularElement, isRectangularElement,
@ -797,7 +798,7 @@ export const bindPointToSnapToElementOutline = (
isVertical isVertical
? Math.abs(p[1] - i[1]) < 0.1 ? Math.abs(p[1] - i[1]) < 0.1
: Math.abs(p[0] - i[0]) < 0.1, : Math.abs(p[0] - i[0]) < 0.1,
)[0] ?? point; )[0] ?? p;
} }
return p; return p;
@ -1013,7 +1014,7 @@ const updateBoundPoint = (
const direction = startOrEnd === "startBinding" ? -1 : 1; const direction = startOrEnd === "startBinding" ? -1 : 1;
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1; const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
if (isElbowArrow(linearElement)) { if (isElbowArrow(linearElement) && isFixedPointBinding(binding)) {
const fixedPoint = const fixedPoint =
normalizeFixedPoint(binding.fixedPoint) ?? normalizeFixedPoint(binding.fixedPoint) ??
calculateFixedPointForElbowArrowBinding( calculateFixedPointForElbowArrowBinding(

View file

@ -35,7 +35,6 @@ export const dragSelectedElements = (
) => { ) => {
if ( if (
_selectedElements.length === 1 && _selectedElements.length === 1 &&
isArrowElement(_selectedElements[0]) &&
isElbowArrow(_selectedElements[0]) && isElbowArrow(_selectedElements[0]) &&
(_selectedElements[0].startBinding || _selectedElements[0].endBinding) (_selectedElements[0].startBinding || _selectedElements[0].endBinding)
) { ) {
@ -43,13 +42,7 @@ export const dragSelectedElements = (
} }
const selectedElements = _selectedElements.filter( const selectedElements = _selectedElements.filter(
(el) => (el) => !(isElbowArrow(el) && el.startBinding && el.endBinding),
!(
isArrowElement(el) &&
isElbowArrow(el) &&
el.startBinding &&
el.endBinding
),
); );
// we do not want a frame and its elements to be selected at the same time // we do not want a frame and its elements to be selected at the same time

View file

@ -102,6 +102,7 @@ export class LinearElementEditor {
public readonly endBindingElement: ExcalidrawBindableElement | null | "keep"; public readonly endBindingElement: ExcalidrawBindableElement | null | "keep";
public readonly hoverPointIndex: number; public readonly hoverPointIndex: number;
public readonly segmentMidPointHoveredCoords: GlobalPoint | null; public readonly segmentMidPointHoveredCoords: GlobalPoint | null;
public readonly elbowed: boolean;
constructor(element: NonDeleted<ExcalidrawLinearElement>) { constructor(element: NonDeleted<ExcalidrawLinearElement>) {
this.elementId = element.id as string & { this.elementId = element.id as string & {
@ -131,6 +132,7 @@ export class LinearElementEditor {
}; };
this.hoverPointIndex = -1; this.hoverPointIndex = -1;
this.segmentMidPointHoveredCoords = null; this.segmentMidPointHoveredCoords = null;
this.elbowed = isElbowArrow(element) && element.elbowed;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -1477,7 +1479,9 @@ export class LinearElementEditor {
nextPoints, nextPoints,
vector(offsetX, offsetY), vector(offsetX, offsetY),
bindings, bindings,
options, {
isDragging: options?.isDragging,
},
); );
} else { } else {
const nextCoords = getElementPointsCoords(element, nextPoints); const nextCoords = getElementPointsCoords(element, nextPoints);

View file

@ -223,7 +223,6 @@ export const newTextElement = (
verticalAlign?: VerticalAlign; verticalAlign?: VerticalAlign;
containerId?: ExcalidrawTextContainer["id"] | null; containerId?: ExcalidrawTextContainer["id"] | null;
lineHeight?: ExcalidrawTextElement["lineHeight"]; lineHeight?: ExcalidrawTextElement["lineHeight"];
strokeWidth?: ExcalidrawTextElement["strokeWidth"];
autoResize?: ExcalidrawTextElement["autoResize"]; autoResize?: ExcalidrawTextElement["autoResize"];
} & ElementConstructorOpts, } & ElementConstructorOpts,
): NonDeleted<ExcalidrawTextElement> => { ): NonDeleted<ExcalidrawTextElement> => {

View file

@ -9,6 +9,7 @@ import type {
ExcalidrawTextElementWithContainer, ExcalidrawTextElementWithContainer,
ExcalidrawImageElement, ExcalidrawImageElement,
ElementsMap, ElementsMap,
ExcalidrawArrowElement,
NonDeletedSceneElementsMap, NonDeletedSceneElementsMap,
SceneElementsMap, SceneElementsMap,
} from "./types"; } from "./types";
@ -909,6 +910,8 @@ export const resizeMultipleElements = (
fontSize?: ExcalidrawTextElement["fontSize"]; fontSize?: ExcalidrawTextElement["fontSize"];
scale?: ExcalidrawImageElement["scale"]; scale?: ExcalidrawImageElement["scale"];
boundTextFontSize?: ExcalidrawTextElement["fontSize"]; boundTextFontSize?: ExcalidrawTextElement["fontSize"];
startBinding?: ExcalidrawArrowElement["startBinding"];
endBinding?: ExcalidrawArrowElement["endBinding"];
}; };
}[] = []; }[] = [];
@ -993,19 +996,6 @@ export const resizeMultipleElements = (
mutateElement(element, update, false); mutateElement(element, update, false);
if (isArrowElement(element) && isElbowArrow(element)) {
mutateElbowArrow(
element,
elementsMap,
element.points,
undefined,
undefined,
{
informMutation: false,
},
);
}
updateBoundElements(element, elementsMap, { updateBoundElements(element, elementsMap, {
simultaneouslyUpdated: elementsToUpdate, simultaneouslyUpdated: elementsToUpdate,
oldSize: { width: oldWidth, height: oldHeight }, oldSize: { width: oldWidth, height: oldHeight },
@ -1059,7 +1049,7 @@ const rotateMultipleElements = (
(centerAngle + origAngle - element.angle) as Radians, (centerAngle + origAngle - element.angle) as Radians,
); );
if (isArrowElement(element) && isElbowArrow(element)) { if (isElbowArrow(element)) {
const points = getArrowLocalFixedPoints(element, elementsMap); const points = getArrowLocalFixedPoints(element, elementsMap);
mutateElbowArrow(element, elementsMap, points); mutateElbowArrow(element, elementsMap, points);
} else { } else {

View file

@ -94,7 +94,16 @@ describe("elbow arrow routing", () => {
describe("elbow arrow ui", () => { describe("elbow arrow ui", () => {
beforeEach(async () => { beforeEach(async () => {
localStorage.clear();
await render(<Excalidraw handleKeyboardGlobally={true} />); await render(<Excalidraw handleKeyboardGlobally={true} />);
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = UI.queryContextMenu();
fireEvent.click(queryByTestId(contextMenu!, "stats")!);
}); });
it("can follow bound shapes", async () => { it("can follow bound shapes", async () => {
@ -130,8 +139,8 @@ describe("elbow arrow ui", () => {
expect(arrow.elbowed).toBe(true); expect(arrow.elbowed).toBe(true);
expect(arrow.points).toEqual([ expect(arrow.points).toEqual([
[0, 0], [0, 0],
[35, 0], [45, 0],
[35, 200], [45, 200],
[90, 200], [90, 200],
]); ]);
}); });
@ -163,14 +172,6 @@ describe("elbow arrow ui", () => {
h.state, h.state,
)[0] as ExcalidrawArrowElement; )[0] as ExcalidrawArrowElement;
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2,
clientX: 1,
clientY: 1,
});
const contextMenu = UI.queryContextMenu();
fireEvent.click(queryByTestId(contextMenu!, "stats")!);
mouse.click(51, 51); mouse.click(51, 51);
const inputAngle = UI.queryStatsProperty("A")?.querySelector( const inputAngle = UI.queryStatsProperty("A")?.querySelector(
@ -182,8 +183,8 @@ describe("elbow arrow ui", () => {
[0, 0], [0, 0],
[35, 0], [35, 0],
[35, 90], [35, 90],
[25, 90], [35, 90], // Note that coordinates are rounded above!
[25, 165], [35, 165],
[103, 165], [103, 165],
]); ]);
}); });

View file

@ -36,11 +36,11 @@ import {
HEADING_UP, HEADING_UP,
vectorToHeading, vectorToHeading,
} from "./heading"; } from "./heading";
import type { ElementUpdate } from "./mutateElement";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import { isBindableElement, isRectanguloidElement } from "./typeChecks"; import { isBindableElement, isRectanguloidElement } from "./typeChecks";
import type { import type {
ExcalidrawElbowArrowElement, ExcalidrawElbowArrowElement,
FixedPointBinding,
NonDeletedSceneElementsMap, NonDeletedSceneElementsMap,
SceneElementsMap, SceneElementsMap,
} from "./types"; } from "./types";
@ -72,16 +72,48 @@ export const mutateElbowArrow = (
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap, elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
nextPoints: readonly LocalPoint[], nextPoints: readonly LocalPoint[],
offset?: Vector, offset?: Vector,
otherUpdates?: { otherUpdates?: Omit<
startBinding?: FixedPointBinding | null; ElementUpdate<ExcalidrawElbowArrowElement>,
endBinding?: FixedPointBinding | null; "angle" | "x" | "y" | "width" | "height" | "elbowed" | "points"
>,
options?: {
isDragging?: boolean;
informMutation?: boolean;
}, },
) => {
const update = updateElbowArrow(
arrow,
elementsMap,
nextPoints,
offset,
options,
);
if (update) {
mutateElement(
arrow,
{
...otherUpdates,
...update,
angle: 0 as Radians,
},
options?.informMutation,
);
} else {
console.error("Elbow arrow cannot find a route");
}
};
export const updateElbowArrow = (
arrow: ExcalidrawElbowArrowElement,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
nextPoints: readonly LocalPoint[],
offset?: Vector,
options?: { options?: {
isDragging?: boolean; isDragging?: boolean;
disableBinding?: boolean; disableBinding?: boolean;
informMutation?: boolean; informMutation?: boolean;
}, },
) => { ): ElementUpdate<ExcalidrawElbowArrowElement> | null => {
const origStartGlobalPoint: GlobalPoint = pointTranslate( const origStartGlobalPoint: GlobalPoint = pointTranslate(
pointTranslate<LocalPoint, GlobalPoint>( pointTranslate<LocalPoint, GlobalPoint>(
nextPoints[0], nextPoints[0],
@ -235,6 +267,8 @@ export const mutateElbowArrow = (
BASE_PADDING, BASE_PADDING,
), ),
boundsOverlap, boundsOverlap,
hoveredStartElement && aabbForElement(hoveredStartElement),
hoveredEndElement && aabbForElement(hoveredEndElement),
); );
const startDonglePosition = getDonglePosition( const startDonglePosition = getDonglePosition(
dynamicAABBs[0], dynamicAABBs[0],
@ -295,18 +329,10 @@ export const mutateElbowArrow = (
startDongle && points.unshift(startGlobalPoint); startDongle && points.unshift(startGlobalPoint);
endDongle && points.push(endGlobalPoint); endDongle && points.push(endGlobalPoint);
mutateElement( return normalizedArrowElementUpdate(simplifyElbowArrowPoints(points), 0, 0);
arrow,
{
...otherUpdates,
...normalizedArrowElementUpdate(simplifyElbowArrowPoints(points), 0, 0),
angle: 0 as Radians,
},
options?.informMutation,
);
} else {
console.error("Elbow arrow cannot find a route");
} }
return null;
}; };
const offsetFromHeading = ( const offsetFromHeading = (
@ -475,7 +501,11 @@ const generateDynamicAABBs = (
startDifference?: [number, number, number, number], startDifference?: [number, number, number, number],
endDifference?: [number, number, number, number], endDifference?: [number, number, number, number],
disableSideHack?: boolean, disableSideHack?: boolean,
startElementBounds?: Bounds | null,
endElementBounds?: Bounds | null,
): Bounds[] => { ): Bounds[] => {
const startEl = startElementBounds ?? a;
const endEl = endElementBounds ?? b;
const [startUp, startRight, startDown, startLeft] = startDifference ?? [ const [startUp, startRight, startDown, startLeft] = startDifference ?? [
0, 0, 0, 0, 0, 0, 0, 0,
]; ];
@ -484,29 +514,29 @@ const generateDynamicAABBs = (
const first = [ const first = [
a[0] > b[2] a[0] > b[2]
? a[1] > b[3] || a[3] < b[1] ? a[1] > b[3] || a[3] < b[1]
? Math.min((a[0] + b[2]) / 2, a[0] - startLeft) ? Math.min((startEl[0] + endEl[2]) / 2, a[0] - startLeft)
: (a[0] + b[2]) / 2 : (startEl[0] + endEl[2]) / 2
: a[0] > b[0] : a[0] > b[0]
? a[0] - startLeft ? a[0] - startLeft
: common[0] - startLeft, : common[0] - startLeft,
a[1] > b[3] a[1] > b[3]
? a[0] > b[2] || a[2] < b[0] ? a[0] > b[2] || a[2] < b[0]
? Math.min((a[1] + b[3]) / 2, a[1] - startUp) ? Math.min((startEl[1] + endEl[3]) / 2, a[1] - startUp)
: (a[1] + b[3]) / 2 : (startEl[1] + endEl[3]) / 2
: a[1] > b[1] : a[1] > b[1]
? a[1] - startUp ? a[1] - startUp
: common[1] - startUp, : common[1] - startUp,
a[2] < b[0] a[2] < b[0]
? a[1] > b[3] || a[3] < b[1] ? a[1] > b[3] || a[3] < b[1]
? Math.max((a[2] + b[0]) / 2, a[2] + startRight) ? Math.max((startEl[2] + endEl[0]) / 2, a[2] + startRight)
: (a[2] + b[0]) / 2 : (startEl[2] + endEl[0]) / 2
: a[2] < b[2] : a[2] < b[2]
? a[2] + startRight ? a[2] + startRight
: common[2] + startRight, : common[2] + startRight,
a[3] < b[1] a[3] < b[1]
? a[0] > b[2] || a[2] < b[0] ? a[0] > b[2] || a[2] < b[0]
? Math.max((a[3] + b[1]) / 2, a[3] + startDown) ? Math.max((startEl[3] + endEl[1]) / 2, a[3] + startDown)
: (a[3] + b[1]) / 2 : (startEl[3] + endEl[1]) / 2
: a[3] < b[3] : a[3] < b[3]
? a[3] + startDown ? a[3] + startDown
: common[3] + startDown, : common[3] + startDown,
@ -514,29 +544,29 @@ const generateDynamicAABBs = (
const second = [ const second = [
b[0] > a[2] b[0] > a[2]
? b[1] > a[3] || b[3] < a[1] ? b[1] > a[3] || b[3] < a[1]
? Math.min((b[0] + a[2]) / 2, b[0] - endLeft) ? Math.min((endEl[0] + startEl[2]) / 2, b[0] - endLeft)
: (b[0] + a[2]) / 2 : (endEl[0] + startEl[2]) / 2
: b[0] > a[0] : b[0] > a[0]
? b[0] - endLeft ? b[0] - endLeft
: common[0] - endLeft, : common[0] - endLeft,
b[1] > a[3] b[1] > a[3]
? b[0] > a[2] || b[2] < a[0] ? b[0] > a[2] || b[2] < a[0]
? Math.min((b[1] + a[3]) / 2, b[1] - endUp) ? Math.min((endEl[1] + startEl[3]) / 2, b[1] - endUp)
: (b[1] + a[3]) / 2 : (endEl[1] + startEl[3]) / 2
: b[1] > a[1] : b[1] > a[1]
? b[1] - endUp ? b[1] - endUp
: common[1] - endUp, : common[1] - endUp,
b[2] < a[0] b[2] < a[0]
? b[1] > a[3] || b[3] < a[1] ? b[1] > a[3] || b[3] < a[1]
? Math.max((b[2] + a[0]) / 2, b[2] + endRight) ? Math.max((endEl[2] + startEl[0]) / 2, b[2] + endRight)
: (b[2] + a[0]) / 2 : (endEl[2] + startEl[0]) / 2
: b[2] < a[2] : b[2] < a[2]
? b[2] + endRight ? b[2] + endRight
: common[2] + endRight, : common[2] + endRight,
b[3] < a[1] b[3] < a[1]
? b[0] > a[2] || b[2] < a[0] ? b[0] > a[2] || b[2] < a[0]
? Math.max((b[3] + a[1]) / 2, b[3] + endDown) ? Math.max((endEl[3] + startEl[1]) / 2, b[3] + endDown)
: (b[3] + a[1]) / 2 : (endEl[3] + startEl[1]) / 2
: b[3] < a[3] : b[3] < a[3]
? b[3] + endDown ? b[3] + endDown
: common[3] + endDown, : common[3] + endDown,

View file

@ -2,7 +2,7 @@ import type { ElementsMap, ExcalidrawElement } from "./types";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import { isFreeDrawElement, isLinearElement } from "./typeChecks"; import { isFreeDrawElement, isLinearElement } from "./typeChecks";
import { SHIFT_LOCKING_ANGLE } from "../constants"; 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 { getCommonBounds, getElementBounds } from "./bounds";
import { viewportCoordsToSceneCoords } from "../utils"; import { viewportCoordsToSceneCoords } from "../utils";
@ -67,12 +67,7 @@ export const isElementCompletelyInViewport = (
scrollY: number; scrollY: number;
}, },
elementsMap: ElementsMap, elementsMap: ElementsMap,
padding?: Partial<{ padding?: Offsets,
top: number;
right: number;
bottom: number;
left: number;
}>,
) => { ) => {
const [x1, y1, x2, y2] = getCommonBounds(elements, elementsMap); // scene coordinates const [x1, y1, x2, y2] = getCommonBounds(elements, elementsMap); // scene coordinates
const topLeftSceneCoords = viewportCoordsToSceneCoords( const topLeftSceneCoords = viewportCoordsToSceneCoords(

View file

@ -247,7 +247,7 @@ export const textWysiwyg = ({
// adding left and right padding buffer, so that browser does not cut the glyphs (does not work in Safari) // adding left and right padding buffer, so that browser does not cut the glyphs (does not work in Safari)
const padding = !isSafari const padding = !isSafari
? Math.ceil(updatedTextElement.fontSize / 2) ? Math.ceil(updatedTextElement.fontSize / appState.zoom.value / 2)
: 0; : 0;
// Make sure text editor height doesn't go beyond viewport // Make sure text editor height doesn't go beyond viewport

View file

@ -320,9 +320,12 @@ export const getDefaultRoundnessTypeForElement = (
}; };
export const isFixedPointBinding = ( export const isFixedPointBinding = (
binding: PointBinding, binding: PointBinding | FixedPointBinding,
): binding is FixedPointBinding => { ): binding is FixedPointBinding => {
return binding.fixedPoint != null; return (
Object.hasOwn(binding, "fixedPoint") &&
(binding as FixedPointBinding).fixedPoint != null
);
}; };
// TODO: Move this to @excalidraw/math // TODO: Move this to @excalidraw/math

View file

@ -193,6 +193,7 @@ export type ExcalidrawElement =
| ExcalidrawGenericElement | ExcalidrawGenericElement
| ExcalidrawTextElement | ExcalidrawTextElement
| ExcalidrawLinearElement | ExcalidrawLinearElement
| ExcalidrawArrowElement
| ExcalidrawFreeDrawElement | ExcalidrawFreeDrawElement
| ExcalidrawImageElement | ExcalidrawImageElement
| ExcalidrawFrameElement | ExcalidrawFrameElement
@ -268,15 +269,19 @@ export type PointBinding = {
elementId: ExcalidrawBindableElement["id"]; elementId: ExcalidrawBindableElement["id"];
focus: number; focus: number;
gap: number; gap: number;
// Represents the fixed point binding information in form of a vertical and
// horizontal ratio (i.e. a percentage value in the 0.0-1.0 range). This ratio
// gives the user selected fixed point by multiplying the bound element width
// with fixedPoint[0] and the bound element height with fixedPoint[1] to get the
// bound element-local point coordinate.
fixedPoint: FixedPoint | null;
}; };
export type FixedPointBinding = Merge<PointBinding, { fixedPoint: FixedPoint }>; export type FixedPointBinding = Merge<
PointBinding,
{
// Represents the fixed point binding information in form of a vertical and
// horizontal ratio (i.e. a percentage value in the 0.0-1.0 range). This ratio
// gives the user selected fixed point by multiplying the bound element width
// with fixedPoint[0] and the bound element height with fixedPoint[1] to get the
// bound element-local point coordinate.
fixedPoint: FixedPoint;
}
>;
export type Arrowhead = export type Arrowhead =
| "arrow" | "arrow"

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 { LOCAL_FONT_PROTOCOL } from "./metadata";
import loadWoff2 from "./wasm/woff2.loader"; import loadWoff2 from "./wasm/woff2.loader";
import loadHbSubset from "./wasm/hb-subset.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 // it's dataurl (server), the font is inlined as base64, no need to fetch
if (url.protocol === "data:") { if (url.protocol === "data:") {
const arrayBuffer = Buffer.from( const arrayBuffer = base64ToArrayBuffer(url.toString().split(",")[1]);
url.toString().split(",")[1],
"base64",
).buffer;
const base64 = await ExcalidrawFont.subsetGlyphsByCodePoints( const base64 = await ExcalidrawFont.subsetGlyphsByCodePoints(
arrayBuffer, arrayBuffer,

View file

@ -24,14 +24,14 @@ import Cascadia from "./assets/CascadiaCode-Regular.woff2";
import ComicShanns from "./assets/ComicShanns-Regular.woff2"; import ComicShanns from "./assets/ComicShanns-Regular.woff2";
import LiberationSans from "./assets/LiberationSans-Regular.woff2"; import LiberationSans from "./assets/LiberationSans-Regular.woff2";
import LilitaLatin from "https://fonts.gstatic.com/s/lilitaone/v15/i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2"; import LilitaLatin from "./assets/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2";
import LilitaLatinExt from "https://fonts.gstatic.com/s/lilitaone/v15/i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2"; import LilitaLatinExt from "./assets/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2";
import NunitoLatin from "https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2"; import NunitoLatin from "./assets/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2";
import NunitoLatinExt from "https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2"; import NunitoLatinExt from "./assets/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2";
import NunitoCyrilic from "https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2"; import NunitoCyrilic from "./assets/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2";
import NunitoCyrilicExt from "https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2"; import NunitoCyrilicExt from "./assets/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2";
import NunitoVietnamese from "https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2"; import NunitoVietnamese from "./assets/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2";
export class Fonts { export class Fonts {
// it's ok to track fonts across multiple instances only once, so let's use // it's ok to track fonts across multiple instances only once, so let's use

View file

@ -167,7 +167,7 @@
"noMatch": "No matches found...", "noMatch": "No matches found...",
"singleResult": "result", "singleResult": "result",
"multipleResults": "results", "multipleResults": "results",
"placeholder": "Find text..." "placeholder": "Find text on canvas..."
}, },
"buttons": { "buttons": {
"clearReset": "Reset the canvas", "clearReset": "Reset the canvas",

View file

@ -52,7 +52,6 @@ import {
} from "./helpers"; } from "./helpers";
import oc from "open-color"; import oc from "open-color";
import { import {
isArrowElement,
isElbowArrow, isElbowArrow,
isFrameLikeElement, isFrameLikeElement,
isLinearElement, isLinearElement,
@ -807,7 +806,6 @@ const _renderInteractiveScene = ({
// Elbow arrow elements cannot be selected when bound on either end // Elbow arrow elements cannot be selected when bound on either end
( (
isSingleLinearElementSelected && isSingleLinearElementSelected &&
isArrowElement(element) &&
isElbowArrow(element) && isElbowArrow(element) &&
(element.startBinding || element.endBinding) (element.startBinding || element.endBinding)
) )

View file

@ -421,6 +421,7 @@ const renderElementToSvg = (
image.setAttribute("width", "100%"); image.setAttribute("width", "100%");
image.setAttribute("height", "100%"); image.setAttribute("height", "100%");
image.setAttribute("href", fileData.dataURL); image.setAttribute("href", fileData.dataURL);
image.setAttribute("preserveAspectRatio", "none");
symbol.appendChild(image); symbol.appendChild(image);

View file

@ -185,6 +185,11 @@ export const exportToCanvas = async (
exportingFrame ?? null, exportingFrame ?? null,
appState.frameRendering ?? null, appState.frameRendering ?? null,
); );
// for canvas export, don't clip if exporting a specific frame as it would
// clip the corners of the content
if (exportingFrame) {
frameRendering.clip = false;
}
const elementsForRender = prepareElementsForRender({ const elementsForRender = prepareElementsForRender({
elements, elements,
@ -351,6 +356,11 @@ export const exportToSvg = async (
}) rotate(${frame.angle} ${cx} ${cy})" }) rotate(${frame.angle} ${cx} ${cy})"
width="${frame.width}" width="${frame.width}"
height="${frame.height}" height="${frame.height}"
${
exportingFrame
? ""
: `rx=${FRAME_STYLE.radius} ry=${FRAME_STYLE.radius}`
}
> >
</rect> </rect>
</clipPath>`; </clipPath>`;

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 type { ExcalidrawElement } from "../element/types";
import { import {
getCommonBounds, getCommonBounds,
@ -31,14 +31,28 @@ export const centerScrollOn = ({
scenePoint, scenePoint,
viewportDimensions, viewportDimensions,
zoom, zoom,
offsets,
}: { }: {
scenePoint: PointerCoords; scenePoint: PointerCoords;
viewportDimensions: { height: number; width: number }; viewportDimensions: { height: number; width: number };
zoom: Zoom; 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 { return {
scrollX: viewportDimensions.width / 2 / zoom.value - scenePoint.x, scrollX,
scrollY: viewportDimensions.height / 2 / zoom.value - scenePoint.y, scrollY,
}; };
}; };

File diff suppressed because one or more lines are too long

View file

@ -8430,6 +8430,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1`
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
"hoverPointIndex": -1, "hoverPointIndex": -1,
@ -8649,6 +8650,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`]
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
"hoverPointIndex": -1, "hoverPointIndex": -1,
@ -9058,6 +9060,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1`
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
"hoverPointIndex": -1, "hoverPointIndex": -1,
@ -9454,6 +9457,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`]
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
"hoverPointIndex": -1, "hoverPointIndex": -1,

View file

@ -9,6 +9,8 @@ import type {
ExcalidrawFrameElement, ExcalidrawFrameElement,
ExcalidrawElementType, ExcalidrawElementType,
ExcalidrawMagicFrameElement, ExcalidrawMagicFrameElement,
ExcalidrawElbowArrowElement,
ExcalidrawArrowElement,
} from "../../element/types"; } from "../../element/types";
import { newElement, newTextElement, newLinearElement } from "../../element"; import { newElement, newTextElement, newLinearElement } from "../../element";
import { DEFAULT_VERTICAL_ALIGN, ROUNDNESS } from "../../constants"; import { DEFAULT_VERTICAL_ALIGN, ROUNDNESS } from "../../constants";
@ -127,6 +129,10 @@ export class API {
expect(API.getSelectedElements().length).toBe(0); expect(API.getSelectedElements().length).toBe(0);
}; };
static getElement = <T extends ExcalidrawElement>(element: T): T => {
return h.app.scene.getElementsMapIncludingDeleted().get(element.id) as T || element;
}
static createElement = < static createElement = <
T extends Exclude<ExcalidrawElementType, "selection"> = "rectangle", T extends Exclude<ExcalidrawElementType, "selection"> = "rectangle",
>({ >({
@ -179,10 +185,16 @@ export class API {
scale?: T extends "image" ? ExcalidrawImageElement["scale"] : never; scale?: T extends "image" ? ExcalidrawImageElement["scale"] : never;
status?: T extends "image" ? ExcalidrawImageElement["status"] : never; status?: T extends "image" ? ExcalidrawImageElement["status"] : never;
startBinding?: T extends "arrow" startBinding?: T extends "arrow"
? ExcalidrawLinearElement["startBinding"] ? ExcalidrawArrowElement["startBinding"] | ExcalidrawElbowArrowElement["startBinding"]
: never; : never;
endBinding?: T extends "arrow" endBinding?: T extends "arrow"
? ExcalidrawLinearElement["endBinding"] ? ExcalidrawArrowElement["endBinding"] | ExcalidrawElbowArrowElement["endBinding"]
: never;
startArrowhead?: T extends "arrow"
? ExcalidrawArrowElement["startArrowhead"] | ExcalidrawElbowArrowElement["startArrowhead"]
: never;
endArrowhead?: T extends "arrow"
? ExcalidrawArrowElement["endArrowhead"] | ExcalidrawElbowArrowElement["endArrowhead"]
: never; : never;
elbowed?: boolean; elbowed?: boolean;
}): T extends "arrow" | "line" }): T extends "arrow" | "line"
@ -340,6 +352,8 @@ export class API {
if (element.type === "arrow") { if (element.type === "arrow") {
element.startBinding = rest.startBinding ?? null; element.startBinding = rest.startBinding ?? null;
element.endBinding = rest.endBinding ?? null; element.endBinding = rest.endBinding ?? null;
element.startArrowhead = rest.startArrowhead ?? null;
element.endArrowhead = rest.endArrowhead ?? null;
} }
if (id) { if (id) {
element.id = id; element.id = id;

View file

@ -31,6 +31,7 @@ import type {
ExcalidrawGenericElement, ExcalidrawGenericElement,
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FixedPointBinding,
FractionalIndex, FractionalIndex,
SceneElementsMap, SceneElementsMap,
} from "../element/types"; } from "../element/types";
@ -2049,13 +2050,13 @@ describe("history", () => {
focus: -0.001587301587301948, focus: -0.001587301587301948,
gap: 5, gap: 5,
fixedPoint: [1.0318471337579618, 0.49920634920634904], fixedPoint: [1.0318471337579618, 0.49920634920634904],
}, } as FixedPointBinding,
endBinding: { endBinding: {
elementId: "u2JGnnmoJ0VATV4vCNJE5", elementId: "u2JGnnmoJ0VATV4vCNJE5",
focus: -0.0016129032258049847, focus: -0.0016129032258049847,
gap: 3.537079145500037, gap: 3.537079145500037,
fixedPoint: [0.4991935483870975, -0.03875193720914723], fixedPoint: [0.4991935483870975, -0.03875193720914723],
}, } as FixedPointBinding,
}, },
], ],
storeAction: StoreAction.CAPTURE, storeAction: StoreAction.CAPTURE,
@ -4455,7 +4456,7 @@ describe("history", () => {
elements: [ elements: [
h.elements[0], h.elements[0],
newElementWith(h.elements[1], { boundElements: [] }), newElementWith(h.elements[1], { boundElements: [] }),
newElementWith(h.elements[2] as ExcalidrawLinearElement, { newElementWith(h.elements[2] as ExcalidrawElbowArrowElement, {
endBinding: { endBinding: {
elementId: remoteContainer.id, elementId: remoteContainer.id,
gap: 1, gap: 1,
@ -4655,7 +4656,7 @@ describe("history", () => {
// Simulate remote update // Simulate remote update
API.updateScene({ API.updateScene({
elements: [ elements: [
newElementWith(h.elements[0] as ExcalidrawLinearElement, { newElementWith(h.elements[0] as ExcalidrawElbowArrowElement, {
startBinding: { startBinding: {
elementId: rect1.id, elementId: rect1.id,
gap: 1, gap: 1,

View file

@ -4,6 +4,7 @@ import { render } from "./test-utils";
import { reseed } from "../random"; import { reseed } from "../random";
import { UI, Keyboard, Pointer } from "./helpers/ui"; import { UI, Keyboard, Pointer } from "./helpers/ui";
import type { import type {
ExcalidrawElbowArrowElement,
ExcalidrawFreeDrawElement, ExcalidrawFreeDrawElement,
ExcalidrawLinearElement, ExcalidrawLinearElement,
} from "../element/types"; } from "../element/types";
@ -333,6 +334,62 @@ describe("arrow element", () => {
expect(label.angle).toBeCloseTo(0); expect(label.angle).toBeCloseTo(0);
expect(label.fontSize).toEqual(20); expect(label.fontSize).toEqual(20);
}); });
it("flips the fixed point binding on negative resize for single bindable", () => {
const rectangle = UI.createElement("rectangle", {
x: -100,
y: -75,
width: 95,
height: 100,
});
UI.clickTool("arrow");
UI.clickOnTestId("elbow-arrow");
mouse.reset();
mouse.moveTo(-5, 0);
mouse.click();
mouse.moveTo(120, 200);
mouse.click();
const arrow = h.scene.getSelectedElements(
h.state,
)[0] as ExcalidrawElbowArrowElement;
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
UI.resize(rectangle, "se", [-200, -150]);
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
});
it("flips the fixed point binding on negative resize for group selection", () => {
const rectangle = UI.createElement("rectangle", {
x: -100,
y: -75,
width: 95,
height: 100,
});
UI.clickTool("arrow");
UI.clickOnTestId("elbow-arrow");
mouse.reset();
mouse.moveTo(-5, 0);
mouse.click();
mouse.moveTo(120, 200);
mouse.click();
const arrow = h.scene.getSelectedElements(
h.state,
)[0] as ExcalidrawElbowArrowElement;
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
UI.resize([rectangle, arrow], "nw", [300, 350]);
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(-0.144, 2);
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.25);
});
}); });
describe("text element", () => { describe("text element", () => {
@ -828,7 +885,6 @@ describe("multiple selection", () => {
expect(leftBoundArrow.endBinding?.elementId).toBe( expect(leftBoundArrow.endBinding?.elementId).toBe(
leftArrowBinding.elementId, leftArrowBinding.elementId,
); );
expect(leftBoundArrow.endBinding?.fixedPoint).toBeNull();
expect(leftBoundArrow.endBinding?.focus).toBe(leftArrowBinding.focus); expect(leftBoundArrow.endBinding?.focus).toBe(leftArrowBinding.focus);
expect(rightBoundArrow.x).toBeCloseTo(210); expect(rightBoundArrow.x).toBeCloseTo(210);
@ -843,7 +899,6 @@ describe("multiple selection", () => {
expect(rightBoundArrow.endBinding?.elementId).toBe( expect(rightBoundArrow.endBinding?.elementId).toBe(
rightArrowBinding.elementId, rightArrowBinding.elementId,
); );
expect(rightBoundArrow.endBinding?.fixedPoint).toBeNull();
expect(rightBoundArrow.endBinding?.focus).toBe(rightArrowBinding.focus); expect(rightBoundArrow.endBinding?.focus).toBe(rightArrowBinding.focus);
}); });

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { act, render, waitFor } from "./test-utils"; import { act, render, waitFor } from "./test-utils";
import { Excalidraw } from "../index"; 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 { Keyboard } from "./helpers/ui";
import { KEYS } from "../keys"; import { KEYS } from "../keys";
import { updateTextEditor } from "./queries/dom"; import { updateTextEditor } from "./queries/dom";
@ -34,7 +34,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F); Keyboard.keyPress(KEYS.F);
}); });
expect(h.app.state.openSidebar).not.toBeNull(); 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(); const searchInput = await querySearchInput();
expect(searchInput.matches(":focus")).toBe(true); expect(searchInput.matches(":focus")).toBe(true);
@ -78,7 +79,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F); Keyboard.keyPress(KEYS.F);
}); });
expect(h.app.state.openSidebar).not.toBeNull(); 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(); const searchInput = await querySearchInput();
@ -122,7 +124,8 @@ describe("search", () => {
Keyboard.keyPress(KEYS.F); Keyboard.keyPress(KEYS.F);
}); });
expect(h.app.state.openSidebar).not.toBeNull(); 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(); const searchInput = await querySearchInput();

View file

@ -851,3 +851,10 @@ export type GenerateDiagramToCode = (props: {
frame: ExcalidrawMagicFrameElement; frame: ExcalidrawMagicFrameElement;
children: readonly ExcalidrawElement[]; children: readonly ExcalidrawElement[];
}) => MaybePromise<{ html: string }>; }) => MaybePromise<{ html: string }>;
export type Offsets = Partial<{
top: number;
right: number;
bottom: number;
left: number;
}>;

View file

@ -110,8 +110,8 @@ export const debugDrawBoundingBox = (
export const debugDrawBounds = ( export const debugDrawBounds = (
box: Bounds | Bounds[], box: Bounds | Bounds[],
opts?: { opts?: {
color: string; color?: string;
permanent: boolean; permanent?: boolean;
}, },
) => { ) => {
(isBounds(box) ? [box] : box).forEach((bbox) => (isBounds(box) ? [box] : box).forEach((bbox) =>
@ -136,7 +136,7 @@ export const debugDrawBounds = (
], ],
{ {
color: opts?.color ?? "green", color: opts?.color ?? "green",
permanent: opts?.permanent, permanent: !!opts?.permanent,
}, },
), ),
); );

View file

@ -1,14 +1,27 @@
export const PRECISION = 10e-5; 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); 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); 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; export const average = (a: number, b: number) => (a + b) / 2;

View file

@ -68,7 +68,6 @@
"css-loader": "6.7.1", "css-loader": "6.7.1",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"fonteditor-core": "2.4.0", "fonteditor-core": "2.4.0",
"node-fetch": "3.3.2",
"sass-loader": "13.0.2", "sass-loader": "13.0.2",
"ts-loader": "9.3.1", "ts-loader": "9.3.1",
"typescript": "4.9.4", "typescript": "4.9.4",

View file

@ -1,7 +1,6 @@
const { build } = require("esbuild"); const { build } = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin"); const { sassPlugin } = require("esbuild-sass-plugin");
const { externalGlobalPlugin } = require("esbuild-plugin-external-global"); const { externalGlobalPlugin } = require("esbuild-plugin-external-global");
const { woff2BrowserPlugin } = require("./woff2/woff2-esbuild-plugins");
// Will be used later for treeshaking // Will be used later for treeshaking
//const fs = require("fs"); //const fs = require("fs");
@ -45,13 +44,15 @@ const browserConfig = {
format: "esm", format: "esm",
plugins: [ plugins: [
sassPlugin(), sassPlugin(),
woff2BrowserPlugin(),
externalGlobalPlugin({ externalGlobalPlugin({
react: "React", react: "React",
"react-dom": "ReactDOM", "react-dom": "ReactDOM",
}), }),
], ],
splitting: true, splitting: true,
loader: {
".woff2": "file",
},
}; };
const createESMBrowserBuild = async () => { const createESMBrowserBuild = async () => {
// Development unminified build with source maps // Development unminified build with source maps
@ -100,9 +101,10 @@ const rawConfig = {
entryPoints: ["index.tsx"], entryPoints: ["index.tsx"],
bundle: true, bundle: true,
format: "esm", format: "esm",
plugins: [sassPlugin(), woff2BrowserPlugin()], plugins: [sassPlugin()],
loader: { loader: {
".json": "copy", ".json": "copy",
".woff2": "file",
}, },
packages: "external", packages: "external",
}; };

View file

@ -1,17 +1,17 @@
const fs = require("fs"); const fs = require("fs");
const { build } = require("esbuild"); const { build } = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin"); const { sassPlugin } = require("esbuild-sass-plugin");
const { const { woff2ServerPlugin } = require("./woff2/woff2-esbuild-plugins");
woff2BrowserPlugin,
woff2ServerPlugin,
} = require("./woff2/woff2-esbuild-plugins");
const browserConfig = { const browserConfig = {
entryPoints: ["index.ts"], entryPoints: ["index.ts"],
bundle: true, bundle: true,
format: "esm", format: "esm",
plugins: [sassPlugin(), woff2BrowserPlugin()], plugins: [sassPlugin()],
assetNames: "assets/[name]", assetNames: "assets/[name]",
loader: {
".woff2": "file",
},
}; };
// Will be used later for treeshaking // Will be used later for treeshaking

View file

@ -2,45 +2,9 @@ const fs = require("fs");
const path = require("path"); const path = require("path");
const { execSync } = require("child_process"); const { execSync } = require("child_process");
const which = require("which"); const which = require("which");
const fetch = require("node-fetch");
const wawoff = require("wawoff2"); const wawoff = require("wawoff2");
const { Font } = require("fonteditor-core"); const { Font } = require("fonteditor-core");
/**
* Custom esbuild plugin to convert url woff2 imports into a text.
* Other woff2 imports are handled by a "file" loader.
*
* @returns {import("esbuild").Plugin}
*/
module.exports.woff2BrowserPlugin = () => {
return {
name: "woff2BrowserPlugin",
setup(build) {
build.initialOptions.loader = {
".woff2": "file",
...build.initialOptions.loader,
};
build.onResolve({ filter: /^https:\/\/.+?\.woff2$/ }, (args) => {
return {
path: args.path,
namespace: "woff2BrowserPlugin",
};
});
build.onLoad(
{ filter: /.*/, namespace: "woff2BrowserPlugin" },
async (args) => {
return {
contents: args.path,
loader: "text",
};
},
);
},
};
};
/** /**
* Custom esbuild plugin to: * Custom esbuild plugin to:
* 1. inline all woff2 (url and relative imports) as base64 for server-side use cases (no need for additional font fetch; works in both esm and commonjs) * 1. inline all woff2 (url and relative imports) as base64 for server-side use cases (no need for additional font fetch; works in both esm and commonjs)
@ -53,27 +17,6 @@ module.exports.woff2BrowserPlugin = () => {
* @returns {import("esbuild").Plugin} * @returns {import("esbuild").Plugin}
*/ */
module.exports.woff2ServerPlugin = (options = {}) => { module.exports.woff2ServerPlugin = (options = {}) => {
// google CDN fails time to time, so let's retry
async function fetchRetry(url, options = {}, retries = 0, delay = 1000) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`Status: ${response.status}, ${await response.json()}`);
}
return response;
} catch (e) {
if (retries > 0) {
await new Promise((resolve) => setTimeout(resolve, delay));
return fetchRetry(url, options, retries - 1, delay * 2);
}
console.error(`Couldn't fetch: ${url}, error: ${e.message}`);
throw e;
}
}
return { return {
name: "woff2ServerPlugin", name: "woff2ServerPlugin",
setup(build) { setup(build) {
@ -82,9 +25,7 @@ module.exports.woff2ServerPlugin = (options = {}) => {
const fonts = new Map(); const fonts = new Map();
build.onResolve({ filter: /\.woff2$/ }, (args) => { build.onResolve({ filter: /\.woff2$/ }, (args) => {
const resolvedPath = args.path.startsWith("http") const resolvedPath = path.resolve(args.resolveDir, args.path);
? args.path // url
: path.resolve(args.resolveDir, args.path); // absolute path
return { return {
path: resolvedPath, path: resolvedPath,
@ -101,9 +42,7 @@ module.exports.woff2ServerPlugin = (options = {}) => {
// read local woff2 as a buffer (WARN: `readFileSync` does not work!) // read local woff2 as a buffer (WARN: `readFileSync` does not work!)
woff2Buffer = await fs.promises.readFile(args.path); woff2Buffer = await fs.promises.readFile(args.path);
} else { } else {
// fetch remote woff2 as a buffer (i.e. from a cdn) throw new Error(`Font path has to be absolute! "${args.path}"`);
const response = await fetchRetry(args.path, {}, 3);
woff2Buffer = await response.buffer();
} }
// google's brotli decompression into snft // google's brotli decompression into snft

View file

@ -1,15 +1,13 @@
// `EXCALIDRAW_ASSET_PATH` as a SSOT
const OSS_FONTS_CDN = const OSS_FONTS_CDN =
"https://excalidraw.nyc3.cdn.digitaloceanspaces.com/fonts/oss/"; "https://excalidraw.nyc3.cdn.digitaloceanspaces.com/fonts/oss/";
/** /**
* Custom vite plugin to convert url woff2 imports into a text. * Custom vite plugin for auto-prefixing `EXCALIDRAW_ASSET_PATH` woff2 fonts in `excalidraw-app`.
* Other woff2 imports are automatically served and resolved as a file uri.
* *
* @returns {import("vite").PluginOption} * @returns {import("vite").PluginOption}
*/ */
module.exports.woff2BrowserPlugin = () => { module.exports.woff2BrowserPlugin = () => {
// for now limited to woff2 only, might be extended to any assets in the future
const regex = /^https:\/\/.+?\.woff2$/;
let isDev; let isDev;
return { return {
@ -18,34 +16,9 @@ module.exports.woff2BrowserPlugin = () => {
config(_, { command }) { config(_, { command }) {
isDev = command === "serve"; isDev = command === "serve";
}, },
resolveId(source) {
if (!regex.test(source)) {
return null;
}
// getting the url to the dependency tree
return source;
},
load(id) {
if (!regex.test(id)) {
return null;
}
// loading the url as string
return `export default "${id}"`;
},
// necessary for dev as vite / rollup does skips https imports in serve (~dev) mode
// aka dev mode equivalent of "export default x" above (resolveId + load)
transform(code, id) { transform(code, id) {
// treat https woff2 imports as a text // using copy / replace as fonts defined in the `.css` don't have to be manually copied over (vite/rollup does this automatically),
if (isDev && id.endsWith("/excalidraw/fonts/index.ts")) { // but at the same time can't be easily prefixed with the `EXCALIDRAW_ASSET_PATH` only for the `excalidraw-app`
return code.replaceAll(
/import\s+(\w+)\s+from\s+(["']https:\/\/.+?\.woff2["'])/g,
`const $1 = $2`,
);
}
// use CDN for Assistant
if (!isDev && id.endsWith("/excalidraw/fonts/assets/fonts.css")) { if (!isDev && id.endsWith("/excalidraw/fonts/assets/fonts.css")) {
return `/* WARN: The following content is generated during excalidraw-app build */ return `/* WARN: The following content is generated during excalidraw-app build */
@ -90,7 +63,6 @@ module.exports.woff2BrowserPlugin = () => {
}`; }`;
} }
// using EXCALIDRAW_ASSET_PATH as a SSOT
if (!isDev && id.endsWith("excalidraw-app/index.html")) { if (!isDev && id.endsWith("excalidraw-app/index.html")) {
return code.replace( return code.replace(
"<!-- PLACEHOLDER:EXCALIDRAW_APP_FONTS -->", "<!-- PLACEHOLDER:EXCALIDRAW_APP_FONTS -->",
@ -110,9 +82,10 @@ module.exports.woff2BrowserPlugin = () => {
type="font/woff2" type="font/woff2"
crossorigin="anonymous" crossorigin="anonymous"
/> />
<!-- For Nunito only preload the latin range, which should be good enough for now -->
<link <link
rel="preload" rel="preload"
href="${OSS_FONTS_CDN}Virgil-Regular-hO16qHwV.woff2" href="${OSS_FONTS_CDN}Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg-DqUjjPte.woff2"
as="font" as="font"
type="font/woff2" type="font/woff2"
crossorigin="anonymous" crossorigin="anonymous"
@ -124,6 +97,13 @@ module.exports.woff2BrowserPlugin = () => {
type="font/woff2" type="font/woff2"
crossorigin="anonymous" crossorigin="anonymous"
/> />
<link
rel="preload"
href="${OSS_FONTS_CDN}Virgil-Regular-hO16qHwV.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
`, `,
); );
} }

View file

@ -1,14 +1,12 @@
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
import { woff2BrowserPlugin } from "./scripts/woff2/woff2-vite-plugins";
export default defineConfig({ export default defineConfig({
//@ts-ignore //@ts-ignore
plugins: [woff2BrowserPlugin()],
test: { test: {
// Since hooks are running in stack in v2, which means all hooks run serially whereas // Since hooks are running in stack in v2, which means all hooks run serially whereas
// we need to run them in parallel // we need to run them in parallel
sequence: { sequence: {
hooks: 'parallel', hooks: "parallel",
}, },
setupFiles: ["./setupTests.ts"], setupFiles: ["./setupTests.ts"],
globals: true, globals: true,
@ -19,10 +17,10 @@ export default defineConfig({
// Additionally the thresholds also needs to be updated slightly as a result of this change // Additionally the thresholds also needs to be updated slightly as a result of this change
ignoreEmptyLines: false, ignoreEmptyLines: false,
thresholds: { thresholds: {
lines: 66, lines: 60,
branches: 70, branches: 70,
functions: 63, functions: 63,
statements: 66, statements: 60,
}, },
}, },
}, },

308
yarn.lock
View file

@ -1519,11 +1519,6 @@
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== 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": "@esbuild/android-arm64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz#ef31015416dd79398082409b77aaaa2ade4d531a" 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" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== 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": "@esbuild/android-arm@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.10.tgz#1c23c7e75473aae9fb323be5d9db225142f47f52" 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" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== 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": "@esbuild/android-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.10.tgz#df6a4e6d6eb8da5595cfce16d4e3f6bc24464707" 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" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== 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": "@esbuild/darwin-arm64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz#8462a55db07c1b2fad61c8244ce04469ef1043be" 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" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== 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": "@esbuild/darwin-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz#d1de20bfd41bb75b955ba86a6b1004539e8218c1" 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" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== 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": "@esbuild/freebsd-arm64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz#16904879e34c53a2e039d1284695d2db3e664d57" 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" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== 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": "@esbuild/freebsd-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz#8ad9e5ca9786ca3f1ef1411bfd10b08dcd9d4cef" 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" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== 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": "@esbuild/linux-arm64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz#d82cf2c590faece82d28bbf1cfbe36f22ae25bd2" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== 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": "@esbuild/linux-arm@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz#477b8e7c7bcd34369717b04dd9ee6972c84f4029" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== 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": "@esbuild/linux-ia32@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz#d55ff822cf5b0252a57112f86857ff23be6cab0e" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== 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": "@esbuild/linux-loong64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz#a9ad057d7e48d6c9f62ff50f6f208e331c4543c7" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== 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": "@esbuild/linux-mips64el@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz#b011a96924773d60ebab396fbd7a08de66668179" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== 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": "@esbuild/linux-ppc64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz#5d8b59929c029811e473f2544790ea11d588d4dd" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== 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": "@esbuild/linux-riscv64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz#292b06978375b271bd8bc0a554e0822957508d22" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== 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": "@esbuild/linux-s390x@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz#d30af63530f8d4fa96930374c9dd0d62bf59e069" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== 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": "@esbuild/linux-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz#898c72eeb74d9f2fb43acf316125b475548b75ce" 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" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== 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": "@esbuild/netbsd-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz#fd473a5ae261b43eab6dad4dbd5a3155906e6c91" 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" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== 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": "@esbuild/openbsd-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz#96eb8992e526717b5272321eaad3e21f3a608e46" 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" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== 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": "@esbuild/sunos-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz#c16ee1c167f903eaaa6acf7372bee42d5a89c9bc" 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" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== 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": "@esbuild/win32-arm64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz#7e417d1971dbc7e469b4eceb6a5d1d667b5e3dcc" 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" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== 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": "@esbuild/win32-ia32@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz#2b52dfec6cd061ecb36171c13bae554888b439e5" 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" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== 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": "@esbuild/win32-x64@0.19.10":
version "0.19.10" version "0.19.10"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz#bd123a74f243d2f3a1f046447bb9b363ee25d072" 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" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== 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": "@eslint-community/eslint-utils@^4.2.0":
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" 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" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27"
integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== 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": "@rollup/rollup-android-arm64@4.18.0":
version "4.18.0" version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203"
integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== 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": "@rollup/rollup-darwin-arm64@4.18.0":
version "4.18.0" version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096"
integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== 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": "@rollup/rollup-darwin-x64@4.18.0":
version "4.18.0" version "4.18.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c"
integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== 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": "@rollup/rollup-linux-arm-gnueabihf@4.18.0":
version "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" 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== 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": "@rollup/rollup-linux-arm-musleabihf@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549"
integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== 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": "@rollup/rollup-linux-arm64-gnu@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577"
integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== 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": "@rollup/rollup-linux-arm64-musl@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c"
integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== 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": "@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
version "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" 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== 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": "@rollup/rollup-linux-riscv64-gnu@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9"
integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== 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": "@rollup/rollup-linux-s390x-gnu@4.18.0":
version "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" 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== 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": "@rollup/rollup-linux-x64-gnu@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942"
integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== 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": "@rollup/rollup-linux-x64-musl@4.18.0":
version "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" 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== 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": "@rollup/rollup-win32-arm64-msvc@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf"
integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== 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": "@rollup/rollup-win32-ia32-msvc@4.18.0":
version "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" 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== 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": "@rollup/rollup-win32-x64-msvc@4.18.0":
version "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" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4"
integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== 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": "@rushstack/eslint-patch@^1.1.0":
version "1.10.3" version "1.10.3"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20"
@ -5389,11 +5194,6 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
data-urls@^4.0.0: data-urls@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4"
@ -5999,35 +5799,6 @@ esbuild@^0.20.1:
"@esbuild/win32-ia32" "0.20.2" "@esbuild/win32-ia32" "0.20.2"
"@esbuild/win32-x64" "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: escalade@^3.1.1, escalade@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
@ -6486,14 +6257,6 @@ fd-slicer@~1.1.0:
dependencies: dependencies:
pend "~1.2.0" pend "~1.2.0"
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
fflate@^0.8.2: fflate@^0.8.2:
version "0.8.2" version "0.8.2"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
@ -6632,13 +6395,6 @@ form-data@^4.0.0:
combined-stream "^1.0.8" combined-stream "^1.0.8"
mime-types "^2.1.12" mime-types "^2.1.12"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
fraction.js@^4.2.0: fraction.js@^4.2.0:
version "4.3.7" version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
@ -8480,11 +8236,6 @@ no-case@^3.0.4:
lower-case "^2.0.2" lower-case "^2.0.2"
tslib "^2.0.3" tslib "^2.0.3"
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@2.6.1: node-fetch@2.6.1:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
@ -8497,15 +8248,6 @@ node-fetch@2.6.7:
dependencies: dependencies:
whatwg-url "^5.0.0" whatwg-url "^5.0.0"
node-fetch@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b"
integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
node-html-parser@^5.3.3: node-html-parser@^5.3.3:
version "5.4.2" version "5.4.2"
resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a" resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a"
@ -8991,15 +8733,6 @@ postcss@^8.4.32, postcss@^8.4.38, postcss@^8.4.7:
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.2.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: prelude-ls@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@ -9475,31 +9208,6 @@ rollup@^4.13.0, rollup@^4.2.0:
"@rollup/rollup-win32-x64-msvc" "4.18.0" "@rollup/rollup-win32-x64-msvc" "4.18.0"
fsevents "~2.3.2" 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: roughjs@4.6.4:
version "4.6.4" version "4.6.4"
resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.4.tgz#b6f39b44645854a6e0a4a28b078368701eb7f939" resolved "https://registry.yarnpkg.com/roughjs/-/roughjs-4.6.4.tgz#b6f39b44645854a6e0a4a28b078368701eb7f939"
@ -10695,17 +10403,6 @@ vite@5.0.12:
optionalDependencies: optionalDependencies:
fsevents "~2.3.3" 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: vite@^5.0.0:
version "5.2.11" version "5.2.11"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.11.tgz#726ec05555431735853417c3c0bfb36003ca0cbd" resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.11.tgz#726ec05555431735853417c3c0bfb36003ca0cbd"
@ -10815,11 +10512,6 @@ wawoff2@2.0.1:
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
web-streams-polyfill@^3.0.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
web-worker@^1.2.0: web-worker@^1.2.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.3.0.tgz#e5f2df5c7fe356755a5fb8f8410d4312627e6776" resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.3.0.tgz#e5f2df5c7fe356755a5fb8f8410d4312627e6776"