@tmagic/editor 1.8.0-beta.3 → 1.8.0-beta.5

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.
Files changed (92) hide show
  1. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +15 -1
  2. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
  3. package/dist/es/hooks/use-code-block-edit.js +6 -3
  4. package/dist/es/hooks/use-data-source-edit.js +5 -2
  5. package/dist/es/hooks/use-stage.js +6 -3
  6. package/dist/es/index.js +6 -3
  7. package/dist/es/initService.js +1 -1
  8. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +19 -51
  11. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -36
  12. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +152 -93
  13. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +36 -10
  14. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +334 -208
  15. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +28 -7
  16. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +47 -57
  17. package/dist/es/layouts/history-list/composables.js +118 -25
  18. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
  19. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +5 -1
  20. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
  22. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +3 -3
  23. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  24. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
  25. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  26. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
  27. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +5 -5
  28. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  29. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +1 -1
  30. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
  31. package/dist/es/services/codeBlock.js +105 -42
  32. package/dist/es/services/dataSource.js +85 -35
  33. package/dist/es/services/editor.js +317 -122
  34. package/dist/es/services/history.js +270 -204
  35. package/dist/es/services/keybinding.js +3 -3
  36. package/dist/es/services/ui.js +3 -0
  37. package/dist/es/style.css +65 -10
  38. package/dist/es/utils/content-menu.js +17 -9
  39. package/dist/es/utils/editor.js +35 -1
  40. package/dist/es/utils/history.js +223 -0
  41. package/dist/es/utils/indexed-db.js +86 -0
  42. package/dist/es/utils/undo-redo.js +60 -1
  43. package/dist/style.css +65 -10
  44. package/dist/tmagic-editor.umd.cjs +2047 -954
  45. package/package.json +7 -7
  46. package/src/components/CompareForm.vue +21 -1
  47. package/src/components/ToolButton.vue +2 -2
  48. package/src/hooks/use-code-block-edit.ts +4 -3
  49. package/src/hooks/use-data-source-edit.ts +2 -2
  50. package/src/hooks/use-stage.ts +3 -3
  51. package/src/index.ts +1 -0
  52. package/src/initService.ts +1 -1
  53. package/src/layouts/Framework.vue +1 -1
  54. package/src/layouts/NavMenu.vue +1 -1
  55. package/src/layouts/history-list/Bucket.vue +34 -67
  56. package/src/layouts/history-list/BucketTab.vue +46 -51
  57. package/src/layouts/history-list/GroupRow.vue +150 -98
  58. package/src/layouts/history-list/HistoryDiffDialog.vue +96 -69
  59. package/src/layouts/history-list/HistoryListPanel.vue +298 -111
  60. package/src/layouts/history-list/InitialRow.vue +25 -9
  61. package/src/layouts/history-list/PageTab.vue +58 -48
  62. package/src/layouts/history-list/composables.ts +210 -29
  63. package/src/layouts/page-bar/PageBar.vue +4 -2
  64. package/src/layouts/props-panel/PropsPanel.vue +5 -1
  65. package/src/layouts/sidebar/ComponentListPanel.vue +9 -5
  66. package/src/layouts/sidebar/Sidebar.vue +6 -1
  67. package/src/layouts/sidebar/code-block/CodeBlockList.vue +5 -5
  68. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
  69. package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
  70. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  71. package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
  72. package/src/layouts/sidebar/layer/LayerMenu.vue +19 -11
  73. package/src/layouts/sidebar/layer/LayerNodeTool.vue +7 -4
  74. package/src/layouts/workspace/viewer/Stage.vue +1 -1
  75. package/src/layouts/workspace/viewer/ViewerMenu.vue +8 -8
  76. package/src/services/codeBlock.ts +129 -35
  77. package/src/services/dataSource.ts +105 -34
  78. package/src/services/editor.ts +470 -157
  79. package/src/services/history.ts +312 -203
  80. package/src/services/keybinding.ts +3 -3
  81. package/src/services/ui.ts +7 -0
  82. package/src/theme/history-list-panel.scss +87 -6
  83. package/src/theme/page-bar.scss +0 -4
  84. package/src/theme/props-panel.scss +3 -3
  85. package/src/type.ts +221 -61
  86. package/src/utils/content-menu.ts +18 -9
  87. package/src/utils/editor.ts +41 -1
  88. package/src/utils/history.ts +298 -0
  89. package/src/utils/index.ts +2 -0
  90. package/src/utils/indexed-db.ts +122 -0
  91. package/src/utils/undo-redo.ts +88 -0
  92. package/types/index.d.ts +871 -308
@@ -19,6 +19,7 @@ import type {
19
19
  } from '@editor/type';
20
20
  import { getFormConfig, getFormValue } from '@editor/utils/data-source';
21
21
  import { COPY_DS_STORAGE_KEY } from '@editor/utils/editor';
22
+ import { describeRevertStep } from '@editor/utils/history';
22
23
 
23
24
  import BaseService from './BaseService';
24
25
 
@@ -54,19 +55,6 @@ const canUsePluginMethods = {
54
55
 
55
56
  type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
56
57
 
57
- /**
58
- * 「回滚」生成的新 step 简短描述。
59
- * 仅在 service 层使用,避免依赖 UI 层 composables。
60
- */
61
- const describeRevertDataSourceStep = (step: DataSourceStepValue): string => {
62
- const { oldSchema, newSchema, changeRecords, id } = step;
63
- if (oldSchema === null && newSchema) return `撤回新增 ${newSchema.title || newSchema.id || id}`;
64
- if (oldSchema && newSchema === null) return `还原已删除的 ${oldSchema.title || oldSchema.id || id}`;
65
- const title = newSchema?.title || oldSchema?.title || `${id}`;
66
- const propPath = changeRecords?.[0]?.propPath;
67
- return propPath ? `还原 ${title} · ${propPath}` : `还原 ${title}`;
68
- };
69
-
70
58
  class DataSource extends BaseService {
71
59
  private state = reactive<State>({
72
60
  datasourceTypeList: [],
@@ -78,6 +66,13 @@ class DataSource extends BaseService {
78
66
  methods: {},
79
67
  });
80
68
 
69
+ /**
70
+ * 最近一次写入历史栈的数据源历史记录 uuid。
71
+ * 供 *AndGetHistoryId 系列方法在调用 add / update / remove 后取回本次产生的历史记录 id;
72
+ * 普通方法不读取它,调用前由 *AndGetHistoryId 重置为 null。
73
+ */
74
+ private lastPushedHistoryId: string | null = null;
75
+
81
76
  constructor() {
82
77
  super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
83
78
  }
@@ -129,7 +124,10 @@ class DataSource extends BaseService {
129
124
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
130
125
  * @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
131
126
  */
132
- public add(config: DataSourceSchema, { doNotPushHistory = false, historyDescription }: HistoryOpOptions = {}) {
127
+ public add(
128
+ config: DataSourceSchema,
129
+ { doNotPushHistory = false, historyDescription, historySource }: HistoryOpOptions = {},
130
+ ) {
133
131
  const newConfig = {
134
132
  ...config,
135
133
  id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId(),
@@ -138,7 +136,13 @@ class DataSource extends BaseService {
138
136
  this.get('dataSources').push(newConfig);
139
137
 
140
138
  if (!doNotPushHistory) {
141
- historyService.pushDataSource(newConfig.id, { oldSchema: null, newSchema: newConfig, historyDescription });
139
+ this.lastPushedHistoryId =
140
+ historyService.pushDataSource(newConfig.id, {
141
+ oldSchema: null,
142
+ newSchema: newConfig,
143
+ historyDescription,
144
+ source: historySource,
145
+ })?.uuid ?? null;
142
146
  }
143
147
 
144
148
  this.emit('add', newConfig);
@@ -156,7 +160,12 @@ class DataSource extends BaseService {
156
160
  */
157
161
  public update(
158
162
  config: DataSourceSchema,
159
- { changeRecords = [], doNotPushHistory = false, historyDescription }: HistoryOpOptionsWithChangeRecords = {},
163
+ {
164
+ changeRecords = [],
165
+ doNotPushHistory = false,
166
+ historyDescription,
167
+ historySource,
168
+ }: HistoryOpOptionsWithChangeRecords = {},
160
169
  ) {
161
170
  const dataSources = this.get('dataSources');
162
171
 
@@ -168,12 +177,14 @@ class DataSource extends BaseService {
168
177
  dataSources[index] = newConfig;
169
178
 
170
179
  if (!doNotPushHistory) {
171
- historyService.pushDataSource(newConfig.id, {
172
- oldSchema: oldConfig ? cloneDeep(oldConfig) : null,
173
- newSchema: newConfig,
174
- changeRecords,
175
- historyDescription,
176
- });
180
+ this.lastPushedHistoryId =
181
+ historyService.pushDataSource(newConfig.id, {
182
+ oldSchema: oldConfig ? cloneDeep(oldConfig) : null,
183
+ newSchema: newConfig,
184
+ changeRecords,
185
+ historyDescription,
186
+ source: historySource,
187
+ })?.uuid ?? null;
177
188
  }
178
189
 
179
190
  this.emit('update', newConfig, {
@@ -191,19 +202,59 @@ class DataSource extends BaseService {
191
202
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
192
203
  * @param options.historyDescription 入栈时附带的人类可读描述,用于历史面板展示
193
204
  */
194
- public remove(id: string, { doNotPushHistory = false, historyDescription }: HistoryOpOptions = {}) {
205
+ public remove(id: string, { doNotPushHistory = false, historyDescription, historySource }: HistoryOpOptions = {}) {
195
206
  const dataSources = this.get('dataSources');
196
207
  const index = dataSources.findIndex((ds) => ds.id === id);
197
208
  const oldConfig = index !== -1 ? dataSources[index] : null;
198
209
  dataSources.splice(index, 1);
199
210
 
200
211
  if (oldConfig && !doNotPushHistory) {
201
- historyService.pushDataSource(id, { oldSchema: cloneDeep(oldConfig), newSchema: null, historyDescription });
212
+ this.lastPushedHistoryId =
213
+ historyService.pushDataSource(id, {
214
+ oldSchema: cloneDeep(oldConfig),
215
+ newSchema: null,
216
+ historyDescription,
217
+ source: historySource,
218
+ })?.uuid ?? null;
202
219
  }
203
220
 
204
221
  this.emit('remove', id);
205
222
  }
206
223
 
224
+ // #region AndGetHistoryId
225
+ /**
226
+ * 下列 *AndGetHistoryId 方法与对应的 add / update / remove 行为完全一致,
227
+ * 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link DataSourceStepValue.uuid}),
228
+ * 而非数据源配置。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
229
+ *
230
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时返回 null。
231
+ */
232
+
233
+ /** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
234
+ public addAndGetHistoryId(config: DataSourceSchema, options: HistoryOpOptions = {}): string | null {
235
+ this.lastPushedHistoryId = null;
236
+ this.add(config, options);
237
+ return this.lastPushedHistoryId;
238
+ }
239
+
240
+ /** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
241
+ public updateAndGetHistoryId(
242
+ config: DataSourceSchema,
243
+ options: HistoryOpOptionsWithChangeRecords = {},
244
+ ): string | null {
245
+ this.lastPushedHistoryId = null;
246
+ this.update(config, options);
247
+ return this.lastPushedHistoryId;
248
+ }
249
+
250
+ /** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
251
+ public removeAndGetHistoryId(id: string, options: HistoryOpOptions = {}): string | null {
252
+ this.lastPushedHistoryId = null;
253
+ this.remove(id, options);
254
+ return this.lastPushedHistoryId;
255
+ }
256
+ // #endregion AndGetHistoryId
257
+
207
258
  /**
208
259
  * 撤销指定数据源的最近一次变更。
209
260
  *
@@ -278,10 +329,27 @@ class DataSource extends BaseService {
278
329
  const list = historyService.getDataSourceStepList(id);
279
330
  const entry = list[index];
280
331
  if (!entry?.applied) return null;
281
- const description = `回滚 #${index + 1}: ${describeRevertDataSourceStep(entry.step)}`;
332
+ // 更新类步骤(前后 schema 都存在)必须带 changeRecords 才支持回滚,否则只能整 schema 替换,会冲掉后续无关变更。
333
+ const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
334
+ if (oldSchema && newSchema && !changeRecords?.length) return null;
335
+ const description = `回滚 #${index + 1}: ${describeRevertStep<DataSourceSchema>(entry.step.id, entry.step.diff?.[0], (s) => s.title)}`;
282
336
  return this.applyRevertStep(entry.step, description);
283
337
  }
284
338
 
339
+ /**
340
+ * 通过历史记录 uuid 回滚某条数据源历史步骤,语义同 {@link revert},
341
+ * 仅无需调用方再传 dataSourceId 与 index:内部会按 uuid({@link DataSourceStepValue.uuid})
342
+ * 在全部数据源栈中定位对应步骤后再回滚。
343
+ *
344
+ * @param uuid 目标历史记录的 uuid,通常由 {@link addAndGetHistoryId} 等方法返回
345
+ * @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
346
+ */
347
+ public revertById(uuid: string): DataSourceStepValue | null {
348
+ const location = historyService.findDataSourceStepLocationByUuid(uuid);
349
+ if (!location) return null;
350
+ return this.revert(location.id, location.index);
351
+ }
352
+
285
353
  public createId(): string {
286
354
  return `ds_${guid()}`;
287
355
  }
@@ -362,17 +430,18 @@ class DataSource extends BaseService {
362
430
  * 同构,差异仅在于走对应的公共 add / update / remove 而不是带 doNotPushHistory 的版本。
363
431
  */
364
432
  private applyRevertStep(step: DataSourceStepValue, historyDescription: string): DataSourceStepValue | null {
365
- const { id, oldSchema, newSchema, changeRecords } = step;
433
+ const { id } = step;
434
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
366
435
 
367
436
  // 原本是新增 → revert 即删除
368
- if (oldSchema === null && newSchema) {
369
- this.remove(`${id}`, { historyDescription });
437
+ if (!oldSchema && newSchema) {
438
+ this.remove(`${id}`, { historyDescription, historySource: 'rollback' });
370
439
  return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
371
440
  }
372
441
 
373
442
  // 原本是删除 → revert 即重新加回
374
- if (oldSchema && newSchema === null) {
375
- this.add(cloneDeep(oldSchema), { historyDescription });
443
+ if (oldSchema && !newSchema) {
444
+ this.add(cloneDeep(oldSchema), { historyDescription, historySource: 'rollback' });
376
445
  return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
377
446
  }
378
447
 
@@ -395,11 +464,12 @@ class DataSource extends BaseService {
395
464
  this.update(fallbackToFullReplace ? cloneDeep(oldSchema) : patched, {
396
465
  changeRecords,
397
466
  historyDescription,
467
+ historySource: 'rollback',
398
468
  });
399
469
  return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
400
470
  }
401
471
 
402
- this.update(cloneDeep(oldSchema), { historyDescription });
472
+ this.update(cloneDeep(oldSchema), { historyDescription, historySource: 'rollback' });
403
473
  return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
404
474
  }
405
475
 
@@ -418,10 +488,11 @@ class DataSource extends BaseService {
418
488
  * @param reverse true=撤销,false=重做
419
489
  */
420
490
  private applyHistoryStep(step: DataSourceStepValue, reverse: boolean): void {
421
- const { id, oldSchema, newSchema, changeRecords } = step;
491
+ const { id } = step;
492
+ const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
422
493
 
423
494
  // 新增 / 删除:直接 add 或 remove,不走 patch 逻辑
424
- if (oldSchema === null && newSchema) {
495
+ if (!oldSchema && newSchema) {
425
496
  if (reverse) {
426
497
  this.remove(`${id}`, { doNotPushHistory: true });
427
498
  } else {
@@ -430,7 +501,7 @@ class DataSource extends BaseService {
430
501
  return;
431
502
  }
432
503
 
433
- if (oldSchema && newSchema === null) {
504
+ if (oldSchema && !newSchema) {
434
505
  if (reverse) {
435
506
  this.add(cloneDeep(oldSchema), { doNotPushHistory: true });
436
507
  } else {