@subvo/renderer 1.2.1 → 1.2.3

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.
@@ -18,6 +18,7 @@ type RenderLine = {
18
18
  startMs: number;
19
19
  endMs: number;
20
20
  mode?: RenderMode;
21
+ motion?: RenderMotion;
21
22
  breakHints?: string[];
22
23
  words: RenderWord[];
23
24
  };
@@ -39,7 +40,13 @@ type RenderMetadata = {
39
40
  };
40
41
  type RenderPosition = "top_safe" | "center" | "bottom_safe";
41
42
  type RenderPreset = "fire" | "clean" | "luxury" | "pop" | "ghost";
42
- type RenderMode = "phrase_highlight" | "progressive_build";
43
+ type RenderMode = "karaoke" | "phrase_highlight" | "static" | "progressive_build";
44
+ type RenderMotion = {
45
+ policy?: string;
46
+ resolvedMode?: RenderMode;
47
+ reason?: string;
48
+ metrics?: Record<string, unknown>;
49
+ };
43
50
  type RenderStyle = {
44
51
  fontFamily: string;
45
52
  fontSize: number;
@@ -70,4 +77,4 @@ declare function renderFrame(ctx: CanvasRenderingContext2D, renderJob: RenderJob
70
77
 
71
78
  declare const VERSION: string;
72
79
 
73
- export { type RenderJob, type RenderLayoutHints, type RenderLine, type RenderMetadata, type RenderPosition, type RenderPreset, type RenderStyle, type RenderWord, type SchemaVersion, VERSION, renderFrame };
80
+ export { type RenderJob, type RenderLayoutHints, type RenderLine, type RenderMetadata, type RenderMotion, type RenderPosition, type RenderPreset, type RenderStyle, type RenderWord, type SchemaVersion, VERSION, renderFrame };
@@ -345,10 +345,11 @@ var DEFAULT_TRANSITION_MS = 150;
345
345
  var MIN_TRANSITION_MS = 120;
346
346
  var MAX_TRANSITION_MS = 180;
347
347
  var TOP_UNSAFE_RATIO = 0.14;
348
- var BOTTOM_UNSAFE_RATIO = 0.22;
348
+ var BOTTOM_UNSAFE_RATIO = 0.24;
349
349
  var TOP_SAFE_ANCHOR_RATIO = 0.22;
350
350
  var CENTER_ANCHOR_RATIO = 0.5;
351
- var BOTTOM_SAFE_ANCHOR_RATIO = 0.7;
351
+ var BOTTOM_SAFE_ANCHOR_RATIO = 0.68;
352
+ var PHRASE_HIGHLIGHT_MS = 180;
352
353
  function renderFrame(ctx, renderJob, timeMs) {
353
354
  var _a;
354
355
  const { playRes, style } = renderJob;
@@ -394,14 +395,15 @@ function renderFrame(ctx, renderJob, timeMs) {
394
395
  let x = centerX - row.width / 2;
395
396
  const rowY = baselineY + rowIndex * lineStep;
396
397
  for (const { word, text, width } of row.words) {
398
+ const motionMode = resolveLineMotionMode(entry.line);
397
399
  const visibleWord = isWordVisible(entry.line, word, timeMs);
398
400
  if (visibleWord) {
399
- const activeWord = timeMs >= word.startMs && timeMs <= word.endMs;
400
- const emphasis = (_a = word.emphasis) != null ? _a : 0;
401
- const progress = activeWord ? computeWordProgress(word, timeMs) : 0;
402
- const impactProgress = activeWord ? computeImpactProgress(word, timeMs) : 0;
403
- const pulseScale = computePulseScale(style, progress);
404
- const bounceScale = shouldBounceWord(style, emphasis) ? computeImpactScale(impactProgress) : 1;
401
+ const activeWord = isWordActive(entry.line, word, timeMs, motionMode);
402
+ const emphasis = motionMode === "static" ? 0 : (_a = word.emphasis) != null ? _a : 0;
403
+ const progress = activeWord ? computeMotionProgress(entry.line, word, timeMs, motionMode) : 0;
404
+ const impactProgress = activeWord && motionMode === "karaoke" ? computeImpactProgress(word, timeMs) : 0;
405
+ const pulseScale = motionMode === "static" ? 1 : computePulseScale(style, progress);
406
+ const bounceScale = shouldBounceWord(style, emphasis, motionMode) ? computeImpactScale(impactProgress) : 1;
405
407
  const scaleFactor = pulseScale * bounceScale;
406
408
  drawWord(
407
409
  ctx,
@@ -431,9 +433,8 @@ function resolveTransitionMs(style) {
431
433
  );
432
434
  }
433
435
  function computeLineAlpha(line, timeMs, transitionMs) {
434
- const padding = transitionMs / 2;
435
- const visibleStart = line.startMs - padding;
436
- const visibleEnd = line.endMs + padding;
436
+ const visibleStart = line.startMs;
437
+ const visibleEnd = line.endMs;
437
438
  if (timeMs < visibleStart || timeMs > visibleEnd) {
438
439
  return 0;
439
440
  }
@@ -447,7 +448,36 @@ function isWordVisible(line, word, timeMs) {
447
448
  }
448
449
  return timeMs >= word.startMs;
449
450
  }
450
- function shouldBounceWord(style, emphasis) {
451
+ function resolveLineMotionMode(line) {
452
+ var _a;
453
+ const motionMode = (_a = line.motion) == null ? void 0 : _a.resolvedMode;
454
+ if (motionMode === "karaoke" || motionMode === "phrase_highlight" || motionMode === "static") {
455
+ return motionMode;
456
+ }
457
+ if (line.mode === "static" || line.mode === "karaoke" || line.mode === "progressive_build") {
458
+ return line.mode;
459
+ }
460
+ return "phrase_highlight";
461
+ }
462
+ function isWordActive(line, word, timeMs, motionMode) {
463
+ if (motionMode === "static") {
464
+ return false;
465
+ }
466
+ if (motionMode === "phrase_highlight") {
467
+ return timeMs >= line.startMs && timeMs <= line.startMs + PHRASE_HIGHLIGHT_MS;
468
+ }
469
+ return timeMs >= word.startMs && timeMs <= word.endMs;
470
+ }
471
+ function computeMotionProgress(line, word, timeMs, motionMode) {
472
+ if (motionMode === "phrase_highlight") {
473
+ return clamp((timeMs - line.startMs) / PHRASE_HIGHLIGHT_MS);
474
+ }
475
+ return computeWordProgress(word, timeMs);
476
+ }
477
+ function shouldBounceWord(style, emphasis, motionMode) {
478
+ if (motionMode !== "karaoke") {
479
+ return false;
480
+ }
451
481
  if (style.preset === "ghost") {
452
482
  return false;
453
483
  }
@@ -481,7 +511,7 @@ function clampBaselineY(baselineY, rowCount, ascent, descent, lineStep, safeTop,
481
511
  }
482
512
 
483
513
  // src/index.ts
484
- var injectedVersion = "1.2.1".length > 0 ? "1.2.1" : "dev";
514
+ var injectedVersion = "1.2.2".length > 0 ? "1.2.2" : "dev";
485
515
  var VERSION = injectedVersion;
486
516
  export {
487
517
  VERSION,
@@ -372,10 +372,11 @@ var SubvoRenderer = (() => {
372
372
  var MIN_TRANSITION_MS = 120;
373
373
  var MAX_TRANSITION_MS = 180;
374
374
  var TOP_UNSAFE_RATIO = 0.14;
375
- var BOTTOM_UNSAFE_RATIO = 0.22;
375
+ var BOTTOM_UNSAFE_RATIO = 0.24;
376
376
  var TOP_SAFE_ANCHOR_RATIO = 0.22;
377
377
  var CENTER_ANCHOR_RATIO = 0.5;
378
- var BOTTOM_SAFE_ANCHOR_RATIO = 0.7;
378
+ var BOTTOM_SAFE_ANCHOR_RATIO = 0.68;
379
+ var PHRASE_HIGHLIGHT_MS = 180;
379
380
  function renderFrame(ctx, renderJob, timeMs) {
380
381
  var _a;
381
382
  const { playRes, style } = renderJob;
@@ -421,14 +422,15 @@ var SubvoRenderer = (() => {
421
422
  let x = centerX - row.width / 2;
422
423
  const rowY = baselineY + rowIndex * lineStep;
423
424
  for (const { word, text, width } of row.words) {
425
+ const motionMode = resolveLineMotionMode(entry.line);
424
426
  const visibleWord = isWordVisible(entry.line, word, timeMs);
425
427
  if (visibleWord) {
426
- const activeWord = timeMs >= word.startMs && timeMs <= word.endMs;
427
- const emphasis = (_a = word.emphasis) != null ? _a : 0;
428
- const progress = activeWord ? computeWordProgress(word, timeMs) : 0;
429
- const impactProgress = activeWord ? computeImpactProgress(word, timeMs) : 0;
430
- const pulseScale = computePulseScale(style, progress);
431
- const bounceScale = shouldBounceWord(style, emphasis) ? computeImpactScale(impactProgress) : 1;
428
+ const activeWord = isWordActive(entry.line, word, timeMs, motionMode);
429
+ const emphasis = motionMode === "static" ? 0 : (_a = word.emphasis) != null ? _a : 0;
430
+ const progress = activeWord ? computeMotionProgress(entry.line, word, timeMs, motionMode) : 0;
431
+ const impactProgress = activeWord && motionMode === "karaoke" ? computeImpactProgress(word, timeMs) : 0;
432
+ const pulseScale = motionMode === "static" ? 1 : computePulseScale(style, progress);
433
+ const bounceScale = shouldBounceWord(style, emphasis, motionMode) ? computeImpactScale(impactProgress) : 1;
432
434
  const scaleFactor = pulseScale * bounceScale;
433
435
  drawWord(
434
436
  ctx,
@@ -458,9 +460,8 @@ var SubvoRenderer = (() => {
458
460
  );
459
461
  }
460
462
  function computeLineAlpha(line, timeMs, transitionMs) {
461
- const padding = transitionMs / 2;
462
- const visibleStart = line.startMs - padding;
463
- const visibleEnd = line.endMs + padding;
463
+ const visibleStart = line.startMs;
464
+ const visibleEnd = line.endMs;
464
465
  if (timeMs < visibleStart || timeMs > visibleEnd) {
465
466
  return 0;
466
467
  }
@@ -474,7 +475,36 @@ var SubvoRenderer = (() => {
474
475
  }
475
476
  return timeMs >= word.startMs;
476
477
  }
477
- function shouldBounceWord(style, emphasis) {
478
+ function resolveLineMotionMode(line) {
479
+ var _a;
480
+ const motionMode = (_a = line.motion) == null ? void 0 : _a.resolvedMode;
481
+ if (motionMode === "karaoke" || motionMode === "phrase_highlight" || motionMode === "static") {
482
+ return motionMode;
483
+ }
484
+ if (line.mode === "static" || line.mode === "karaoke" || line.mode === "progressive_build") {
485
+ return line.mode;
486
+ }
487
+ return "phrase_highlight";
488
+ }
489
+ function isWordActive(line, word, timeMs, motionMode) {
490
+ if (motionMode === "static") {
491
+ return false;
492
+ }
493
+ if (motionMode === "phrase_highlight") {
494
+ return timeMs >= line.startMs && timeMs <= line.startMs + PHRASE_HIGHLIGHT_MS;
495
+ }
496
+ return timeMs >= word.startMs && timeMs <= word.endMs;
497
+ }
498
+ function computeMotionProgress(line, word, timeMs, motionMode) {
499
+ if (motionMode === "phrase_highlight") {
500
+ return clamp((timeMs - line.startMs) / PHRASE_HIGHLIGHT_MS);
501
+ }
502
+ return computeWordProgress(word, timeMs);
503
+ }
504
+ function shouldBounceWord(style, emphasis, motionMode) {
505
+ if (motionMode !== "karaoke") {
506
+ return false;
507
+ }
478
508
  if (style.preset === "ghost") {
479
509
  return false;
480
510
  }
@@ -508,7 +538,7 @@ var SubvoRenderer = (() => {
508
538
  }
509
539
 
510
540
  // src/index.ts
511
- var injectedVersion = "1.2.1".length > 0 ? "1.2.1" : "dev";
541
+ var injectedVersion = "1.2.2".length > 0 ? "1.2.2" : "dev";
512
542
  var VERSION = injectedVersion;
513
543
  return __toCommonJS(src_exports);
514
544
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subvo/renderer",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "main": "./dist/renderer.esm.js",
6
6
  "module": "./dist/renderer.esm.js",