@real-music-packages/web-core 0.21.0 → 0.22.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.
@@ -678,12 +678,11 @@ declare const notationFactory: LayerFactory<NotationProps>;
678
678
 
679
679
  interface ScrollCursorProps {
680
680
  /**
681
- * Cursor pacing mode. Default `'audio'` (foolproof): the cursor is driven
682
- * note-by-note off the audio clock against each note's `onsetMs`, so it lands
683
- * on every note as it sounds and can never race/desync the full score width —
684
- * correct by construction regardless of timeline. `'linear'` is the legacy
685
- * measure-linear sweep paced over `musicMs` (RSR's original behaviour) — kept
686
- * for back-compat; prefer leaving this unset.
681
+ * DEPRECATED / back-compat only. The cursor is ALWAYS audio-onset locked now —
682
+ * driven note-by-note off the audio clock against each note's `onsetMs` (see
683
+ * `ctx.score`). Both `'audio'` and `'linear'` resolve to that single onset path;
684
+ * `'linear'` no longer re-enables the legacy measure-linear sweep (removed
685
+ * that was the drift footgun). Prefer leaving this unset.
687
686
  */
688
687
  mode?: 'audio' | 'linear';
689
688
  /**
@@ -704,9 +703,9 @@ interface ScrollCursorProps {
704
703
  /** Opening-zoom duration in ms before the music/cursor start (RSR INTRO_MS=900).
705
704
  * During this lead-in the follow window is held at the start. Default 900. */
706
705
  openingZoomMs?: number;
707
- /** Total music length in ms (RSR musicMs). Required by `'linear'` to pace
708
- * camProgress; in `'audio'` mode it is optional (the onset clock drives pacing)
709
- * and used only as a fallback when the score has no notes. */
706
+ /** DEPRECATED. Total music length in ms (RSR musicMs). No longer used to
707
+ * position the cursor (the onset clock drives all pacing); accepted for
708
+ * back-compat and ignored. */
710
709
  musicMs?: number;
711
710
  /** Cursor stroke colour. Defaults to theme.accent. */
712
711
  color?: string;
@@ -818,25 +818,18 @@ var notationFactory = {
818
818
  };
819
819
 
820
820
  // src/scene/layers/scrollCursor.ts
821
- function followLayoutFor(ctx, camProgress01, scrollMode) {
821
+ function followLayoutFor(ctx, progress012, scrollMode) {
822
822
  const eng = getNotationEngraving(ctx);
823
823
  if (!eng) return null;
824
824
  const nBars = measureCount(eng.rendered);
825
825
  if (nBars <= 0) return eng.base;
826
- const focusBox = scrollMode === "vstack" ? vstackFollowBox(eng.rendered, camProgress01) : followBoxAt(eng.rendered, followWindowStart(eng.rendered, camProgress01));
826
+ const focusBox = scrollMode === "vstack" ? vstackFollowBox(eng.rendered, progress012) : followBoxAt(eng.rendered, followWindowStart(eng.rendered, progress012));
827
827
  return notationLayout(eng.rendered, ctx.W, ctx.H, eng.bandTop, eng.bandHeight, { focusBox });
828
828
  }
829
829
  function scrollCursorLayer() {
830
- let mode = "audio";
831
830
  let scrollMode = "hstack";
832
- let musicMs = 0;
833
831
  let openingZoomMs = 900;
834
832
  let color;
835
- function camProgress(tMs) {
836
- const phMs = tMs - openingZoomMs;
837
- if (phMs < 0 || musicMs <= 0) return 0;
838
- return Math.min(1, phMs / musicMs);
839
- }
840
833
  function onsetsFor(ctx) {
841
834
  const notes = ctx.score?.notes;
842
835
  return notes && notes.length ? distinctOnsets(notes) : [];
@@ -859,9 +852,7 @@ function scrollCursorLayer() {
859
852
  return (lo + segFrac) / (n - 1);
860
853
  }
861
854
  function followProgress(ctx, tMs) {
862
- if (mode === "linear") return camProgress(tMs);
863
- const onsets = onsetsFor(ctx);
864
- return onsets.length ? notesProgress(onsets, tMs) : camProgress(tMs);
855
+ return notesProgress(onsetsFor(ctx), tMs);
865
856
  }
866
857
  function layoutAt(ctx, tMs) {
867
858
  const eng = getNotationEngraving(ctx);
@@ -870,9 +861,7 @@ function scrollCursorLayer() {
870
861
  return {
871
862
  key: "scroll-cursor",
872
863
  init(ctx, props) {
873
- mode = props.mode ?? "audio";
874
864
  scrollMode = props.scrollMode ?? "hstack";
875
- musicMs = props.musicMs ?? 0;
876
865
  openingZoomMs = props.openingZoomMs ?? 900;
877
866
  color = props.color;
878
867
  setFollowLayoutProvider(ctx, layoutAt);
@@ -881,16 +870,12 @@ function scrollCursorLayer() {
881
870
  const eng = getNotationEngraving(ctx);
882
871
  if (!eng) return;
883
872
  const layout = layoutAt(ctx, tMs);
873
+ const onsets = onsetsFor(ctx);
884
874
  let line;
885
- const onsets = mode === "audio" ? onsetsFor(ctx) : [];
886
- if (mode === "audio" && onsets.length) {
887
- if (scrollMode === "vstack") {
888
- line = vstackAudioPlayheadLine(layout, onsets, tMs, measureCount(eng.rendered));
889
- } else {
890
- line = audioPlayheadLine(layout, onsets, tMs);
891
- }
875
+ if (scrollMode === "vstack") {
876
+ line = vstackAudioPlayheadLine(layout, onsets, tMs, measureCount(eng.rendered));
892
877
  } else {
893
- line = playheadLine(layout, camProgress(tMs));
878
+ line = audioPlayheadLine(layout, onsets, tMs);
894
879
  }
895
880
  if (!line) return;
896
881
  const c = ctx.ctx2d;
@@ -917,8 +902,6 @@ var scrollCursorFactory = {
917
902
  errs.push('scroll-cursor.mode must be "audio" | "linear"');
918
903
  if (p.scrollMode != null && p.scrollMode !== "hstack" && p.scrollMode !== "vstack")
919
904
  errs.push('scroll-cursor.scrollMode must be "hstack" | "vstack"');
920
- if (p.mode === "linear" && (typeof p.musicMs !== "number" || !(p.musicMs > 0)))
921
- errs.push('scroll-cursor.musicMs must be a positive number for mode:"linear"');
922
905
  if (p.musicMs != null && (typeof p.musicMs !== "number" || !(p.musicMs > 0)))
923
906
  errs.push("scroll-cursor.musicMs must be a positive number");
924
907
  if (p.followBars != null && (typeof p.followBars !== "number" || p.followBars < 1))