jvs-ppt-ui 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -1
- package/dist/lib/jvs-ppt-ui.es.js +21367 -21291
- package/dist/lib/jvs-ppt-ui.umd.js +85 -85
- package/dist/lib/style.css +1 -1
- package/package.json +7 -7
- package/scripts/check_connectors.cjs +25 -0
- package/scripts/check_slide1.cjs +11 -0
- package/scripts/check_slide123.cjs +25 -0
- package/scripts/convert-fonts-to-woff2.cjs +97 -0
- package/scripts/extract-slides.ts +149 -0
- package/scripts/find_candidate_pptx.cjs +34 -0
- package/scripts/find_orange_slide.cjs +29 -0
- package/scripts/generate-complete-ppt-preview.js +385 -0
- package/scripts/inspect_315_327.cjs +19 -0
- package/scripts/mcp-smoke-test.js +568 -0
- package/scripts/patch-pptxtojson.cjs +81 -0
- package/scripts/verify-fonts.cjs +20 -0
- package/src/App.vue +1 -1
- package/src/components/CanvasArea.vue +38 -16
- package/src/components/Header.vue +1587 -1564
- package/src/components/PptEditor.vue +5 -0
- package/src/components/SidebarLeft.vue +14 -3
- package/src/components/canvas/elements/CanvasShapeElement.vue +3 -3
- package/src/components/canvas/elements/CanvasTextElement.vue +4 -4
- package/src/composables/canvas/useCanvasShortcuts.ts +50 -1
- package/src/composables/canvas/useCanvasTable.ts +8 -3
- package/src/composables/canvas/useCanvasText.ts +7 -3
- package/src/composables/useSlides.ts +14 -5
|
@@ -66,7 +66,7 @@ const {
|
|
|
66
66
|
globalThemeColor, globalFontColor, globalFontFamily, globalBgColor, getActiveSlideThemeRecipe, croppingElementId,
|
|
67
67
|
isMasterEditingView, slideMasters, activeMasterId, activeLayoutId, activeLayout, sortSlideElements,
|
|
68
68
|
selectedSlideIndexes, isMouseOverSlideList, selectAllSlides, deleteSelectedSlides, copySelectedSlides, cutSelectedSlides, duplicateSelectedSlides, duplicateSelectedElements,
|
|
69
|
-
getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource
|
|
69
|
+
getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource, isReadonly
|
|
70
70
|
} = useSlides()
|
|
71
71
|
const activeTab = activeTabRaw as any
|
|
72
72
|
|
|
@@ -205,7 +205,8 @@ const {
|
|
|
205
205
|
applyTextStyleToHtml: (html: string, style: Record<string, any>) => applyTextStyleToHtml(html, style),
|
|
206
206
|
renderLatexToSvg: (latex: string) => renderLatexToSvg(latex),
|
|
207
207
|
sanitizeNbspInHtml: (html: string) => sanitizeNbspInHtml(html),
|
|
208
|
-
updateTextFontScale: (el: any) => updateTextFontScale(el)
|
|
208
|
+
updateTextFontScale: (el: any) => updateTextFontScale(el),
|
|
209
|
+
isReadonly
|
|
209
210
|
})
|
|
210
211
|
const lastMouseDownTarget = ref<HTMLElement | null>(null)
|
|
211
212
|
|
|
@@ -405,7 +406,8 @@ const {
|
|
|
405
406
|
getOverlayWidth: (el: any) => getOverlayWidth(el),
|
|
406
407
|
getOverlayHeight: (el: any) => getOverlayHeight(el),
|
|
407
408
|
selectedElementWidth,
|
|
408
|
-
selectedElementHeight
|
|
409
|
+
selectedElementHeight,
|
|
410
|
+
isReadonly
|
|
409
411
|
})
|
|
410
412
|
|
|
411
413
|
const getSelectedCellCount = () => selectedCellCount.value
|
|
@@ -1509,7 +1511,8 @@ const { handleGlobalKeyDown } = useCanvasShortcuts({
|
|
|
1509
1511
|
getElementPixels: (el: any) => getElementPixels(el),
|
|
1510
1512
|
shapeFormatPainter,
|
|
1511
1513
|
notify,
|
|
1512
|
-
addHistorySnapshot
|
|
1514
|
+
addHistorySnapshot,
|
|
1515
|
+
isReadonly
|
|
1513
1516
|
})
|
|
1514
1517
|
|
|
1515
1518
|
watch(fitTrigger, () => {
|
|
@@ -2397,6 +2400,7 @@ function getLineConfigFromContent(content: string) {
|
|
|
2397
2400
|
}
|
|
2398
2401
|
|
|
2399
2402
|
function handleCanvasDblClick(e: MouseEvent) {
|
|
2403
|
+
if (isReadonly.value) return
|
|
2400
2404
|
// 1. 如果事件目标本身或其父级就在某元素内
|
|
2401
2405
|
const target = e.target as HTMLElement
|
|
2402
2406
|
const closestElDom = target?.closest?.('.canvas-element, [id^="canvas-el-"]') as HTMLElement | null
|
|
@@ -2555,6 +2559,12 @@ function handleCanvasMouseDown(e: MouseEvent) {
|
|
|
2555
2559
|
return
|
|
2556
2560
|
}
|
|
2557
2561
|
|
|
2562
|
+
if (isReadonly.value) {
|
|
2563
|
+
selectedElementId.value = null
|
|
2564
|
+
selectedElementIds.value = []
|
|
2565
|
+
return
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2558
2568
|
if (shapeFormatPainter.value) {
|
|
2559
2569
|
shapeFormatPainter.value = null
|
|
2560
2570
|
return
|
|
@@ -3256,7 +3266,7 @@ let lineControlPointStartX = 0
|
|
|
3256
3266
|
let lineControlPointStartY = 0
|
|
3257
3267
|
|
|
3258
3268
|
function startDragLineControlPoint(e: MouseEvent, el: any, type: string) {
|
|
3259
|
-
if (el.locked) return
|
|
3269
|
+
if (isReadonly.value || el.locked) return
|
|
3260
3270
|
e.stopPropagation()
|
|
3261
3271
|
e.preventDefault()
|
|
3262
3272
|
|
|
@@ -3578,6 +3588,10 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
|
|
|
3578
3588
|
|
|
3579
3589
|
e.stopPropagation()
|
|
3580
3590
|
|
|
3591
|
+
if (isReadonly.value) {
|
|
3592
|
+
return
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3581
3595
|
if (shapeFormatPainter.value) {
|
|
3582
3596
|
if (shapeFormatPainter.value.mode === 'text-range') {
|
|
3583
3597
|
return
|
|
@@ -3606,6 +3620,7 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
|
|
|
3606
3620
|
}
|
|
3607
3621
|
|
|
3608
3622
|
function handleBorderMouseDown(e: MouseEvent, el: any) {
|
|
3623
|
+
if (isReadonly.value) return
|
|
3609
3624
|
if (e.button !== 0) return
|
|
3610
3625
|
if (isLockedMasterElement(el) || el.locked) {
|
|
3611
3626
|
return
|
|
@@ -3650,6 +3665,7 @@ function handleBorderMouseDown(e: MouseEvent, el: any) {
|
|
|
3650
3665
|
}
|
|
3651
3666
|
|
|
3652
3667
|
function handleElementDblClick(e: MouseEvent, el: any) {
|
|
3668
|
+
if (isReadonly.value) return
|
|
3653
3669
|
let target = el
|
|
3654
3670
|
if (selectedElementIds.value.length > 0 && !selectedElementIds.value.includes(el.id)) {
|
|
3655
3671
|
const activeSelectedEl = findSelectedElementUnderPoint(e.clientX, e.clientY)
|
|
@@ -3762,6 +3778,7 @@ function handleChildDragKeyUp(e: KeyboardEvent) {
|
|
|
3762
3778
|
}
|
|
3763
3779
|
|
|
3764
3780
|
function startChildDrag(e: MouseEvent, child: any, parentGroup: any) {
|
|
3781
|
+
if (isReadonly.value) return
|
|
3765
3782
|
const childContainerEl = document.getElementById(`editable-${child.id}`)
|
|
3766
3783
|
const childCanvasElDom = document.getElementById(`canvas-el-${child.id}`)
|
|
3767
3784
|
const isChildClickInside = (childContainerEl && (childContainerEl === e.target || childContainerEl.contains(e.target as Node))) ||
|
|
@@ -3975,6 +3992,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
3975
3992
|
checkAndExitEditText(undefined, true)
|
|
3976
3993
|
}
|
|
3977
3994
|
e.stopPropagation()
|
|
3995
|
+
if (isReadonly.value) return
|
|
3978
3996
|
|
|
3979
3997
|
if (shapeFormatPainter.value) {
|
|
3980
3998
|
if (shapeFormatPainter.value.mode === 'text-range') {
|
|
@@ -4039,7 +4057,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4039
4057
|
}
|
|
4040
4058
|
|
|
4041
4059
|
function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
4042
|
-
if (activeTool.value) return
|
|
4060
|
+
if (isReadonly.value || activeTool.value) return
|
|
4043
4061
|
if (isLockedMasterElement(child)) return
|
|
4044
4062
|
e.stopPropagation()
|
|
4045
4063
|
if (dragOccurred) {
|
|
@@ -4073,6 +4091,7 @@ function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4073
4091
|
}
|
|
4074
4092
|
|
|
4075
4093
|
function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
4094
|
+
if (isReadonly.value) return
|
|
4076
4095
|
e.stopPropagation()
|
|
4077
4096
|
if (isLockedMasterElement(child)) return
|
|
4078
4097
|
// 已在编辑该子元素时的双击 = 原生选词,保持选区不重进编辑
|
|
@@ -4096,6 +4115,7 @@ function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4096
4115
|
}
|
|
4097
4116
|
|
|
4098
4117
|
function handleElementClick(e: MouseEvent, elId: string) {
|
|
4118
|
+
if (isReadonly.value) return
|
|
4099
4119
|
if (activeTool.value) return
|
|
4100
4120
|
if (elId) {
|
|
4101
4121
|
const targetEl = activeSlide.value?.elements.find(x => x.id === elId)
|
|
@@ -4495,6 +4515,7 @@ function handleTextMouseLeave(_e: MouseEvent, el: any) {
|
|
|
4495
4515
|
}
|
|
4496
4516
|
|
|
4497
4517
|
function startDrag(e: MouseEvent, el: any) {
|
|
4518
|
+
if (isReadonly.value) return
|
|
4498
4519
|
const target = e.target as HTMLElement
|
|
4499
4520
|
const container = document.getElementById(`editable-${el.id}`)
|
|
4500
4521
|
const canvasElDom = document.getElementById(`canvas-el-${el.id}`)
|
|
@@ -7821,7 +7842,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
7821
7842
|
</defs>
|
|
7822
7843
|
</svg>
|
|
7823
7844
|
<!-- Smart Guides & Corner Decorations -->
|
|
7824
|
-
<CanvasSmartGuides :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
|
|
7845
|
+
<CanvasSmartGuides v-if="!isReadonly" :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
|
|
7825
7846
|
|
|
7826
7847
|
<!-- Ctrl Drag Copy Origin Replica (原元素在原地 100% 完整原样真实呈现,杜绝空虚高亮块) -->
|
|
7827
7848
|
<template v-if="ctrlDragReplicas.length > 0">
|
|
@@ -8025,14 +8046,14 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8025
8046
|
width: (el.type === 'text' && !isMasterOrPlaceholder(el) && (el.autoFit?.type === 'shape' || el.style?.width === 'auto') && !el.content?.includes('<table') && !el.latex && (el.style?.writingMode === 'vertical-rl' || el.style?.writingMode === 'vertical-lr')) ? 'auto' : (el.style?.width || 'auto'),
|
|
8026
8047
|
height: (el.type === 'text' && !isMasterOrPlaceholder(el) && (el.autoFit?.type === 'shape' || el.style?.height === 'auto') && !el.content?.includes('<table') && !el.latex && el.style?.writingMode !== 'vertical-rl' && el.style?.writingMode !== 'vertical-lr') ? 'auto' : (el.style?.height || (el.height ? `${el.height}px` : 'auto')),
|
|
8027
8048
|
zIndex: 3,
|
|
8028
|
-
cursor: (isEditingText && (selectedElementId === el.id || editingElementId === el.id) && (el.type === 'text' || el.type === 'shape') && !el.latex && !el.content?.includes('<table')) ? 'text' : undefined,
|
|
8049
|
+
cursor: isReadonly ? 'default' : ((isEditingText && (selectedElementId === el.id || editingElementId === el.id) && (el.type === 'text' || el.type === 'shape') && !el.latex && !el.content?.includes('<table')) ? 'text' : undefined),
|
|
8029
8050
|
transform: el.type === 'line' ? 'none' : `rotate(${el.style?.rotate || 0}deg) scale(${el.type !== 'text' && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${el.type !== 'text' && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
|
|
8030
8051
|
opacity: (el.style as any)?.opacity !== undefined ? (el.style as any)?.opacity : 1,
|
|
8031
8052
|
filter: getElementDropShadowFilter(el),
|
|
8032
8053
|
pointerEvents: (isDragging || isCanvasDragging) ? 'none' : ((isDraggingLine && el.id === selectedElementId) ? 'none' : (isLockedMasterElement(el) ? 'none' : (el.type === 'group' ? 'none' : (el.type === 'line' ? 'none' : (((el.type === 'shape' || !el.type) && !hasElementFill(el) && !hasElementBorder(el) && !isImagePlaceholder(el)) ? 'none' : 'auto')))))
|
|
8033
8054
|
}" @mousedown="handleElementMouseDown($event, el)" @click="handleElementClick($event, el.id)"
|
|
8034
8055
|
@contextmenu.stop.prevent="handleElementContextMenu($event, el)"
|
|
8035
|
-
@dblclick.stop="handleElementDblClick($event, el)"
|
|
8056
|
+
@dblclick.stop="isReadonly ? null : handleElementDblClick($event, el)"
|
|
8036
8057
|
@mouseenter="!(isDragging || isCanvasDragging) && (hoveredElementId = el.id)"
|
|
8037
8058
|
@mouseleave="!(isDragging || isCanvasDragging) && (hoveredElementId = (hoveredElementId === el.id ? null : hoveredElementId))">
|
|
8038
8059
|
|
|
@@ -8124,7 +8145,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8124
8145
|
@focusout="handleTextBlur($event, el)"
|
|
8125
8146
|
@input="handleInput($event, el)"
|
|
8126
8147
|
@keydown="handleTextKeyDown($event, el)"
|
|
8127
|
-
@dblclick="el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event))"
|
|
8148
|
+
@dblclick="isReadonly ? null : (el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event)))"
|
|
8128
8149
|
@mousemove="handleTextMouseMove($event, el)"
|
|
8129
8150
|
@mouseleave="handleTextMouseLeave($event, el)"
|
|
8130
8151
|
@table-dbl-click="handleTableDblClick($event, el)"
|
|
@@ -8154,7 +8175,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8154
8175
|
@text-key-down="handleTextKeyDown($event, el)"
|
|
8155
8176
|
@mousemove="handleTextMouseMove($event, el)"
|
|
8156
8177
|
@mouseleave="handleTextMouseLeave($event, el)"
|
|
8157
|
-
@dblclick="startEditingShapeText(el, $event)"
|
|
8178
|
+
@dblclick="isReadonly ? null : startEditingShapeText(el, $event)"
|
|
8158
8179
|
@start-editing="(ev) => startEditingShapeText(el, ev)"
|
|
8159
8180
|
@placeholder-upload="(id) => triggerPlaceholderImageUpload(id)"
|
|
8160
8181
|
/>
|
|
@@ -8466,7 +8487,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8466
8487
|
</div>
|
|
8467
8488
|
|
|
8468
8489
|
<!-- Global Selection Overlay (renders on top of all canvas elements to avoid masking) -->
|
|
8469
|
-
<div v-for="selEl in selectedElements" :key="selEl.id" class="global-selection-overlay" :class="{
|
|
8490
|
+
<div v-for="selEl in (isReadonly ? [] : selectedElements)" :key="selEl.id" class="global-selection-overlay" :class="{
|
|
8470
8491
|
'line-selection': selEl.type === 'line',
|
|
8471
8492
|
'locked-selection': selEl.locked,
|
|
8472
8493
|
'table-selection': selEl.type === 'text' && selEl.content && selEl.content.includes('<table'),
|
|
@@ -8636,7 +8657,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8636
8657
|
</template>
|
|
8637
8658
|
|
|
8638
8659
|
<!-- Draggable border overlay to facilitate dragging and state switching (available in both editing & selected states) -->
|
|
8639
|
-
<div v-if="selEl.type !== 'line' && !selEl.locked && croppingElementId !== selEl.id && selectedElementIds.includes(selEl.id)"
|
|
8660
|
+
<div v-if="!isReadonly && selEl.type !== 'line' && !selEl.locked && croppingElementId !== selEl.id && selectedElementIds.includes(selEl.id)"
|
|
8640
8661
|
class="text-edit-drag-helper"
|
|
8641
8662
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 200;"
|
|
8642
8663
|
@contextmenu.stop.prevent="handleElementContextMenu($event, selEl)"
|
|
@@ -8697,7 +8718,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8697
8718
|
|
|
8698
8719
|
<!-- Transform, Resize, Line Endpoints & Crop Handles -->
|
|
8699
8720
|
<CanvasTransformHandles
|
|
8700
|
-
v-if="selectedElementIds.includes(selEl.id)"
|
|
8721
|
+
v-if="!isReadonly && selectedElementIds.includes(selEl.id)"
|
|
8701
8722
|
:element="selEl"
|
|
8702
8723
|
:selected-element-id="selectedElementId"
|
|
8703
8724
|
:selected-element-ids="selectedElementIds"
|
|
@@ -8860,7 +8881,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8860
8881
|
|
|
8861
8882
|
<!-- Floating Quick Toolbar (No rotation, rendered as sibling of main-slide inside slide-zoom-container) -->
|
|
8862
8883
|
<CanvasQuickToolbar
|
|
8863
|
-
v-if="!isDragging && !isResizing && !isRotating && !isDraggingLine"
|
|
8884
|
+
v-if="!isReadonly && !isDragging && !isResizing && !isRotating && !isDraggingLine"
|
|
8864
8885
|
:selected-element="selectedElement"
|
|
8865
8886
|
:selected-element-ids="selectedElementIds"
|
|
8866
8887
|
:is-dragging="isDragging"
|
|
@@ -8934,11 +8955,12 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8934
8955
|
|
|
8935
8956
|
<!-- Footer Notes -->
|
|
8936
8957
|
<div class="canvas-footer">
|
|
8937
|
-
<input class="canvas-footer-notes" v-model="activeSlide.notes" placeholder="点击输入演讲者备注" />
|
|
8958
|
+
<input class="canvas-footer-notes" :readonly="isReadonly" v-model="activeSlide.notes" :placeholder="isReadonly ? '演讲者备注' : '点击输入演讲者备注'" />
|
|
8938
8959
|
</div>
|
|
8939
8960
|
|
|
8940
8961
|
<!-- Custom Context Menu Component -->
|
|
8941
8962
|
<CanvasContextMenu
|
|
8963
|
+
v-if="!isReadonly"
|
|
8942
8964
|
v-model:visible="showContextMenu"
|
|
8943
8965
|
:position="contextMenuPos"
|
|
8944
8966
|
:type="contextMenuType"
|