Compare commits

..

1 Commits

Author SHA1 Message Date
5639bb8e87 Updates to point to excalidraw-storage-backend 2025-05-25 22:22:50 -04:00
14 changed files with 27 additions and 184 deletions

View File

@ -1,5 +1,3 @@
MODE="development"
VITE_APP_BACKEND_V2_GET_URL=https://json-dev.excalidraw.com/api/v2/ VITE_APP_BACKEND_V2_GET_URL=https://json-dev.excalidraw.com/api/v2/
VITE_APP_BACKEND_V2_POST_URL=https://json-dev.excalidraw.com/api/v2/post/ VITE_APP_BACKEND_V2_POST_URL=https://json-dev.excalidraw.com/api/v2/post/

View File

@ -1,7 +1,5 @@
MODE="production" VITE_APP_BACKEND_V2_GET_URL=https://ex.dylanbanta.com/api/v2/scenes/
VITE_APP_BACKEND_V2_POST_URL=https://ex.dylanbanta.com/api/v2/scenes/
VITE_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
VITE_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
VITE_APP_LIBRARY_URL=https://libraries.excalidraw.com VITE_APP_LIBRARY_URL=https://libraries.excalidraw.com
VITE_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries VITE_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries

View File

@ -34,9 +34,6 @@
<a href="https://discord.gg/UexuTaE"> <a href="https://discord.gg/UexuTaE">
<img alt="Chat on Discord" src="https://img.shields.io/discord/723672430744174682?color=738ad6&label=Chat%20on%20Discord&logo=discord&logoColor=ffffff&widge=false"/> <img alt="Chat on Discord" src="https://img.shields.io/discord/723672430744174682?color=738ad6&label=Chat%20on%20Discord&logo=discord&logoColor=ffffff&widge=false"/>
</a> </a>
<a href="https://deepwiki.com/excalidraw/excalidraw">
<img alt="Ask DeepWiki" src="https://deepwiki.com/badge.svg" />
</a>
<a href="https://twitter.com/excalidraw"> <a href="https://twitter.com/excalidraw">
<img alt="Follow Excalidraw on Twitter" src="https://img.shields.io/twitter/follow/excalidraw.svg?label=follow+@excalidraw&style=social&logo=twitter"/> <img alt="Follow Excalidraw on Twitter" src="https://img.shields.io/twitter/follow/excalidraw.svg?label=follow+@excalidraw&style=social&logo=twitter"/>
</a> </a>

View File

@ -19,7 +19,7 @@ services:
- ./:/opt/node_app/app:delegated - ./:/opt/node_app/app:delegated
- ./package.json:/opt/node_app/package.json - ./package.json:/opt/node_app/package.json
- ./yarn.lock:/opt/node_app/yarn.lock - ./yarn.lock:/opt/node_app/yarn.lock
- notused:/opt/node_app/app/node_modules # - notused:/opt/node_app/app/node_modules
volumes: # volumes:
notused: # notused:

View File

@ -926,16 +926,21 @@ const ExcalidrawWrapper = () => {
<ShareDialog <ShareDialog
collabAPI={collabAPI} collabAPI={collabAPI}
onExportToBackend={async () => { onExportToBackend={async () => {
if (excalidrawAPI) { if (!excalidrawAPI) {
try { return;
await onExportToBackend( }
excalidrawAPI.getSceneElements(), try {
excalidrawAPI.getAppState(), const { url, errorMessage } = await exportToBackend(
excalidrawAPI.getFiles(), excalidrawAPI.getSceneElements(),
); excalidrawAPI.getAppState(),
} catch (error: any) { excalidrawAPI.getFiles(),
setErrorMessage(error.message); );
if (errorMessage) {
throw new Error(errorMessage);
} }
setLatestShareableLink(url);
} catch (error: any) {
setErrorMessage(error.message);
} }
}} }}
/> />

View File

@ -41,8 +41,8 @@
"prettier": "@excalidraw/prettier-config", "prettier": "@excalidraw/prettier-config",
"scripts": { "scripts": {
"build-node": "node ./scripts/build-node.js", "build-node": "node ./scripts/build-node.js",
"build:app:docker": "cross-env VITE_APP_DISABLE_SENTRY=true vite build", "build:app:docker": "vite build",
"build:app": "cross-env VITE_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA cross-env VITE_APP_ENABLE_TRACKING=true vite build", "build:app": "vite build",
"build:version": "node ../scripts/build-version.js", "build:version": "node ../scripts/build-version.js",
"build": "yarn build:app && yarn build:version", "build": "yarn build:app && yarn build:version",
"start": "yarn && vite", "start": "yarn && vite",

View File

@ -974,25 +974,6 @@ export const updateElbowArrowPoints = (
), ),
"Elbow arrow segments must be either horizontal or vertical", "Elbow arrow segments must be either horizontal or vertical",
); );
invariant(
updates.fixedSegments?.find(
(segment) =>
segment.index === 1 &&
pointsEqual(segment.start, (updates.points ?? arrow.points)[0]),
) == null &&
updates.fixedSegments?.find(
(segment) =>
segment.index === (updates.points ?? arrow.points).length - 1 &&
pointsEqual(
segment.end,
(updates.points ?? arrow.points)[
(updates.points ?? arrow.points).length - 1
],
),
) == null,
"The first and last segments cannot be fixed",
);
} }
const fixedSegments = updates.fixedSegments ?? arrow.fixedSegments ?? []; const fixedSegments = updates.fixedSegments ?? arrow.fixedSegments ?? [];

View File

@ -117,7 +117,6 @@ export class LinearElementEditor {
public readonly hoverPointIndex: number; public readonly hoverPointIndex: number;
public readonly segmentMidPointHoveredCoords: GlobalPoint | null; public readonly segmentMidPointHoveredCoords: GlobalPoint | null;
public readonly elbowed: boolean; public readonly elbowed: boolean;
public readonly customLineAngle: number | null;
constructor( constructor(
element: NonDeleted<ExcalidrawLinearElement>, element: NonDeleted<ExcalidrawLinearElement>,
@ -151,7 +150,6 @@ export class LinearElementEditor {
this.hoverPointIndex = -1; this.hoverPointIndex = -1;
this.segmentMidPointHoveredCoords = null; this.segmentMidPointHoveredCoords = null;
this.elbowed = isElbowArrow(element) && element.elbowed; this.elbowed = isElbowArrow(element) && element.elbowed;
this.customLineAngle = null;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -255,7 +253,6 @@ export class LinearElementEditor {
const { elementId } = linearElementEditor; const { elementId } = linearElementEditor;
const elementsMap = scene.getNonDeletedElementsMap(); const elementsMap = scene.getNonDeletedElementsMap();
const element = LinearElementEditor.getElement(elementId, elementsMap); const element = LinearElementEditor.getElement(elementId, elementsMap);
let customLineAngle = linearElementEditor.customLineAngle;
if (!element) { if (!element) {
return null; return null;
} }
@ -296,12 +293,6 @@ export class LinearElementEditor {
const selectedIndex = selectedPointsIndices[0]; const selectedIndex = selectedPointsIndices[0];
const referencePoint = const referencePoint =
element.points[selectedIndex === 0 ? 1 : selectedIndex - 1]; element.points[selectedIndex === 0 ? 1 : selectedIndex - 1];
customLineAngle =
linearElementEditor.customLineAngle ??
Math.atan2(
element.points[selectedIndex][1] - referencePoint[1],
element.points[selectedIndex][0] - referencePoint[0],
);
const [width, height] = LinearElementEditor._getShiftLockedDelta( const [width, height] = LinearElementEditor._getShiftLockedDelta(
element, element,
@ -309,7 +300,6 @@ export class LinearElementEditor {
referencePoint, referencePoint,
pointFrom(scenePointerX, scenePointerY), pointFrom(scenePointerX, scenePointerY),
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(), event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
customLineAngle,
); );
LinearElementEditor.movePoints( LinearElementEditor.movePoints(
@ -431,7 +421,6 @@ export class LinearElementEditor {
? lastClickedPoint ? lastClickedPoint
: -1, : -1,
isDragging: true, isDragging: true,
customLineAngle,
}; };
} }
@ -535,7 +524,6 @@ export class LinearElementEditor {
: selectedPointsIndices, : selectedPointsIndices,
isDragging: false, isDragging: false,
pointerOffset: { x: 0, y: 0 }, pointerOffset: { x: 0, y: 0 },
customLineAngle: null,
}; };
} }
@ -1543,7 +1531,6 @@ export class LinearElementEditor {
referencePoint: LocalPoint, referencePoint: LocalPoint,
scenePointer: GlobalPoint, scenePointer: GlobalPoint,
gridSize: NullableGridSize, gridSize: NullableGridSize,
customLineAngle?: number,
) { ) {
const referencePointCoords = LinearElementEditor.getPointGlobalCoordinates( const referencePointCoords = LinearElementEditor.getPointGlobalCoordinates(
element, element,
@ -1569,7 +1556,6 @@ export class LinearElementEditor {
referencePointCoords[1], referencePointCoords[1],
gridX, gridX,
gridY, gridY,
customLineAngle,
); );
return pointRotateRads( return pointRotateRads(

View File

@ -2,12 +2,6 @@ import {
SHIFT_LOCKING_ANGLE, SHIFT_LOCKING_ANGLE,
viewportCoordsToSceneCoords, viewportCoordsToSceneCoords,
} from "@excalidraw/common"; } from "@excalidraw/common";
import {
normalizeRadians,
radiansBetweenAngles,
radiansDifference,
type Radians,
} from "@excalidraw/math";
import type { AppState, Offsets, Zoom } from "@excalidraw/excalidraw/types"; import type { AppState, Offsets, Zoom } from "@excalidraw/excalidraw/types";
@ -140,42 +134,13 @@ export const getLockedLinearCursorAlignSize = (
originY: number, originY: number,
x: number, x: number,
y: number, y: number,
customAngle?: number,
) => { ) => {
let width = x - originX; let width = x - originX;
let height = y - originY; let height = y - originY;
const angle = Math.atan2(height, width) as Radians; const lockedAngle =
let lockedAngle = (Math.round(angle / SHIFT_LOCKING_ANGLE) * Math.round(Math.atan(height / width) / SHIFT_LOCKING_ANGLE) *
SHIFT_LOCKING_ANGLE) as Radians; SHIFT_LOCKING_ANGLE;
if (customAngle) {
// If custom angle is provided, we check if the angle is close to the
// custom angle, snap to that if close engough, otherwise snap to the
// higher or lower angle depending on the current angle vs custom angle.
const lower = (Math.floor(customAngle / SHIFT_LOCKING_ANGLE) *
SHIFT_LOCKING_ANGLE) as Radians;
if (
radiansBetweenAngles(
angle,
lower,
(lower + SHIFT_LOCKING_ANGLE) as Radians,
)
) {
if (
radiansDifference(angle, customAngle as Radians) <
SHIFT_LOCKING_ANGLE / 6
) {
lockedAngle = customAngle as Radians;
} else if (
normalizeRadians(angle) > normalizeRadians(customAngle as Radians)
) {
lockedAngle = (lower + SHIFT_LOCKING_ANGLE) as Radians;
} else {
lockedAngle = lower;
}
}
}
if (lockedAngle === 0) { if (lockedAngle === 0) {
height = 0; height = 0;

View File

@ -1411,55 +1411,5 @@ describe("Test Linear Elements", () => {
expect(line.points[line.points.length - 1][0]).toBe(20); expect(line.points[line.points.length - 1][0]).toBe(20);
expect(line.points[line.points.length - 1][1]).toBe(-20); expect(line.points[line.points.length - 1][1]).toBe(-20);
}); });
it("should preserve original angle when dragging endpoint with SHIFT key", () => {
createTwoPointerLinearElement("line");
const line = h.elements[0] as ExcalidrawLinearElement;
enterLineEditingMode(line);
const elementsMap = arrayToMap(h.elements);
const points = LinearElementEditor.getPointsGlobalCoordinates(
line,
elementsMap,
);
// Calculate original angle between first and last point
const originalAngle = Math.atan2(
points[1][1] - points[0][1],
points[1][0] - points[0][0],
);
// Drag the second point (endpoint) with SHIFT key pressed
const startPoint = pointFrom<GlobalPoint>(points[1][0], points[1][1]);
const endPoint = pointFrom<GlobalPoint>(
startPoint[0] + 4,
startPoint[1] + 4,
);
// Perform drag with SHIFT key modifier
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.downAt(startPoint[0], startPoint[1]);
mouse.moveTo(endPoint[0], endPoint[1]);
mouse.upAt(endPoint[0], endPoint[1]);
});
// Get updated points after drag
const updatedPoints = LinearElementEditor.getPointsGlobalCoordinates(
line,
elementsMap,
);
// Calculate new angle
const newAngle = Math.atan2(
updatedPoints[1][1] - updatedPoints[0][1],
updatedPoints[1][0] - updatedPoints[0][0],
);
// The angle should be preserved (within a small tolerance for floating point precision)
const angleDifference = Math.abs(newAngle - originalAngle);
const tolerance = 0.01; // Small tolerance for floating point precision
expect(angleDifference).toBeLessThan(tolerance);
});
}); });
}); });

View File

@ -1483,13 +1483,13 @@ const getArrowheadOptions = (flip: boolean) => {
value: "crowfoot_one", value: "crowfoot_one",
text: t("labels.arrowhead_crowfoot_one"), text: t("labels.arrowhead_crowfoot_one"),
icon: <ArrowheadCrowfootOneIcon flip={flip} />, icon: <ArrowheadCrowfootOneIcon flip={flip} />,
keyBinding: "x", keyBinding: "c",
}, },
{ {
value: "crowfoot_many", value: "crowfoot_many",
text: t("labels.arrowhead_crowfoot_many"), text: t("labels.arrowhead_crowfoot_many"),
icon: <ArrowheadCrowfootIcon flip={flip} />, icon: <ArrowheadCrowfootIcon flip={flip} />,
keyBinding: "c", keyBinding: "x",
}, },
{ {
value: "crowfoot_one_or_many", value: "crowfoot_one_or_many",

View File

@ -564,7 +564,7 @@ export const convertElementTypes = (
continue; continue;
} }
const fixedSegments: FixedSegment[] = []; const fixedSegments: FixedSegment[] = [];
for (let i = 1; i < nextPoints.length - 2; i++) { for (let i = 0; i < nextPoints.length - 1; i++) {
fixedSegments.push({ fixedSegments.push({
start: nextPoints[i], start: nextPoints[i],
end: nextPoints[i + 1], end: nextPoints[i + 1],
@ -581,7 +581,6 @@ export const convertElementTypes = (
); );
mutateElement(element, app.scene.getNonDeletedElementsMap(), { mutateElement(element, app.scene.getNonDeletedElementsMap(), {
...updates, ...updates,
endArrowhead: "arrow",
}); });
} else { } else {
// if we're converting to non-elbow linear element, check if // if we're converting to non-elbow linear element, check if

View File

@ -8628,7 +8628,6 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1`
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"elbowed": false, "elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
@ -8852,7 +8851,6 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`]
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"elbowed": false, "elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
@ -9269,7 +9267,6 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1`
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"elbowed": false, "elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",
@ -9673,7 +9670,6 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`]
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
"selectedLinearElement": LinearElementEditor { "selectedLinearElement": LinearElementEditor {
"customLineAngle": null,
"elbowed": false, "elbowed": false,
"elementId": "id0", "elementId": "id0",
"endBindingElement": "keep", "endBindingElement": "keep",

View File

@ -49,35 +49,3 @@ export function radiansToDegrees(degrees: Radians): Degrees {
export function isRightAngleRads(rads: Radians): boolean { export function isRightAngleRads(rads: Radians): boolean {
return Math.abs(Math.sin(2 * rads)) < PRECISION; return Math.abs(Math.sin(2 * rads)) < PRECISION;
} }
export function radiansBetweenAngles(
a: Radians,
min: Radians,
max: Radians,
): boolean {
a = normalizeRadians(a);
min = normalizeRadians(min);
max = normalizeRadians(max);
if (min < max) {
return a >= min && a <= max;
}
// The range wraps around the 0 angle
return a >= min || a <= max;
}
export function radiansDifference(a: Radians, b: Radians): Radians {
a = normalizeRadians(a);
b = normalizeRadians(b);
let diff = a - b;
if (diff < -Math.PI) {
diff = (diff + 2 * Math.PI) as Radians;
} else if (diff > Math.PI) {
diff = (diff - 2 * Math.PI) as Radians;
}
return Math.abs(diff) as Radians;
}