import { readActivePrefsSlot, type ActivePrefs } from './active-prefs.svelte'; import type { MotionEffective, MotionSource } from '$libs/motion'; /** * The `motion` slot of an open `ActivePrefs`, as the `MotionSource` port the * animation runtimes consume. * * This is the ONE place the effective reduced-motion value is decided for * everything downstream: the motion engine, the scene engine, sema's haptic * channel and every eidos component read it from here, so an explicit * `motion: 'allow'` reaches them all and a `reduce` degrades them all. Asking * the OS again (`ActiveDom.prefersReducedMotion`) is asking a question prefs * has already answered — the media query is an ENVIRONMENT hint, and * `resolveMotion` folded it with the user's intent long before this call. * * The slot is read DEFENSIVELY (the shape `dom-projection.ts` uses): an app is * free to compose a schema without a `motion` dimension, and the answer then is * `undefined` — no source, so each consumer keeps its own documented default * rather than inventing a policy nobody declared. * * Reads go through `slot.get()` on every call: the slot is `$state`-backed, so * a caller inside a `$derived` / `$effect` tracks it, and a caller outside one * still sees the current value. */ export function createMotionSourceFromPrefs(prefs: ActivePrefs): MotionSource | undefined { const slot = readActivePrefsSlot(prefs, 'motion'); if (!slot) return undefined; return { get: () => slot.get(), onChange: (handler) => slot.onChange(handler) }; }