@shotstack/shotstack-canvas 2.1.5 → 2.1.7

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.
@@ -590,8 +590,7 @@ var richCaptionFontSchema = import_zod.z.object({
590
590
  weight: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).default("400"),
591
591
  color: import_zod.z.string().regex(HEX6).default("#ffffff"),
592
592
  opacity: import_zod.z.number().min(0).max(1).default(1),
593
- background: import_zod.z.string().regex(HEX6).optional(),
594
- textDecoration: import_zod.z.enum(["none", "underline", "line-through"]).default("none")
593
+ background: import_zod.z.string().regex(HEX6).optional()
595
594
  });
596
595
  var richCaptionActiveSchema = import_zod2.richCaptionActiveSchema.extend({
597
596
  font: import_zod.z.object({
@@ -600,23 +599,28 @@ var richCaptionActiveSchema = import_zod2.richCaptionActiveSchema.extend({
600
599
  opacity: import_zod.z.number().min(0).max(1).optional(),
601
600
  textDecoration: import_zod.z.enum(["none", "underline", "line-through"]).optional()
602
601
  }).optional(),
603
- stroke: import_zod.z.object({
604
- width: import_zod.z.number().min(0).optional(),
605
- color: import_zod.z.string().regex(HEX6).optional(),
606
- opacity: import_zod.z.number().min(0).max(1).optional()
607
- }).optional(),
608
- shadow: import_zod.z.object({
609
- offsetX: import_zod.z.number().optional(),
610
- offsetY: import_zod.z.number().optional(),
611
- blur: import_zod.z.number().min(0).optional(),
612
- color: import_zod.z.string().regex(HEX6).optional(),
613
- opacity: import_zod.z.number().min(0).max(1).optional()
614
- }).optional(),
602
+ stroke: import_zod.z.union([
603
+ import_zod.z.object({
604
+ width: import_zod.z.number().min(0).optional(),
605
+ color: import_zod.z.string().regex(HEX6).optional(),
606
+ opacity: import_zod.z.number().min(0).max(1).optional()
607
+ }),
608
+ import_zod.z.literal("none")
609
+ ]).optional(),
610
+ shadow: import_zod.z.union([
611
+ import_zod.z.object({
612
+ offsetX: import_zod.z.number().optional(),
613
+ offsetY: import_zod.z.number().optional(),
614
+ blur: import_zod.z.number().min(0).optional(),
615
+ color: import_zod.z.string().regex(HEX6).optional(),
616
+ opacity: import_zod.z.number().min(0).max(1).optional()
617
+ }),
618
+ import_zod.z.literal("none")
619
+ ]).optional(),
615
620
  scale: import_zod.z.number().min(0.5).max(2).default(1)
616
621
  });
617
- var richCaptionWordAnimationSchema = import_zod2.richCaptionWordAnimationSchema.extend({
622
+ var richCaptionWordAnimationSchema = import_zod.z.object({
618
623
  style: import_zod.z.enum(["karaoke", "highlight", "pop", "fade", "slide", "bounce", "typewriter", "none"]).default("highlight"),
619
- speed: import_zod.z.number().min(0.5).max(2).default(1),
620
624
  direction: import_zod.z.enum(["left", "right", "up", "down"]).default("up")
621
625
  });
622
626
  var richCaptionAssetSchema = import_zod.z.object({
@@ -1534,7 +1538,7 @@ var LayoutEngine = class {
1534
1538
  }
1535
1539
  async layout(params) {
1536
1540
  try {
1537
- const { textTransform, desc, fontSize, letterSpacing, width, emojiFallback } = params;
1541
+ const { textTransform, desc, fontSize, letterSpacing, wordSpacing = 0, width, emojiFallback } = params;
1538
1542
  const input = this.transformText(params.text, textTransform);
1539
1543
  if (!input || input.length === 0) {
1540
1544
  return [];
@@ -1564,9 +1568,10 @@ var LayoutEngine = class {
1564
1568
  char = String.fromCodePoint(codePoint);
1565
1569
  }
1566
1570
  }
1571
+ const isSpace = char === " ";
1567
1572
  return {
1568
1573
  id: g.g,
1569
- xAdvance: g.ax * scale + letterSpacing,
1574
+ xAdvance: g.ax * scale + letterSpacing + (isSpace ? wordSpacing : 0),
1570
1575
  xOffset: g.dx * scale,
1571
1576
  yOffset: -g.dy * scale,
1572
1577
  cluster: g.cl,
@@ -2843,10 +2848,10 @@ var CaptionLayoutEngine = class {
2843
2848
  let spaceWidth;
2844
2849
  if (config.measureTextWidth) {
2845
2850
  const fontString = `${config.fontWeight} ${config.fontSize}px "${config.fontFamily}"`;
2846
- spaceWidth = config.measureTextWidth(" ", fontString) + config.wordSpacing;
2851
+ spaceWidth = config.measureTextWidth(" ", fontString);
2847
2852
  } else {
2848
2853
  const spaceWord = await this.measureWord(" ", measurementConfig);
2849
- spaceWidth = spaceWord.width + config.wordSpacing;
2854
+ spaceWidth = spaceWord.width;
2850
2855
  }
2851
2856
  const groups = wordGroups.flatMap((indices) => {
2852
2857
  const groupWidths = indices.map((i) => store.widths[i]);
@@ -3045,12 +3050,8 @@ function calculateWordProgress(ctx) {
3045
3050
  function isWordActive(ctx) {
3046
3051
  return ctx.currentTime >= ctx.wordStart && ctx.currentTime < ctx.wordEnd;
3047
3052
  }
3048
- function calculateKaraokeState(ctx, speed) {
3053
+ function calculateKaraokeState(ctx) {
3049
3054
  const isActive = isWordActive(ctx);
3050
- const wordDuration = ctx.wordEnd - ctx.wordStart;
3051
- const adjustedDuration = wordDuration / speed;
3052
- const adjustedEnd = ctx.wordStart + adjustedDuration;
3053
- const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
3054
3055
  if (ctx.currentTime < ctx.wordStart) {
3055
3056
  return {
3056
3057
  fillProgress: 0,
@@ -3058,7 +3059,7 @@ function calculateKaraokeState(ctx, speed) {
3058
3059
  opacity: 1
3059
3060
  };
3060
3061
  }
3061
- if (ctx.currentTime >= adjustedEnd) {
3062
+ if (ctx.currentTime >= ctx.wordEnd) {
3062
3063
  return {
3063
3064
  fillProgress: 1,
3064
3065
  isActive: false,
@@ -3066,7 +3067,7 @@ function calculateKaraokeState(ctx, speed) {
3066
3067
  };
3067
3068
  }
3068
3069
  return {
3069
- fillProgress: calculateWordProgress(adjustedCtx),
3070
+ fillProgress: calculateWordProgress(ctx),
3070
3071
  isActive,
3071
3072
  opacity: 1
3072
3073
  };
@@ -3079,7 +3080,7 @@ function calculateHighlightState(ctx) {
3079
3080
  opacity: 1
3080
3081
  };
3081
3082
  }
3082
- function calculatePopState(ctx, activeScale, speed) {
3083
+ function calculatePopState(ctx, activeScale) {
3083
3084
  if (ctx.currentTime < ctx.wordStart) {
3084
3085
  return {
3085
3086
  scale: 0.5,
@@ -3088,9 +3089,7 @@ function calculatePopState(ctx, activeScale, speed) {
3088
3089
  fillProgress: 0
3089
3090
  };
3090
3091
  }
3091
- const adjustedDuration = ctx.animationDuration / speed;
3092
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
3093
- const progress = calculateAnimationProgress(adjustedCtx);
3092
+ const progress = calculateAnimationProgress(ctx);
3094
3093
  const easedProgress = easeOutBack(progress);
3095
3094
  const startScale = 0.5;
3096
3095
  const isActive = isWordActive(ctx);
@@ -3103,7 +3102,7 @@ function calculatePopState(ctx, activeScale, speed) {
3103
3102
  fillProgress: isActive ? 1 : 0
3104
3103
  };
3105
3104
  }
3106
- function calculateFadeState(ctx, speed) {
3105
+ function calculateFadeState(ctx) {
3107
3106
  if (ctx.currentTime < ctx.wordStart) {
3108
3107
  return {
3109
3108
  opacity: 0,
@@ -3111,9 +3110,7 @@ function calculateFadeState(ctx, speed) {
3111
3110
  fillProgress: 0
3112
3111
  };
3113
3112
  }
3114
- const adjustedDuration = ctx.animationDuration / speed;
3115
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
3116
- const progress = calculateAnimationProgress(adjustedCtx);
3113
+ const progress = calculateAnimationProgress(ctx);
3117
3114
  const easedProgress = easeInOutQuad(progress);
3118
3115
  const isActive = isWordActive(ctx);
3119
3116
  return {
@@ -3122,7 +3119,7 @@ function calculateFadeState(ctx, speed) {
3122
3119
  fillProgress: isActive ? 1 : 0
3123
3120
  };
3124
3121
  }
3125
- function calculateSlideState(ctx, direction, speed, fontSize) {
3122
+ function calculateSlideState(ctx, direction, fontSize) {
3126
3123
  const slideDistance = fontSize * 1.5;
3127
3124
  if (ctx.currentTime < ctx.wordStart) {
3128
3125
  const offset2 = getDirectionOffset(direction, slideDistance);
@@ -3134,9 +3131,7 @@ function calculateSlideState(ctx, direction, speed, fontSize) {
3134
3131
  fillProgress: 0
3135
3132
  };
3136
3133
  }
3137
- const adjustedDuration = ctx.animationDuration / speed;
3138
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
3139
- const progress = calculateAnimationProgress(adjustedCtx);
3134
+ const progress = calculateAnimationProgress(ctx);
3140
3135
  const easedProgress = easeOutCirc(progress);
3141
3136
  const offset = getDirectionOffset(direction, slideDistance);
3142
3137
  const translateX = offset.x * (1 - easedProgress);
@@ -3162,7 +3157,7 @@ function getDirectionOffset(direction, distance) {
3162
3157
  return { x: 0, y: -distance };
3163
3158
  }
3164
3159
  }
3165
- function calculateBounceState(ctx, speed, fontSize) {
3160
+ function calculateBounceState(ctx, fontSize) {
3166
3161
  const bounceDistance = fontSize * 0.8;
3167
3162
  if (ctx.currentTime < ctx.wordStart) {
3168
3163
  return {
@@ -3172,9 +3167,7 @@ function calculateBounceState(ctx, speed, fontSize) {
3172
3167
  fillProgress: 0
3173
3168
  };
3174
3169
  }
3175
- const adjustedDuration = ctx.animationDuration / speed;
3176
- const adjustedCtx = { ...ctx, animationDuration: adjustedDuration };
3177
- const progress = calculateAnimationProgress(adjustedCtx);
3170
+ const progress = calculateAnimationProgress(ctx);
3178
3171
  const easedProgress = easeOutBounce(progress);
3179
3172
  const isActive = isWordActive(ctx);
3180
3173
  return {
@@ -3184,11 +3177,7 @@ function calculateBounceState(ctx, speed, fontSize) {
3184
3177
  fillProgress: isActive ? 1 : 0
3185
3178
  };
3186
3179
  }
3187
- function calculateTypewriterState(ctx, charCount, speed) {
3188
- const wordDuration = ctx.wordEnd - ctx.wordStart;
3189
- const adjustedDuration = wordDuration / speed;
3190
- const adjustedEnd = ctx.wordStart + adjustedDuration;
3191
- const adjustedCtx = { ...ctx, wordEnd: adjustedEnd };
3180
+ function calculateTypewriterState(ctx, charCount) {
3192
3181
  if (ctx.currentTime < ctx.wordStart) {
3193
3182
  return {
3194
3183
  visibleCharacters: 0,
@@ -3197,7 +3186,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
3197
3186
  fillProgress: 0
3198
3187
  };
3199
3188
  }
3200
- if (ctx.currentTime >= adjustedEnd) {
3189
+ if (ctx.currentTime >= ctx.wordEnd) {
3201
3190
  return {
3202
3191
  visibleCharacters: charCount,
3203
3192
  opacity: 1,
@@ -3205,7 +3194,7 @@ function calculateTypewriterState(ctx, charCount, speed) {
3205
3194
  fillProgress: 0
3206
3195
  };
3207
3196
  }
3208
- const progress = calculateWordProgress(adjustedCtx);
3197
+ const progress = calculateWordProgress(ctx);
3209
3198
  const visibleCharacters = Math.ceil(progress * charCount);
3210
3199
  const isActive = isWordActive(ctx);
3211
3200
  return {
@@ -3223,7 +3212,6 @@ function calculateNoneState(_ctx) {
3223
3212
  };
3224
3213
  }
3225
3214
  function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
3226
- const safeSpeed = config.speed > 0 ? config.speed : 1;
3227
3215
  const ctx = {
3228
3216
  wordStart,
3229
3217
  wordEnd,
@@ -3234,27 +3222,27 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
3234
3222
  let partialState;
3235
3223
  switch (config.style) {
3236
3224
  case "karaoke":
3237
- partialState = calculateKaraokeState(ctx, safeSpeed);
3225
+ partialState = calculateKaraokeState(ctx);
3238
3226
  break;
3239
3227
  case "highlight":
3240
3228
  partialState = calculateHighlightState(ctx);
3241
3229
  break;
3242
3230
  case "pop":
3243
- partialState = calculatePopState(ctx, activeScale, safeSpeed);
3231
+ partialState = calculatePopState(ctx, activeScale);
3244
3232
  break;
3245
3233
  case "fade":
3246
- partialState = calculateFadeState(ctx, safeSpeed);
3234
+ partialState = calculateFadeState(ctx);
3247
3235
  break;
3248
3236
  case "slide": {
3249
3237
  const slideDir = mirrorAnimationDirection(config.direction, isRTL);
3250
- partialState = calculateSlideState(ctx, slideDir, config.speed, fontSize);
3238
+ partialState = calculateSlideState(ctx, slideDir, fontSize);
3251
3239
  break;
3252
3240
  }
3253
3241
  case "bounce":
3254
- partialState = calculateBounceState(ctx, safeSpeed, fontSize);
3242
+ partialState = calculateBounceState(ctx, fontSize);
3255
3243
  break;
3256
3244
  case "typewriter":
3257
- partialState = calculateTypewriterState(ctx, charCount, safeSpeed);
3245
+ partialState = calculateTypewriterState(ctx, charCount);
3258
3246
  break;
3259
3247
  case "none":
3260
3248
  default:
@@ -3287,7 +3275,6 @@ function calculateAnimationStatesForGroup(words, currentTime, config, activeScal
3287
3275
  function getDefaultAnimationConfig() {
3288
3276
  return {
3289
3277
  style: "highlight",
3290
- speed: 1,
3291
3278
  direction: "up"
3292
3279
  };
3293
3280
  }
@@ -3328,18 +3315,17 @@ function extractFontConfig(asset) {
3328
3315
  function extractStrokeConfig(asset, isActive) {
3329
3316
  const baseStroke = asset.stroke;
3330
3317
  const activeStroke = asset.active?.stroke;
3331
- if (!baseStroke && !activeStroke) {
3332
- return void 0;
3333
- }
3334
3318
  if (isActive) {
3335
- if (!activeStroke) {
3319
+ if (activeStroke === "none") {
3336
3320
  return void 0;
3337
3321
  }
3338
- return {
3339
- width: activeStroke.width ?? baseStroke?.width ?? 0,
3340
- color: activeStroke.color ?? baseStroke?.color ?? "#000000",
3341
- opacity: activeStroke.opacity ?? baseStroke?.opacity ?? 1
3342
- };
3322
+ if (activeStroke && typeof activeStroke === "object") {
3323
+ return {
3324
+ width: activeStroke.width ?? baseStroke?.width ?? 0,
3325
+ color: activeStroke.color ?? baseStroke?.color ?? "#000000",
3326
+ opacity: activeStroke.opacity ?? baseStroke?.opacity ?? 1
3327
+ };
3328
+ }
3343
3329
  }
3344
3330
  if (baseStroke) {
3345
3331
  return {
@@ -3353,20 +3339,19 @@ function extractStrokeConfig(asset, isActive) {
3353
3339
  function extractShadowConfig(asset, isActive) {
3354
3340
  const baseShadow = asset.shadow;
3355
3341
  const activeShadow = asset.active?.shadow;
3356
- if (!baseShadow && !activeShadow) {
3357
- return void 0;
3358
- }
3359
3342
  if (isActive) {
3360
- if (!activeShadow) {
3343
+ if (activeShadow === "none") {
3361
3344
  return void 0;
3362
3345
  }
3363
- return {
3364
- offsetX: activeShadow.offsetX ?? baseShadow?.offsetX ?? 0,
3365
- offsetY: activeShadow.offsetY ?? baseShadow?.offsetY ?? 0,
3366
- blur: activeShadow.blur ?? baseShadow?.blur ?? 0,
3367
- color: activeShadow.color ?? baseShadow?.color ?? "#000000",
3368
- opacity: activeShadow.opacity ?? baseShadow?.opacity ?? 0.5
3369
- };
3346
+ if (activeShadow && typeof activeShadow === "object") {
3347
+ return {
3348
+ offsetX: activeShadow.offsetX ?? baseShadow?.offsetX ?? 0,
3349
+ offsetY: activeShadow.offsetY ?? baseShadow?.offsetY ?? 0,
3350
+ blur: activeShadow.blur ?? baseShadow?.blur ?? 0,
3351
+ color: activeShadow.color ?? baseShadow?.color ?? "#000000",
3352
+ opacity: activeShadow.opacity ?? baseShadow?.opacity ?? 0.5
3353
+ };
3354
+ }
3370
3355
  }
3371
3356
  if (baseShadow) {
3372
3357
  return {
@@ -3382,7 +3367,12 @@ function extractShadowConfig(asset, isActive) {
3382
3367
  function extractBackgroundConfig(asset, isActive, fontSize) {
3383
3368
  const fontBackground = asset.font?.background;
3384
3369
  const activeBackground = asset.active?.font?.background;
3385
- const bgColor = isActive && activeBackground ? activeBackground : fontBackground;
3370
+ let bgColor;
3371
+ if (isActive) {
3372
+ bgColor = activeBackground ?? fontBackground;
3373
+ } else {
3374
+ bgColor = fontBackground;
3375
+ }
3386
3376
  if (!bgColor) {
3387
3377
  return void 0;
3388
3378
  }
@@ -3433,7 +3423,7 @@ function extractCaptionBorder(asset) {
3433
3423
  };
3434
3424
  }
3435
3425
  function extractTextDecoration(asset, isActive) {
3436
- const baseDecoration = asset.font?.textDecoration;
3426
+ const baseDecoration = asset.style?.textDecoration;
3437
3427
  const activeDecoration = asset.active?.font?.textDecoration;
3438
3428
  if (isActive && activeDecoration !== void 0) {
3439
3429
  return activeDecoration === "none" ? void 0 : activeDecoration;
@@ -3450,7 +3440,6 @@ function extractAnimationConfig(asset) {
3450
3440
  }
3451
3441
  return {
3452
3442
  style: wordAnim.style ?? "highlight",
3453
- speed: wordAnim.speed ?? 1,
3454
3443
  direction: wordAnim.direction ?? "up"
3455
3444
  };
3456
3445
  }
@@ -5497,11 +5486,14 @@ function extractSvgDimensions(svgString) {
5497
5486
  }
5498
5487
 
5499
5488
  // src/core/canvas-text-measurer.ts
5500
- async function createCanvasTextMeasurer() {
5489
+ async function createCanvasTextMeasurer(letterSpacing) {
5501
5490
  const canvasMod = await import("canvas");
5502
5491
  const canvas = canvasMod.createCanvas(1, 1);
5503
5492
  const ctx = canvas.getContext("2d");
5504
5493
  ctx.textBaseline = "alphabetic";
5494
+ if (letterSpacing) {
5495
+ ctx.letterSpacing = `${letterSpacing}px`;
5496
+ }
5505
5497
  let lastFont = "";
5506
5498
  return (text, font) => {
5507
5499
  if (font !== lastFont) {
@@ -5870,7 +5862,7 @@ function findActiveWordIndex(store, groupWordIndices, timeMs) {
5870
5862
  }
5871
5863
  return -1;
5872
5864
  }
5873
- function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, speed) {
5865
+ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle) {
5874
5866
  if (groupWordIndices.length === 0) {
5875
5867
  return "idle";
5876
5868
  }
@@ -5887,7 +5879,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
5887
5879
  return "after";
5888
5880
  }
5889
5881
  if (TRANSITION_ANIMATION_STYLES.has(animationStyle)) {
5890
- const transitionDurationMs = (ANIMATION_DURATION_MS[animationStyle] ?? 200) / speed;
5882
+ const transitionDurationMs = ANIMATION_DURATION_MS[animationStyle] ?? 200;
5891
5883
  for (const idx of groupWordIndices) {
5892
5884
  const wordStart = store.startTimes[idx];
5893
5885
  if (timeMs >= wordStart && timeMs < wordStart + transitionDurationMs) {
@@ -5909,7 +5901,7 @@ function getAnimationPhase(store, groupWordIndices, timeMs, animationStyle, spee
5909
5901
  }
5910
5902
  return "before";
5911
5903
  }
5912
- function computeStateSignature(layout, timeMs, animationStyle, speed) {
5904
+ function computeStateSignature(layout, timeMs, animationStyle) {
5913
5905
  const groupIndex = findGroupIndexAtTime(layout.groups, timeMs);
5914
5906
  if (groupIndex === -1) {
5915
5907
  return { groupIndex: -1, activeWordIndex: -1, animationPhase: "idle" };
@@ -5920,21 +5912,20 @@ function computeStateSignature(layout, timeMs, animationStyle, speed) {
5920
5912
  layout.store,
5921
5913
  group.wordIndices,
5922
5914
  timeMs,
5923
- animationStyle,
5924
- speed
5915
+ animationStyle
5925
5916
  );
5926
5917
  return { groupIndex, activeWordIndex, animationPhase };
5927
5918
  }
5928
5919
  function signaturesMatch(a, b) {
5929
5920
  return a.groupIndex === b.groupIndex && a.activeWordIndex === b.activeWordIndex && a.animationPhase === b.animationPhase;
5930
5921
  }
5931
- function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight", speed = 1) {
5922
+ function createFrameSchedule(layout, durationMs, fps, animationStyle = "highlight") {
5932
5923
  const totalFrames = Math.max(2, Math.round(durationMs / 1e3 * fps) + 1);
5933
5924
  const renderFrames = [];
5934
5925
  let previousSignature = null;
5935
5926
  for (let frame = 0; frame < totalFrames; frame++) {
5936
5927
  const timeMs = frame / (totalFrames - 1) * durationMs;
5937
- const signature = computeStateSignature(layout, timeMs, animationStyle, speed);
5928
+ const signature = computeStateSignature(layout, timeMs, animationStyle);
5938
5929
  const isAnimating = signature.animationPhase === "animating";
5939
5930
  if (isAnimating || previousSignature === null || !signaturesMatch(signature, previousSignature)) {
5940
5931
  renderFrames.push({
@@ -6281,7 +6272,7 @@ var RichCaptionRenderer = class {
6281
6272
  }
6282
6273
  const font = asset.font;
6283
6274
  const style = asset.style;
6284
- const measureTextWidth = await createCanvasTextMeasurer();
6275
+ const measureTextWidth = await createCanvasTextMeasurer(style?.letterSpacing ?? 0);
6285
6276
  const fontSize = font?.size ?? 24;
6286
6277
  const lineHeight = style?.lineHeight ?? 1.2;
6287
6278
  const padding = extractCaptionPadding(asset);
@@ -6302,7 +6293,6 @@ var RichCaptionRenderer = class {
6302
6293
  fontFamily: font?.family ?? "Roboto",
6303
6294
  fontWeight: String(font?.weight ?? "400"),
6304
6295
  letterSpacing: style?.letterSpacing ?? 0,
6305
- wordSpacing: typeof style?.wordSpacing === "number" ? style.wordSpacing : 0,
6306
6296
  lineHeight,
6307
6297
  textTransform: style?.textTransform ?? "none",
6308
6298
  pauseThreshold: this.currentAsset?.pauseThreshold ?? 500,
@@ -6336,14 +6326,12 @@ var RichCaptionRenderer = class {
6336
6326
  throw new Error("No asset loaded. Call loadAsset() first.");
6337
6327
  }
6338
6328
  const animationStyle = this.extractAnimationStyle();
6339
- const animationSpeed = this.extractAnimationSpeed();
6340
6329
  const durationMs = duration * 1e3;
6341
6330
  const schedule = createFrameSchedule(
6342
6331
  this.currentLayout,
6343
6332
  durationMs,
6344
6333
  this.fps,
6345
- animationStyle,
6346
- animationSpeed
6334
+ animationStyle
6347
6335
  );
6348
6336
  const bgColor = options?.bgColor;
6349
6337
  const hasAlpha = !bgColor;
@@ -6504,13 +6492,11 @@ var RichCaptionRenderer = class {
6504
6492
  throw new Error("No asset loaded. Call loadAsset() first.");
6505
6493
  }
6506
6494
  const animationStyle = this.extractAnimationStyle();
6507
- const animationSpeed = this.extractAnimationSpeed();
6508
6495
  return createFrameSchedule(
6509
6496
  this.currentLayout,
6510
6497
  duration * 1e3,
6511
6498
  this.fps,
6512
- animationStyle,
6513
- animationSpeed
6499
+ animationStyle
6514
6500
  );
6515
6501
  }
6516
6502
  getStats() {
@@ -6548,10 +6534,6 @@ var RichCaptionRenderer = class {
6548
6534
  const wordAnim = this.currentAsset?.wordAnimation;
6549
6535
  return wordAnim?.style ?? "highlight";
6550
6536
  }
6551
- extractAnimationSpeed() {
6552
- const wordAnim = this.currentAsset?.wordAnimation;
6553
- return wordAnim?.speed ?? 1;
6554
- }
6555
6537
  logProgress(pct, framesProcessed, totalFrames, uniqueProcessed, uniqueTotal, fps, eta) {
6556
6538
  if (typeof process !== "undefined" && process.stderr) {
6557
6539
  process.stderr.write(
@@ -6864,6 +6846,7 @@ async function createTextEngine(opts = {}) {
6864
6846
  text: asset.text,
6865
6847
  width: (asset.width ?? width) - padding2.left - padding2.right,
6866
6848
  letterSpacing: asset.style?.letterSpacing ?? 0,
6849
+ wordSpacing: typeof asset.style?.wordSpacing === "number" ? asset.style.wordSpacing : 0,
6867
6850
  fontSize: main.size,
6868
6851
  lineHeight: asset.style?.lineHeight ?? 1.2,
6869
6852
  desc,
@@ -271,11 +271,6 @@ declare const richCaptionAssetSchema: z.ZodObject<{
271
271
  color: z.ZodDefault<z.ZodString>;
272
272
  opacity: z.ZodDefault<z.ZodNumber>;
273
273
  background: z.ZodOptional<z.ZodString>;
274
- textDecoration: z.ZodDefault<z.ZodEnum<{
275
- none: "none";
276
- underline: "underline";
277
- "line-through": "line-through";
278
- }>>;
279
274
  }, z.core.$strip>>;
280
275
  style: z.ZodOptional<z.ZodObject<{
281
276
  wordSpacing: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>, z.ZodString]>>>;
@@ -356,39 +351,38 @@ declare const richCaptionAssetSchema: z.ZodObject<{
356
351
  "line-through": "line-through";
357
352
  }>>;
358
353
  }, z.core.$strip>>;
359
- stroke: z.ZodOptional<z.ZodObject<{
354
+ stroke: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
360
355
  width: z.ZodOptional<z.ZodNumber>;
361
356
  color: z.ZodOptional<z.ZodString>;
362
357
  opacity: z.ZodOptional<z.ZodNumber>;
363
- }, z.core.$strip>>;
364
- shadow: z.ZodOptional<z.ZodObject<{
358
+ }, z.core.$strip>, z.ZodLiteral<"none">]>>;
359
+ shadow: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
365
360
  offsetX: z.ZodOptional<z.ZodNumber>;
366
361
  offsetY: z.ZodOptional<z.ZodNumber>;
367
362
  blur: z.ZodOptional<z.ZodNumber>;
368
363
  color: z.ZodOptional<z.ZodString>;
369
364
  opacity: z.ZodOptional<z.ZodNumber>;
370
- }, z.core.$strip>>;
365
+ }, z.core.$strip>, z.ZodLiteral<"none">]>>;
371
366
  scale: z.ZodDefault<z.ZodNumber>;
372
367
  }, z.core.$strict>>;
373
368
  wordAnimation: z.ZodOptional<z.ZodObject<{
374
369
  style: z.ZodDefault<z.ZodEnum<{
375
370
  typewriter: "typewriter";
376
371
  none: "none";
372
+ pop: "pop";
377
373
  karaoke: "karaoke";
378
374
  highlight: "highlight";
379
- pop: "pop";
380
375
  fade: "fade";
381
376
  slide: "slide";
382
377
  bounce: "bounce";
383
378
  }>>;
384
- speed: z.ZodDefault<z.ZodNumber>;
385
379
  direction: z.ZodDefault<z.ZodEnum<{
386
380
  right: "right";
387
381
  left: "left";
388
382
  up: "up";
389
383
  down: "down";
390
384
  }>>;
391
- }, z.core.$strict>>;
385
+ }, z.core.$strip>>;
392
386
  pauseThreshold: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
393
387
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
394
388
  src: z.ZodString;
@@ -414,11 +408,6 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
414
408
  color: z.ZodDefault<z.ZodString>;
415
409
  opacity: z.ZodDefault<z.ZodNumber>;
416
410
  background: z.ZodOptional<z.ZodString>;
417
- textDecoration: z.ZodDefault<z.ZodEnum<{
418
- none: "none";
419
- underline: "underline";
420
- "line-through": "line-through";
421
- }>>;
422
411
  }, z.core.$strip>>;
423
412
  style: z.ZodOptional<z.ZodObject<{
424
413
  wordSpacing: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>, z.ZodString]>>>;
@@ -499,39 +488,38 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
499
488
  "line-through": "line-through";
500
489
  }>>;
501
490
  }, z.core.$strip>>;
502
- stroke: z.ZodOptional<z.ZodObject<{
491
+ stroke: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
503
492
  width: z.ZodOptional<z.ZodNumber>;
504
493
  color: z.ZodOptional<z.ZodString>;
505
494
  opacity: z.ZodOptional<z.ZodNumber>;
506
- }, z.core.$strip>>;
507
- shadow: z.ZodOptional<z.ZodObject<{
495
+ }, z.core.$strip>, z.ZodLiteral<"none">]>>;
496
+ shadow: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
508
497
  offsetX: z.ZodOptional<z.ZodNumber>;
509
498
  offsetY: z.ZodOptional<z.ZodNumber>;
510
499
  blur: z.ZodOptional<z.ZodNumber>;
511
500
  color: z.ZodOptional<z.ZodString>;
512
501
  opacity: z.ZodOptional<z.ZodNumber>;
513
- }, z.core.$strip>>;
502
+ }, z.core.$strip>, z.ZodLiteral<"none">]>>;
514
503
  scale: z.ZodDefault<z.ZodNumber>;
515
504
  }, z.core.$strict>>;
516
505
  wordAnimation: z.ZodOptional<z.ZodObject<{
517
506
  style: z.ZodDefault<z.ZodEnum<{
518
507
  typewriter: "typewriter";
519
508
  none: "none";
509
+ pop: "pop";
520
510
  karaoke: "karaoke";
521
511
  highlight: "highlight";
522
- pop: "pop";
523
512
  fade: "fade";
524
513
  slide: "slide";
525
514
  bounce: "bounce";
526
515
  }>>;
527
- speed: z.ZodDefault<z.ZodNumber>;
528
516
  direction: z.ZodDefault<z.ZodEnum<{
529
517
  right: "right";
530
518
  left: "left";
531
519
  up: "up";
532
520
  down: "down";
533
521
  }>>;
534
- }, z.core.$strict>>;
522
+ }, z.core.$strip>>;
535
523
  pauseThreshold: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
536
524
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
537
525
  src: z.ZodString;
@@ -663,7 +651,6 @@ interface CaptionLayoutConfig {
663
651
  fontFamily: string;
664
652
  fontWeight: string | number;
665
653
  letterSpacing: number;
666
- wordSpacing: number;
667
654
  lineHeight: number;
668
655
  textTransform: "none" | "uppercase" | "lowercase" | "capitalize";
669
656
  pauseThreshold: number;
@@ -745,7 +732,6 @@ type AnimationStyle = "karaoke" | "highlight" | "pop" | "fade" | "slide" | "boun
745
732
  type AnimationDirection = "left" | "right" | "up" | "down";
746
733
  interface WordAnimationConfig {
747
734
  style: AnimationStyle;
748
- speed: number;
749
735
  direction: AnimationDirection;
750
736
  }
751
737
  interface WordAnimationState {
@@ -1183,7 +1169,7 @@ interface FrameSchedule {
1183
1169
  uniqueFrameCount: number;
1184
1170
  skipRatio: number;
1185
1171
  }
1186
- declare function createFrameSchedule(layout: CaptionLayout, durationMs: number, fps: number, animationStyle?: AnimationStyle, speed?: number): FrameSchedule;
1172
+ declare function createFrameSchedule(layout: CaptionLayout, durationMs: number, fps: number, animationStyle?: AnimationStyle): FrameSchedule;
1187
1173
 
1188
1174
  interface RichCaptionRendererOptions {
1189
1175
  width: number;
@@ -1252,7 +1238,6 @@ declare class RichCaptionRenderer {
1252
1238
  private mapVerticalAlign;
1253
1239
  private mapHorizontalAlign;
1254
1240
  private extractAnimationStyle;
1255
- private extractAnimationSpeed;
1256
1241
  private logProgress;
1257
1242
  private logCompletion;
1258
1243
  private checkMemoryUsage;