html2pptx-local-mcp 1.1.17
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/app/docs/content.js +2082 -0
- package/cli/dist/commands/config-show.d.ts +1 -0
- package/cli/dist/commands/config-show.js +16 -0
- package/cli/dist/commands/convert.d.ts +10 -0
- package/cli/dist/commands/convert.js +311 -0
- package/cli/dist/commands/edit.d.ts +33 -0
- package/cli/dist/commands/edit.js +588 -0
- package/cli/dist/commands/init.d.ts +1 -0
- package/cli/dist/commands/init.js +35 -0
- package/cli/dist/commands/logout.d.ts +1 -0
- package/cli/dist/commands/logout.js +19 -0
- package/cli/dist/commands/publish.d.ts +10 -0
- package/cli/dist/commands/publish.js +17 -0
- package/cli/dist/commands/status.d.ts +5 -0
- package/cli/dist/commands/status.js +71 -0
- package/cli/dist/commands/templates.d.ts +13 -0
- package/cli/dist/commands/templates.js +85 -0
- package/cli/dist/commands/whoami.d.ts +5 -0
- package/cli/dist/commands/whoami.js +51 -0
- package/cli/dist/config.d.ts +7 -0
- package/cli/dist/config.js +24 -0
- package/cli/dist/index.d.ts +2 -0
- package/cli/dist/index.js +93 -0
- package/cli/dist/update-check.d.ts +1 -0
- package/cli/dist/update-check.js +30 -0
- package/cli/package.json +46 -0
- package/lib/local-slide-editor-launcher.js +353 -0
- package/lib/pptx-studio-mcp-core.js +1744 -0
- package/lib/server/template-html-policy.mjs +354 -0
- package/mcp/pptx-studio-mcp-server.mjs +198 -0
- package/package.json +32 -0
- package/scripts/install-mcp.mjs +316 -0
- package/src/animation-injector.js +724 -0
- package/src/animation-renderers.js +584 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
// src/animation-renderers.js
|
|
2
|
+
//
|
|
3
|
+
// Per-preset OOXML timing renderers.
|
|
4
|
+
//
|
|
5
|
+
// This replaces the previous "opacity 0→1 for every preset" approach with
|
|
6
|
+
// proper preset-specific animation XML. Each renderer returns the
|
|
7
|
+
// <p:childTnLst> contents for a given entrance / emphasis / exit effect.
|
|
8
|
+
//
|
|
9
|
+
// Three families:
|
|
10
|
+
//
|
|
11
|
+
// 1. animEffect — a high-level PowerPoint filter ("fade", "wipe(right)",
|
|
12
|
+
// "blinds(vertical)", etc.). PowerPoint renders them natively and
|
|
13
|
+
// correctly — this covers ~70% of the good-looking presets.
|
|
14
|
+
//
|
|
15
|
+
// 2. anim(attr) — drive a specific attribute (ppt_x, ppt_y, ppt_w,
|
|
16
|
+
// ppt_h, style.rotation, style.opacity) over time. Used for motion-
|
|
17
|
+
// based effects like flyin (ppt_x), zoom (ppt_w), spin (rotation).
|
|
18
|
+
//
|
|
19
|
+
// 3. combinations — entrance effects often need BOTH (visibility set +
|
|
20
|
+
// motion anim). Renderers compose them.
|
|
21
|
+
//
|
|
22
|
+
// All renderers take a uniform context object:
|
|
23
|
+
// { baseId, durMs, delayMs, spid, direction, easing }
|
|
24
|
+
// and return an XML string that fits inside <p:childTnLst>…</p:childTnLst>.
|
|
25
|
+
//
|
|
26
|
+
// The outer <p:par><p:cTn presetID=... presetClass=... presetSubtype=...>
|
|
27
|
+
// wrapper is built by the caller in animation-injector.js.
|
|
28
|
+
|
|
29
|
+
// ----------------------------------------------------------------------
|
|
30
|
+
// Ease curves
|
|
31
|
+
// ----------------------------------------------------------------------
|
|
32
|
+
//
|
|
33
|
+
// <p:anim> supports:
|
|
34
|
+
// calcmode="lin" — linear
|
|
35
|
+
// calcmode="discrete" — stepped (used only for set-style effects)
|
|
36
|
+
// calcmode="spline" — with explicit keySplines ("cpX1 cpY1 cpX2 cpY2")
|
|
37
|
+
//
|
|
38
|
+
// We use ease-out by default (natural deceleration) which reads as
|
|
39
|
+
// "premium / Apple-like" rather than mechanical.
|
|
40
|
+
const EASE_OUT_SPLINE = '0,0 0.25,1';
|
|
41
|
+
const EASE_IN_OUT_SPLINE = '0.42,0 0.58,1';
|
|
42
|
+
|
|
43
|
+
// ----------------------------------------------------------------------
|
|
44
|
+
// Helpers
|
|
45
|
+
// ----------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
function escapeXmlAttr(s) {
|
|
48
|
+
return String(s)
|
|
49
|
+
.replace(/&/g, '&')
|
|
50
|
+
.replace(/"/g, '"')
|
|
51
|
+
.replace(/</g, '<')
|
|
52
|
+
.replace(/>/g, '>');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setVisible(spid, id) {
|
|
56
|
+
return `
|
|
57
|
+
<p:set>
|
|
58
|
+
<p:cBhvr>
|
|
59
|
+
<p:cTn id="${id}" dur="1" fill="hold">
|
|
60
|
+
<p:stCondLst><p:cond delay="0"/></p:stCondLst>
|
|
61
|
+
</p:cTn>
|
|
62
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
63
|
+
<p:attrNameLst><p:attrName>style.visibility</p:attrName></p:attrNameLst>
|
|
64
|
+
</p:cBhvr>
|
|
65
|
+
<p:to><p:strVal val="visible"/></p:to>
|
|
66
|
+
</p:set>`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function setHidden(spid, id) {
|
|
70
|
+
return `
|
|
71
|
+
<p:set>
|
|
72
|
+
<p:cBhvr>
|
|
73
|
+
<p:cTn id="${id}" dur="1" fill="hold">
|
|
74
|
+
<p:stCondLst><p:cond delay="0"/></p:stCondLst>
|
|
75
|
+
</p:cTn>
|
|
76
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
77
|
+
<p:attrNameLst><p:attrName>style.visibility</p:attrName></p:attrNameLst>
|
|
78
|
+
</p:cBhvr>
|
|
79
|
+
<p:to><p:strVal val="hidden"/></p:to>
|
|
80
|
+
</p:set>`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Emit a <p:animEffect> block with the given filter.
|
|
84
|
+
// Transition is "in" for entrance, "out" for exit, "none" for emphasis.
|
|
85
|
+
function animEffect({ id, durMs, spid, filter, transition = 'in' }) {
|
|
86
|
+
return `
|
|
87
|
+
<p:animEffect transition="${transition}" filter="${escapeXmlAttr(filter)}">
|
|
88
|
+
<p:cBhvr>
|
|
89
|
+
<p:cTn id="${id}" dur="${durMs}"/>
|
|
90
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
91
|
+
</p:cBhvr>
|
|
92
|
+
</p:animEffect>`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Emit a <p:anim> block that drives a single attribute (e.g. ppt_x) from
|
|
96
|
+
// `fromVal` to `toVal` over `durMs`.
|
|
97
|
+
function attrAnim({ id, durMs, spid, attrName, fromVal, toVal, easing = 'spline', spline = EASE_OUT_SPLINE, valueType = 'num' }) {
|
|
98
|
+
const calcmode = easing === 'lin' ? 'lin' : 'spline';
|
|
99
|
+
const splineAttr = calcmode === 'spline' ? ` keySpline="${spline}"` : '';
|
|
100
|
+
return `
|
|
101
|
+
<p:anim calcmode="${calcmode}"${splineAttr} valueType="${valueType}">
|
|
102
|
+
<p:cBhvr additive="base">
|
|
103
|
+
<p:cTn id="${id}" dur="${durMs}" fill="hold"/>
|
|
104
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
105
|
+
<p:attrNameLst><p:attrName>${attrName}</p:attrName></p:attrNameLst>
|
|
106
|
+
</p:cBhvr>
|
|
107
|
+
<p:tavLst>
|
|
108
|
+
<p:tav tm="0"><p:val><p:strVal val="${escapeXmlAttr(fromVal)}"/></p:val></p:tav>
|
|
109
|
+
<p:tav tm="100000"><p:val><p:strVal val="${escapeXmlAttr(toVal)}"/></p:val></p:tav>
|
|
110
|
+
</p:tavLst>
|
|
111
|
+
</p:anim>`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Fade-based opacity animation (used as emphasis helper)
|
|
115
|
+
function opacityAnim({ id, durMs, spid, from, to }) {
|
|
116
|
+
return attrAnim({ id, durMs, spid, attrName: 'style.opacity', fromVal: String(from), toVal: String(to) });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ----------------------------------------------------------------------
|
|
120
|
+
// Direction helpers
|
|
121
|
+
// ----------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
// For flyin/flyout, map cardinal direction → from/to values on ppt_x or ppt_y.
|
|
124
|
+
// - "left" means "enter from left edge of slide" → from ppt_x = negative width
|
|
125
|
+
// - "right" means "enter from right edge" → from ppt_x = slide_w
|
|
126
|
+
// - "up" means "enter from top" → from ppt_y = negative height
|
|
127
|
+
// - "down" means "enter from bottom" → from ppt_y = slide_h
|
|
128
|
+
//
|
|
129
|
+
// We use PowerPoint formula syntax: #ppt_x references the shape's current X.
|
|
130
|
+
// For "from offscreen left": x = "#ppt_x - #ppt_w"
|
|
131
|
+
function flyFromForEntrance(direction) {
|
|
132
|
+
switch (direction) {
|
|
133
|
+
case 'right': return { attr: 'ppt_x', from: '#ppt_x + #ppt_w', to: '#ppt_x' };
|
|
134
|
+
case 'up': return { attr: 'ppt_y', from: '#ppt_y - #ppt_h', to: '#ppt_y' };
|
|
135
|
+
case 'down': return { attr: 'ppt_y', from: '#ppt_y + #ppt_h', to: '#ppt_y' };
|
|
136
|
+
case 'topleft': return null; // covered by two anims — caller handles
|
|
137
|
+
case 'topright': return null;
|
|
138
|
+
case 'bottomleft': return null;
|
|
139
|
+
case 'bottomright': return null;
|
|
140
|
+
case 'left':
|
|
141
|
+
default: return { attr: 'ppt_x', from: '#ppt_x - #ppt_w', to: '#ppt_x' };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function flyToForExit(direction) {
|
|
145
|
+
switch (direction) {
|
|
146
|
+
case 'right': return { attr: 'ppt_x', from: '#ppt_x', to: '#ppt_x + #ppt_w' };
|
|
147
|
+
case 'up': return { attr: 'ppt_y', from: '#ppt_y', to: '#ppt_y - #ppt_h' };
|
|
148
|
+
case 'down': return { attr: 'ppt_y', from: '#ppt_y', to: '#ppt_y + #ppt_h' };
|
|
149
|
+
case 'left':
|
|
150
|
+
default: return { attr: 'ppt_x', from: '#ppt_x', to: '#ppt_x - #ppt_w' };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// For animEffect-based wipes, map our cardinal direction → PowerPoint filter
|
|
155
|
+
function wipeFilter(direction) {
|
|
156
|
+
switch (direction) {
|
|
157
|
+
case 'right': return 'wipe(right)';
|
|
158
|
+
case 'up': return 'wipe(up)';
|
|
159
|
+
case 'down': return 'wipe(down)';
|
|
160
|
+
case 'left':
|
|
161
|
+
default: return 'wipe(left)';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ======================================================================
|
|
166
|
+
// ENTRANCE RENDERERS
|
|
167
|
+
// ======================================================================
|
|
168
|
+
|
|
169
|
+
function renderFadein(ctx) {
|
|
170
|
+
const { baseId, durMs, spid } = ctx;
|
|
171
|
+
return [
|
|
172
|
+
setVisible(spid, baseId + 1),
|
|
173
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
174
|
+
].join('');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function renderAppear(ctx) {
|
|
178
|
+
const { baseId, spid } = ctx;
|
|
179
|
+
return setVisible(spid, baseId + 1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderFlyin(ctx) {
|
|
183
|
+
const { baseId, durMs, spid, direction = 'left' } = ctx;
|
|
184
|
+
const fly = flyFromForEntrance(direction);
|
|
185
|
+
// Diagonal flyin uses both X and Y
|
|
186
|
+
if (!fly) {
|
|
187
|
+
const xDir = direction.includes('right') ? 'right' : 'left';
|
|
188
|
+
const yDir = direction.includes('top') ? 'up' : 'down';
|
|
189
|
+
const flyX = flyFromForEntrance(xDir);
|
|
190
|
+
const flyY = flyFromForEntrance(yDir);
|
|
191
|
+
return [
|
|
192
|
+
setVisible(spid, baseId + 1),
|
|
193
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: flyX.attr, fromVal: flyX.from, toVal: flyX.to }),
|
|
194
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: flyY.attr, fromVal: flyY.from, toVal: flyY.to }),
|
|
195
|
+
].join('');
|
|
196
|
+
}
|
|
197
|
+
return [
|
|
198
|
+
setVisible(spid, baseId + 1),
|
|
199
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: fly.attr, fromVal: fly.from, toVal: fly.to }),
|
|
200
|
+
].join('');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function renderFloatin(ctx) {
|
|
204
|
+
// Float in = fade + slight position drift (shorter distance than flyin).
|
|
205
|
+
const { baseId, durMs, spid, direction = 'up' } = ctx;
|
|
206
|
+
const drift = direction === 'down' ? '#ppt_y - #ppt_h/3' : '#ppt_y + #ppt_h/3';
|
|
207
|
+
const attrName = 'ppt_y';
|
|
208
|
+
return [
|
|
209
|
+
setVisible(spid, baseId + 1),
|
|
210
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
211
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName, fromVal: drift, toVal: '#ppt_y' }),
|
|
212
|
+
].join('');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function renderZoom(ctx) {
|
|
216
|
+
// Scale from 0 → full size, from center.
|
|
217
|
+
const { baseId, durMs, spid, direction = 'in' } = ctx;
|
|
218
|
+
const isOut = direction === 'out';
|
|
219
|
+
const fromScale = isOut ? '1.5' : '0';
|
|
220
|
+
const toScale = '1';
|
|
221
|
+
return [
|
|
222
|
+
setVisible(spid, baseId + 1),
|
|
223
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
224
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: 'ppt_w', fromVal: fromScale, toVal: toScale, valueType: 'num' }),
|
|
225
|
+
attrAnim({ id: baseId + 4, durMs, spid, attrName: 'ppt_h', fromVal: fromScale, toVal: toScale, valueType: 'num' }),
|
|
226
|
+
].join('');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function renderWipe(ctx) {
|
|
230
|
+
const { baseId, durMs, spid, direction = 'left' } = ctx;
|
|
231
|
+
return [
|
|
232
|
+
setVisible(spid, baseId + 1),
|
|
233
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: wipeFilter(direction), transition: 'in' }),
|
|
234
|
+
].join('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function renderDissolve(ctx) {
|
|
238
|
+
const { baseId, durMs, spid } = ctx;
|
|
239
|
+
return [
|
|
240
|
+
setVisible(spid, baseId + 1),
|
|
241
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'dissolve', transition: 'in' }),
|
|
242
|
+
].join('');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function renderSplit(ctx) {
|
|
246
|
+
const { baseId, durMs, spid, direction = 'out' } = ctx;
|
|
247
|
+
const filter = direction === 'in' ? 'barn(inVertical)' : 'barn(outVertical)';
|
|
248
|
+
return [
|
|
249
|
+
setVisible(spid, baseId + 1),
|
|
250
|
+
animEffect({ id: baseId + 2, durMs, spid, filter, transition: 'in' }),
|
|
251
|
+
].join('');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function renderStretch(ctx) {
|
|
255
|
+
// Stretch = reveal with horizontal scale from 0 to 1.
|
|
256
|
+
const { baseId, durMs, spid } = ctx;
|
|
257
|
+
return [
|
|
258
|
+
setVisible(spid, baseId + 1),
|
|
259
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: 'ppt_w', fromVal: '0', toVal: '1' }),
|
|
260
|
+
animEffect({ id: baseId + 3, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
261
|
+
].join('');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function renderBounce(ctx) {
|
|
265
|
+
// Elegant bounce entrance = vertical spring motion with fade-in.
|
|
266
|
+
const { baseId, durMs, spid } = ctx;
|
|
267
|
+
return [
|
|
268
|
+
setVisible(spid, baseId + 1),
|
|
269
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
270
|
+
// Vertical bounce: from 30% above, overshoot slightly, settle
|
|
271
|
+
`<p:anim calcmode="spline" keySpline="0.68,-0.55 0.265,1.55" valueType="num">
|
|
272
|
+
<p:cBhvr additive="base">
|
|
273
|
+
<p:cTn id="${baseId + 3}" dur="${durMs}" fill="hold"/>
|
|
274
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
275
|
+
<p:attrNameLst><p:attrName>ppt_y</p:attrName></p:attrNameLst>
|
|
276
|
+
</p:cBhvr>
|
|
277
|
+
<p:tavLst>
|
|
278
|
+
<p:tav tm="0"><p:val><p:strVal val="#ppt_y - #ppt_h/2"/></p:val></p:tav>
|
|
279
|
+
<p:tav tm="100000"><p:val><p:strVal val="#ppt_y"/></p:val></p:tav>
|
|
280
|
+
</p:tavLst>
|
|
281
|
+
</p:anim>`,
|
|
282
|
+
].join('');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function renderSpin(ctx) {
|
|
286
|
+
// Full rotation + fade-in (entrance variant of spin).
|
|
287
|
+
const { baseId, durMs, spid } = ctx;
|
|
288
|
+
return [
|
|
289
|
+
setVisible(spid, baseId + 1),
|
|
290
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
291
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: 'style.rotation', fromVal: '-180', toVal: '0', easing: 'spline' }),
|
|
292
|
+
].join('');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function renderRiseUp(ctx) {
|
|
296
|
+
// Rise up = subtle slide up from below with fade. Modern "classy" reveal.
|
|
297
|
+
const { baseId, durMs, spid } = ctx;
|
|
298
|
+
return [
|
|
299
|
+
setVisible(spid, baseId + 1),
|
|
300
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
301
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: 'ppt_y', fromVal: '#ppt_y + #ppt_h/4', toVal: '#ppt_y' }),
|
|
302
|
+
].join('');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function renderDescend(ctx) {
|
|
306
|
+
const { baseId, durMs, spid } = ctx;
|
|
307
|
+
return [
|
|
308
|
+
setVisible(spid, baseId + 1),
|
|
309
|
+
animEffect({ id: baseId + 2, durMs, spid, filter: 'fade', transition: 'in' }),
|
|
310
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: 'ppt_y', fromVal: '#ppt_y - #ppt_h/4', toVal: '#ppt_y' }),
|
|
311
|
+
].join('');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Fallback: pure fade-in (used for less common entrance presets)
|
|
315
|
+
function renderGenericEntrance(ctx) {
|
|
316
|
+
return renderFadein(ctx);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ======================================================================
|
|
320
|
+
// EMPHASIS RENDERERS
|
|
321
|
+
// ======================================================================
|
|
322
|
+
|
|
323
|
+
function renderPulse(ctx) {
|
|
324
|
+
// Pulse scales 1 → 1.08 → 1 over the duration.
|
|
325
|
+
const { baseId, durMs, spid } = ctx;
|
|
326
|
+
return `
|
|
327
|
+
<p:anim calcmode="spline" keySpline="0.42,0 0.58,1 0.42,0 0.58,1" valueType="num">
|
|
328
|
+
<p:cBhvr additive="base">
|
|
329
|
+
<p:cTn id="${baseId + 1}" dur="${durMs}" fill="hold"/>
|
|
330
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
331
|
+
<p:attrNameLst><p:attrName>ppt_w</p:attrName></p:attrNameLst>
|
|
332
|
+
</p:cBhvr>
|
|
333
|
+
<p:tavLst>
|
|
334
|
+
<p:tav tm="0"><p:val><p:strVal val="#ppt_w"/></p:val></p:tav>
|
|
335
|
+
<p:tav tm="50000"><p:val><p:strVal val="#ppt_w * 1.08"/></p:val></p:tav>
|
|
336
|
+
<p:tav tm="100000"><p:val><p:strVal val="#ppt_w"/></p:val></p:tav>
|
|
337
|
+
</p:tavLst>
|
|
338
|
+
</p:anim>
|
|
339
|
+
<p:anim calcmode="spline" keySpline="0.42,0 0.58,1 0.42,0 0.58,1" valueType="num">
|
|
340
|
+
<p:cBhvr additive="base">
|
|
341
|
+
<p:cTn id="${baseId + 2}" dur="${durMs}" fill="hold"/>
|
|
342
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
343
|
+
<p:attrNameLst><p:attrName>ppt_h</p:attrName></p:attrNameLst>
|
|
344
|
+
</p:cBhvr>
|
|
345
|
+
<p:tavLst>
|
|
346
|
+
<p:tav tm="0"><p:val><p:strVal val="#ppt_h"/></p:val></p:tav>
|
|
347
|
+
<p:tav tm="50000"><p:val><p:strVal val="#ppt_h * 1.08"/></p:val></p:tav>
|
|
348
|
+
<p:tav tm="100000"><p:val><p:strVal val="#ppt_h"/></p:val></p:tav>
|
|
349
|
+
</p:tavLst>
|
|
350
|
+
</p:anim>`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function renderEmphasisSpin(ctx) {
|
|
354
|
+
const { baseId, durMs, spid } = ctx;
|
|
355
|
+
return attrAnim({ id: baseId + 1, durMs, spid, attrName: 'style.rotation', fromVal: '0', toVal: '360', easing: 'lin' });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function renderGrow(ctx) {
|
|
359
|
+
// Grow 1 → 1.15
|
|
360
|
+
const { baseId, durMs, spid } = ctx;
|
|
361
|
+
return [
|
|
362
|
+
attrAnim({ id: baseId + 1, durMs, spid, attrName: 'ppt_w', fromVal: '#ppt_w', toVal: '#ppt_w * 1.15' }),
|
|
363
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: 'ppt_h', fromVal: '#ppt_h', toVal: '#ppt_h * 1.15' }),
|
|
364
|
+
].join('');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function renderShrink(ctx) {
|
|
368
|
+
const { baseId, durMs, spid } = ctx;
|
|
369
|
+
return [
|
|
370
|
+
attrAnim({ id: baseId + 1, durMs, spid, attrName: 'ppt_w', fromVal: '#ppt_w', toVal: '#ppt_w * 0.85' }),
|
|
371
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: 'ppt_h', fromVal: '#ppt_h', toVal: '#ppt_h * 0.85' }),
|
|
372
|
+
].join('');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function renderFlash(ctx) {
|
|
376
|
+
const { baseId, durMs, spid } = ctx;
|
|
377
|
+
return `
|
|
378
|
+
<p:anim calcmode="discrete" valueType="num">
|
|
379
|
+
<p:cBhvr additive="base">
|
|
380
|
+
<p:cTn id="${baseId + 1}" dur="${durMs}" fill="hold"/>
|
|
381
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
382
|
+
<p:attrNameLst><p:attrName>style.opacity</p:attrName></p:attrNameLst>
|
|
383
|
+
</p:cBhvr>
|
|
384
|
+
<p:tavLst>
|
|
385
|
+
<p:tav tm="0"><p:val><p:strVal val="1"/></p:val></p:tav>
|
|
386
|
+
<p:tav tm="25000"><p:val><p:strVal val="0.2"/></p:val></p:tav>
|
|
387
|
+
<p:tav tm="50000"><p:val><p:strVal val="1"/></p:val></p:tav>
|
|
388
|
+
<p:tav tm="75000"><p:val><p:strVal val="0.2"/></p:val></p:tav>
|
|
389
|
+
<p:tav tm="100000"><p:val><p:strVal val="1"/></p:val></p:tav>
|
|
390
|
+
</p:tavLst>
|
|
391
|
+
</p:anim>`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function renderTeeter(ctx) {
|
|
395
|
+
// Teeter = tilt back and forth around center (+8°, -8°, +4°, 0°)
|
|
396
|
+
const { baseId, durMs, spid } = ctx;
|
|
397
|
+
return `
|
|
398
|
+
<p:anim calcmode="spline" keySpline="0.4,0 0.6,1 0.4,0 0.6,1 0.4,0 0.6,1 0.4,0 0.6,1" valueType="num">
|
|
399
|
+
<p:cBhvr additive="base">
|
|
400
|
+
<p:cTn id="${baseId + 1}" dur="${durMs}" fill="hold"/>
|
|
401
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
402
|
+
<p:attrNameLst><p:attrName>style.rotation</p:attrName></p:attrNameLst>
|
|
403
|
+
</p:cBhvr>
|
|
404
|
+
<p:tavLst>
|
|
405
|
+
<p:tav tm="0"><p:val><p:strVal val="0"/></p:val></p:tav>
|
|
406
|
+
<p:tav tm="25000"><p:val><p:strVal val="8"/></p:val></p:tav>
|
|
407
|
+
<p:tav tm="50000"><p:val><p:strVal val="-8"/></p:val></p:tav>
|
|
408
|
+
<p:tav tm="75000"><p:val><p:strVal val="4"/></p:val></p:tav>
|
|
409
|
+
<p:tav tm="100000"><p:val><p:strVal val="0"/></p:val></p:tav>
|
|
410
|
+
</p:tavLst>
|
|
411
|
+
</p:anim>`;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function renderBlink(ctx) {
|
|
415
|
+
const { baseId, durMs, spid } = ctx;
|
|
416
|
+
return `
|
|
417
|
+
<p:anim calcmode="discrete" valueType="num">
|
|
418
|
+
<p:cBhvr additive="base">
|
|
419
|
+
<p:cTn id="${baseId + 1}" dur="${durMs}" fill="hold"/>
|
|
420
|
+
<p:tgtEl><p:spTgt spid="${spid}"/></p:tgtEl>
|
|
421
|
+
<p:attrNameLst><p:attrName>style.opacity</p:attrName></p:attrNameLst>
|
|
422
|
+
</p:cBhvr>
|
|
423
|
+
<p:tavLst>
|
|
424
|
+
<p:tav tm="0"><p:val><p:strVal val="1"/></p:val></p:tav>
|
|
425
|
+
<p:tav tm="50000"><p:val><p:strVal val="0"/></p:val></p:tav>
|
|
426
|
+
<p:tav tm="100000"><p:val><p:strVal val="1"/></p:val></p:tav>
|
|
427
|
+
</p:tavLst>
|
|
428
|
+
</p:anim>`;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Fallback for less common emphasis effects
|
|
432
|
+
function renderGenericEmphasis(ctx) {
|
|
433
|
+
return renderPulse(ctx);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ======================================================================
|
|
437
|
+
// EXIT RENDERERS
|
|
438
|
+
// ======================================================================
|
|
439
|
+
|
|
440
|
+
function renderFadeout(ctx) {
|
|
441
|
+
const { baseId, durMs, spid } = ctx;
|
|
442
|
+
return [
|
|
443
|
+
animEffect({ id: baseId + 1, durMs, spid, filter: 'fade', transition: 'out' }),
|
|
444
|
+
setHidden(spid, baseId + 2),
|
|
445
|
+
].join('');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function renderDisappear(ctx) {
|
|
449
|
+
const { baseId, spid } = ctx;
|
|
450
|
+
return setHidden(spid, baseId + 1);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function renderFlyout(ctx) {
|
|
454
|
+
const { baseId, durMs, spid, direction = 'left' } = ctx;
|
|
455
|
+
const fly = flyToForExit(direction);
|
|
456
|
+
return [
|
|
457
|
+
attrAnim({ id: baseId + 1, durMs, spid, attrName: fly.attr, fromVal: fly.from, toVal: fly.to }),
|
|
458
|
+
setHidden(spid, baseId + 2),
|
|
459
|
+
].join('');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function renderZoomout(ctx) {
|
|
463
|
+
const { baseId, durMs, spid, direction = 'out' } = ctx;
|
|
464
|
+
const toScale = direction === 'in' ? '1.5' : '0';
|
|
465
|
+
return [
|
|
466
|
+
animEffect({ id: baseId + 1, durMs, spid, filter: 'fade', transition: 'out' }),
|
|
467
|
+
attrAnim({ id: baseId + 2, durMs, spid, attrName: 'ppt_w', fromVal: '1', toVal: toScale }),
|
|
468
|
+
attrAnim({ id: baseId + 3, durMs, spid, attrName: 'ppt_h', fromVal: '1', toVal: toScale }),
|
|
469
|
+
setHidden(spid, baseId + 4),
|
|
470
|
+
].join('');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function renderWipeout(ctx) {
|
|
474
|
+
const { baseId, durMs, spid, direction = 'left' } = ctx;
|
|
475
|
+
return [
|
|
476
|
+
animEffect({ id: baseId + 1, durMs, spid, filter: wipeFilter(direction), transition: 'out' }),
|
|
477
|
+
setHidden(spid, baseId + 2),
|
|
478
|
+
].join('');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function renderDissolveout(ctx) {
|
|
482
|
+
const { baseId, durMs, spid } = ctx;
|
|
483
|
+
return [
|
|
484
|
+
animEffect({ id: baseId + 1, durMs, spid, filter: 'dissolve', transition: 'out' }),
|
|
485
|
+
setHidden(spid, baseId + 2),
|
|
486
|
+
].join('');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function renderGenericExit(ctx) {
|
|
490
|
+
return renderFadeout(ctx);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ======================================================================
|
|
494
|
+
// REGISTRY
|
|
495
|
+
// ======================================================================
|
|
496
|
+
//
|
|
497
|
+
// Maps entrance/emphasis/exit preset names → renderer function.
|
|
498
|
+
// Presets not in the registry fall through to a sensible generic renderer.
|
|
499
|
+
|
|
500
|
+
export const PRESET_RENDERERS = {
|
|
501
|
+
// --- Entrance ---
|
|
502
|
+
appear: renderAppear,
|
|
503
|
+
fadein: renderFadein,
|
|
504
|
+
flyin: renderFlyin,
|
|
505
|
+
floatin: renderFloatin,
|
|
506
|
+
split: renderSplit,
|
|
507
|
+
wipe: renderWipe,
|
|
508
|
+
zoom: renderZoom,
|
|
509
|
+
bounce: renderBounce,
|
|
510
|
+
swivel: renderSpin,
|
|
511
|
+
dissolvein: renderDissolve,
|
|
512
|
+
expand: renderStretch,
|
|
513
|
+
stretch: renderStretch,
|
|
514
|
+
riseup: renderRiseUp,
|
|
515
|
+
ascend: renderRiseUp,
|
|
516
|
+
descend: renderDescend,
|
|
517
|
+
risefall: renderDescend,
|
|
518
|
+
float: renderFloatin,
|
|
519
|
+
glidein: renderFadein,
|
|
520
|
+
fadeswivel: renderSpin,
|
|
521
|
+
zoomcenter: renderZoom,
|
|
522
|
+
basiczoom: renderZoom,
|
|
523
|
+
fadedzoom: renderZoom,
|
|
524
|
+
compress: renderZoom,
|
|
525
|
+
centerrevolve: renderSpin,
|
|
526
|
+
spinner: renderSpin,
|
|
527
|
+
pinwheel: renderSpin,
|
|
528
|
+
boomerang: renderBounce,
|
|
529
|
+
credits: renderRiseUp,
|
|
530
|
+
curveup: renderRiseUp,
|
|
531
|
+
flipin: renderSpin,
|
|
532
|
+
foldin: renderStretch,
|
|
533
|
+
gridout: renderWipe,
|
|
534
|
+
spiral: renderSpin,
|
|
535
|
+
thread: renderFadein,
|
|
536
|
+
whip: renderFlyin,
|
|
537
|
+
// --- Emphasis ---
|
|
538
|
+
pulse: renderPulse,
|
|
539
|
+
spin: renderEmphasisSpin,
|
|
540
|
+
grow: renderGrow,
|
|
541
|
+
shrink: renderShrink,
|
|
542
|
+
flash: renderFlash,
|
|
543
|
+
colorpulse: renderPulse,
|
|
544
|
+
teeter: renderTeeter,
|
|
545
|
+
shimmer: renderPulse,
|
|
546
|
+
blink: renderBlink,
|
|
547
|
+
bold: renderPulse,
|
|
548
|
+
wave: renderTeeter,
|
|
549
|
+
desaturate: renderPulse,
|
|
550
|
+
darken: renderPulse,
|
|
551
|
+
lighten: renderPulse,
|
|
552
|
+
transparent: renderPulse,
|
|
553
|
+
colorwave: renderPulse,
|
|
554
|
+
brushon: renderPulse,
|
|
555
|
+
wobble: renderTeeter,
|
|
556
|
+
// --- Exit ---
|
|
557
|
+
disappear: renderDisappear,
|
|
558
|
+
fadeout: renderFadeout,
|
|
559
|
+
flyout: renderFlyout,
|
|
560
|
+
floatout: renderFadeout,
|
|
561
|
+
zoomout: renderZoomout,
|
|
562
|
+
splitout: renderFadeout,
|
|
563
|
+
wipeout: renderWipeout,
|
|
564
|
+
shrinkout: renderZoomout,
|
|
565
|
+
dissolveout: renderDissolveout,
|
|
566
|
+
peekout: renderFadeout,
|
|
567
|
+
bounceout: renderFadeout,
|
|
568
|
+
swivelout: renderFadeout,
|
|
569
|
+
spiralout: renderFadeout,
|
|
570
|
+
flipout: renderFadeout,
|
|
571
|
+
foldout: renderFadeout,
|
|
572
|
+
glideout: renderFadeout,
|
|
573
|
+
boomerangout: renderFadeout,
|
|
574
|
+
contract: renderZoomout,
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
export function renderPresetChildTimeline(name, ctx) {
|
|
578
|
+
const renderer = PRESET_RENDERERS[name];
|
|
579
|
+
if (renderer) return renderer(ctx);
|
|
580
|
+
// Fallback — sensible default per preset class
|
|
581
|
+
if (ctx.presetClass === 'exit') return renderGenericExit(ctx);
|
|
582
|
+
if (ctx.presetClass === 'emph') return renderGenericEmphasis(ctx);
|
|
583
|
+
return renderGenericEntrance(ctx);
|
|
584
|
+
}
|