@a3s-lab/office 0.33.0 → 0.34.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.
- package/README.md +5 -3
- package/dist/0~5809.js +123 -0
- package/dist/{0~7614.js → 0~7828.js} +376 -5
- package/dist/0~pdf-viewer.js +5 -1
- package/dist/0~presentation-editor.js +578 -66
- package/dist/0~work-pptx-export.js +50 -14
- package/dist/0~work-pptx-import.js +21 -9
- package/dist/4121.js +122 -1
- package/dist/4560.js +33 -0
- package/dist/internal/features/work/editors/presentation-animation-panel.d.ts +11 -0
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +16 -1
- package/dist/internal/features/work/editors/presentation-presenter-view.d.ts +2 -1
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +11 -3
- package/dist/internal/features/work/editors/use-presentation-animation-commands.d.ts +15 -0
- package/dist/internal/features/work/work-pptx-animation.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-groups.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation-constraints.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation.d.ts +20 -0
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +10 -3
- package/dist/internal/features/work/work-types.d.ts +17 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +123 -0
- package/package.json +4 -1
- package/dist/0~work-presentation-transition.js +0 -38
|
@@ -3,7 +3,7 @@ import { normalizeRadarStyle, presentationChartShowsLegend, presentationChartAxe
|
|
|
3
3
|
import { directChildren, xmlNamespacePrefix, parseXml, firstDescendant, directChild, descendants, childPath, attribute } from "./4121.js";
|
|
4
4
|
import { workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartAxisIsCategoryAxis, workSpreadsheetChartAxisIsValueAxis } from "./8715.js";
|
|
5
5
|
import { presentationGroupPath, withPresentationDesign } from "./0~1403.js";
|
|
6
|
-
import { patchPptxChartLayoutAndSeriesStyles, patchPptxTransitions, patchPptxComments, patchPptxChartDataLabels, patchPptxChartSeriesAnalysis } from "./0~
|
|
6
|
+
import { patchPptxChartLayoutAndSeriesStyles, patchPptxTransitions, patchPptxComments, patchPptxChartDataLabels, patchPptxChartSeriesAnalysis, patchPptxAnimations } from "./0~7828.js";
|
|
7
7
|
const DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
|
|
8
8
|
async function patchPptxChartAxes(buffer, slides) {
|
|
9
9
|
const charts = slides.flatMap((slide)=>slide.elements.flatMap((element)=>'chart' === element.type && element.chart ? [
|
|
@@ -270,27 +270,40 @@ const PRESENTATIONML_NAMESPACE = 'http://schemas.openxmlformats.org/presentation
|
|
|
270
270
|
const DRAWINGML_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
|
|
271
271
|
const GROUPABLE_PART = /^ppt\/(?:slides|slideLayouts|slideMasters)\/[^/]+\.xml$/;
|
|
272
272
|
class PptxGroupExportRegistry {
|
|
273
|
+
animatedElementKeys = new Set();
|
|
273
274
|
bindings = new Map();
|
|
275
|
+
bindingsByElement = new Map();
|
|
274
276
|
roleCounts = new Map();
|
|
275
277
|
markerSequence = 0;
|
|
276
278
|
get size() {
|
|
277
279
|
return this.bindings.size;
|
|
278
280
|
}
|
|
281
|
+
registerAnimatedElements(scope, elementIds) {
|
|
282
|
+
for (const elementId of elementIds)this.animatedElementKeys.add(exportElementKey(scope, elementId));
|
|
283
|
+
}
|
|
279
284
|
objectName(scope, element, role) {
|
|
280
285
|
const groupPath = presentationGroupPath(element);
|
|
281
|
-
|
|
286
|
+
const elementKey = exportElementKey(scope, element.id);
|
|
287
|
+
if (!groupPath.length && !this.animatedElementKeys.has(elementKey)) return;
|
|
288
|
+
const existing = this.bindingsByElement.get(elementKey);
|
|
289
|
+
if (existing) return existing.marker;
|
|
282
290
|
const roleCount = (this.roleCounts.get(role) ?? 0) + 1;
|
|
283
291
|
this.roleCounts.set(role, roleCount);
|
|
284
292
|
this.markerSequence += 1;
|
|
285
293
|
const marker = `${EXPORT_MARKER_PREFIX}${this.markerSequence}`;
|
|
286
|
-
|
|
294
|
+
const binding = {
|
|
287
295
|
displayName: `${pptxRoleName(role)} ${roleCount}`,
|
|
288
296
|
groupPath,
|
|
289
297
|
groupScope: scope,
|
|
290
298
|
marker
|
|
291
|
-
}
|
|
299
|
+
};
|
|
300
|
+
this.bindings.set(marker, binding);
|
|
301
|
+
this.bindingsByElement.set(elementKey, binding);
|
|
292
302
|
return marker;
|
|
293
303
|
}
|
|
304
|
+
markerForElement(scope, elementId) {
|
|
305
|
+
return this.bindingsByElement.get(exportElementKey(scope, elementId))?.marker;
|
|
306
|
+
}
|
|
294
307
|
binding(marker) {
|
|
295
308
|
return this.bindings.get(marker);
|
|
296
309
|
}
|
|
@@ -315,7 +328,7 @@ async function patchPptxNativeGroups(buffer, registry) {
|
|
|
315
328
|
}
|
|
316
329
|
for (const binding of registry.values()){
|
|
317
330
|
const count = found.get(binding.marker) ?? 0;
|
|
318
|
-
if (1 !== count) throw new Error(`PPTX
|
|
331
|
+
if (1 !== count) throw new Error(`PPTX export expected one generated object for ${binding.displayName}, but found ${count}.`);
|
|
319
332
|
}
|
|
320
333
|
return archive.generateAsync({
|
|
321
334
|
type: 'arraybuffer',
|
|
@@ -323,6 +336,7 @@ async function patchPptxNativeGroups(buffer, registry) {
|
|
|
323
336
|
});
|
|
324
337
|
}
|
|
325
338
|
function patchPptxShapeTree(shapeTree, registry, found) {
|
|
339
|
+
normalizePptxNonVisualIds(shapeTree);
|
|
326
340
|
const sceneNodes = directChildren(shapeTree).filter(isPptxSceneNode).map((node, order)=>{
|
|
327
341
|
const properties = firstDescendant(node, 'cNvPr');
|
|
328
342
|
const objectName = properties ? attribute(properties, 'name') : null;
|
|
@@ -342,9 +356,9 @@ function patchPptxShapeTree(shapeTree, registry, found) {
|
|
|
342
356
|
});
|
|
343
357
|
if (!sceneNodes.some((item)=>item.binding)) return false;
|
|
344
358
|
const roots = new Map();
|
|
345
|
-
const rootNodes = sceneNodes.filter((item)=>!item.binding);
|
|
359
|
+
const rootNodes = sceneNodes.filter((item)=>!item.binding?.groupPath.length);
|
|
346
360
|
for (const item of sceneNodes){
|
|
347
|
-
if (!item.binding) continue;
|
|
361
|
+
if (!item.binding?.groupPath.length) continue;
|
|
348
362
|
let siblings = roots;
|
|
349
363
|
let bucket;
|
|
350
364
|
for (const groupId of item.binding.groupPath){
|
|
@@ -378,7 +392,6 @@ function patchPptxShapeTree(shapeTree, registry, found) {
|
|
|
378
392
|
].sort((left, right)=>left.order - right.order);
|
|
379
393
|
const extensionList = directChild(shapeTree, 'extLst') ?? null;
|
|
380
394
|
for (const item of renderedRoots)shapeTree.insertBefore(item.node, extensionList);
|
|
381
|
-
normalizePptxNonVisualIds(shapeTree);
|
|
382
395
|
return true;
|
|
383
396
|
}
|
|
384
397
|
function renderPptxGroup(bucket, document, nextId, nextGroupNumber) {
|
|
@@ -492,6 +505,9 @@ function normalizePptxNonVisualIds(shapeTree) {
|
|
|
492
505
|
function groupBucketKey(scope, groupId) {
|
|
493
506
|
return `${scope}\u0000${groupId}`;
|
|
494
507
|
}
|
|
508
|
+
function exportElementKey(scope, elementId) {
|
|
509
|
+
return `${scope}\u0000${elementId}`;
|
|
510
|
+
}
|
|
495
511
|
function isPptxSceneNode(node) {
|
|
496
512
|
return [
|
|
497
513
|
'contentPart',
|
|
@@ -735,7 +751,9 @@ function createPptxExportState(artifact, PptxGenJS) {
|
|
|
735
751
|
if (!source.useLayoutBackground) slide.background = {
|
|
736
752
|
color: source.background.replace('#', '')
|
|
737
753
|
};
|
|
738
|
-
|
|
754
|
+
const groupScope = `slide:${source.id}`;
|
|
755
|
+
groups.registerAnimatedElements(groupScope, source.animations?.map((animation)=>animation.elementId) ?? []);
|
|
756
|
+
for (const element of source.elements)addPresentationElement(slide, element, presentation, slideWidth, slideHeight, groups, groupScope, element.placeholder ? binding?.placeholderNames.get(element.placeholder.key) : void 0);
|
|
739
757
|
if (source.notes?.trim()) slide.addNotes(source.notes);
|
|
740
758
|
}
|
|
741
759
|
return {
|
|
@@ -758,7 +776,8 @@ async function createPptxBlob(artifact, PptxGenJS) {
|
|
|
758
776
|
const withChartSeriesAnalysis = await patchPptxChartSeriesAnalysis(withChartAxes, slides);
|
|
759
777
|
const withTransitions = await patchPptxTransitions(withChartSeriesAnalysis, slides);
|
|
760
778
|
const withComments = await patchPptxComments(withTransitions, slides, 'presentation' === artifact.content.type ? artifact.content.width ?? 13.333 : 13.333, 'presentation' === artifact.content.type ? artifact.content.height ?? 7.5 : 7.5);
|
|
761
|
-
const
|
|
779
|
+
const withAnimations = await patchPptxAnimations(withComments, slides, groups);
|
|
780
|
+
const patched = await patchPptxNativeGroups(withAnimations, groups);
|
|
762
781
|
return new Blob([
|
|
763
782
|
patched
|
|
764
783
|
], {
|
|
@@ -886,8 +905,13 @@ function addPresentationElement(slide, element, presentation, slideWidth, slideH
|
|
|
886
905
|
});
|
|
887
906
|
return;
|
|
888
907
|
}
|
|
889
|
-
if ('shape' === element.type)
|
|
890
|
-
|
|
908
|
+
if ('shape' === element.type) {
|
|
909
|
+
const objectName = groups.objectName(groupScope, element, 'shape');
|
|
910
|
+
if (element.text || element.textRuns?.length) addText(slide, element, x, y, width, height, resolvedPlaceholder, objectName, pptxShapeType(presentation, element));
|
|
911
|
+
else addShape(slide, element, presentation, x, y, width, height, resolvedPlaceholder, objectName);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if ('text' === element.type) addText(slide, element, x, y, width, height, resolvedPlaceholder, groups.objectName(groupScope, element, 'text'));
|
|
891
915
|
}
|
|
892
916
|
function pptxChartData(chart) {
|
|
893
917
|
if (presentationChartUsesNumericXAxis(chart.type)) return [
|
|
@@ -926,7 +950,7 @@ function pptxLegendPosition(position) {
|
|
|
926
950
|
return 'r';
|
|
927
951
|
}
|
|
928
952
|
function addShape(slide, element, presentation, x, y, width, height, placeholder, objectName) {
|
|
929
|
-
const shapeType =
|
|
953
|
+
const shapeType = pptxShapeType(presentation, element);
|
|
930
954
|
const fillColor = element.fill.replace('#', '');
|
|
931
955
|
const borderColor = (element.borderColor ?? element.fill).replace('#', '');
|
|
932
956
|
const options = {
|
|
@@ -954,7 +978,7 @@ function addShape(slide, element, presentation, x, y, width, height, placeholder
|
|
|
954
978
|
};
|
|
955
979
|
slide.addShape(shapeType, options);
|
|
956
980
|
}
|
|
957
|
-
function addText(slide, element, x, y, width, height, placeholder, objectName) {
|
|
981
|
+
function addText(slide, element, x, y, width, height, placeholder, objectName, shape) {
|
|
958
982
|
const text = element.textRuns?.length ? element.textRuns.map((run)=>({
|
|
959
983
|
text: run.text,
|
|
960
984
|
options: {
|
|
@@ -989,9 +1013,14 @@ function addText(slide, element, x, y, width, height, placeholder, objectName) {
|
|
|
989
1013
|
valign: element.verticalAlign ?? 'middle',
|
|
990
1014
|
margin: 'transparent' === element.fill ? 0 : 10,
|
|
991
1015
|
breakLine: false,
|
|
1016
|
+
shape,
|
|
992
1017
|
fill: 'transparent' === element.fill ? void 0 : {
|
|
993
1018
|
color: element.fill.replace('#', '')
|
|
994
1019
|
},
|
|
1020
|
+
line: element.borderWidth && element.borderColor ? {
|
|
1021
|
+
color: element.borderColor.replace('#', ''),
|
|
1022
|
+
width: element.borderWidth
|
|
1023
|
+
} : void 0,
|
|
995
1024
|
hyperlink: element.href ? {
|
|
996
1025
|
url: element.href
|
|
997
1026
|
} : void 0,
|
|
@@ -1001,6 +1030,13 @@ function addText(slide, element, x, y, width, height, placeholder, objectName) {
|
|
|
1001
1030
|
} : {}
|
|
1002
1031
|
});
|
|
1003
1032
|
}
|
|
1033
|
+
function pptxShapeType(presentation, element) {
|
|
1034
|
+
if ('ellipse' === element.shapeType) return presentation.ShapeType.ellipse;
|
|
1035
|
+
if ('triangle' === element.shapeType) return presentation.ShapeType.triangle;
|
|
1036
|
+
if ('diamond' === element.shapeType) return presentation.ShapeType.diamond;
|
|
1037
|
+
if ('roundRect' === element.shapeType) return presentation.ShapeType.roundRect;
|
|
1038
|
+
return presentation.ShapeType.rect;
|
|
1039
|
+
}
|
|
1004
1040
|
async function presentationArrayBuffer(output) {
|
|
1005
1041
|
if (output instanceof ArrayBuffer) return output;
|
|
1006
1042
|
if (output instanceof Blob) return output.arrayBuffer();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { contentTypeForPart, directChildren, bytesToDataUrl, OoxmlPackage, firstDescendant, descendants, directChild, childPath, attribute, createOfficeId as createWorkId } from "./4121.js";
|
|
2
2
|
import { normalizeRadarStyle, normalizePresentationChartLegendPosition, presentationChartUsesNumericXAxis, normalizePresentationBubbleScale, normalizePresentationBubbleSizeRepresents, withPresentationChartLayout, normalizeDoughnutHoleSize, normalizePresentationScatterStyle } from "./0~work-presentation-charts.js";
|
|
3
|
-
import { loadPptxCommentAuthors, readPptxSlideComments, readPptxChartLayoutAndSeriesStyles, readPptxTransition, readPptxChartSeriesAnalysis, readPptxChartDataLabels } from "./0~
|
|
3
|
+
import { loadPptxCommentAuthors, readPptxSlideComments, readPptxChartLayoutAndSeriesStyles, readPptxAnimations, readPptxTransition, readPptxChartSeriesAnalysis, readPptxChartDataLabels } from "./0~7828.js";
|
|
4
4
|
import { parseXlsxChartAxes } from "./4166.js";
|
|
5
5
|
import { scaledPresentationVisuals } from "./0~work-presentation-visual-scale.js";
|
|
6
6
|
async function loadPptxTheme(archive, presentationRelationships) {
|
|
@@ -547,6 +547,7 @@ async function parseSlide(archive, slidePart, slideNumber, slideWidthEmu, slideH
|
|
|
547
547
|
theme,
|
|
548
548
|
issues,
|
|
549
549
|
imageBudget,
|
|
550
|
+
elementIdByPptxShapeId: new Map(),
|
|
550
551
|
location: `幻灯片 ${slideNumber}`
|
|
551
552
|
};
|
|
552
553
|
const design = await ensurePptxDesignDefinitions(archive, inheritance.layout, inheritance.master, context, designs);
|
|
@@ -554,6 +555,7 @@ async function parseSlide(archive, slidePart, slideNumber, slideWidthEmu, slideH
|
|
|
554
555
|
const shapeTree = firstDescendant(document1, 'spTree');
|
|
555
556
|
for (const child of shapeTree ? directChildren(shapeTree) : [])elements.push(...await parseSlideNode(child, context));
|
|
556
557
|
const resolvedElements = inheritPptxPlaceholderStyles(elements, design.master, design.layout);
|
|
558
|
+
const animationResult = readPptxAnimations(document1, context.elementIdByPptxShapeId);
|
|
557
559
|
const transition = readPptxTransition(document1);
|
|
558
560
|
const commentResult = await readPptxSlideComments(archive, relationships, commentAuthors, slideNumber, slideWidthEmu, slideHeightEmu);
|
|
559
561
|
if (commentResult.comments.length) addIssue(context, 'pptx.comments', 'Comments', `${commentResult.comments.length} traditional slide comment(s), authors, dates, and positions are preserved and editable.`, 'info');
|
|
@@ -564,7 +566,8 @@ async function parseSlide(archive, slidePart, slideNumber, slideWidthEmu, slideH
|
|
|
564
566
|
addIssue(context, 'pptx.transition', 'Slide transitions', transition.transition ? 'Basic slide transitions and advance timing are preserved, editable, and replayed in presentation preview.' : 'This slide transition is not editable and remains in the original PPTX only.', transition.transition ? 'info' : 'warning');
|
|
565
567
|
for (const diagnostic of transition.diagnostics)addIssue(context, diagnostic.code, 'Slide transitions', diagnostic.message);
|
|
566
568
|
}
|
|
567
|
-
if (
|
|
569
|
+
if (animationResult.animations.length) addIssue(context, 'pptx.animation', 'Animations', `${animationResult.animations.length} supported object entrance animation(s) are preserved, editable, replayed, and exported.`, 'info');
|
|
570
|
+
for (const diagnostic of animationResult.diagnostics)addIssue(context, diagnostic.code, 'Animations', diagnostic.message);
|
|
568
571
|
for (const relationship of relationships.values())if (relationship.type.includes('/audio') || relationship.type.includes('/video') || relationship.type.includes('/media')) addIssue(context, 'pptx.media', 'Embedded media', 'Audio and video remain in the original file but are not playable yet.');
|
|
569
572
|
else if (relationship.type.endsWith('/oleObject') || relationship.type.endsWith('/package')) addIssue(context, 'pptx.ole', 'Embedded objects', 'Embedded Office or OLE objects remain in the original file only.');
|
|
570
573
|
const slideBackground = readPptxBackground(document1, theme);
|
|
@@ -578,7 +581,8 @@ async function parseSlide(archive, slidePart, slideNumber, slideWidthEmu, slideH
|
|
|
578
581
|
elements: resolvedElements,
|
|
579
582
|
notes: await readSpeakerNotes(archive, relationships, context),
|
|
580
583
|
comments: commentResult.comments.length ? commentResult.comments : void 0,
|
|
581
|
-
transition: transition.transition
|
|
584
|
+
transition: transition.transition,
|
|
585
|
+
animations: animationResult.animations.length ? animationResult.animations : void 0
|
|
582
586
|
};
|
|
583
587
|
}
|
|
584
588
|
async function loadPptxPresentationDesigns(archive, relationships, slideWidthEmu, slideHeightEmu, theme, issues, imageBudget, registry) {
|
|
@@ -592,6 +596,7 @@ async function loadPptxPresentationDesigns(archive, relationships, slideWidthEmu
|
|
|
592
596
|
theme,
|
|
593
597
|
issues,
|
|
594
598
|
imageBudget,
|
|
599
|
+
elementIdByPptxShapeId: new Map(),
|
|
595
600
|
location: '母版与布局'
|
|
596
601
|
};
|
|
597
602
|
for (const relationship of relationships.values()){
|
|
@@ -659,6 +664,7 @@ function ensureFallbackPptxMaster(registry) {
|
|
|
659
664
|
async function parsePptxDesignElements(part, context) {
|
|
660
665
|
const partContext = {
|
|
661
666
|
...context,
|
|
667
|
+
elementIdByPptxShapeId: new Map(),
|
|
662
668
|
relationships: part.relationships,
|
|
663
669
|
placeholders: new Map()
|
|
664
670
|
};
|
|
@@ -712,24 +718,30 @@ function inheritPptxPlaceholderStyle(element, definition) {
|
|
|
712
718
|
};
|
|
713
719
|
}
|
|
714
720
|
async function parseSlideNode(node, context, transform) {
|
|
715
|
-
|
|
721
|
+
let elements;
|
|
722
|
+
if ('sp' === node.localName) elements = [
|
|
716
723
|
withImportedGroupTransform(parseShape(node, context, transform), transform)
|
|
717
724
|
];
|
|
718
|
-
if ('pic' === node.localName)
|
|
725
|
+
else if ('pic' === node.localName) elements = [
|
|
719
726
|
withImportedGroupTransform(await parsePicture(node, context, transform), transform)
|
|
720
727
|
];
|
|
721
|
-
if ('graphicFrame' === node.localName)
|
|
728
|
+
else if ('graphicFrame' === node.localName) elements = [
|
|
722
729
|
withImportedGroupTransform(await parseGraphicFrame(node, context, transform), transform)
|
|
723
730
|
];
|
|
724
|
-
if ('cxnSp' === node.localName)
|
|
731
|
+
else if ('cxnSp' === node.localName) elements = [
|
|
725
732
|
withImportedGroupTransform(parseConnector(node, context, transform), transform)
|
|
726
733
|
];
|
|
727
|
-
|
|
734
|
+
else {
|
|
735
|
+
if ('grpSp' === node.localName) return parseGroup(node, context, transform);
|
|
736
|
+
elements = [];
|
|
737
|
+
}
|
|
728
738
|
if ([
|
|
729
739
|
'contentPart',
|
|
730
740
|
'oleObj'
|
|
731
741
|
].includes(node.localName)) addIssue(context, 'pptx.content-part', 'Embedded content', 'An embedded slide object remains available in the original PPTX only.');
|
|
732
|
-
|
|
742
|
+
const pptxShapeId = attribute(firstDescendant(node, 'cNvPr') ?? node, 'id');
|
|
743
|
+
if (pptxShapeId && 1 === elements.length) context.elementIdByPptxShapeId.set(pptxShapeId, elements[0].id);
|
|
744
|
+
return elements;
|
|
733
745
|
}
|
|
734
746
|
function parseShape(node, context, transform) {
|
|
735
747
|
const box = elementBox(node, context, transform);
|
package/dist/4121.js
CHANGED
|
@@ -2918,6 +2918,13 @@ const WORK_TEMPLATES = [
|
|
|
2918
2918
|
name: '策略汇报',
|
|
2919
2919
|
description: '结论先行的三页汇报',
|
|
2920
2920
|
accent: '#c85637'
|
|
2921
|
+
},
|
|
2922
|
+
{
|
|
2923
|
+
id: 'animated-deck',
|
|
2924
|
+
kind: 'presentation',
|
|
2925
|
+
name: '入场动画',
|
|
2926
|
+
description: '出现、淡入、飞入、缩放与三种触发方式',
|
|
2927
|
+
accent: '#7657c8'
|
|
2921
2928
|
}
|
|
2922
2929
|
];
|
|
2923
2930
|
function createWorkArtifact(templateId) {
|
|
@@ -2951,7 +2958,8 @@ function initialTitle(templateId, kind) {
|
|
|
2951
2958
|
'proofing-languages': '校对语言示例',
|
|
2952
2959
|
'quarterly-plan': '季度执行计划',
|
|
2953
2960
|
'data-validation': '数据验证示例',
|
|
2954
|
-
'strategy-deck': '业务策略汇报'
|
|
2961
|
+
'strategy-deck': '业务策略汇报',
|
|
2962
|
+
'animated-deck': '入场动画示例'
|
|
2955
2963
|
};
|
|
2956
2964
|
if (titles[templateId]) return titles[templateId];
|
|
2957
2965
|
if ('document' === kind) return '无标题文字';
|
|
@@ -3250,6 +3258,7 @@ function contentForTemplate(templateId) {
|
|
|
3250
3258
|
sheets: dataValidationTemplateSheets()
|
|
3251
3259
|
};
|
|
3252
3260
|
if ('strategy-deck' === templateId) return strategyPresentation();
|
|
3261
|
+
if ('animated-deck' === templateId) return animatedPresentation();
|
|
3253
3262
|
if ('blank-spreadsheet' === templateId) return {
|
|
3254
3263
|
type: 'spreadsheet',
|
|
3255
3264
|
sheets: [
|
|
@@ -3836,6 +3845,118 @@ function strategyPresentation() {
|
|
|
3836
3845
|
slides
|
|
3837
3846
|
};
|
|
3838
3847
|
}
|
|
3848
|
+
function animatedPresentation() {
|
|
3849
|
+
const titleId = createWorkId('element');
|
|
3850
|
+
const subtitleId = createWorkId('element');
|
|
3851
|
+
const sequenceId = createWorkId('element');
|
|
3852
|
+
const conclusionId = createWorkId('element');
|
|
3853
|
+
return {
|
|
3854
|
+
type: 'presentation',
|
|
3855
|
+
slides: [
|
|
3856
|
+
{
|
|
3857
|
+
id: createWorkId('slide'),
|
|
3858
|
+
name: '入场动画',
|
|
3859
|
+
background: '#17162b',
|
|
3860
|
+
notes: '从“动画”选项卡编辑效果、触发方式、持续时间、延迟和顺序,再从头放映验证逐步播放。',
|
|
3861
|
+
elements: [
|
|
3862
|
+
{
|
|
3863
|
+
id: titleId,
|
|
3864
|
+
type: 'text',
|
|
3865
|
+
x: 8,
|
|
3866
|
+
y: 10,
|
|
3867
|
+
width: 76,
|
|
3868
|
+
height: 16,
|
|
3869
|
+
text: '让信息按叙事顺序出现',
|
|
3870
|
+
fontSize: 34,
|
|
3871
|
+
color: '#ffffff',
|
|
3872
|
+
fill: 'transparent',
|
|
3873
|
+
bold: true,
|
|
3874
|
+
align: 'left'
|
|
3875
|
+
},
|
|
3876
|
+
{
|
|
3877
|
+
id: subtitleId,
|
|
3878
|
+
type: 'text',
|
|
3879
|
+
x: 8,
|
|
3880
|
+
y: 28,
|
|
3881
|
+
width: 68,
|
|
3882
|
+
height: 9,
|
|
3883
|
+
text: '单击开始,下一对象与标题同时淡入',
|
|
3884
|
+
fontSize: 16,
|
|
3885
|
+
color: '#b9b7dc',
|
|
3886
|
+
fill: 'transparent',
|
|
3887
|
+
bold: false,
|
|
3888
|
+
align: 'left'
|
|
3889
|
+
},
|
|
3890
|
+
{
|
|
3891
|
+
id: sequenceId,
|
|
3892
|
+
type: 'shape',
|
|
3893
|
+
x: 8,
|
|
3894
|
+
y: 46,
|
|
3895
|
+
width: 52,
|
|
3896
|
+
height: 20,
|
|
3897
|
+
text: '上一动画完成后,从左侧飞入',
|
|
3898
|
+
fontSize: 19,
|
|
3899
|
+
color: '#211f35',
|
|
3900
|
+
fill: '#d9d3ff',
|
|
3901
|
+
bold: true,
|
|
3902
|
+
align: 'center',
|
|
3903
|
+
radius: 4
|
|
3904
|
+
},
|
|
3905
|
+
{
|
|
3906
|
+
id: conclusionId,
|
|
3907
|
+
type: 'shape',
|
|
3908
|
+
x: 66,
|
|
3909
|
+
y: 46,
|
|
3910
|
+
width: 26,
|
|
3911
|
+
height: 20,
|
|
3912
|
+
text: '再次单击\n缩放结论',
|
|
3913
|
+
fontSize: 17,
|
|
3914
|
+
color: '#ffffff',
|
|
3915
|
+
fill: '#7657c8',
|
|
3916
|
+
bold: true,
|
|
3917
|
+
align: 'center',
|
|
3918
|
+
radius: 5
|
|
3919
|
+
}
|
|
3920
|
+
],
|
|
3921
|
+
animations: [
|
|
3922
|
+
{
|
|
3923
|
+
id: createWorkId('slide-animation'),
|
|
3924
|
+
elementId: titleId,
|
|
3925
|
+
effect: 'appear',
|
|
3926
|
+
trigger: 'on-click',
|
|
3927
|
+
durationMs: 300,
|
|
3928
|
+
delayMs: 0
|
|
3929
|
+
},
|
|
3930
|
+
{
|
|
3931
|
+
id: createWorkId('slide-animation'),
|
|
3932
|
+
elementId: subtitleId,
|
|
3933
|
+
effect: 'fade',
|
|
3934
|
+
trigger: 'with-previous',
|
|
3935
|
+
durationMs: 600,
|
|
3936
|
+
delayMs: 100
|
|
3937
|
+
},
|
|
3938
|
+
{
|
|
3939
|
+
id: createWorkId('slide-animation'),
|
|
3940
|
+
elementId: sequenceId,
|
|
3941
|
+
effect: 'fly-in',
|
|
3942
|
+
trigger: 'after-previous',
|
|
3943
|
+
durationMs: 700,
|
|
3944
|
+
delayMs: 150,
|
|
3945
|
+
direction: 'left'
|
|
3946
|
+
},
|
|
3947
|
+
{
|
|
3948
|
+
id: createWorkId('slide-animation'),
|
|
3949
|
+
elementId: conclusionId,
|
|
3950
|
+
effect: 'zoom',
|
|
3951
|
+
trigger: 'on-click',
|
|
3952
|
+
durationMs: 650,
|
|
3953
|
+
delayMs: 0
|
|
3954
|
+
}
|
|
3955
|
+
]
|
|
3956
|
+
}
|
|
3957
|
+
]
|
|
3958
|
+
};
|
|
3959
|
+
}
|
|
3839
3960
|
const OFFICE_KERNEL_SPREADSHEET_MAX_ROWS = 1048576;
|
|
3840
3961
|
const spreadsheetErrors = new Set([
|
|
3841
3962
|
'#BLOCKED!',
|
package/dist/4560.js
CHANGED
|
@@ -44,8 +44,37 @@ function validateSlide(value) {
|
|
|
44
44
|
slide.elements = validateRecords(record.elements, `element in slide '${slide.id}'`, validateElement);
|
|
45
45
|
if (void 0 !== record.layoutId) slide.layoutId = requiredIdentifier(record.layoutId, 'slide layout');
|
|
46
46
|
if (void 0 !== record.comments) slide.comments = validateRecords(record.comments, `comment in slide '${slide.id}'`, validateComment);
|
|
47
|
+
if (void 0 !== record.animations) slide.animations = validateSlideAnimations(record.animations, slide);
|
|
47
48
|
return slide;
|
|
48
49
|
}
|
|
50
|
+
function validateSlideAnimations(value, slide) {
|
|
51
|
+
if (!Array.isArray(value) || value.length > 256) invalidWorkOfficePresentationInput("an array of at most 256 slide animations");
|
|
52
|
+
const animationIds = new Set();
|
|
53
|
+
const targetIds = new Set();
|
|
54
|
+
const elementIds = new Set(slide.elements.map((element)=>element.id));
|
|
55
|
+
return value.map((item)=>{
|
|
56
|
+
const record = requiredInputRecord(item, 'slide animation');
|
|
57
|
+
const animation = validateJsonRecord(record, 'slide animation');
|
|
58
|
+
animation.id = requiredIdentifier(record.id, 'slide animation');
|
|
59
|
+
animation.elementId = requiredIdentifier(record.elementId, 'slide animation element');
|
|
60
|
+
if (animationIds.has(animation.id)) invalidWorkOfficePresentationInput(`a unique slide animation ID; '${animation.id}' is repeated`);
|
|
61
|
+
if (!elementIds.has(animation.elementId)) invalidWorkOfficePresentationInput(`slide animation '${animation.id}' to reference an existing element`);
|
|
62
|
+
if (targetIds.has(animation.elementId)) invalidWorkOfficePresentationInput(`at most one entrance animation for element '${animation.elementId}'`);
|
|
63
|
+
if ('appear' !== record.effect && 'fade' !== record.effect && 'fly-in' !== record.effect && 'zoom' !== record.effect) invalidWorkOfficePresentationInput('a supported slide entrance animation effect');
|
|
64
|
+
animation.effect = record.effect;
|
|
65
|
+
if ('on-click' !== record.trigger && 'with-previous' !== record.trigger && 'after-previous' !== record.trigger) invalidWorkOfficePresentationInput('a supported slide animation trigger');
|
|
66
|
+
animation.trigger = record.trigger;
|
|
67
|
+
animation.durationMs = requiredIntegerInRange(record.durationMs, 100, 60000, 'slide animation duration');
|
|
68
|
+
animation.delayMs = requiredIntegerInRange(record.delayMs, 0, 60000, 'slide animation delay');
|
|
69
|
+
if ('fly-in' === animation.effect) {
|
|
70
|
+
if ('left' !== record.direction && 'right' !== record.direction && 'up' !== record.direction && 'down' !== record.direction) invalidWorkOfficePresentationInput('a direction for each fly-in animation');
|
|
71
|
+
animation.direction = record.direction;
|
|
72
|
+
} else if (void 0 !== record.direction) invalidWorkOfficePresentationInput('slide animation directions only on fly-in effects');
|
|
73
|
+
animationIds.add(animation.id);
|
|
74
|
+
targetIds.add(animation.elementId);
|
|
75
|
+
return animation;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
49
78
|
function validateMaster(value) {
|
|
50
79
|
const record = requiredInputRecord(value, 'presentation master');
|
|
51
80
|
const master = validateJsonRecord(record, 'presentation master');
|
|
@@ -129,6 +158,10 @@ function requiredFiniteNumber(value, label) {
|
|
|
129
158
|
if ('number' != typeof value || !Number.isFinite(value)) invalidWorkOfficePresentationInput(`a finite number for ${label}`);
|
|
130
159
|
return value;
|
|
131
160
|
}
|
|
161
|
+
function requiredIntegerInRange(value, minimum, maximum, label) {
|
|
162
|
+
if ('number' != typeof value || !Number.isSafeInteger(value) || value < minimum || value > maximum) invalidWorkOfficePresentationInput(`an integer ${label} from ${minimum} through ${maximum}`);
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
132
165
|
function invalidWorkOfficePresentationInput(expected) {
|
|
133
166
|
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `Presentation collaboration requires ${expected}.`);
|
|
134
167
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WorkSlideAnimation, WorkSlideAnimationEffect } from '../work-types';
|
|
2
|
+
export declare function PresentationAnimationPanel({ animation, canMove, canPreview, editable, onMove, onPreview, onSetEffect, onUpdate, }: {
|
|
3
|
+
animation: WorkSlideAnimation | undefined;
|
|
4
|
+
canMove: (direction: -1 | 1) => boolean;
|
|
5
|
+
canPreview: boolean;
|
|
6
|
+
editable: boolean;
|
|
7
|
+
onMove: (direction: -1 | 1) => void;
|
|
8
|
+
onPreview: () => void;
|
|
9
|
+
onSetEffect: (effect: WorkSlideAnimationEffect | undefined) => void;
|
|
10
|
+
onUpdate: (patch: Partial<WorkSlideAnimation>) => void;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OfficeKernelPresentationAlignment } from '../../../kernel/office-kernel-protocol';
|
|
2
|
-
import type { WorkPresentationContent, WorkSlideElement, WorkSlideTransition, WorkSlideTextRun } from '../work-types';
|
|
2
|
+
import type { WorkPresentationContent, WorkSlideAnimation, WorkSlideAnimationEffect, WorkSlideElement, WorkSlideTransition, WorkSlideTextRun } from '../work-types';
|
|
3
3
|
import type { OfficeEditorCanCommands } from './office-editor-extension';
|
|
4
4
|
import type { PresentationDesignMode } from './presentation-editor-types';
|
|
5
5
|
import type { PresentationDistribution } from './presentation-selection';
|
|
@@ -37,9 +37,11 @@ export interface PresentationEditorCommands {
|
|
|
37
37
|
groupElements: () => boolean;
|
|
38
38
|
instantiatePlaceholder: (definition: WorkSlideElement) => PresentationCommandResult;
|
|
39
39
|
locatePresentationComment: (slideId: string, commentId: string) => PresentationCommandResult;
|
|
40
|
+
moveEntranceAnimation: (direction: -1 | 1) => PresentationCommandResult;
|
|
40
41
|
nudgeSelection: (key: string, distance: number) => boolean;
|
|
41
42
|
openComment: (commentId: string) => PresentationCommandResult;
|
|
42
43
|
pasteSelection: () => boolean;
|
|
44
|
+
previewAnimations: () => PresentationCommandResult;
|
|
43
45
|
redo: () => boolean;
|
|
44
46
|
renamePresentationLayout: (name: string) => PresentationCommandResult;
|
|
45
47
|
renamePresentationMaster: (name: string) => PresentationCommandResult;
|
|
@@ -50,6 +52,7 @@ export interface PresentationEditorCommands {
|
|
|
50
52
|
selectSlide: (slideId: string, returnToSlideMode?: boolean) => PresentationCommandResult;
|
|
51
53
|
setPresentationContent: (content: WorkPresentationContent) => PresentationCommandResult;
|
|
52
54
|
setBackground: (color: string) => PresentationCommandResult;
|
|
55
|
+
setEntranceAnimation: (effect: WorkSlideAnimationEffect | undefined) => PresentationCommandResult;
|
|
53
56
|
setPresentationLayoutBackground: (color: string | undefined) => PresentationCommandResult;
|
|
54
57
|
setPresentationMasterBackground: (color: string) => PresentationCommandResult;
|
|
55
58
|
setTransition: (transition: WorkSlideTransition | undefined) => PresentationCommandResult;
|
|
@@ -66,6 +69,7 @@ export interface PresentationEditorCommands {
|
|
|
66
69
|
updateElement: (patch: Partial<WorkSlideElement>, options?: {
|
|
67
70
|
restoreTextFocus?: boolean;
|
|
68
71
|
}) => PresentationCommandResult;
|
|
72
|
+
updateEntranceAnimation: (patch: Partial<WorkSlideAnimation>) => PresentationCommandResult;
|
|
69
73
|
updateNotes: (notes: string) => PresentationCommandResult;
|
|
70
74
|
updatePresentationComment: (slideId: string, commentId: string, text: string) => PresentationCommandResult;
|
|
71
75
|
updateTextElement: (elementId: string, value: {
|
|
@@ -107,6 +111,16 @@ export interface PresentationSlideCommandPort {
|
|
|
107
111
|
setTransition: (transition: WorkSlideTransition | undefined) => PresentationCommandResult;
|
|
108
112
|
updateNotes: (notes: string) => PresentationCommandResult;
|
|
109
113
|
}
|
|
114
|
+
export interface PresentationAnimationCommandPort {
|
|
115
|
+
canMoveEntranceAnimation: (direction: -1 | 1) => boolean;
|
|
116
|
+
canPreviewAnimations: boolean;
|
|
117
|
+
canSetEntranceAnimation: boolean;
|
|
118
|
+
canUpdateEntranceAnimation: boolean;
|
|
119
|
+
moveEntranceAnimation: (direction: -1 | 1) => PresentationCommandResult;
|
|
120
|
+
previewAnimations: () => PresentationCommandResult;
|
|
121
|
+
setEntranceAnimation: (effect: WorkSlideAnimationEffect | undefined) => PresentationCommandResult;
|
|
122
|
+
updateEntranceAnimation: (patch: Partial<WorkSlideAnimation>) => PresentationCommandResult;
|
|
123
|
+
}
|
|
110
124
|
export interface PresentationInsertCommandPort {
|
|
111
125
|
enabled: boolean;
|
|
112
126
|
addChart: () => PresentationCommandResult;
|
|
@@ -192,6 +206,7 @@ export interface PresentationKeyboardCommandPort {
|
|
|
192
206
|
selectedElementCount: number;
|
|
193
207
|
}
|
|
194
208
|
export interface PresentationCommandContext {
|
|
209
|
+
animations: PresentationAnimationCommandPort;
|
|
195
210
|
clipboard: PresentationClipboardCommandPort;
|
|
196
211
|
design: PresentationDesignCommandPort;
|
|
197
212
|
document: PresentationDocumentCommandPort;
|
|
@@ -4,7 +4,8 @@ export interface PresentationTimerController {
|
|
|
4
4
|
runningSinceMilliseconds: number | null;
|
|
5
5
|
}
|
|
6
6
|
export declare function createPresentationTimerController(now?: number): PresentationTimerController;
|
|
7
|
-
export declare function PresentationPresenterView({ content, slide, nextSlide, index, total, aspectRatio, timer, }: {
|
|
7
|
+
export declare function PresentationPresenterView({ animationCueIndex, content, slide, nextSlide, index, total, aspectRatio, timer, }: {
|
|
8
|
+
animationCueIndex?: number;
|
|
8
9
|
content: WorkPresentationContent;
|
|
9
10
|
slide: WorkSlide;
|
|
10
11
|
nextSlide?: WorkSlide;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { type WorkPresentationDesignContent } from '../work-presentation-layouts';
|
|
2
|
-
import type { WorkPresentationContent, WorkSlide, WorkSlideElement } from '../work-types';
|
|
3
|
-
export declare function SlideCanvas({ content, designContent, slide, interactive, aspectRatio, showPlaceholders, }: {
|
|
2
|
+
import type { WorkPresentationContent, WorkSlide, WorkSlideAnimation, WorkSlideElement } from '../work-types';
|
|
3
|
+
export declare function SlideCanvas({ content, designContent, slide, interactive, aspectRatio, animationCueIndex, showPlaceholders, }: {
|
|
4
4
|
content?: WorkPresentationContent;
|
|
5
5
|
designContent?: WorkPresentationDesignContent;
|
|
6
6
|
slide: WorkSlide;
|
|
7
7
|
interactive: boolean;
|
|
8
8
|
aspectRatio: string;
|
|
9
|
+
animationCueIndex?: number;
|
|
9
10
|
showPlaceholders?: boolean;
|
|
10
11
|
}): import("react").JSX.Element;
|
|
11
|
-
export declare function SlideElementPreview({ element, origin, showPlaceholder, }: {
|
|
12
|
+
export declare function SlideElementPreview({ animationPlayback, element, origin, showPlaceholder, }: {
|
|
13
|
+
animationPlayback?: SlideElementAnimationPlayback;
|
|
12
14
|
element: WorkSlideElement;
|
|
13
15
|
origin: 'inherited' | 'slide';
|
|
14
16
|
showPlaceholder?: boolean;
|
|
@@ -18,6 +20,11 @@ export declare function EditableSlideTable({ element, onChange, }: {
|
|
|
18
20
|
onChange: (rows: string[][]) => void;
|
|
19
21
|
}): import("react").JSX.Element | null;
|
|
20
22
|
export declare function slideElementStyle(element: WorkSlideElement): React.CSSProperties;
|
|
23
|
+
interface SlideElementAnimationPlayback {
|
|
24
|
+
animation: WorkSlideAnimation;
|
|
25
|
+
startOffsetMs: number;
|
|
26
|
+
state: 'finished' | 'pending' | 'playing';
|
|
27
|
+
}
|
|
21
28
|
export declare function slideTextStyle(element: WorkSlideElement): React.CSSProperties;
|
|
22
29
|
export declare function SlideElementTextPreview({ element, showPlaceholder, }: {
|
|
23
30
|
element: WorkSlideElement;
|
|
@@ -26,3 +33,4 @@ export declare function SlideElementTextPreview({ element, showPlaceholder, }: {
|
|
|
26
33
|
export declare function SlideTablePreview({ element }: {
|
|
27
34
|
element: WorkSlideElement;
|
|
28
35
|
}): import("react").JSX.Element | null;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WorkPresentationContent, WorkSlide, WorkSlideAnimation, WorkSlideAnimationEffect } from '../work-types';
|
|
2
|
+
export interface PresentationAnimationCommands {
|
|
3
|
+
canMoveEntranceAnimation: (direction: -1 | 1) => boolean;
|
|
4
|
+
canSetEntranceAnimation: boolean;
|
|
5
|
+
canUpdateEntranceAnimation: boolean;
|
|
6
|
+
moveEntranceAnimation: (direction: -1 | 1) => boolean;
|
|
7
|
+
setEntranceAnimation: (effect: WorkSlideAnimationEffect | undefined) => boolean;
|
|
8
|
+
updateEntranceAnimation: (patch: Partial<WorkSlideAnimation>) => boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function usePresentationAnimationCommands({ content, onChange, selectedElementId, selectedSlide, }: {
|
|
11
|
+
content: WorkPresentationContent;
|
|
12
|
+
onChange: (content: WorkPresentationContent) => void;
|
|
13
|
+
selectedElementId: string | undefined;
|
|
14
|
+
selectedSlide: WorkSlide;
|
|
15
|
+
}): PresentationAnimationCommands;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PptxGroupExportRegistry } from './work-pptx-groups';
|
|
2
|
+
import type { WorkSlide, WorkSlideAnimation } from './work-types';
|
|
3
|
+
interface PptxAnimationDiagnostic {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
interface PptxAnimationReadResult {
|
|
8
|
+
animations: WorkSlideAnimation[];
|
|
9
|
+
diagnostics: PptxAnimationDiagnostic[];
|
|
10
|
+
}
|
|
11
|
+
export declare function readPptxAnimations(document: Document, elementIdByPptxShapeId: ReadonlyMap<string, string>): PptxAnimationReadResult;
|
|
12
|
+
export declare function patchPptxAnimations(buffer: ArrayBuffer, slides: readonly WorkSlide[], registry: PptxGroupExportRegistry): Promise<ArrayBuffer>;
|
|
13
|
+
export {};
|
|
@@ -7,11 +7,15 @@ interface PptxExportBinding {
|
|
|
7
7
|
marker: string;
|
|
8
8
|
}
|
|
9
9
|
export declare class PptxGroupExportRegistry {
|
|
10
|
+
private readonly animatedElementKeys;
|
|
10
11
|
private readonly bindings;
|
|
12
|
+
private readonly bindingsByElement;
|
|
11
13
|
private readonly roleCounts;
|
|
12
14
|
private markerSequence;
|
|
13
15
|
get size(): number;
|
|
16
|
+
registerAnimatedElements(scope: string, elementIds: Iterable<string>): void;
|
|
14
17
|
objectName(scope: string, element: WorkSlideElement, role: PptxExportObjectRole): string | undefined;
|
|
18
|
+
markerForElement(scope: string, elementId: string): string | undefined;
|
|
15
19
|
binding(marker: string): PptxExportBinding | undefined;
|
|
16
20
|
values(): IterableIterator<PptxExportBinding>;
|
|
17
21
|
}
|