@tsdraw/core 0.5.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +158 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +158 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -74,6 +74,11 @@ interface TsdrawEditorSnapshot {
|
|
|
74
74
|
document: TsdrawDocumentSnapshot;
|
|
75
75
|
state: TsdrawSessionStateSnapshot;
|
|
76
76
|
}
|
|
77
|
+
interface TsdrawHistorySnapshot {
|
|
78
|
+
version: 1;
|
|
79
|
+
undoStack: TsdrawDocumentSnapshot[];
|
|
80
|
+
redoStack: TsdrawDocumentSnapshot[];
|
|
81
|
+
}
|
|
77
82
|
interface DocumentStoreSnapshot {
|
|
78
83
|
page: PageState;
|
|
79
84
|
order: ShapeId[];
|
|
@@ -289,7 +294,16 @@ declare class Editor {
|
|
|
289
294
|
private drawStyle;
|
|
290
295
|
private readonly toolStateContext;
|
|
291
296
|
private readonly listeners;
|
|
297
|
+
private readonly historyListeners;
|
|
298
|
+
private undoStack;
|
|
299
|
+
private redoStack;
|
|
300
|
+
private lastDocumentSnapshot;
|
|
301
|
+
private suppressHistoryCapture;
|
|
302
|
+
private historyBatchDepth;
|
|
303
|
+
private historyBatchStartSnapshot;
|
|
304
|
+
private historyBatchChanged;
|
|
292
305
|
constructor(opts?: EditorOptions);
|
|
306
|
+
private captureDocumentHistory;
|
|
293
307
|
registerToolDefinition(toolDefinition: ToolDefinition): void;
|
|
294
308
|
private getDefaultToolDefinitions;
|
|
295
309
|
createShapeId(): ShapeId;
|
|
@@ -331,13 +345,25 @@ declare class Editor {
|
|
|
331
345
|
selectedShapeIds?: ShapeId[];
|
|
332
346
|
}): TsdrawEditorSnapshot;
|
|
333
347
|
loadPersistenceSnapshot(snapshot: Partial<TsdrawEditorSnapshot>): ShapeId[];
|
|
348
|
+
getHistorySnapshot(): TsdrawHistorySnapshot;
|
|
349
|
+
loadHistorySnapshot(snapshot: TsdrawHistorySnapshot | null | undefined): void;
|
|
350
|
+
clearRedoHistory(): void;
|
|
351
|
+
beginHistoryEntry(): void;
|
|
352
|
+
endHistoryEntry(): void;
|
|
353
|
+
canUndo(): boolean;
|
|
354
|
+
canRedo(): boolean;
|
|
355
|
+
undo(): boolean;
|
|
356
|
+
redo(): boolean;
|
|
334
357
|
listen(listener: EditorListener): () => void;
|
|
358
|
+
listenHistory(listener: EditorListener): () => void;
|
|
335
359
|
screenToPage(screenX: number, screenY: number): {
|
|
336
360
|
x: number;
|
|
337
361
|
y: number;
|
|
338
362
|
};
|
|
339
363
|
render(ctx: CanvasRenderingContext2D): void;
|
|
340
364
|
private emitChange;
|
|
365
|
+
private emitHistoryChange;
|
|
366
|
+
private runWithoutHistoryCapture;
|
|
341
367
|
}
|
|
342
368
|
|
|
343
369
|
declare class PenIdleState extends StateNode {
|
|
@@ -524,4 +550,4 @@ declare function decodePathToPoints(segments: {
|
|
|
524
550
|
y: number;
|
|
525
551
|
}[];
|
|
526
552
|
|
|
527
|
-
export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
|
|
553
|
+
export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
|
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,11 @@ interface TsdrawEditorSnapshot {
|
|
|
74
74
|
document: TsdrawDocumentSnapshot;
|
|
75
75
|
state: TsdrawSessionStateSnapshot;
|
|
76
76
|
}
|
|
77
|
+
interface TsdrawHistorySnapshot {
|
|
78
|
+
version: 1;
|
|
79
|
+
undoStack: TsdrawDocumentSnapshot[];
|
|
80
|
+
redoStack: TsdrawDocumentSnapshot[];
|
|
81
|
+
}
|
|
77
82
|
interface DocumentStoreSnapshot {
|
|
78
83
|
page: PageState;
|
|
79
84
|
order: ShapeId[];
|
|
@@ -289,7 +294,16 @@ declare class Editor {
|
|
|
289
294
|
private drawStyle;
|
|
290
295
|
private readonly toolStateContext;
|
|
291
296
|
private readonly listeners;
|
|
297
|
+
private readonly historyListeners;
|
|
298
|
+
private undoStack;
|
|
299
|
+
private redoStack;
|
|
300
|
+
private lastDocumentSnapshot;
|
|
301
|
+
private suppressHistoryCapture;
|
|
302
|
+
private historyBatchDepth;
|
|
303
|
+
private historyBatchStartSnapshot;
|
|
304
|
+
private historyBatchChanged;
|
|
292
305
|
constructor(opts?: EditorOptions);
|
|
306
|
+
private captureDocumentHistory;
|
|
293
307
|
registerToolDefinition(toolDefinition: ToolDefinition): void;
|
|
294
308
|
private getDefaultToolDefinitions;
|
|
295
309
|
createShapeId(): ShapeId;
|
|
@@ -331,13 +345,25 @@ declare class Editor {
|
|
|
331
345
|
selectedShapeIds?: ShapeId[];
|
|
332
346
|
}): TsdrawEditorSnapshot;
|
|
333
347
|
loadPersistenceSnapshot(snapshot: Partial<TsdrawEditorSnapshot>): ShapeId[];
|
|
348
|
+
getHistorySnapshot(): TsdrawHistorySnapshot;
|
|
349
|
+
loadHistorySnapshot(snapshot: TsdrawHistorySnapshot | null | undefined): void;
|
|
350
|
+
clearRedoHistory(): void;
|
|
351
|
+
beginHistoryEntry(): void;
|
|
352
|
+
endHistoryEntry(): void;
|
|
353
|
+
canUndo(): boolean;
|
|
354
|
+
canRedo(): boolean;
|
|
355
|
+
undo(): boolean;
|
|
356
|
+
redo(): boolean;
|
|
334
357
|
listen(listener: EditorListener): () => void;
|
|
358
|
+
listenHistory(listener: EditorListener): () => void;
|
|
335
359
|
screenToPage(screenX: number, screenY: number): {
|
|
336
360
|
x: number;
|
|
337
361
|
y: number;
|
|
338
362
|
};
|
|
339
363
|
render(ctx: CanvasRenderingContext2D): void;
|
|
340
364
|
private emitChange;
|
|
365
|
+
private emitHistoryChange;
|
|
366
|
+
private runWithoutHistoryCapture;
|
|
341
367
|
}
|
|
342
368
|
|
|
343
369
|
declare class PenIdleState extends StateNode {
|
|
@@ -524,4 +550,4 @@ declare function decodePathToPoints(segments: {
|
|
|
524
550
|
y: number;
|
|
525
551
|
}[];
|
|
526
552
|
|
|
527
|
-
export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
|
|
553
|
+
export { type Bounds, CanvasRenderer, type ColorStyle, DEFAULT_COLORS, DRAG_DISTANCE_SQUARED, type DashStyle, type DefaultToolId, DocumentStore, type DocumentStoreSnapshot, type DrawSegment, type DrawShape, ERASER_MARGIN, Editor, type EditorOptions, EraserErasingState, EraserIdleState, EraserPointingState, HandDraggingState, HandIdleState, type ICanvasRenderer, type IEditor, InputManager, MAX_POINTS_PER_SHAPE, type PageState, PenDrawingState, PenIdleState, type PointerInput, type ResizeHandle, STROKE_WIDTHS, type SegmentType, SelectIdleState, type SelectionBounds, type Shape, type ShapeId, type SizeStyle, StateNode, type StateNodeConstructor, type ToolDefinition, type ToolId, type ToolKeyInfo, ToolManager, type ToolPointerDownInfo, type ToolPointerMoveInfo, type ToolStateContext, type ToolStateTransitionInfo, type TransformSnapshot, type TsdrawDocumentSnapshot, type TsdrawEditorSnapshot, type TsdrawHistorySnapshot, type TsdrawPageRecord, type TsdrawPersistedRecord, type TsdrawRenderTheme, type TsdrawSessionStateSnapshot, type TsdrawShapeRecord, type Vec3, type Viewport, applyMove, applyResize, applyRotation, boundsContainPoint, boundsIntersect, boundsOf, buildStartPositions, buildTransformSnapshots, closestOnSegment, createViewport, decodeFirstPoint, decodeLastPoint, decodePathToPoints, decodePoints, distance, documentSnapshotToRecords, encodePoints, getSelectionBoundsPage, getShapeBounds, getShapesInBounds, getTopShapeAtPoint, isSelectTool, minDistanceToPolyline, normalizeSelectionBounds, padBounds, pageToScreen, panViewport, pointHitsShape, recordsToDocumentSnapshot, resolveThemeColor, rotatePoint, screenToPage, segmentHitsShape, segmentTouchesPolyline, setViewport, shapePagePoints, sqDistance, zoomViewport };
|
package/dist/index.js
CHANGED
|
@@ -148,6 +148,12 @@ var DocumentStore = class {
|
|
|
148
148
|
loadSnapshot(snapshot) {
|
|
149
149
|
const pageState = cloneValue(snapshot.page);
|
|
150
150
|
const normalizedOrder = [...snapshot.order].filter((shapeId) => pageState.shapes[shapeId] != null);
|
|
151
|
+
const orderedSet = new Set(normalizedOrder);
|
|
152
|
+
for (const shapeId of Object.keys(pageState.shapes)) {
|
|
153
|
+
if (!orderedSet.has(shapeId)) {
|
|
154
|
+
normalizedOrder.push(shapeId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
151
157
|
this.state = {
|
|
152
158
|
id: pageState.id,
|
|
153
159
|
shapes: pageState.shapes,
|
|
@@ -863,13 +869,14 @@ var PenDrawingState = class extends StateNode {
|
|
|
863
869
|
type: "straight",
|
|
864
870
|
path: encodePoints([prevEnd, { ...anchorPt, z: pressure }])
|
|
865
871
|
};
|
|
872
|
+
const withStraightSeg = [...segments, seg];
|
|
866
873
|
this.editor.updateShapes([
|
|
867
874
|
{
|
|
868
875
|
id,
|
|
869
876
|
type: "draw",
|
|
870
877
|
props: {
|
|
871
|
-
segments:
|
|
872
|
-
isClosed: this.detectClosure(
|
|
878
|
+
segments: withStraightSeg,
|
|
879
|
+
isClosed: this.detectClosure(withStraightSeg, size, scale)
|
|
873
880
|
}
|
|
874
881
|
}
|
|
875
882
|
]);
|
|
@@ -945,7 +952,7 @@ var PenDrawingState = class extends StateNode {
|
|
|
945
952
|
type: "draw",
|
|
946
953
|
props: {
|
|
947
954
|
segments: updated,
|
|
948
|
-
isClosed: this.detectClosure(
|
|
955
|
+
isClosed: this.detectClosure(updated, size, scale)
|
|
949
956
|
}
|
|
950
957
|
}
|
|
951
958
|
]);
|
|
@@ -1308,10 +1315,26 @@ function recordsToDocumentSnapshot(records) {
|
|
|
1308
1315
|
// src/editor/Editor.ts
|
|
1309
1316
|
var shapeIdCounter = 0;
|
|
1310
1317
|
var shapeIdRuntimeSeed = Math.random().toString(36).slice(2, 8);
|
|
1318
|
+
var MAX_HISTORY_ENTRIES = 100;
|
|
1311
1319
|
function createShapeId() {
|
|
1312
1320
|
shapeIdCounter += 1;
|
|
1313
1321
|
return `shape:${Date.now().toString(36)}-${shapeIdRuntimeSeed}-${shapeIdCounter.toString(36)}`;
|
|
1314
1322
|
}
|
|
1323
|
+
function cloneDocumentSnapshot(snapshot) {
|
|
1324
|
+
if (typeof structuredClone === "function") {
|
|
1325
|
+
return structuredClone(snapshot);
|
|
1326
|
+
}
|
|
1327
|
+
return JSON.parse(JSON.stringify(snapshot));
|
|
1328
|
+
}
|
|
1329
|
+
function areDocumentSnapshotsEqual(left, right) {
|
|
1330
|
+
if (left.records.length !== right.records.length) return false;
|
|
1331
|
+
for (let i = 0; i < left.records.length; i += 1) {
|
|
1332
|
+
if (JSON.stringify(left.records[i]) !== JSON.stringify(right.records[i])) {
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return true;
|
|
1337
|
+
}
|
|
1315
1338
|
var Editor = class {
|
|
1316
1339
|
store = new DocumentStore();
|
|
1317
1340
|
input = new InputManager();
|
|
@@ -1327,10 +1350,22 @@ var Editor = class {
|
|
|
1327
1350
|
};
|
|
1328
1351
|
toolStateContext;
|
|
1329
1352
|
listeners = /* @__PURE__ */ new Set();
|
|
1353
|
+
historyListeners = /* @__PURE__ */ new Set();
|
|
1354
|
+
undoStack = [];
|
|
1355
|
+
redoStack = [];
|
|
1356
|
+
lastDocumentSnapshot;
|
|
1357
|
+
suppressHistoryCapture = false;
|
|
1358
|
+
historyBatchDepth = 0;
|
|
1359
|
+
historyBatchStartSnapshot = null;
|
|
1360
|
+
historyBatchChanged = false;
|
|
1330
1361
|
// Creates a new editor instance with the given options (with defaults if not provided)
|
|
1331
1362
|
constructor(opts = {}) {
|
|
1332
1363
|
this.options = { dragDistanceSquared: opts.dragDistanceSquared ?? DRAG_DISTANCE_SQUARED };
|
|
1333
|
-
this.
|
|
1364
|
+
this.lastDocumentSnapshot = this.getDocumentSnapshot();
|
|
1365
|
+
this.store.listen(() => {
|
|
1366
|
+
this.captureDocumentHistory();
|
|
1367
|
+
this.emitChange();
|
|
1368
|
+
});
|
|
1334
1369
|
this.toolStateContext = {
|
|
1335
1370
|
transition: (id, info) => this.tools.transition(id, info)
|
|
1336
1371
|
};
|
|
@@ -1341,6 +1376,25 @@ var Editor = class {
|
|
|
1341
1376
|
this.registerToolDefinition(customTool);
|
|
1342
1377
|
}
|
|
1343
1378
|
this.setCurrentTool(opts.initialToolId ?? "pen");
|
|
1379
|
+
this.lastDocumentSnapshot = this.getDocumentSnapshot();
|
|
1380
|
+
}
|
|
1381
|
+
captureDocumentHistory() {
|
|
1382
|
+
const nextSnapshot = this.getDocumentSnapshot();
|
|
1383
|
+
const previousSnapshot = this.lastDocumentSnapshot;
|
|
1384
|
+
this.lastDocumentSnapshot = nextSnapshot;
|
|
1385
|
+
if (this.suppressHistoryCapture || areDocumentSnapshotsEqual(previousSnapshot, nextSnapshot)) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
if (this.historyBatchDepth > 0) {
|
|
1389
|
+
this.historyBatchChanged = true;
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
this.undoStack.push(cloneDocumentSnapshot(previousSnapshot));
|
|
1393
|
+
if (this.undoStack.length > MAX_HISTORY_ENTRIES) {
|
|
1394
|
+
this.undoStack.splice(0, this.undoStack.length - MAX_HISTORY_ENTRIES);
|
|
1395
|
+
}
|
|
1396
|
+
this.redoStack = [];
|
|
1397
|
+
this.emitHistoryChange();
|
|
1344
1398
|
}
|
|
1345
1399
|
registerToolDefinition(toolDefinition) {
|
|
1346
1400
|
for (const stateConstructor of toolDefinition.stateConstructors) {
|
|
@@ -1414,10 +1468,11 @@ var Editor = class {
|
|
|
1414
1468
|
this.emitChange();
|
|
1415
1469
|
}
|
|
1416
1470
|
setViewport(partial) {
|
|
1471
|
+
const rawZoom = partial.zoom ?? this.viewport.zoom;
|
|
1417
1472
|
this.viewport = {
|
|
1418
1473
|
x: partial.x ?? this.viewport.x,
|
|
1419
1474
|
y: partial.y ?? this.viewport.y,
|
|
1420
|
-
zoom:
|
|
1475
|
+
zoom: Math.max(0.1, Math.min(4, rawZoom))
|
|
1421
1476
|
};
|
|
1422
1477
|
this.emitChange();
|
|
1423
1478
|
}
|
|
@@ -1435,7 +1490,9 @@ var Editor = class {
|
|
|
1435
1490
|
loadDocumentSnapshot(snapshot) {
|
|
1436
1491
|
const documentSnapshot = recordsToDocumentSnapshot(snapshot.records);
|
|
1437
1492
|
if (!documentSnapshot) return;
|
|
1438
|
-
this.
|
|
1493
|
+
this.runWithoutHistoryCapture(() => {
|
|
1494
|
+
this.store.loadSnapshot(documentSnapshot);
|
|
1495
|
+
});
|
|
1439
1496
|
}
|
|
1440
1497
|
getSessionStateSnapshot(args) {
|
|
1441
1498
|
return {
|
|
@@ -1473,12 +1530,92 @@ var Editor = class {
|
|
|
1473
1530
|
}
|
|
1474
1531
|
return [];
|
|
1475
1532
|
}
|
|
1533
|
+
getHistorySnapshot() {
|
|
1534
|
+
return {
|
|
1535
|
+
version: 1,
|
|
1536
|
+
undoStack: this.undoStack.map(cloneDocumentSnapshot),
|
|
1537
|
+
redoStack: this.redoStack.map(cloneDocumentSnapshot)
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
loadHistorySnapshot(snapshot) {
|
|
1541
|
+
if (!snapshot || snapshot.version !== 1) return;
|
|
1542
|
+
this.undoStack = snapshot.undoStack.map(cloneDocumentSnapshot).slice(-MAX_HISTORY_ENTRIES);
|
|
1543
|
+
this.redoStack = snapshot.redoStack.map(cloneDocumentSnapshot).slice(-MAX_HISTORY_ENTRIES);
|
|
1544
|
+
this.emitHistoryChange();
|
|
1545
|
+
}
|
|
1546
|
+
clearRedoHistory() {
|
|
1547
|
+
if (this.redoStack.length === 0) return;
|
|
1548
|
+
this.redoStack = [];
|
|
1549
|
+
this.emitHistoryChange();
|
|
1550
|
+
}
|
|
1551
|
+
beginHistoryEntry() {
|
|
1552
|
+
if (this.historyBatchDepth === 0) {
|
|
1553
|
+
this.historyBatchStartSnapshot = cloneDocumentSnapshot(this.lastDocumentSnapshot);
|
|
1554
|
+
this.historyBatchChanged = false;
|
|
1555
|
+
}
|
|
1556
|
+
this.historyBatchDepth += 1;
|
|
1557
|
+
}
|
|
1558
|
+
endHistoryEntry() {
|
|
1559
|
+
if (this.historyBatchDepth === 0) return;
|
|
1560
|
+
this.historyBatchDepth -= 1;
|
|
1561
|
+
if (this.historyBatchDepth > 0) return;
|
|
1562
|
+
const startSnapshot = this.historyBatchStartSnapshot;
|
|
1563
|
+
this.historyBatchStartSnapshot = null;
|
|
1564
|
+
if (!startSnapshot) return;
|
|
1565
|
+
const endSnapshot = this.getDocumentSnapshot();
|
|
1566
|
+
this.lastDocumentSnapshot = endSnapshot;
|
|
1567
|
+
const didDocumentChange = this.historyBatchChanged || !areDocumentSnapshotsEqual(startSnapshot, endSnapshot);
|
|
1568
|
+
this.historyBatchChanged = false;
|
|
1569
|
+
if (!didDocumentChange) return;
|
|
1570
|
+
this.undoStack.push(cloneDocumentSnapshot(startSnapshot));
|
|
1571
|
+
if (this.undoStack.length > MAX_HISTORY_ENTRIES) {
|
|
1572
|
+
this.undoStack.splice(0, this.undoStack.length - MAX_HISTORY_ENTRIES);
|
|
1573
|
+
}
|
|
1574
|
+
this.redoStack = [];
|
|
1575
|
+
this.emitHistoryChange();
|
|
1576
|
+
}
|
|
1577
|
+
canUndo() {
|
|
1578
|
+
return this.undoStack.length > 0;
|
|
1579
|
+
}
|
|
1580
|
+
canRedo() {
|
|
1581
|
+
return this.redoStack.length > 0;
|
|
1582
|
+
}
|
|
1583
|
+
undo() {
|
|
1584
|
+
const previousSnapshot = this.undoStack.pop();
|
|
1585
|
+
if (!previousSnapshot) return false;
|
|
1586
|
+
const currentSnapshot = this.getDocumentSnapshot();
|
|
1587
|
+
this.redoStack.push(cloneDocumentSnapshot(currentSnapshot));
|
|
1588
|
+
if (this.redoStack.length > MAX_HISTORY_ENTRIES) {
|
|
1589
|
+
this.redoStack.splice(0, this.redoStack.length - MAX_HISTORY_ENTRIES);
|
|
1590
|
+
}
|
|
1591
|
+
this.loadDocumentSnapshot(previousSnapshot);
|
|
1592
|
+
this.emitHistoryChange();
|
|
1593
|
+
return true;
|
|
1594
|
+
}
|
|
1595
|
+
redo() {
|
|
1596
|
+
const nextSnapshot = this.redoStack.pop();
|
|
1597
|
+
if (!nextSnapshot) return false;
|
|
1598
|
+
const currentSnapshot = this.getDocumentSnapshot();
|
|
1599
|
+
this.undoStack.push(cloneDocumentSnapshot(currentSnapshot));
|
|
1600
|
+
if (this.undoStack.length > MAX_HISTORY_ENTRIES) {
|
|
1601
|
+
this.undoStack.splice(0, this.undoStack.length - MAX_HISTORY_ENTRIES);
|
|
1602
|
+
}
|
|
1603
|
+
this.loadDocumentSnapshot(nextSnapshot);
|
|
1604
|
+
this.emitHistoryChange();
|
|
1605
|
+
return true;
|
|
1606
|
+
}
|
|
1476
1607
|
listen(listener) {
|
|
1477
1608
|
this.listeners.add(listener);
|
|
1478
1609
|
return () => {
|
|
1479
1610
|
this.listeners.delete(listener);
|
|
1480
1611
|
};
|
|
1481
1612
|
}
|
|
1613
|
+
listenHistory(listener) {
|
|
1614
|
+
this.historyListeners.add(listener);
|
|
1615
|
+
return () => {
|
|
1616
|
+
this.historyListeners.delete(listener);
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1482
1619
|
// Convert screen coords to page coords
|
|
1483
1620
|
screenToPage(screenX, screenY) {
|
|
1484
1621
|
return screenToPage(this.viewport, screenX, screenY);
|
|
@@ -1495,6 +1632,21 @@ var Editor = class {
|
|
|
1495
1632
|
listener();
|
|
1496
1633
|
}
|
|
1497
1634
|
}
|
|
1635
|
+
emitHistoryChange() {
|
|
1636
|
+
for (const listener of this.historyListeners) {
|
|
1637
|
+
listener();
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
runWithoutHistoryCapture(fn) {
|
|
1641
|
+
const previousValue = this.suppressHistoryCapture;
|
|
1642
|
+
this.suppressHistoryCapture = true;
|
|
1643
|
+
try {
|
|
1644
|
+
fn();
|
|
1645
|
+
} finally {
|
|
1646
|
+
this.suppressHistoryCapture = previousValue;
|
|
1647
|
+
this.lastDocumentSnapshot = this.getDocumentSnapshot();
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1498
1650
|
};
|
|
1499
1651
|
|
|
1500
1652
|
// src/tools/select/selectHelpers.ts
|