From 00dcaad206a9d110272c5e145695aec297feb299 Mon Sep 17 00:00:00 2001 From: Yunfei He Date: Thu, 28 May 2026 14:38:06 +0800 Subject: [PATCH] feat(runtime): provide shared animation scheduler per app, dispose on teardown --- packages/runtime/src/render.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/runtime/src/render.ts b/packages/runtime/src/render.ts index 8610b48..eaea8e3 100644 --- a/packages/runtime/src/render.ts +++ b/packages/runtime/src/render.ts @@ -20,6 +20,7 @@ import { createRoot, emitLayoutListeners, type TuiRoot, type TuiNode } from "./h import { attachYoga, detachYoga } from "./host/yoga.ts"; import { buildNodeOps } from "./host/node-ops.ts"; import { createCommitScheduler } from "./scheduler.ts"; +import { createAnimationScheduler } from "./animation-scheduler.ts"; import { paint, paintIsolated } from "./paint/paint.ts"; import { findStatics } from "./paint/static-channel.ts"; import { createFrameWriter } from "./io/frame-writer.ts"; @@ -28,6 +29,7 @@ import { AppContextKey, FocusContextKey, StdinContextKey, + AnimationSchedulerKey, type AppContext, type CursorPosition, type FocusContext, @@ -171,6 +173,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp let mountedGetLastOutput: (() => string) | null = null; let mountedRestoreConsole: (() => void) | null = null; let mountedScheduler: ReturnType | null = null; + let mountedAnimationScheduler: ReturnType | null = null; let mountedCommit: (() => void) | null = null; let mountedAlternateScreen = false; let mountedClear: (() => void) | null = null; @@ -248,6 +251,10 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp } catch { // Vue's unmount may throw on double-unmount; swallow for idempotency. } + // Dispose the animation scheduler after Vue unmount: each useAnimation's + // onScopeDispose has already unsubscribed, so this is an idempotent backstop. + mountedAnimationScheduler?.dispose(); + mountedAnimationScheduler = null; if (mountedKittyController) { mountedKittyController.dispose(); mountedKittyController = null; @@ -615,6 +622,9 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp baseApp.provide(AppContextKey, appContext); baseApp.provide(FocusContextKey, focusContext); baseApp.provide(StdinContextKey, stdinController); + const animationScheduler = createAnimationScheduler(); + mountedAnimationScheduler = animationScheduler; + baseApp.provide(AnimationSchedulerKey, animationScheduler); if (typeof __VUE_TUI_DEV__ !== "undefined" && __VUE_TUI_DEV__) { baseApp.provide(DevStateKey, devState); }