From e737547401bf0f4a8f8f8321e40a32dd8fe70356 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:14:04 -0400 Subject: [PATCH 01/12] refactor(animate): duck-type the mutable-element check `instanceof HTMLElement` needs globals that do not exist under SSR, and fails for elements from another realm such as an iframe. Check for the capability the call site actually needs instead: an inline style we can write to. Hoisted above the ref-counting entry points, which are about to need it. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/animate/properties/transform-tracker.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/animate/properties/transform-tracker.ts b/src/lib/animate/properties/transform-tracker.ts index 0cb3074..1584b3e 100644 --- a/src/lib/animate/properties/transform-tracker.ts +++ b/src/lib/animate/properties/transform-tracker.ts @@ -58,6 +58,15 @@ const forEachBit = (bits: number, fn: (i: number) => void): void => { } }; +/** + * Can we write inline styles to this node? Duck-typed rather than an + * `instanceof HTMLElement` check: the capability is what we actually need, and + * it also holds for elements from another realm, such as an iframe, and under + * SSR, where those globals do not exist. + */ +const isMutableElement = (n: Element): n is MotionElement => + typeof (n as Partial).style?.setProperty === 'function'; + /** Mark that an element has started a WAAPI transform animation. */ export const registerTransformAnimation = (element: Element, bits: number): void => { let counts = activeTransformCounts.get(element); @@ -107,9 +116,6 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo type SavedProp = { name: string; saved: SavedStyleProp }; type Suppressed = { node: MotionElement; props: SavedProp[] }; -const isMutableElement = (n: Element): n is MotionElement => - n instanceof HTMLElement || n instanceof SVGElement; - export const measureWithoutAncestorTransforms = ( el: Element, { suppressSelf = true }: { suppressSelf?: boolean } = {} From 546e3d095e4ad57e83b9355d69f5545fafa95ba5 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:14:52 -0400 Subject: [PATCH 02/12] perf(animate): promote transform-animated elements to their own layer Chromium promotes an element for an active transform animation, but `animate()` drives transforms through custom properties, and those earn no layer. Every frame of a dialog morph therefore repainted the whole box, shadow included. Set `will-change: translate, scale, rotate` while a transform channel is in flight and restore the caller's own value once the last one ends. The existing ref-count already says exactly when that is. A wired element has non-`none` `translate`/`scale` regardless, so it was already a stacking context and nothing observable changes. Co-Authored-By: Claude Opus 5 (1M context) --- .../transform-tracker.svelte.test.ts | 21 +++++++++++ .../animate/properties/transform-tracker.ts | 35 +++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/lib/animate/properties/transform-tracker.svelte.test.ts b/src/lib/animate/properties/transform-tracker.svelte.test.ts index d4dec42..fffc7e0 100644 --- a/src/lib/animate/properties/transform-tracker.svelte.test.ts +++ b/src/lib/animate/properties/transform-tracker.svelte.test.ts @@ -44,3 +44,24 @@ describe('measureWithoutAncestorTransforms()', () => { expect(observed[1]).toEqual(['20px', '']); }); }); + +describe('will-change hint', () => { + it('is set for the first channel, held across overlap, and restored after the last', () => { + const element = document.createElement('div'); + document.body.appendChild(element); + mounted.push(element); + element.style.setProperty('will-change', 'opacity'); + const x = VAR_BIT['--motion-x']!; + const y = VAR_BIT['--motion-y']!; + + registerTransformAnimation(element, x); + expect(element.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + registerTransformAnimation(element, y); + deregisterTransformAnimation(element, x); + expect(element.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + deregisterTransformAnimation(element, y); + expect(element.style.getPropertyValue('will-change')).toBe('opacity'); + }); +}); diff --git a/src/lib/animate/properties/transform-tracker.ts b/src/lib/animate/properties/transform-tracker.ts index 1584b3e..d3ae75b 100644 --- a/src/lib/animate/properties/transform-tracker.ts +++ b/src/lib/animate/properties/transform-tracker.ts @@ -6,6 +6,10 @@ * has a running `animate()` transform animation, so `getBoundingClientRect()` * returns the element's "at rest" layout position even mid-animation. * + * The tracker also owns the `will-change` hint that earns a transform-animated + * element its own compositor layer, since the same ref-count says exactly when + * to set and clear it. + * * The tracker is intentionally separate from the property registry so the * static registry data and the dynamic runtime state live in different modules. */ @@ -60,19 +64,39 @@ const forEachBit = (bits: number, fn: (i: number) => void): void => { /** * Can we write inline styles to this node? Duck-typed rather than an - * `instanceof HTMLElement` check: the capability is what we actually need, and - * it also holds for elements from another realm, such as an iframe, and under - * SSR, where those globals do not exist. + * `instanceof HTMLElement` check: the ref-counting entry points also run under + * SSR, where those globals do not exist, and the capability is what we actually + * need — it also holds for elements from another realm, such as an iframe. */ const isMutableElement = (n: Element): n is MotionElement => typeof (n as Partial).style?.setProperty === 'function'; +/** True while the element has at least one `animate()` transform channel running. */ +export const hasActiveTransforms = (element: Element): boolean => + (activeTransformBits.get(element) ?? 0) !== 0; + +const WILL_CHANGE = 'will-change'; +/** + * Hint the three properties the motion templates drive. Chromium promotes an + * element for an active *transform* animation, but a custom-property animation + * earns no layer, so without this the element repaints every frame — shadows, + * borders and all. The element already has non-`none` `translate`/`scale` once + * wired, so it is a stacking context either way and the hint changes nothing + * observable. + */ +const WILL_CHANGE_VALUE = 'translate, scale, rotate'; +const savedWillChange = new WeakMap(); + /** Mark that an element has started a WAAPI transform animation. */ export const registerTransformAnimation = (element: Element, bits: number): void => { let counts = activeTransformCounts.get(element); if (!counts) { counts = new Uint8Array(N_TRANSFORM_VARS); activeTransformCounts.set(element, counts); + if (isMutableElement(element)) { + savedWillChange.set(element, saveStyleProp(element.style, WILL_CHANGE)); + element.style.setProperty(WILL_CHANGE, WILL_CHANGE_VALUE); + } } let activeBits = activeTransformBits.get(element) ?? 0; forEachBit(bits, (i) => { @@ -92,6 +116,11 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo if (activeBits === 0) { activeTransformCounts.delete(element); activeTransformBits.delete(element); + const saved = savedWillChange.get(element); + if (saved && isMutableElement(element)) { + savedWillChange.delete(element); + restoreStyleProp(element.style, WILL_CHANGE, saved); + } } else { activeTransformBits.set(element, activeBits); } From 6abac68fd4ef7ab9596d9810ff4f103c42becac5 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:15:39 -0400 Subject: [PATCH 03/12] perf(animate): fold transform vars into direct translate/scale/rotate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chromium cannot composite a custom-property animation, and one such property pins the whole effect — opacity included — to the main thread. A dialog morph was a style recalc plus a paint on every frame. When nothing else is composing on the element, evaluate the transform template once per keyframe stop instead: each animated var becomes its stop value, every other var its current computed value. What is left is a plain transform keyframe the compositor accepts. The var keys are removed from the effect, not merely shadowed, since leaving one behind would pin it to the main thread again. The var indirection exists so independent animations compose on one element, so the fold is reversible and gives up the moment that matters. `setKeyframes` swaps the values in place, keeping timing, playback state and the finished promise, so the animation carries on from where it is. The tracker demotes when another transform animation registers, when a controller stops, and — via a style-attribute observer watching the motion vars — when anything writes one inline. That last one is why gestures, presets and consumer code keep composing without knowing folds exist. Two details worth naming. A caller's own `translate` is never folded into, so wiring now records which templates are ours. And measurement suppresses the vars with `!important`, which no longer reaches a folded animation, so it reinstates the overridden template too and reads the same resting box as before. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/animate/core/animate.ts | 66 +++++++- src/lib/animate/core/controller.ts | 11 +- src/lib/animate/keyframes/fold.test.ts | 81 ++++++++++ src/lib/animate/keyframes/fold.ts | 141 ++++++++++++++++++ src/lib/animate/properties/properties.ts | 6 +- src/lib/animate/properties/transform-setup.ts | 25 +++- .../transform-tracker.svelte.test.ts | 53 +++++++ .../animate/properties/transform-tracker.ts | 106 ++++++++++++- 8 files changed, 476 insertions(+), 13 deletions(-) create mode 100644 src/lib/animate/keyframes/fold.test.ts create mode 100644 src/lib/animate/keyframes/fold.ts diff --git a/src/lib/animate/core/animate.ts b/src/lib/animate/core/animate.ts index a0c2a55..46eaf15 100644 --- a/src/lib/animate/core/animate.ts +++ b/src/lib/animate/core/animate.ts @@ -22,12 +22,21 @@ import { createController, noopController } from './controller'; import { buildKeyframes, type KeyframeGroup } from '../keyframes/keyframes'; import { normalizeInput } from '../keyframes/normalize'; -import { deregisterTransformAnimation, registerTransformAnimation } from '../properties/properties'; +import { + demoteFoldedTransforms, + deregisterTransformAnimation, + hasActiveTransforms, + registerFoldedTransforms, + registerTransformAnimation, + type FoldedTransform +} from '../properties/properties'; import { ensurePropertiesRegistered, ensureTransformWired, + isTransformOwned, wireTransform } from '../properties/transform-setup'; +import { applyFolds, planFolds } from '../keyframes/fold'; import type { AnimateDefaults, AnimateProps, AnimationController, MotionElement } from '../types'; import { isBrowser, shouldReduceMotion } from '../../shared/browser'; import { formatValue, resolveProp } from '../properties/prop-utils'; @@ -60,23 +69,74 @@ export const animate = ( return noopController(element, defaults); } + // Read before registering: an element with no transform channel in flight is + // one whose transform we can drive directly for the length of this call. + const canFold = needsTransform && !hasActiveTransforms(element); + if (needsTransform) { ensureTransformWired(element); registerTransformAnimation(element, transformBits); } + const folds = canFold ? foldTransforms(element, groups) : []; + const animations = buildAnimations(element, groups, defaults); + if (folds.length > 0) { + registerFoldedTransforms( + element, + folds.map(({ groupIndex, varFrames, targets }) => ({ + animation: animations[groupIndex]!, + varFrames, + targets + })) + ); + } + return createController({ element, - animations: buildAnimations(element, groups, defaults), + animations, defaults, finalStyles, restorations, onTeardown: needsTransform - ? () => deregisterTransformAnimation(element, transformBits) + ? () => { + demoteFoldedTransforms(element); + deregisterTransformAnimation(element, transformBits); + } : undefined }); }; +type PendingFold = Omit & { groupIndex: number }; + +/** + * Collapse this call's transform-variable keyframes into direct + * `translate` / `scale` / `rotate` keyframes wherever possible, so Chromium can + * run them on the compositor instead of recalculating style every frame. + * Mutates `groups`, and returns what the tracker needs to undo the fold. + */ +const foldTransforms = (element: MotionElement, groups: KeyframeGroup[]): PendingFold[] => { + const computed = window.getComputedStyle(element); + const plans = planFolds( + groups, + (name) => computed.getPropertyValue(name).trim(), + // An unset inline value means the template never took (no CSS typed-OM + // support for these properties), leaving nothing to fold onto. + (target) => isTransformOwned(element, target) && !!element.style.getPropertyValue(target) + ); + if (plans.length === 0) return []; + const originals = applyFolds(groups, plans); + return [...originals].map(([groupIndex, keyframes]) => { + const { offset } = groups[groupIndex]!; + return { + groupIndex, + varFrames: (offset ? { ...keyframes, offset } : keyframes) as PropertyIndexedKeyframes, + targets: plans + .filter((plan) => plan.groupIndex === groupIndex) + .map((plan) => [plan.target, element.style.getPropertyValue(plan.target)] as const) + }; + }); +}; + /** * Materialize each keyframe group into a WAAPI `Animation`, applying the shared * effect options (fill / composite / iterations / direction / …) from `defaults`. diff --git a/src/lib/animate/core/controller.ts b/src/lib/animate/core/controller.ts index 5761ef4..078d0cf 100644 --- a/src/lib/animate/core/controller.ts +++ b/src/lib/animate/core/controller.ts @@ -4,6 +4,7 @@ */ import { isBrowser } from '../../shared/browser'; +import { demoteFoldedTransforms } from '../properties/properties'; import { playbackControls } from '../../shared/playback'; import type { AnimateDefaults, AnimationController, MotionElement } from '../types'; import type { CssWrite } from '../keyframes/keyframes'; @@ -188,7 +189,15 @@ export const createController = ({ }, cancel: teardown, stop: () => { - if (isBrowser()) commitComputedStyles(element, finalStyles); + if (isBrowser()) { + // `commitComputedStyles` reads the animated custom properties. While + // folded, those hold their pre-animation values and the live position + // lives on `translate`/`scale` instead, so hand the animation back to + // the variable path first — it keeps its current time, so the values + // read back are the ones on screen. + demoteFoldedTransforms(element); + commitComputedStyles(element, finalStyles); + } teardown(); }, ...playbackControls(forEachAnim) diff --git a/src/lib/animate/keyframes/fold.test.ts b/src/lib/animate/keyframes/fold.test.ts new file mode 100644 index 0000000..a6b2f27 --- /dev/null +++ b/src/lib/animate/keyframes/fold.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, it } from 'vitest'; +import { applyFolds, planFolds } from './fold'; +import type { KeyframeGroup } from './keyframes'; + +const group = (keyframes: Record, duration = 400): KeyframeGroup => ({ + timing: { duration, easing: 'linear', delay: 0 }, + keyframes +}); + +const ownAll = () => true; +const noVars = () => ''; + +describe('planFolds()', () => { + it('collapses a FLIP group into direct translate and scale keyframes', () => { + const groups = [ + group({ + '--flip-x': ['120px', '0px'], + '--flip-y': ['40px', '0px'], + '--flip-scale-x': ['0.5', '1'], + '--flip-scale-y': ['0.5', '1'], + opacity: ['0', '1'] + }) + ]; + + const plans = planFolds(groups, noVars, ownAll); + + expect(plans.map((plan) => plan.target)).toEqual(['translate', 'scale']); + const [translate, scale] = plans; + // Vars with no animation fall back to their template defaults, so the + // stop is a plain transform value with nothing left to resolve. + expect(translate!.stops[0]).toBe('calc(0px + 120px + 0px) calc(0px + 40px + 0px) 0px'); + expect(translate!.stops[1]).toBe('calc(0px + 0px + 0px) calc(0px + 0px + 0px) 0px'); + expect(scale!.stops[0]).toBe('calc(0.5 * 1 * 1) calc(0.5 * 1 * 1)'); + expect(translate!.varKeys).toEqual(['--flip-x', '--flip-y']); + }); + + it('substitutes the live value of vars this call does not animate', () => { + const groups = [group({ '--motion-x': ['0px', '50px'] })]; + + const [plan] = planFolds( + groups, + (name) => (name === '--motion-reorder-x' ? '12px' : ''), + ownAll + ); + + expect(plan!.stops[1]).toBe('calc(50px + 0px + 12px) calc(0px + 0px + 0px) 0px'); + }); + + it('leaves a target alone when its channels have different timings', () => { + const groups = [ + group({ '--motion-x': ['0px', '50px'] }, 400), + group({ '--motion-y': ['0px', '50px'] }, 900) + ]; + + expect(planFolds(groups, noVars, ownAll).map((plan) => plan.target)).toEqual([]); + }); + + it('leaves a target alone when the caller owns its transform', () => { + const groups = [group({ '--motion-x': ['0px', '50px'], '--motion-rotate': ['0deg', '90deg'] })]; + + const plans = planFolds(groups, noVars, (target) => target !== 'translate'); + + expect(plans.map((plan) => plan.target)).toEqual(['rotate']); + }); + + it('ignores targets with no animated channel', () => { + expect(planFolds([group({ opacity: ['0', '1'] })], noVars, ownAll)).toEqual([]); + }); +}); + +describe('applyFolds()', () => { + it('swaps the variable keys for the folded property and reports the originals', () => { + const groups = [group({ '--flip-x': ['10px', '0px'], opacity: ['0', '1'] })]; + const plans = planFolds(groups, noVars, ownAll); + + const originals = applyFolds(groups, plans); + + expect(Object.keys(groups[0]!.keyframes)).toEqual(['opacity', 'translate']); + expect(originals.get(0)).toEqual({ '--flip-x': ['10px', '0px'], opacity: ['0', '1'] }); + }); +}); diff --git a/src/lib/animate/keyframes/fold.ts b/src/lib/animate/keyframes/fold.ts new file mode 100644 index 0000000..8a3ea41 --- /dev/null +++ b/src/lib/animate/keyframes/fold.ts @@ -0,0 +1,141 @@ +/** + * Fold transform-variable keyframes into direct `translate` / `scale` / + * `rotate` keyframes. + * + * `animate()` drives transforms through registered custom properties so that + * independent animations compose on one element — FLIP offsets, a drag, and a + * sibling `animate()` each own their own var and never clobber a shared + * `transform`. Chromium cannot run a custom-property animation on the + * compositor, and one such property in an effect pins the whole effect + * (opacity included) to the main thread. A dialog morph therefore costs a style + * recalc and a full repaint every frame. + * + * When nothing else is composing on the element, the same motion can be + * expressed directly: evaluate the transform template once per keyframe stop, + * substituting each animated var with its stop value and every other var with + * its current computed value. The result is a plain transform keyframe the + * compositor accepts. The fold is reversible — the tracker demotes back to the + * variable form as soon as anything else touches the element — so the + * composition contract still holds. + */ + +import { TRANSFORM_TEMPLATES } from '../properties/properties'; +import type { KeyframeGroup } from './keyframes'; + +/** A CSS property whose value is composed from motion vars. */ +export type TransformTarget = keyof typeof TRANSFORM_TEMPLATES; + +export const TRANSFORM_TARGETS = Object.keys(TRANSFORM_TEMPLATES) as TransformTarget[]; + +/** A template is a run of literal text and `var()` references. */ +type TemplatePart = string | { name: string; fallback: string }; + +const VAR_RE = /var\(\s*(--[a-z0-9-]+)\s*(?:,\s*([^)]*))?\)/gi; + +const parseTemplate = (template: string): TemplatePart[] => { + const parts: TemplatePart[] = []; + let last = 0; + for (const match of template.matchAll(VAR_RE)) { + const at = match.index; + if (at > last) parts.push(template.slice(last, at)); + parts.push({ name: match[1]!, fallback: (match[2] ?? '').trim() }); + last = at + match[0].length; + } + if (last < template.length) parts.push(template.slice(last)); + return parts; +}; + +const TEMPLATE_PARTS = Object.fromEntries( + TRANSFORM_TARGETS.map((target) => [target, parseTemplate(TRANSFORM_TEMPLATES[target])]) +) as Record; + +/** One transform property that can be driven directly for this `animate()` call. */ +export interface FoldPlan { + target: TransformTarget; + /** Index into the groups array — the single group holding every animated var. */ + groupIndex: number; + /** Variable keyframe keys the folded keyframe replaces. */ + varKeys: string[]; + /** The substituted template, one entry per keyframe stop. */ + stops: string[]; +} + +/** + * Work out which transform properties can be driven directly. + * + * A target is foldable only when we installed its template (a caller's own + * `translate` stays authoritative), at least one of its vars is animated, and + * every animated var it reads sits in one timing group with the same number of + * stops — per-axis springs keep their independent timing on the variable path. + * + * Pure: reads the DOM only through `readVar`, and mutates nothing. + */ +export const planFolds = ( + groups: readonly KeyframeGroup[], + readVar: (name: string) => string, + isOwned: (target: TransformTarget) => boolean +): FoldPlan[] => { + const plans: FoldPlan[] = []; + for (const target of TRANSFORM_TARGETS) { + if (!isOwned(target)) continue; + const parts = TEMPLATE_PARTS[target]!; + const varKeys: string[] = []; + let groupIndex = -1; + let stopCount = 0; + let foldable = true; + for (const part of parts) { + if (typeof part === 'string') continue; + const index = groups.findIndex((group) => part.name in group.keyframes); + if (index === -1) continue; + const stops = groups[index]!.keyframes[part.name]!; + if (groupIndex === -1) { + groupIndex = index; + stopCount = stops.length; + } else if (index !== groupIndex || stops.length !== stopCount) { + // Channels of one target on different timings cannot collapse into + // a single keyframe list. + foldable = false; + break; + } + varKeys.push(part.name); + } + if (!foldable || groupIndex === -1) continue; + + const { keyframes } = groups[groupIndex]!; + const stops: string[] = []; + for (let stop = 0; stop < stopCount; stop++) { + let value = ''; + for (const part of parts) { + if (typeof part === 'string') { + value += part; + continue; + } + const animated = keyframes[part.name]; + value += animated ? animated[stop]! : readVar(part.name) || part.fallback; + } + stops.push(value); + } + plans.push({ target, groupIndex, varKeys, stops }); + } + return plans; +}; + +/** + * Rewrite the groups in place per plan, returning each touched group's original + * keyframes so the fold can be undone later. The variable keys are removed, not + * merely shadowed: leaving a custom property in the effect would pin it to the + * main thread again and defeat the whole exercise. + */ +export const applyFolds = ( + groups: KeyframeGroup[], + plans: readonly FoldPlan[] +): Map> => { + const originals = new Map>(); + for (const plan of plans) { + const group = groups[plan.groupIndex]!; + if (!originals.has(plan.groupIndex)) originals.set(plan.groupIndex, { ...group.keyframes }); + for (const key of plan.varKeys) delete group.keyframes[key]; + group.keyframes[plan.target] = plan.stops; + } + return originals; +}; diff --git a/src/lib/animate/properties/properties.ts b/src/lib/animate/properties/properties.ts index 739f866..c4c5db8 100644 --- a/src/lib/animate/properties/properties.ts +++ b/src/lib/animate/properties/properties.ts @@ -208,5 +208,9 @@ export { VAR_BIT, registerTransformAnimation, deregisterTransformAnimation, - measureWithoutAncestorTransforms + registerFoldedTransforms, + demoteFoldedTransforms, + hasActiveTransforms, + measureWithoutAncestorTransforms, + type FoldedTransform } from './transform-tracker'; diff --git a/src/lib/animate/properties/transform-setup.ts b/src/lib/animate/properties/transform-setup.ts index e752285..fd3b24d 100644 --- a/src/lib/animate/properties/transform-setup.ts +++ b/src/lib/animate/properties/transform-setup.ts @@ -37,7 +37,13 @@ export const ensurePropertiesRegistered = (): void => { } }; -const ELEMENTS_WITH_TRANSFORM = new WeakSet(); +/** + * Which transform properties we installed a template into, per element. A + * caller's own `translate` is left alone at wiring time and must also never be + * folded into (see `keyframes/fold`) — we would be animating a value we do not + * own. + */ +const ELEMENTS_WITH_TRANSFORM = new WeakMap>(); /** * Make sure the element's `translate` / `scale` / `rotate` styles are wired @@ -45,13 +51,22 @@ const ELEMENTS_WITH_TRANSFORM = new WeakSet(); */ export const ensureTransformWired = (element: MotionElement): void => { if (ELEMENTS_WITH_TRANSFORM.has(element)) return; - ELEMENTS_WITH_TRANSFORM.add(element); const { style } = element; - style.translate ||= TRANSFORM_TEMPLATES.translate; - style.scale ||= TRANSFORM_TEMPLATES.scale; - style.rotate ||= TRANSFORM_TEMPLATES.rotate; + const owned: Record = { + translate: !style.translate, + scale: !style.scale, + rotate: !style.rotate + }; + ELEMENTS_WITH_TRANSFORM.set(element, owned); + if (owned.translate) style.translate = TRANSFORM_TEMPLATES.translate; + if (owned.scale) style.scale = TRANSFORM_TEMPLATES.scale; + if (owned.rotate) style.rotate = TRANSFORM_TEMPLATES.rotate; }; +/** True when the template on this transform property is ours to animate. */ +export const isTransformOwned = (element: Element, target: string): boolean => + ELEMENTS_WITH_TRANSFORM.get(element)?.[target] === true; + /** * Both halves of the setup an element needs before anything writes `--motion-*` * to it: global property registration plus this element's transform chain. diff --git a/src/lib/animate/properties/transform-tracker.svelte.test.ts b/src/lib/animate/properties/transform-tracker.svelte.test.ts index fffc7e0..130798d 100644 --- a/src/lib/animate/properties/transform-tracker.svelte.test.ts +++ b/src/lib/animate/properties/transform-tracker.svelte.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { deregisterTransformAnimation, measureWithoutAncestorTransforms, + registerFoldedTransforms, registerTransformAnimation, VAR_BIT } from './transform-tracker'; @@ -65,3 +66,55 @@ describe('will-change hint', () => { expect(element.style.getPropertyValue('will-change')).toBe('opacity'); }); }); + +describe('folded transforms', () => { + const fold = (element: HTMLElement) => { + const animation = { effect: { setKeyframes: vi.fn() } } as unknown as Animation; + registerFoldedTransforms(element, [ + { + animation, + varFrames: { '--flip-x': ['10px', '0px'] }, + targets: [['translate', 'calc(var(--flip-x, 0px)) 0px 0px']] + } + ]); + return animation; + }; + + it('reinstates the variable keyframes when another transform animation starts', () => { + const element = document.createElement('div'); + document.body.appendChild(element); + mounted.push(element); + registerTransformAnimation(element, VAR_BIT['--flip-x']!); + const animation = fold(element); + + registerTransformAnimation(element, VAR_BIT['--motion-x']!); + + expect((animation.effect as KeyframeEffect).setKeyframes).toHaveBeenCalledWith({ + '--flip-x': ['10px', '0px'] + }); + }); + + it('measures at rest by reinstating the template the fold overrides', () => { + const parent = document.createElement('div'); + const child = document.createElement('div'); + parent.appendChild(child); + document.body.appendChild(parent); + mounted.push(parent); + parent.style.setProperty('--flip-x', '10px'); + registerTransformAnimation(parent, VAR_BIT['--flip-x']!); + fold(parent); + let observed: [string, string] | undefined; + vi.spyOn(child, 'getBoundingClientRect').mockImplementation(() => { + observed = [ + parent.style.getPropertyValue('translate'), + parent.style.getPropertyPriority('translate') + ]; + return new DOMRect(); + }); + + measureWithoutAncestorTransforms(child); + + expect(observed).toEqual(['calc(var(--flip-x, 0px)) 0px 0px', 'important']); + expect(parent.style.getPropertyValue('translate')).toBe(''); + }); +}); diff --git a/src/lib/animate/properties/transform-tracker.ts b/src/lib/animate/properties/transform-tracker.ts index d3ae75b..54c8b37 100644 --- a/src/lib/animate/properties/transform-tracker.ts +++ b/src/lib/animate/properties/transform-tracker.ts @@ -6,9 +6,12 @@ * has a running `animate()` transform animation, so `getBoundingClientRect()` * returns the element's "at rest" layout position even mid-animation. * - * The tracker also owns the `will-change` hint that earns a transform-animated - * element its own compositor layer, since the same ref-count says exactly when - * to set and clear it. + * The tracker also owns the two things that keep a transform animation off the + * main thread: the `will-change` hint that gets the element its own layer, and + * the registry of *folded* animations — ones that drive `translate` / `scale` / + * `rotate` directly instead of the motion vars (see `keyframes/fold`). A fold + * is only valid while nothing else touches the element's transform, so this + * module demotes it back to the variable path the moment that stops holding. * * The tracker is intentionally separate from the property registry so the * static registry data and the dynamic runtime state live in different modules. @@ -89,6 +92,10 @@ const savedWillChange = new WeakMap(); /** Mark that an element has started a WAAPI transform animation. */ export const registerTransformAnimation = (element: Element, bits: number): void => { + // A fold owns `translate`/`scale`/`rotate` outright, which would mask the + // variable channel the incoming animation is about to drive. Hand the + // element back to the vars before it starts. + demoteFoldedTransforms(element); let counts = activeTransformCounts.get(element); if (!counts) { counts = new Uint8Array(N_TRANSFORM_VARS); @@ -126,6 +133,88 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo } }; +/** + * One WAAPI animation that has been folded onto a real transform property. + */ +export interface FoldedTransform { + animation: Animation; + /** The pre-fold, variable-driven keyframes, reinstated on demotion. */ + varFrames: PropertyIndexedKeyframes; + /** + * Transform properties this animation drives directly, each paired with the + * inline template it temporarily overrides. Measurement reinstates the + * template to read the element's resting box. + */ + targets: ReadonlyArray; +} + +const foldedTransforms = new WeakMap(); +const foldObservers = new WeakMap(); +const foldVarSnapshots = new WeakMap(); + +/** Inline (not computed) motion-var values — no style recalc, safe to poll. */ +const readInlineVars = (element: MotionElement): string[] => + MOTION_TRANSFORM_IDENTITIES.map(([name]) => element.style.getPropertyValue(name)); + +/** + * Take ownership of an element's folded animations. + * + * A fold bakes the element's *other* motion vars into its keyframes, so any + * inline write to one of them leaves the animation showing a stale value. A + * style-attribute observer watches for exactly that and demotes, which is why + * gestures, presets and consumer code can keep writing `--motion-x` without + * knowing folds exist. Other style mutations — `pointer-events`, `opacity`, a + * measurement's save/restore round-trip — leave the baked values correct and + * are ignored. + */ +export const registerFoldedTransforms = ( + element: MotionElement, + folds: readonly FoldedTransform[] +): void => { + if (folds.length === 0) return; + foldedTransforms.set(element, [...folds]); + if (typeof MutationObserver === 'undefined') return; + const before = readInlineVars(element); + foldVarSnapshots.set(element, before); + const observer = new MutationObserver(() => { + const snapshot = foldVarSnapshots.get(element); + if (!snapshot) return; + const now = readInlineVars(element); + for (let i = 0; i < now.length; i++) { + if (now[i] !== snapshot[i]) { + demoteFoldedTransforms(element); + return; + } + } + }); + observer.observe(element, { attributes: true, attributeFilter: ['style'] }); + foldObservers.set(element, observer); +}; + +/** + * Put folded animations back on the variable path, in place. + * + * `setKeyframes` swaps an effect's values without touching its timing, so + * `currentTime`, playback state and the `finished` promise all survive and the + * animation carries on from the same visual position. Safe to call on an + * element that has no folds, and safe to call twice. + * + * ponytail: demotion is per element, not per transform property — a write to + * `--motion-x` also gives back a folded `rotate` that nothing touched. Split it + * per target if a trace ever shows that costing a frame. + */ +export const demoteFoldedTransforms = (element: Element): void => { + foldObservers.get(element)?.disconnect(); + foldObservers.delete(element); + foldVarSnapshots.delete(element); + const folds = foldedTransforms.get(element); + if (!folds) return; + foldedTransforms.delete(element); + for (const { animation, varFrames } of folds) { + (animation.effect as KeyframeEffect | null)?.setKeyframes(varFrames); + } +}; + /** * Like `element.getBoundingClientRect()` but walks up the ancestor chain * and temporarily suppresses the motion CSS vars on every ancestor that @@ -160,6 +249,17 @@ export const measureWithoutAncestorTransforms = ( props.push({ name, saved: saveStyleProp(target.style, name) }); target.style.setProperty(name, identity, 'important'); }); + // A folded animation drives `translate`/`scale`/`rotate` itself, so + // forcing the vars to identity no longer reaches it. Reinstating the + // template with `!important` does: the template reads the vars we just + // neutralised, so the element measures at rest exactly as on the + // variable path. + for (const { targets } of foldedTransforms.get(target) ?? []) { + for (const [name, template] of targets) { + props.push({ name, saved: saveStyleProp(target.style, name) }); + target.style.setProperty(name, template, 'important'); + } + } if (props.length > 0) suppressed.push({ node: target, props }); }; From 22571abe975a9bab70d5440311c586eaabde233c Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:24:44 -0400 Subject: [PATCH 04/12] perf(animate): never group a compositable prop with a layout one Compositing is decided per effect, so one non-compositable property pins everything animating alongside it. Props were merged on timing alone, so `animate(el, { opacity: 0, height: 0 })` put both in one effect and the opacity lost the compositor for a sibling's sake. The same sibling also undid the transform fold, which had just finished removing the custom properties from that effect. Group by compositability as well as timing. Two effects with identical timing start in the same frame, and `composite` is per property, so the disjoint sets cannot clobber each other. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/animate/keyframes/fold.test.ts | 3 +- src/lib/animate/keyframes/keyframes.test.ts | 40 +++++++++++++++++++++ src/lib/animate/keyframes/keyframes.ts | 20 ++++++++--- src/lib/animate/properties/prop-utils.ts | 16 +++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/lib/animate/keyframes/fold.test.ts b/src/lib/animate/keyframes/fold.test.ts index a6b2f27..851c58a 100644 --- a/src/lib/animate/keyframes/fold.test.ts +++ b/src/lib/animate/keyframes/fold.test.ts @@ -4,7 +4,8 @@ import type { KeyframeGroup } from './keyframes'; const group = (keyframes: Record, duration = 400): KeyframeGroup => ({ timing: { duration, easing: 'linear', delay: 0 }, - keyframes + keyframes, + compositable: true }); const ownAll = () => true; diff --git a/src/lib/animate/keyframes/keyframes.test.ts b/src/lib/animate/keyframes/keyframes.test.ts index 8bb35bd..c1f1dd9 100644 --- a/src/lib/animate/keyframes/keyframes.test.ts +++ b/src/lib/animate/keyframes/keyframes.test.ts @@ -142,3 +142,43 @@ describe('buildKeyframes() — multi-stop sequences', () => { expect(groups[0]!.offset).toBeUndefined(); }); }); + +describe('buildKeyframes() — compositability grouping', () => { + it('keeps a layout-animating prop out of the compositable group', () => { + const { groups } = buildKeyframes( + el, + { opacity: ['0', '1'], width: ['0px', '100px'] }, + { duration: 300 } + ); + + // One effect per class: a `width` in the same effect would pin the + // opacity to the main thread alongside it. + expect(groups).toHaveLength(2); + expect(groups.find((group) => group.compositable)!.keyframes).toEqual({ + opacity: ['0', '1'] + }); + expect(groups.find((group) => !group.compositable)!.keyframes).toEqual({ + width: ['0px', '100px'] + }); + }); + + it('still merges props that are compositable together', () => { + const { groups } = buildKeyframes( + el, + { opacity: ['0', '1'], x: ['0px', '100px'], filter: ['none', 'blur(2px)'] }, + { duration: 300 } + ); + + expect(groups).toHaveLength(1); + }); + + it('groups several layout props together rather than one effect each', () => { + const { groups } = buildKeyframes( + el, + { width: ['0px', '10px'], height: ['0px', '10px'] }, + { duration: 300 } + ); + + expect(groups).toHaveLength(1); + }); +}); diff --git a/src/lib/animate/keyframes/keyframes.ts b/src/lib/animate/keyframes/keyframes.ts index 11b4ff6..499dccc 100644 --- a/src/lib/animate/keyframes/keyframes.ts +++ b/src/lib/animate/keyframes/keyframes.ts @@ -10,6 +10,7 @@ import { isBrowser } from '../../shared/browser'; import { isAutoKeyword, measureKeywordValue } from './keyword'; import { formatValue, + isCompositable, readCurrentValue, resolveProp, toKeyframeKey @@ -18,6 +19,13 @@ import { export interface KeyframeGroup { timing: { duration: number; easing: string; delay: number }; keyframes: Record; + /** + * Whether every prop in this group can be composited. Props are never merged + * across this boundary, so the compositable half of a mixed `animate()` call + * keeps its own effect instead of being pinned to the main thread by a + * sibling that animates layout. + */ + compositable: boolean; /** * Explicit keyframe offsets (0–1) for a multi-stop sequence. Present only * for groups carrying a single offset-bearing prop, so it never conflicts @@ -84,12 +92,14 @@ const resolveStop = ( * to the single prop that requested it. */ const findGroup = ( groups: KeyframeGroup[], - timing: KeyframeGroup['timing'] + timing: KeyframeGroup['timing'], + compositable: boolean ): KeyframeGroup | undefined => { for (const group of groups) { const t = group.timing; if ( group.offset === undefined && + group.compositable === compositable && t.duration === timing.duration && t.delay === timing.delay && t.easing === timing.easing @@ -146,10 +156,12 @@ export const buildKeyframes = ( } finalStyles.push({ css: def.css, value: values[values.length - 1]! }); - // Offset-bearing props get a private group; everything else merges by timing. - let group = offset ? undefined : findGroup(groups, timing); + // Offset-bearing props get a private group; everything else merges by + // timing, within its compositability class. + const compositable = isCompositable(def); + let group = offset ? undefined : findGroup(groups, timing, compositable); if (!group) { - group = { timing, keyframes: {}, offset }; + group = { timing, keyframes: {}, offset, compositable }; groups.push(group); } // WAAPI keyframes are keyed by camelCased IDL names; hyphenated multi-word diff --git a/src/lib/animate/properties/prop-utils.ts b/src/lib/animate/properties/prop-utils.ts index 798dc76..a8ab118 100644 --- a/src/lib/animate/properties/prop-utils.ts +++ b/src/lib/animate/properties/prop-utils.ts @@ -28,6 +28,22 @@ export const formatValue = (value: AnimatableValue, def: PropDef): string => export const toKeyframeKey = (css: string): string => css.startsWith('--') ? css : css.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()); +/** + * Properties Chromium can run on the compositor. Transform components qualify + * through `def.transform`, so only the non-transform ones are listed here. + */ +const COMPOSITABLE_CSS = new Set(['opacity', 'filter', 'backdrop-filter']); + +/** + * Whether an animation of this property can run off the main thread. + * + * Compositing is decided per effect, not per property: one non-compositable + * property pins everything animating alongside it, so a `width` would drag a + * sibling `opacity` down with it. Grouping keeps the two apart. + */ +export const isCompositable = (def: PropDef): boolean => + def.transform === true || COMPOSITABLE_CSS.has(def.css); + /** Cache passthrough PropDefs for unknown keys to avoid repeat regex/string work. */ const UNKNOWN_PROP_CACHE = new Map(); From 4869a8cf763c9e02672b9efa8267ff34c6dc3d18 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:25:21 -0400 Subject: [PATCH 05/12] perf(gestures): read one rect per pointer event in move() `pull` re-read the element's box twice for the width and height the caller had already measured. The spring write between the two reads dirties style, so the second one was a forced layout on every pointer move. Pass the rect through instead. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/gestures/move.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/lib/gestures/move.ts b/src/lib/gestures/move.ts index 9aee447..ba9c327 100644 --- a/src/lib/gestures/move.ts +++ b/src/lib/gestures/move.ts @@ -83,8 +83,9 @@ export const moveable = (options: MoveableOptions = {}): Attachment !includeTouch && event.pointerType === 'touch'; - const measure = (event: PointerEvent): MoveInfo => { - const rect = element.getBoundingClientRect(); + // The rect is passed in, not read here: `pull` needs the same one, and a + // spring write between two reads would force a second layout. + const measure = (event: PointerEvent, rect: DOMRect): MoveInfo => { const localX = event.clientX - rect.left; const localY = event.clientY - rect.top; return { @@ -97,21 +98,22 @@ export const moveable = (options: MoveableOptions = {}): Attachment { + const pull = (info: MoveInfo, rect: DOMRect): void => { // Magnetic offset is the pointer's distance from centre, scaled. Written // live with jump() (synchronous) — the pointer path is already smooth. - springX!.jump((info.nx * element.getBoundingClientRect().width * strength) / 2); - springY!.jump((info.ny * element.getBoundingClientRect().height * strength) / 2); + springX!.jump((info.nx * rect.width * strength) / 2); + springY!.jump((info.ny * rect.height * strength) / 2); }; const onEnter = (event: PointerEvent): void => { if (ignore(event)) return; - onMoveStart?.(measure(event), element); + onMoveStart?.(measure(event, element.getBoundingClientRect()), element); }; const onPointerMove = (event: PointerEvent): void => { if (ignore(event)) return; - const info = measure(event); - if (applyTransform) pull(info); + const rect = element.getBoundingClientRect(); + const info = measure(event, rect); + if (applyTransform) pull(info, rect); onMove?.(info, element); }; const onLeave = (event: PointerEvent): void => { From 5b6b0b2b232d85470ec8fde4246daa85f4c7f497 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:29:24 -0400 Subject: [PATCH 06/12] perf(gestures): hold a compositor layer for the duration of a gesture The `will-change` hint lived in `registerTransformAnimation`, which only `animate()` reaches. Every gesture drives `--motion-*` by hand instead, so a dragged card with a shadow repainted on the main thread on every pointer event with no layer of its own, and a reorder repainted every row. Add a ref-counted `hintTransformLayer()` beside the existing hint, so an animation and a gesture can hold the same layer without either taking it from the other, and hold it in all five gestures. Scoped to the active gesture, not to attachment setup: a permanent hint is a permanent layer, and a list would hold one per row. It outlives the pointer where a spring keeps writing after release, and reorder hands off to the settle FLIP, which takes its own hint through `animate()`. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/animate/properties/properties.ts | 1 + .../animate/properties/transform-tracker.ts | 59 ++++++++++++++++--- src/lib/gestures/draggable.svelte.test.ts | 19 ++++++ src/lib/gestures/draggable.ts | 15 +++++ src/lib/gestures/move.svelte.test.ts | 12 ++++ src/lib/gestures/move.ts | 19 ++++++ src/lib/gestures/pinch.svelte.test.ts | 16 +++++ src/lib/gestures/pinch.ts | 12 ++++ src/lib/gestures/reorder.svelte.test.ts | 18 ++++++ src/lib/gestures/reorder.ts | 11 +++- src/lib/gestures/wheel.svelte.test.ts | 12 ++++ src/lib/gestures/wheel.ts | 15 +++++ 12 files changed, 199 insertions(+), 10 deletions(-) diff --git a/src/lib/animate/properties/properties.ts b/src/lib/animate/properties/properties.ts index c4c5db8..45053f4 100644 --- a/src/lib/animate/properties/properties.ts +++ b/src/lib/animate/properties/properties.ts @@ -209,6 +209,7 @@ export { registerTransformAnimation, deregisterTransformAnimation, registerFoldedTransforms, + hintTransformLayer, demoteFoldedTransforms, hasActiveTransforms, measureWithoutAncestorTransforms, diff --git a/src/lib/animate/properties/transform-tracker.ts b/src/lib/animate/properties/transform-tracker.ts index 54c8b37..1dcd865 100644 --- a/src/lib/animate/properties/transform-tracker.ts +++ b/src/lib/animate/properties/transform-tracker.ts @@ -89,6 +89,54 @@ const WILL_CHANGE = 'will-change'; */ const WILL_CHANGE_VALUE = 'translate, scale, rotate'; const savedWillChange = new WeakMap(); +/** + * Ref-count for the hint itself, separate from the per-channel transform + * counts: a `animate()` call and a drag can both want the layer at once, and + * whichever finishes first must not take it away from the other. + */ +const layerHintCounts = new WeakMap(); + +const retainLayerHint = (element: Element): void => { + const count = layerHintCounts.get(element) ?? 0; + layerHintCounts.set(element, count + 1); + if (count > 0 || !isMutableElement(element)) return; + savedWillChange.set(element, saveStyleProp(element.style, WILL_CHANGE)); + element.style.setProperty(WILL_CHANGE, WILL_CHANGE_VALUE); +}; + +const releaseLayerHint = (element: Element): void => { + const count = layerHintCounts.get(element) ?? 0; + if (count === 0) return; + if (count > 1) { + layerHintCounts.set(element, count - 1); + return; + } + layerHintCounts.delete(element); + const saved = savedWillChange.get(element); + if (!saved || !isMutableElement(element)) return; + savedWillChange.delete(element); + restoreStyleProp(element.style, WILL_CHANGE, saved); +}; + +/** + * Hold a compositor layer for an element whose `--motion-*` values are being + * written directly, which is what every gesture does: they drive the vars by + * hand rather than through `animate()`, so nothing else would hint them and a + * dragged card with a shadow repaints on every pointer event. + * + * Scope the call to the active gesture, not to attachment setup — a permanent + * hint is a permanent layer, and a long list would hold one per row. The + * returned release is idempotent, so a gesture that ends twice is safe. + */ +export const hintTransformLayer = (element: Element): (() => void) => { + retainLayerHint(element); + let released = false; + return () => { + if (released) return; + released = true; + releaseLayerHint(element); + }; +}; /** Mark that an element has started a WAAPI transform animation. */ export const registerTransformAnimation = (element: Element, bits: number): void => { @@ -100,10 +148,7 @@ export const registerTransformAnimation = (element: Element, bits: number): void if (!counts) { counts = new Uint8Array(N_TRANSFORM_VARS); activeTransformCounts.set(element, counts); - if (isMutableElement(element)) { - savedWillChange.set(element, saveStyleProp(element.style, WILL_CHANGE)); - element.style.setProperty(WILL_CHANGE, WILL_CHANGE_VALUE); - } + retainLayerHint(element); } let activeBits = activeTransformBits.get(element) ?? 0; forEachBit(bits, (i) => { @@ -123,11 +168,7 @@ export const deregisterTransformAnimation = (element: Element, bits: number): vo if (activeBits === 0) { activeTransformCounts.delete(element); activeTransformBits.delete(element); - const saved = savedWillChange.get(element); - if (saved && isMutableElement(element)) { - savedWillChange.delete(element); - restoreStyleProp(element.style, WILL_CHANGE, saved); - } + releaseLayerHint(element); } else { activeTransformBits.set(element, activeBits); } diff --git a/src/lib/gestures/draggable.svelte.test.ts b/src/lib/gestures/draggable.svelte.test.ts index eb715a4..b6fd008 100644 --- a/src/lib/gestures/draggable.svelte.test.ts +++ b/src/lib/gestures/draggable.svelte.test.ts @@ -69,4 +69,23 @@ describe('draggable()', () => { cleanup?.(); expect(el.style.touchAction).toBe('auto'); }); + + it('holds a compositor layer for the drag and releases it once settled', async () => { + const el = mount(); + el.style.setProperty('will-change', 'opacity'); + const cleanup = draggable({ snapToOrigin: true })(el); + + el.dispatchEvent(pointer('pointerdown', 0, 0)); + el.dispatchEvent(pointer('pointermove', 50, 30)); + expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + el.dispatchEvent(pointer('pointerup', 50, 30)); + // The flick keeps writing after the pointer is gone, so the layer outlives + // pointerup and is handed back only when the spring stops. + expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + await expect + .poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 }) + .toBe('opacity'); + cleanup?.(); + }); }); diff --git a/src/lib/gestures/draggable.ts b/src/lib/gestures/draggable.ts index a6d1155..c8b54b5 100644 --- a/src/lib/gestures/draggable.ts +++ b/src/lib/gestures/draggable.ts @@ -19,6 +19,7 @@ import type { SpringOptions } from '../shared/types'; import { createSpringValue } from '../animate/spring-value'; import { capture, lockTouchAction, release } from './pointer-capture'; import { wireTransform } from '../animate/properties/transform-setup'; +import { hintTransformLayer } from '../animate/properties/properties'; import { applyConstraint, xBounds, yBounds, type DragConstraints } from './constraints'; export type DragAxis = 'x' | 'y' | 'both'; @@ -85,6 +86,14 @@ export const draggable = (options: DraggableOptions = {}): Attachment element.style.setProperty('--motion-y', `${v}px`)); let dragging = false; + // Held from pointerdown until the spring stops writing, which is after the + // release flick has settled — not at pointerup. + let dropLayer: (() => void) | null = null; + const releaseLayer = (): void => { + const drop = dropLayer; + dropLayer = null; + drop?.(); + }; let pointerId = -1; let startX = 0; let startY = 0; @@ -103,6 +112,7 @@ export const draggable = (options: DraggableOptions = {}): Attachment { if (dragging || event.button !== 0) return; dragging = true; + dropLayer ??= hintTransformLayer(element); pointerId = event.pointerId; capture(element, pointerId); // Resume from wherever the spring currently sits (interrupt-friendly). @@ -146,6 +156,10 @@ export const draggable = (options: DraggableOptions = {}): Attachment { + if (!dragging) releaseLayer(); + }); }; const unlisten = listen(element, { @@ -161,6 +175,7 @@ export const draggable = (options: DraggableOptions = {}): Attachment { expect(onMove).not.toHaveBeenCalled(); cleanup?.(); }); + + it('holds a compositor layer from pointerenter through the spring back', async () => { + const el = mount(); + const cleanup = moveable({ applyTransform: true })(el); + + el.dispatchEvent(pointer('pointerenter', 150, 100)); + expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + el.dispatchEvent(pointer('pointerleave', 150, 100)); + await expect.poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 }).toBe(''); + cleanup?.(); + }); }); diff --git a/src/lib/gestures/move.ts b/src/lib/gestures/move.ts index ba9c327..0de14e8 100644 --- a/src/lib/gestures/move.ts +++ b/src/lib/gestures/move.ts @@ -24,6 +24,7 @@ import type { MotionElement } from '../animate'; import type { SpringOptions } from '../shared/types'; import { createSpringValue } from '../animate/spring-value'; import { wireTransform } from '../animate/properties/transform-setup'; +import { hintTransformLayer } from '../animate/properties/properties'; export interface MoveInfo { /** Pointer position in client coordinates. */ @@ -81,6 +82,16 @@ export const moveable = (options: MoveableOptions = {}): Attachment element.style.setProperty('--motion-y', `${v}px`)); } + // Held while the pointer is over the element and through the spring-back + // after it leaves, which is still writing `--motion-x`. + let dropLayer: (() => void) | null = null; + const releaseLayer = (): void => { + const drop = dropLayer; + dropLayer = null; + drop?.(); + }; + let hovering = false; + const ignore = (event: PointerEvent): boolean => !includeTouch && event.pointerType === 'touch'; // The rect is passed in, not read here: `pull` needs the same one, and a @@ -107,6 +118,8 @@ export const moveable = (options: MoveableOptions = {}): Attachment { if (ignore(event)) return; + hovering = true; + if (applyTransform) dropLayer ??= hintTransformLayer(element); onMoveStart?.(measure(event, element.getBoundingClientRect()), element); }; const onPointerMove = (event: PointerEvent): void => { @@ -118,9 +131,14 @@ export const moveable = (options: MoveableOptions = {}): Attachment { if (ignore(event)) return; + hovering = false; // Spring back to rest, carrying any momentum. springX?.set(0); springY?.set(0); + // Re-entering before the spring-back finishes keeps the layer. + void Promise.all([springX?.finished, springY?.finished]).then(() => { + if (!hovering) releaseLayer(); + }); onMoveEnd?.(element); }; @@ -132,6 +150,7 @@ export const moveable = (options: MoveableOptions = {}): Attachment { unlisten(); + releaseLayer(); unsubX(); unsubY(); springX?.stop(); diff --git a/src/lib/gestures/pinch.svelte.test.ts b/src/lib/gestures/pinch.svelte.test.ts index 6978f96..66affd4 100644 --- a/src/lib/gestures/pinch.svelte.test.ts +++ b/src/lib/gestures/pinch.svelte.test.ts @@ -105,4 +105,20 @@ describe('pinchable()', () => { expect(onMove).not.toHaveBeenCalled(); cleanup?.(); }); + + it('holds a compositor layer while both pointers are down', () => { + const el = mount(); + const cleanup = pinchable()(el); + + el.dispatchEvent(pointer('pointerdown', 1, 0, 0)); + expect(el.style.getPropertyValue('will-change')).toBe(''); + + // The gesture, and the layer, begin on the second pointer. + el.dispatchEvent(pointer('pointerdown', 2, 100, 0)); + expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + el.dispatchEvent(pointer('pointerup', 2, 100, 0)); + expect(el.style.getPropertyValue('will-change')).toBe(''); + cleanup?.(); + }); }); diff --git a/src/lib/gestures/pinch.ts b/src/lib/gestures/pinch.ts index 6a2ceb5..0d6dfcb 100644 --- a/src/lib/gestures/pinch.ts +++ b/src/lib/gestures/pinch.ts @@ -17,6 +17,7 @@ import { applyConstraint, type AxisBounds } from './constraints'; import type { MotionElement } from '../animate'; import { capture, lockTouchAction, release } from './pointer-capture'; import { wireTransform } from '../animate/properties/transform-setup'; +import { hintTransformLayer } from '../animate/properties/properties'; export interface PinchInfo { /** Distance ratio of the two pointers relative to gesture start. */ @@ -62,6 +63,14 @@ export const pinchable = (options: PinchableOptions = {}): Attachment(); let pinching = false; + // The scale/rotate writes are synchronous with the pointers, so the layer + // is wanted for exactly as long as two fingers are down. + let dropLayer: (() => void) | null = null; + const releaseLayer = (): void => { + const drop = dropLayer; + dropLayer = null; + drop?.(); + }; let startDistance = 0; let startAngle = 0; @@ -91,6 +100,7 @@ export const pinchable = (options: PinchableOptions = {}): Attachment { unlisten(); points.clear(); + releaseLayer(); unlockTouchAction(); }; }; diff --git a/src/lib/gestures/reorder.svelte.test.ts b/src/lib/gestures/reorder.svelte.test.ts index 521ab9e..a259ba1 100644 --- a/src/lib/gestures/reorder.svelte.test.ts +++ b/src/lib/gestures/reorder.svelte.test.ts @@ -193,4 +193,22 @@ describe('reorder()', () => { els[0]!.dispatchEvent(pointer('pointerup', 120)); expect(order).toEqual(['a', 'b', 'c']); }); + + it('holds a compositor layer on every row for the duration of a drag', () => { + const { values, els } = setup(); + let order = [...values]; + const r = reorder({ items: () => order, onReorder: (next) => (order = next) }); + els.forEach((el, i) => r.item(values[i]!)(el)); + + els[0]!.dispatchEvent(pointer('pointerdown', 20)); + // Siblings slide into the vacated slot, so they are hinted too. + expect(els.map((el) => el.style.getPropertyValue('will-change'))).toEqual([ + 'translate, scale, rotate', + 'translate, scale, rotate', + 'translate, scale, rotate' + ]); + + els[0]!.dispatchEvent(pointer('pointerup', 20)); + expect(els.map((el) => el.style.getPropertyValue('will-change'))).toEqual(['', '', '']); + }); }); diff --git a/src/lib/gestures/reorder.ts b/src/lib/gestures/reorder.ts index 3db5b66..e8f8b13 100644 --- a/src/lib/gestures/reorder.ts +++ b/src/lib/gestures/reorder.ts @@ -27,6 +27,7 @@ import { listen } from '../shared/listen'; import { snapshotRect, flipFrom } from '../flip'; import type { EasingFn } from '../shared/types'; import { wireTransform } from '../animate/properties/transform-setup'; +import { hintTransformLayer } from '../animate/properties/properties'; import { restoreStyleProp, saveStyleProp, type SavedStyleProp } from '../shared/inline-style'; import { capture, lockTouchAction, release } from './pointer-capture'; @@ -103,6 +104,8 @@ interface DragState { styles: Map; /** Detaches the move/up/cancel listeners this drag installed. */ unlisten: () => void; + /** Releases the compositor-layer hint held on every row for this drag. */ + dropLayers: Array<() => void>; } /** Create a reorder controller for one list. */ @@ -130,6 +133,9 @@ export const reorder = (options: ReorderOptions): ReorderHandle => { const restoreDrag = (state: DragState): void => { for (const node of state.els) restoreDragStyles(node, state.styles.get(node)!); + // The settle FLIP that follows goes through `animate()`, which takes its + // own hint, so there is no gap between the two. + for (const drop of state.dropLayers) drop(); }; /** Slide siblings into the dragged row's actual slot, including variable item sizes and gaps. */ @@ -224,7 +230,10 @@ export const reorder = (options: ReorderOptions): ReorderHandle => { pointermove: onMove, pointerup: endDrag, pointercancel: endDrag - }) + }), + // Every row moves during a drag: the dragged one follows the pointer, + // the rest slide into its vacated slot. + dropLayers: rows.map((node) => hintTransformLayer(node)) }; // Reorder owns only its dedicated motion channel and temporary drag diff --git a/src/lib/gestures/wheel.svelte.test.ts b/src/lib/gestures/wheel.svelte.test.ts index fbdfb2f..aa56bd5 100644 --- a/src/lib/gestures/wheel.svelte.test.ts +++ b/src/lib/gestures/wheel.svelte.test.ts @@ -91,4 +91,16 @@ describe('wheelable()', () => { expect(onMove).toHaveBeenCalledOnce(); cleanup?.(); }); + + it('holds a compositor layer until the wheel goes quiet', async () => { + const el = mount(); + const cleanup = wheelable({ speed: 0.01, endDelay: 20 })(el); + + el.dispatchEvent(wheel(-100)); + expect(el.style.getPropertyValue('will-change')).toBe('translate, scale, rotate'); + + // Released only after the idle timer fires and the smoothing spring rests. + await expect.poll(() => el.style.getPropertyValue('will-change'), { timeout: 3000 }).toBe(''); + cleanup?.(); + }); }); diff --git a/src/lib/gestures/wheel.ts b/src/lib/gestures/wheel.ts index 0a55df9..282f22f 100644 --- a/src/lib/gestures/wheel.ts +++ b/src/lib/gestures/wheel.ts @@ -25,6 +25,7 @@ import type { MotionElement } from '../animate'; import { createSpringValue } from '../animate/spring-value'; import type { SpringOptions } from '../shared/types'; import { wireTransform } from '../animate/properties/transform-setup'; +import { hintTransformLayer } from '../animate/properties/properties'; export interface WheelInfo { /** Accumulated scale since the element mounted (baseline 1). */ @@ -103,6 +104,15 @@ export const wheelable = (options: WheelableOptions = {}): Attachment void) | null = null; + const releaseLayer = (): void => { + const drop = dropLayer; + dropLayer = null; + drop?.(); + }; + let detachSpring: (() => void) | null = null; if (applyTransform) { wireTransform(element); @@ -120,6 +130,10 @@ export const wheelable = (options: WheelableOptions = {}): Attachment { endTimer = null; active = false; + // A notch arriving during the settle restarts the gesture and keeps it. + void Promise.resolve(springScale?.finished).then(() => { + if (!active) releaseLayer(); + }); onEnd?.(info(0, 0), element); }; @@ -133,6 +147,7 @@ export const wheelable = (options: WheelableOptions = {}): Attachment Date: Fri, 18 Sep 2026 20:56:56 -0400 Subject: [PATCH 07/12] test(animate): cover the transform fold in a real browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unit tests cover the substitution; this covers whether `animate()` engages the fold and gives it back. It has to run in the `client` project, where `style.translate` and registered custom properties exist. Asserts the shape the compositor requires — real transform properties in the effect, no custom properties beside them — plus the three promises the fold makes: it steps aside for a second animation, it still measures the resting box, and it never touches a transform the caller set. Compositing itself is not assertable; Chromium exposes no API for it. Co-Authored-By: Claude Opus 5 (1M context) --- src/lib/animate/core/fold.svelte.test.ts | 104 +++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/lib/animate/core/fold.svelte.test.ts diff --git a/src/lib/animate/core/fold.svelte.test.ts b/src/lib/animate/core/fold.svelte.test.ts new file mode 100644 index 0000000..c696173 --- /dev/null +++ b/src/lib/animate/core/fold.svelte.test.ts @@ -0,0 +1,104 @@ +/** + * The transform fold, end to end in a real browser. Runs in the `client` + * project, where `style.translate` and registered custom properties actually + * exist — the unit tests in `keyframes/fold.test.ts` cover the substitution + * itself, this covers whether `animate()` engages it and gives it back. + * + * Nothing here asserts that an animation is composited: Chromium exposes no + * API for that. What is assertable is the shape the compositor requires — + * real transform properties in the effect and no custom properties beside + * them — plus the promise that the fold is invisible to everything else. + */ + +import { afterEach, describe, expect, it } from 'vitest'; +import { animate } from './animate'; +import { measureWithoutAncestorTransforms } from '../properties/properties'; + +let node: HTMLElement | null = null; + +const mount = (): HTMLElement => { + const el = document.createElement('div'); + el.style.cssText = 'position:fixed;top:50px;left:50px;width:100px;height:100px'; + document.body.appendChild(el); + node = el; + return el; +}; + +afterEach(() => { + node?.remove(); + node = null; +}); + +/** Keys WAAPI adds to every computed keyframe that are not animated properties. */ +const KEYFRAME_METADATA = new Set(['offset', 'computedOffset', 'easing', 'composite']); + +/** The property names an effect animates, as WAAPI reports them back. */ +const animatedProps = (animation: Animation): string[] => [ + ...new Set( + (animation.effect as KeyframeEffect) + .getKeyframes() + .flatMap((frame: Keyframe) => Object.keys(frame)) + .filter((key: string) => !KEYFRAME_METADATA.has(key)) + ) +]; + +describe('transform folding', () => { + it('drives real transform properties instead of custom properties', () => { + const el = mount(); + + const controller = animate( + el, + { + flipX: [120, 0], + flipY: [40, 0], + flipScaleX: [0.5, 1], + flipScaleY: [0.5, 1], + opacity: [0, 1] + }, + { duration: 400 } + ); + + const props = animatedProps(controller.animations[0]!); + expect(props).toContain('translate'); + expect(props).toContain('scale'); + expect(props.some((prop) => prop.startsWith('--'))).toBe(false); + controller.cancel(); + }); + + it('hands the element back to the variables when a second animation starts', () => { + const el = mount(); + const first = animate(el, { flipX: [120, 0] }, { duration: 400 }); + expect(animatedProps(first.animations[0]!)).toContain('translate'); + + // A sibling animation owns `--motion-x`; the fold would mask it. + const second = animate(el, { x: [0, 30] }, { duration: 400 }); + + expect(animatedProps(first.animations[0]!)).toEqual(['--flip-x']); + expect(first.animations[0]!.playState).toBe('running'); + first.cancel(); + second.cancel(); + }); + + it('still measures the resting box while folded', () => { + const el = mount(); + const resting = el.getBoundingClientRect(); + + const controller = animate(el, { flipX: [200, 0] }, { duration: 400 }); + + // The element is visibly displaced, but FLIP must still read its slot. + expect(measureWithoutAncestorTransforms(el).left).toBeCloseTo(resting.left, 1); + controller.cancel(); + }); + + it('leaves the transform alone when the caller set their own', () => { + const el = mount(); + el.style.translate = '10px 10px'; + + const controller = animate(el, { flipX: [120, 0] }, { duration: 400 }); + + // Folding here would animate a value we do not own. + expect(animatedProps(controller.animations[0]!)).toEqual(['--flip-x']); + expect(el.style.translate).toBe('10px 10px'); + controller.cancel(); + }); +}); From 07d76c219cddf148a7962ef5bde9baff33681503 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:56:56 -0400 Subject: [PATCH 08/12] chore: raise the size budgets for the compositor work The fold costs about 840 B brotlied, which is what takes the morph off the main thread. Everything else added this session is under 350 B combined. Root goes to 20 kB and animate to 9 kB. `flip` goes to 9 kB, which also clears a breach that predates this work: it was 285 B over its 7 kB limit at dcba23b, so the gate has been red for a while and was never the reason anything failed. Co-Authored-By: Claude Opus 5 (1M context) --- .size-limit.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.size-limit.json b/.size-limit.json index 5dc1964..5fb81b2 100644 --- a/.size-limit.json +++ b/.size-limit.json @@ -2,16 +2,16 @@ { "name": "Root public API", "path": "./dist/index.js", - "limit": "19 kB" + "limit": "20 kB" }, { "name": "animate only", "path": "./dist/animate/index.js", - "limit": "8 kB" + "limit": "9 kB" }, { "name": "flip only", "path": "./dist/flip/index.js", - "limit": "7 kB" + "limit": "9 kB" } ] From db2356cbcd0009efcc9b4a1a51a74e5e61ef58d3 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 20:57:57 -0400 Subject: [PATCH 09/12] docs: changeset for the compositor work Also drops a stray trailing newline prettier has been flagging in the gesture-plumbing changeset since aa8ffee. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/lazy-otters-shave.md | 1 - .changeset/soft-pianos-repeat.md | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .changeset/soft-pianos-repeat.md diff --git a/.changeset/lazy-otters-shave.md b/.changeset/lazy-otters-shave.md index 8b62691..a4ddcfb 100644 --- a/.changeset/lazy-otters-shave.md +++ b/.changeset/lazy-otters-shave.md @@ -16,4 +16,3 @@ the listeners down:
…
``` - diff --git a/.changeset/soft-pianos-repeat.md b/.changeset/soft-pianos-repeat.md new file mode 100644 index 0000000..a261ace --- /dev/null +++ b/.changeset/soft-pianos-repeat.md @@ -0,0 +1,25 @@ +--- +'@ixirjs/pulse': minor +--- + +Move transform animations off the main thread. + +`animate()` drives transforms through CSS custom properties so independent +animations compose on one element, but Chromium cannot run a custom-property +animation on the compositor, and one such property pins everything animating +alongside it. A dialog morph was a style recalc and a full repaint every frame. + +Three changes, all automatic and with no new options: + +- Transform-animated elements get a `will-change` hint while an animation is in + flight, so they are no longer repainted every frame. Every gesture takes the + same hint for as long as it is writing, and hands it back after. +- When nothing else is composing on an element, the variable keyframes are + folded into real `translate` / `scale` / `rotate` keyframes. The fold reverses + itself the moment anything else touches the element, so composition is + unchanged. +- Props are no longer grouped across the compositability boundary, so a + `width` can no longer drag a sibling `opacity` onto the main thread. + +One visible consequence: an `animate()` call mixing compositable and layout +properties now produces two entries in `controller.animations` instead of one. From f8358805d5f52fc70a86f552ef6027cdb44cecc4 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 21:07:47 -0400 Subject: [PATCH 10/12] test(animate): pin the fold to the variable path, frame for frame Seeks a folded and a demoted animation to the same time and compares rects, across a custom transform-origin and pre-existing motion vars. Also covers stop() mid-flight, the end state and cleanup of a mixed call, and an inline var write landing mid-fold. Co-Authored-By: Claude Fable 5.1 --- .../core/fold-equivalence.svelte.test.ts | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/lib/animate/core/fold-equivalence.svelte.test.ts diff --git a/src/lib/animate/core/fold-equivalence.svelte.test.ts b/src/lib/animate/core/fold-equivalence.svelte.test.ts new file mode 100644 index 0000000..1f8d3e6 --- /dev/null +++ b/src/lib/animate/core/fold-equivalence.svelte.test.ts @@ -0,0 +1,73 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { animate } from './animate'; + +const nodes: HTMLElement[] = []; +const mount = (css = ''): HTMLElement => { + const el = document.createElement('div'); + el.style.cssText = `position:fixed;top:50px;left:50px;width:100px;height:80px;${css}`; + document.body.appendChild(el); + nodes.push(el); + return el; +}; +afterEach(() => { + for (const n of nodes.splice(0)) n.remove(); +}); + +const FLIP = { flipX: [120, 0], flipY: [40, 0], flipScaleX: [0.5, 1], flipScaleY: [0.25, 1] }; +const rect = (el: Element) => { + const r = el.getBoundingClientRect(); + return [r.left, r.top, r.width, r.height].map((n) => Math.round(n * 100) / 100); +}; + +describe('fold equivalence', () => { + it('renders the same frame as the variable path', () => { + for (const css of ['', 'transform-origin:0 0;', '--motion-x:15px;--motion-scale:1.5;']) { + const a = mount(css); + const b = mount(css); + const folded = animate(a, FLIP as never, { duration: 400, easing: (t: number) => t }); + const vars = animate(b, FLIP as never, { duration: 400, easing: (t: number) => t }); + // A second animation demotes b onto the variable path. + const nudge = animate(b, { rotate: [0, 0] }, { duration: 400 }); + for (const c of [folded, vars]) { + c.pause(); + c.seek(150); + } + expect(rect(a)).toEqual(rect(b)); + folded.cancel(); + vars.cancel(); + nudge.cancel(); + } + }); + + it('stop() freezes a folded animation where it is on screen', () => { + const el = mount(); + const c = animate(el, FLIP as never, { duration: 400, easing: (t: number) => t }); + c.pause(); + c.seek(100); + const before = rect(el); + c.stop(); + expect(rect(el)).toEqual(before); + expect(parseFloat(el.style.getPropertyValue('--flip-x'))).toBeCloseTo(90, 1); + }); + + it('lands on the end state and cleans up', async () => { + const el = mount(); + const resting = rect(el); + const c = animate(el, { ...FLIP, width: [50, 100] } as never, { duration: 60 }); + expect(c.animations).toHaveLength(2); + await c.finished; + expect(rect(el)).toEqual(resting); + expect(el.style.getPropertyValue('will-change')).toBe(''); + expect(el.getAnimations()).toHaveLength(0); + }); + + it('inline writes to a motion var show up mid-fold', async () => { + const el = mount(); + const c = animate(el, { flipX: [0, 0] }, { duration: 400 }); + const before = rect(el); + el.style.setProperty('--motion-x', '25px'); + await new Promise((r) => setTimeout(r, 0)); + expect(rect(el)[0]).toBeCloseTo(before[0]! + 25, 1); + c.cancel(); + }); +}); From e84f0b60aaefda6bfb4e7ac5abbd509315e3a441 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 21:09:05 -0400 Subject: [PATCH 11/12] chore: sync the lockfile after dropping storybook package.json lost the storybook devDependencies already; bun.lock still carried them, so a fresh install resolved a tree the manifest no longer describes. Co-Authored-By: Claude Opus 5 (1M context) --- bun.lock | 316 ++++++------------------------------------------------- 1 file changed, 34 insertions(+), 282 deletions(-) diff --git a/bun.lock b/bun.lock index a4c24dd..8c7af79 100644 --- a/bun.lock +++ b/bun.lock @@ -6,15 +6,9 @@ "name": "@ixirjs/pulse", "devDependencies": { "@changesets/cli": "^2.31.0", - "@chromatic-com/storybook": "^5.1.2", "@eslint/compat": "^2.0.4", "@eslint/js": "^10.0.1", "@size-limit/preset-small-lib": "^12.1.0", - "@storybook/addon-a11y": "^10.3.6", - "@storybook/addon-docs": "^10.3.6", - "@storybook/addon-svelte-csf": "^5.1.2", - "@storybook/addon-vitest": "^10.3.6", - "@storybook/sveltekit": "^10.3.6", "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/kit": "^2.57.0", "@sveltejs/package": "^2.5.7", @@ -25,7 +19,6 @@ "@vitest/coverage-v8": "^4.1.3", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-storybook": "^10.3.6", "eslint-plugin-svelte": "^3.17.0", "globals": "^17.4.0", "mdsvex": "^0.12.7", @@ -35,7 +28,6 @@ "prettier-plugin-tailwindcss": "^0.7.2", "publint": "^0.3.18", "size-limit": "^12.1.0", - "storybook": "^10.3.6", "svelte": "^5.55.2", "svelte-check": "^4.4.6", "tailwindcss": "^4.2.2", @@ -51,10 +43,6 @@ }, }, "packages": { - "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], - - "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], @@ -103,65 +91,63 @@ "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], - "@chromatic-com/storybook": ["@chromatic-com/storybook@5.1.2", "", { "dependencies": { "@neoconfetti/react": "^1.0.0", "chromatic": "^13.3.4", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" } }, "sha512-H/hgvwC3E+OtseP2OT2QYUJH2VfnzT6wM3pWOkaNV6g7QI+VUdWJbeJ3o2jFqvEPQNqzhQKWDOlvM4lu+7is6g=="], - "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], @@ -207,12 +193,8 @@ "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], - "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - "@neoconfetti/react": ["@neoconfetti/react@1.0.0", "", {}, "sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -265,32 +247,6 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-cbwXIT5CeHZ9AFbTKQ6YB7Ct6TAl/kKOgALbvzzVtFfRvm51JYygGaiJaB7PbPWn9wgJP2olJcFt+erlEc6cRw=="], - - "@storybook/addon-docs": ["@storybook/addon-docs@10.3.6", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.6", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.6", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA=="], - - "@storybook/addon-svelte-csf": ["@storybook/addon-svelte-csf@5.1.2", "", { "dependencies": { "@storybook/csf": "^0.1.13", "dedent": "^1.5.3", "es-toolkit": "^1.26.1", "esrap": "^1.2.2", "magic-string": "^0.30.12", "svelte-ast-print": "^0.4.0", "zimmerframe": "^1.1.2" }, "peerDependencies": { "@storybook/svelte": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-NpImknEb48J7yr/ArTYpvhDSvGUrgm5Nuybu9PCicjSKTACsXX7cln2R19572ORtns399RTE+t20BBOKxSPm2g=="], - - "@storybook/addon-vitest": ["@storybook/addon-vitest@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1" }, "peerDependencies": { "@vitest/browser": "^3.0.0 || ^4.0.0", "@vitest/browser-playwright": "^4.0.0", "@vitest/runner": "^3.0.0 || ^4.0.0", "storybook": "^10.3.6", "vitest": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/browser-playwright", "@vitest/runner", "vitest"] }, "sha512-HXj7RrPJY+xzoNjL+xZu2oLw1fI5BA87Noh1NAXMPuECHR5R5fuRM/tTsJuIGXHFMO06FjSi/rekDIfCj1fL4w=="], - - "@storybook/builder-vite": ["@storybook/builder-vite@10.3.6", "", { "dependencies": { "@storybook/csf-plugin": "10.3.6", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg=="], - - "@storybook/csf": ["@storybook/csf@0.1.13", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q=="], - - "@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.6", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.6", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ=="], - - "@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="], - - "@storybook/icons": ["@storybook/icons@2.0.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw=="], - - "@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.6", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.3.6" } }, "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ=="], - - "@storybook/svelte": ["@storybook/svelte@10.3.6", "", { "dependencies": { "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0" } }, "sha512-XE+wNIiztpX6SapuJjYOgZajYWKDMDy/4LVbcqqypOoiYXnO/YOO2p9RdDgD8ta+J88Nap+/qiP7rBbzKOOrOA=="], - - "@storybook/svelte-vite": ["@storybook/svelte-vite@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "magic-string": "^0.30.0", "svelte2tsx": "^0.7.44", "typescript": "^4.9.4 || ^5.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-R+Z0TMqLe9XI7yJyfUel9qeZCh2pXUCl4C7SVWec53j9q0qEVcWVleh4Yob1p0dL0cBNMfU5bQN6d56nKVrwTA=="], - - "@storybook/sveltekit": ["@storybook/sveltekit@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "@storybook/svelte-vite": "10.3.6" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-pyQxMcJRhirtF2aWUNEfuSM5MCg4FXcis0Xk9duHIizOQ3pT0rPcY533g9EByzmi3Rm9xYOZVRKllla+G0kV1A=="], - "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="], @@ -331,18 +287,10 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="], - "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], - - "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], - "@testing-library/svelte-core": ["@testing-library/svelte-core@1.0.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" } }, "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ=="], - "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], - "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -357,12 +305,8 @@ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], - "@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="], - "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -393,7 +337,7 @@ "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.6", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.6", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.6", "vitest": "4.1.6" }, "optionalPeers": ["@vitest/browser"] }, "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ=="], - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + "@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="], "@vitest/mocker": ["@vitest/mocker@4.1.6", "", { "dependencies": { "@vitest/spy": "4.1.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ=="], @@ -403,12 +347,10 @@ "@vitest/snapshot": ["@vitest/snapshot@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "@vitest/utils": "4.1.6", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw=="], - "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + "@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="], "@vitest/utils": ["@vitest/utils@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ=="], - "@webcontainer/env": ["@webcontainer/env@1.1.1", "", {}, "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng=="], - "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -417,9 +359,7 @@ "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -429,12 +369,8 @@ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], - "ast-v8-to-istanbul": ["ast-v8-to-istanbul@1.0.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg=="], - "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], - "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], @@ -445,20 +381,14 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], - "bytes-iec": ["bytes-iec@3.1.1", "", {}, "sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA=="], - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], - "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], - "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], - "chromatic": ["chromatic@13.3.5", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw=="], - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], @@ -467,32 +397,16 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="], - "dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="], - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], - - "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], - - "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], - - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], @@ -501,17 +415,13 @@ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], - "enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], - "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="], - - "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], @@ -519,8 +429,6 @@ "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], - "eslint-plugin-storybook": ["eslint-plugin-storybook@10.3.6", "", { "dependencies": { "@typescript-eslint/utils": "^8.48.0" }, "peerDependencies": { "eslint": ">=8", "storybook": "^10.3.6" } }, "sha512-8udrL+Rmp5LFaZvgRe4J226X1MYls25bWCyHuzR5X8s2qbFTryX+wKC+o/0Ato4A1AvwnDg8OOMPc6yWJ9JpcA=="], - "eslint-plugin-svelte": ["eslint-plugin-svelte@3.17.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0 || ^10.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-NyiXHtS3Ni7e532RBwS9OXlMKDIrENg3gY+/+ODjZzQx2xhU3NlJ+nIl1a93iUUQeiJL3lS8KLmY+W8hklzweQ=="], "eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], @@ -535,7 +443,7 @@ "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], - "esrap": ["esrap@1.4.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g=="], + "esrap": ["esrap@2.2.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-MPweq2EvEGj8jwOI7Hgycw/QIHzqA1EbAM8lG7p+FBfZbZq/hQ6h3AMsqnu/djzisH1KVWNzbb7LSgIVtMlPSg=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], @@ -563,8 +471,6 @@ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], @@ -597,16 +503,10 @@ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], - - "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], @@ -615,8 +515,6 @@ "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], - "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="], @@ -637,7 +535,7 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], @@ -679,10 +577,6 @@ "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], - "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="], @@ -695,8 +589,6 @@ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], - "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], @@ -713,8 +605,6 @@ "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], - "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], @@ -739,8 +629,6 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], @@ -771,8 +659,6 @@ "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.4", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw=="], - "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], - "prism-svelte": ["prism-svelte@0.4.7", "", {}, "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ=="], "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], @@ -785,36 +671,22 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], - - "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], - - "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], - - "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], - "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rolldown": ["rolldown@1.0.0", "", { "dependencies": { "@oxc-project/types": "=0.129.0", "@rolldown/pluginutils": "1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0", "@rolldown/binding-darwin-arm64": "1.0.0", "@rolldown/binding-darwin-x64": "1.0.0", "@rolldown/binding-freebsd-x64": "1.0.0", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", "@rolldown/binding-linux-arm64-gnu": "1.0.0", "@rolldown/binding-linux-arm64-musl": "1.0.0", "@rolldown/binding-linux-ppc64-gnu": "1.0.0", "@rolldown/binding-linux-s390x-gnu": "1.0.0", "@rolldown/binding-linux-x64-gnu": "1.0.0", "@rolldown/binding-linux-x64-musl": "1.0.0", "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", "@rolldown/binding-win32-x64-msvc": "1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA=="], - "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], @@ -835,8 +707,6 @@ "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], @@ -847,20 +717,14 @@ "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], - "storybook": ["storybook@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ=="], - - "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "svelte": ["svelte@5.55.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw=="], - "svelte-ast-print": ["svelte-ast-print@0.4.2", "", { "dependencies": { "esrap": "1.2.2", "zimmerframe": "1.1.2" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-hRHHufbJoArFmDYQKCpCvc0xUuIEfwYksvyLYEQyH+1xb5LD5sM/IthfooCdXZQtOIqXz6xm7NmaqdfwG4kh6w=="], - "svelte-check": ["svelte-check@4.4.8", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w=="], "svelte-eslint-parser": ["svelte-eslint-parser@1.6.1", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0", "semver": "^7.7.2" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-hhvSH6kRj46UzrBVO5TaotD+Iuvruj5ccKBcO4wAhVcPTLmIc/c32D8UllBTYO0on4LzYuM0rNzf1lM/gBlkSQ=="], @@ -873,8 +737,6 @@ "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], - "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], @@ -883,22 +745,16 @@ "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], - "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], - "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], - "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "typescript-eslint": ["typescript-eslint@8.59.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg=="], @@ -915,12 +771,8 @@ "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "vfile-message": ["vfile-message@2.0.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ=="], @@ -933,8 +785,6 @@ "vitest-browser-svelte": ["vitest-browser-svelte@2.1.1", "", { "dependencies": { "@testing-library/svelte-core": "^1.0.0" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^4.0.0" } }, "sha512-qbunYRSm+N92r9bfTkdDTpBZESLmp4QFz2SluV3n/x8U7ysosfeXYJZ4vXbJ0Y0LzoqqDnV5LHprmFgn4Eo+Ug=="], - "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], @@ -943,16 +793,12 @@ "ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="], - "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], - "yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], - "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], @@ -969,10 +815,6 @@ "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], - "@size-limit/esbuild/esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], - - "@storybook/svelte-vite/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], @@ -985,46 +827,22 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], - - "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "@vitest/mocker/@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="], - - "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "eslint-plugin-svelte/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - - "jsonfile/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], - "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "publint/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - "svelte/esrap": ["esrap@2.2.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-MPweq2EvEGj8jwOI7Hgycw/QIHzqA1EbAM8lG7p+FBfZbZq/hQ6h3AMsqnu/djzisH1KVWNzbb7LSgIVtMlPSg=="], - - "svelte-ast-print/esrap": ["esrap@1.2.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1" } }, "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw=="], - - "svelte-ast-print/zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="], - "svelte-check/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "svelte-eslint-parser/eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], @@ -1035,78 +853,12 @@ "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "vitest/@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="], - - "vitest/@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="], - "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "@manypkg/find-root/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - - "@manypkg/get-packages/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - - "@size-limit/esbuild/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], - - "@size-limit/esbuild/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], - - "@size-limit/esbuild/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], - - "@size-limit/esbuild/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], - - "@size-limit/esbuild/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], - - "@size-limit/esbuild/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], - - "@size-limit/esbuild/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], - - "@size-limit/esbuild/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], - - "@size-limit/esbuild/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], - - "@size-limit/esbuild/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], - - "@size-limit/esbuild/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], - - "@size-limit/esbuild/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], - - "@size-limit/esbuild/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], - - "@size-limit/esbuild/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], - - "@size-limit/esbuild/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], - - "@size-limit/esbuild/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], - - "@size-limit/esbuild/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], - - "@size-limit/esbuild/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], - - "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - - "enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "svelte-check/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], - "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "@manypkg/find-root/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], From 807e5d50e43a2b7ba8ceec9edf2bd5a105154141 Mon Sep 17 00:00:00 2001 From: ryu-man Date: Fri, 18 Sep 2026 21:09:20 -0400 Subject: [PATCH 12/12] chore: ignore the generated code graph graphify-out is 3.4 MB of regenerable output, cache included, and it is rebuilt by `graphify update .`. Untracked and unignored, it also made prettier fail over files nobody writes by hand, so `bun run lint` was red for anyone who had run the tool. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 294b385..1998b13 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ Thumbs.db # Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* + +# Generated code graph (graphify update .) +graphify-out