feat(studio): fill the ruler with ticks across the full timeline width

The ruler now generates ticks across the visible width instead of stopping
at the composition end, so a zoomed-out ruler stays labelled to the right
edge. The tick interval stays pps-driven, so spacing is unchanged. Logic
lives in a new generateVisibleTicks helper to keep Timeline.tsx under the
600-line cap.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-08 23:46:30 -04:00
parent eb31e86d4f
commit 6f0bf75b69
2 changed files with 19 additions and 3 deletions
@@ -27,7 +27,7 @@ import { buildStackingTimelineLayers, insertPreviewTrackOrder } from "./timeline
import { getTimelineLayerGroupHeaderTotalHeight } from "./TimelineLayerGroupHeader";
import {
GUTTER,
generateTicks,
generateVisibleTicks,
getTimelineCanvasHeight,
shouldShowTimelineShortcutHint,
computeTimelineBasisDuration,
@@ -362,8 +362,8 @@ export const Timeline = memo(function Timeline({
});
const { major, minor } = useMemo(
() => generateTicks(effectiveDuration, pps),
[effectiveDuration, pps],
() => generateVisibleTicks(effectiveDuration, pps, viewportWidth, GUTTER),
[effectiveDuration, pps, viewportWidth],
);
const majorTickInterval = major.length >= 2 ? major[1] - major[0] : effectiveDuration;
@@ -87,6 +87,22 @@ export function generateTicks(
return { major, minor };
}
/**
* Ticks spanning the full visible ruler width, not just the composition, so a
* zoomed-out ruler stays filled with labels instead of ending mid-panel. The
* major/minor interval is driven by pixelsPerSecond (pixel spacing), so widening
* the range keeps spacing identical — it only adds ticks past the content end.
*/
export function generateVisibleTicks(
effectiveDuration: number,
pixelsPerSecond: number,
viewportWidth: number,
gutter: number,
): { major: number[]; minor: number[] } {
const visible = viewportWidth > gutter ? (viewportWidth - gutter) / pixelsPerSecond : 0;
return generateTicks(Math.max(effectiveDuration, visible), pixelsPerSecond);
}
export function formatTimelineTickLabel(time: number, duration: number, majorInterval: number) {
if (!Number.isFinite(time)) return "0:00";
const safeTime = Math.max(0, time);