jvs-ppt-ui 0.0.7 → 0.0.8

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.
@@ -14,6 +14,16 @@ export interface TableOptions {
14
14
  selectedElementHeight?: Ref<string>
15
15
  }
16
16
 
17
+ interface TableMeasure {
18
+ ver: number
19
+ sig: string
20
+ offset: { left: number; top: number; width: number; height: number }
21
+ cols: number[]
22
+ rows: number[]
23
+ colDots: number[]
24
+ rowDots: number[]
25
+ }
26
+
17
27
  export function useCanvasTable(options: TableOptions) {
18
28
  const {
19
29
  activeSlide,
@@ -54,6 +64,7 @@ export function useCanvasTable(options: TableOptions) {
54
64
 
55
65
  let tableCleanupFunc: (() => void) | null = null
56
66
  let tableSelectionActive = false
67
+ let updateBordersRafId = 0
57
68
  let startCell: HTMLTableCellElement | null = null
58
69
  let tableResizeActiveTable: HTMLTableElement | null = null
59
70
  let tableResizeActiveEl: any = null
@@ -71,37 +82,64 @@ export function useCanvasTable(options: TableOptions) {
71
82
  return { width, height }
72
83
  }
73
84
 
74
- function getTableOffset(el: any) {
75
- if (!el) return { left: 0, top: 0, width: 500, height: 200 }
85
+ // ---- 表格几何单遍合并测量(渲染期回流治理)----
86
+ // 模板每次渲染原本重复调 getTableOffset×7 + getTableInfo×5 + 行/列圆点 O(n²),
87
+ // 全是 offset* 强制布局读;合并为每次失效只做一次 DOM 遍历,版本号+签名双缓存。
88
+ const tableMeasureVersion = ref(0)
89
+ const tableMeasureCache = new WeakMap<object, TableMeasure>()
90
+
91
+ function bumpTableMeasure() {
92
+ tableMeasureVersion.value++
93
+ }
94
+
95
+ // 表格内容提交统一入口:写 content + 失效测量缓存 + 落历史快照
96
+ function commitTableContent(targetEl: any, html: string) {
97
+ targetEl.content = html
98
+ bumpTableMeasure()
99
+ addHistorySnapshot()
100
+ }
101
+
102
+ function getTableMeasure(el: any): TableMeasure | null {
103
+ if (!el) return null
104
+ // 显式读取 RO 尺寸 ref:表格增高引起 ref 变化时,确保依赖收集覆盖本测量
105
+ void selectedElementWidth?.value
106
+ void selectedElementHeight?.value
107
+ const ver = tableMeasureVersion.value
108
+ // 签名只含响应式输入(零 DOM 读):内容长度 + 影响排版的 style + RO 尺寸
109
+ const st = el.style || {}
110
+ const sig = `${el.content?.length || 0}|${st.fontSize || ''}|${st.fontFamily || ''}|${st.fontWeight || ''}|${st.lineHeight || ''}|${st.letterSpacing || ''}|${st.padding || ''}|${st.whiteSpace || ''}|${st.writingMode || ''}|${st.width || ''}|${st.height || ''}|${selectedElementWidth?.value || ''}|${selectedElementHeight?.value || ''}`
111
+ const cached = tableMeasureCache.get(el)
112
+ if (cached && cached.ver === ver && cached.sig === sig) return cached
113
+
114
+ const hasContent = !!el.content && typeof el.content === 'string' && el.content.includes('<table')
76
115
  const container = document.getElementById(`editable-${el.id}`)
77
116
  const tableEl = container?.querySelector('table')
117
+
118
+ // offset(原 getTableOffset 逐分支等价)
119
+ let offset: TableMeasure['offset']
78
120
  if (tableEl) {
79
- return {
121
+ offset = {
80
122
  left: tableEl.offsetLeft || 0,
81
123
  top: tableEl.offsetTop || 0,
82
124
  width: tableEl.offsetWidth || parseFloat(getOverlayWidth(el)) || 500,
83
125
  height: tableEl.offsetHeight || parseFloat(getOverlayHeight(el)) || 200
84
126
  }
127
+ } else {
128
+ offset = {
129
+ left: 0,
130
+ top: 0,
131
+ width: parseFloat(getOverlayWidth(el)) || 500,
132
+ height: parseFloat(getOverlayHeight(el)) || 200
133
+ }
85
134
  }
86
- return {
87
- left: 0,
88
- top: 0,
89
- width: parseFloat(getOverlayWidth(el)) || 500,
90
- height: parseFloat(getOverlayHeight(el)) || 200
91
- }
92
- }
93
-
94
- function getTableInfo(el: any) {
95
- if (!el || !el.content || typeof el.content !== 'string' || !el.content.includes('<table')) return { cols: [], rows: [] }
96
135
 
97
- const container = document.getElementById(`editable-${el.id}`)
98
- const tableEl = container?.querySelector('table')
99
-
100
- let trs: HTMLTableRowElement[] = []
101
- if (tableEl) {
102
- trs = Array.from(tableEl.rows || [])
103
- }
136
+ // cols/rows/圆点(原 getTableInfo 与两个圆点函数逐分支等价,单遍产出)
137
+ let cols: number[] = []
138
+ let rows: number[] = []
139
+ let colDots: number[] = []
140
+ let rowDots: number[] = []
104
141
 
142
+ const trs: HTMLTableRowElement[] = tableEl ? Array.from(tableEl.rows || []) : []
105
143
  if (trs.length > 0) {
106
144
  let bestRow = trs[0]
107
145
  for (const tr of trs) {
@@ -110,51 +148,79 @@ export function useCanvasTable(options: TableOptions) {
110
148
  }
111
149
  }
112
150
  const cells = Array.from(bestRow.cells)
113
- const cols: number[] = []
114
151
  if (cells.length > 0) {
115
- for (let i = 0; i < cells.length; i++) {
116
- if (i < cells.length - 1) {
117
- cols.push(cells[i + 1].offsetLeft - cells[i].offsetLeft)
118
- } else {
119
- cols.push(cells[i].offsetWidth)
120
- }
121
- }
152
+ cols = cells.map((c, i) => (i < cells.length - 1 ? cells[i + 1].offsetLeft - cells[i].offsetLeft : c.offsetWidth))
153
+ colDots = [0, ...cells.slice(1).map(c => c.offsetLeft)]
154
+ const lastTd = cells[cells.length - 1]
155
+ colDots.push(lastTd.offsetLeft + lastTd.offsetWidth)
122
156
  }
123
157
 
124
- const rows: number[] = []
125
- for (let i = 0; i < trs.length; i++) {
126
- if (i < trs.length - 1) {
127
- rows.push(trs[i + 1].offsetTop - trs[i].offsetTop)
158
+ rows = trs.map((tr, i) => (i < trs.length - 1 ? trs[i + 1].offsetTop - trs[i].offsetTop : tr.offsetHeight))
159
+ rowDots = [0, ...trs.slice(1).map(tr => tr.offsetTop)]
160
+ const lastTr = trs[trs.length - 1]
161
+ rowDots.push(lastTr.offsetTop + lastTr.offsetHeight)
162
+ } else if (hasContent) {
163
+ // DOM 表不在场:DOMParser 结构 + 均分(原 getTableInfo fallback / 圆点累积路径)
164
+ const { width: targetW, height: targetH } = getTableActualDimensions(el)
165
+ try {
166
+ const doc = new DOMParser().parseFromString(el.content, 'text/html')
167
+ const parsedTable = doc.querySelector('table')
168
+ if (parsedTable) {
169
+ const numRows = parsedTable.rows.length || 1
170
+ let bestRow = parsedTable.rows[0]
171
+ for (let r = 0; r < parsedTable.rows.length; r++) {
172
+ if (parsedTable.rows[r].cells.length > bestRow.cells.length) {
173
+ bestRow = parsedTable.rows[r]
174
+ }
175
+ }
176
+ const numCols = bestRow ? bestRow.cells.length : 1
177
+ cols = Array(numCols).fill(targetW / numCols)
178
+ rows = Array(numRows).fill(targetH / numRows)
128
179
  } else {
129
- rows.push(trs[i].offsetHeight)
180
+ cols = [targetW]
181
+ rows = [targetH]
130
182
  }
183
+ } catch {
184
+ cols = [targetW]
185
+ rows = [targetH]
131
186
  }
132
-
133
- return { cols, rows }
134
- }
135
-
136
- const { width: targetW, height: targetH } = getTableActualDimensions(el)
137
- try {
138
- const doc = new DOMParser().parseFromString(el.content, 'text/html')
139
- const parsedTable = doc.querySelector('table')
140
- if (parsedTable) {
141
- const numRows = parsedTable.rows.length || 1
142
- let bestRow = parsedTable.rows[0]
143
- for (let r = 0; r < parsedTable.rows.length; r++) {
144
- if (parsedTable.rows[r].cells.length > bestRow.cells.length) {
145
- bestRow = parsedTable.rows[r]
146
- }
187
+ if (cols.length > 0) {
188
+ colDots = [0]
189
+ let cx = 0
190
+ for (let i = 0; i < cols.length; i++) {
191
+ cx += cols[i]
192
+ colDots.push(cx)
147
193
  }
148
- const numCols = bestRow ? bestRow.cells.length : 1
149
- return {
150
- cols: Array(numCols).fill(targetW / numCols),
151
- rows: Array(numRows).fill(targetH / numRows)
194
+ }
195
+ if (rows.length > 0) {
196
+ rowDots = [0]
197
+ let cy = 0
198
+ for (let i = 0; i < rows.length; i++) {
199
+ cy += rows[i]
200
+ rowDots.push(cy)
152
201
  }
153
202
  }
154
- } catch {
155
- return { cols: [targetW], rows: [targetH] }
156
203
  }
157
- return { cols: [targetW], rows: [targetH] }
204
+ // 与原 getTableInfo 对齐:content 不含 <table> 时 cols/rows 恒为空
205
+ if (!hasContent) {
206
+ cols = []
207
+ rows = []
208
+ }
209
+
210
+ const entry: TableMeasure = { ver, sig, offset, cols, rows, colDots, rowDots }
211
+ tableMeasureCache.set(el, entry)
212
+ return entry
213
+ }
214
+
215
+ function getTableOffset(el: any) {
216
+ const m = getTableMeasure(el)
217
+ return m ? { ...m.offset } : { left: 0, top: 0, width: 500, height: 200 }
218
+ }
219
+
220
+ function getTableInfo(el: any) {
221
+ const m = getTableMeasure(el)
222
+ if (!m) return { cols: [], rows: [] }
223
+ return { cols: m.cols.slice(), rows: m.rows.slice() }
158
224
  }
159
225
 
160
226
  function reindexTable(tableEl: HTMLTableElement) {
@@ -280,6 +346,7 @@ export function useCanvasTable(options: TableOptions) {
280
346
  const selectedTds = Array.from(tableEl.querySelectorAll('td.selected-cell')) as HTMLTableCellElement[]
281
347
  selectedCellCount.value = selectedTds.length
282
348
  tableSelectionTrigger.value++
349
+ bumpTableMeasure()
283
350
 
284
351
  if (selectedTds.length === 1) {
285
352
  const td = selectedTds[0]
@@ -574,63 +641,13 @@ export function useCanvasTable(options: TableOptions) {
574
641
  }
575
642
 
576
643
  function getColDotPositions(el: any): number[] {
577
- const container = document.getElementById(`editable-${el.id}`)
578
- const tableEl = container?.querySelector('table')
579
- if (tableEl && tableEl.rows.length > 0) {
580
- let bestRow = tableEl.rows[0]
581
- for (let r = 0; r < tableEl.rows.length; r++) {
582
- if (tableEl.rows[r].cells.length > bestRow.cells.length) {
583
- bestRow = tableEl.rows[r]
584
- }
585
- }
586
- const cells = Array.from(bestRow.cells)
587
- if (cells.length > 0) {
588
- const positions: number[] = [0]
589
- for (let i = 1; i < cells.length; i++) {
590
- positions.push(cells[i].offsetLeft)
591
- }
592
- const lastTd = cells[cells.length - 1]
593
- positions.push(lastTd.offsetLeft + lastTd.offsetWidth)
594
- return positions
595
- }
596
- }
597
-
598
- const info = getTableInfo(el)
599
- if (!info.cols || info.cols.length === 0) return []
600
- const positions = [0]
601
- let currentX = 0
602
- for (let i = 0; i < info.cols.length; i++) {
603
- currentX += info.cols[i]
604
- positions.push(currentX)
605
- }
606
- return positions
644
+ const m = getTableMeasure(el)
645
+ return m ? m.colDots.slice() : []
607
646
  }
608
647
 
609
648
  function getRowDotPositions(el: any): number[] {
610
- const container = document.getElementById(`editable-${el.id}`)
611
- const tableEl = container?.querySelector('table')
612
- if (tableEl && tableEl.rows.length > 0) {
613
- const trs = Array.from(tableEl.rows)
614
- if (trs.length > 0) {
615
- const positions: number[] = [0]
616
- for (let i = 1; i < trs.length; i++) {
617
- positions.push(trs[i].offsetTop)
618
- }
619
- const lastTr = trs[trs.length - 1]
620
- positions.push(lastTr.offsetTop + lastTr.offsetHeight)
621
- return positions
622
- }
623
- }
624
-
625
- const info = getTableInfo(el)
626
- if (!info.rows || info.rows.length === 0) return []
627
- const positions = [0]
628
- let currentY = 0
629
- for (let i = 0; i < info.rows.length; i++) {
630
- currentY += info.rows[i]
631
- positions.push(currentY)
632
- }
633
- return positions
649
+ const m = getTableMeasure(el)
650
+ return m ? m.rowDots.slice() : []
634
651
  }
635
652
 
636
653
  function getSelectedCellCoords(container: HTMLElement) {
@@ -805,8 +822,7 @@ export function useCanvasTable(options: TableOptions) {
805
822
 
806
823
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
807
824
  cleanTableDOMForSaving(cloneTable)
808
- targetEl.content = cloneTable.outerHTML
809
- addHistorySnapshot()
825
+ commitTableContent(targetEl, cloneTable.outerHTML)
810
826
 
811
827
  nextTick(() => {
812
828
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -932,8 +948,7 @@ export function useCanvasTable(options: TableOptions) {
932
948
 
933
949
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
934
950
  cleanTableDOMForSaving(cloneTable)
935
- targetEl.content = cloneTable.outerHTML
936
- addHistorySnapshot()
951
+ commitTableContent(targetEl, cloneTable.outerHTML)
937
952
 
938
953
  nextTick(() => {
939
954
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -995,8 +1010,7 @@ export function useCanvasTable(options: TableOptions) {
995
1010
 
996
1011
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
997
1012
  cleanTableDOMForSaving(cloneTable)
998
- targetEl.content = cloneTable.outerHTML
999
- addHistorySnapshot()
1013
+ commitTableContent(targetEl, cloneTable.outerHTML)
1000
1014
 
1001
1015
  nextTick(() => {
1002
1016
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -1064,8 +1078,7 @@ export function useCanvasTable(options: TableOptions) {
1064
1078
 
1065
1079
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
1066
1080
  cleanTableDOMForSaving(cloneTable)
1067
- targetEl.content = cloneTable.outerHTML
1068
- addHistorySnapshot()
1081
+ commitTableContent(targetEl, cloneTable.outerHTML)
1069
1082
 
1070
1083
  nextTick(() => {
1071
1084
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -1131,8 +1144,7 @@ export function useCanvasTable(options: TableOptions) {
1131
1144
 
1132
1145
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
1133
1146
  cleanTableDOMForSaving(cloneTable)
1134
- targetEl.content = cloneTable.outerHTML
1135
- addHistorySnapshot()
1147
+ commitTableContent(targetEl, cloneTable.outerHTML)
1136
1148
 
1137
1149
  nextTick(() => {
1138
1150
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -1234,8 +1246,7 @@ export function useCanvasTable(options: TableOptions) {
1234
1246
 
1235
1247
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
1236
1248
  cleanTableDOMForSaving(cloneTable)
1237
- targetEl.content = cloneTable.outerHTML
1238
- addHistorySnapshot()
1249
+ commitTableContent(targetEl, cloneTable.outerHTML)
1239
1250
 
1240
1251
  nextTick(() => {
1241
1252
  const freshContainer = document.getElementById(`editable-${targetEl.id}`)
@@ -1332,12 +1343,21 @@ export function useCanvasTable(options: TableOptions) {
1332
1343
  selectedElementWidth.value = `${sumW.toFixed(1)}px`
1333
1344
  }
1334
1345
  }
1335
- updateSelectionBorders(tableEl)
1346
+ // rAF 节流:高轮询率鼠标下避免每 move 一次边框重算
1347
+ if (updateBordersRafId) cancelAnimationFrame(updateBordersRafId)
1348
+ updateBordersRafId = requestAnimationFrame(() => {
1349
+ updateBordersRafId = 0
1350
+ if (isResizingTable.value) updateSelectionBorders(tableEl)
1351
+ })
1336
1352
  }
1337
1353
 
1338
1354
  const onMouseUp = () => {
1339
1355
  window.removeEventListener('mousemove', onMouseMove)
1340
1356
  window.removeEventListener('mouseup', onMouseUp)
1357
+ if (updateBordersRafId) {
1358
+ cancelAnimationFrame(updateBordersRafId)
1359
+ updateBordersRafId = 0
1360
+ }
1341
1361
  isResizingTable.value = false
1342
1362
  tableResizeType.value = null
1343
1363
  tableResizeTd.value = null
@@ -1345,8 +1365,7 @@ export function useCanvasTable(options: TableOptions) {
1345
1365
 
1346
1366
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
1347
1367
  cleanTableDOMForSaving(cloneTable)
1348
- el.content = cloneTable.outerHTML
1349
- addHistorySnapshot()
1368
+ commitTableContent(el, cloneTable.outerHTML)
1350
1369
  }
1351
1370
 
1352
1371
  window.addEventListener('mousemove', onMouseMove)
@@ -1417,12 +1436,21 @@ export function useCanvasTable(options: TableOptions) {
1417
1436
  selectedElementHeight.value = `${sumH.toFixed(1)}px`
1418
1437
  }
1419
1438
  }
1420
- updateSelectionBorders(tableEl)
1439
+ // rAF 节流:高轮询率鼠标下避免每 move 一次边框重算
1440
+ if (updateBordersRafId) cancelAnimationFrame(updateBordersRafId)
1441
+ updateBordersRafId = requestAnimationFrame(() => {
1442
+ updateBordersRafId = 0
1443
+ if (isResizingTable.value) updateSelectionBorders(tableEl)
1444
+ })
1421
1445
  }
1422
1446
 
1423
1447
  const onMouseUp = () => {
1424
1448
  window.removeEventListener('mousemove', onMouseMove)
1425
1449
  window.removeEventListener('mouseup', onMouseUp)
1450
+ if (updateBordersRafId) {
1451
+ cancelAnimationFrame(updateBordersRafId)
1452
+ updateBordersRafId = 0
1453
+ }
1426
1454
  isResizingTable.value = false
1427
1455
  tableResizeType.value = null
1428
1456
  tableResizeTd.value = null
@@ -1430,8 +1458,7 @@ export function useCanvasTable(options: TableOptions) {
1430
1458
 
1431
1459
  const cloneTable = tableEl.cloneNode(true) as HTMLTableElement
1432
1460
  cleanTableDOMForSaving(cloneTable)
1433
- el.content = cloneTable.outerHTML
1434
- addHistorySnapshot()
1461
+ commitTableContent(el, cloneTable.outerHTML)
1435
1462
  }
1436
1463
 
1437
1464
  window.addEventListener('mousemove', onMouseMove)
@@ -1456,26 +1483,34 @@ export function useCanvasTable(options: TableOptions) {
1456
1483
  reindexTable(tableEl)
1457
1484
  const borderTolerance = 6
1458
1485
 
1486
+ let mousemoveRafId = 0
1459
1487
  const mousemoveHandler = (e: MouseEvent) => {
1460
1488
  if (isResizingTable.value) return
1489
+ // rAF 节流:每帧最多一次 getBoundingClientRect,消除高频 mousemove 的强制回流
1490
+ if (mousemoveRafId) return
1491
+ const clientX = e.clientX
1492
+ const clientY = e.clientY
1461
1493
  const target = e.target as HTMLElement
1462
- const td = target.closest('td') as HTMLTableCellElement | null
1463
- if (!td || !tableEl.contains(td)) {
1464
- tableEl.style.cursor = 'default'
1465
- return
1466
- }
1494
+ mousemoveRafId = requestAnimationFrame(() => {
1495
+ mousemoveRafId = 0
1496
+ const td = target.closest('td') as HTMLTableCellElement | null
1497
+ if (!td || !tableEl.contains(td)) {
1498
+ tableEl.style.cursor = 'default'
1499
+ return
1500
+ }
1467
1501
 
1468
- const rect = td.getBoundingClientRect()
1469
- const distRight = Math.abs(rect.right - e.clientX)
1470
- const distBottom = Math.abs(rect.bottom - e.clientY)
1502
+ const rect = td.getBoundingClientRect()
1503
+ const distRight = Math.abs(rect.right - clientX)
1504
+ const distBottom = Math.abs(rect.bottom - clientY)
1471
1505
 
1472
- if (distRight <= borderTolerance && distRight <= distBottom) {
1473
- tableEl.style.cursor = 'col-resize'
1474
- } else if (distBottom <= borderTolerance) {
1475
- tableEl.style.cursor = 'row-resize'
1476
- } else {
1477
- tableEl.style.cursor = 'default'
1478
- }
1506
+ if (distRight <= borderTolerance && distRight <= distBottom) {
1507
+ tableEl.style.cursor = 'col-resize'
1508
+ } else if (distBottom <= borderTolerance) {
1509
+ tableEl.style.cursor = 'row-resize'
1510
+ } else {
1511
+ tableEl.style.cursor = 'default'
1512
+ }
1513
+ })
1479
1514
  }
1480
1515
 
1481
1516
  const mousedownHandler = (e: MouseEvent) => {
@@ -1536,6 +1571,10 @@ export function useCanvasTable(options: TableOptions) {
1536
1571
  window.addEventListener('mouseup', mouseupHandler)
1537
1572
 
1538
1573
  const cleanup = () => {
1574
+ if (mousemoveRafId) {
1575
+ cancelAnimationFrame(mousemoveRafId)
1576
+ mousemoveRafId = 0
1577
+ }
1539
1578
  tableEl.removeEventListener('mousemove', mousemoveHandler)
1540
1579
  tableEl.removeEventListener('mousedown', mousedownHandler)
1541
1580
  tableEl.removeEventListener('mouseover', mouseoverHandler)
@@ -1610,9 +1649,8 @@ export function useCanvasTable(options: TableOptions) {
1610
1649
  }
1611
1650
  }
1612
1651
 
1613
- el.content = div.innerHTML
1614
1652
  if (selectedElementHeight) selectedElementHeight.value = `${totalHeight}px`
1615
- addHistorySnapshot()
1653
+ commitTableContent(el, div.innerHTML)
1616
1654
  }
1617
1655
 
1618
1656
  function equalizeTableColWidths() {
@@ -1638,9 +1676,8 @@ export function useCanvasTable(options: TableOptions) {
1638
1676
  }
1639
1677
  }
1640
1678
 
1641
- el.content = div.innerHTML
1642
1679
  if (selectedElementWidth) selectedElementWidth.value = `${totalWidth}px`
1643
- addHistorySnapshot()
1680
+ commitTableContent(el, div.innerHTML)
1644
1681
  }
1645
1682
 
1646
1683
  return {
@@ -32,7 +32,7 @@ export function useSidebarTextFormat(
32
32
  activeSlide: Ref<any>,
33
33
  slides: Ref<any[]>,
34
34
  activeIndex: Ref<number>,
35
- historyStack: Ref<string[]>,
35
+ historyStack: Ref<string[][]>,
36
36
  historyIndex: Ref<number>,
37
37
  globalFontColor: Ref<string>,
38
38
  shapeFormatPainter: Ref<any>,