diff --git a/excalidraw-app/tests/collab.test.tsx b/excalidraw-app/tests/collab.test.tsx index ed19c96e8a..4998c007aa 100644 --- a/excalidraw-app/tests/collab.test.tsx +++ b/excalidraw-app/tests/collab.test.tsx @@ -205,6 +205,7 @@ describe("collaboration", () => { // with explicit undo (as addition) we expect our item to be restored from the snapshot! await waitFor(() => { expect(API.getUndoStack().length).toBe(1); + expect(API.getRedoStack().length).toBe(1); expect(API.getSnapshot()).toEqual([ expect.objectContaining(rect1Props), expect.objectContaining({ ...rect2Props, isDeleted: false }), @@ -247,79 +248,5 @@ describe("collaboration", () => { expect.objectContaining({ ...rect2Props, isDeleted: true }), ]); }); - - act(() => h.app.actionManager.executeAction(undoAction)); - - // simulate local update - API.updateScene({ - elements: syncInvalidIndices([ - h.elements[0], - newElementWith(h.elements[1], { x: 100 }), - ]), - captureUpdate: CaptureUpdateAction.IMMEDIATELY, - }); - - await waitFor(() => { - expect(API.getUndoStack().length).toBe(2); - expect(API.getRedoStack().length).toBe(0); - expect(API.getSnapshot()).toEqual([ - expect.objectContaining(rect1Props), - expect.objectContaining({ ...rect2Props, isDeleted: false, x: 100 }), - ]); - expect(h.elements).toEqual([ - expect.objectContaining(rect1Props), - expect.objectContaining({ ...rect2Props, isDeleted: false, x: 100 }), - ]); - }); - - act(() => h.app.actionManager.executeAction(undoAction)); - - // we expect to iterate the stack to the first visible change - await waitFor(() => { - expect(API.getUndoStack().length).toBe(1); - expect(API.getRedoStack().length).toBe(1); - expect(API.getSnapshot()).toEqual([ - expect.objectContaining(rect1Props), - expect.objectContaining({ ...rect2Props, isDeleted: false, x: 0 }), - ]); - expect(h.elements).toEqual([ - expect.objectContaining(rect1Props), - expect.objectContaining({ ...rect2Props, isDeleted: false, x: 0 }), - ]); - }); - - // simulate force deleting the element remotely - API.updateScene({ - elements: syncInvalidIndices([rect1]), - captureUpdate: CaptureUpdateAction.NEVER, - }); - - // snapshot was correctly updated and marked the element as deleted - await waitFor(() => { - expect(API.getUndoStack().length).toBe(1); - expect(API.getRedoStack().length).toBe(1); - expect(API.getSnapshot()).toEqual([ - expect.objectContaining(rect1Props), - expect.objectContaining({ ...rect2Props, isDeleted: true, x: 0 }), - ]); - expect(h.elements).toEqual([expect.objectContaining(rect1Props)]); - }); - - act(() => h.app.actionManager.executeAction(redoAction)); - - // with explicit redo (as update) we again restored the element from the snapshot! - await waitFor(() => { - expect(API.getUndoStack().length).toBe(2); - expect(API.getRedoStack().length).toBe(0); - expect(API.getSnapshot()).toEqual([ - expect.objectContaining({ id: "A", isDeleted: false }), - expect.objectContaining({ id: "B", isDeleted: true, x: 100 }), - ]); - expect(h.history.isRedoStackEmpty).toBeTruthy(); - expect(h.elements).toEqual([ - expect.objectContaining({ id: "A", isDeleted: false }), - expect.objectContaining({ id: "B", isDeleted: true, x: 100 }), - ]); - }); }); }); diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 39e2e9149c..824e88b636 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -714,8 +714,8 @@ export const arrayToObject = ( array: readonly T[], groupBy?: (value: T) => string | number, ) => - array.reduce((acc, value) => { - acc[groupBy ? groupBy(value) : String(value)] = value; + array.reduce((acc, value, idx) => { + acc[groupBy ? groupBy(value) : idx] = value; return acc; }, {} as { [key: string]: T }); diff --git a/packages/element/src/delta.ts b/packages/element/src/delta.ts index 45b1fc3487..17d9f49ee5 100644 --- a/packages/element/src/delta.ts +++ b/packages/element/src/delta.ts @@ -5,11 +5,12 @@ import { isDevEnv, isShallowEqual, isTestEnv, + randomInteger, } from "@excalidraw/common"; import type { ExcalidrawElement, - ExcalidrawImageElement, + ExcalidrawFreeDrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, NonDeleted, @@ -18,7 +19,12 @@ import type { SceneElementsMap, } from "@excalidraw/element/types"; -import type { DTO, SubtypeOf, ValueOf } from "@excalidraw/common/utility-types"; +import type { + DTO, + Mutable, + SubtypeOf, + ValueOf, +} from "@excalidraw/common/utility-types"; import type { AppState, @@ -51,6 +57,8 @@ import { orderByFractionalIndex, syncMovedIndices } from "./fractionalIndex"; import { Scene } from "./Scene"; +import { StoreSnapshot } from "./store"; + import type { BindableProp, BindingProp } from "./binding"; import type { ElementUpdate } from "./mutateElement"; @@ -73,13 +81,20 @@ export class Delta { public static create( deleted: Partial, inserted: Partial, - modifier?: (delta: Partial) => Partial, - modifierOptions?: "deleted" | "inserted", + modifier?: ( + delta: Partial, + partialType: "deleted" | "inserted", + ) => Partial, + modifierOptions?: "deleted" | "inserted" | "both", ) { const modifiedDeleted = - modifier && modifierOptions !== "inserted" ? modifier(deleted) : deleted; + modifier && modifierOptions !== "inserted" + ? modifier(deleted, "deleted") + : deleted; const modifiedInserted = - modifier && modifierOptions !== "deleted" ? modifier(inserted) : inserted; + modifier && modifierOptions !== "deleted" + ? modifier(inserted, "inserted") + : inserted; return new Delta(modifiedDeleted, modifiedInserted); } @@ -113,11 +128,7 @@ export class Delta { // - we do this only on previously detected changed elements // - we do shallow compare only on the first level of properties (not going any deeper) // - # of properties is reasonably small - for (const key of this.distinctKeysIterator( - "full", - prevObject, - nextObject, - )) { + for (const key of this.getDifferences(prevObject, nextObject)) { deleted[key as keyof T] = prevObject[key]; inserted[key as keyof T] = nextObject[key]; } @@ -256,12 +267,14 @@ export class Delta { arrayToObject(deletedArray, groupBy), arrayToObject(insertedArray, groupBy), ), + (x) => x, ); const insertedDifferences = arrayToObject( Delta.getRightDifferences( arrayToObject(deletedArray, groupBy), arrayToObject(insertedArray, groupBy), ), + (x) => x, ); if ( @@ -320,6 +333,42 @@ export class Delta { return !!anyDistinctKey; } + /** + * Compares if shared properties of object1 and object2 contain any different value (aka inner join). + */ + public static isInnerDifferent( + object1: T, + object2: T, + skipShallowCompare = false, + ): boolean { + const anyDistinctKey = !!this.distinctKeysIterator( + "inner", + object1, + object2, + skipShallowCompare, + ).next().value; + + return !!anyDistinctKey; + } + + /** + * Compares if any properties of object1 and object2 contain any different value (aka full join). + */ + public static isDifferent( + object1: T, + object2: T, + skipShallowCompare = false, + ): boolean { + const anyDistinctKey = !!this.distinctKeysIterator( + "full", + object1, + object2, + skipShallowCompare, + ).next().value; + + return !!anyDistinctKey; + } + /** * Returns sorted object1 keys that have distinct values. */ @@ -346,6 +395,32 @@ export class Delta { ).sort(); } + /** + * Returns sorted keys of shared object1 and object2 properties that have distinct values (aka inner join). + */ + public static getInnerDifferences( + object1: T, + object2: T, + skipShallowCompare = false, + ) { + return Array.from( + this.distinctKeysIterator("inner", object1, object2, skipShallowCompare), + ).sort(); + } + + /** + * Returns sorted keys that have distinct values between object1 and object2 (aka full join). + */ + public static getDifferences( + object1: T, + object2: T, + skipShallowCompare = false, + ) { + return Array.from( + this.distinctKeysIterator("full", object1, object2, skipShallowCompare), + ).sort(); + } + /** * Iterator comparing values of object properties based on the passed joining strategy. * @@ -354,7 +429,7 @@ export class Delta { * WARN: it's based on shallow compare performed only on the first level and doesn't go deeper than that. */ private static *distinctKeysIterator( - join: "left" | "right" | "full", + join: "left" | "right" | "inner" | "full", object1: T, object2: T, skipShallowCompare = false, @@ -369,6 +444,8 @@ export class Delta { keys = Object.keys(object1); } else if (join === "right") { keys = Object.keys(object2); + } else if (join === "inner") { + keys = Object.keys(object1).filter((key) => key in object2); } else if (join === "full") { keys = Array.from( new Set([...Object.keys(object1), ...Object.keys(object2)]), @@ -382,17 +459,17 @@ export class Delta { } for (const key of keys) { - const object1Value = object1[key as keyof T]; - const object2Value = object2[key as keyof T]; + const value1 = object1[key as keyof T]; + const value2 = object2[key as keyof T]; - if (object1Value !== object2Value) { + if (value1 !== value2) { if ( !skipShallowCompare && - typeof object1Value === "object" && - typeof object2Value === "object" && - object1Value !== null && - object2Value !== null && - isShallowEqual(object1Value, object2Value) + typeof value1 === "object" && + typeof value2 === "object" && + value1 !== null && + value2 !== null && + isShallowEqual(value1, value2) ) { continue; } @@ -858,10 +935,17 @@ export class AppStateDelta implements DeltaContainer { } } -type ElementPartial = Omit< - ElementUpdate>, - "seed" ->; +type ElementPartial = + Omit>, "id" | "updated" | "seed">; + +export type ApplyToOptions = { + excludedProperties: Set; +}; + +type ApplyToFlags = { + containsVisibleDifference: boolean; + containsZindexDifference: boolean; +}; /** * Elements change is a low level primitive to capture a change between two sets of elements. @@ -944,13 +1028,33 @@ export class ElementsDelta implements DeltaContainer { inserted, }: Delta) => !!deleted.isDeleted === !!inserted.isDeleted; + private static satisfiesCommmonInvariants = ({ + deleted, + inserted, + }: Delta) => + !!( + deleted.version && + inserted.version && + // versions are required integers + Number.isInteger(deleted.version) && + Number.isInteger(inserted.version) && + // versions should be positive, zero included + deleted.version >= 0 && + inserted.version >= 0 && + // versions should never be the same + deleted.version !== inserted.version + ); + private static validate( elementsDelta: ElementsDelta, type: "added" | "removed" | "updated", - satifies: (delta: Delta) => boolean, + satifiesSpecialInvariants: (delta: Delta) => boolean, ) { for (const [id, delta] of Object.entries(elementsDelta[type])) { - if (!satifies(delta)) { + if ( + !this.satisfiesCommmonInvariants(delta) || + !satifiesSpecialInvariants(delta) + ) { console.error( `Broken invariant for "${type}" delta, element "${id}", delta:`, delta, @@ -986,7 +1090,12 @@ export class ElementsDelta implements DeltaContainer { if (!nextElement) { const deleted = { ...prevElement, isDeleted: false } as ElementPartial; - const inserted = { isDeleted: true } as ElementPartial; + + const inserted = { + isDeleted: true, + version: prevElement.version + 1, + versionNonce: randomInteger(), + } as ElementPartial; const delta = Delta.create( deleted, @@ -1002,7 +1111,12 @@ export class ElementsDelta implements DeltaContainer { const prevElement = prevElements.get(nextElement.id); if (!prevElement) { - const deleted = { isDeleted: true } as ElementPartial; + const deleted = { + isDeleted: true, + version: nextElement.version - 1, + versionNonce: randomInteger(), + } as ElementPartial; + const inserted = { ...nextElement, isDeleted: false, @@ -1087,16 +1201,40 @@ export class ElementsDelta implements DeltaContainer { /** * Update delta/s based on the existing elements. * - * @param elements current elements + * @param nextElements current elements * @param modifierOptions defines which of the delta (`deleted` or `inserted`) will be updated * @returns new instance with modified delta/s */ public applyLatestChanges( - elements: SceneElementsMap, - modifierOptions: "deleted" | "inserted", + prevElements: SceneElementsMap, + nextElements: SceneElementsMap, + modifierOptions?: "deleted" | "inserted", ): ElementsDelta { const modifier = - (element: OrderedExcalidrawElement) => (partial: ElementPartial) => { + ( + prevElement: OrderedExcalidrawElement | undefined, + nextElement: OrderedExcalidrawElement | undefined, + ) => + (partial: ElementPartial, partialType: "deleted" | "inserted") => { + let element: OrderedExcalidrawElement | undefined; + + switch (partialType) { + case "deleted": + element = prevElement; + break; + case "inserted": + element = nextElement; + break; + } + + // the element wasn't found -> don't update the partial + if (!element) { + console.error( + `Element not found when trying to apply latest changes`, + ); + return partial; + } + const latestPartial: { [key: string]: unknown } = {}; for (const key of Object.keys(partial) as Array) { @@ -1120,19 +1258,25 @@ export class ElementsDelta implements DeltaContainer { const modifiedDeltas: Record> = {}; for (const [id, delta] of Object.entries(deltas)) { - const existingElement = elements.get(id); + const prevElement = prevElements.get(id); + const nextElement = nextElements.get(id); - if (existingElement) { - const modifiedDelta = Delta.create( + let latestDelta: Delta | null = null; + + if (prevElement || nextElement) { + latestDelta = Delta.create( delta.deleted, delta.inserted, - modifier(existingElement), + modifier(prevElement, nextElement), modifierOptions, ); - - modifiedDeltas[id] = modifiedDelta; } else { - modifiedDeltas[id] = delta; + latestDelta = delta; + } + + // it might happen that after applying latest changes the delta itself does not contain any changes + if (Delta.isInnerDifferent(latestDelta.deleted, latestDelta.inserted)) { + modifiedDeltas[id] = latestDelta; } } @@ -1150,12 +1294,15 @@ export class ElementsDelta implements DeltaContainer { public applyTo( elements: SceneElementsMap, - elementsSnapshot: Map, + snapshot: StoreSnapshot["elements"] = StoreSnapshot.empty().elements, + options: ApplyToOptions = { + excludedProperties: new Set(), + }, ): [SceneElementsMap, boolean] { let nextElements = new Map(elements) as SceneElementsMap; let changedElements: Map; - const flags = { + const flags: ApplyToFlags = { containsVisibleDifference: false, containsZindexDifference: false, }; @@ -1164,13 +1311,14 @@ export class ElementsDelta implements DeltaContainer { try { const applyDeltas = ElementsDelta.createApplier( nextElements, - elementsSnapshot, + snapshot, + options, flags, ); - const addedElements = applyDeltas("added", this.added); - const removedElements = applyDeltas("removed", this.removed); - const updatedElements = applyDeltas("updated", this.updated); + const addedElements = applyDeltas(this.added); + const removedElements = applyDeltas(this.removed); + const updatedElements = applyDeltas(this.updated); const affectedElements = this.resolveConflicts(elements, nextElements); @@ -1229,18 +1377,12 @@ export class ElementsDelta implements DeltaContainer { private static createApplier = ( nextElements: SceneElementsMap, - snapshot: Map, - flags: { - containsVisibleDifference: boolean; - containsZindexDifference: boolean; - }, + snapshot: StoreSnapshot["elements"], + options: ApplyToOptions, + flags: ApplyToFlags, ) => - ( - type: "added" | "removed" | "updated", - deltas: Record>, - ) => { + (deltas: Record>) => { const getElement = ElementsDelta.createGetter( - type, nextElements, snapshot, flags, @@ -1250,7 +1392,13 @@ export class ElementsDelta implements DeltaContainer { const element = getElement(id, delta.inserted); if (element) { - const newElement = ElementsDelta.applyDelta(element, delta, flags); + const newElement = ElementsDelta.applyDelta( + element, + delta, + options, + flags, + ); + nextElements.set(newElement.id, newElement); acc.set(newElement.id, newElement); } @@ -1261,13 +1409,9 @@ export class ElementsDelta implements DeltaContainer { private static createGetter = ( - type: "added" | "removed" | "updated", elements: SceneElementsMap, - snapshot: Map, - flags: { - containsVisibleDifference: boolean; - containsZindexDifference: boolean; - }, + snapshot: StoreSnapshot["elements"], + flags: ApplyToFlags, ) => (id: string, partial: ElementPartial) => { let element = elements.get(id); @@ -1281,10 +1425,7 @@ export class ElementsDelta implements DeltaContainer { flags.containsZindexDifference = true; // as the element was force deleted, we need to check if adding it back results in a visible change - if ( - partial.isDeleted === false || - (partial.isDeleted !== true && element.isDeleted === false) - ) { + if (!partial.isDeleted || (partial.isDeleted && !element.isDeleted)) { flags.containsVisibleDifference = true; } } else { @@ -1304,16 +1445,28 @@ export class ElementsDelta implements DeltaContainer { private static applyDelta( element: OrderedExcalidrawElement, delta: Delta, - flags: { - containsVisibleDifference: boolean; - containsZindexDifference: boolean; - } = { - // by default we don't care about about the flags - containsVisibleDifference: true, - containsZindexDifference: true, - }, + options: ApplyToOptions, + flags: ApplyToFlags, ) { - const { boundElements, ...directlyApplicablePartial } = delta.inserted; + const directlyApplicablePartial: Mutable = {}; + + // some properties are not directly applicable, such as: + // - boundElements which contains only diff) + // - version & versionNonce, if we don't want to return to previous versions + for (const key of Object.keys(delta.inserted) as Array< + keyof typeof delta.inserted + >) { + if (key === "boundElements") { + continue; + } + + if (options.excludedProperties.has(key)) { + continue; + } + + const value = delta.inserted[key]; + Reflect.set(directlyApplicablePartial, key, value); + } if ( delta.deleted.boundElements?.length || @@ -1331,19 +1484,6 @@ export class ElementsDelta implements DeltaContainer { }); } - // TODO: this looks wrong, shouldn't be here - if (element.type === "image") { - const _delta = delta as Delta>; - // we want to override `crop` only if modified so that we don't reset - // when undoing/redoing unrelated change - if (_delta.deleted.crop || _delta.inserted.crop) { - Object.assign(directlyApplicablePartial, { - // apply change verbatim - crop: _delta.inserted.crop ?? null, - }); - } - } - if (!flags.containsVisibleDifference) { // strip away fractional index, as even if it would be different, it doesn't have to result in visible change const { index, ...rest } = directlyApplicablePartial; @@ -1650,6 +1790,29 @@ export class ElementsDelta implements DeltaContainer { ): [ElementPartial, ElementPartial] { try { Delta.diffArrays(deleted, inserted, "boundElements", (x) => x.id); + + // don't diff the points as: + // - we can't ensure the multiplayer order consistency without fractional index on each point + // - we prefer to not merge the points, as it might just lead to unexpected / incosistent results + const deletedPoints = + ( + deleted as ElementPartial< + ExcalidrawFreeDrawElement | ExcalidrawLinearElement + > + ).points ?? []; + + const insertedPoints = + ( + inserted as ElementPartial< + ExcalidrawFreeDrawElement | ExcalidrawLinearElement + > + ).points ?? []; + + if (!Delta.isDifferent(deletedPoints, insertedPoints)) { + // delete the points from delta if there is no difference, otherwise leave them as they were captured due to consistency + Reflect.deleteProperty(deleted, "points"); + Reflect.deleteProperty(inserted, "points"); + } } catch (e) { // if postprocessing fails, it does not make sense to bubble up, but let's make sure we know about it console.error(`Couldn't postprocess elements delta.`); @@ -1665,7 +1828,7 @@ export class ElementsDelta implements DeltaContainer { private static stripIrrelevantProps( partial: Partial, ): ElementPartial { - const { id, updated, version, versionNonce, ...strippedPartial } = partial; + const { id, updated, ...strippedPartial } = partial; return strippedPartial; } diff --git a/packages/element/src/fractionalIndex.ts b/packages/element/src/fractionalIndex.ts index 84505365ec..44ca523c80 100644 --- a/packages/element/src/fractionalIndex.ts +++ b/packages/element/src/fractionalIndex.ts @@ -2,7 +2,7 @@ import { generateNKeysBetween } from "fractional-indexing"; import { arrayToMap } from "@excalidraw/common"; -import { mutateElement } from "./mutateElement"; +import { mutateElement, newElementWith } from "./mutateElement"; import { getBoundTextElement } from "./textElement"; import { hasBoundTextElement } from "./typeChecks"; @@ -11,6 +11,7 @@ import type { ExcalidrawElement, FractionalIndex, OrderedExcalidrawElement, + SceneElementsMap, } from "./types"; export class InvalidFractionalIndexError extends Error { @@ -161,9 +162,15 @@ export const syncMovedIndices = ( // try generatating indices, throws on invalid movedElements const elementsUpdates = generateIndices(elements, indicesGroups); - const elementsCandidates = elements.map((x) => - elementsUpdates.has(x) ? { ...x, ...elementsUpdates.get(x) } : x, - ); + const elementsCandidates = elements.map((x) => { + const elementUpdates = elementsUpdates.get(x); + + if (elementUpdates) { + return { ...x, index: elementUpdates.index }; + } + + return x; + }); // ensure next indices are valid before mutation, throws on invalid ones validateFractionalIndices( @@ -177,8 +184,8 @@ export const syncMovedIndices = ( ); // split mutation so we don't end up in an incosistent state - for (const [element, update] of elementsUpdates) { - mutateElement(element, elementsMap, update); + for (const [element, { index }] of elementsUpdates) { + mutateElement(element, elementsMap, { index }); } } catch (e) { // fallback to default sync @@ -189,7 +196,7 @@ export const syncMovedIndices = ( }; /** - * Synchronizes all invalid fractional indices with the array order by mutating passed elements. + * Synchronizes all invalid fractional indices within the array order by mutating elements in the passed array. * * WARN: in edge cases it could modify the elements which were not moved, as it's impossible to guess the actually moved elements from the elements array itself. */ @@ -200,13 +207,32 @@ export const syncInvalidIndices = ( const indicesGroups = getInvalidIndicesGroups(elements); const elementsUpdates = generateIndices(elements, indicesGroups); - for (const [element, update] of elementsUpdates) { - mutateElement(element, elementsMap, update); + for (const [element, { index }] of elementsUpdates) { + mutateElement(element, elementsMap, { index }); } return elements as OrderedExcalidrawElement[]; }; +/** + * Synchronizes all invalid fractional indices within the array order by creating new instances of elements with corrected indices. + * + * WARN: in edge cases it could modify the elements which were not moved, as it's impossible to guess the actually moved elements from the elements array itself. + */ +export const syncInvalidIndicesImmutable = ( + elements: readonly ExcalidrawElement[], +): SceneElementsMap | undefined => { + const syncedElements = arrayToMap(elements); + const indicesGroups = getInvalidIndicesGroups(elements); + const elementsUpdates = generateIndices(elements, indicesGroups); + + for (const [element, { index }] of elementsUpdates) { + syncedElements.set(element.id, newElementWith(element, { index })); + } + + return syncedElements as SceneElementsMap; +}; + /** * Get contiguous groups of indices of passed moved elements. * diff --git a/packages/element/src/mutateElement.ts b/packages/element/src/mutateElement.ts index 84785c31c9..4b5526917f 100644 --- a/packages/element/src/mutateElement.ts +++ b/packages/element/src/mutateElement.ts @@ -23,7 +23,7 @@ import type { export type ElementUpdate = Omit< Partial, - "id" | "version" | "versionNonce" | "updated" + "id" | "updated" >; /** @@ -137,8 +137,8 @@ export const mutateElement = >( ShapeCache.delete(element); } - element.version++; - element.versionNonce = randomInteger(); + element.version = updates.version ?? element.version + 1; + element.versionNonce = updates.versionNonce ?? randomInteger(); element.updated = getUpdatedTimestamp(); return element; @@ -172,9 +172,9 @@ export const newElementWith = ( return { ...element, ...updates, + version: updates.version ?? element.version + 1, + versionNonce: updates.versionNonce ?? randomInteger(), updated: getUpdatedTimestamp(), - version: element.version + 1, - versionNonce: randomInteger(), }; }; diff --git a/packages/element/src/store.ts b/packages/element/src/store.ts index fb8926d88b..da7352e3ee 100644 --- a/packages/element/src/store.ts +++ b/packages/element/src/store.ts @@ -19,9 +19,17 @@ import { newElementWith } from "./mutateElement"; import { ElementsDelta, AppStateDelta, Delta } from "./delta"; -import { hashElementsVersion, hashString } from "./index"; +import { + syncInvalidIndicesImmutable, + hashElementsVersion, + hashString, +} from "./index"; -import type { OrderedExcalidrawElement, SceneElementsMap } from "./types"; +import type { + ExcalidrawElement, + OrderedExcalidrawElement, + SceneElementsMap, +} from "./types"; export const CaptureUpdateAction = { /** @@ -105,7 +113,7 @@ export class Store { params: | { action: CaptureUpdateActionType; - elements: SceneElementsMap | undefined; + elements: readonly ExcalidrawElement[] | undefined; appState: AppState | ObservedAppState | undefined; } | { @@ -133,9 +141,15 @@ export class Store { this.app.scene.getElementsMapIncludingDeleted(), this.app.state, ); + const scheduledSnapshot = currentSnapshot.maybeClone( action, - params.elements, + // let's sync invalid indices first, so that we could detect this change + // also have the synced elements immutable, so that we don't mutate elements, + // that are already in the scene, otherwise we wouldn't see any change + params.elements + ? syncInvalidIndicesImmutable(params.elements) + : undefined, params.appState, ); @@ -213,16 +227,7 @@ export class Store { // using the same instance, since in history we have a check against `HistoryEntry`, so that we don't re-record the same delta again storeDelta = delta; } else { - // calculate the deltas based on the previous and next snapshot - const elementsDelta = snapshot.metadata.didElementsChange - ? ElementsDelta.calculate(prevSnapshot.elements, snapshot.elements) - : ElementsDelta.empty(); - - const appStateDelta = snapshot.metadata.didAppStateChange - ? AppStateDelta.calculate(prevSnapshot.appState, snapshot.appState) - : AppStateDelta.empty(); - - storeDelta = StoreDelta.create(elementsDelta, appStateDelta); + storeDelta = StoreDelta.calculate(prevSnapshot, snapshot); } if (!storeDelta.isEmpty()) { @@ -505,6 +510,24 @@ export class StoreDelta { return new this(opts.id, elements, appState); } + /** + * Calculate the delta between the previous and next snapshot. + */ + public static calculate( + prevSnapshot: StoreSnapshot, + nextSnapshot: StoreSnapshot, + ) { + const elementsDelta = nextSnapshot.metadata.didElementsChange + ? ElementsDelta.calculate(prevSnapshot.elements, nextSnapshot.elements) + : ElementsDelta.empty(); + + const appStateDelta = nextSnapshot.metadata.didAppStateChange + ? AppStateDelta.calculate(prevSnapshot.appState, nextSnapshot.appState) + : AppStateDelta.empty(); + + return this.create(elementsDelta, appStateDelta); + } + /** * Restore a store delta instance from a DTO. */ @@ -524,9 +547,7 @@ export class StoreDelta { id, elements: { added, removed, updated }, }: DTO) { - const elements = ElementsDelta.create(added, removed, updated, { - shouldRedistribute: false, - }); + const elements = ElementsDelta.create(added, removed, updated); return new this(id, elements, AppStateDelta.empty()); } @@ -534,27 +555,10 @@ export class StoreDelta { /** * Inverse store delta, creates new instance of `StoreDelta`. */ - public static inverse(delta: StoreDelta): StoreDelta { + public static inverse(delta: StoreDelta) { return this.create(delta.elements.inverse(), delta.appState.inverse()); } - /** - * Apply latest (remote) changes to the delta, creates new instance of `StoreDelta`. - */ - public static applyLatestChanges( - delta: StoreDelta, - elements: SceneElementsMap, - modifierOptions: "deleted" | "inserted", - ): StoreDelta { - return this.create( - delta.elements.applyLatestChanges(elements, modifierOptions), - delta.appState, - { - id: delta.id, - }, - ); - } - /** * Apply the delta to the passed elements and appState, does not modify the snapshot. */ @@ -562,12 +566,9 @@ export class StoreDelta { delta: StoreDelta, elements: SceneElementsMap, appState: AppState, - prevSnapshot: StoreSnapshot = StoreSnapshot.empty(), ): [SceneElementsMap, AppState, boolean] { - const [nextElements, elementsContainVisibleChange] = delta.elements.applyTo( - elements, - prevSnapshot.elements, - ); + const [nextElements, elementsContainVisibleChange] = + delta.elements.applyTo(elements); const [nextAppState, appStateContainsVisibleChange] = delta.appState.applyTo(appState, nextElements); @@ -578,6 +579,28 @@ export class StoreDelta { return [nextElements, nextAppState, appliedVisibleChanges]; } + /** + * Apply latest (remote) changes to the delta, creates new instance of `StoreDelta`. + */ + public static applyLatestChanges( + delta: StoreDelta, + prevElements: SceneElementsMap, + nextElements: SceneElementsMap, + modifierOptions?: "deleted" | "inserted", + ): StoreDelta { + return this.create( + delta.elements.applyLatestChanges( + prevElements, + nextElements, + modifierOptions, + ), + delta.appState, + { + id: delta.id, + }, + ); + } + public isEmpty() { return this.elements.isEmpty() && this.appState.isEmpty(); } @@ -687,11 +710,10 @@ export class StoreSnapshot { nextElements.set(id, changedElement); } - const nextAppState = Object.assign( - {}, - this.appState, - change.appState, - ) as ObservedAppState; + const nextAppState = getObservedAppState({ + ...this.appState, + ...change.appState, + }); return StoreSnapshot.create(nextElements, nextAppState, { // by default we assume that change is different from what we have in the snapshot @@ -944,18 +966,26 @@ const getDefaultObservedAppState = (): ObservedAppState => { }; }; -export const getObservedAppState = (appState: AppState): ObservedAppState => { +export const getObservedAppState = ( + appState: AppState | ObservedAppState, +): ObservedAppState => { const observedAppState = { name: appState.name, editingGroupId: appState.editingGroupId, viewBackgroundColor: appState.viewBackgroundColor, selectedElementIds: appState.selectedElementIds, selectedGroupIds: appState.selectedGroupIds, - editingLinearElementId: appState.editingLinearElement?.elementId || null, - selectedLinearElementId: appState.selectedLinearElement?.elementId || null, croppingElementId: appState.croppingElementId, activeLockedId: appState.activeLockedId, lockedMultiSelections: appState.lockedMultiSelections, + editingLinearElementId: + (appState as AppState).editingLinearElement?.elementId ?? // prefer app state, as it's likely newer + (appState as ObservedAppState).editingLinearElementId ?? // fallback to observed app state, as it's likely older coming from a previous snapshot + null, + selectedLinearElementId: + (appState as AppState).selectedLinearElement?.elementId ?? + (appState as ObservedAppState).selectedLinearElementId ?? + null, }; Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, { diff --git a/packages/element/tests/duplicate.test.tsx b/packages/element/tests/duplicate.test.tsx index ed25083904..10b9346a6c 100644 --- a/packages/element/tests/duplicate.test.tsx +++ b/packages/element/tests/duplicate.test.tsx @@ -505,8 +505,6 @@ describe("group-related duplication", () => { mouse.up(frame.x + frame.width + 50, frame.y + frame.height + 50); }); - // console.log(h.elements); - assertElements(h.elements, [ { id: frame.id }, { id: rectangle1.id, frameId: frame.id }, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 7206737b38..c6231415a2 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -103,6 +103,7 @@ import { } from "@excalidraw/common"; import { + getObservedAppState, getCommonBounds, getElementAbsoluteCoords, bindOrUnbindLinearElements, @@ -260,7 +261,6 @@ import type { ExcalidrawNonSelectionElement, ExcalidrawArrowElement, ExcalidrawElbowArrowElement, - SceneElementsMap, } from "@excalidraw/element/types"; import type { Mutable, ValueOf } from "@excalidraw/common/utility-types"; @@ -702,6 +702,8 @@ class App extends React.Component { addFiles: this.addFiles, resetScene: this.resetScene, getSceneElementsIncludingDeleted: this.getSceneElementsIncludingDeleted, + getSceneElementsMapIncludingDeleted: + this.getSceneElementsMapIncludingDeleted, history: { clear: this.resetHistory, }, @@ -3909,22 +3911,18 @@ class App extends React.Component { }) => { const { elements, appState, collaborators, captureUpdate } = sceneData; - const nextElements = elements ? syncInvalidIndices(elements) : undefined; - if (captureUpdate) { - const nextElementsMap = elements - ? (arrayToMap(nextElements ?? []) as SceneElementsMap) - : undefined; - - const nextAppState = appState - ? // new instance, with partial appstate applied to previously captured one, including hidden prop inside `prevCommittedAppState` - Object.assign({}, this.store.snapshot.appState, appState) + const observedAppState = appState + ? getObservedAppState({ + ...this.store.snapshot.appState, + ...appState, + }) : undefined; this.store.scheduleMicroAction({ action: captureUpdate, - elements: nextElementsMap, - appState: nextAppState, + elements: elements ?? [], + appState: observedAppState, }); } @@ -3932,8 +3930,8 @@ class App extends React.Component { this.setState(appState); } - if (nextElements) { - this.scene.replaceAllElements(nextElements); + if (elements) { + this.scene.replaceAllElements(elements); } if (collaborators) { @@ -10550,7 +10548,7 @@ class App extends React.Component { // otherwise we would end up with duplicated fractional indices on undo this.store.scheduleMicroAction({ action: CaptureUpdateAction.NEVER, - elements: arrayToMap(elements) as SceneElementsMap, + elements, appState: undefined, }); diff --git a/packages/excalidraw/history.ts b/packages/excalidraw/history.ts index cd9dcffe23..7250dd600a 100644 --- a/packages/excalidraw/history.ts +++ b/packages/excalidraw/history.ts @@ -4,14 +4,81 @@ import { CaptureUpdateAction, StoreChange, StoreDelta, - type Store, } from "@excalidraw/element"; +import type { StoreSnapshot, Store } from "@excalidraw/element"; + import type { SceneElementsMap } from "@excalidraw/element/types"; import type { AppState } from "./types"; -class HistoryEntry extends StoreDelta {} +export class HistoryDelta extends StoreDelta { + /** + * Apply the delta to the passed elements and appState, does not modify the snapshot. + */ + public applyTo( + elements: SceneElementsMap, + appState: AppState, + snapshot: StoreSnapshot, + ): [SceneElementsMap, AppState, boolean] { + const [nextElements, elementsContainVisibleChange] = this.elements.applyTo( + elements, + // used to fallback into local snapshot in case we couldn't apply the delta + // due to a missing (force deleted) elements in the scene + snapshot.elements, + // we don't want to apply the `version` and `versionNonce` properties for history + // as we always need to end up with a new version due to collaboration, + // approaching each undo / redo as a new user action + { + excludedProperties: new Set(["version", "versionNonce"]), + }, + ); + + const [nextAppState, appStateContainsVisibleChange] = this.appState.applyTo( + appState, + nextElements, + ); + + const appliedVisibleChanges = + elementsContainVisibleChange || appStateContainsVisibleChange; + + return [nextElements, nextAppState, appliedVisibleChanges]; + } + + /** + * Overriding once to avoid type casting everywhere. + */ + public static override calculate( + prevSnapshot: StoreSnapshot, + nextSnapshot: StoreSnapshot, + ) { + return super.calculate(prevSnapshot, nextSnapshot) as HistoryDelta; + } + + /** + * Overriding once to avoid type casting everywhere. + */ + public static override inverse(delta: StoreDelta): HistoryDelta { + return super.inverse(delta) as HistoryDelta; + } + + /** + * Overriding once to avoid type casting everywhere. + */ + public static override applyLatestChanges( + delta: StoreDelta, + prevElements: SceneElementsMap, + nextElements: SceneElementsMap, + modifierOptions?: "deleted" | "inserted", + ) { + return super.applyLatestChanges( + delta, + prevElements, + nextElements, + modifierOptions, + ) as HistoryDelta; + } +} export class HistoryChangedEvent { constructor( @@ -25,8 +92,8 @@ export class History { [HistoryChangedEvent] >(); - public readonly undoStack: HistoryEntry[] = []; - public readonly redoStack: HistoryEntry[] = []; + public readonly undoStack: HistoryDelta[] = []; + public readonly redoStack: HistoryDelta[] = []; public get isUndoStackEmpty() { return this.undoStack.length === 0; @@ -48,16 +115,16 @@ export class History { * Do not re-record history entries, which were already pushed to undo / redo stack, as part of history action. */ public record(delta: StoreDelta) { - if (delta.isEmpty() || delta instanceof HistoryEntry) { + if (delta.isEmpty() || delta instanceof HistoryDelta) { return; } // construct history entry, so once it's emitted, it's not recorded again - const entry = HistoryEntry.inverse(delta); + const historyDelta = HistoryDelta.inverse(delta); - this.undoStack.push(entry); + this.undoStack.push(historyDelta); - if (!entry.elements.isEmpty()) { + if (!historyDelta.elements.isEmpty()) { // don't reset redo stack on local appState changes, // as a simple click (unselect) could lead to losing all the redo entries // only reset on non empty elements changes! @@ -74,7 +141,7 @@ export class History { elements, appState, () => History.pop(this.undoStack), - (entry: HistoryEntry) => History.push(this.redoStack, entry, elements), + (entry: HistoryDelta) => History.push(this.redoStack, entry), ); } @@ -83,20 +150,20 @@ export class History { elements, appState, () => History.pop(this.redoStack), - (entry: HistoryEntry) => History.push(this.undoStack, entry, elements), + (entry: HistoryDelta) => History.push(this.undoStack, entry), ); } private perform( elements: SceneElementsMap, appState: AppState, - pop: () => HistoryEntry | null, - push: (entry: HistoryEntry) => void, + pop: () => HistoryDelta | null, + push: (entry: HistoryDelta) => void, ): [SceneElementsMap, AppState] | void { try { - let historyEntry = pop(); + let historyDelta = pop(); - if (historyEntry === null) { + if (historyDelta === null) { return; } @@ -108,41 +175,47 @@ export class History { let nextAppState = appState; let containsVisibleChange = false; - // iterate through the history entries in case they result in no visible changes - while (historyEntry) { + // iterate through the history entries in case ;they result in no visible changes + while (historyDelta) { try { [nextElements, nextAppState, containsVisibleChange] = - StoreDelta.applyTo( - historyEntry, - nextElements, - nextAppState, - prevSnapshot, - ); + historyDelta.applyTo(nextElements, nextAppState, prevSnapshot); + const prevElements = prevSnapshot.elements; const nextSnapshot = prevSnapshot.maybeClone( action, nextElements, nextAppState, ); - // schedule immediate capture, so that it's emitted for the sync purposes - this.store.scheduleMicroAction({ - action, - change: StoreChange.create(prevSnapshot, nextSnapshot), - delta: historyEntry, - }); + const change = StoreChange.create(prevSnapshot, nextSnapshot); + const delta = HistoryDelta.applyLatestChanges( + historyDelta, + prevElements, + nextElements, + ); + + if (!delta.isEmpty()) { + // schedule immediate capture, so that it's emitted for the sync purposes + this.store.scheduleMicroAction({ + action, + change, + delta, + }); + + historyDelta = delta; + } prevSnapshot = nextSnapshot; } finally { - // make sure to always push, even if the delta is corrupted - push(historyEntry); + push(historyDelta); } if (containsVisibleChange) { break; } - historyEntry = pop(); + historyDelta = pop(); } return [nextElements, nextAppState]; @@ -155,7 +228,7 @@ export class History { } } - private static pop(stack: HistoryEntry[]): HistoryEntry | null { + private static pop(stack: HistoryDelta[]): HistoryDelta | null { if (!stack.length) { return null; } @@ -169,18 +242,8 @@ export class History { return null; } - private static push( - stack: HistoryEntry[], - entry: HistoryEntry, - prevElements: SceneElementsMap, - ) { - const inversedEntry = HistoryEntry.inverse(entry); - const updatedEntry = HistoryEntry.applyLatestChanges( - inversedEntry, - prevElements, - "inserted", - ); - - return stack.push(updatedEntry); + private static push(stack: HistoryDelta[], entry: HistoryDelta) { + const inversedEntry = HistoryDelta.inverse(entry); + return stack.push(inversedEntry); } } diff --git a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap index 54fce16156..079e82da93 100644 --- a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap @@ -1269,12 +1269,14 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -20, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1420,14 +1422,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1014066025, + "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, - "versionNonce": 1604849351, + "versionNonce": 1505387817, "width": 20, "x": 20, "y": 30, @@ -1459,7 +1461,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 23633383, + "versionNonce": 915032327, "width": 20, "x": -10, "y": 0, @@ -1511,12 +1513,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1563,12 +1567,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1598,9 +1604,11 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings "id0": { "deleted": { "index": "a2", + "version": 4, }, "inserted": { "index": "a0", + "version": 3, }, }, }, @@ -1745,14 +1753,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1014066025, + "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, - "versionNonce": 1604849351, + "versionNonce": 1505387817, "width": 20, "x": 20, "y": 30, @@ -1784,7 +1792,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 23633383, + "versionNonce": 915032327, "width": 20, "x": -10, "y": 0, @@ -1836,12 +1844,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1888,12 +1898,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1923,9 +1935,11 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings "id0": { "deleted": { "index": "a2", + "version": 4, }, "inserted": { "index": "a0", + "version": 3, }, }, }, @@ -2131,12 +2145,14 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -20, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2287,7 +2303,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 1116226695, + "versionNonce": 1014066025, "width": 10, "x": -20, "y": -10, @@ -2339,12 +2355,14 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -20, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2370,9 +2388,11 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen "id0": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "isDeleted": false, + "version": 3, }, }, }, @@ -2551,14 +2571,14 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1014066025, + "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, - "versionNonce": 400692809, + "versionNonce": 1604849351, "width": 10, "x": -10, "y": 0, @@ -2610,12 +2630,14 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -20, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2662,12 +2684,14 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -2827,7 +2851,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 493213705, + "versionNonce": 81784553, "width": 20, "x": -10, "y": 0, @@ -2854,14 +2878,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1014066025, + "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 915032327, + "versionNonce": 747212839, "width": 20, "x": 20, "y": 30, @@ -2913,12 +2937,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2965,12 +2991,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3020,9 +3048,11 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "groupIds": [ "id9", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -3030,9 +3060,11 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group "groupIds": [ "id9", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -3186,7 +3218,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 908564423, + "versionNonce": 1359939303, "width": 20, "x": -10, "y": 0, @@ -3211,14 +3243,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "opacity": 60, "roughness": 2, "roundness": null, - "seed": 1315507081, + "seed": 640725609, "strokeColor": "#e03131", "strokeStyle": "dotted", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 9, - "versionNonce": 406373543, + "versionNonce": 908564423, "width": 20, "x": 20, "y": 30, @@ -3270,12 +3302,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3322,12 +3356,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3349,9 +3385,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "strokeColor": "#e03131", + "version": 4, }, "inserted": { "strokeColor": "#1e1e1e", + "version": 3, }, }, }, @@ -3372,9 +3410,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "backgroundColor": "#a5d8ff", + "version": 5, }, "inserted": { "backgroundColor": "transparent", + "version": 4, }, }, }, @@ -3395,9 +3435,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "fillStyle": "cross-hatch", + "version": 6, }, "inserted": { "fillStyle": "solid", + "version": 5, }, }, }, @@ -3418,9 +3460,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "strokeStyle": "dotted", + "version": 7, }, "inserted": { "strokeStyle": "solid", + "version": 6, }, }, }, @@ -3441,9 +3485,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "roughness": 2, + "version": 8, }, "inserted": { "roughness": 1, + "version": 7, }, }, }, @@ -3464,9 +3510,11 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "id3": { "deleted": { "opacity": 60, + "version": 9, }, "inserted": { "opacity": 100, + "version": 8, }, }, }, @@ -3500,6 +3548,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roughness": 2, "strokeColor": "#e03131", "strokeStyle": "dotted", + "version": 4, }, "inserted": { "backgroundColor": "transparent", @@ -3508,6 +3557,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s "roughness": 1, "strokeColor": "#1e1e1e", "strokeStyle": "solid", + "version": 3, }, }, }, @@ -3652,14 +3702,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1150084233, + "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 1604849351, + "versionNonce": 23633383, "width": 20, "x": 20, "y": 30, @@ -3743,12 +3793,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3795,12 +3847,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3822,9 +3876,11 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e "id3": { "deleted": { "index": "Zz", + "version": 4, }, "inserted": { "index": "a1", + "version": 3, }, }, }, @@ -3969,14 +4025,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1014066025, + "seed": 238820263, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 23633383, + "versionNonce": 915032327, "width": 20, "x": 20, "y": 30, @@ -4060,12 +4116,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4112,12 +4170,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4139,9 +4199,11 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el "id3": { "deleted": { "index": "Zz", + "version": 4, }, "inserted": { "index": "a1", + "version": 3, }, }, }, @@ -4296,7 +4358,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "type": "rectangle", "updated": 1, "version": 5, - "versionNonce": 1723083209, + "versionNonce": 1006504105, "width": 20, "x": -10, "y": 0, @@ -4321,14 +4383,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "opacity": 100, "roughness": 1, "roundness": null, - "seed": 238820263, + "seed": 400692809, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 5, - "versionNonce": 760410951, + "versionNonce": 289600103, "width": 20, "x": 20, "y": 30, @@ -4380,12 +4442,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4432,12 +4496,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 20, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4487,9 +4553,11 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "groupIds": [ "id9", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -4497,9 +4565,11 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "groupIds": [ "id9", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -4526,21 +4596,25 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung "id0": { "deleted": { "groupIds": [], + "version": 5, }, "inserted": { "groupIds": [ "id9", ], + "version": 4, }, }, "id3": { "deleted": { "groupIds": [], + "version": 5, }, "inserted": { "groupIds": [ "id9", ], + "version": 4, }, }, }, @@ -5594,14 +5668,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1604849351, + "seed": 1505387817, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 3, - "versionNonce": 493213705, + "versionNonce": 915032327, "width": 10, "x": 12, "y": 0, @@ -5653,12 +5727,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5705,12 +5781,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 12, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6786,7 +6864,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 81784553, + "versionNonce": 1723083209, "width": 10, "x": -10, "y": 0, @@ -6813,14 +6891,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "opacity": 100, "roughness": 1, "roundness": null, - "seed": 238820263, + "seed": 400692809, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 747212839, + "versionNonce": 760410951, "width": 10, "x": 12, "y": 0, @@ -6872,12 +6950,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6924,12 +7004,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 12, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7001,9 +7083,11 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -7011,9 +7095,11 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -9822,12 +9908,14 @@ exports[`contextMenu element > shows context menu for element > [end of test] un "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -20, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index 0dc6e525ba..191add45d8 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -284,9 +284,18 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "added": {}, "removed": {}, "updated": { + "id687": { + "deleted": { + "version": 17, + }, + "inserted": { + "version": 15, + }, + }, "id688": { "deleted": { "boundElements": [], + "version": 9, }, "inserted": { "boundElements": [ @@ -295,6 +304,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 8, }, }, "id691": { @@ -308,7 +318,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": 0, "gap": 1, }, - "height": "68.55969", + "height": "68.58402", "points": [ [ 0, @@ -316,7 +326,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl ], [ 98, - "68.55969", + "68.58402", ], ], "startBinding": { @@ -324,6 +334,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": "0.02970", "gap": 1, }, + "version": 35, }, "inserted": { "endBinding": { @@ -347,6 +358,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": "0.02000", "gap": 1, }, + "version": 32, }, }, "id702": { @@ -357,9 +369,11 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 12, }, "inserted": { "boundElements": [], + "version": 11, }, }, }, @@ -380,6 +394,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "id687": { "deleted": { "boundElements": [], + "version": 18, }, "inserted": { "boundElements": [ @@ -388,6 +403,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 17, }, }, "id691": { @@ -404,6 +420,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl ], ], "startBinding": null, + "version": 37, "y": 0, }, "inserted": { @@ -423,9 +440,18 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": "0.02970", "gap": 1, }, + "version": 35, "y": "35.82151", }, }, + "id702": { + "deleted": { + "version": 14, + }, + "inserted": { + "version": 12, + }, + }, }, }, "id": "id710", @@ -466,12 +492,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id688": { @@ -495,12 +523,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -565,12 +595,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 4, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -783,7 +815,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 100, + 0, 0, ], ], @@ -799,7 +831,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 33, - "width": 100, + "width": 0, "x": 149, "y": 0, } @@ -822,9 +854,18 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "added": {}, "removed": {}, "updated": { + "id664": { + "deleted": { + "version": 18, + }, + "inserted": { + "version": 16, + }, + }, "id665": { "deleted": { "boundElements": [], + "version": 9, }, "inserted": { "boundElements": [ @@ -833,21 +874,13 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 8, }, }, "id668": { "deleted": { "endBinding": null, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], + "version": 32, }, "inserted": { "endBinding": { @@ -855,16 +888,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": -0, "gap": 1, }, - "points": [ - [ - 0, - 0, - ], - [ - 0, - 0, - ], - ], + "version": 30, }, }, }, @@ -885,6 +909,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "id664": { "deleted": { "boundElements": [], + "version": 19, }, "inserted": { "boundElements": [ @@ -893,38 +918,21 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 18, }, }, "id668": { "deleted": { - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": null, + "version": 33, }, "inserted": { - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": { "elementId": "id664", "focus": 0, "gap": 1, }, + "version": 32, }, }, }, @@ -967,12 +975,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id665": { @@ -996,12 +1006,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -1066,12 +1078,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 4, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -1368,12 +1382,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 6, }, }, "id712": { @@ -1397,12 +1413,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -1427,10 +1445,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "focus": 0, "gap": 1, }, + "version": 11, }, "inserted": { "endBinding": null, "startBinding": null, + "version": 8, }, }, }, @@ -1725,7 +1745,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "11.63758", + "height": "1.36342", "index": "a0", "isDeleted": false, "lastCommittedPoint": null, @@ -1739,7 +1759,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl ], [ 98, - "11.63758", + "1.36342", ], ], "roughness": 1, @@ -1758,12 +1778,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 11, "width": 98, "x": 1, "y": 0, }, "inserted": { "isDeleted": true, + "version": 8, }, }, }, @@ -1776,9 +1798,11 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 12, }, "inserted": { "boundElements": [], + "version": 9, }, }, "id721": { @@ -1789,9 +1813,11 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 11, }, "inserted": { "boundElements": [], + "version": 8, }, }, }, @@ -2015,12 +2041,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id733": { @@ -2044,12 +2072,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -2339,12 +2369,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id737": { @@ -2368,12 +2400,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -2415,7 +2449,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "370.26975", "index": "a2", "isDeleted": false, "lastCommittedPoint": null, @@ -2428,8 +2462,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 100, - 0, + "498.00000", + "-370.26975", ], ], "roughness": 1, @@ -2446,12 +2480,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", - "width": 100, - "x": 0, - "y": 0, + "version": 10, + "width": "498.00000", + "x": 1, + "y": "-37.92697", }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -2464,9 +2500,11 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 7, }, "inserted": { "boundElements": [], + "version": 4, }, }, "id737": { @@ -2477,9 +2515,11 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", }, ], + "version": 8, }, "inserted": { "boundElements": [], + "version": 5, }, }, }, @@ -2632,7 +2672,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 7, + "version": 6, "width": 100, "x": 10, "y": 10, @@ -2709,7 +2749,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "textAlign": "left", "type": "text", "updated": 1, - "version": 11, + "version": 7, "verticalAlign": "top", "width": 30, "x": 15, @@ -2734,41 +2774,12 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "added": {}, "removed": {}, "updated": { - "id613": { + "id618": { "deleted": { - "isDeleted": false, + "version": 7, }, "inserted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 100, - "index": "a0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 100, - "x": 10, - "y": 10, - }, - }, - "id614": { - "deleted": { - "containerId": null, - }, - "inserted": { - "containerId": null, + "version": 5, }, }, }, @@ -2923,7 +2934,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 11, + "version": 7, "width": 100, "x": 10, "y": 10, @@ -3000,7 +3011,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "textAlign": "left", "type": "text", "updated": 1, - "version": 11, + "version": 5, "verticalAlign": "top", "width": 30, "x": 15, @@ -3027,29 +3038,17 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "deleted": { "containerId": "id623", "isDeleted": true, + "version": 9, }, "inserted": { "containerId": null, "isDeleted": false, + "version": 8, }, }, }, "removed": {}, - "updated": { - "id623": { - "deleted": { - "boundElements": [], - }, - "inserted": { - "boundElements": [ - { - "id": "id624", - "type": "text", - }, - ], - }, - }, - }, + "updated": {}, }, "id": "id632", }, @@ -3311,6 +3310,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 10, }, "inserted": { "boundElements": [ @@ -3319,22 +3319,27 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 9, }, }, "id578": { "deleted": { "containerId": null, + "version": 9, }, "inserted": { "containerId": "id577", + "version": 8, }, }, "id582": { "deleted": { "containerId": "id577", + "version": 7, }, "inserted": { "containerId": null, + "version": 6, }, }, }, @@ -3586,6 +3591,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "id587": { "deleted": { "boundElements": [], + "version": 8, }, "inserted": { "boundElements": [ @@ -3594,14 +3600,17 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 7, }, }, "id588": { "deleted": { "containerId": "id592", + "version": 13, }, "inserted": { "containerId": "id587", + "version": 11, }, }, "id592": { @@ -3612,9 +3621,11 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 8, }, "inserted": { "boundElements": [], + "version": 7, }, }, }, @@ -3830,6 +3841,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "id568": { "deleted": { "boundElements": [], + "version": 9, }, "inserted": { "boundElements": [ @@ -3838,14 +3850,17 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 8, }, }, "id569": { "deleted": { "containerId": null, + "version": 10, }, "inserted": { "containerId": "id568", + "version": 9, }, }, }, @@ -4085,12 +4100,14 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 100, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -4098,9 +4115,11 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "id598": { "deleted": { "containerId": "id597", + "version": 12, }, "inserted": { "containerId": null, + "version": 9, }, }, }, @@ -4346,6 +4365,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "text": "que pasa", "textAlign": "left", "type": "text", + "version": 8, "verticalAlign": "top", "width": 80, "x": 15, @@ -4353,6 +4373,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -4365,9 +4386,11 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "type": "text", }, ], + "version": 11, }, "inserted": { "boundElements": [], + "version": 8, }, }, }, @@ -4586,11 +4609,13 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "id658": { "deleted": { "angle": 0, + "version": 5, "x": 15, "y": 15, }, "inserted": { "angle": 0, + "version": 7, "x": 15, "y": 15, }, @@ -4815,11 +4840,13 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "id649": { "deleted": { "angle": 90, + "version": 8, "x": 200, "y": 200, }, "inserted": { "angle": 0, + "version": 7, "x": 10, "y": 10, }, @@ -5037,6 +5064,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "deleted": { "boundElements": [], "isDeleted": false, + "version": 8, }, "inserted": { "boundElements": [ @@ -5046,6 +5074,7 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and }, ], "isDeleted": true, + "version": 7, }, }, }, @@ -5267,10 +5296,12 @@ exports[`history > multiplayer undo/redo > conflicts in bound text elements and "deleted": { "containerId": null, "isDeleted": false, + "version": 8, }, "inserted": { "containerId": "id641", "isDeleted": true, + "version": 7, }, }, }, @@ -5496,12 +5527,14 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 8, "width": 100, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -5522,10 +5555,10 @@ exports[`history > multiplayer undo/redo > conflicts in frames and their childre "updated": { "id746": { "deleted": { - "frameId": "id745", + "version": 10, }, "inserted": { - "frameId": null, + "version": 8, }, }, }, @@ -5674,7 +5707,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 5, + "version": 4, "width": 100, "x": 0, "y": 0, @@ -5706,7 +5739,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 4, + "version": 3, "width": 100, "x": 100, "y": 100, @@ -5742,70 +5775,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "elements": { "added": {}, "removed": {}, - "updated": { - "id468": { - "deleted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "A", - ], - "height": 100, - "index": "a0", - "isDeleted": true, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 100, - "x": 0, - "y": 0, - }, - "inserted": { - "isDeleted": true, - }, - }, - "id469": { - "deleted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "A", - ], - "height": 100, - "index": "a1", - "isDeleted": true, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 100, - "x": 100, - "y": 100, - }, - "inserted": { - "isDeleted": true, - }, - }, - }, + "updated": {}, }, "id": "id481", }, @@ -6109,12 +6079,14 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6140,37 +6112,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "elements": { "added": {}, "removed": {}, - "updated": { - "id413": { - "deleted": { - "angle": 0, - "backgroundColor": "#ffc9c9", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 10, - "index": "a1", - "isDeleted": true, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 10, - "x": 20, - "y": 0, - }, - "inserted": { - "isDeleted": true, - }, - }, - }, + "updated": {}, }, "id": "id428", }, @@ -6188,9 +6130,11 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id413": { "deleted": { "backgroundColor": "#ffc9c9", + "version": 7, }, "inserted": { "backgroundColor": "transparent", + "version": 6, }, }, }, @@ -6215,37 +6159,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "elements": { "added": {}, "removed": {}, - "updated": { - "id418": { - "deleted": { - "angle": 0, - "backgroundColor": "#ffc9c9", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 10, - "index": "a2", - "isDeleted": true, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 10, - "x": 50, - "y": 50, - }, - "inserted": { - "isDeleted": true, - }, - }, - }, + "updated": {}, }, "id": "id430", }, @@ -6262,10 +6176,12 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "updated": { "id418": { "deleted": { + "version": 7, "x": 50, "y": 50, }, "inserted": { + "version": 6, "x": 30, "y": 30, }, @@ -6529,12 +6445,14 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id433": { @@ -6558,12 +6476,14 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id434": { @@ -6587,12 +6507,14 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 30, "y": 30, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -7188,12 +7110,14 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 6, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -7424,37 +7348,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "elements": { "added": {}, "removed": {}, - "updated": { - "id401": { - "deleted": { - "angle": 0, - "backgroundColor": "#ffec99", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 10, - "index": "a0", - "isDeleted": true, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "width": 10, - "x": 10, - "y": 0, - }, - "inserted": { - "isDeleted": true, - }, - }, - }, + "updated": {}, }, "id": "id408", }, @@ -7472,9 +7366,11 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id401": { "deleted": { "backgroundColor": "#ffec99", + "version": 7, }, "inserted": { "backgroundColor": "transparent", + "version": 6, }, }, }, @@ -7706,9 +7602,11 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id514": { "deleted": { "index": "a1", + "version": 7, }, "inserted": { "index": "a3", + "version": 6, }, }, }, @@ -7733,6 +7631,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id513": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -7754,6 +7653,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 10, "y": 10, @@ -7762,6 +7662,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id514": { "deleted": { "isDeleted": true, + "version": 8, }, "inserted": { "angle": 0, @@ -7772,7 +7673,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "frameId": null, "groupIds": [], "height": 100, - "index": "a3", + "index": "a1", "isDeleted": false, "link": null, "locked": false, @@ -7783,6 +7684,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 100, "x": 20, "y": 20, @@ -7791,6 +7693,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id515": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -7812,6 +7715,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 30, "y": 30, @@ -8050,9 +7954,11 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id502": { "deleted": { "index": "a1", + "version": 6, }, "inserted": { "index": "Zz", + "version": 5, }, }, }, @@ -8077,6 +7983,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id501": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -8098,6 +8005,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 10, "y": 10, @@ -8106,6 +8014,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id502": { "deleted": { "isDeleted": true, + "version": 7, }, "inserted": { "angle": 0, @@ -8116,7 +8025,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "frameId": null, "groupIds": [], "height": 100, - "index": "Zz", + "index": "a1", "isDeleted": false, "link": null, "locked": false, @@ -8127,6 +8036,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 6, "width": 100, "x": 20, "y": 20, @@ -8135,6 +8045,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "id503": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -8156,6 +8067,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 30, "y": 30, @@ -8425,12 +8337,14 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 8, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -8477,12 +8391,14 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 8, "width": 10, "x": 30, "y": 30, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -8545,20 +8461,24 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "updated": { "id542": { "deleted": { + "version": 9, "x": 90, "y": 90, }, "inserted": { + "version": 8, "x": 10, "y": 10, }, }, "id545": { "deleted": { + "version": 9, "x": 110, "y": 110, }, "inserted": { + "version": 8, "x": 30, "y": 30, }, @@ -8839,12 +8759,14 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "strokeStyle": "solid", "strokeWidth": 2, "type": "freedraw", + "version": 7, "width": 50, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -9074,12 +8996,14 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 8, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -9101,10 +9025,12 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte "id531": { "deleted": { "height": 90, + "version": 9, "width": 90, }, "inserted": { "height": 10, + "version": 8, "width": 10, }, }, @@ -9309,9 +9235,11 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "id333": { "deleted": { "backgroundColor": "transparent", + "version": 7, }, "inserted": { "backgroundColor": "#ffc9c9", + "version": 6, }, }, }, @@ -9360,12 +9288,14 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -9565,12 +9495,14 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -9592,9 +9524,11 @@ exports[`history > multiplayer undo/redo > should not override remote changes on "id342": { "deleted": { "backgroundColor": "#ffc9c9", + "version": 7, }, "inserted": { "backgroundColor": "transparent", + "version": 6, }, }, }, @@ -9874,9 +9808,11 @@ exports[`history > multiplayer undo/redo > should override remotely added groups "A", "B", ], + "version": 6, }, "inserted": { "groupIds": [], + "version": 5, }, }, "id372": { @@ -9885,9 +9821,11 @@ exports[`history > multiplayer undo/redo > should override remotely added groups "A", "B", ], + "version": 6, }, "inserted": { "groupIds": [], + "version": 5, }, }, }, @@ -10140,12 +10078,14 @@ exports[`history > multiplayer undo/redo > should override remotely added points "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 12, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 11, }, }, }, @@ -10193,6 +10133,7 @@ exports[`history > multiplayer undo/redo > should override remotely added points 20, ], ], + "version": 13, "width": 30, }, "inserted": { @@ -10211,6 +10152,7 @@ exports[`history > multiplayer undo/redo > should override remotely added points 10, ], ], + "version": 12, "width": 10, }, }, @@ -10426,12 +10368,14 @@ exports[`history > multiplayer undo/redo > should redistribute deltas when eleme "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -10455,16 +10399,7 @@ exports[`history > multiplayer undo/redo > should redistribute deltas when eleme "elements": { "added": {}, "removed": {}, - "updated": { - "id392": { - "deleted": { - "isDeleted": false, - }, - "inserted": { - "isDeleted": false, - }, - }, - }, + "updated": {}, }, "id": "id400", }, @@ -10733,6 +10668,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "6Rm4g567UQM4WjLwej2Vc": { "deleted": { "isDeleted": true, + "version": 3, }, "inserted": { "angle": 0, @@ -10793,6 +10729,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 2, "width": "178.90000", "x": 1035, "y": "274.90000", @@ -10804,6 +10741,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "KPrBI4g_v9qUB1XxYLgSz": { "deleted": { "boundElements": [], + "version": 3, }, "inserted": { "boundElements": [ @@ -10812,11 +10750,13 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "type": "arrow", }, ], + "version": 2, }, }, "u2JGnnmoJ0VATV4vCNJE5": { "deleted": { "boundElements": [], + "version": 3, }, "inserted": { "boundElements": [ @@ -10825,6 +10765,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "type": "arrow", }, ], + "version": 2, }, }, }, @@ -10843,6 +10784,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "KPrBI4g_v9qUB1XxYLgSz": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -10864,14 +10806,16 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 157, - "x": 600, - "y": 0, + "x": 873, + "y": 212, }, }, "u2JGnnmoJ0VATV4vCNJE5": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -10893,6 +10837,7 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 124, "x": 1152, "y": 516, @@ -11073,9 +11018,11 @@ exports[`history > multiplayer undo/redo > should update history entries after r "id349": { "deleted": { "backgroundColor": "#d0bfff", + "version": 12, }, "inserted": { "backgroundColor": "#ffec99", + "version": 11, }, }, }, @@ -11096,9 +11043,11 @@ exports[`history > multiplayer undo/redo > should update history entries after r "id349": { "deleted": { "backgroundColor": "transparent", + "version": 13, }, "inserted": { "backgroundColor": "#d0bfff", + "version": 12, }, }, }, @@ -11147,12 +11096,14 @@ exports[`history > multiplayer undo/redo > should update history entries after r "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11359,6 +11310,7 @@ exports[`history > singleplayer undo/redo > remounting undo/redo buttons should "id329": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -11380,6 +11332,7 @@ exports[`history > singleplayer undo/redo > remounting undo/redo buttons should "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, @@ -11615,12 +11568,14 @@ exports[`history > singleplayer undo/redo > should clear the redo stack on eleme "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 20, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11926,12 +11881,14 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": -10, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12014,12 +11971,14 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f "strokeStyle": "solid", "strokeWidth": 2, "type": "freedraw", + "version": 4, "width": 50, "x": 130, "y": -30, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -12228,9 +12187,11 @@ exports[`history > singleplayer undo/redo > should create new history entry on s "A": { "deleted": { "isDeleted": true, + "version": 5, }, "inserted": { "isDeleted": false, + "version": 4, }, }, }, @@ -12255,12 +12216,14 @@ exports[`history > singleplayer undo/redo > should create new history entry on s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -12490,12 +12453,14 @@ exports[`history > singleplayer undo/redo > should disable undo/redo buttons whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -12725,12 +12690,14 @@ exports[`history > singleplayer undo/redo > should end up with no history entry "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -12949,6 +12916,7 @@ exports[`history > singleplayer undo/redo > should iterate through the history w "id53": { "deleted": { "isDeleted": true, + "version": 4, }, "inserted": { "angle": 0, @@ -12970,6 +12938,7 @@ exports[`history > singleplayer undo/redo > should iterate through the history w "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, @@ -13205,12 +13174,14 @@ exports[`history > singleplayer undo/redo > should not clear the redo stack on s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13297,12 +13268,14 @@ exports[`history > singleplayer undo/redo > should not clear the redo stack on s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": 20, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -13716,12 +13689,14 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id5": { @@ -13747,12 +13722,14 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -13977,12 +13954,14 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id11": { @@ -14006,12 +13985,14 @@ exports[`history > singleplayer undo/redo > should not end up with history entry "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -14188,9 +14169,11 @@ exports[`history > singleplayer undo/redo > should not override appstate changes "id29": { "deleted": { "backgroundColor": "#ffc9c9", + "version": 10, }, "inserted": { "backgroundColor": "#a5d8ff", + "version": 9, }, }, }, @@ -14211,9 +14194,11 @@ exports[`history > singleplayer undo/redo > should not override appstate changes "id29": { "deleted": { "backgroundColor": "transparent", + "version": 11, }, "inserted": { "backgroundColor": "#ffc9c9", + "version": 10, }, }, }, @@ -14282,12 +14267,14 @@ exports[`history > singleplayer undo/redo > should not override appstate changes "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -14772,29 +14759,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id243": { "deleted": { "isDeleted": false, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], + "version": 10, }, "inserted": { "isDeleted": true, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], + "version": 7, }, }, }, @@ -14807,9 +14776,19 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 8, }, "inserted": { "boundElements": [], + "version": 5, + }, + }, + "id231": { + "deleted": { + "version": 6, + }, + "inserted": { + "version": 4, }, }, "id232": { @@ -14820,9 +14799,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 7, }, "inserted": { "boundElements": [], + "version": 4, }, }, }, @@ -14865,12 +14846,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id231": { @@ -14902,6 +14885,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "text": "ola", "textAlign": "left", "type": "text", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -14909,6 +14893,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id232": { @@ -14932,12 +14917,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -15010,9 +14997,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "text", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, "id231": { @@ -15020,6 +15009,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": "id230", "height": 25, "textAlign": "center", + "version": 4, "verticalAlign": "middle", "width": 30, "x": -65, @@ -15029,6 +15019,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": null, "height": 100, "textAlign": "left", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -15106,12 +15097,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 6, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -15124,9 +15117,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 4, }, "inserted": { "boundElements": [], + "version": 3, }, }, "id232": { @@ -15137,9 +15132,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, }, @@ -15473,12 +15470,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id213": { @@ -15510,6 +15509,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "text": "ola", "textAlign": "left", "type": "text", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -15517,6 +15517,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id214": { @@ -15540,12 +15541,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -15618,9 +15621,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "text", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, "id213": { @@ -15628,6 +15633,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": "id212", "height": 25, "textAlign": "center", + "version": 4, "verticalAlign": "middle", "width": 30, "x": -65, @@ -15637,6 +15643,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": null, "height": 100, "textAlign": "left", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -15696,7 +15703,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 100, + 98, 0, ], ], @@ -15714,12 +15721,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", - "width": 100, - "x": 0, + "version": 10, + "width": 98, + "x": 1, "y": 0, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -15732,9 +15741,19 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 8, }, "inserted": { "boundElements": [], + "version": 5, + }, + }, + "id213": { + "deleted": { + "version": 8, + }, + "inserted": { + "version": 6, }, }, "id214": { @@ -15745,9 +15764,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 7, }, "inserted": { "boundElements": [], + "version": 4, }, }, }, @@ -16081,12 +16102,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 8, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 7, }, }, "id250": { @@ -16118,6 +16141,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "text": "ola", "textAlign": "left", "type": "text", + "version": 9, "verticalAlign": "top", "width": 100, "x": -200, @@ -16125,6 +16149,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding }, "inserted": { "isDeleted": true, + "version": 8, }, }, "id251": { @@ -16148,12 +16173,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 6, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -16226,9 +16253,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "text", }, ], + "version": 9, }, "inserted": { "boundElements": [], + "version": 8, }, }, "id250": { @@ -16236,6 +16265,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": "id249", "height": 25, "textAlign": "center", + "version": 10, "verticalAlign": "middle", "width": 30, "x": -65, @@ -16245,6 +16275,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": null, "height": 100, "textAlign": "left", + "version": 9, "verticalAlign": "top", "width": 100, "x": -200, @@ -16304,7 +16335,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 100, + 98, 0, ], ], @@ -16322,12 +16353,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", - "width": 100, - "x": 0, + "version": 10, + "width": 98, + "x": 1, "y": 0, }, "inserted": { "isDeleted": true, + "version": 7, }, }, }, @@ -16340,9 +16373,19 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 12, }, "inserted": { "boundElements": [], + "version": 9, + }, + }, + "id250": { + "deleted": { + "version": 12, + }, + "inserted": { + "version": 10, }, }, "id251": { @@ -16353,9 +16396,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 9, }, "inserted": { "boundElements": [], + "version": 6, }, }, }, @@ -16673,51 +16718,45 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id275": { "deleted": { "isDeleted": false, + "version": 8, }, "inserted": { "isDeleted": true, + "version": 5, }, }, "id276": { "deleted": { "isDeleted": false, + "version": 8, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, "updated": { + "id277": { + "deleted": { + "version": 5, + }, + "inserted": { + "version": 3, + }, + }, "id288": { "deleted": { - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": { "elementId": "id275", "focus": 0, "gap": 1, }, + "version": 10, }, "inserted": { - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": null, + "version": 7, }, }, }, @@ -16760,12 +16799,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id276": { @@ -16797,6 +16838,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "text": "ola", "textAlign": "left", "type": "text", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -16804,6 +16846,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id277": { @@ -16827,12 +16870,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -16905,9 +16950,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "text", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, "id276": { @@ -16915,6 +16962,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": "id275", "height": 25, "textAlign": "center", + "version": 4, "verticalAlign": "middle", "width": 30, "x": -65, @@ -16924,6 +16972,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": null, "height": 100, "textAlign": "left", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -17001,12 +17050,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 6, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -17019,9 +17070,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 4, }, "inserted": { "boundElements": [], + "version": 3, }, }, "id277": { @@ -17032,9 +17085,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, }, @@ -17380,25 +17435,31 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "id297": { "deleted": { "isDeleted": false, + "version": 8, }, "inserted": { "isDeleted": true, + "version": 5, }, }, "id298": { "deleted": { "isDeleted": false, + "version": 8, }, "inserted": { "isDeleted": true, + "version": 5, }, }, "id299": { "deleted": { "isDeleted": false, + "version": 5, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -17410,35 +17471,17 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "focus": -0, "gap": 1, }, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": { "elementId": "id297", "focus": 0, "gap": 1, }, + "version": 11, }, "inserted": { "endBinding": null, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], "startBinding": null, + "version": 8, }, }, }, @@ -17481,12 +17524,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": -100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id298": { @@ -17518,6 +17563,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "text": "ola", "textAlign": "left", "type": "text", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -17525,6 +17571,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id299": { @@ -17548,12 +17595,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": -50, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -17626,9 +17675,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "text", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, "id298": { @@ -17636,6 +17687,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": "id297", "height": 25, "textAlign": "center", + "version": 4, "verticalAlign": "middle", "width": 30, "x": -65, @@ -17645,6 +17697,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "containerId": null, "height": 100, "textAlign": "left", + "version": 2, "verticalAlign": "top", "width": 100, "x": -200, @@ -17722,12 +17775,14 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 6, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -17740,9 +17795,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 4, }, "inserted": { "boundElements": [], + "version": 3, }, }, "id299": { @@ -17753,9 +17810,11 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", }, ], + "version": 3, }, "inserted": { "boundElements": [], + "version": 2, }, }, }, @@ -18061,12 +18120,14 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -18113,12 +18174,14 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -18165,12 +18228,14 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 40, "y": 40, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -18192,9 +18257,11 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "id195": { "deleted": { "index": "a0V", + "version": 6, }, "inserted": { "index": "a2", + "version": 5, }, }, }, @@ -18257,17 +18324,21 @@ exports[`history > singleplayer undo/redo > should support changes in elements' "id189": { "deleted": { "index": "a2", + "version": 7, }, "inserted": { "index": "Zz", + "version": 6, }, }, "id195": { "deleted": { "index": "a3", + "version": 10, }, "inserted": { "index": "a0", + "version": 9, }, }, }, @@ -18640,12 +18711,14 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id161": { @@ -18671,12 +18744,14 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 100, "x": 100, "y": 100, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -18733,12 +18808,14 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 4, "width": 100, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, "id186": { @@ -18764,12 +18841,14 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 4, "width": 100, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -19029,12 +19108,14 @@ exports[`history > singleplayer undo/redo > should support element creation, del "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -19081,12 +19162,14 @@ exports[`history > singleplayer undo/redo > should support element creation, del "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -19133,12 +19216,14 @@ exports[`history > singleplayer undo/redo > should support element creation, del "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 40, "y": 40, }, "inserted": { "isDeleted": true, + "version": 6, }, }, }, @@ -19207,17 +19292,21 @@ exports[`history > singleplayer undo/redo > should support element creation, del "id93": { "deleted": { "isDeleted": true, + "version": 8, }, "inserted": { "isDeleted": false, + "version": 7, }, }, "id96": { "deleted": { "isDeleted": true, + "version": 8, }, "inserted": { "isDeleted": false, + "version": 7, }, }, }, @@ -19466,12 +19555,14 @@ exports[`history > singleplayer undo/redo > should support linear element creati "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 13, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 12, }, }, }, @@ -19510,6 +19601,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati 0, ], ], + "version": 14, "width": 20, }, "inserted": { @@ -19527,6 +19619,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati 10, ], ], + "version": 13, "width": 10, }, }, @@ -19598,6 +19691,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati 20, ], ], + "version": 15, }, "inserted": { "height": 10, @@ -19615,6 +19709,7 @@ exports[`history > singleplayer undo/redo > should support linear element creati 0, ], ], + "version": 14, }, }, }, diff --git a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap index ddbb76c273..52614ed5f4 100644 --- a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap @@ -25,7 +25,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 5`] = ` "type": "rectangle", "updated": 1, "version": 5, - "versionNonce": 1505387817, + "versionNonce": 23633383, "width": 30, "x": 30, "y": 20, @@ -50,14 +50,14 @@ exports[`duplicate element on move when ALT is clicked > rectangle 6`] = ` "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1604849351, + "seed": 1505387817, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 7, - "versionNonce": 915032327, + "versionNonce": 81784553, "width": 30, "x": -10, "y": 60, @@ -89,7 +89,7 @@ exports[`move element > rectangle 5`] = ` "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 1116226695, + "versionNonce": 1014066025, "width": 30, "x": 0, "y": 40, @@ -126,7 +126,7 @@ exports[`move element > rectangles with binding arrow 5`] = ` "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 1723083209, + "versionNonce": 1006504105, "width": 100, "x": 0, "y": 0, @@ -156,14 +156,14 @@ exports[`move element > rectangles with binding arrow 6`] = ` "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1150084233, + "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", "updated": 1, "version": 7, - "versionNonce": 1051383431, + "versionNonce": 1984422985, "width": 300, "x": 201, "y": 2, @@ -208,7 +208,7 @@ exports[`move element > rectangles with binding arrow 7`] = ` "roundness": { "type": 2, }, - "seed": 1604849351, + "seed": 23633383, "startArrowhead": null, "startBinding": { "elementId": "id0", @@ -221,7 +221,7 @@ exports[`move element > rectangles with binding arrow 7`] = ` "type": "arrow", "updated": 1, "version": 11, - "versionNonce": 1996028265, + "versionNonce": 1573789895, "width": "81.00000", "x": "110.00000", "y": 50, diff --git a/packages/excalidraw/tests/__snapshots__/multiPointCreate.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/multiPointCreate.test.tsx.snap index 1b0092757a..ee3f024903 100644 --- a/packages/excalidraw/tests/__snapshots__/multiPointCreate.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/multiPointCreate.test.tsx.snap @@ -50,7 +50,7 @@ exports[`multi point mode in linear elements > arrow 3`] = ` "type": "arrow", "updated": 1, "version": 8, - "versionNonce": 400692809, + "versionNonce": 1604849351, "width": 70, "x": 30, "y": 30, @@ -105,7 +105,7 @@ exports[`multi point mode in linear elements > line 3`] = ` "type": "line", "updated": 1, "version": 8, - "versionNonce": 400692809, + "versionNonce": 1604849351, "width": 70, "x": 30, "y": 30, diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap index 60fe9249fc..d90fe92914 100644 --- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap @@ -169,12 +169,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -221,12 +223,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 30, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -273,12 +277,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 60, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -351,10 +357,12 @@ exports[`given element A and group of elements B and given both are selected whe "id15", ], "index": "a2", + "version": 5, }, "inserted": { "groupIds": [], "index": "a0", + "version": 3, }, }, "id6": { @@ -363,10 +371,12 @@ exports[`given element A and group of elements B and given both are selected whe "id15", ], "index": "a3", + "version": 5, }, "inserted": { "groupIds": [], "index": "a2", + "version": 3, }, }, }, @@ -587,12 +597,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -639,12 +651,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -691,12 +705,14 @@ exports[`given element A and group of elements B and given both are selected whe "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 100, "x": 220, "y": 220, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -747,10 +763,12 @@ exports[`given element A and group of elements B and given both are selected whe "id12", ], "index": "a2", + "version": 5, }, "inserted": { "groupIds": [], "index": "a0", + "version": 3, }, }, "id6": { @@ -759,10 +777,12 @@ exports[`given element A and group of elements B and given both are selected whe "id12", ], "index": "a3", + "version": 5, }, "inserted": { "groupIds": [], "index": "a2", + "version": 3, }, }, }, @@ -984,12 +1004,14 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1036,12 +1058,14 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1113,9 +1137,11 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -1123,9 +1149,11 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -1199,12 +1227,14 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 60, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1284,11 +1314,13 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "id12", "id28", ], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, "id19": { @@ -1296,9 +1328,11 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "groupIds": [ "id28", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -1307,11 +1341,13 @@ exports[`regression tests > Cmd/Ctrl-click exclusively select element under poin "id12", "id28", ], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, }, @@ -1534,12 +1570,14 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1560,10 +1598,12 @@ exports[`regression tests > Drags selected element when hitting only bounding bo "updated": { "id0": { "deleted": { + "version": 4, "x": 25, "y": 25, }, "inserted": { + "version": 3, "x": 0, "y": 0, }, @@ -1742,12 +1782,14 @@ exports[`regression tests > adjusts z order when grouping > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1794,12 +1836,14 @@ exports[`regression tests > adjusts z order when grouping > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1846,12 +1890,14 @@ exports[`regression tests > adjusts z order when grouping > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -1924,10 +1970,12 @@ exports[`regression tests > adjusts z order when grouping > [end of test] undo s "id15", ], "index": "a2", + "version": 5, }, "inserted": { "groupIds": [], "index": "a0", + "version": 3, }, }, "id6": { @@ -1936,10 +1984,12 @@ exports[`regression tests > adjusts z order when grouping > [end of test] undo s "id15", ], "index": "a3", + "version": 5, }, "inserted": { "groupIds": [], "index": "a2", + "version": 3, }, }, }, @@ -2113,12 +2163,14 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] undo "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2165,16 +2217,27 @@ exports[`regression tests > alt-drag duplicates an element > [end of test] undo "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 6, "width": 10, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 5, + }, + }, + }, + "updated": { + "id0": { + "deleted": { + "version": 5, + }, + "inserted": { + "version": 3, }, }, }, - "updated": {}, }, "id": "id6", }, @@ -2343,12 +2406,14 @@ exports[`regression tests > arrow keys > [end of test] undo stack 1`] = ` "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2523,12 +2588,14 @@ exports[`regression tests > can drag element that covers another element, while "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 200, "x": 100, "y": 100, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2575,12 +2642,14 @@ exports[`regression tests > can drag element that covers another element, while "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 200, "x": 100, "y": 100, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2627,12 +2696,14 @@ exports[`regression tests > can drag element that covers another element, while "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 350, "x": 300, "y": 300, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2661,10 +2732,12 @@ exports[`regression tests > can drag element that covers another element, while "updated": { "id3": { "deleted": { + "version": 4, "x": 300, "y": 300, }, "inserted": { + "version": 3, "x": 100, "y": 100, }, @@ -2838,12 +2911,14 @@ exports[`regression tests > change the properties of a shape > [end of test] und "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -2865,9 +2940,11 @@ exports[`regression tests > change the properties of a shape > [end of test] und "id0": { "deleted": { "backgroundColor": "#ffec99", + "version": 4, }, "inserted": { "backgroundColor": "transparent", + "version": 3, }, }, }, @@ -2888,9 +2965,11 @@ exports[`regression tests > change the properties of a shape > [end of test] und "id0": { "deleted": { "backgroundColor": "#ffc9c9", + "version": 5, }, "inserted": { "backgroundColor": "#ffec99", + "version": 4, }, }, }, @@ -2911,9 +2990,11 @@ exports[`regression tests > change the properties of a shape > [end of test] und "id0": { "deleted": { "strokeColor": "#1971c2", + "version": 6, }, "inserted": { "strokeColor": "#1e1e1e", + "version": 5, }, }, }, @@ -3067,7 +3148,7 @@ exports[`regression tests > click on an element and drag it > [dragged] element "type": "rectangle", "updated": 1, "version": 4, - "versionNonce": 1116226695, + "versionNonce": 1014066025, "width": 10, "x": 20, "y": 20, @@ -3119,12 +3200,14 @@ exports[`regression tests > click on an element and drag it > [dragged] undo sta "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3145,10 +3228,12 @@ exports[`regression tests > click on an element and drag it > [dragged] undo sta "updated": { "id0": { "deleted": { + "version": 4, "x": 20, "y": 20, }, "inserted": { + "version": 3, "x": 10, "y": 10, }, @@ -3324,12 +3409,14 @@ exports[`regression tests > click on an element and drag it > [end of test] undo "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3350,10 +3437,12 @@ exports[`regression tests > click on an element and drag it > [end of test] undo "updated": { "id0": { "deleted": { + "version": 4, "x": 20, "y": 20, }, "inserted": { + "version": 3, "x": 10, "y": 10, }, @@ -3375,10 +3464,12 @@ exports[`regression tests > click on an element and drag it > [end of test] undo "updated": { "id0": { "deleted": { + "version": 5, "x": 10, "y": 10, }, "inserted": { + "version": 4, "x": 20, "y": 20, }, @@ -3554,12 +3645,14 @@ exports[`regression tests > click to select a shape > [end of test] undo stack 1 "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3606,12 +3699,14 @@ exports[`regression tests > click to select a shape > [end of test] undo stack 1 "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3809,12 +3904,14 @@ exports[`regression tests > click-drag to select a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3861,12 +3958,14 @@ exports[`regression tests > click-drag to select a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -3913,12 +4012,14 @@ exports[`regression tests > click-drag to select a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4116,12 +4217,14 @@ exports[`regression tests > deleting last but one element in editing group shoul "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4168,12 +4271,14 @@ exports[`regression tests > deleting last but one element in editing group shoul "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4245,9 +4350,11 @@ exports[`regression tests > deleting last but one element in editing group shoul "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -4255,9 +4362,11 @@ exports[`regression tests > deleting last but one element in editing group shoul "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -4316,9 +4425,11 @@ exports[`regression tests > deleting last but one element in editing group shoul "id0": { "deleted": { "isDeleted": true, + "version": 5, }, "inserted": { "isDeleted": false, + "version": 4, }, }, }, @@ -4489,7 +4600,7 @@ exports[`regression tests > deselects group of selected elements on pointer down "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1505387817, + "seed": 493213705, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, @@ -4569,12 +4680,14 @@ exports[`regression tests > deselects group of selected elements on pointer down "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4621,12 +4734,14 @@ exports[`regression tests > deselects group of selected elements on pointer down "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4820,12 +4935,14 @@ exports[`regression tests > deselects group of selected elements on pointer up w "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -4872,12 +4989,14 @@ exports[`regression tests > deselects group of selected elements on pointer up w "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5039,7 +5158,7 @@ exports[`regression tests > deselects selected element on pointer down when poin "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1150084233, + "seed": 1116226695, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, @@ -5119,12 +5238,14 @@ exports[`regression tests > deselects selected element on pointer down when poin "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5297,12 +5418,14 @@ exports[`regression tests > deselects selected element, on pointer up, when clic "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 100, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5495,12 +5618,14 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5547,12 +5672,14 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5599,12 +5726,14 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5655,9 +5784,11 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -5665,9 +5796,11 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id6": { @@ -5675,9 +5808,11 @@ exports[`regression tests > double click to edit a group > [end of test] undo st "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -5880,12 +6015,14 @@ exports[`regression tests > drags selected elements from point inside common bou "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5932,12 +6069,14 @@ exports[`regression tests > drags selected elements from point inside common bou "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -5978,20 +6117,24 @@ exports[`regression tests > drags selected elements from point inside common bou "updated": { "id0": { "deleted": { + "version": 4, "x": 25, "y": 25, }, "inserted": { + "version": 3, "x": 0, "y": 0, }, }, "id3": { "deleted": { + "version": 4, "x": 135, "y": 135, }, "inserted": { + "version": 3, "x": 110, "y": 110, }, @@ -6163,12 +6306,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 10, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6215,12 +6360,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 20, "x": 40, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6267,12 +6414,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 20, "x": 70, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -6339,12 +6488,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 4, "width": 50, "x": 130, "y": -10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -6409,12 +6560,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "line", + "version": 4, "width": 50, "x": 220, "y": -10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -6484,12 +6637,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 6, "width": 50, "x": 310, "y": -10, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -6529,6 +6684,7 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack 20, ], ], + "version": 8, "width": 80, }, "inserted": { @@ -6547,6 +6703,7 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack 10, ], ], + "version": 6, "width": 50, }, }, @@ -6632,12 +6789,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "line", + "version": 6, "width": 50, "x": 430, "y": -10, }, "inserted": { "isDeleted": true, + "version": 5, }, }, }, @@ -6677,6 +6836,7 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack 20, ], ], + "version": 8, "width": 80, }, "inserted": { @@ -6695,6 +6855,7 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack 10, ], ], + "version": 6, "width": 50, }, }, @@ -6799,12 +6960,14 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "freedraw", + "version": 4, "width": 50, "x": 550, "y": -10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -6980,12 +7143,14 @@ exports[`regression tests > given a group of selected elements with an element t "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7032,12 +7197,14 @@ exports[`regression tests > given a group of selected elements with an element t "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 100, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7084,12 +7251,14 @@ exports[`regression tests > given a group of selected elements with an element t "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 100, "x": 310, "y": 310, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7308,12 +7477,14 @@ exports[`regression tests > given a selected element A and a not selected elemen "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 1000, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7360,12 +7531,14 @@ exports[`regression tests > given a selected element A and a not selected elemen "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 1000, "x": 500, "y": 500, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -7582,12 +7755,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 1000, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id1": { @@ -7611,12 +7786,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 500, "x": 500, "y": 500, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -7813,12 +7990,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 1000, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 1, }, }, "id1": { @@ -7842,12 +8021,14 @@ exports[`regression tests > given selected element A with lower z-index than uns "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 2, "width": 500, "x": 500, "y": 500, }, "inserted": { "isDeleted": true, + "version": 1, }, }, }, @@ -7868,10 +8049,12 @@ exports[`regression tests > given selected element A with lower z-index than uns "updated": { "id0": { "deleted": { + "version": 3, "x": 100, "y": 100, }, "inserted": { + "version": 2, "x": 0, "y": 0, }, @@ -8045,12 +8228,14 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -8223,12 +8408,14 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] undo stac "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -8401,12 +8588,14 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] undo stac "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -8625,12 +8814,14 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -8847,12 +9038,14 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1 "strokeStyle": "solid", "strokeWidth": 2, "type": "line", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -9041,12 +9234,14 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] undo sta "strokeStyle": "solid", "strokeWidth": 2, "type": "freedraw", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -9265,12 +9460,14 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -9443,12 +9640,14 @@ exports[`regression tests > key d selects diamond tool > [end of test] undo stac "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -9665,12 +9864,14 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1 "strokeStyle": "solid", "strokeWidth": 2, "type": "line", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -9843,12 +10044,14 @@ exports[`regression tests > key o selects ellipse tool > [end of test] undo stac "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10037,12 +10240,14 @@ exports[`regression tests > key p selects freedraw tool > [end of test] undo sta "strokeStyle": "solid", "strokeWidth": 2, "type": "freedraw", + "version": 4, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 3, }, }, }, @@ -10215,12 +10420,14 @@ exports[`regression tests > key r selects rectangle tool > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10401,12 +10608,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10453,12 +10662,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10505,12 +10716,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10561,9 +10774,11 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -10571,9 +10786,11 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id6": { @@ -10581,9 +10798,11 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -10641,12 +10860,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 6, }, }, "id18": { @@ -10672,12 +10893,14 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 40, "y": 20, }, "inserted": { "isDeleted": true, + "version": 6, }, }, "id19": { @@ -10703,16 +10926,43 @@ exports[`regression tests > make a group and duplicate it > [end of test] undo s "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 7, "width": 10, "x": 60, "y": 20, }, "inserted": { "isDeleted": true, + "version": 6, + }, + }, + }, + "updated": { + "id0": { + "deleted": { + "version": 6, + }, + "inserted": { + "version": 4, + }, + }, + "id3": { + "deleted": { + "version": 6, + }, + "inserted": { + "version": 4, + }, + }, + "id6": { + "deleted": { + "version": 6, + }, + "inserted": { + "version": 4, }, }, }, - "updated": {}, }, "id": "id21", }, @@ -10883,12 +11133,14 @@ exports[`regression tests > noop interaction after undo shouldn't create history "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -10935,12 +11187,14 @@ exports[`regression tests > noop interaction after undo shouldn't create history "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, @@ -11280,12 +11534,14 @@ exports[`regression tests > shift click on selected element should deselect it o "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11482,12 +11738,14 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11534,12 +11792,14 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11602,20 +11862,24 @@ exports[`regression tests > shift-click to multiselect, then drag > [end of test "updated": { "id0": { "deleted": { + "version": 4, "x": 20, "y": 20, }, "inserted": { + "version": 3, "x": 10, "y": 10, }, }, "id3": { "deleted": { + "version": 4, "x": 40, "y": 20, }, "inserted": { + "version": 3, "x": 30, "y": 10, }, @@ -11795,12 +12059,14 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11847,12 +12113,14 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 30, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11899,12 +12167,14 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -11955,9 +12225,11 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -11965,9 +12237,11 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id6": { @@ -11975,9 +12249,11 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -12004,31 +12280,37 @@ exports[`regression tests > should group elements and ungroup them > [end of tes "id0": { "deleted": { "groupIds": [], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, "id3": { "deleted": { "groupIds": [], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, "id6": { "deleted": { "groupIds": [], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, }, @@ -12210,12 +12492,14 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12262,12 +12546,14 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12339,9 +12625,11 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -12349,9 +12637,11 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "groupIds": [ "id12", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -12402,12 +12692,14 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 10, "y": 50, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12454,12 +12746,14 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 50, "y": 50, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12531,9 +12825,11 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "groupIds": [ "id27", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id18": { @@ -12541,9 +12837,11 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "groupIds": [ "id27", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -12601,11 +12899,13 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "id12", "id32", ], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, "id15": { @@ -12614,11 +12914,13 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "id27", "id32", ], + "version": 5, }, "inserted": { "groupIds": [ "id27", ], + "version": 4, }, }, "id18": { @@ -12627,11 +12929,13 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "id27", "id32", ], + "version": 5, }, "inserted": { "groupIds": [ "id27", ], + "version": 4, }, }, "id3": { @@ -12640,11 +12944,13 @@ exports[`regression tests > single-clicking on a subgroup of a selected group sh "id12", "id32", ], + "version": 5, }, "inserted": { "groupIds": [ "id12", ], + "version": 4, }, }, }, @@ -12944,12 +13250,14 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 50, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -12996,12 +13304,14 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 50, "x": 100, "y": 100, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13048,12 +13358,14 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 50, "x": 200, "y": 200, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13104,9 +13416,11 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id3": { @@ -13114,9 +13428,11 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, "id6": { @@ -13124,9 +13440,11 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "groupIds": [ "id11", ], + "version": 4, }, "inserted": { "groupIds": [], + "version": 3, }, }, }, @@ -13204,12 +13522,14 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "id11", ], "index": "a2", + "version": 6, }, "inserted": { "groupIds": [ "id11", ], "index": "a0", + "version": 4, }, }, "id6": { @@ -13219,12 +13539,14 @@ exports[`regression tests > supports nested groups > [end of test] undo stack 1` "id11", ], "index": "a3", + "version": 6, }, "inserted": { "groupIds": [ "id11", ], "index": "a2", + "version": 4, }, }, }, @@ -13508,7 +13830,7 @@ exports[`regression tests > switches from group of selected elements to another "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1723083209, + "seed": 289600103, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, @@ -13588,12 +13910,14 @@ exports[`regression tests > switches from group of selected elements to another "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13640,12 +13964,14 @@ exports[`regression tests > switches from group of selected elements to another "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 100, "x": 110, "y": 110, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13692,12 +14018,14 @@ exports[`regression tests > switches from group of selected elements to another "strokeStyle": "solid", "strokeWidth": 2, "type": "diamond", + "version": 3, "width": 100, "x": 310, "y": 310, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13840,7 +14168,7 @@ exports[`regression tests > switches selected element on pointer down > [end of "opacity": 100, "roughness": 1, "roundness": null, - "seed": 1604849351, + "seed": 23633383, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, @@ -13920,12 +14248,14 @@ exports[`regression tests > switches selected element on pointer down > [end of "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 10, "x": 0, "y": 0, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -13972,12 +14302,14 @@ exports[`regression tests > switches selected element on pointer down > [end of "strokeStyle": "solid", "strokeWidth": 2, "type": "ellipse", + "version": 3, "width": 10, "x": 20, "y": 20, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -14280,6 +14612,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st 10, ], ], + "version": 9, "width": 60, }, "inserted": { @@ -14302,6 +14635,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st 20, ], ], + "version": 8, "width": 100, }, }, @@ -14329,6 +14663,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st "id6": { "deleted": { "isDeleted": true, + "version": 10, }, "inserted": { "angle": 0, @@ -14371,6 +14706,7 @@ exports[`regression tests > undo/redo drawing an element > [end of test] redo st "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", + "version": 9, "width": 60, "x": 130, "y": 10, @@ -14424,12 +14760,14 @@ exports[`regression tests > undo/redo drawing an element > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 3, "width": 20, "x": 10, "y": -10, }, "inserted": { "isDeleted": true, + "version": 2, }, }, }, @@ -14476,12 +14814,14 @@ exports[`regression tests > undo/redo drawing an element > [end of test] undo st "strokeStyle": "solid", "strokeWidth": 2, "type": "rectangle", + "version": 5, "width": 30, "x": 40, "y": 0, }, "inserted": { "isDeleted": true, + "version": 4, }, }, }, diff --git a/packages/excalidraw/tests/test-utils.ts b/packages/excalidraw/tests/test-utils.ts index 56af57c80b..78e11d1821 100644 --- a/packages/excalidraw/tests/test-utils.ts +++ b/packages/excalidraw/tests/test-utils.ts @@ -431,12 +431,17 @@ export const assertElements = >( expect(h.state.selectedElementIds).toEqual(selectedElementIds); }; -const stripSeed = (deltas: Record) => +const stripProps = ( + deltas: Record, + props: string[], +) => Object.entries(deltas).reduce((acc, curr) => { const { inserted, deleted, ...rest } = curr[1]; - delete inserted.seed; - delete deleted.seed; + for (const prop of props) { + delete inserted[prop]; + delete deleted[prop]; + } acc[curr[0]] = { inserted, @@ -453,9 +458,9 @@ export const checkpointHistory = (history: History, name: string) => { ...x, elements: { ...x.elements, - added: stripSeed(x.elements.added), - removed: stripSeed(x.elements.removed), - updated: stripSeed(x.elements.updated), + added: stripProps(x.elements.added, ["seed", "versionNonce"]), + removed: stripProps(x.elements.removed, ["seed", "versionNonce"]), + updated: stripProps(x.elements.updated, ["seed", "versionNonce"]), }, })), ).toMatchSnapshot(`[${name}] undo stack`); @@ -465,9 +470,9 @@ export const checkpointHistory = (history: History, name: string) => { ...x, elements: { ...x.elements, - added: stripSeed(x.elements.added), - removed: stripSeed(x.elements.removed), - updated: stripSeed(x.elements.updated), + added: stripProps(x.elements.added, ["seed", "versionNonce"]), + removed: stripProps(x.elements.removed, ["seed", "versionNonce"]), + updated: stripProps(x.elements.updated, ["seed", "versionNonce"]), }, })), ).toMatchSnapshot(`[${name}] redo stack`); diff --git a/packages/excalidraw/types.ts b/packages/excalidraw/types.ts index 6f3fd0efa8..e0f908177b 100644 --- a/packages/excalidraw/types.ts +++ b/packages/excalidraw/types.ts @@ -813,6 +813,9 @@ export interface ExcalidrawImperativeAPI { getSceneElementsIncludingDeleted: InstanceType< typeof App >["getSceneElementsIncludingDeleted"]; + getSceneElementsMapIncludingDeleted: InstanceType< + typeof App + >["getSceneElementsMapIncludingDeleted"]; history: { clear: InstanceType["resetHistory"]; };