@a3s-lab/office 0.38.0 → 0.39.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 +21 -7
- package/dist/0~7828.js +51 -29
- package/dist/0~presentation-editor.js +264 -118
- package/dist/0~work-pptx-import.js +1 -1
- package/dist/0~work-presentation-transition.js +38 -0
- package/dist/4121.js +42 -9
- package/dist/4560.js +124 -9
- package/dist/internal/features/work/editors/presentation-animation-panel.d.ts +9 -7
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +10 -10
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +6 -4
- package/dist/internal/features/work/editors/use-presentation-animation-commands.d.ts +7 -7
- package/dist/internal/features/work/work-presentation-animation.d.ts +6 -2
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +0 -5
- package/dist/internal/features/work/work-types.d.ts +7 -3
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +149 -26
- package/package.json +2 -1
- package/dist/0~5809.js +0 -123
package/README.md
CHANGED
|
@@ -116,8 +116,8 @@ package and one set of bounded contracts.
|
|
|
116
116
|
formulas, tables, formatting, sort/filter, validation, pivots, charts, and
|
|
117
117
|
print workflows.
|
|
118
118
|
- **Presentation** (`PPTX`) — slides, typed scene objects, masters/layouts,
|
|
119
|
-
transitions, bounded entrance animations, notes, slideshow, and
|
|
120
|
-
view.
|
|
119
|
+
transitions, bounded entrance/exit animations, notes, slideshow, and
|
|
120
|
+
presenter view.
|
|
121
121
|
- **Markdown** (`MD`) — GFM source, visual mode, split preview, direct round
|
|
122
122
|
trips, and native automation.
|
|
123
123
|
- **PDF** — PDFium rendering, search, forms, annotations, history, save, and
|
|
@@ -309,8 +309,20 @@ and [CLI reference](./docs/latest/en/cli-reference.md).
|
|
|
309
309
|
|
|
310
310
|
## Current release
|
|
311
311
|
|
|
312
|
-
Version `0.
|
|
313
|
-
|
|
312
|
+
Version `0.39.0` extends the local, testable Presentation workflow with a
|
|
313
|
+
complete bounded object-animation story:
|
|
314
|
+
|
|
315
|
+
- A slide object can now own one entrance and one exit animation. The shared
|
|
316
|
+
object-centric Animation tab authors appear/disappear, fade in/out, fly in/out,
|
|
317
|
+
and zoom in/out effects with one ordered trigger, timing, direction, preview,
|
|
318
|
+
collaboration, clipboard, and Undo model. Browser playback and native PPTX
|
|
319
|
+
timing-tree import/export retain both classes, while malformed or overlapping
|
|
320
|
+
sequences fail closed with diagnostics.
|
|
321
|
+
|
|
322
|
+
- The documentation now includes a first-class bilingual, version-aware
|
|
323
|
+
[What's new](https://a3s-lab.github.io/Office/docs/changelog.html) timeline.
|
|
324
|
+
It presents editor scope, user outcome, compatibility evidence, and detailed
|
|
325
|
+
references while frozen documentation hides releases that did not yet exist.
|
|
314
326
|
|
|
315
327
|
- Writer's shared advanced Font dialog now edits the complete bounded Office
|
|
316
328
|
2010 OpenType typography set: 16 ligature combinations, numeral form and
|
|
@@ -340,8 +352,9 @@ workflows:
|
|
|
340
352
|
- PDF page organization performs insert, delete, rotate, reorder, extract,
|
|
341
353
|
merge, and split in a dedicated Worker with Blob-level Undo/Redo.
|
|
342
354
|
|
|
343
|
-
|
|
344
|
-
|
|
355
|
+
Browse the product-focused [What's new](https://a3s-lab.github.io/Office/docs/changelog.html)
|
|
356
|
+
page or the exhaustive engineering [changelog](./CHANGELOG.md). The Playground
|
|
357
|
+
exposes recent workflows from one release-labelled capability gallery.
|
|
345
358
|
|
|
346
359
|
## Capabilities and boundaries
|
|
347
360
|
|
|
@@ -444,13 +457,14 @@ matrix.
|
|
|
444
457
|
|
|
445
458
|
- [Live Playground](https://a3s-lab.github.io/Office/playground/)
|
|
446
459
|
- [Documentation center](https://a3s-lab.github.io/Office/docs/)
|
|
460
|
+
- [What's new](https://a3s-lab.github.io/Office/docs/changelog.html)
|
|
447
461
|
- [React, Vue, Web Component, and Core API](https://a3s-lab.github.io/Office/docs/components/)
|
|
448
462
|
- [Real-time collaboration](https://a3s-lab.github.io/Office/docs/components/collaboration.html)
|
|
449
463
|
- [CLI, MCP, and Office Skill](https://a3s-lab.github.io/Office/docs/automation/)
|
|
450
464
|
- [Browser editor architecture](./docs/latest/en/browser-editor-architecture.md)
|
|
451
465
|
- [Native Office engine](./docs/latest/en/native-office-engine.md)
|
|
452
466
|
- [Editor quality roadmap](./docs/latest/en/editor-quality-roadmap.md)
|
|
453
|
-
- [
|
|
467
|
+
- [Engineering changelog](./CHANGELOG.md)
|
|
454
468
|
|
|
455
469
|
## Community and security
|
|
456
470
|
|
package/dist/0~7828.js
CHANGED
|
@@ -3,7 +3,8 @@ import { directChildren, xmlNamespacePrefix, parseXml, firstDescendant, descenda
|
|
|
3
3
|
import { normalizePresentationChartDataLabelPosition, normalizePresentationChartDataLabels, normalizePresentationChartSeriesStyle } from "./0~work-presentation-charts.js";
|
|
4
4
|
import { parseXlsxChartSeriesStyle, xlsxChartSeriesMarkerXml, parseXlsxTrendline, isSupportedXlsxChartLegend, isSupportedXlsxChartPlotLayout, isSupportedXlsxChartSeriesFormatting, parseXlsxChartPlotLayout, parseXlsxChartLegend, xlsxChartSeriesShapePropertiesXml, parseXlsxErrorBars } from "./4166.js";
|
|
5
5
|
import { workSpreadsheetChartSupportsErrorBars, workSpreadsheetChartSupportsGrouping, normalizeWorkSpreadsheetChartSmoothLines, workSpreadsheetChartSupportsSeriesAnalysis, normalizeWorkSpreadsheetChartSeriesStyle, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetChartOverlap, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartLegendOverlay, normalizeWorkSpreadsheetChartGapWidth, normalizeWorkSpreadsheetChartGrouping, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartSupportsSmoothLines, normalizeWorkSpreadsheetTrendline } from "./8715.js";
|
|
6
|
-
import { normalizeWorkSlideAnimation,
|
|
6
|
+
import { normalizeWorkSlideAnimation, workSlideAnimationClass, workSlideAnimationSequenceIssue } from "./4560.js";
|
|
7
|
+
import { createWorkSlideTransition } from "./0~work-presentation-transition.js";
|
|
7
8
|
const PRESENTATION_NAMESPACE = 'http://schemas.openxmlformats.org/presentationml/2006/main';
|
|
8
9
|
const DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
|
|
9
10
|
const OFFICE_RELATIONSHIPS_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
@@ -922,7 +923,7 @@ function readPptxAnimations(document, elementIdByPptxShapeId) {
|
|
|
922
923
|
]
|
|
923
924
|
};
|
|
924
925
|
const animations = [];
|
|
925
|
-
const
|
|
926
|
+
const animatedClasses = new Set();
|
|
926
927
|
const effectNodes = pptxDescendants(timing, 'cTn').filter((node)=>[
|
|
927
928
|
'clickEffect',
|
|
928
929
|
'withEffect',
|
|
@@ -933,18 +934,20 @@ function readPptxAnimations(document, elementIdByPptxShapeId) {
|
|
|
933
934
|
message: "Only the first 256 object animation timing records on a slide are inspected and imported."
|
|
934
935
|
});
|
|
935
936
|
for (const node of effectNodes.slice(0, 256)){
|
|
936
|
-
|
|
937
|
+
const presetClass = attribute(node, 'presetClass');
|
|
938
|
+
if ('entr' !== presetClass && 'exit' !== presetClass) {
|
|
937
939
|
diagnostics.push({
|
|
938
940
|
code: 'pptx.animation.class',
|
|
939
|
-
message: 'An emphasis,
|
|
941
|
+
message: 'An emphasis, motion-path, or malformed animation remains in the source PPTX only.'
|
|
940
942
|
});
|
|
941
943
|
continue;
|
|
942
944
|
}
|
|
945
|
+
const animationClass = 'exit' === presetClass ? 'exit' : 'entrance';
|
|
943
946
|
const targets = Array.from(new Set(pptxDescendants(node, 'spTgt').map((target)=>attribute(target, 'spid')?.trim()).filter((target)=>Boolean(target))));
|
|
944
947
|
if (1 !== targets.length) {
|
|
945
948
|
diagnostics.push({
|
|
946
949
|
code: 'pptx.animation.target',
|
|
947
|
-
message: 'An
|
|
950
|
+
message: 'An object animation with a missing or ambiguous target was ignored.'
|
|
948
951
|
});
|
|
949
952
|
continue;
|
|
950
953
|
}
|
|
@@ -952,22 +955,23 @@ function readPptxAnimations(document, elementIdByPptxShapeId) {
|
|
|
952
955
|
if (!elementId) {
|
|
953
956
|
diagnostics.push({
|
|
954
957
|
code: 'pptx.animation.target',
|
|
955
|
-
message: 'An
|
|
958
|
+
message: 'An object animation points to an unavailable slide object and was ignored.'
|
|
956
959
|
});
|
|
957
960
|
continue;
|
|
958
961
|
}
|
|
959
|
-
|
|
962
|
+
const animatedClass = `${elementId}\u0000${animationClass}`;
|
|
963
|
+
if (animatedClasses.has(animatedClass)) {
|
|
960
964
|
diagnostics.push({
|
|
961
965
|
code: 'pptx.animation.duplicate-target',
|
|
962
|
-
message:
|
|
966
|
+
message: `Only the first supported ${animationClass} animation for an object is editable; additional effects remain in the source PPTX only.`
|
|
963
967
|
});
|
|
964
968
|
continue;
|
|
965
969
|
}
|
|
966
|
-
const effectResult = readPptxAnimationEffect(node);
|
|
970
|
+
const effectResult = readPptxAnimationEffect(node, animationClass);
|
|
967
971
|
if (!effectResult.effect) {
|
|
968
972
|
diagnostics.push({
|
|
969
973
|
code: 'pptx.animation.effect',
|
|
970
|
-
message: 'An entrance animation uses an unsupported or malformed effect and remains in the source PPTX only.'
|
|
974
|
+
message: 'An entrance or exit animation uses an unsupported or malformed effect and remains in the source PPTX only.'
|
|
971
975
|
});
|
|
972
976
|
continue;
|
|
973
977
|
}
|
|
@@ -985,12 +989,22 @@ function readPptxAnimations(document, elementIdByPptxShapeId) {
|
|
|
985
989
|
delayMs: timingResult.delayMs,
|
|
986
990
|
direction: effectResult.direction
|
|
987
991
|
});
|
|
992
|
+
if ('overlapping-target' === workSlideAnimationSequenceIssue([
|
|
993
|
+
...animations,
|
|
994
|
+
animation
|
|
995
|
+
])) {
|
|
996
|
+
diagnostics.push({
|
|
997
|
+
code: 'pptx.animation.overlap',
|
|
998
|
+
message: 'Overlapping animations for one object in one playback cue remain in the source PPTX only.'
|
|
999
|
+
});
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
988
1002
|
animations.push(animation);
|
|
989
|
-
|
|
1003
|
+
animatedClasses.add(animatedClass);
|
|
990
1004
|
}
|
|
991
1005
|
if (!effectNodes.length) diagnostics.push({
|
|
992
1006
|
code: 'pptx.animation.structure',
|
|
993
|
-
message: 'The slide timing tree does not contain a supported object
|
|
1007
|
+
message: 'The slide timing tree does not contain a supported object animation sequence and remains in the source PPTX only.'
|
|
994
1008
|
});
|
|
995
1009
|
return {
|
|
996
1010
|
animations,
|
|
@@ -1041,30 +1055,35 @@ function clonePptxAnimationElement(document, source) {
|
|
|
1041
1055
|
for (const child of Array.from(source.childNodes))element.append(child instanceof Element ? clonePptxAnimationElement(document, child) : document.createTextNode(child.textContent ?? ''));
|
|
1042
1056
|
return element;
|
|
1043
1057
|
}
|
|
1044
|
-
function readPptxAnimationEffect(node) {
|
|
1058
|
+
function readPptxAnimationEffect(node, animationClass) {
|
|
1045
1059
|
const presetId = attribute(node, 'presetID');
|
|
1046
1060
|
const animationEffects = pptxDescendants(node, 'animEffect');
|
|
1061
|
+
const expectedTransition = 'exit' === animationClass ? 'out' : 'in';
|
|
1062
|
+
if (animationEffects.some((effect)=>{
|
|
1063
|
+
const transition = attribute(effect, 'transition');
|
|
1064
|
+
return null !== transition && transition !== expectedTransition;
|
|
1065
|
+
})) return {};
|
|
1047
1066
|
const filters = animationEffects.map((effect)=>attribute(effect, 'filter')?.trim()).filter((filter)=>Boolean(filter));
|
|
1048
1067
|
const attributes = new Set(pptxDescendants(node, 'attrName').map((name)=>name.textContent?.trim()));
|
|
1049
1068
|
if (filters.some((filter)=>'fade' === filter)) return {
|
|
1050
|
-
effect: 'fade'
|
|
1069
|
+
effect: 'exit' === animationClass ? 'fade-out' : 'fade'
|
|
1051
1070
|
};
|
|
1052
1071
|
for (const filter of filters){
|
|
1053
1072
|
const direction = directionFromPptxSlideFilter(filter);
|
|
1054
1073
|
if (direction) return {
|
|
1055
|
-
effect: 'fly-in',
|
|
1074
|
+
effect: 'exit' === animationClass ? 'fly-out' : 'fly-in',
|
|
1056
1075
|
direction
|
|
1057
1076
|
};
|
|
1058
1077
|
}
|
|
1059
1078
|
if ('23' === presetId || pptxDescendants(node, 'animScale').length > 0 || attributes.has('ppt_w') && attributes.has('ppt_h')) return {
|
|
1060
|
-
effect: 'zoom'
|
|
1079
|
+
effect: 'exit' === animationClass ? 'zoom-out' : 'zoom'
|
|
1061
1080
|
};
|
|
1062
1081
|
if ('2' === presetId && (attributes.has('ppt_x') || attributes.has('ppt_y'))) return {
|
|
1063
|
-
effect: 'fly-in',
|
|
1082
|
+
effect: 'exit' === animationClass ? 'fly-out' : 'fly-in',
|
|
1064
1083
|
direction: directionFromPptxMotion(node)
|
|
1065
1084
|
};
|
|
1066
1085
|
if ('1' === presetId && pptxDescendants(node, 'set').length > 0 && 0 === animationEffects.length && 0 === pptxDescendants(node, 'anim').length && 0 === pptxDescendants(node, 'animScale').length) return {
|
|
1067
|
-
effect: 'appear'
|
|
1086
|
+
effect: 'exit' === animationClass ? 'disappear' : 'appear'
|
|
1068
1087
|
};
|
|
1069
1088
|
return {};
|
|
1070
1089
|
}
|
|
@@ -1131,7 +1150,7 @@ function pptxAnimationTimingXml(targets) {
|
|
|
1131
1150
|
const delay = group.automatic ? '0' : 'indefinite';
|
|
1132
1151
|
return `<p:par><p:cTn id="${outerId}" fill="hold"><p:stCondLst><p:cond delay="${delay}"/></p:stCondLst><p:childTnLst>${wrappers}</p:childTnLst></p:cTn></p:par>`;
|
|
1133
1152
|
}).join('');
|
|
1134
|
-
const builds = targets.map(({ spid })=>`<p:bldP spid="${spid}" grpId="0" animBg="1"/>`).join('');
|
|
1153
|
+
const builds = Array.from(new Set(targets.map(({ spid })=>spid))).map((spid)=>`<p:bldP spid="${spid}" grpId="0" animBg="1"/>`).join('');
|
|
1135
1154
|
return `<p:timing xmlns:p="${PRESENTATIONML_NAMESPACE}"><p:tnLst><p:par><p:cTn id="${rootId}" dur="indefinite" restart="never" nodeType="tmRoot"><p:childTnLst><p:seq concurrent="1" nextAc="seek"><p:cTn id="${mainSequenceId}" dur="indefinite" nodeType="mainSeq"><p:childTnLst>${groupXml}</p:childTnLst></p:cTn><p:prevCondLst><p:cond evt="onPrev" delay="0"><p:tgtEl><p:sldTgt/></p:tgtEl></p:cond></p:prevCondLst><p:nextCondLst><p:cond evt="onNext" delay="0"><p:tgtEl><p:sldTgt/></p:tgtEl></p:cond></p:nextCondLst></p:seq></p:childTnLst></p:cTn></p:par></p:tnLst><p:bldLst>${builds}</p:bldLst></p:timing>`;
|
|
1136
1155
|
}
|
|
1137
1156
|
function pptxAnimationGroups(animations) {
|
|
@@ -1156,36 +1175,39 @@ function pptxAnimationGroups(animations) {
|
|
|
1156
1175
|
}
|
|
1157
1176
|
function pptxAnimationEffectXml(target, nextId) {
|
|
1158
1177
|
const animation = normalizeWorkSlideAnimation(target.animation);
|
|
1178
|
+
const animationClass = workSlideAnimationClass(animation.effect);
|
|
1159
1179
|
const preset = pptxAnimationPreset(animation);
|
|
1160
1180
|
const leafId = nextId();
|
|
1161
1181
|
const visibilityId = nextId();
|
|
1162
|
-
const
|
|
1182
|
+
const visibilityDelay = 'exit' === animationClass ? Math.max(0, animation.durationMs - 1) : 0;
|
|
1183
|
+
const visibility = `<p:set><p:cBhvr><p:cTn id="${visibilityId}" dur="1" fill="hold"><p:stCondLst><p:cond delay="${visibilityDelay}"/></p:stCondLst></p:cTn><p:tgtEl><p:spTgt spid="${target.spid}"/></p:tgtEl><p:attrNameLst><p:attrName>style.visibility</p:attrName></p:attrNameLst></p:cBhvr><p:to><p:strVal val="${'exit' === animationClass ? 'hidden' : 'visible'}"/></p:to></p:set>`;
|
|
1163
1184
|
const behavior = pptxAnimationBehaviorXml(animation, target.spid, nextId);
|
|
1164
|
-
return `<p:par><p:cTn id="${leafId}" presetID="${preset.id}" presetClass="entr" presetSubtype="${preset.subtype}" fill="hold" nodeType="${pptxAnimationNodeType(animation.trigger)}" dur="${animation.durationMs}"><p:stCondLst><p:cond delay="${animation.delayMs}"/></p:stCondLst><p:childTnLst>${visibility}${behavior}</p:childTnLst></p:cTn></p:par>`;
|
|
1185
|
+
return `<p:par><p:cTn id="${leafId}" presetID="${preset.id}" presetClass="${'exit' === animationClass ? 'exit' : 'entr'}" presetSubtype="${preset.subtype}" fill="hold" nodeType="${pptxAnimationNodeType(animation.trigger)}" dur="${animation.durationMs}"><p:stCondLst><p:cond delay="${animation.delayMs}"/></p:stCondLst><p:childTnLst>${visibility}${behavior}</p:childTnLst></p:cTn></p:par>`;
|
|
1165
1186
|
}
|
|
1166
1187
|
function pptxAnimationBehaviorXml(animation, spid, nextId) {
|
|
1167
|
-
|
|
1168
|
-
if ('
|
|
1188
|
+
const animationClass = workSlideAnimationClass(animation.effect);
|
|
1189
|
+
if ('appear' === animation.effect || 'disappear' === animation.effect) return '';
|
|
1190
|
+
if ('zoom' === animation.effect || 'zoom-out' === animation.effect) return [
|
|
1169
1191
|
'ppt_w',
|
|
1170
1192
|
'ppt_h'
|
|
1171
1193
|
].map((attributeName)=>{
|
|
1172
1194
|
const id = nextId();
|
|
1173
|
-
return `<p:anim calcMode="lin" valueType="num"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}" fill="hold"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl><p:attrNameLst><p:attrName>${attributeName}</p:attrName></p:attrNameLst></p:cBhvr><p:tavLst><p:tav tm="0"><p:val><p:fltVal val="0"/></p:val></p:tav><p:tav tm="100000"><p:val><p:strVal val="#${attributeName}"/></p:val></p:tav></p:tavLst></p:anim
|
|
1195
|
+
return `<p:anim calcMode="lin" valueType="num"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}" fill="hold"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl><p:attrNameLst><p:attrName>${attributeName}</p:attrName></p:attrNameLst></p:cBhvr>` + ('exit' === animationClass ? `<p:tavLst><p:tav tm="0"><p:val><p:strVal val="#${attributeName}"/></p:val></p:tav><p:tav tm="100000"><p:val><p:fltVal val="0"/></p:val></p:tav></p:tavLst></p:anim>` : `<p:tavLst><p:tav tm="0"><p:val><p:fltVal val="0"/></p:val></p:tav><p:tav tm="100000"><p:val><p:strVal val="#${attributeName}"/></p:val></p:tav></p:tavLst></p:anim>`);
|
|
1174
1196
|
}).join('');
|
|
1175
1197
|
const id = nextId();
|
|
1176
|
-
const filter = 'fade' === animation.effect ? 'fade' : pptxSlideFilter(animation.direction ?? 'left');
|
|
1177
|
-
return `<p:animEffect transition="in" filter="${filter}"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl></p:cBhvr></p:animEffect>`;
|
|
1198
|
+
const filter = 'fade' === animation.effect || 'fade-out' === animation.effect ? 'fade' : pptxSlideFilter(animation.direction ?? 'left');
|
|
1199
|
+
return `<p:animEffect transition="${'exit' === animationClass ? 'out' : 'in'}" filter="${filter}"><p:cBhvr><p:cTn id="${id}" dur="${animation.durationMs}"/><p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl></p:cBhvr></p:animEffect>`;
|
|
1178
1200
|
}
|
|
1179
1201
|
function pptxAnimationPreset(animation) {
|
|
1180
|
-
if ('appear' === animation.effect) return {
|
|
1202
|
+
if ('appear' === animation.effect || 'disappear' === animation.effect) return {
|
|
1181
1203
|
id: 1,
|
|
1182
1204
|
subtype: 0
|
|
1183
1205
|
};
|
|
1184
|
-
if ('fade' === animation.effect) return {
|
|
1206
|
+
if ('fade' === animation.effect || 'fade-out' === animation.effect) return {
|
|
1185
1207
|
id: 10,
|
|
1186
1208
|
subtype: 0
|
|
1187
1209
|
};
|
|
1188
|
-
if ('zoom' === animation.effect) return {
|
|
1210
|
+
if ('zoom' === animation.effect || 'zoom-out' === animation.effect) return {
|
|
1189
1211
|
id: 23,
|
|
1190
1212
|
subtype: 16
|
|
1191
1213
|
};
|