jvs-ppt-ui 0.0.14 → 0.0.16

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 (67) hide show
  1. package/dist/lib/check_imgs.html +18 -0
  2. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.css +187 -27
  3. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.js +1 -1
  4. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.json +345 -65
  5. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.ttf +0 -0
  6. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.woff +0 -0
  7. package/dist/lib/jvs-ppt-ui/iconfont/iconfont.woff2 +0 -0
  8. package/dist/lib/jvs-ppt-ui/img/al.png +0 -0
  9. package/dist/lib/jvs-ppt-ui/img/d.png +0 -0
  10. package/dist/lib/jvs-ppt-ui/img/dl.png +0 -0
  11. package/dist/lib/jvs-ppt-ui/img/l.png +0 -0
  12. package/dist/lib/jvs-ppt-ui.es.js +44973 -43349
  13. package/dist/lib/jvs-ppt-ui.umd.js +102 -102
  14. package/dist/lib/style.css +1 -1
  15. package/package.json +1 -1
  16. package/scripts/check-icons.mjs +22 -0
  17. package/scripts/patch-anim-icons.mjs +45 -0
  18. package/src/assets/iconfont/iconfont.css +187 -27
  19. package/src/assets/iconfont/iconfont.js +1 -1
  20. package/src/assets/iconfont/iconfont.json +345 -65
  21. package/src/assets/iconfont/iconfont.ttf +0 -0
  22. package/src/assets/iconfont/iconfont.woff +0 -0
  23. package/src/assets/iconfont/iconfont.woff2 +0 -0
  24. package/src/components/CanvasArea.vue +218 -193
  25. package/src/components/ChartTypeSelector.vue +509 -148
  26. package/src/components/Header.vue +327 -44
  27. package/src/components/PresentationGroupChild.vue +44 -13
  28. package/src/components/PresentationMode.vue +84 -33
  29. package/src/components/QuickBorderPopover.vue +51 -11
  30. package/src/components/QuickColorPopover.vue +65 -21
  31. package/src/components/SidebarLeft.vue +286 -107
  32. package/src/components/SidebarRight.vue +45 -38
  33. package/src/components/TemplateGallery.vue +1110 -1237
  34. package/src/components/ToastNotification.vue +2 -2
  35. package/src/components/Toolbar.vue +825 -733
  36. package/src/components/canvas/CanvasQuickToolbar.vue +17 -30
  37. package/src/components/canvas/elements/CanvasGroupElement.vue +6 -0
  38. package/src/components/canvas/elements/CanvasShapeElement.vue +198 -38
  39. package/src/components/canvas/elements/CanvasTextElement.vue +4 -3
  40. package/src/components/sidebar/common/SidebarAlignGroup.vue +104 -50
  41. package/src/components/sidebar/common/SidebarBorderAccordion.vue +375 -148
  42. package/src/components/sidebar/common/SidebarPositionGroup.vue +358 -149
  43. package/src/components/sidebar/common/SidebarReflectionAccordion.vue +429 -203
  44. package/src/components/sidebar/common/SidebarShadowAccordion.vue +844 -416
  45. package/src/components/sidebar/panels/GradientEditor.vue +671 -316
  46. package/src/components/sidebar/panels/LayoutThumbSvg.vue +33 -16
  47. package/src/components/sidebar/panels/SidebarAnimationPanel.vue +701 -138
  48. package/src/components/sidebar/panels/SidebarChartFormatPanel.vue +3 -3
  49. package/src/components/sidebar/panels/SidebarDesignPanel.vue +1754 -416
  50. package/src/components/sidebar/panels/SidebarMediaFormatPanel.vue +12 -0
  51. package/src/components/sidebar/panels/SidebarShapeFormatPanel.vue +813 -174
  52. package/src/components/sidebar/panels/SidebarTableFormatPanel.vue +2 -2
  53. package/src/components/sidebar/panels/SidebarTextFormatPanel.vue +1938 -1427
  54. package/src/components/sidebar/panels/SidebarTransitionPanel.vue +791 -210
  55. package/src/composables/canvas/useCanvasShortcuts.ts +20 -6
  56. package/src/composables/canvas/useCanvasText.ts +1 -1
  57. package/src/composables/canvas/useCanvasTransform.ts +44 -0
  58. package/src/composables/presetThemes.ts +43 -44
  59. package/src/composables/sidebar/useSidebarAnimation.ts +101 -25
  60. package/src/composables/sidebar/useSidebarDesign.ts +30 -11
  61. package/src/composables/sidebar/useSidebarElementTransform.ts +10 -1
  62. package/src/composables/sidebar/useSidebarImage.ts +10 -2
  63. package/src/composables/sidebar/useSidebarShapeFormat.ts +562 -107
  64. package/src/composables/useSlides.ts +331 -153
  65. package/src/main.ts +2 -0
  66. package/src/style.css +926 -359
  67. package/src/utils/canvas/elementStyles.ts +42 -1
@@ -468,7 +468,7 @@ function handleFpClick(el: any) {
468
468
 
469
469
  <!-- Shape Selector (Only for shapes, single selection) -->
470
470
  <template v-if="!((selectedElementIds || []).length > 1) && selectedElement.type === 'shape' && !isImagePlaceholderHelper(selectedElement)">
471
- <el-popover v-model:visible="quickShapePopoverVisible" placement="bottom" :width="380" trigger="click"
471
+ <el-popover v-model:visible="quickShapePopoverVisible" placement="bottom" :offset="4" :width="380" trigger="click"
472
472
  popper-class="shape-popover-popper" :show-arrow="false">
473
473
  <template #reference>
474
474
  <button class="quick-toolbar-btn shape-dropdown-btn" title="更换形状"
@@ -539,7 +539,7 @@ function handleFpClick(el: any) {
539
539
 
540
540
  <!-- Line Selector (Only for lines) -->
541
541
  <template v-if="selectedElement.type === 'line'">
542
- <el-popover v-model:visible="quickLineTypePopoverVisible" placement="bottom" :width="240" trigger="click"
542
+ <el-popover v-model:visible="quickLineTypePopoverVisible" placement="bottom" :offset="4" :width="240" trigger="click"
543
543
  popper-class="line-dropdown-popper" :show-arrow="false">
544
544
  <template #reference>
545
545
  <button class="quick-toolbar-btn shape-dropdown-btn" title="更换线条"
@@ -606,44 +606,31 @@ function handleFpClick(el: any) {
606
606
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'solid' }"
607
607
  @click="emit('changeLineType', selectedElement, 'solid'); quickLineTypePopoverVisible = false"
608
608
  title="实线">
609
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg">
610
- <line x1="8" y1="32" x2="32" y2="8" />
611
- </svg>
609
+ <img src="/jvs-ppt-ui/img/l.png" class="line-img" alt="实线" />
612
610
  </div>
613
611
  <div class="line-grid-item"
614
612
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'dashed' }"
615
613
  @click="emit('changeLineType', selectedElement, 'dashed'); quickLineTypePopoverVisible = false"
616
614
  title="虚线">
617
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg dashed">
618
- <line x1="8" y1="32" x2="32" y2="8" />
619
- </svg>
615
+ <img src="/jvs-ppt-ui/img/d.png" class="line-img" alt="虚线" />
620
616
  </div>
621
617
  <div class="line-grid-item"
622
618
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'arrow' }"
623
619
  @click="emit('changeLineType', selectedElement, 'arrow'); quickLineTypePopoverVisible = false"
624
620
  title="单向箭头线">
625
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
626
- <line x1="8" y1="32" x2="30" y2="10" />
627
- <polygon points="30,10 22,12 28,18" fill="currentColor" class="line-polygon-head" />
628
- </svg>
621
+ <img src="/jvs-ppt-ui/img/al.png" class="line-img" alt="实线单向箭头" />
629
622
  </div>
630
623
  <div class="line-grid-item"
631
624
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'dashed-arrow' }"
632
625
  @click="emit('changeLineType', selectedElement, 'dashed-arrow'); quickLineTypePopoverVisible = false"
633
626
  title="虚线单向箭头线">
634
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
635
- <line x1="8" y1="32" x2="30" y2="10" class="line-thumb-svg dashed" />
636
- <polygon points="30,10 22,12 28,18" fill="currentColor" class="line-polygon-head" />
637
- </svg>
627
+ <img src="/jvs-ppt-ui/img/dl.png" class="line-img" alt="虚线单向箭头" />
638
628
  </div>
639
629
  <div class="line-grid-item"
640
630
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'dot' }"
641
631
  @click="emit('changeLineType', selectedElement, 'dot'); quickLineTypePopoverVisible = false"
642
- title="圆点连接线">
643
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
644
- <line x1="8" y1="32" x2="28" y2="12" />
645
- <circle cx="30" cy="10" r="3.2" fill="currentColor" class="line-polygon-head" />
646
- </svg>
632
+ title="直线">
633
+ <img src="/jvs-ppt-ui/img/l.png" class="line-img" alt="直线" />
647
634
  </div>
648
635
  </div>
649
636
 
@@ -653,7 +640,7 @@ function handleFpClick(el: any) {
653
640
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'elbow' }"
654
641
  @click="emit('changeLineType', selectedElement, 'elbow'); quickLineTypePopoverVisible = false"
655
642
  title="直角折线连接线">
656
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
643
+ <svg viewBox="0 0 40 40" width="16" height="16" class="line-thumb-svg fill-none">
657
644
  <path d="M10 10 v20 h20" />
658
645
  <polygon points="30,30 24,27 24,33" fill="currentColor" class="line-polygon-head" />
659
646
  </svg>
@@ -662,7 +649,7 @@ function handleFpClick(el: any) {
662
649
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'double-elbow' }"
663
650
  @click="emit('changeLineType', selectedElement, 'double-elbow'); quickLineTypePopoverVisible = false"
664
651
  title="双折折线连接线">
665
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
652
+ <svg viewBox="0 0 40 40" width="16" height="16" class="line-thumb-svg fill-none">
666
653
  <path d="M10 10 h10 v20 h10" />
667
654
  <polygon points="30,30 24,27 24,33" fill="currentColor" class="line-polygon-head" />
668
655
  </svg>
@@ -671,7 +658,7 @@ function handleFpClick(el: any) {
671
658
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'curve' }"
672
659
  @click="emit('changeLineType', selectedElement, 'curve'); quickLineTypePopoverVisible = false"
673
660
  title="圆弧曲线连接线">
674
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
661
+ <svg viewBox="0 0 40 40" width="16" height="16" class="line-thumb-svg fill-none">
675
662
  <path d="M10 10 Q30 10 30 30" />
676
663
  <polygon points="30,30 27,24 33,24" fill="currentColor" class="line-polygon-head" />
677
664
  </svg>
@@ -680,7 +667,7 @@ function handleFpClick(el: any) {
680
667
  :class="{ active: getSelectedLineTypeHelper(selectedElement) === 'wavy' }"
681
668
  @click="emit('changeLineType', selectedElement, 'wavy'); quickLineTypePopoverVisible = false"
682
669
  title="波浪曲线连接线">
683
- <svg viewBox="0 0 40 40" width="28" height="28" class="line-thumb-svg fill-none">
670
+ <svg viewBox="0 0 40 40" width="16" height="16" class="line-thumb-svg fill-none">
684
671
  <path d="M10 10 C20 10 20 30 30 30" />
685
672
  <polygon points="30,30 24,27 24,33" fill="currentColor" class="line-polygon-head" />
686
673
  </svg>
@@ -754,7 +741,7 @@ function handleFpClick(el: any) {
754
741
  <!-- Multiple Elements Alignment & Grouping (When multiple elements are selected) -->
755
742
  <template v-if="(selectedElementIds || []).length > 1">
756
743
  <!-- Alignment Popover -->
757
- <el-popover v-model:visible="quickMultiAlignPopoverVisible" placement="top" :width="168" trigger="click"
744
+ <el-popover v-model:visible="quickMultiAlignPopoverVisible" placement="top" :offset="4" :width="168" trigger="click"
758
745
  popper-class="quick-multi-align-popover" :show-arrow="false">
759
746
  <template #reference>
760
747
  <button class="quick-toolbar-btn" :class="{ 'panel-active': quickMultiAlignPopoverVisible }" title="对齐排版">
@@ -887,7 +874,7 @@ function handleFpClick(el: any) {
887
874
  </el-select>
888
875
 
889
876
  <!-- Text Align / Font Style Popover -->
890
- <el-popover v-model:visible="quickAlignPopoverVisible" placement="bottom" :width="260" trigger="click"
877
+ <el-popover v-model:visible="quickAlignPopoverVisible" placement="bottom" :offset="4" :width="260" trigger="click"
891
878
  popper-class="quick-align-popover" :show-arrow="false">
892
879
  <template #reference>
893
880
  <button class="quick-toolbar-btn" title="对齐与样式"
@@ -1021,7 +1008,7 @@ function handleFpClick(el: any) {
1021
1008
  </el-popover>
1022
1009
 
1023
1010
  <!-- Unordered List Quick Button & Popover -->
1024
- <el-popover v-model:visible="isQuickUlVisible" placement="bottom" :width="250" trigger="click"
1011
+ <el-popover v-model:visible="isQuickUlVisible" placement="bottom" :offset="4" :width="250" trigger="click"
1025
1012
  popper-class="list-popover-panel" :show-arrow="false">
1026
1013
  <template #reference>
1027
1014
  <button class="quick-toolbar-btn"
@@ -1086,7 +1073,7 @@ function handleFpClick(el: any) {
1086
1073
  </el-popover>
1087
1074
 
1088
1075
  <!-- Ordered List Quick Button & Popover -->
1089
- <el-popover v-model:visible="isQuickOlVisible" placement="bottom" :width="250" trigger="click"
1076
+ <el-popover v-model:visible="isQuickOlVisible" placement="bottom" :offset="4" :width="250" trigger="click"
1090
1077
  popper-class="list-popover-panel" :show-arrow="false">
1091
1078
  <template #reference>
1092
1079
  <button class="quick-toolbar-btn"
@@ -1167,7 +1154,7 @@ function handleFpClick(el: any) {
1167
1154
  </button>
1168
1155
 
1169
1156
  <!-- Z-Index Layer Control -->
1170
- <el-popover v-model:visible="quickZIndexPopoverVisible" placement="top" :width="140" trigger="click"
1157
+ <el-popover v-model:visible="quickZIndexPopoverVisible" placement="top" :offset="4" :width="140" trigger="click"
1171
1158
  popper-class="quick-z-index-popover" :show-arrow="false">
1172
1159
  <template #reference>
1173
1160
  <button class="quick-toolbar-btn" :class="{ 'panel-active': quickZIndexPopoverVisible }" title="层级">
@@ -63,6 +63,8 @@ const emit = defineEmits<{
63
63
  (e: 'textBlur', ev: FocusEvent, child: any): void
64
64
  (e: 'textInput', ev: Event, child: any): void
65
65
  (e: 'textKeyDown', ev: KeyboardEvent, child: any): void
66
+ (e: 'textMouseMove', ev: MouseEvent, child: any): void
67
+ (e: 'textMouseLeave', ev: MouseEvent, child: any): void
66
68
  (e: 'startEditing', id: string, ev?: MouseEvent): void
67
69
  (e: 'formulaDblClick', child: any): void
68
70
  (e: 'tableDblClick', ev: MouseEvent, child: any): void
@@ -118,6 +120,8 @@ function sortGroupElements(elements: any[]) {
118
120
  @focusout="emit('textBlur', $event, child)"
119
121
  @input="emit('textInput', $event, child)"
120
122
  @keydown="emit('textKeyDown', $event, child)"
123
+ @mousemove="emit('textMouseMove', $event, child)"
124
+ @mouseleave="emit('textMouseLeave', $event, child)"
121
125
  @dblclick="emit('startEditing', child.id)"
122
126
  @formula-dbl-click="emit('formulaDblClick', child)"
123
127
  @table-dbl-click="emit('tableDblClick', $event, child)"
@@ -193,6 +197,8 @@ function sortGroupElements(elements: any[]) {
193
197
  @text-blur="(ev, c) => emit('textBlur', ev, c)"
194
198
  @text-input="(ev, c) => emit('textInput', ev, c)"
195
199
  @text-key-down="(ev, c) => emit('textKeyDown', ev, c)"
200
+ @text-mouse-move="(ev, c) => emit('textMouseMove', ev, c)"
201
+ @text-mouse-leave="(ev, c) => emit('textMouseLeave', ev, c)"
196
202
  @start-editing="(id, ev) => emit('startEditing', id, ev)"
197
203
  />
198
204
  </template>
@@ -2,6 +2,8 @@
2
2
  import { computed, ref, watch, onBeforeUnmount, nextTick } from 'vue'
3
3
  import {
4
4
  getElementPattern,
5
+ isElementPatternTile,
6
+ getElementPatternTileSize,
5
7
  getElementGradientConfig,
6
8
  getElementGradientCss,
7
9
  getSvgGradientCoords,
@@ -24,7 +26,8 @@ import { createTextSelectionOverlay, type TextSelectionOverlayHandle } from '../
24
26
 
25
27
  const { shapeFormatPainter, isReadonly } = useSlides()
26
28
  const isTextPaintActive = computed(() => !!(shapeFormatPainter.value && shapeFormatPainter.value.mode === 'text-range'))
27
- const isTextSelectable = computed(() => (props.isEditingText && props.selectedElementId === props.element.id) || isTextPaintActive.value)
29
+ const svgIdKey = computed(() => props.isReflection ? `${props.element.id}-refl` : `${props.element.id}`)
30
+ const isTextSelectable = computed(() => !props.isReflection && ((props.isEditingText && props.selectedElementId === props.element.id) || isTextPaintActive.value))
28
31
 
29
32
  function getEffectiveBorderWidth(el: any): number {
30
33
  if (props.isImagePlaceholder && props.isImagePlaceholder(el) && el.style?.enableBorder !== true) return 0
@@ -35,6 +38,23 @@ function getEffectiveBorderWidth(el: any): number {
35
38
  return 0
36
39
  }
37
40
 
41
+ function getShapePatternTransform(el: any, isTriangle = false): string | undefined {
42
+ if (!el || !isElementPatternTile(el)) return undefined
43
+ const { width, height } = getElementWidthHeight(el)
44
+ if (isTriangle) {
45
+ const sx = width > 0 ? 100 / width : 1
46
+ const sy = height > 0 ? 100 / height : 1
47
+ return `scale(${sx}, ${sy})`
48
+ }
49
+ const vbW = el.viewBox ? el.viewBox[0] : (width || 200)
50
+ const vbH = el.viewBox ? el.viewBox[1] : (height || 200)
51
+ const hasBorder = hasElementBorder(el)
52
+ const bw = hasBorder ? getEffectiveBorderWidth(el) : 0
53
+ const effW = bw > 0 ? Math.max(1, width - bw) : Math.max(1, width)
54
+ const effH = bw > 0 ? Math.max(1, height - bw) : Math.max(1, height)
55
+ return `scale(${vbW / effW}, ${vbH / effH})`
56
+ }
57
+
38
58
  function getShapeSvgTransform(el: any): string | undefined {
39
59
  if (!el) return undefined
40
60
  const { width, height } = getElementWidthHeight(el)
@@ -65,7 +85,7 @@ function getShapeSvgTransform(el: any): string | undefined {
65
85
  function getEffectiveBorderColor(el: any, fallback = '#10b981'): string {
66
86
  if (props.isImagePlaceholder && props.isImagePlaceholder(el) && el.style?.enableBorder !== true) return 'transparent'
67
87
  if (el.style?.enableBorder === false) return 'transparent'
68
- if (hasElementBorderGradient(el)) return `url(#shape-border-gradient-${el.id})`
88
+ if (hasElementBorderGradient(el)) return `url(#shape-border-gradient-${svgIdKey.value})`
69
89
  return el.style?.borderColor || getElementBorderColor(el) || fallback
70
90
  }
71
91
 
@@ -75,6 +95,75 @@ function isOpenLineShape(el: any): boolean {
75
95
  return shapType === 'arc' || shapType === 'straightConnector1' || shapType === 'line' || (typeof el.width === 'number' && el.width <= 0) || (typeof el.height === 'number' && el.height <= 0)
76
96
  }
77
97
 
98
+ function getShapeVb(el: any): { w: number; h: number } {
99
+ if (!el) return { w: 200, h: 200 }
100
+ const { width, height } = getElementWidthHeight(el)
101
+ const w = el.viewBox ? el.viewBox[0] : (width || 200)
102
+ const h = el.viewBox ? el.viewBox[1] : (height || 200)
103
+ return { w, h }
104
+ }
105
+
106
+ function isRectShape(el: any): boolean {
107
+ if (!el) return false
108
+ if (el.shapType === 'rect' || el.shapType === 'roundRect') return true
109
+ if (el.content === 'rect') return true
110
+ if (el.pathFormula === 'roundRect') return true
111
+ if (el.title === '矩形' || el.title === '圆角矩形') return true
112
+ const raw = (el.path || el.content || '').trim().replace(/\s+/g, '').toUpperCase()
113
+ if (raw === 'M00L2000L200200L0200Z' || raw === 'M00H200V200H0Z') return true
114
+ if (raw.startsWith('M500L1500Q200020050L200150Q200200150200')) return true
115
+ if (/^M00L([0-9.]+)0L\1([0-9.]+)L0\2Z$/i.test(raw)) return true
116
+ if (/^M00H([0-9.]+)V([0-9.]+)H0Z$/i.test(raw)) return true
117
+ if ((el.style?.borderRadius && parseFloat(String(el.style.borderRadius)) > 0) || (el.radius && parseFloat(String(el.radius)) > 0)) {
118
+ if (raw.includes('L2000L200200L0200') || (raw.startsWith('M00L') && raw.endsWith('Z'))) return true
119
+ }
120
+ return false
121
+ }
122
+
123
+ function getRectRadiusInVb(el: any): { rx: number; ry: number } {
124
+ if (!el) return { rx: 0, ry: 0 }
125
+ const { width, height } = getElementWidthHeight(el)
126
+ let r = 0
127
+ const isRoundRect = el.pathFormula === 'roundRect' || el.shapType === 'roundRect' || el.title === '圆角矩形'
128
+ const rVal = el.style?.borderRadius !== undefined && el.style?.borderRadius !== null && el.style?.borderRadius !== ''
129
+ ? el.style.borderRadius
130
+ : el.radius
131
+
132
+ if (isRoundRect && Array.isArray(el.keypoints) && el.keypoints[0] !== undefined && el.keypoints[0] !== null) {
133
+ const kp = Number(el.keypoints[0])
134
+ if (!isNaN(kp)) {
135
+ r = Math.min(width, height) * Math.max(0, Math.min(0.5, kp))
136
+ }
137
+ } else if (isRoundRect && el.keypoints && typeof el.keypoints === 'object' && !Array.isArray(el.keypoints) && (el.keypoints as any).adj !== undefined) {
138
+ const adj = Number((el.keypoints as any).adj)
139
+ if (!isNaN(adj)) {
140
+ r = Math.min(width, height) * Math.max(0, Math.min(0.5, adj * 0.5))
141
+ }
142
+ } else if (rVal !== undefined && rVal !== null && rVal !== '') {
143
+ const parsed = parseFloat(String(rVal))
144
+ if (!isNaN(parsed)) r = parsed
145
+ } else if (isRoundRect) {
146
+ r = Math.min(width, height) * 0.125
147
+ }
148
+
149
+ if (r <= 0) return { rx: 0, ry: 0 }
150
+
151
+ const { w: vbW, h: vbH } = getShapeVb(el)
152
+
153
+ const maxR = Math.min(width, height) / 2
154
+ const clampedR = Math.min(r, maxR)
155
+
156
+ const hasBorder = hasElementBorder(el)
157
+ const bw = hasBorder ? getEffectiveBorderWidth(el) : 0
158
+ const effW = bw > 0 ? Math.max(1, width - bw) : width
159
+ const effH = bw > 0 ? Math.max(1, height - bw) : height
160
+
161
+ const rx = (clampedR * vbW) / effW
162
+ const ry = (clampedR * vbH) / effH
163
+
164
+ return { rx, ry }
165
+ }
166
+
78
167
  const props = defineProps<{
79
168
  element: any
80
169
  activeSlide?: any
@@ -87,6 +176,7 @@ const props = defineProps<{
87
176
  getElementTextShadow?: (el: any) => string
88
177
  getReflectionLeadingCompensation?: (el: any) => number
89
178
  isImagePlaceholder?: (el: any) => boolean
179
+ isReflection?: boolean
90
180
  // 直接父盒子(组合)的像素尺寸,用于把 % 样式的宽高解析成 px。
91
181
  // 由 CanvasGroupElement 传入;顶层元素不传,走 px 样式分支。
92
182
  parentWidthPx?: number
@@ -239,15 +329,29 @@ function getRawText(el: any): string {
239
329
  return val
240
330
  }
241
331
 
332
+ function getImagePlaceholderFill(el: any): string {
333
+ const fill = getElementFill(el)
334
+ if (!fill || fill === '#f1f5f9' || fill === '#f8fafc') {
335
+ return '#d9dde3'
336
+ }
337
+ return fill
338
+ }
339
+
340
+ function getImagePlaceholderRadius(el: any): string {
341
+ const raw = el.style?.borderRadius !== undefined
342
+ ? (typeof el.style.borderRadius === 'number' ? `${el.style.borderRadius}px` : el.style.borderRadius)
343
+ : (el.radius ? `${el.radius}px` : '0px')
344
+ if (!raw || raw === '0px' || raw === '4px') {
345
+ return '12px'
346
+ }
347
+ return raw
348
+ }
349
+
242
350
  function formatShapeHtml(el: any): string {
243
- let raw = getRawText(el)
244
351
  if (props.isImagePlaceholder && props.isImagePlaceholder(el)) {
245
- const stripped = raw ? raw.replace(/<[^>]*>/g, '').trim() : ''
246
- if (!stripped) {
247
- const prompt = el.placeholder?.prompt || el.name || '图片占位符'
248
- raw = `<div>${prompt}</div>`
249
- }
352
+ return ''
250
353
  }
354
+ const raw = getRawText(el)
251
355
  const norm = props.normalizeHtmlFontFamily ? props.normalizeHtmlFontFamily(raw) : raw
252
356
  if (el.style?.textBackground && el.style.textBackground !== 'transparent') {
253
357
  if (!norm.includes('background-color')) {
@@ -272,9 +376,9 @@ function formatShapeHtml(el: any): string {
272
376
  height: '100%',
273
377
  backgroundColor: getElementPattern(element) ? 'transparent' : ((getElementGradientCss(element) || (getElementFill(element) !== undefined && getElementFill(element) !== 'none')) ? (getElementGradientCss(element) ? 'transparent' : getElementFill(element)) : (getElementFill(element) === 'none' ? 'transparent' : '#3b82f6')),
274
378
  backgroundImage: getElementPattern(element) ? `url('${getElementPattern(element)}')` : (getElementGradientCss(element) || 'none'),
275
- backgroundSize: getElementPattern(element) ? 'cover' : undefined,
276
- backgroundPosition: getElementPattern(element) ? 'center center' : undefined,
277
- backgroundRepeat: getElementPattern(element) ? 'no-repeat' : undefined,
379
+ backgroundSize: getElementPattern(element) ? (isElementPatternTile(element) ? `${getElementPatternTileSize(element).w}px ${getElementPatternTileSize(element).h}px` : 'cover') : undefined,
380
+ backgroundPosition: getElementPattern(element) ? (isElementPatternTile(element) ? '0 0' : 'center center') : undefined,
381
+ backgroundRepeat: getElementPattern(element) ? (isElementPatternTile(element) ? 'repeat' : 'no-repeat') : undefined,
278
382
  borderRadius: '50%',
279
383
  borderWidth: (isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true)
280
384
  ? '0px'
@@ -302,7 +406,7 @@ function formatShapeHtml(el: any): string {
302
406
  <defs v-if="getElementBorderGradientConfig(element)">
303
407
  <radialGradient
304
408
  v-if="getElementBorderGradientConfig(element)!.path && getElementBorderGradientConfig(element)!.path !== 'line'"
305
- :id="`shape-border-gradient-${element.id}`"
409
+ :id="`shape-border-gradient-${svgIdKey}`"
306
410
  :cx="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.x * 100) : 50}%`"
307
411
  :cy="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.y * 100) : 50}%`"
308
412
  :r="getElementBorderGradientConfig(element)!.path === 'rect' ? '60%' : (getElementBorderGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -319,7 +423,7 @@ function formatShapeHtml(el: any): string {
319
423
  </radialGradient>
320
424
  <linearGradient
321
425
  v-else
322
- :id="`shape-border-gradient-${element.id}`"
426
+ :id="`shape-border-gradient-${svgIdKey}`"
323
427
  :x1="getSvgGradientCoords(getElementBorderGradientConfig(element)).x1"
324
428
  :y1="getSvgGradientCoords(getElementBorderGradientConfig(element)).y1"
325
429
  :x2="getSvgGradientCoords(getElementBorderGradientConfig(element)).x2"
@@ -357,13 +461,26 @@ function formatShapeHtml(el: any): string {
357
461
  :style="{ filter: (element.shadow && element.shadow.type === 'inner') ? `url(#shape-inner-shadow-${element.id})` : '' }"
358
462
  >
359
463
  <defs>
360
- <pattern v-if="getElementPattern(element)" :id="`shape-pattern-${element.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
361
- <image :href="getElementPattern(element)" width="1" height="1" preserveAspectRatio="none" />
464
+ <pattern
465
+ v-if="getElementPattern(element)"
466
+ :id="`shape-pattern-${svgIdKey}`"
467
+ :width="isElementPatternTile(element) ? getElementPatternTileSize(element).w : '100%'"
468
+ :height="isElementPatternTile(element) ? getElementPatternTileSize(element).h : '100%'"
469
+ :patternUnits="isElementPatternTile(element) ? 'userSpaceOnUse' : 'objectBoundingBox'"
470
+ :patternContentUnits="isElementPatternTile(element) ? 'userSpaceOnUse' : 'objectBoundingBox'"
471
+ :patternTransform="getShapePatternTransform(element, true)"
472
+ >
473
+ <image
474
+ :href="getElementPattern(element)"
475
+ :width="isElementPatternTile(element) ? getElementPatternTileSize(element).w : 1"
476
+ :height="isElementPatternTile(element) ? getElementPatternTileSize(element).h : 1"
477
+ preserveAspectRatio="none"
478
+ />
362
479
  </pattern>
363
480
  <template v-if="getElementGradientConfig(element)">
364
481
  <radialGradient
365
482
  v-if="getElementGradientConfig(element)!.path && getElementGradientConfig(element)!.path !== 'line'"
366
- :id="`shape-gradient-${element.id}`"
483
+ :id="`shape-gradient-${svgIdKey}`"
367
484
  :cx="`${getElementGradientConfig(element)!.center ? Math.round(getElementGradientConfig(element)!.center!.x * 100) : 50}%`"
368
485
  :cy="`${getElementGradientConfig(element)!.center ? Math.round(getElementGradientConfig(element)!.center!.y * 100) : 50}%`"
369
486
  :r="getElementGradientConfig(element)!.path === 'rect' ? '60%' : (getElementGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -380,7 +497,7 @@ function formatShapeHtml(el: any): string {
380
497
  </radialGradient>
381
498
  <linearGradient
382
499
  v-else
383
- :id="`shape-gradient-${element.id}`"
500
+ :id="`shape-gradient-${svgIdKey}`"
384
501
  :x1="getSvgGradientCoords(element).x1"
385
502
  :y1="getSvgGradientCoords(element).y1"
386
503
  :x2="getSvgGradientCoords(element).x2"
@@ -398,7 +515,7 @@ function formatShapeHtml(el: any): string {
398
515
  <template v-if="getElementBorderGradientConfig(element)">
399
516
  <radialGradient
400
517
  v-if="getElementBorderGradientConfig(element)!.path && getElementBorderGradientConfig(element)!.path !== 'line'"
401
- :id="`shape-border-gradient-${element.id}`"
518
+ :id="`shape-border-gradient-${svgIdKey}`"
402
519
  :cx="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.x * 100) : 50}%`"
403
520
  :cy="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.y * 100) : 50}%`"
404
521
  :r="getElementBorderGradientConfig(element)!.path === 'rect' ? '60%' : (getElementBorderGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -415,7 +532,7 @@ function formatShapeHtml(el: any): string {
415
532
  </radialGradient>
416
533
  <linearGradient
417
534
  v-else
418
- :id="`shape-border-gradient-${element.id}`"
535
+ :id="`shape-border-gradient-${svgIdKey}`"
419
536
  :x1="getSvgGradientCoords(getElementBorderGradientConfig(element)).x1"
420
537
  :y1="getSvgGradientCoords(getElementBorderGradientConfig(element)).y1"
421
538
  :x2="getSvgGradientCoords(getElementBorderGradientConfig(element)).x2"
@@ -433,7 +550,7 @@ function formatShapeHtml(el: any): string {
433
550
  </defs>
434
551
  <polygon
435
552
  points="50,0 0,100 100,100"
436
- :fill="hasElementFill(element) ? (getElementPattern(element) ? `url(#shape-pattern-${element.id})` : (getElementGradientConfig(element) ? `url(#shape-gradient-${element.id})` : (element.style?.fill !== undefined ? element.style.fill : '#f59e0b'))) : 'none'"
553
+ :fill="hasElementFill(element) ? (getElementPattern(element) ? `url(#shape-pattern-${svgIdKey})` : (getElementGradientConfig(element) ? `url(#shape-gradient-${svgIdKey})` : (element.style?.fill !== undefined ? element.style.fill : '#f59e0b'))) : 'none'"
437
554
  :stroke="getEffectiveBorderColor(element, '#d97706')"
438
555
  :stroke-width="getEffectiveBorderWidth(element)"
439
556
  :stroke-dasharray="element.style?.borderStyle === 'dashed' ? '5 5' : ''"
@@ -452,13 +569,26 @@ function formatShapeHtml(el: any): string {
452
569
  :style="{ filter: (element.shadow && element.shadow.type === 'inner') ? `url(#shape-inner-shadow-${element.id})` : '' }"
453
570
  >
454
571
  <defs>
455
- <pattern v-if="getElementPattern(element)" :id="`shape-pattern-${element.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
456
- <image :href="getElementPattern(element)" width="1" height="1" preserveAspectRatio="none" />
572
+ <pattern
573
+ v-if="getElementPattern(element)"
574
+ :id="`shape-pattern-${svgIdKey}`"
575
+ :width="isElementPatternTile(element) ? getElementPatternTileSize(element).w : '100%'"
576
+ :height="isElementPatternTile(element) ? getElementPatternTileSize(element).h : '100%'"
577
+ :patternUnits="isElementPatternTile(element) ? 'userSpaceOnUse' : 'objectBoundingBox'"
578
+ :patternContentUnits="isElementPatternTile(element) ? 'userSpaceOnUse' : 'objectBoundingBox'"
579
+ :patternTransform="getShapePatternTransform(element, false)"
580
+ >
581
+ <image
582
+ :href="getElementPattern(element)"
583
+ :width="isElementPatternTile(element) ? getElementPatternTileSize(element).w : 1"
584
+ :height="isElementPatternTile(element) ? getElementPatternTileSize(element).h : 1"
585
+ preserveAspectRatio="none"
586
+ />
457
587
  </pattern>
458
588
  <template v-if="getElementGradientConfig(element)">
459
589
  <radialGradient
460
590
  v-if="getElementGradientConfig(element)!.path && getElementGradientConfig(element)!.path !== 'line'"
461
- :id="`shape-gradient-${element.id}`"
591
+ :id="`shape-gradient-${svgIdKey}`"
462
592
  :cx="`${getElementGradientConfig(element)!.center ? Math.round(getElementGradientConfig(element)!.center!.x * 100) : 50}%`"
463
593
  :cy="`${getElementGradientConfig(element)!.center ? Math.round(getElementGradientConfig(element)!.center!.y * 100) : 50}%`"
464
594
  :r="getElementGradientConfig(element)!.path === 'rect' ? '60%' : (getElementGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -475,7 +605,7 @@ function formatShapeHtml(el: any): string {
475
605
  </radialGradient>
476
606
  <linearGradient
477
607
  v-else
478
- :id="`shape-gradient-${element.id}`"
608
+ :id="`shape-gradient-${svgIdKey}`"
479
609
  :x1="getSvgGradientCoords(element).x1"
480
610
  :y1="getSvgGradientCoords(element).y1"
481
611
  :x2="getSvgGradientCoords(element).x2"
@@ -493,7 +623,7 @@ function formatShapeHtml(el: any): string {
493
623
  <template v-if="getElementBorderGradientConfig(element)">
494
624
  <radialGradient
495
625
  v-if="getElementBorderGradientConfig(element)!.path && getElementBorderGradientConfig(element)!.path !== 'line'"
496
- :id="`shape-border-gradient-${element.id}`"
626
+ :id="`shape-border-gradient-${svgIdKey}`"
497
627
  :cx="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.x * 100) : 50}%`"
498
628
  :cy="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.y * 100) : 50}%`"
499
629
  :r="getElementBorderGradientConfig(element)!.path === 'rect' ? '60%' : (getElementBorderGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -510,7 +640,7 @@ function formatShapeHtml(el: any): string {
510
640
  </radialGradient>
511
641
  <linearGradient
512
642
  v-else
513
- :id="`shape-border-gradient-${element.id}`"
643
+ :id="`shape-border-gradient-${svgIdKey}`"
514
644
  :x1="getSvgGradientCoords(getElementBorderGradientConfig(element)).x1"
515
645
  :y1="getSvgGradientCoords(getElementBorderGradientConfig(element)).y1"
516
646
  :x2="getSvgGradientCoords(getElementBorderGradientConfig(element)).x2"
@@ -529,10 +659,26 @@ function formatShapeHtml(el: any): string {
529
659
  <g
530
660
  :transform="getShapeSvgTransform(element)"
531
661
  >
662
+ <rect
663
+ v-if="isRectShape(element)"
664
+ x="0"
665
+ y="0"
666
+ :width="getShapeVb(element).w"
667
+ :height="getShapeVb(element).h"
668
+ :rx="getRectRadiusInVb(element).rx"
669
+ :ry="getRectRadiusInVb(element).ry"
670
+ vector-effect="non-scaling-stroke"
671
+ :fill="hasElementFill(element) ? (getElementPattern(element) ? `url(#shape-pattern-${svgIdKey})` : (getElementGradientConfig(element) ? `url(#shape-gradient-${svgIdKey})` : (getElementFill(element) !== undefined ? getElementFill(element) : '#3b82f6'))) : 'none'"
672
+ :stroke="(isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true) ? 'none' : (element.style?.enableBorder === false ? 'none' : getEffectiveBorderColor(element, 'none'))"
673
+ :stroke-width="(isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true) ? 0 : (element.style?.enableBorder === false ? 0 : getElementBorderWidth(element))"
674
+ :stroke-linecap="element.style?.strokeLinecap || element.borderLineCap || element.lineCap || (element.shapType === 'arc' ? 'round' : (isOpenLineShape(element) ? 'butt' : undefined))"
675
+ :stroke-dasharray="element.style?.borderStyle === 'dashed' || element.borderType === 'dashed' ? '5 5' : ''"
676
+ />
532
677
  <path
678
+ v-else
533
679
  vector-effect="non-scaling-stroke"
534
680
  :d="element.path || element.content"
535
- :fill="hasElementFill(element) ? (getElementPattern(element) ? `url(#shape-pattern-${element.id})` : (getElementGradientConfig(element) ? `url(#shape-gradient-${element.id})` : (getElementFill(element) !== undefined ? getElementFill(element) : '#3b82f6'))) : 'none'"
681
+ :fill="hasElementFill(element) ? (getElementPattern(element) ? `url(#shape-pattern-${svgIdKey})` : (getElementGradientConfig(element) ? `url(#shape-gradient-${svgIdKey})` : (getElementFill(element) !== undefined ? getElementFill(element) : '#3b82f6'))) : 'none'"
536
682
  :stroke="(isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true) ? 'none' : (element.style?.enableBorder === false ? 'none' : getEffectiveBorderColor(element, 'none'))"
537
683
  :stroke-width="(isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true) ? 0 : (element.style?.enableBorder === false ? 0 : getElementBorderWidth(element))"
538
684
  :stroke-linecap="element.style?.strokeLinecap || element.borderLineCap || element.lineCap || (element.shapType === 'arc' ? 'round' : (isOpenLineShape(element) ? 'butt' : undefined))"
@@ -558,12 +704,12 @@ function formatShapeHtml(el: any): string {
558
704
  :style="{
559
705
  width: '100%',
560
706
  height: '100%',
561
- backgroundColor: getElementPattern(element) ? 'transparent' : ((getElementGradientCss(element) || (getElementFill(element) !== undefined && getElementFill(element) !== 'none')) ? (getElementGradientCss(element) ? 'transparent' : getElementFill(element)) : (getElementFill(element) === 'none' ? 'transparent' : '#10b981')),
707
+ backgroundColor: getElementPattern(element) ? 'transparent' : ((isImagePlaceholder && isImagePlaceholder(element) && !getElementGradientCss(element)) ? getImagePlaceholderFill(element) : ((getElementGradientCss(element) || (getElementFill(element) !== undefined && getElementFill(element) !== 'none')) ? (getElementGradientCss(element) ? 'transparent' : getElementFill(element)) : (getElementFill(element) === 'none' ? 'transparent' : '#10b981'))),
562
708
  backgroundImage: getElementPattern(element) ? `url('${getElementPattern(element)}')` : (getElementGradientCss(element) || 'none'),
563
- backgroundSize: getElementPattern(element) ? 'cover' : undefined,
564
- backgroundPosition: getElementPattern(element) ? 'center center' : undefined,
565
- backgroundRepeat: getElementPattern(element) ? 'no-repeat' : undefined,
566
- borderRadius: element.style?.borderRadius !== undefined ? element.style.borderRadius : '0px',
709
+ backgroundSize: getElementPattern(element) ? (isElementPatternTile(element) ? `${getElementPatternTileSize(element).w}px ${getElementPatternTileSize(element).h}px` : 'cover') : undefined,
710
+ backgroundPosition: getElementPattern(element) ? (isElementPatternTile(element) ? '0 0' : 'center center') : undefined,
711
+ backgroundRepeat: getElementPattern(element) ? (isElementPatternTile(element) ? 'repeat' : 'no-repeat') : undefined,
712
+ borderRadius: (isImagePlaceholder && isImagePlaceholder(element)) ? getImagePlaceholderRadius(element) : (element.style?.borderRadius !== undefined ? (typeof element.style.borderRadius === 'number' ? `${element.style.borderRadius}px` : element.style.borderRadius) : (element.radius ? `${element.radius}px` : '0px')),
567
713
  borderWidth: (isImagePlaceholder && isImagePlaceholder(element) && element.style?.enableBorder !== true)
568
714
  ? '0px'
569
715
  : (element.style?.enableBorder === false ? '0px' : `${getElementBorderWidth(element)}px`),
@@ -588,7 +734,7 @@ function formatShapeHtml(el: any): string {
588
734
  <defs v-if="getElementBorderGradientConfig(element)">
589
735
  <radialGradient
590
736
  v-if="getElementBorderGradientConfig(element)!.path && getElementBorderGradientConfig(element)!.path !== 'line'"
591
- :id="`shape-border-gradient-${element.id}`"
737
+ :id="`shape-border-gradient-${svgIdKey}`"
592
738
  :cx="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.x * 100) : 50}%`"
593
739
  :cy="`${getElementBorderGradientConfig(element)!.center ? Math.round(getElementBorderGradientConfig(element)!.center!.y * 100) : 50}%`"
594
740
  :r="getElementBorderGradientConfig(element)!.path === 'rect' ? '60%' : (getElementBorderGradientConfig(element)!.path === 'path' ? '70%' : '50%')"
@@ -605,7 +751,7 @@ function formatShapeHtml(el: any): string {
605
751
  </radialGradient>
606
752
  <linearGradient
607
753
  v-else
608
- :id="`shape-border-gradient-${element.id}`"
754
+ :id="`shape-border-gradient-${svgIdKey}`"
609
755
  :x1="getSvgGradientCoords(getElementBorderGradientConfig(element)).x1"
610
756
  :y1="getSvgGradientCoords(getElementBorderGradientConfig(element)).y1"
611
757
  :x2="getSvgGradientCoords(getElementBorderGradientConfig(element)).x2"
@@ -625,8 +771,8 @@ function formatShapeHtml(el: any): string {
625
771
  y="0"
626
772
  width="100%"
627
773
  height="100%"
628
- :rx="parseFloat(element.style?.borderRadius) || 0"
629
- :ry="parseFloat(element.style?.borderRadius) || 0"
774
+ :rx="parseFloat(element.style?.borderRadius) || parseFloat(element.radius) || 0"
775
+ :ry="parseFloat(element.style?.borderRadius) || parseFloat(element.radius) || 0"
630
776
  fill="none"
631
777
  :stroke="getEffectiveBorderColor(element, '#10b981')"
632
778
  :stroke-width="getEffectiveBorderWidth(element)"
@@ -636,8 +782,22 @@ function formatShapeHtml(el: any): string {
636
782
  </svg>
637
783
  </div>
638
784
 
785
+ <!-- Image Placeholder Centered Icon Overlay -->
786
+ <div
787
+ v-if="isImagePlaceholder && isImagePlaceholder(element)"
788
+ class="image-placeholder-icon-overlay"
789
+ style="position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none;"
790
+ >
791
+ <svg width="56" height="56" viewBox="0 0 24 24" fill="none" style="max-width: 45%; max-height: 45%;">
792
+ <rect x="3" y="3" width="18" height="18" rx="2" stroke="#475569" stroke-width="2" />
793
+ <circle cx="8.5" cy="8.5" r="1.5" fill="#475569" />
794
+ <path d="M21 15l-5-5L5 21" stroke="#475569" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
795
+ </svg>
796
+ </div>
797
+
639
798
  <!-- Embedded Shape Text Overlay -->
640
799
  <div
800
+ v-else
641
801
  class="shape-text-overlay-wrapper"
642
802
  style="position: absolute; display: flex; flex-direction: column; box-sizing: border-box; pointer-events: none;"
643
803
  :style="{
@@ -656,7 +816,7 @@ function formatShapeHtml(el: any): string {
656
816
  }">
657
817
  <div
658
818
  ref="shapeEditableRef"
659
- :id="`editable-${element.id}`"
819
+ :id="isReflection ? undefined : `editable-${element.id}`"
660
820
  :contenteditable="isTextSelectable ? 'true' : 'false'"
661
821
  @mousedown="isTextSelectable ? $event.stopPropagation() : null"
662
822
  @click="isTextSelectable ? $event.stopPropagation() : null"
@@ -681,7 +841,7 @@ function formatShapeHtml(el: any): string {
681
841
  boxSizing: 'content-box',
682
842
  overflow: 'wrap',
683
843
  wordBreak: 'break-word',
684
- pointerEvents: isTextSelectable ? 'auto' : (getRawText(element).trim() ? 'auto' : 'none'),
844
+ pointerEvents: isReflection ? 'none' : (isTextSelectable ? 'auto' : (getRawText(element).trim() ? 'auto' : 'none')),
685
845
  cursor: isTextSelectable ? 'text' : undefined,
686
846
  userSelect: isTextSelectable ? 'text' : 'none',
687
847
  color: element.style?.textColor || element.style?.color || element.textColor || ((isImagePlaceholder && isImagePlaceholder(element)) ? '#6366f1' : (element.style?.fill === '#ffffff' || element.style?.fill === '#FFFFFF' ? '#475569' : (themeRecipe?.shape?.textColor || '#ffffff'))),