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:
Yunfei He
2026-06-04 13:58:52 +08:00
committed by GitHub
parent 11e9a2e684
commit 51cca1cf05
4 changed files with 38 additions and 43 deletions
+4 -3
View File
@@ -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);
}
+6 -6
View File
@@ -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;
+9 -7
View File
@@ -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.