@tmagic/editor 1.5.22 → 1.6.0-beta.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.
Files changed (39) hide show
  1. package/LICENSE +1 -1
  2. package/dist/style.css +2 -9
  3. package/dist/tmagic-editor.js +3662 -1728
  4. package/dist/tmagic-editor.umd.cjs +3655 -1721
  5. package/package.json +10 -10
  6. package/src/Editor.vue +5 -0
  7. package/src/editorProps.ts +2 -3
  8. package/src/fields/DataSourceFields.vue +1 -1
  9. package/src/fields/DataSourceMethods.vue +1 -1
  10. package/src/hooks/index.ts +1 -1
  11. package/src/hooks/use-filter.ts +6 -4
  12. package/src/hooks/use-stage.ts +1 -0
  13. package/src/index.ts +1 -1
  14. package/src/initService.ts +64 -11
  15. package/src/layouts/page-bar/PageBarScrollContainer.vue +2 -1
  16. package/src/layouts/props-panel/FormPanel.vue +6 -1
  17. package/src/layouts/props-panel/PropsPanel.vue +6 -0
  18. package/src/layouts/workspace/viewer/Stage.vue +4 -29
  19. package/src/layouts/workspace/viewer/StageOverlay.vue +25 -6
  20. package/src/services/BaseService.ts +18 -9
  21. package/src/services/codeBlock.ts +1 -1
  22. package/src/services/componentList.ts +1 -1
  23. package/src/services/dep.ts +1 -1
  24. package/src/services/editor.ts +1 -1
  25. package/src/services/events.ts +1 -1
  26. package/src/services/history.ts +1 -1
  27. package/src/services/keybinding.ts +12 -0
  28. package/src/services/props.ts +1 -1
  29. package/src/services/stageOverlay.ts +1 -1
  30. package/src/services/ui.ts +1 -1
  31. package/src/theme/stage.scss +6 -11
  32. package/src/type.ts +4 -4
  33. package/src/utils/config.ts +1 -1
  34. package/src/utils/editor.ts +1 -1
  35. package/src/utils/index.ts +1 -1
  36. package/src/utils/logger.ts +1 -1
  37. package/src/utils/props.ts +16 -2
  38. package/src/utils/undo-redo.ts +1 -1
  39. package/types/index.d.ts +65 -145
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.22",
2
+ "version": "1.6.0-beta.0",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -58,14 +58,14 @@
58
58
  "moveable": "^0.53.0",
59
59
  "serialize-javascript": "^6.0.2",
60
60
  "sortablejs": "^1.15.6",
61
- "@tmagic/design": "1.5.22",
62
- "@tmagic/form": "1.5.22",
63
- "@tmagic/table": "1.5.22",
64
- "@tmagic/utils": "1.5.22",
65
- "@tmagic/stage": "1.5.22"
61
+ "@tmagic/design": "1.6.0-beta.0",
62
+ "@tmagic/form": "1.6.0-beta.0",
63
+ "@tmagic/stage": "1.6.0-beta.0",
64
+ "@tmagic/table": "1.6.0-beta.0",
65
+ "@tmagic/utils": "1.6.0-beta.0"
66
66
  },
67
67
  "devDependencies": {
68
- "@types/events": "^3.0.0",
68
+ "@types/events": "^3.0.3",
69
69
  "@types/lodash-es": "^4.17.4",
70
70
  "@types/serialize-javascript": "^5.0.4",
71
71
  "@types/sortablejs": "^1.15.8",
@@ -74,9 +74,9 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "monaco-editor": "^0.48.0",
77
- "typescript": "*",
78
- "vue": ">=3.5.0",
79
- "@tmagic/core": "1.5.22"
77
+ "typescript": "^5.8.3",
78
+ "vue": "^3.5.17",
79
+ "@tmagic/core": "1.6.0-beta.0"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "typescript": {
package/src/Editor.vue CHANGED
@@ -97,6 +97,7 @@
97
97
  :extend-state="extendFormState"
98
98
  :disabled-show-src="disabledShowSrc"
99
99
  @mounted="propsPanelMountedHandler"
100
+ @unmounted="propsPanelUnmountedHandler"
100
101
  @form-error="propsPanelFormErrorHandler"
101
102
  @submit-error="propsPanelSubmitErrorHandler"
102
103
  >
@@ -163,6 +164,7 @@ defineOptions({
163
164
 
164
165
  const emit = defineEmits<{
165
166
  'props-panel-mounted': [instance: InstanceType<typeof FormPanel>];
167
+ 'props-panel-unmounted': [];
166
168
  'update:modelValue': [value: MApp | null];
167
169
  'props-form-error': [e: any];
168
170
  'props-submit-error': [e: any];
@@ -235,6 +237,9 @@ provide<EventBus>('eventBus', new EventEmitter());
235
237
  const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
236
238
  emit('props-panel-mounted', e);
237
239
  };
240
+ const propsPanelUnmountedHandler = () => {
241
+ emit('props-panel-unmounted');
242
+ };
238
243
 
239
244
  const propsPanelSubmitErrorHandler = (e: any) => {
240
245
  emit('props-submit-error', e);
@@ -3,9 +3,8 @@ import type { FormConfig, FormState } from '@tmagic/form';
3
3
  import StageCore, {
4
4
  CONTAINER_HIGHLIGHT_CLASS_NAME,
5
5
  ContainerHighlightType,
6
- type CustomizeMoveableOptionsCallbackConfig,
6
+ type CustomizeMoveableOptions,
7
7
  type GuidesOptions,
8
- type MoveableOptions,
9
8
  RenderType,
10
9
  type UpdateDragEl,
11
10
  } from '@tmagic/stage';
@@ -56,7 +55,7 @@ export interface EditorProps {
56
55
  datasourceConfigs?: Record<string, FormConfig>;
57
56
  datasourceEventMethodList?: Record<string, { events: EventOption[]; methods: EventOption[] }>;
58
57
  /** 画布中组件选中框的移动范围 */
59
- moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
58
+ moveableOptions?: CustomizeMoveableOptions;
60
59
  /** 编辑器初始化时默认选中的组件ID */
61
60
  defaultSelected?: Id;
62
61
  /** 拖入画布中容器时,识别到容器后给容器根dom加上的class */
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="m-editor-data-source-fields">
3
- <MagicTable :data="model[name]" :columns="fieldColumns"></MagicTable>
3
+ <MagicTable :data="model[name]" :columns="fieldColumns" :border="true"></MagicTable>
4
4
 
5
5
  <div class="m-editor-data-source-fields-footer">
6
6
  <TMagicButton size="small" :disabled="disabled" plain @click="newFromJsonHandler()">快速添加</TMagicButton>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="m-editor-data-source-methods">
3
- <MagicTable :data="model[name]" :columns="methodColumns"></MagicTable>
3
+ <MagicTable :data="model[name]" :columns="methodColumns" :border="true"></MagicTable>
4
4
 
5
5
  <div class="m-editor-data-source-methods-footer">
6
6
  <TMagicButton size="small" type="primary" :disabled="disabled" plain @click="createCodeHandler"
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -32,10 +32,12 @@ export const useFilter = (
32
32
  const visible = filterIsMatch(text, node);
33
33
  if (visible && parents.length) {
34
34
  parents.forEach((parent) => {
35
- updateStatus(nodeStatusMap.value!, parent.id, {
36
- visible,
37
- expand: true,
38
- });
35
+ if (text || text.length) {
36
+ updateStatus(nodeStatusMap.value!, parent.id, {
37
+ visible,
38
+ expand: true,
39
+ });
40
+ }
39
41
  });
40
42
  }
41
43
 
@@ -45,6 +45,7 @@ export const useStage = (stageOptions: StageOptions) => {
45
45
  updateDragEl: stageOptions.updateDragEl,
46
46
  guidesOptions: stageOptions.guidesOptions,
47
47
  disabledMultiSelect: stageOptions.disabledMultiSelect,
48
+ disabledRule: stageOptions.disabledRule,
48
49
  });
49
50
 
50
51
  watch(
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
1
+ import { nextTick, onBeforeUnmount, reactive, toRaw, watch } from 'vue';
2
2
  import { cloneDeep } from 'lodash-es';
3
3
 
4
4
  import type TMagicCore from '@tmagic/core';
@@ -21,9 +21,11 @@ import {
21
21
  NODE_CONDS_KEY,
22
22
  NodeType,
23
23
  Target,
24
+ updateNode,
24
25
  } from '@tmagic/core';
25
26
  import { ChangeRecord } from '@tmagic/form';
26
- import { getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils';
27
+ import StageCore from '@tmagic/stage';
28
+ import { getDepNodeIds, getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils';
27
29
 
28
30
  import PropsPanel from './layouts/PropsPanel.vue';
29
31
  import { isIncludeDataSource } from './utils/editor';
@@ -233,14 +235,15 @@ export const initServiceEvents = (
233
235
  ) => {
234
236
  let getTMagicAppPrimise: Promise<TMagicCore | undefined> | null = null;
235
237
 
236
- const getTMagicApp = (): Promise<TMagicCore | undefined> => {
237
- const renderer = editorService.get('stage')?.renderer;
238
+ const getTMagicApp = async (): Promise<TMagicCore | undefined> => {
239
+ const stage = await getStage();
240
+ const { renderer } = stage;
238
241
  if (!renderer) {
239
- return Promise.resolve(void 0);
242
+ return void 0;
240
243
  }
241
244
 
242
245
  if (renderer.runtime) {
243
- return Promise.resolve(renderer.runtime.getApp?.());
246
+ return renderer.runtime.getApp?.();
244
247
  }
245
248
 
246
249
  if (getTMagicAppPrimise) {
@@ -344,6 +347,60 @@ export const initServiceEvents = (
344
347
  },
345
348
  );
346
349
 
350
+ const getStage = (): Promise<StageCore> => {
351
+ const stage = editorService.get('stage');
352
+ if (stage) {
353
+ return Promise.resolve(stage);
354
+ }
355
+
356
+ return new Promise<StageCore>((resolve) => {
357
+ const unWatch = watch(
358
+ () => editorService.get('stage'),
359
+ (stage) => {
360
+ if (stage) {
361
+ resolve(stage);
362
+ nextTick(() => {
363
+ unWatch();
364
+ });
365
+ }
366
+ },
367
+ );
368
+ });
369
+ };
370
+
371
+ const updateStageDsl = async (value: MApp | null) => {
372
+ const stage = await getStage();
373
+
374
+ const runtime = await stage.renderer?.getRuntime();
375
+ const app = await getTMagicApp();
376
+
377
+ if (!app?.dataSourceManager) {
378
+ runtime?.updateRootConfig?.(cloneDeep(toRaw(value))!);
379
+ }
380
+
381
+ const page = editorService.get('page');
382
+ const node = editorService.get('node');
383
+ page?.id && runtime?.updatePageId?.(page.id);
384
+ setTimeout(() => {
385
+ node && stage?.select(toRaw(node.id));
386
+ });
387
+
388
+ if (value) {
389
+ depService.clearIdleTasks();
390
+
391
+ await (typeof Worker === 'undefined' ? collectIdle(value.items, true) : depService.collectByWorker(value));
392
+
393
+ const dsl = cloneDeep(toRaw(value));
394
+ if (dsl.dataSources && dsl.dataSourceDeps && app?.dataSourceManager) {
395
+ for (const node of getNodes(getDepNodeIds(dsl.dataSourceDeps), dsl.items)) {
396
+ updateNode(app.dataSourceManager.compiledNode(node), dsl);
397
+ }
398
+ }
399
+
400
+ runtime?.updateRootConfig?.(dsl);
401
+ }
402
+ };
403
+
347
404
  const initDataSourceDepTarget = (ds: DataSourceSchema) => {
348
405
  depService.addTarget(createDataSourceTarget(ds, reactive({})));
349
406
  depService.addTarget(createDataSourceMethodTarget(ds, reactive({})));
@@ -370,11 +427,7 @@ export const initServiceEvents = (
370
427
  }
371
428
 
372
429
  if (Array.isArray(value.items)) {
373
- depService.clearIdleTasks();
374
-
375
- (typeof Worker === 'undefined' ? collectIdle(value.items, true) : depService.collectByWorker(value)).then(() => {
376
- updateStageNodes(value.items);
377
- });
430
+ updateStageDsl(value);
378
431
  } else {
379
432
  depService.clear();
380
433
  delete value.dataSourceDeps;
@@ -78,6 +78,7 @@ const resizeObserver = new ResizeObserver(() => {
78
78
 
79
79
  onMounted(() => {
80
80
  pageBarEl.value && resizeObserver.observe(pageBarEl.value);
81
+ itemsContainerEl.value && resizeObserver.observe(itemsContainerEl.value);
81
82
  });
82
83
 
83
84
  onBeforeUnmount(() => {
@@ -136,7 +137,7 @@ watch(
136
137
  let beforeDragList: Id[] = [];
137
138
  const options = {
138
139
  ...{
139
- dataIdAttr: 'page-id', // 获取排序后的数据
140
+ dataIdAttr: 'data-page-id', // 获取排序后的数据
140
141
  onStart: async (event: SortableEvent) => {
141
142
  if (typeof props.pageBarSortOptions?.beforeStart === 'function') {
142
143
  await props.pageBarSortOptions.beforeStart(event, sortable);
@@ -42,7 +42,7 @@
42
42
  </template>
43
43
 
44
44
  <script setup lang="ts">
45
- import { computed, getCurrentInstance, inject, onMounted, ref, useTemplateRef, watchEffect } from 'vue';
45
+ import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, useTemplateRef, watchEffect } from 'vue';
46
46
  import { Document as DocumentIcon } from '@element-plus/icons-vue';
47
47
 
48
48
  import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
@@ -78,6 +78,7 @@ const emit = defineEmits<{
78
78
  'submit-error': [e: any];
79
79
  'form-error': [e: any];
80
80
  mounted: [internalInstance: any];
81
+ unmounted: [];
81
82
  }>();
82
83
 
83
84
  const services = useServices();
@@ -104,6 +105,10 @@ onMounted(() => {
104
105
  emit('mounted', internalInstance?.proxy);
105
106
  });
106
107
 
108
+ onUnmounted(() => {
109
+ emit('unmounted');
110
+ });
111
+
107
112
  const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
108
113
  try {
109
114
  const values = await configFormRef.value?.submitForm();
@@ -13,6 +13,7 @@
13
13
  @submit-error="errorHandler"
14
14
  @form-error="errorHandler"
15
15
  @mounted="mountedHandler"
16
+ @unmounted="unmountedHandler"
16
17
  ></FormPanel>
17
18
 
18
19
  <Resizer v-if="showStylePanel" @change="widthChange"></Resizer>
@@ -89,6 +90,7 @@ const emit = defineEmits<{
89
90
  'submit-error': [e: any];
90
91
  'form-error': [e: any];
91
92
  mounted: [internalInstance: InstanceType<typeof FormPanel>];
93
+ unmounted: [];
92
94
  }>();
93
95
 
94
96
  const { editorService, uiService, propsService, storageService } = useServices();
@@ -165,6 +167,10 @@ const mountedHandler = () => {
165
167
  }
166
168
  };
167
169
 
170
+ const unmountedHandler = () => {
171
+ emit('unmounted');
172
+ };
173
+
168
174
  const propsPanelEl = useTemplateRef('propsPanel');
169
175
  const propsPanelWidth = ref(
170
176
  storageService.getItem(PROPS_PANEL_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) || 300,
@@ -43,20 +43,9 @@
43
43
  </template>
44
44
 
45
45
  <script lang="ts" setup>
46
- import {
47
- computed,
48
- markRaw,
49
- nextTick,
50
- onBeforeUnmount,
51
- onMounted,
52
- toRaw,
53
- useTemplateRef,
54
- watch,
55
- watchEffect,
56
- } from 'vue';
57
- import { cloneDeep } from 'lodash-es';
58
-
59
- import type { MApp, MContainer } from '@tmagic/core';
46
+ import { computed, markRaw, nextTick, onBeforeUnmount, onMounted, useTemplateRef, watch, watchEffect } from 'vue';
47
+
48
+ import type { MContainer } from '@tmagic/core';
60
49
  import StageCore, { getOffset, Runtime } from '@tmagic/stage';
61
50
  import { calcValueByFontsize, getIdFromEl } from '@tmagic/utils';
62
51
 
@@ -130,12 +119,6 @@ watchEffect(() => {
130
119
 
131
120
  stage.on('runtime-ready', (rt) => {
132
121
  runtime = rt;
133
- // toRaw返回的值是一个引用而非快照,需要cloneDeep
134
- root.value && runtime?.updateRootConfig?.(cloneDeep(toRaw(root.value)));
135
- page.value?.id && runtime?.updatePageId?.(page.value.id);
136
- setTimeout(() => {
137
- node.value && stage?.select(toRaw(node.value.id));
138
- });
139
122
  });
140
123
  });
141
124
 
@@ -184,14 +167,6 @@ watch(page, (page) => {
184
167
  }
185
168
  });
186
169
 
187
- const rootChangeHandler = (root: MApp) => {
188
- if (runtime && root) {
189
- runtime.updateRootConfig?.(cloneDeep(toRaw(root)));
190
- }
191
- };
192
-
193
- editorService.on('root-change', rootChangeHandler);
194
-
195
170
  const resizeObserver = new ResizeObserver((entries) => {
196
171
  for (const { contentRect } of entries) {
197
172
  uiService.set('stageContainerRect', {
@@ -210,10 +185,10 @@ onMounted(() => {
210
185
 
211
186
  onBeforeUnmount(() => {
212
187
  stage?.destroy();
188
+ stage = null;
213
189
  resizeObserver.disconnect();
214
190
  editorService.set('stage', null);
215
191
  keybindingService.unregisterEl('stage');
216
- editorService.off('root-change', rootChangeHandler);
217
192
  });
218
193
 
219
194
  const parseDSL = getEditorConfig('parseDSL');
@@ -1,9 +1,19 @@
1
1
  <template>
2
- <div v-if="stageOverlayVisible" class="m-editor-stage-overlay" @click="closeOverlayHandler">
3
- <TMagicIcon class="m-editor-stage-overlay-close" :size="'20'" @click="closeOverlayHandler"
2
+ <div v-if="stageOverlayVisible" class="m-editor-stage-overlay">
3
+ <TMagicIcon class="m-editor-stage-overlay-close" :size="'30'" @click="closeOverlayHandler"
4
4
  ><CloseBold
5
5
  /></TMagicIcon>
6
- <div ref="stageOverlay" class="m-editor-stage-overlay-container" :style="style" @click.stop></div>
6
+
7
+ <ScrollViewer
8
+ class="m-editor-stage"
9
+ :width="wrapWidth"
10
+ :height="wrapHeight"
11
+ :wrap-width="columnWidth.center"
12
+ :wrap-height="frameworkRect.height"
13
+ :zoom="zoom"
14
+ >
15
+ <div ref="stageOverlay" class="m-editor-stage-container" :style="style"></div>
16
+ </ScrollViewer>
7
17
  </div>
8
18
  </template>
9
19
 
@@ -13,10 +23,11 @@ import { CloseBold } from '@element-plus/icons-vue';
13
23
 
14
24
  import { TMagicIcon } from '@tmagic/design';
15
25
 
26
+ import ScrollViewer from '@editor/components/ScrollViewer.vue';
16
27
  import { useServices } from '@editor/hooks/use-services';
17
28
  import type { StageOptions } from '@editor/type';
18
29
 
19
- const { stageOverlayService, editorService } = useServices();
30
+ const { stageOverlayService, editorService, uiService } = useServices();
20
31
 
21
32
  const stageOptions = inject<StageOptions>('stageOptions');
22
33
 
@@ -26,10 +37,12 @@ const stageOverlayVisible = computed(() => stageOverlayService.get('stageOverlay
26
37
  const wrapWidth = computed(() => stageOverlayService.get('wrapWidth'));
27
38
  const wrapHeight = computed(() => stageOverlayService.get('wrapHeight'));
28
39
  const stage = computed(() => editorService.get('stage'));
40
+ const zoom = computed(() => uiService.get('zoom'));
41
+ const columnWidth = computed(() => uiService.get('columnWidth'));
42
+ const frameworkRect = computed(() => uiService.get('frameworkRect'));
29
43
 
30
44
  const style = computed(() => ({
31
- width: `${wrapWidth.value}px`,
32
- height: `${wrapHeight.value}px`,
45
+ transform: `scale(${zoom.value})`,
33
46
  }));
34
47
 
35
48
  watch(stage, (stage) => {
@@ -43,6 +56,12 @@ watch(stage, (stage) => {
43
56
  }
44
57
  });
45
58
 
59
+ watch(zoom, (zoom) => {
60
+ const stage = stageOverlayService.get('stage');
61
+ if (!stage || !zoom) return;
62
+ stage.setZoom(zoom);
63
+ });
64
+
46
65
  watch(stageOverlayEl, (stageOverlay) => {
47
66
  const subStage = stageOverlayService.createStage(stageOptions);
48
67
  stageOverlayService.set('stage', subStage);
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * Tencent is pleased to support the open source community by making TMagicEditor available.
4
4
  *
5
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
5
+ * Copyright (C) 2025 Tencent. All rights reserved.
6
6
  *
7
7
  * Licensed under the Apache License, Version 2.0 (the "License");
8
8
  * you may not use this file except in compliance with the License.
@@ -199,24 +199,33 @@ export default class extends EventEmitter {
199
199
  * @deprecated 请使用usePlugin代替
200
200
  */
201
201
  public use(options: Record<string, Function>) {
202
- Object.entries(options).forEach(([methodName, method]: [string, Function]) => {
202
+ for (const [methodName, method] of Object.entries(options)) {
203
203
  if (typeof method === 'function') this.middleware[methodName].push(method);
204
- });
204
+ }
205
205
  }
206
206
 
207
207
  public usePlugin(options: Record<string, Function>) {
208
- Object.entries(options).forEach(([methodName, method]: [string, Function]) => {
208
+ for (const [methodName, method] of Object.entries(options)) {
209
209
  if (typeof method === 'function') this.pluginOptionsList[methodName].push(method);
210
- });
210
+ }
211
+ }
212
+
213
+ public removePlugin(options: Record<string, Function>) {
214
+ for (const [methodName, method] of Object.entries(options)) {
215
+ if (Array.isArray(this.pluginOptionsList[methodName])) {
216
+ this.pluginOptionsList[methodName] = this.pluginOptionsList[methodName].filter((item) => item !== method);
217
+ }
218
+ }
211
219
  }
212
220
 
213
221
  public removeAllPlugins() {
214
- Object.keys(this.pluginOptionsList).forEach((key) => {
222
+ for (const key of Object.keys(this.pluginOptionsList)) {
215
223
  this.pluginOptionsList[key] = [];
216
- });
217
- Object.keys(this.middleware).forEach((key) => {
224
+ }
225
+
226
+ for (const key of Object.keys(this.middleware)) {
218
227
  this.middleware[key] = [];
219
- });
228
+ }
220
229
  }
221
230
 
222
231
  private async doTask() {
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -145,6 +145,18 @@ class Keybinding extends BaseService {
145
145
  for (const [type = '', eventType = 'keydown'] of when) {
146
146
  const cacheItem: KeyBindingCacheItem = { type, command, keybinding, eventType, bound: false };
147
147
 
148
+ if (
149
+ this.bindingList.find(
150
+ (item) =>
151
+ item.command === command &&
152
+ item.eventType === eventType &&
153
+ item.type === type &&
154
+ item.keybinding === keybinding,
155
+ )
156
+ ) {
157
+ continue;
158
+ }
159
+
148
160
  this.bindingList.push(cacheItem);
149
161
  }
150
162
  }
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -97,9 +97,9 @@ class StageOverlay extends BaseService {
97
97
  public createStage(stageOptions: StageOptions = {}) {
98
98
  return useStage({
99
99
  ...stageOptions,
100
- zoom: 1,
101
100
  runtimeUrl: '',
102
101
  autoScrollIntoView: false,
102
+ disabledRule: true,
103
103
  render: async (stage: StageCore) => {
104
104
  this.copyDocumentElement();
105
105
 
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.