docs(divergences)+refactor(scheduler): prune mis-scoped idioms, fix throttle figure, drop dead constant (#133)
Audit of the "byte-identical reconciler/runtime mechanics" subsection, each entry verified against vue-tui + Ink v7.0.4 source. Doc: - Rewrite the TuiComment/Transform entry cause-first (Vue materializes a comment placeholder where React renders nothing -> vue makes it inert) for clarity. - Fix the commit-throttle figure: it is `ceil(1000/maxFps)` = 34ms at the default maxFps=30, not "~32ms". The 32 was vue's own dead fallback constant, never the production value; Ink has no 32 either. - Drop the keyed-lists (LIS) entry: it restated the section header and guarded no vue-authored code (patchKeyedChildren is upstream Vue). - Drop the wrapText-truncate and animation-scheduler entries: both are vue-tui implementation choices, not Vue-vs-React framework differences, and both are already explained by their in-code comments. Code (no behavior change; verified by `vp run ready`): - Remove the dead `DEFAULT_THROTTLE_MS = 32` fallback in scheduler.ts. Production always passes throttleMs (render.ts derives it from maxFps) and the immediate path never reads it, so the 32 fallback never gated a frame. Make throttleMs required; render.ts always passes it (0 when unthrottled). - Tighten the animation-scheduler ceil comment (drop the "busy-loop" overstatement; the fractional-delay truncation it describes is real and keeps the Math.ceil). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,9 +55,10 @@ export function createAnimationScheduler(renderThrottleMs = 0): AnimationSchedul
|
||||
}
|
||||
if (earliest === Number.POSITIVE_INFINITY) return;
|
||||
scheduledDueTime = earliest;
|
||||
// Round up: setTimeout truncates fractional delays, which would fire the
|
||||
// timer before `earliest`. onTick then skips (now < nextDueTime) and
|
||||
// reschedules a ~0ms delay, busy-looping until the clock catches up.
|
||||
// Round up: setTimeout truncates a fractional delay, so the timer would fire
|
||||
// just before `earliest`; onTick then finds nothing due (now < nextDueTime),
|
||||
// skips, and reschedules — one wasted wakeup per frame. Ceiling makes it fire
|
||||
// at-or-after the due time, so the frame lands on the first wakeup.
|
||||
const delay = Math.ceil(Math.max(0, earliest - performance.now()));
|
||||
timer = setTimeout(onTick, delay);
|
||||
}
|
||||
|
||||
@@ -1008,13 +1008,13 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
const unthrottled = debug || isScreenReaderEnabled;
|
||||
const renderThrottleMs = !unthrottled && maxFps > 0 ? Math.max(1, Math.ceil(1000 / maxFps)) : 0;
|
||||
|
||||
const schedulerOptions: { immediate: boolean; throttleMs?: number } = {
|
||||
// Unthrottled (debug / screen-reader) commits fire every tick, so the
|
||||
// throttle window is unused there — renderThrottleMs is already 0. Otherwise
|
||||
// it's the maxFps-derived window (34ms at the default maxFps=30).
|
||||
const scheduler = createCommitScheduler(commit, {
|
||||
immediate: unthrottled,
|
||||
};
|
||||
if (!unthrottled) {
|
||||
schedulerOptions.throttleMs = renderThrottleMs;
|
||||
}
|
||||
const scheduler = createCommitScheduler(commit, schedulerOptions);
|
||||
throttleMs: renderThrottleMs,
|
||||
});
|
||||
mountedScheduler = scheduler;
|
||||
mountedCommit = commit;
|
||||
scheduledCommit = scheduler.schedule;
|
||||
|
||||
@@ -12,19 +12,21 @@ export interface CommitScheduler {
|
||||
export interface CommitSchedulerOptions {
|
||||
/** Disable time-based throttle (used in tests / debug mode). */
|
||||
immediate?: boolean;
|
||||
/** Override throttle interval in ms. Takes precedence over the default 32ms. */
|
||||
throttleMs?: number;
|
||||
/**
|
||||
* Throttle window in ms — the leading+trailing commit interval. The caller
|
||||
* derives it from `maxFps` (`ceil(1000/maxFps)`, i.e. 34ms at the default
|
||||
* maxFps=30, matching Ink). Unused when `immediate` is set (commits fire
|
||||
* every tick); pass 0 there.
|
||||
*/
|
||||
throttleMs: number;
|
||||
}
|
||||
|
||||
/** Default minimum interval between commits (~30fps). */
|
||||
const DEFAULT_THROTTLE_MS = 32;
|
||||
|
||||
export function createCommitScheduler(
|
||||
commit: () => void,
|
||||
options: CommitSchedulerOptions = {},
|
||||
options: CommitSchedulerOptions,
|
||||
): CommitScheduler {
|
||||
const immediate = options.immediate ?? false;
|
||||
const throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS;
|
||||
const throttleMs = options.throttleMs;
|
||||
let scheduled = false;
|
||||
// Multiple concurrent flush() callers can be waiting on the same pending
|
||||
// commit; settle all of them rather than overwriting a single resolver.
|
||||
|
||||
Reference in New Issue
Block a user