@flowgram.ai/history 0.1.0
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/esm/index.js +890 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +639 -0
- package/dist/index.d.ts +639 -0
- package/dist/index.js +928 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/operation/operation-contribution.ts
|
|
13
|
+
var OperationContribution = Symbol("OperationContribution");
|
|
14
|
+
|
|
15
|
+
// src/operation/operation-registry.ts
|
|
16
|
+
import { injectable, multiInject, optional, postConstruct } from "inversify";
|
|
17
|
+
import { Disposable, DisposableCollection } from "@flowgram.ai/utils";
|
|
18
|
+
var OperationRegistry = class {
|
|
19
|
+
constructor() {
|
|
20
|
+
this._operationMetas = /* @__PURE__ */ new Map();
|
|
21
|
+
this.contributions = [];
|
|
22
|
+
}
|
|
23
|
+
init() {
|
|
24
|
+
for (const contrib of this.contributions) {
|
|
25
|
+
contrib.registerOperationMeta?.(this);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 注册操作的元数据
|
|
30
|
+
* @param operationMeta 操作的元数据
|
|
31
|
+
* @returns 销毁函数
|
|
32
|
+
*/
|
|
33
|
+
registerOperationMeta(operationMeta) {
|
|
34
|
+
if (this._operationMetas.has(operationMeta.type)) {
|
|
35
|
+
console.warn(`A operation meta ${operationMeta.type} is already registered.`);
|
|
36
|
+
return Disposable.NULL;
|
|
37
|
+
}
|
|
38
|
+
const toDispose = new DisposableCollection(this._doRegisterOperationMetaMeta(operationMeta));
|
|
39
|
+
return toDispose;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 获取操作的元数据
|
|
43
|
+
* @param type 操作类型
|
|
44
|
+
* @returns 操作的元数据
|
|
45
|
+
*/
|
|
46
|
+
getOperationMeta(type) {
|
|
47
|
+
return this._operationMetas.get(type);
|
|
48
|
+
}
|
|
49
|
+
_doRegisterOperationMetaMeta(operationMeta) {
|
|
50
|
+
this._operationMetas.set(operationMeta.type, operationMeta);
|
|
51
|
+
return {
|
|
52
|
+
dispose: () => {
|
|
53
|
+
this._operationMetas.delete(operationMeta.type);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
__decorateClass([
|
|
59
|
+
multiInject(OperationContribution),
|
|
60
|
+
optional()
|
|
61
|
+
], OperationRegistry.prototype, "contributions", 2);
|
|
62
|
+
__decorateClass([
|
|
63
|
+
postConstruct()
|
|
64
|
+
], OperationRegistry.prototype, "init", 1);
|
|
65
|
+
OperationRegistry = __decorateClass([
|
|
66
|
+
injectable()
|
|
67
|
+
], OperationRegistry);
|
|
68
|
+
|
|
69
|
+
// src/operation/operation-service.ts
|
|
70
|
+
import { injectable as injectable4, inject, postConstruct as postConstruct2 } from "inversify";
|
|
71
|
+
import { DisposableCollection as DisposableCollection2, Emitter } from "@flowgram.ai/utils";
|
|
72
|
+
|
|
73
|
+
// src/history-context.ts
|
|
74
|
+
import { injectable as injectable2 } from "inversify";
|
|
75
|
+
var HistoryContext = class {
|
|
76
|
+
};
|
|
77
|
+
HistoryContext = __decorateClass([
|
|
78
|
+
injectable2()
|
|
79
|
+
], HistoryContext);
|
|
80
|
+
|
|
81
|
+
// src/history-config.ts
|
|
82
|
+
import { nanoid } from "nanoid";
|
|
83
|
+
import { injectable as injectable3 } from "inversify";
|
|
84
|
+
var HistoryConfig = class {
|
|
85
|
+
constructor() {
|
|
86
|
+
this.generateId = () => nanoid();
|
|
87
|
+
this.getSnapshot = () => "";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
HistoryConfig = __decorateClass([
|
|
91
|
+
injectable3()
|
|
92
|
+
], HistoryConfig);
|
|
93
|
+
|
|
94
|
+
// src/operation/operation-service.ts
|
|
95
|
+
var OperationService = class {
|
|
96
|
+
constructor() {
|
|
97
|
+
this.applyEmitter = new Emitter();
|
|
98
|
+
this.onApply = this.applyEmitter.event;
|
|
99
|
+
this._toDispose = new DisposableCollection2();
|
|
100
|
+
}
|
|
101
|
+
init() {
|
|
102
|
+
this._toDispose.push(this.applyEmitter);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 执行操作
|
|
106
|
+
* @param op
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
applyOperation(op, options) {
|
|
110
|
+
const meta = this.operationRegistry.getOperationMeta(op.type);
|
|
111
|
+
if (!meta) {
|
|
112
|
+
throw new Error(`Operation meta ${op.type} has not registered.`);
|
|
113
|
+
}
|
|
114
|
+
let res;
|
|
115
|
+
if (!options?.noApply) {
|
|
116
|
+
res = meta.apply(op, this.context.source);
|
|
117
|
+
}
|
|
118
|
+
this.applyEmitter.fire(op);
|
|
119
|
+
return res;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 根据操作类型获取操作的label
|
|
123
|
+
* @param operation 操作
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
126
|
+
getOperationLabel(operation) {
|
|
127
|
+
const operationMeta = this.operationRegistry.getOperationMeta(operation.type);
|
|
128
|
+
if (operationMeta && operationMeta.getLabel) {
|
|
129
|
+
return operationMeta.getLabel(operation, this.context.source);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 根据操作类型获取操作的description
|
|
134
|
+
* @param operation 操作
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
getOperationDescription(operation) {
|
|
138
|
+
const operationMeta = this.operationRegistry.getOperationMeta(operation.type);
|
|
139
|
+
if (operationMeta && operationMeta.getDescription) {
|
|
140
|
+
return operationMeta.getDescription(operation, this.context.source);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 操作取反
|
|
145
|
+
* @param operations
|
|
146
|
+
* @returns
|
|
147
|
+
*/
|
|
148
|
+
inverseOperations(operations) {
|
|
149
|
+
return operations.map((op) => this.inverseOperation(op)).reverse();
|
|
150
|
+
}
|
|
151
|
+
inverseOperation(op) {
|
|
152
|
+
const meta = this.operationRegistry.getOperationMeta(op.type);
|
|
153
|
+
if (!meta) {
|
|
154
|
+
throw new Error(`Operation meta ${op.type} has not registered.`);
|
|
155
|
+
}
|
|
156
|
+
return meta.inverse(op);
|
|
157
|
+
}
|
|
158
|
+
dispose() {
|
|
159
|
+
this._toDispose.dispose();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
__decorateClass([
|
|
163
|
+
inject(OperationRegistry)
|
|
164
|
+
], OperationService.prototype, "operationRegistry", 2);
|
|
165
|
+
__decorateClass([
|
|
166
|
+
inject(HistoryContext)
|
|
167
|
+
], OperationService.prototype, "context", 2);
|
|
168
|
+
__decorateClass([
|
|
169
|
+
inject(HistoryConfig)
|
|
170
|
+
], OperationService.prototype, "config", 2);
|
|
171
|
+
__decorateClass([
|
|
172
|
+
postConstruct2()
|
|
173
|
+
], OperationService.prototype, "init", 1);
|
|
174
|
+
OperationService = __decorateClass([
|
|
175
|
+
injectable4()
|
|
176
|
+
], OperationService);
|
|
177
|
+
|
|
178
|
+
// src/history/undo-redo-service.ts
|
|
179
|
+
import { injectable as injectable5 } from "inversify";
|
|
180
|
+
import { Emitter as Emitter2, DisposableCollection as DisposableCollection3 } from "@flowgram.ai/utils";
|
|
181
|
+
|
|
182
|
+
// src/history/types.ts
|
|
183
|
+
var UndoRedoChangeType = /* @__PURE__ */ ((UndoRedoChangeType2) => {
|
|
184
|
+
UndoRedoChangeType2["UNDO"] = "undo";
|
|
185
|
+
UndoRedoChangeType2["REDO"] = "redo";
|
|
186
|
+
UndoRedoChangeType2["PUSH"] = "push";
|
|
187
|
+
UndoRedoChangeType2["CLEAR"] = "clear";
|
|
188
|
+
return UndoRedoChangeType2;
|
|
189
|
+
})(UndoRedoChangeType || {});
|
|
190
|
+
var HistoryStackChangeType = /* @__PURE__ */ ((HistoryStackChangeType2) => {
|
|
191
|
+
HistoryStackChangeType2["ADD"] = "add";
|
|
192
|
+
HistoryStackChangeType2["UPDATE"] = "update";
|
|
193
|
+
HistoryStackChangeType2["CLEAR"] = "clear";
|
|
194
|
+
HistoryStackChangeType2["ADD_OPERATION"] = "add_operation";
|
|
195
|
+
HistoryStackChangeType2["UPDATE_OPERATION"] = "update_operation";
|
|
196
|
+
return HistoryStackChangeType2;
|
|
197
|
+
})(HistoryStackChangeType || {});
|
|
198
|
+
var HistoryMergeEventType = /* @__PURE__ */ ((HistoryMergeEventType2) => {
|
|
199
|
+
HistoryMergeEventType2["ADD"] = "ADD";
|
|
200
|
+
HistoryMergeEventType2["UPDATE"] = "UPDATE";
|
|
201
|
+
return HistoryMergeEventType2;
|
|
202
|
+
})(HistoryMergeEventType || {});
|
|
203
|
+
|
|
204
|
+
// src/history/undo-redo-service.ts
|
|
205
|
+
var UndoRedoService = class {
|
|
206
|
+
constructor() {
|
|
207
|
+
this._undoing = false;
|
|
208
|
+
this._redoing = false;
|
|
209
|
+
this._limit = 100;
|
|
210
|
+
this.onChangeEmitter = new Emitter2();
|
|
211
|
+
this.onChange = this.onChangeEmitter.event;
|
|
212
|
+
this._toDispose = new DisposableCollection3();
|
|
213
|
+
this._undoStack = [];
|
|
214
|
+
this._redoStack = [];
|
|
215
|
+
this._toDispose.push(this.onChangeEmitter);
|
|
216
|
+
}
|
|
217
|
+
setLimit(limit) {
|
|
218
|
+
this._limit = limit;
|
|
219
|
+
}
|
|
220
|
+
pushElement(element) {
|
|
221
|
+
this._redoStack = [];
|
|
222
|
+
this._stackPush(this._undoStack, element);
|
|
223
|
+
this._toDispose.push(element);
|
|
224
|
+
this._emitChange("push" /* PUSH */, element);
|
|
225
|
+
}
|
|
226
|
+
getUndoStack() {
|
|
227
|
+
return this._undoStack;
|
|
228
|
+
}
|
|
229
|
+
getRedoStack() {
|
|
230
|
+
return this._redoStack;
|
|
231
|
+
}
|
|
232
|
+
getLastElement() {
|
|
233
|
+
return this._undoStack[this._undoStack.length - 1];
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 执行undo
|
|
237
|
+
* @returns void
|
|
238
|
+
*/
|
|
239
|
+
async undo() {
|
|
240
|
+
if (!this.canUndo()) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (this._undoing) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this._undoing = true;
|
|
247
|
+
const item = this._undoStack.pop();
|
|
248
|
+
try {
|
|
249
|
+
await item.undo();
|
|
250
|
+
} finally {
|
|
251
|
+
this._stackPush(this._redoStack, item);
|
|
252
|
+
this._emitChange("undo" /* UNDO */, item);
|
|
253
|
+
this._undoing = false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* 执行redo
|
|
258
|
+
* @returns void
|
|
259
|
+
*/
|
|
260
|
+
async redo() {
|
|
261
|
+
if (!this.canRedo()) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (this._redoing) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this._redoing = true;
|
|
268
|
+
const item = this._redoStack.pop();
|
|
269
|
+
try {
|
|
270
|
+
await item.redo();
|
|
271
|
+
} finally {
|
|
272
|
+
this._stackPush(this._undoStack, item);
|
|
273
|
+
this._emitChange("redo" /* REDO */, item);
|
|
274
|
+
this._redoing = false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 是否可undo
|
|
279
|
+
* @returns true代表可以,false代表不可以
|
|
280
|
+
*/
|
|
281
|
+
canUndo() {
|
|
282
|
+
return this._undoStack.length > 0;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 是否可redo
|
|
286
|
+
* @returns true代表可以,false代表不可以
|
|
287
|
+
*/
|
|
288
|
+
canRedo() {
|
|
289
|
+
return this._redoStack.length > 0;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* 是否可以push
|
|
293
|
+
* @returns true代表可以,false代表不可以
|
|
294
|
+
*/
|
|
295
|
+
canPush() {
|
|
296
|
+
return !this._redoing && !this._undoing;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* 清空
|
|
300
|
+
*/
|
|
301
|
+
clear() {
|
|
302
|
+
this.clearRedoStack();
|
|
303
|
+
this.clearUndoStack();
|
|
304
|
+
this._emitChange("clear" /* CLEAR */);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 清空redo栈
|
|
308
|
+
*/
|
|
309
|
+
clearRedoStack() {
|
|
310
|
+
this._redoStack.forEach((element) => {
|
|
311
|
+
element.dispose();
|
|
312
|
+
});
|
|
313
|
+
this._redoStack = [];
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 清空undo栈
|
|
317
|
+
*/
|
|
318
|
+
clearUndoStack() {
|
|
319
|
+
this._undoStack.forEach((element) => {
|
|
320
|
+
element.dispose();
|
|
321
|
+
});
|
|
322
|
+
this._undoStack = [];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* 销毁
|
|
326
|
+
*/
|
|
327
|
+
dispose() {
|
|
328
|
+
this.clear();
|
|
329
|
+
this._toDispose.dispose();
|
|
330
|
+
}
|
|
331
|
+
_stackPush(stack, element) {
|
|
332
|
+
stack.push(element);
|
|
333
|
+
if (stack.length > this._limit) {
|
|
334
|
+
stack.shift();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
_emitChange(type, element) {
|
|
338
|
+
if (element) {
|
|
339
|
+
this.onChangeEmitter.fire({ type, element });
|
|
340
|
+
} else {
|
|
341
|
+
this.onChangeEmitter.fire({ type });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
UndoRedoService = __decorateClass([
|
|
346
|
+
injectable5()
|
|
347
|
+
], UndoRedoService);
|
|
348
|
+
|
|
349
|
+
// src/history/history-service.ts
|
|
350
|
+
import { pick } from "lodash";
|
|
351
|
+
import { injectable as injectable8, inject as inject4, postConstruct as postConstruct3 } from "inversify";
|
|
352
|
+
import { DisposableCollection as DisposableCollection7, Emitter as Emitter4 } from "@flowgram.ai/utils";
|
|
353
|
+
|
|
354
|
+
// src/history/stack-operation.ts
|
|
355
|
+
import { cloneDeep } from "lodash";
|
|
356
|
+
import { DisposableCollection as DisposableCollection4 } from "@flowgram.ai/utils";
|
|
357
|
+
var StackOperation = class {
|
|
358
|
+
constructor(operationService, operations = []) {
|
|
359
|
+
this._toDispose = new DisposableCollection4();
|
|
360
|
+
this._timestamp = Date.now();
|
|
361
|
+
this._operationService = operationService;
|
|
362
|
+
this._operations = operations.map((op) => this._operation(op));
|
|
363
|
+
this._id = operationService.config.generateId();
|
|
364
|
+
}
|
|
365
|
+
get id() {
|
|
366
|
+
return this._id;
|
|
367
|
+
}
|
|
368
|
+
getTimestamp() {
|
|
369
|
+
return this._timestamp;
|
|
370
|
+
}
|
|
371
|
+
pushOperation(operation) {
|
|
372
|
+
const op = this._operation(operation);
|
|
373
|
+
this._operations.push(op);
|
|
374
|
+
return op;
|
|
375
|
+
}
|
|
376
|
+
getOperations() {
|
|
377
|
+
return this._operations;
|
|
378
|
+
}
|
|
379
|
+
getChangeOperations(type) {
|
|
380
|
+
if (type === "undo" /* UNDO */) {
|
|
381
|
+
return this._operationService.inverseOperations(this._operations);
|
|
382
|
+
}
|
|
383
|
+
return this._operations;
|
|
384
|
+
}
|
|
385
|
+
getFirstOperation() {
|
|
386
|
+
return this._operations[0];
|
|
387
|
+
}
|
|
388
|
+
getLastOperation() {
|
|
389
|
+
return this._operations[this._operations.length - 1];
|
|
390
|
+
}
|
|
391
|
+
async undo() {
|
|
392
|
+
const inverseOps = this._operationService.inverseOperations(this._operations);
|
|
393
|
+
for (const op of inverseOps) {
|
|
394
|
+
await this._apply(op);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async redo() {
|
|
398
|
+
for (const op of this._operations) {
|
|
399
|
+
await this._apply(op);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
revert(type) {
|
|
403
|
+
let operations = this._operations;
|
|
404
|
+
if (type !== "undo" /* UNDO */) {
|
|
405
|
+
operations = this._operations.map((op) => this._inverse(op)).reverse();
|
|
406
|
+
}
|
|
407
|
+
for (const op of operations) {
|
|
408
|
+
this._apply(op);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
_inverse(op) {
|
|
412
|
+
return this._operationService.inverseOperation(op);
|
|
413
|
+
}
|
|
414
|
+
async _apply(op) {
|
|
415
|
+
await this._operationService.applyOperation(op);
|
|
416
|
+
}
|
|
417
|
+
_operation(op) {
|
|
418
|
+
return {
|
|
419
|
+
...op,
|
|
420
|
+
value: cloneDeep(op.value),
|
|
421
|
+
id: this._operationService.config.generateId()
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
dispose() {
|
|
425
|
+
this._toDispose.dispose();
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// src/history/history-manager.ts
|
|
430
|
+
import { inject as inject3, injectable as injectable7 } from "inversify";
|
|
431
|
+
import { DisposableCollection as DisposableCollection6 } from "@flowgram.ai/utils";
|
|
432
|
+
|
|
433
|
+
// src/history/history-stack.ts
|
|
434
|
+
import { cloneDeep as cloneDeep2 } from "lodash";
|
|
435
|
+
import { injectable as injectable6, inject as inject2 } from "inversify";
|
|
436
|
+
import { DisposableCollection as DisposableCollection5, Emitter as Emitter3 } from "@flowgram.ai/utils";
|
|
437
|
+
var HistoryStack = class {
|
|
438
|
+
constructor() {
|
|
439
|
+
this._items = [];
|
|
440
|
+
this.onChangeEmitter = new Emitter3();
|
|
441
|
+
this.onChange = this.onChangeEmitter.event;
|
|
442
|
+
this._toDispose = new DisposableCollection5();
|
|
443
|
+
this.limit = 100;
|
|
444
|
+
this._toDispose.push(this.onChangeEmitter);
|
|
445
|
+
}
|
|
446
|
+
get items() {
|
|
447
|
+
return this._items;
|
|
448
|
+
}
|
|
449
|
+
add(service, item) {
|
|
450
|
+
const historyItem = this._getHistoryItem(service, item);
|
|
451
|
+
this._items.unshift(historyItem);
|
|
452
|
+
if (this._items.length > this.limit) {
|
|
453
|
+
this._items.pop();
|
|
454
|
+
}
|
|
455
|
+
this.onChangeEmitter.fire({
|
|
456
|
+
type: "add" /* ADD */,
|
|
457
|
+
value: historyItem,
|
|
458
|
+
service
|
|
459
|
+
});
|
|
460
|
+
return historyItem;
|
|
461
|
+
}
|
|
462
|
+
findById(id) {
|
|
463
|
+
return this._items.find((item) => item.id === id);
|
|
464
|
+
}
|
|
465
|
+
changeByIndex(index, service, item) {
|
|
466
|
+
const historyItem = this._getHistoryItem(service, item);
|
|
467
|
+
this._items[index] = historyItem;
|
|
468
|
+
this.onChangeEmitter.fire({
|
|
469
|
+
type: "update" /* UPDATE */,
|
|
470
|
+
value: historyItem,
|
|
471
|
+
service
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
addOperation(service, id, op) {
|
|
475
|
+
const historyItem = this._items.find((item) => item.id === id);
|
|
476
|
+
if (!historyItem) {
|
|
477
|
+
console.warn("no history item found");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const newOperatopn = this._getHistoryOperation(service, op);
|
|
481
|
+
historyItem.operations.push(newOperatopn);
|
|
482
|
+
this.onChangeEmitter.fire({
|
|
483
|
+
type: "add_operation" /* ADD_OPERATION */,
|
|
484
|
+
value: {
|
|
485
|
+
historyItem,
|
|
486
|
+
operation: newOperatopn
|
|
487
|
+
},
|
|
488
|
+
service
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
updateOperation(service, id, op) {
|
|
492
|
+
const historyItem = this._items.find((item) => item.id === id);
|
|
493
|
+
if (!historyItem) {
|
|
494
|
+
console.warn("no history item found");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const index = historyItem.operations.findIndex((op2) => op2.id === op2.id);
|
|
498
|
+
if (index < 0) {
|
|
499
|
+
console.warn("no operation found");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const newOperatopn = this._getHistoryOperation(service, op);
|
|
503
|
+
historyItem.operations.splice(index, 1, newOperatopn);
|
|
504
|
+
this.onChangeEmitter.fire({
|
|
505
|
+
type: "update_operation" /* UPDATE_OPERATION */,
|
|
506
|
+
value: {
|
|
507
|
+
historyItem,
|
|
508
|
+
operation: newOperatopn
|
|
509
|
+
},
|
|
510
|
+
service
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
clear() {
|
|
514
|
+
this._items = [];
|
|
515
|
+
}
|
|
516
|
+
dispose() {
|
|
517
|
+
this._items = [];
|
|
518
|
+
this._toDispose.dispose();
|
|
519
|
+
}
|
|
520
|
+
_getHistoryItem(service, item) {
|
|
521
|
+
return {
|
|
522
|
+
...item,
|
|
523
|
+
uri: service.context.uri,
|
|
524
|
+
time: HistoryStack.dateFormat(item.timestamp),
|
|
525
|
+
operations: item.operations.map(
|
|
526
|
+
(op) => this._getHistoryOperation(service, op, item.type !== "push" /* PUSH */)
|
|
527
|
+
)
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
_getHistoryOperation(service, op, generateId = false) {
|
|
531
|
+
let id;
|
|
532
|
+
if (generateId) {
|
|
533
|
+
id = this.historyConfig.generateId();
|
|
534
|
+
} else {
|
|
535
|
+
const oldId = op.id;
|
|
536
|
+
if (!oldId) {
|
|
537
|
+
throw new Error("no operation id found");
|
|
538
|
+
}
|
|
539
|
+
id = oldId;
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
...cloneDeep2(op),
|
|
543
|
+
id,
|
|
544
|
+
label: service.operationService.getOperationLabel(op),
|
|
545
|
+
description: service.operationService.getOperationDescription(op),
|
|
546
|
+
timestamp: Date.now()
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
static dateFormat(timestamp) {
|
|
550
|
+
return new Date(timestamp).toLocaleString();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
__decorateClass([
|
|
554
|
+
inject2(HistoryConfig)
|
|
555
|
+
], HistoryStack.prototype, "historyConfig", 2);
|
|
556
|
+
HistoryStack = __decorateClass([
|
|
557
|
+
injectable6()
|
|
558
|
+
], HistoryStack);
|
|
559
|
+
|
|
560
|
+
// src/history/history-manager.ts
|
|
561
|
+
var HistoryManager = class {
|
|
562
|
+
constructor() {
|
|
563
|
+
this._historyServices = /* @__PURE__ */ new Map();
|
|
564
|
+
this._toDispose = new DisposableCollection6();
|
|
565
|
+
}
|
|
566
|
+
registerHistoryService(service) {
|
|
567
|
+
const toDispose = new DisposableCollection6();
|
|
568
|
+
toDispose.pushAll([
|
|
569
|
+
service.undoRedoService.onChange((event) => {
|
|
570
|
+
if (event.type === "clear" /* CLEAR */) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const { type, element } = event;
|
|
574
|
+
const operations = element.getChangeOperations(type);
|
|
575
|
+
const historyStackItem = {
|
|
576
|
+
id: type === "push" /* PUSH */ ? element.id : this.historyConfig.generateId(),
|
|
577
|
+
type,
|
|
578
|
+
uri: service.context.uri,
|
|
579
|
+
operations,
|
|
580
|
+
timestamp: Date.now()
|
|
581
|
+
};
|
|
582
|
+
this.historyStack.add(service, historyStackItem);
|
|
583
|
+
}),
|
|
584
|
+
service.onMerge((event) => {
|
|
585
|
+
this._handleMerge(service, event);
|
|
586
|
+
})
|
|
587
|
+
]);
|
|
588
|
+
this._historyServices.set(service, toDispose);
|
|
589
|
+
this._toDispose.push(
|
|
590
|
+
service.onWillDispose(() => {
|
|
591
|
+
this.unregisterHistoryService(service);
|
|
592
|
+
})
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
unregisterHistoryService(service) {
|
|
596
|
+
const disposable = this._historyServices.get(service);
|
|
597
|
+
if (!disposable) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
disposable.dispose();
|
|
601
|
+
this._historyServices.delete(service);
|
|
602
|
+
}
|
|
603
|
+
getHistoryServiceByURI(uri) {
|
|
604
|
+
for (const service of this._historyServices.keys()) {
|
|
605
|
+
if (service.context.uri === uri) {
|
|
606
|
+
return service;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
getFirstHistoryService() {
|
|
611
|
+
for (const service of this._historyServices.keys()) {
|
|
612
|
+
return service;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
dispose() {
|
|
616
|
+
this._toDispose.dispose();
|
|
617
|
+
this.historyStack.dispose();
|
|
618
|
+
this._historyServices.forEach((service) => service.dispose());
|
|
619
|
+
this._historyServices.clear();
|
|
620
|
+
}
|
|
621
|
+
_handleMerge(service, event) {
|
|
622
|
+
const { element, operation } = event.value;
|
|
623
|
+
const find = this.historyStack.findById(element.id);
|
|
624
|
+
if (!find) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (!operation.id) {
|
|
628
|
+
console.warn("no operation id found");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (event.type === "UPDATE" /* UPDATE */) {
|
|
632
|
+
this.historyStack.updateOperation(
|
|
633
|
+
service,
|
|
634
|
+
element.id,
|
|
635
|
+
operation
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
if (event.type === "ADD" /* ADD */) {
|
|
639
|
+
this.historyStack.addOperation(
|
|
640
|
+
service,
|
|
641
|
+
element.id,
|
|
642
|
+
operation
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
__decorateClass([
|
|
648
|
+
inject3(HistoryStack)
|
|
649
|
+
], HistoryManager.prototype, "historyStack", 2);
|
|
650
|
+
__decorateClass([
|
|
651
|
+
inject3(HistoryConfig)
|
|
652
|
+
], HistoryManager.prototype, "historyConfig", 2);
|
|
653
|
+
HistoryManager = __decorateClass([
|
|
654
|
+
injectable7()
|
|
655
|
+
], HistoryManager);
|
|
656
|
+
|
|
657
|
+
// src/history/history-service.ts
|
|
658
|
+
var HistoryService = class {
|
|
659
|
+
constructor() {
|
|
660
|
+
this._toDispose = new DisposableCollection7();
|
|
661
|
+
this._transacting = false;
|
|
662
|
+
this._transactOperation = null;
|
|
663
|
+
this._locked = false;
|
|
664
|
+
this._willDisposeEmitter = new Emitter4();
|
|
665
|
+
this._mergeEmitter = new Emitter4();
|
|
666
|
+
this.onWillDispose = this._willDisposeEmitter.event;
|
|
667
|
+
this.onMerge = this._mergeEmitter.event;
|
|
668
|
+
}
|
|
669
|
+
get onApply() {
|
|
670
|
+
return this.operationService.onApply;
|
|
671
|
+
}
|
|
672
|
+
init() {
|
|
673
|
+
this._toDispose.push(this._willDisposeEmitter);
|
|
674
|
+
this._toDispose.push(this._mergeEmitter);
|
|
675
|
+
}
|
|
676
|
+
start() {
|
|
677
|
+
this._locked = false;
|
|
678
|
+
}
|
|
679
|
+
stop() {
|
|
680
|
+
this._locked = true;
|
|
681
|
+
}
|
|
682
|
+
limit(num) {
|
|
683
|
+
this.undoRedoService.setLimit(num);
|
|
684
|
+
}
|
|
685
|
+
startTransaction() {
|
|
686
|
+
if (this._transacting) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
this._transacting = true;
|
|
690
|
+
const stackOperation = new StackOperation(this.operationService, []);
|
|
691
|
+
this._transactOperation = stackOperation;
|
|
692
|
+
}
|
|
693
|
+
endTransaction() {
|
|
694
|
+
const stackOperation = this._transactOperation;
|
|
695
|
+
if (!stackOperation) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
if (stackOperation.getOperations().length === 0) {
|
|
699
|
+
throw new Error("no operation be pushed");
|
|
700
|
+
}
|
|
701
|
+
this._pushStackOperation(stackOperation);
|
|
702
|
+
this._transactOperation = null;
|
|
703
|
+
this._transacting = false;
|
|
704
|
+
}
|
|
705
|
+
transact(transaction) {
|
|
706
|
+
if (this._transacting) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
this.startTransaction();
|
|
710
|
+
transaction();
|
|
711
|
+
this.endTransaction();
|
|
712
|
+
}
|
|
713
|
+
pushOperation(operation, options) {
|
|
714
|
+
if (!this._canPush()) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const prev = this._transactOperation || this.undoRedoService.getLastElement();
|
|
718
|
+
const operationMeta = this.operationRegistry.getOperationMeta(operation.type);
|
|
719
|
+
if (!operationMeta) {
|
|
720
|
+
throw new Error(`Operation meta ${operation.type} has not registered.`);
|
|
721
|
+
}
|
|
722
|
+
if (operationMeta.shouldSave && !operationMeta.shouldSave(operation)) {
|
|
723
|
+
return operationMeta.apply(operation, this.context.source);
|
|
724
|
+
}
|
|
725
|
+
const res = this.operationService.applyOperation(operation, { noApply: options?.noApply });
|
|
726
|
+
if (operationMeta.getURI && !operation.uri) {
|
|
727
|
+
operation.uri = operationMeta.getURI(operation, this.context.source);
|
|
728
|
+
}
|
|
729
|
+
const shouldMerge = this._shouldMerge(operation, prev, operationMeta);
|
|
730
|
+
if (shouldMerge) {
|
|
731
|
+
if (typeof shouldMerge === "object") {
|
|
732
|
+
const operation2 = prev.getLastOperation();
|
|
733
|
+
operation2.value = shouldMerge.value;
|
|
734
|
+
this._mergeEmitter.fire({
|
|
735
|
+
type: "UPDATE" /* UPDATE */,
|
|
736
|
+
value: {
|
|
737
|
+
element: prev,
|
|
738
|
+
operation: operation2,
|
|
739
|
+
value: shouldMerge.value
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
} else {
|
|
743
|
+
const op = prev.pushOperation(operation);
|
|
744
|
+
this._mergeEmitter.fire({
|
|
745
|
+
type: "ADD" /* ADD */,
|
|
746
|
+
value: {
|
|
747
|
+
element: prev,
|
|
748
|
+
operation: op
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
const stackOperation = new StackOperation(this.operationService, [operation]);
|
|
754
|
+
this._pushStackOperation(stackOperation);
|
|
755
|
+
}
|
|
756
|
+
return res;
|
|
757
|
+
}
|
|
758
|
+
getHistoryOperations() {
|
|
759
|
+
return this.historyManager.historyStack.items.reverse().map(
|
|
760
|
+
(item) => item.operations.map((o) => ({
|
|
761
|
+
...pick(o, ["type", "value"]),
|
|
762
|
+
label: o.label || o.type
|
|
763
|
+
}))
|
|
764
|
+
).flat();
|
|
765
|
+
}
|
|
766
|
+
async undo() {
|
|
767
|
+
await this.undoRedoService.undo();
|
|
768
|
+
}
|
|
769
|
+
async redo() {
|
|
770
|
+
await this.undoRedoService.redo();
|
|
771
|
+
}
|
|
772
|
+
canUndo() {
|
|
773
|
+
return this.undoRedoService.canUndo();
|
|
774
|
+
}
|
|
775
|
+
canRedo() {
|
|
776
|
+
return this.undoRedoService.canRedo();
|
|
777
|
+
}
|
|
778
|
+
getSnapshot() {
|
|
779
|
+
return this.config.getSnapshot();
|
|
780
|
+
}
|
|
781
|
+
getRecords() {
|
|
782
|
+
throw new Error("Method not implemented.");
|
|
783
|
+
}
|
|
784
|
+
restore(historyRecord) {
|
|
785
|
+
throw new Error("Method not implemented.");
|
|
786
|
+
}
|
|
787
|
+
clear() {
|
|
788
|
+
this.undoRedoService.clear();
|
|
789
|
+
}
|
|
790
|
+
dispose() {
|
|
791
|
+
this._willDisposeEmitter.fire(this);
|
|
792
|
+
this._toDispose.dispose();
|
|
793
|
+
}
|
|
794
|
+
_canPush() {
|
|
795
|
+
if (this._locked) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
return this.undoRedoService.canPush();
|
|
799
|
+
}
|
|
800
|
+
_pushStackOperation(stackOperation) {
|
|
801
|
+
this.undoRedoService.pushElement(stackOperation);
|
|
802
|
+
this.undoRedoService.clearRedoStack();
|
|
803
|
+
}
|
|
804
|
+
_shouldMerge(operation, prev, operationMeta) {
|
|
805
|
+
if (!prev) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
if (this._transacting) {
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
return operationMeta.shouldMerge && operationMeta.shouldMerge(operation, prev.getLastOperation(), prev);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
__decorateClass([
|
|
815
|
+
inject4(UndoRedoService)
|
|
816
|
+
], HistoryService.prototype, "undoRedoService", 2);
|
|
817
|
+
__decorateClass([
|
|
818
|
+
inject4(OperationRegistry)
|
|
819
|
+
], HistoryService.prototype, "operationRegistry", 2);
|
|
820
|
+
__decorateClass([
|
|
821
|
+
inject4(OperationService)
|
|
822
|
+
], HistoryService.prototype, "operationService", 2);
|
|
823
|
+
__decorateClass([
|
|
824
|
+
inject4(HistoryContext)
|
|
825
|
+
], HistoryService.prototype, "context", 2);
|
|
826
|
+
__decorateClass([
|
|
827
|
+
inject4(HistoryConfig)
|
|
828
|
+
], HistoryService.prototype, "config", 2);
|
|
829
|
+
__decorateClass([
|
|
830
|
+
inject4(HistoryManager)
|
|
831
|
+
], HistoryService.prototype, "historyManager", 2);
|
|
832
|
+
__decorateClass([
|
|
833
|
+
postConstruct3()
|
|
834
|
+
], HistoryService.prototype, "init", 1);
|
|
835
|
+
HistoryService = __decorateClass([
|
|
836
|
+
injectable8()
|
|
837
|
+
], HistoryService);
|
|
838
|
+
|
|
839
|
+
// src/history-container-module.ts
|
|
840
|
+
import { ContainerModule } from "inversify";
|
|
841
|
+
var HistoryContainerModule = new ContainerModule(
|
|
842
|
+
(bind, _unbind, _isBound, _rebind, _unbindAsync, onActivation, _onDeactivation) => {
|
|
843
|
+
bind(OperationRegistry).toSelf().inSingletonScope();
|
|
844
|
+
bind(OperationService).toSelf().inSingletonScope();
|
|
845
|
+
bind(UndoRedoService).toSelf().inSingletonScope();
|
|
846
|
+
bind(HistoryService).toSelf().inSingletonScope();
|
|
847
|
+
bind(HistoryContext).toSelf().inSingletonScope();
|
|
848
|
+
bind(HistoryManager).toSelf().inSingletonScope();
|
|
849
|
+
bind(HistoryStack).toSelf().inSingletonScope();
|
|
850
|
+
bind(HistoryConfig).toSelf().inSingletonScope();
|
|
851
|
+
onActivation(HistoryService, (ctx, historyService) => {
|
|
852
|
+
const historyManager = ctx.container?.parent?.get(HistoryManager) || ctx.container.get(HistoryManager);
|
|
853
|
+
if (!historyManager) {
|
|
854
|
+
return historyService;
|
|
855
|
+
}
|
|
856
|
+
historyService.historyManager = historyManager;
|
|
857
|
+
historyManager.registerHistoryService(historyService);
|
|
858
|
+
return historyService;
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
// src/create-history-plugin.ts
|
|
864
|
+
import { definePluginCreator } from "@flowgram.ai/core";
|
|
865
|
+
var createHistoryPlugin = definePluginCreator({
|
|
866
|
+
onInit: (ctx, opts) => {
|
|
867
|
+
if (opts.onApply) {
|
|
868
|
+
ctx.get(OperationService).onApply(opts.onApply.bind(null, ctx));
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
containerModules: [HistoryContainerModule]
|
|
872
|
+
});
|
|
873
|
+
export {
|
|
874
|
+
HistoryConfig,
|
|
875
|
+
HistoryContainerModule,
|
|
876
|
+
HistoryContext,
|
|
877
|
+
HistoryManager,
|
|
878
|
+
HistoryMergeEventType,
|
|
879
|
+
HistoryService,
|
|
880
|
+
HistoryStack,
|
|
881
|
+
HistoryStackChangeType,
|
|
882
|
+
OperationContribution,
|
|
883
|
+
OperationRegistry,
|
|
884
|
+
OperationService,
|
|
885
|
+
StackOperation,
|
|
886
|
+
UndoRedoChangeType,
|
|
887
|
+
UndoRedoService,
|
|
888
|
+
createHistoryPlugin
|
|
889
|
+
};
|
|
890
|
+
//# sourceMappingURL=index.js.map
|