From a32eed3d04661349c73692dc80319f6d3f1879bf Mon Sep 17 00:00:00 2001 From: John Leider Date: Mon, 23 Mar 2026 09:16:31 -0500 Subject: [PATCH 01/14] docs: add v0 locale integration design spec Design for migrating Vuetify's locale/RTL system to use @vuetify/v0 composables as the runtime baseline. Bumps v0 to ^0.1.7 for RTL support. --- ...2026-03-16-v0-locale-integration-design.md | 276 +++++ packages/vuetify/package.json | 2 +- pnpm-lock.yaml | 949 +++++++++++------- 3 files changed, 873 insertions(+), 354 deletions(-) create mode 100644 docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md diff --git a/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md b/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md new file mode 100644 index 00000000000..544effdfd3d --- /dev/null +++ b/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md @@ -0,0 +1,276 @@ +# v0 Locale Integration Design + +## Overview + +Migrate Vuetify's locale and RTL system to use `@vuetify/v0` composables as the runtime baseline. Vuetify becomes a thin configuration layer providing language packs and per-locale RTL defaults, while v0 owns message resolution, RTL state, number formatting, decimal separators, and the adapter pattern. + +## Architecture + +### Layers + +**v0 (runtime — source of truth):** + +- `createLocale()` — message storage (tokens), locale selection (createSingle), translation (`t()`), number formatting (`n()`), decimal separator inference +- `createRtl()` — reactive boolean direction state, `dir` attribute management via adapter +- `createLocalePlugin()` / `createRtlPlugin()` — Vue plugin installation for DI context +- `LocaleAdapter` — adapter interface for i18n provider integration (including vue-i18n) +- `Locale` provider component — scoped locale + RTL context for subtrees + +**Vuetify (configuration + thin wrapper):** + +- 48 language packs registered as v0 token messages +- Per-locale RTL map (`ar: true`, `fa: true`, `he: true`, etc.) +- `$vuetify.` prefix stripping before delegating to v0's `t()` +- `rtlClasses` computed ref (`v-locale--is-rtl` / `v-locale--is-ltr`) +- Bridge: watches v0 locale selection, updates v0 RTL from per-locale map +- `LocaleSymbol` provide/inject for Vuetify component consumption +- `VLocaleProvider` wrapping v0's `Locale` component with Vuetify CSS classes + +**RTL layering:** v0 manages the document-level `dir` attribute via `Vuetify0RtlAdapter`. Vuetify manages component-level CSS classes (`v-locale--is-rtl` / `v-locale--is-ltr`). These two layers are intentionally independent — a `VLocaleProvider` subtree can have a different direction class than the document `dir` attribute. + +## Initialization + +In `createVuetify()`: + +1. Create v0 locale instance with Vuetify's message packs: + ```ts + const v0Locale = createLocale({ + default: options.locale ?? 'en', + messages: { en, ...options.messages }, + }) + ``` + + Vuetify stores the fallback locale separately (not part of v0's `LocaleOptions`): + ```ts + const fallbackLocale = shallowRef(options.fallback ?? 'en') + ``` + +2. Create v0 RTL instance: + ```ts + const v0Rtl = createRtl({ + default: rtlMap[options.locale ?? 'en'] ?? false, + }) + ``` + +3. Bridge locale → RTL with `immediate: true` to cover initial state: + ```ts + watch(v0Locale.selectedId, locale => { + v0Rtl.isRtl.value = rtlMap[locale] ?? false + }, { immediate: true }) + ``` + +4. Compute Vuetify CSS classes: + ```ts + const rtlClasses = toRef(() => + `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}` + ) + ``` + +5. Provide combined object via `LocaleSymbol` + +6. Install v0 plugins (`createLocalePlugin`, `createRtlPlugin`) so v0's context is also available directly + +## Translation Signature Bridging + +### Problem + +Vuetify's `t()` uses positional params: `t(key: string, ...params: unknown[])` with `{0}`, `{1}` placeholders. + +v0's `t()` uses named params: `t(key: string, params?: Record, fallback?: string)`. + +Components call Vuetify's `t()` like: +```ts +t(props.pageText, startIndex + 1, stopIndex, itemsLength) +// where pageText = '$vuetify.dataFooter.pageText' → '{0}-{1} of {2}' +``` + +### Solution + +Vuetify's wrapper `t()` strips the prefix and passes positional args directly: + +```ts +function t(key: string, ...params: unknown[]): string { + const stripped = key.startsWith('$vuetify.') ? key.slice(9) : key + return v0Locale.t(stripped, params.length ? params : undefined) +} +``` + +v0's `t()` calls `toArray(params)` then spreads as `adapter.t(template, ...args)`. The adapter's positional regex (`/\{(\d+)\}/g`) matches `{0}`, `{1}`, etc. against the spread args. Passing an array of positional values works correctly at runtime. + +**Type note:** v0's `t()` type declares `params` as `Record`, but the runtime handles arrays via `toArray()`. v0 should widen the type to `Record | unknown[]` — listed as a v0 prerequisite. + +**Non-prefixed keys:** When a key doesn't start with `$vuetify.`, it's treated as a raw template string (current behavior preserved). The wrapper passes it through to v0's `t()` which interpolates params directly. + +## Fallback Locale Resolution + +### Problem + +v0's `createLocale()` does not implement automatic fallback locale lookup. When a token path like `fr.dataTable.itemsPerPageText` is missing, v0 returns the raw key. Vuetify relies on fallback to English for untranslated keys. + +### Solution — v0 Prerequisite + +v0 must implement fallback locale resolution: when `{current}.{key}` is not found, automatically try `{fallback}.{key}` before returning the raw key. This is listed as a v0 prerequisite. + +If v0's fallback implementation is not ready in time, Vuetify's wrapper can temporarily implement the fallback chain by doing a direct token lookup for the fallback locale (avoiding reactive side-effects from `select()`): + +```ts +function t(key: string, ...params: unknown[]): string { + const stripped = key.startsWith('$vuetify.') ? key.slice(9) : key + const args = params.length ? params : undefined + + const result = v0Locale.t(stripped, args) + + // If v0 returned the raw key (not found), try fallback via direct token lookup + if (result === stripped && fallbackLocale.value !== v0Locale.selectedId.value) { + const fallbackPath = `${fallbackLocale.value}.${stripped}` + const fallbackMessage = v0Tokens.get(fallbackPath)?.value + if (isString(fallbackMessage)) { + return v0Adapter.t(fallbackMessage, ...params) + } + } + + return result +} +``` + +This requires holding references to the v0 tokens and adapter instances, but avoids mutating `selectedId` (which would trigger watchers and reactivity side-effects). + +**Note:** v0's `resolve()` function already supports cross-locale token references (`{en.some.key}`), which could be leveraged in message templates as a partial workaround. The proper fix is v0 supporting fallback natively. + +## Message Resolution Flow + +``` +component calls t('$vuetify.dataTable.itemsPerPageText', 1, 10, 100) + → Vuetify wrapper strips '$vuetify.' prefix → 'dataTable.itemsPerPageText' + → Vuetify wrapper passes positional args → [1, 10, 100] + → v0 locale.t('dataTable.itemsPerPageText', [1, 10, 100]) + → v0 looks up token: '{currentLocale}.dataTable.itemsPerPageText' + → (if missing, v0 tries '{fallbackLocale}.dataTable.itemsPerPageText') + → v0 adapter interpolates {0}, {1}, {2} from positional args + → '1-10 of 100' returned +``` + +## Consumer Composables + +### useLocale() + +Thin wrapper — injects `LocaleSymbol`, returns combined locale + RTL context: + +```ts +function useLocale() { + const locale = inject(LocaleSymbol) + if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') + return locale +} +``` + +**Consumer-facing API:** `const { t, n, current, isRtl, rtlClasses } = useLocale()` + +- `t` — Vuetify's wrapped `t()` with `$vuetify.` prefix handling and positional param conversion +- `n` — delegates to v0's `n()` +- `current` — writable computed aliased from v0's `selectedId`: + ```ts + const current = computed({ + get: () => v0Locale.selectedId.value, + set: v => v0Locale.select(v), + }) + ``` + v0's `selectedId` is a read-only computed, so writes go through `select()`. +- `isRtl` — from v0's RTL context +- `rtlClasses` — Vuetify-computed CSS class string +- `decimalSeparator` — from v0's locale context (or Vuetify-computed interim, see "Decimal Separator" section) + +### useRtl() + +Thin wrapper — injects `LocaleSymbol`, returns `{ isRtl, rtlClasses }`. + +### provideLocale() + +Replaced by v0's `Locale` provider component. Vuetify wraps it to add `rtlClasses` and provide via `LocaleSymbol`. See "VLocaleProvider" section. + +## vue-i18n Adapter + +Moves to v0 as a `LocaleAdapter` implementation. Vuetify re-exports or provides a thin wrapper for `$vuetify.` prefix handling. User-facing config stays the same: + +```ts +const vuetify = createVuetify({ + locale: { + adapter: createVueI18nAdapter({ i18n, useI18n }), + }, +}) +``` + +For initial implementation, Vuetify can keep a compatibility shim wrapping v0's adapter until the v0 adapter fully ships. + +## VLocaleProvider + +### Target state + +Migrates to v0 as a `Locale` provider component. Vuetify wraps it as `VLocaleProvider` adding: + +- `v-locale-provider` CSS class +- `v-locale--is-rtl` / `v-locale--is-ltr` class +- Per-locale RTL map lookup on locale prop change + +### Interim implementation + +Until v0 ships the `Locale` component, `VLocaleProvider` creates scoped v0 contexts directly: + +1. Creates a new `createLocale()` instance with the provider's props (locale, messages) +2. Creates a new `createRtl()` with direction from per-locale RTL map (or explicit `rtl` prop) +3. Watches scoped locale selection → updates scoped RTL from per-locale map +4. Provides both scoped v0 contexts and Vuetify's `LocaleSymbol` with the scoped values + `rtlClasses` + +Each `VLocaleProvider` instance maintains its own locale→RTL bridge watcher, independent of the root bridge. + +## Breaking Changes + +- `LocaleInstance` type changes internally (backed by v0 refs) +- `LocaleInstance.provide()` method removed — nested contexts use `VLocaleProvider` / v0's `Locale` component +- `provideLocale()` composable removed — replaced by `VLocaleProvider` / v0's `Locale` component internally +- `messages: Ref` removed from public API — messages are managed by v0's token system; consumers register messages via `createVuetify()` options +- `fallback: Ref` removed from public API — fallback is configured at creation time, not mutated at runtime +- `rtl: Ref>` (per-locale RTL map) removed from public API — the map is Vuetify-internal; consumers use `isRtl` (boolean) and `rtlClasses` (string). `VLocaleProvider` uses the internal map for locale→RTL lookup. +- `RtlSymbol` deprecated — RTL state is accessed via `LocaleSymbol` (current behavior) or v0's `useRtl()` directly +- `decimalSeparator` type changes from `ShallowRef` to `Ref` (computed) — unlikely to break consumers in practice +- `LocaleInstance.name` field (`'vuetify'` / `'vue-i18n'`) removed — adapter identity is a v0 concern +- vue-i18n adapter import path may change +- Consumer-facing destructured API (`t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator`) remains stable + +## Decimal Separator + +v0 must expose `decimalSeparator` on `LocaleContext` (inferred from `Intl.NumberFormat` based on current locale). Vuetify's wrapper passes it through: + +```ts +const { decimalSeparator } = useLocale() +``` + +Listed as a v0 prerequisite. If not ready, Vuetify's wrapper computes it temporarily: + +```ts +const decimalSeparator = toRef(() => { + const formatted = v0Locale.n(0.1) + return formatted.includes(',') ? ',' : '.' +}) +``` + +## v0 Prerequisites + +These must land in v0 before or alongside the Vuetify migration: + +1. `useRtl` composable — **done** (v0.1.7) +2. Fallback locale resolution in `createLocale` — needed (try fallback locale when key not found in current) +3. Widen `t()` params type from `Record` to `Record | unknown[]` — needed +4. Decimal separator inference on `LocaleContext` — needed +5. vue-i18n `LocaleAdapter` implementation — needed +6. `Locale` provider component — needed (interim: Vuetify creates scoped contexts directly) + +## Vuetify Responsibilities (post-migration) + +- Ship 48 language message files +- Map locale codes to RTL defaults +- Strip `$vuetify.` prefix and convert positional params in `t()` wrapper +- Generate `v-locale--is-rtl` / `v-locale--is-ltr` CSS classes +- Alias `current` from v0's `selectedId` +- Wrap v0's `Locale` as `VLocaleProvider` with Vuetify styling +- Bridge locale selection → RTL state via per-locale map diff --git a/packages/vuetify/package.json b/packages/vuetify/package.json index 485ea548f43..d2b440962ae 100755 --- a/packages/vuetify/package.json +++ b/packages/vuetify/package.json @@ -140,7 +140,7 @@ "lint:fix": "concurrently -n \"tsc,eslint\" \"tsgo -p tsconfig.checks.json --noEmit --pretty\" \"eslint --fix src\"" }, "dependencies": { - "@vuetify/v0": "^0.1.5" + "@vuetify/v0": "^0.1.7" }, "devDependencies": { "@date-io/core": "3.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14168fba0d1..ff464d299e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -283,7 +283,7 @@ importers: version: 2.0.4(vue@3.5.25(typescript@5.8.3)) '@vuetify/one': specifier: ^2.12.0 - version: 2.12.0(@mdi/js@7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f))(@vue/compiler-sfc@3.5.25)(lodash-es@4.17.22)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))(vuetify@packages+vuetify) + version: 2.12.0(@mdi/js@7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f))(@vue/compiler-sfc@3.5.25)(lodash-es@4.17.23)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))(vuetify@packages+vuetify) algoliasearch: specifier: ^4.24.0 version: 4.24.0 @@ -341,7 +341,7 @@ importers: version: 4.4.1 '@intlify/unplugin-vue-i18n': specifier: ^11.0.1 - version: 11.0.1(@vue/compiler-dom@3.5.25)(eslint@8.57.1)(rollup@4.55.1)(typescript@5.8.3)(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) + version: 11.0.1(@vue/compiler-dom@3.5.25)(eslint@8.57.1)(rollup@4.59.0)(typescript@5.8.3)(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) '@mdi/js': specifier: 7.4.47 version: 7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f) @@ -374,7 +374,7 @@ importers: version: link:../api-generator '@yankeeinlondon/builder-api': specifier: ^1.4.1 - version: 1.4.1(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) + version: 1.4.1(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) ajv: specifier: ^8.17.1 version: 8.17.1 @@ -434,10 +434,10 @@ importers: version: 1.4.0(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) unplugin-vue-components: specifier: ^0.28.0 - version: 0.28.0(@babel/parser@7.28.5)(rollup@4.55.1)(vue@3.5.25(typescript@5.8.3)) + version: 0.28.0(@babel/parser@7.29.0)(rollup@4.59.0)(vue@3.5.25(typescript@5.8.3)) vite-plugin-md: specifier: ^0.22.5 - version: 0.22.5(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) + version: 0.22.5(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) vite-plugin-pages: specifier: ^0.33.0 version: 0.33.1(@vue/compiler-sfc@3.5.25)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3))) @@ -457,8 +457,8 @@ importers: packages/vuetify: dependencies: '@vuetify/v0': - specifier: ^0.1.5 - version: 0.1.5(vue@3.5.25(typescript@5.8.3)) + specifier: ^0.1.7 + version: 0.1.7(vue@3.5.25(typescript@5.8.3)) devDependencies: '@date-io/core': specifier: 3.2.0 @@ -489,16 +489,16 @@ importers: version: 11.1.12 '@rollup/plugin-alias': specifier: ^5.1.1 - version: 5.1.1(rollup@4.55.1) + version: 5.1.1(rollup@4.59.0) '@rollup/plugin-babel': specifier: ^6.1.0 - version: 6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.55.1) + version: 6.1.0(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0) '@rollup/plugin-node-resolve': specifier: ^16.0.3 - version: 16.0.3(rollup@4.55.1) + version: 16.0.3(rollup@4.59.0) '@rollup/plugin-typescript': specifier: ^12.3.0 - version: 12.3.0(rollup@4.55.1)(tslib@2.8.1)(typescript@5.8.3) + version: 12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@5.8.3) '@testing-library/dom': specifier: npm:@vuetify/testing-library-dom@1.0.3 version: '@vuetify/testing-library-dom@1.0.3' @@ -507,7 +507,7 @@ importers: version: 14.6.1(@vuetify/testing-library-dom@1.0.3) '@testing-library/vue': specifier: ^8.1.0 - version: 8.1.0(patch_hash=15ba1e1b9f291286e3951cdf39b2e27f55b6727bc0315792f3e4558c0a3a7db8)(@vue/compiler-sfc@3.5.26)(vue@3.5.25(typescript@5.8.3)) + version: 8.1.0(patch_hash=15ba1e1b9f291286e3951cdf39b2e27f55b6727bc0315792f3e4558c0a3a7db8)(@vue/compiler-sfc@3.5.30)(vue@3.5.25(typescript@5.8.3)) '@vitejs/plugin-vue-jsx': specifier: ^5.1.2 version: 5.1.2(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)) @@ -537,7 +537,7 @@ importers: version: 2.4.6 '@vuetify/babel-plugin-jsx': specifier: ^1.7.0 - version: 1.7.0(@babel/core@7.28.5) + version: 1.7.0(@babel/core@7.29.0) acorn-walk: specifier: ^8.3.4 version: 8.3.4 @@ -546,7 +546,7 @@ importers: version: 10.4.22(postcss@8.5.6) babel-plugin-add-import-extension: specifier: 1.5.1 - version: 1.5.1(@babel/core@7.28.5) + version: 1.5.1(@babel/core@7.29.0) babel-plugin-module-resolver: specifier: ^5.0.2 version: 5.0.2 @@ -618,7 +618,7 @@ importers: version: 19.3.0 unplugin-vue-components: specifier: ^0.28.0 - version: 0.28.0(@babel/parser@7.28.5)(rollup@4.55.1)(vue@3.5.25(typescript@5.8.3)) + version: 0.28.0(@babel/parser@7.29.0)(rollup@4.59.0)(vue@3.5.25(typescript@5.8.3)) upath: specifier: ^2.0.1 version: 2.0.1 @@ -627,7 +627,7 @@ importers: version: 0.4.0(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) vite-ssr: specifier: ^0.17.2 - version: 0.17.2(@vitejs/plugin-vue@6.0.3(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(@vueuse/head@1.3.1(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(rollup@4.55.1)(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) + version: 0.17.2(@vitejs/plugin-vue@6.0.3(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(@vueuse/head@1.3.1(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(rollup@4.59.0)(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) vitest: specifier: ^4.0.13 version: 4.0.13(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(happy-dom@8.9.0(encoding@0.1.13))(jsdom@27.2.0)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1) @@ -726,18 +726,34 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -746,6 +762,10 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.5': resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} @@ -775,12 +795,22 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} @@ -789,6 +819,10 @@ packages: resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.27.1': resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} engines: {node: '>=6.9.0'} @@ -825,6 +859,10 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + '@babel/highlight@7.25.9': resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} engines: {node: '>=6.9.0'} @@ -834,6 +872,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -919,6 +962,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -1310,14 +1359,26 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1462,8 +1523,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1474,8 +1535,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1486,8 +1547,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1498,8 +1559,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1510,8 +1571,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1522,8 +1583,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1534,8 +1595,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1546,8 +1607,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1558,8 +1619,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1570,8 +1631,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1582,8 +1643,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1594,8 +1655,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1606,8 +1667,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1618,8 +1679,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1630,8 +1691,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1642,8 +1703,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1654,8 +1715,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1666,8 +1727,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1678,8 +1739,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1690,8 +1751,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1702,8 +1763,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1714,8 +1775,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1726,8 +1787,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1738,8 +1799,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1750,8 +1811,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1762,8 +1823,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2581,6 +2642,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5': resolution: {integrity: sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ==} + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -2684,8 +2748,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.55.1': - resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] os: [android] @@ -2694,8 +2758,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.55.1': - resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] @@ -2704,8 +2768,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.55.1': - resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] @@ -2714,8 +2778,8 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.55.1': - resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] @@ -2724,8 +2788,8 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.55.1': - resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] @@ -2734,8 +2798,8 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.55.1': - resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] @@ -2745,8 +2809,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.55.1': - resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] libc: [glibc] @@ -2757,8 +2821,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.55.1': - resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] libc: [musl] @@ -2769,8 +2833,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.55.1': - resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] libc: [glibc] @@ -2781,8 +2845,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.55.1': - resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] libc: [musl] @@ -2793,14 +2857,14 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.55.1': - resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.55.1': - resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] libc: [musl] @@ -2811,14 +2875,14 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.55.1': - resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.55.1': - resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] libc: [musl] @@ -2829,8 +2893,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.55.1': - resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -2841,8 +2905,8 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.55.1': - resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] libc: [musl] @@ -2853,8 +2917,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.55.1': - resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] libc: [glibc] @@ -2865,8 +2929,8 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.55.1': - resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] libc: [glibc] @@ -2877,14 +2941,14 @@ packages: os: [linux] libc: [musl] - '@rollup/rollup-linux-x64-musl@4.55.1': - resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.55.1': - resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} cpu: [x64] os: [openbsd] @@ -2893,8 +2957,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.55.1': - resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] @@ -2903,8 +2967,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.55.1': - resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] @@ -2913,8 +2977,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.55.1': - resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] @@ -2923,8 +2987,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.55.1': - resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] @@ -2933,8 +2997,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.55.1': - resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] @@ -3486,6 +3550,13 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 vue: ^3.2.25 + '@vitejs/plugin-vue@6.0.5': + resolution: {integrity: sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + '@vitest/browser-playwright@4.0.13': resolution: {integrity: sha512-oaRY+/pvwS4/sN2rE2aZh9jdli8EkXm5AidmXEbWRu2wW0omG9PmgChWCX2jsD9qRLQxXTSLl5oKezANNF6LnQ==} peerDependencies: @@ -3576,26 +3647,26 @@ packages: '@vue/compiler-core@3.5.25': resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==} - '@vue/compiler-core@3.5.26': - resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==} + '@vue/compiler-core@3.5.30': + resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} '@vue/compiler-dom@3.5.25': resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==} - '@vue/compiler-dom@3.5.26': - resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==} + '@vue/compiler-dom@3.5.30': + resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} '@vue/compiler-sfc@3.5.25': resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==} - '@vue/compiler-sfc@3.5.26': - resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==} + '@vue/compiler-sfc@3.5.30': + resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==} '@vue/compiler-ssr@3.5.25': resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==} - '@vue/compiler-ssr@3.5.26': - resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==} + '@vue/compiler-ssr@3.5.30': + resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -3643,8 +3714,8 @@ packages: '@vue/shared@3.5.25': resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==} - '@vue/shared@3.5.26': - resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} + '@vue/shared@3.5.30': + resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} '@vue/test-utils@2.4.6': resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} @@ -3725,15 +3796,24 @@ packages: resolution: {integrity: sha512-EHJDH+O7eov7yJ3WPR52qELyHq8beQ/GmletheroKRvp4SxTL9IgOEeXqBfw4bOyd49VNH5coyoqvIIlnhavUQ==} engines: {node: '>=18'} - '@vuetify/v0@0.1.5': - resolution: {integrity: sha512-ZWKRUVrmuPbH8DFEJtYTJx31j4QCgrtk/TIjBWY4oZ5FEpeQHGC+0Vd8CQruaFlHBO+I25xQD+tkEwAnbiRwvg==} + '@vuetify/v0@0.1.7': + resolution: {integrity: sha512-07QrP5Yp9CsdWTuyMiW38PDJs54Lg95iCmqI80WBm130nd89nLk3KXt+pY3vhfSRbM4AM4hFp+zGPj59nf3xyw==} peerDependencies: - flagsmith: ^10.0.0 + '@adobe/leonardo-contrast-colors': '>=1.0.0' + '@ant-design/colors': '>=7.0.0' + '@flagsmith/flagsmith': ^11.0.0 + '@material/material-color-utilities': '>=0.3.0' launchdarkly-js-client-sdk: ^3.0.0 posthog-js: ^1.0.0 vue: '>=3.5.0' peerDependenciesMeta: - flagsmith: + '@adobe/leonardo-contrast-colors': + optional: true + '@ant-design/colors': + optional: true + '@flagsmith/flagsmith': + optional: true + '@material/material-color-utilities': optional: true launchdarkly-js-client-sdk: optional: true @@ -4001,6 +4081,11 @@ packages: balanced-match@1.0.0: resolution: {integrity: sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==} + baseline-browser-mapping@2.10.7: + resolution: {integrity: sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==} + engines: {node: '>=6.0.0'} + hasBin: true + baseline-browser-mapping@2.8.31: resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} hasBin: true @@ -4043,6 +4128,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -4109,6 +4199,9 @@ packages: caniuse-lite@1.0.30001757: resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} + caniuse-lite@1.0.30001778: + resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} + chai@6.2.1: resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} @@ -4658,6 +4751,9 @@ packages: electron-to-chromium@1.5.259: resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.313: + resolution: {integrity: sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -4693,8 +4789,8 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - entities@7.0.0: - resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} env-paths@2.2.1: @@ -4750,8 +4846,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} engines: {node: '>=18'} hasBin: true @@ -6068,8 +6164,8 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash-es@4.17.22: - resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -6472,6 +6568,9 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + nopt@1.0.10: resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} hasBin: true @@ -6981,6 +7080,10 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + preact@10.11.3: resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} @@ -7235,8 +7338,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.55.1: - resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -8068,6 +8171,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.2.2: resolution: {integrity: sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==} @@ -8902,8 +9011,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} + '@babel/compat-data@7.29.0': {} + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -8924,6 +9041,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -8932,6 +9069,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.5 @@ -8944,6 +9089,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -8979,8 +9132,8 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -8991,6 +9144,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -9000,12 +9160,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@babel/helper-plugin-utils@7.27.1': {} + '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -9039,9 +9210,9 @@ snapshots: '@babel/helper-wrap-function@7.27.1': dependencies: - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -9050,6 +9221,11 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.5 + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@babel/highlight@7.25.9': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -9061,6 +9237,10 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -9103,22 +9283,22 @@ snapshots: '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': dependencies: @@ -9133,57 +9313,67 @@ snapshots: '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': dependencies: @@ -9642,6 +9832,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -9654,11 +9850,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} '@bufbuild/protobuf@2.10.1': {} @@ -9783,157 +9996,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.2': + '@esbuild/aix-ppc64@0.27.4': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.2': + '@esbuild/android-arm64@0.27.4': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm@0.27.4': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.2': + '@esbuild/android-x64@0.27.4': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/darwin-arm64@0.27.4': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-x64@0.27.4': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/freebsd-arm64@0.27.4': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-x64@0.27.4': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.2': + '@esbuild/linux-arm64@0.27.4': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm@0.27.4': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-ia32@0.27.4': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-loong64@0.27.4': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-mips64el@0.27.4': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-ppc64@0.27.4': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-riscv64@0.27.4': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-s390x@0.27.4': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-x64@0.27.4': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/netbsd-arm64@0.27.4': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-x64@0.27.4': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/openbsd-arm64@0.27.4': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-x64@0.27.4': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openharmony-arm64@0.27.4': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.2': + '@esbuild/sunos-x64@0.27.4': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.2': + '@esbuild/win32-arm64@0.27.4': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-ia32@0.27.4': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-x64@0.27.4': optional: true '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': @@ -10151,13 +10364,13 @@ snapshots: '@intlify/shared@11.2.1': {} - '@intlify/unplugin-vue-i18n@11.0.1(@vue/compiler-dom@3.5.25)(eslint@8.57.1)(rollup@4.55.1)(typescript@5.8.3)(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))': + '@intlify/unplugin-vue-i18n@11.0.1(@vue/compiler-dom@3.5.25)(eslint@8.57.1)(rollup@4.59.0)(typescript@5.8.3)(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) '@intlify/bundle-utils': 11.0.1(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3))) '@intlify/shared': 11.2.1 '@intlify/vue-i18n-extensions': 8.0.0(@vue/compiler-dom@3.5.25)(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) - '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) '@typescript-eslint/scope-manager': 8.44.0 '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.8.3) debug: 4.4.3 @@ -10903,14 +11116,16 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5': {} - '@rollup/plugin-alias@5.1.1(rollup@4.55.1)': + '@rolldown/pluginutils@1.0.0-rc.2': {} + + '@rollup/plugin-alias@5.1.1(rollup@4.59.0)': optionalDependencies: - rollup: 4.55.1 + rollup: 4.59.0 '@rollup/plugin-babel@5.3.1(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@2.79.2)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 + '@babel/helper-module-imports': 7.28.6 '@rollup/pluginutils': 3.1.0(rollup@2.79.2) rollup: 2.79.2 optionalDependencies: @@ -10918,14 +11133,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@rollup/plugin-babel@6.1.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(rollup@4.55.1)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.27.1 - '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.55.1 + rollup: 4.59.0 transitivePeerDependencies: - supports-color @@ -10939,15 +11154,15 @@ snapshots: optionalDependencies: rollup: 2.79.2 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.55.1)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.55.1 + rollup: 4.59.0 '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': dependencies: @@ -10955,11 +11170,11 @@ snapshots: magic-string: 0.25.9 rollup: 2.79.2 - '@rollup/plugin-replace@3.0.0(rollup@4.55.1)': + '@rollup/plugin-replace@3.0.0(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 3.1.0(rollup@4.55.1) + '@rollup/pluginutils': 3.1.0(rollup@4.59.0) magic-string: 0.25.9 - rollup: 4.55.1 + rollup: 4.59.0 '@rollup/plugin-terser@0.4.4(rollup@2.79.2)': dependencies: @@ -10977,13 +11192,13 @@ snapshots: optionalDependencies: rollup: 4.53.3 - '@rollup/plugin-typescript@12.3.0(rollup@4.55.1)(tslib@2.8.1)(typescript@5.8.3)': + '@rollup/plugin-typescript@12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@5.8.3)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) resolve: 1.22.10 typescript: 5.8.3 optionalDependencies: - rollup: 4.55.1 + rollup: 4.59.0 tslib: 2.8.1 '@rollup/pluginutils@3.1.0(rollup@2.79.2)': @@ -11000,12 +11215,12 @@ snapshots: picomatch: 2.3.1 rollup: 4.53.3 - '@rollup/pluginutils@3.1.0(rollup@4.55.1)': + '@rollup/pluginutils@3.1.0(rollup@4.59.0)': dependencies: '@types/estree': 0.0.39 estree-walker: 1.0.1 picomatch: 2.3.1 - rollup: 4.55.1 + rollup: 4.59.0 '@rollup/pluginutils@5.3.0(rollup@2.79.2)': dependencies: @@ -11023,153 +11238,153 @@ snapshots: optionalDependencies: rollup: 4.53.3 - '@rollup/pluginutils@5.3.0(rollup@4.55.1)': + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.55.1 + rollup: 4.59.0 '@rollup/rollup-android-arm-eabi@4.53.3': optional: true - '@rollup/rollup-android-arm-eabi@4.55.1': + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true '@rollup/rollup-android-arm64@4.53.3': optional: true - '@rollup/rollup-android-arm64@4.55.1': + '@rollup/rollup-android-arm64@4.59.0': optional: true '@rollup/rollup-darwin-arm64@4.53.3': optional: true - '@rollup/rollup-darwin-arm64@4.55.1': + '@rollup/rollup-darwin-arm64@4.59.0': optional: true '@rollup/rollup-darwin-x64@4.53.3': optional: true - '@rollup/rollup-darwin-x64@4.55.1': + '@rollup/rollup-darwin-x64@4.59.0': optional: true '@rollup/rollup-freebsd-arm64@4.53.3': optional: true - '@rollup/rollup-freebsd-arm64@4.55.1': + '@rollup/rollup-freebsd-arm64@4.59.0': optional: true '@rollup/rollup-freebsd-x64@4.53.3': optional: true - '@rollup/rollup-freebsd-x64@4.55.1': + '@rollup/rollup-freebsd-x64@4.59.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.55.1': + '@rollup/rollup-linux-arm-musleabihf@4.59.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.55.1': + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true '@rollup/rollup-linux-arm64-musl@4.53.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.55.1': + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true '@rollup/rollup-linux-loong64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.55.1': + '@rollup/rollup-linux-loong64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.55.1': + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true '@rollup/rollup-linux-ppc64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.55.1': + '@rollup/rollup-linux-ppc64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.55.1': + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.55.1': + '@rollup/rollup-linux-riscv64-gnu@4.59.0': optional: true '@rollup/rollup-linux-riscv64-musl@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.55.1': + '@rollup/rollup-linux-riscv64-musl@4.59.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.53.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.55.1': + '@rollup/rollup-linux-s390x-gnu@4.59.0': optional: true '@rollup/rollup-linux-x64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.55.1': + '@rollup/rollup-linux-x64-gnu@4.59.0': optional: true '@rollup/rollup-linux-x64-musl@4.53.3': optional: true - '@rollup/rollup-linux-x64-musl@4.55.1': + '@rollup/rollup-linux-x64-musl@4.59.0': optional: true - '@rollup/rollup-openbsd-x64@4.55.1': + '@rollup/rollup-openbsd-x64@4.59.0': optional: true '@rollup/rollup-openharmony-arm64@4.53.3': optional: true - '@rollup/rollup-openharmony-arm64@4.55.1': + '@rollup/rollup-openharmony-arm64@4.59.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.53.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.55.1': + '@rollup/rollup-win32-arm64-msvc@4.59.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.53.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.55.1': + '@rollup/rollup-win32-ia32-msvc@4.59.0': optional: true '@rollup/rollup-win32-x64-gnu@4.53.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.55.1': + '@rollup/rollup-win32-x64-gnu@4.59.0': optional: true '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.55.1': + '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true '@rtsao/scc@1.1.0': {} @@ -11266,14 +11481,14 @@ snapshots: dependencies: '@testing-library/dom': '@vuetify/testing-library-dom@1.0.3' - '@testing-library/vue@8.1.0(patch_hash=15ba1e1b9f291286e3951cdf39b2e27f55b6727bc0315792f3e4558c0a3a7db8)(@vue/compiler-sfc@3.5.26)(vue@3.5.25(typescript@5.8.3))': + '@testing-library/vue@8.1.0(patch_hash=15ba1e1b9f291286e3951cdf39b2e27f55b6727bc0315792f3e4558c0a3a7db8)(@vue/compiler-sfc@3.5.30)(vue@3.5.25(typescript@5.8.3))': dependencies: '@babel/runtime': 7.28.4 '@testing-library/dom': '@vuetify/testing-library-dom@1.0.3' '@vue/test-utils': 2.4.6 vue: 3.5.25(typescript@5.8.3) optionalDependencies: - '@vue/compiler-sfc': 3.5.26 + '@vue/compiler-sfc': 3.5.30 '@ts-morph/common@0.26.1': dependencies: @@ -11298,8 +11513,8 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 @@ -11310,12 +11525,12 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@types/chai@5.2.3': dependencies: @@ -11735,9 +11950,9 @@ snapshots: vite: 7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.25(typescript@5.8.3) - '@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3))': + '@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.53 + '@rolldown/pluginutils': 1.0.0-rc.2 vite: 7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.25(typescript@5.8.3) @@ -11905,11 +12120,11 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-core@3.5.26': + '@vue/compiler-core@3.5.30': dependencies: - '@babel/parser': 7.28.5 - '@vue/shared': 3.5.26 - entities: 7.0.0 + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.30 + entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 @@ -11918,10 +12133,10 @@ snapshots: '@vue/compiler-core': 3.5.25 '@vue/shared': 3.5.25 - '@vue/compiler-dom@3.5.26': + '@vue/compiler-dom@3.5.30': dependencies: - '@vue/compiler-core': 3.5.26 - '@vue/shared': 3.5.26 + '@vue/compiler-core': 3.5.30 + '@vue/shared': 3.5.30 '@vue/compiler-sfc@3.5.25': dependencies: @@ -11935,16 +12150,16 @@ snapshots: postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-sfc@3.5.26': + '@vue/compiler-sfc@3.5.30': dependencies: - '@babel/parser': 7.28.5 - '@vue/compiler-core': 3.5.26 - '@vue/compiler-dom': 3.5.26 - '@vue/compiler-ssr': 3.5.26 - '@vue/shared': 3.5.26 + '@babel/parser': 7.29.0 + '@vue/compiler-core': 3.5.30 + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.6 + postcss: 8.5.8 source-map-js: 1.2.1 '@vue/compiler-ssr@3.5.25': @@ -11952,10 +12167,10 @@ snapshots: '@vue/compiler-dom': 3.5.25 '@vue/shared': 3.5.25 - '@vue/compiler-ssr@3.5.26': + '@vue/compiler-ssr@3.5.30': dependencies: - '@vue/compiler-dom': 3.5.26 - '@vue/shared': 3.5.26 + '@vue/compiler-dom': 3.5.30 + '@vue/shared': 3.5.30 '@vue/devtools-api@6.6.4': {} @@ -12040,7 +12255,7 @@ snapshots: '@vue/shared@3.5.25': {} - '@vue/shared@3.5.26': {} + '@vue/shared@3.5.30': {} '@vue/test-utils@2.4.6': dependencies: @@ -12072,13 +12287,13 @@ snapshots: '@vuetify/babel-plugin-jsx@1.6.0(@babel/core@7.28.5)': dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@vue/shared': 3.5.26 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.5) + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@vue/shared': 3.5.30 '@vuetify/babel-helper-vue-transform-on': 1.6.0 '@vuetify/babel-plugin-resolve-type': 1.6.0(@babel/core@7.28.5) optionalDependencies: @@ -12086,37 +12301,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@vuetify/babel-plugin-jsx@1.7.0(@babel/core@7.28.5)': + '@vuetify/babel-plugin-jsx@1.7.0(@babel/core@7.29.0)': dependencies: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0) '@babel/template': 7.27.2 '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 '@vue/shared': 3.5.25 '@vuetify/babel-helper-vue-transform-on': 1.7.0 - '@vuetify/babel-plugin-resolve-type': 1.7.0(@babel/core@7.28.5) + '@vuetify/babel-plugin-resolve-type': 1.7.0(@babel/core@7.29.0) optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 transitivePeerDependencies: - supports-color '@vuetify/babel-plugin-resolve-type@1.6.0(@babel/core@7.28.5)': dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.28.5 - '@vue/compiler-sfc': 3.5.26 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/parser': 7.29.0 + '@vue/compiler-sfc': 3.5.30 transitivePeerDependencies: - supports-color - '@vuetify/babel-plugin-resolve-type@1.7.0(@babel/core@7.28.5)': + '@vuetify/babel-plugin-resolve-type@1.7.0(@babel/core@7.29.0)': dependencies: '@babel/code-frame': 7.27.1 - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/parser': 7.28.5 @@ -12148,10 +12363,10 @@ snapshots: vue: 3.5.25(typescript@5.8.3) vuetify: link:packages/vuetify - '@vuetify/one@2.12.0(@mdi/js@7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f))(@vue/compiler-sfc@3.5.25)(lodash-es@4.17.22)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))(vuetify@packages+vuetify)': + '@vuetify/one@2.12.0(@mdi/js@7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f))(@vue/compiler-sfc@3.5.25)(lodash-es@4.17.23)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))(vuetify@packages+vuetify)': dependencies: '@mdi/js': 7.4.47(patch_hash=3c2a78b1509745df3a3100e3e59075dd87718e67632cc14dc64dd9ac34098f9f) - lodash-es: 4.17.22 + lodash-es: 4.17.23 vite-plugin-pages: 0.33.1(@vue/compiler-sfc@3.5.25)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3))) vite-plugin-vue-layouts-next: 1.3.0(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) vue: 3.5.25(typescript@5.8.3) @@ -12175,7 +12390,7 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@vuetify/v0@0.1.5(vue@3.5.25(typescript@5.8.3))': + '@vuetify/v0@0.1.7(vue@3.5.25(typescript@5.8.3))': dependencies: vue: 3.5.25(typescript@5.8.3) optionalDependencies: @@ -12189,14 +12404,14 @@ snapshots: '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.8.3)) vue: 3.5.25(typescript@5.8.3) - '@yankeeinlondon/builder-api@1.4.1(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))': + '@yankeeinlondon/builder-api@1.4.1(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))': dependencies: '@types/markdown-it': 12.2.3 '@yankeeinlondon/happy-wrapper': 2.10.1(encoding@0.1.13) fp-ts: 2.16.9 inferred-types: 0.37.6 markdown-it: 13.0.2 - vite-plugin-md: 0.22.5(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) + vite-plugin-md: 0.22.5(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) transitivePeerDependencies: - '@vitejs/plugin-vue' - encoding @@ -12443,14 +12658,14 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-add-import-extension@1.5.1(@babel/core@7.28.5): + babel-plugin-add-import-extension@1.5.1(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 babel-plugin-istanbul@7.0.1: dependencies: - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 6.0.3 @@ -12537,6 +12752,8 @@ snapshots: balanced-match@1.0.0: {} + baseline-browser-mapping@2.10.7: {} + baseline-browser-mapping@2.8.31: {} before-after-hook@3.0.2: {} @@ -12579,6 +12796,14 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.7 + caniuse-lite: 1.0.30001778 + electron-to-chromium: 1.5.313 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -12657,6 +12882,8 @@ snapshots: caniuse-lite@1.0.30001757: {} + caniuse-lite@1.0.30001778: {} + chai@6.2.1: {} chalk@2.4.2: @@ -13202,6 +13429,8 @@ snapshots: electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.313: {} + emittery@0.13.1: {} emmet@2.4.11: @@ -13228,7 +13457,7 @@ snapshots: entities@6.0.1: {} - entities@7.0.0: {} + entities@7.0.1: {} env-paths@2.2.1: {} @@ -13372,34 +13601,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.2: + esbuild@0.27.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 escalade@3.2.0: {} @@ -15015,7 +15244,7 @@ snapshots: lodash-es@4.17.21: {} - lodash-es@4.17.22: {} + lodash-es@4.17.23: {} lodash.debounce@4.0.8: {} @@ -15506,6 +15735,8 @@ snapshots: node-releases@2.0.27: {} + node-releases@2.0.36: {} + nopt@1.0.10: dependencies: abbrev: 1.1.1 @@ -15719,7 +15950,7 @@ snapshots: parse-json@8.3.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 index-to-position: 1.1.0 type-fest: 4.39.1 @@ -15993,6 +16224,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + preact@10.11.3: {} prelude-ls@1.2.1: {} @@ -16253,35 +16490,35 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 - rollup@4.55.1: + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.55.1 - '@rollup/rollup-android-arm64': 4.55.1 - '@rollup/rollup-darwin-arm64': 4.55.1 - '@rollup/rollup-darwin-x64': 4.55.1 - '@rollup/rollup-freebsd-arm64': 4.55.1 - '@rollup/rollup-freebsd-x64': 4.55.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 - '@rollup/rollup-linux-arm-musleabihf': 4.55.1 - '@rollup/rollup-linux-arm64-gnu': 4.55.1 - '@rollup/rollup-linux-arm64-musl': 4.55.1 - '@rollup/rollup-linux-loong64-gnu': 4.55.1 - '@rollup/rollup-linux-loong64-musl': 4.55.1 - '@rollup/rollup-linux-ppc64-gnu': 4.55.1 - '@rollup/rollup-linux-ppc64-musl': 4.55.1 - '@rollup/rollup-linux-riscv64-gnu': 4.55.1 - '@rollup/rollup-linux-riscv64-musl': 4.55.1 - '@rollup/rollup-linux-s390x-gnu': 4.55.1 - '@rollup/rollup-linux-x64-gnu': 4.55.1 - '@rollup/rollup-linux-x64-musl': 4.55.1 - '@rollup/rollup-openbsd-x64': 4.55.1 - '@rollup/rollup-openharmony-arm64': 4.55.1 - '@rollup/rollup-win32-arm64-msvc': 4.55.1 - '@rollup/rollup-win32-ia32-msvc': 4.55.1 - '@rollup/rollup-win32-x64-gnu': 4.55.1 - '@rollup/rollup-win32-x64-msvc': 4.55.1 + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -17064,10 +17301,10 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.3 - unplugin-vue-components@0.28.0(@babel/parser@7.28.5)(rollup@4.55.1)(vue@3.5.25(typescript@5.8.3)): + unplugin-vue-components@0.28.0(@babel/parser@7.29.0)(rollup@4.59.0)(vue@3.5.25(typescript@5.8.3)): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) chokidar: 3.6.0 debug: 4.4.3 fast-glob: 3.3.3 @@ -17078,7 +17315,7 @@ snapshots: unplugin: 2.3.10 vue: 3.5.25(typescript@5.8.3) optionalDependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 7.29.0 transitivePeerDependencies: - rollup - supports-color @@ -17130,6 +17367,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.2.2: dependencies: punycode: 2.3.1 @@ -17201,10 +17444,10 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-md@0.22.5(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)): + vite-plugin-md@0.22.5(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)): dependencies: - '@vitejs/plugin-vue': 6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)) - '@yankeeinlondon/builder-api': 1.4.1(@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) + '@vitejs/plugin-vue': 6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)) + '@yankeeinlondon/builder-api': 1.4.1(@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)) '@yankeeinlondon/gray-matter': 6.2.1 '@yankeeinlondon/happy-wrapper': 2.10.1(encoding@0.1.13) markdown-it: 13.0.2 @@ -17275,9 +17518,9 @@ snapshots: transitivePeerDependencies: - supports-color - vite-ssr@0.17.2(@vitejs/plugin-vue@6.0.3(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(@vueuse/head@1.3.1(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(rollup@4.55.1)(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)): + vite-ssr@0.17.2(@vitejs/plugin-vue@6.0.3(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3)))(@vueuse/head@1.3.1(vue@3.5.25(typescript@5.8.3)))(encoding@0.1.13)(rollup@4.59.0)(vite@7.2.7(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)): dependencies: - '@rollup/plugin-replace': 3.0.0(rollup@4.55.1) + '@rollup/plugin-replace': 3.0.0(rollup@4.59.0) '@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.8.3)) chalk: 4.1.2 connect: 3.7.0 @@ -17311,11 +17554,11 @@ snapshots: vite@7.3.1(@types/node@24.10.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1): dependencies: - esbuild: 0.27.2 + esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.55.1 + postcss: 8.5.8 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.1 From d9fc3bedffe26ddf741f5420a803b8faf783a2af Mon Sep 17 00:00:00 2001 From: John Leider Date: Mon, 23 Mar 2026 21:14:14 -0500 Subject: [PATCH 02/14] docs: add v0 locale integration implementation plan Updated spec to retain provideLocale() as thin wrapper. Plan covers 4 tasks: core composable rewrite, vue-i18n adapter update, type exports, and integration verification. --- .../plans/2026-03-23-v0-locale-integration.md | 542 ++++++++++++++++++ ...2026-03-16-v0-locale-integration-design.md | 6 +- 2 files changed, 545 insertions(+), 3 deletions(-) create mode 100644 docs/superpowers/plans/2026-03-23-v0-locale-integration.md diff --git a/docs/superpowers/plans/2026-03-23-v0-locale-integration.md b/docs/superpowers/plans/2026-03-23-v0-locale-integration.md new file mode 100644 index 00000000000..8c28781ba55 --- /dev/null +++ b/docs/superpowers/plans/2026-03-23-v0-locale-integration.md @@ -0,0 +1,542 @@ +# v0 Locale Integration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace Vuetify's internal locale/RTL implementation with @vuetify/v0 composables, making v0 the runtime source of truth while Vuetify remains a thin configuration layer. + +**Architecture:** v0's `createLocale()` owns message storage (tokens), locale selection, translation (`t()`), number formatting (`n()`), and fallback resolution. v0's `createRtl()` owns reactive RTL boolean state. Vuetify wraps these with `$vuetify.` prefix stripping, per-locale RTL mapping, CSS class generation, and its existing `LocaleSymbol` provide/inject contract. The consumer-facing API (`t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator`) stays identical. + +**Tech Stack:** Vue 3, @vuetify/v0 ^0.1.9, TypeScript + +**Spec:** `docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md` + +--- + +## File Map + +| Action | File | Responsibility | +|--------|------|----------------| +| Rewrite | `packages/vuetify/src/composables/locale.ts` | Core composable — create, provide, use locale + RTL backed by v0 | +| Delete | `packages/vuetify/src/locale/adapters/vuetify.ts` | Replaced by v0's `Vuetify0LocaleAdapter` | +| Rewrite | `packages/vuetify/src/locale/adapters/vue-i18n.ts` | Thin wrapper that creates v0 locale with `VueI18nLocaleAdapter` | +| No change | `packages/vuetify/src/framework.ts` | `createLocale(options.locale)` call stays identical | +| No change | `packages/vuetify/src/components/VLocaleProvider/VLocaleProvider.tsx` | `provideLocale(props)` call stays identical | +| Modify | `packages/vuetify/src/types.ts` | Update exported types to match new interfaces | +| No change | `packages/vuetify/src/locale/*.ts` | Language pack files unchanged | +| No change | `packages/vuetify/src/locale/index.ts` | Re-exports unchanged | +| No change | All consumer components | `useLocale()` and `useRtl()` return types stay compatible | + +--- + +## Task 1: Rewrite the core locale composable + +**Files:** +- Rewrite: `packages/vuetify/src/composables/locale.ts` +- Delete: `packages/vuetify/src/locale/adapters/vuetify.ts` + +This is the central change. Replace `createLocale`, `provideLocale`, `useLocale`, `createRtl`, `provideRtl`, `useRtl` with v0-backed implementations. + +- [ ] **Step 1: Rewrite `packages/vuetify/src/composables/locale.ts`** + +The new file delegates to v0 for locale selection, token storage, translation, and RTL state. Vuetify adds: `$vuetify.` prefix stripping, per-locale RTL map, `rtlClasses` computation, `LocaleSymbol` DI contract. + +**Design notes:** +- Root `createLocale` stores the full messages ref on the provided data via `_messages` so `provideLocale` can inherit them for scoped contexts. Without this, `` would have zero tokens. +- `provideLocale` does NOT create a `v0Rtl` instance — `provideRtl` already computes RTL from the map. Only the root needs the v0 RTL bridge. +- v0 plugin installation (`createLocalePlugin`/`createRtlPlugin`) is deferred to a follow-up — not needed for the refactor since all Vuetify components use `LocaleSymbol` injection. + +```ts +// Utilities +import { computed, inject, provide, ref, shallowRef, toRef, watch } from 'vue' +import { createLocale as createV0Locale, createRtl as createV0Rtl } from '@vuetify/v0' + +// Locales +import en from '@/locale/en' + +// Types +import type { InjectionKey, Ref } from 'vue' +import type { LocaleContext, RtlContext } from '@vuetify/v0' + +export interface LocaleMessages { + [key: string]: LocaleMessages | string +} + +export interface LocaleOptions { + decimalSeparator?: string + messages?: Record + locale?: string + fallback?: string + adapter?: LocaleInstance +} + +export interface LocaleInstance { + current: Ref + t: (key: string, ...params: unknown[]) => string + n: (value: number) => string + decimalSeparator: Ref +} + +export interface RtlOptions { + rtl?: Record +} + +export interface RtlProps { + rtl?: boolean +} + +export interface RtlInstance { + isRtl: Ref + rtl: Ref> + rtlClasses: Ref +} + +// Internal: carried on provided data so provideLocale can inherit messages +interface InternalLocaleData { + _messages: Record +} + +export const LocaleSymbol: InjectionKey = Symbol.for('vuetify:locale') +export const RtlSymbol: InjectionKey = Symbol.for('vuetify:rtl') + +const LANG_PREFIX = '$vuetify.' + +function genDefaults () { + return { + af: false, + ar: true, + bg: false, + ca: false, + ckb: false, + cs: false, + de: false, + el: false, + en: false, + es: false, + et: false, + fa: true, + fi: false, + fr: false, + hr: false, + hu: false, + he: true, + id: false, + it: false, + ja: false, + km: false, + ko: false, + lv: false, + lt: false, + nl: false, + no: false, + pl: false, + pt: false, + ro: false, + ru: false, + sk: false, + sl: false, + srCyrl: false, + srLatn: false, + sv: false, + th: false, + tr: false, + az: false, + uk: false, + vi: false, + zhHans: false, + zhHant: false, + } +} + +function createLocaleInstance ( + v0Locale: LocaleContext, + options?: { decimalSeparator?: string } +): LocaleInstance { + const current = computed({ + get: () => v0Locale.selectedId.value ?? 'en', + set: v => v0Locale.select(v), + }) + + function t (key: string, ...params: unknown[]): string { + const stripped = key.startsWith(LANG_PREFIX) ? key.slice(LANG_PREFIX.length) : key + return v0Locale.t(stripped, ...params) + } + + function n (value: number): string { + return v0Locale.n(value) + } + + const decimalSeparator = toRef(() => { + if (options?.decimalSeparator) return options.decimalSeparator + const formatted = n(0.1) + return formatted.includes(',') ? ',' : '.' + }) + + return { + current, + t, + n, + decimalSeparator, + } +} + +function createRtlInstance ( + locale: LocaleInstance, + rtlMap: Ref>, + v0Rtl: RtlContext +): RtlInstance { + // Bridge: when locale changes, update v0 RTL from per-locale map + watch(() => locale.current.value, current => { + v0Rtl.isRtl.value = rtlMap.value[current] ?? false + }, { immediate: true }) + + return { + isRtl: v0Rtl.isRtl, + rtl: rtlMap, + rtlClasses: toRef(() => `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}`), + } +} + +// --- Public API --- + +export function createLocale (options?: LocaleOptions & RtlOptions) { + // If a custom adapter (e.g. vue-i18n) is passed, use it directly + if (options?.adapter) { + const rtl = createRtlFromAdapter(options.adapter, options) + return { ...options.adapter, ...rtl } + } + + const messages = { en, ...options?.messages } + const defaultLocale = options?.locale ?? 'en' + const fallback = options?.fallback ?? 'en' + + const v0Locale = createV0Locale({ + default: defaultLocale, + fallback, + messages, + }) + + const v0Rtl = createV0Rtl({ + default: false, + // Pass null to prevent v0 from managing document dir attribute — + // Vuetify manages this via VApp's rtlClasses + target: null, + }) + + const rtlMap = ref>(options?.rtl ?? genDefaults()) + const locale = createLocaleInstance(v0Locale, options) + const rtl = createRtlInstance(locale, rtlMap, v0Rtl) + + return { ...locale, ...rtl, _messages: messages } as LocaleInstance & RtlInstance & InternalLocaleData +} + +function createRtlFromAdapter (adapter: LocaleInstance, options?: RtlOptions): RtlInstance { + const rtl = ref>(options?.rtl ?? genDefaults()) + const isRtl = computed(() => rtl.value[adapter.current.value] ?? false) + + return { + isRtl, + rtl, + rtlClasses: toRef(() => `v-locale--is-${isRtl.value ? 'rtl' : 'ltr'}`), + } +} + +export function useLocale () { + const locale = inject(LocaleSymbol) + + if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') + + return locale +} + +export function provideLocale (props: LocaleOptions & RtlProps) { + const parent = inject(LocaleSymbol) + + if (!parent) throw new Error('[Vuetify] Could not find injected locale instance') + + // If the parent is using a custom adapter (e.g. vue-i18n), delegate to its provide + if ('provide' in parent && typeof (parent as any).provide === 'function') { + const i18n = (parent as any).provide(props) + const rtl = provideRtl(i18n, parent.rtl, props) + const data = { ...i18n, ...rtl } + provide(LocaleSymbol, data) + return data + } + + // Inherit root messages, merge with any child-provided overrides + const parentMessages = (parent as any)._messages ?? {} + const messages = props.messages + ? { ...parentMessages, [props.locale ?? parent.current.value]: props.messages } + : parentMessages + + const v0Locale = createV0Locale({ + default: props.locale ?? parent.current.value, + fallback: props.fallback ?? 'en', + messages, + }) + + const locale = createLocaleInstance(v0Locale, props) + const rtl = provideRtl(locale, parent.rtl, props) + + const data = { ...locale, ...rtl, _messages: messages } as LocaleInstance & RtlInstance & InternalLocaleData + provide(LocaleSymbol, data) + return data +} + +function provideRtl (locale: LocaleInstance, rtl: RtlInstance['rtl'], props: RtlProps): RtlInstance { + const isRtl = computed(() => props.rtl ?? rtl.value[locale.current.value] ?? false) + + return { + isRtl, + rtl, + rtlClasses: toRef(() => `v-locale--is-${isRtl.value ? 'rtl' : 'ltr'}`), + } +} + +export function useRtl () { + const locale = inject(LocaleSymbol) + + if (!locale) throw new Error('[Vuetify] Could not find injected rtl instance') + + return { isRtl: locale.isRtl, rtlClasses: locale.rtlClasses } +} +``` + +- [ ] **Step 2: Delete the old Vuetify adapter** + +```bash +rm packages/vuetify/src/locale/adapters/vuetify.ts +``` + +The `createVuetifyAdapter` function is no longer needed — v0's `Vuetify0LocaleAdapter` handles translation and number formatting internally. + +- [ ] **Step 3: Verify the build compiles** + +```bash +cd packages/vuetify && pnpm build:lib 2>&1 | head -20 +``` + +Expected: No import errors for `@/locale/adapters/vuetify`. All components importing `useLocale` / `useRtl` should compile since the return types are compatible. + +- [ ] **Step 4: Run the existing locale tests** + +```bash +cd packages/vuetify && pnpm test:unit -- --run src/locale/__tests__/index.spec.ts +``` + +Expected: PASS — these tests check language pack exports and structure, not the adapter. + +- [ ] **Step 5: Commit** + +```bash +git add packages/vuetify/src/composables/locale.ts +git add packages/vuetify/src/locale/adapters/vuetify.ts +git commit -m "refactor(locale): replace Vuetify adapter with v0 createLocale/createRtl + +Vuetify's locale composable now delegates to @vuetify/v0 for: +- Message storage (tokens) and translation (t()) +- Number formatting (n()) +- Fallback locale resolution +- RTL boolean state + +Vuetify remains responsible for: +- $vuetify. prefix stripping +- Per-locale RTL map (ar, fa, he → true) +- CSS class generation (v-locale--is-rtl/ltr) +- LocaleSymbol provide/inject contract + +BREAKING CHANGE: LocaleInstance.provide() and .name removed. +LocaleInstance.fallback and .messages removed from public type. +Nested contexts use provideLocale() or VLocaleProvider." +``` + +--- + +## Task 2: Update the vue-i18n adapter + +**Files:** +- Rewrite: `packages/vuetify/src/locale/adapters/vue-i18n.ts` + +The vue-i18n adapter needs to return a `LocaleInstance` that Vuetify's `createLocale` can consume via the `adapter` option. v0 ships its own `VueI18nLocaleAdapter`, but Vuetify's adapter wraps vue-i18n at a higher level — it needs to satisfy the `LocaleInstance` interface (`current`, `t`, `n`, `decimalSeparator`) plus extra properties (`provide`, `messages`, `fallback`, `name`) used for scope nesting. + +- [ ] **Step 1: Rewrite the vue-i18n adapter** + +The adapter continues to wrap vue-i18n directly (not via v0's adapter), since it needs to preserve vue-i18n's full feature set (pluralization, linked messages, datetime formatting) and its own scope nesting via `useI18n({ useScope: 'local' })`. + +```ts +// Composables +import { useProxiedModel } from '@/composables/proxiedModel' + +// Utilities +import { toRef, watch } from 'vue' + +// Types +import type { Ref } from 'vue' +import type { I18n, useI18n } from 'vue-i18n' +import type { LocaleInstance, LocaleMessages, LocaleOptions } from '@/composables/locale' + +type VueI18nAdapterParams = { + i18n: I18n + useI18n: typeof useI18n +} + +function useProvided (props: any, prop: string, provided: Ref) { + const internal = useProxiedModel(props, prop) + + internal.value = props[prop] ?? provided.value + + watch(provided, v => { + if (props[prop] == null) { + internal.value = v + } + }) + + return internal as Ref +} + +function inferDecimalSeparator (format: (v: number) => string) { + return format(0.1).includes(',') ? ',' : '.' +} + +function createProvideFunction (data: { + current: Ref + fallback: Ref + messages: Ref + useI18n: typeof useI18n +}) { + return (props: LocaleOptions): LocaleInstance => { + const current = useProvided(props, 'locale', data.current) + const fallback = useProvided(props, 'fallback', data.fallback) + const messages = useProvided(props, 'messages', data.messages) + + const i18n = data.useI18n({ + locale: current.value, + fallbackLocale: fallback.value, + messages: messages.value as any, + useScope: 'local', + legacy: false, + inheritLocale: false, + }) + + watch(current, v => { + i18n.locale.value = v + }) + + return { + name: 'vue-i18n', + current, + fallback, + messages, + decimalSeparator: toRef(() => props.decimalSeparator ?? inferDecimalSeparator(i18n.n)), + t: (key: string, ...params: unknown[]) => i18n.t(key, params), + n: i18n.n, + provide: createProvideFunction({ current, fallback, messages, useI18n: data.useI18n }), + } + } +} + +export function createVueI18nAdapter ({ i18n, useI18n }: VueI18nAdapterParams): LocaleInstance { + const current = i18n.global.locale + const fallback = i18n.global.fallbackLocale as Ref + const messages = i18n.global.messages + + return { + name: 'vue-i18n', + current, + fallback, + messages, + decimalSeparator: toRef(() => inferDecimalSeparator(i18n.global.n)), + t: (key: string, ...params: unknown[]) => i18n.global.t(key, params), + n: i18n.global.n, + provide: createProvideFunction({ current, fallback, messages, useI18n }), + } +} +``` + +**Key difference from current:** The `LocaleInstance` interface no longer requires `name`, `fallback`, `messages`, or `provide`. The vue-i18n adapter keeps these as extra properties since it uses them for scope nesting — the core `provideLocale` detects the `provide` method via duck-typing and delegates to it. + +- [ ] **Step 2: Verify build** + +```bash +cd packages/vuetify && pnpm build:lib 2>&1 | head -20 +``` + +- [ ] **Step 3: Commit** + +```bash +git add packages/vuetify/src/locale/adapters/vue-i18n.ts +git commit -m "refactor(locale): update vue-i18n adapter for new LocaleInstance interface" +``` + +--- + +## Task 3: Update exported types + +**Files:** +- Modify: `packages/vuetify/src/types.ts` + +- [ ] **Step 1: Update type exports** + +The `LocaleInstance` interface no longer has `messages`, `provide`, or `name` as required fields in the public type. Verify the export in `types.ts` still works: + +```ts +export type { LocaleInstance, LocaleMessages, RtlInstance, LocaleOptions, RtlOptions } from '@/composables/locale' +``` + +This line doesn't need to change — it re-exports from the composable which we've already updated. Just verify it compiles. + +- [ ] **Step 2: Run typecheck** + +```bash +cd packages/vuetify && pnpm tsc --noEmit --pretty 2>&1 | tail -20 +``` + +Expected: No type errors from consumers using `useLocale()` — the return type (`LocaleInstance & RtlInstance`) has `t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator` which all existing consumers destructure. + +- [ ] **Step 3: Commit if types.ts needed changes** + +Only commit if changes were required. Skip if no changes needed. + +--- + +## Task 4: Integration verification + +- [ ] **Step 1: Run the full unit test suite** + +```bash +cd packages/vuetify && pnpm test:unit -- --run 2>&1 | tail -30 +``` + +Check for failures related to locale, translation, RTL, or `VLocaleProvider`. + +- [ ] **Step 2: Run lint** + +```bash +cd packages/vuetify && pnpm lint 2>&1 | tail -20 +``` + +- [ ] **Step 3: Spot-check translation behavior** + +Verify that v0's token flattening correctly handles Vuetify's nested message format. The language packs use nested objects like: + +```ts +{ dataTable: { sortBy: 'Sort by' } } +``` + +v0's `createTokens` flattens these to `en.dataTable.sortBy`. The adapter looks up `${locale}.${key}` — so `t('dataTable.sortBy')` resolves to `en.dataTable.sortBy`. This should work, but verify by checking a component that uses `t()` with a nested key. + +- [ ] **Step 4: Verify VLocaleProvider still works** + +`VLocaleProvider` calls `provideLocale(props)` — unchanged. The new `provideLocale` creates a scoped v0 locale instance. Verify that nested locale providers correctly override the parent's locale. + +- [ ] **Step 5: Verify non-prefixed key behavior** + +Current behavior: keys without `$vuetify.` are treated as raw templates with positional param interpolation. The new code strips the prefix if present, then delegates to v0's `t()`. For non-prefixed keys, v0 will look them up as tokens (which won't exist) and return the key after interpolation. Verify this works correctly. + +- [ ] **Step 6: Final commit with any fixes** + +```bash +git add -u +git commit -m "fix(locale): address integration issues from v0 migration" +``` + +Only if fixes were needed. Skip if everything passed. diff --git a/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md b/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md index 544effdfd3d..2f81c6e66d7 100644 --- a/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md +++ b/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md @@ -186,7 +186,7 @@ Thin wrapper — injects `LocaleSymbol`, returns `{ isRtl, rtlClasses }`. ### provideLocale() -Replaced by v0's `Locale` provider component. Vuetify wraps it to add `rtlClasses` and provide via `LocaleSymbol`. See "VLocaleProvider" section. +Retained as a thin wrapper. Creates a scoped v0 locale + RTL context pair, computes `rtlClasses`, and provides the combined object via `LocaleSymbol`. Used internally by `VLocaleProvider` and available for renderless composable patterns. Will migrate to wrap v0's `Locale` component when it ships. ## vue-i18n Adapter @@ -226,8 +226,8 @@ Each `VLocaleProvider` instance maintains its own locale→RTL bridge watcher, i ## Breaking Changes - `LocaleInstance` type changes internally (backed by v0 refs) -- `LocaleInstance.provide()` method removed — nested contexts use `VLocaleProvider` / v0's `Locale` component -- `provideLocale()` composable removed — replaced by `VLocaleProvider` / v0's `Locale` component internally +- `LocaleInstance.provide()` method removed — `provideLocale()` replaces this (see below) +- `provideLocale()` retained as thin wrapper — creates scoped v0 locale + RTL contexts, provides via `LocaleSymbol`. Used internally by `VLocaleProvider`. Will migrate to wrap v0's `Locale` component when it ships. - `messages: Ref` removed from public API — messages are managed by v0's token system; consumers register messages via `createVuetify()` options - `fallback: Ref` removed from public API — fallback is configured at creation time, not mutated at runtime - `rtl: Ref>` (per-locale RTL map) removed from public API — the map is Vuetify-internal; consumers use `isRtl` (boolean) and `rtlClasses` (string). `VLocaleProvider` uses the internal map for locale→RTL lookup. From 9968b9c45165197c4a27b46f1e77b78f0bfe7c21 Mon Sep 17 00:00:00 2001 From: John Leider Date: Mon, 23 Mar 2026 21:34:21 -0500 Subject: [PATCH 03/14] refactor(locale): replace Vuetify adapter with v0 createLocale/createRtl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vuetify's locale composable now delegates to @vuetify/v0 for: - Message storage (tokens) and translation (t()) - Number formatting (n()) - Fallback locale resolution - RTL boolean state Vuetify remains responsible for: - $vuetify. prefix stripping - Per-locale RTL map (ar, fa, he → true) - CSS class generation (v-locale--is-rtl/ltr) - LocaleSymbol provide/inject contract BREAKING CHANGE: LocaleInstance.provide() and .name removed. LocaleInstance.fallback and .messages removed from public type. Nested contexts use provideLocale() or VLocaleProvider. --- packages/vuetify/src/composables/locale.ts | 198 +++++++++++++----- .../vuetify/src/locale/adapters/vuetify.ts | 120 ----------- 2 files changed, 148 insertions(+), 170 deletions(-) delete mode 100644 packages/vuetify/src/locale/adapters/vuetify.ts diff --git a/packages/vuetify/src/composables/locale.ts b/packages/vuetify/src/composables/locale.ts index 975747f08c8..678b37190a6 100644 --- a/packages/vuetify/src/composables/locale.ts +++ b/packages/vuetify/src/composables/locale.ts @@ -1,9 +1,13 @@ // Utilities -import { computed, inject, provide, ref, toRef } from 'vue' -import { createVuetifyAdapter } from '@/locale/adapters/vuetify' +import { computed, inject, provide, ref, toRef, watch } from 'vue' +import { createLocale as createV0Locale, createRtl as createV0Rtl } from '@vuetify/v0' + +// Locales +import en from '@/locale/en' // Types -import type { InjectionKey, Ref, ShallowRef } from 'vue' +import type { InjectionKey, Ref } from 'vue' +import type { LocaleContext, RtlContext } from '@vuetify/v0' export interface LocaleMessages { [key: string]: LocaleMessages | string @@ -11,61 +15,19 @@ export interface LocaleMessages { export interface LocaleOptions { decimalSeparator?: string - messages?: LocaleMessages + messages?: Record locale?: string fallback?: string adapter?: LocaleInstance } export interface LocaleInstance { - name: string - decimalSeparator: ShallowRef - messages: Ref current: Ref - fallback: Ref t: (key: string, ...params: unknown[]) => string n: (value: number) => string - provide: (props: LocaleOptions) => LocaleInstance -} - -export const LocaleSymbol: InjectionKey = Symbol.for('vuetify:locale') - -function isLocaleInstance (obj: any): obj is LocaleInstance { - return obj.name != null -} - -export function createLocale (options?: LocaleOptions & RtlOptions) { - const i18n = options?.adapter && isLocaleInstance(options?.adapter) ? options?.adapter : createVuetifyAdapter(options) - const rtl = createRtl(i18n, options) - - return { ...i18n, ...rtl } -} - -export function useLocale () { - const locale = inject(LocaleSymbol) - - if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') - - return locale -} - -export function provideLocale (props: LocaleOptions & RtlProps) { - const locale = inject(LocaleSymbol) - - if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') - - const i18n = locale.provide(props) - const rtl = provideRtl(i18n, locale.rtl, props) - - const data = { ...i18n, ...rtl } - - provide(LocaleSymbol, data) - - return data + decimalSeparator: Ref } -// RTL - export interface RtlOptions { rtl?: Record } @@ -80,8 +42,17 @@ export interface RtlInstance { rtlClasses: Ref } +// Internal: carried on provided data so provideLocale can inherit context +interface InternalLocaleData { + _messages: Record + _fallback: string +} + +export const LocaleSymbol: InjectionKey = Symbol.for('vuetify:locale') export const RtlSymbol: InjectionKey = Symbol.for('vuetify:rtl') +const LANG_PREFIX = '$vuetify.' + function genDefaults () { return { af: false, @@ -129,9 +100,91 @@ function genDefaults () { } } -export function createRtl (i18n: LocaleInstance, options?: RtlOptions): RtlInstance { +function createLocaleInstance ( + v0Locale: LocaleContext, + options?: { decimalSeparator?: string } +): LocaleInstance { + const current = computed({ + get: () => String(v0Locale.selectedId.value ?? 'en'), + set: v => v0Locale.select(v), + }) + + function t (key: string, ...params: unknown[]): string { + const stripped = key.startsWith(LANG_PREFIX) ? key.slice(LANG_PREFIX.length) : key + return v0Locale.t(stripped, ...params) + } + + function n (value: number): string { + return v0Locale.n(value) + } + + const decimalSeparator = toRef(() => { + if (options?.decimalSeparator) return options.decimalSeparator + const formatted = n(0.1) + return formatted.includes(',') ? ',' : '.' + }) + + return { + current, + t, + n, + decimalSeparator, + } +} + +function createRtlInstance ( + locale: LocaleInstance, + rtlMap: Ref>, + v0Rtl: RtlContext +): RtlInstance { + // Bridge: when locale changes, update v0 RTL from per-locale map + watch(() => locale.current.value, current => { + v0Rtl.isRtl.value = rtlMap.value[current] ?? false + }, { immediate: true }) + + return { + isRtl: v0Rtl.isRtl, + rtl: rtlMap, + rtlClasses: toRef(() => `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}`), + } +} + +// --- Public API --- + +export function createLocale (options?: LocaleOptions & RtlOptions) { + // If a custom adapter (e.g. vue-i18n) is passed, use it directly + if (options?.adapter) { + const rtl = createRtlFromAdapter(options.adapter, options) + return { ...options.adapter, ...rtl } + } + + const messages = { en, ...options?.messages } + const defaultLocale = options?.locale ?? 'en' + const fallback = options?.fallback ?? 'en' + + const v0Locale = createV0Locale({ + default: defaultLocale, + fallback, + messages, + }) + + const v0Rtl = createV0Rtl({ + default: false, + // Pass null to prevent v0 from managing document dir attribute — + // Vuetify manages this via VApp's rtlClasses + target: null, + }) + + const rtlMap = ref>(options?.rtl ?? genDefaults()) + const locale = createLocaleInstance(v0Locale, options) + const rtl = createRtlInstance(locale, rtlMap, v0Rtl) + + return { ...locale, ...rtl, _messages: messages, _fallback: fallback } as LocaleInstance & RtlInstance & InternalLocaleData +} + +function createRtlFromAdapter (adapter: LocaleInstance, options?: RtlOptions): RtlInstance { const rtl = ref>(options?.rtl ?? genDefaults()) - const isRtl = computed(() => rtl.value[i18n.current.value] ?? false) + const isRtl = computed(() => rtl.value[adapter.current.value] ?? false) return { isRtl, @@ -140,7 +193,52 @@ export function createRtl (i18n: LocaleInstance, options?: RtlOptions): RtlInsta } } -export function provideRtl (locale: LocaleInstance, rtl: RtlInstance['rtl'], props: RtlProps): RtlInstance { +export function useLocale () { + const locale = inject(LocaleSymbol) + + if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') + + return locale +} + +export function provideLocale (props: LocaleOptions & RtlProps) { + const parent = inject(LocaleSymbol) + + if (!parent) throw new Error('[Vuetify] Could not find injected locale instance') + + // If the parent is using a custom adapter (e.g. vue-i18n), delegate to its provide + if ('provide' in parent && typeof (parent as any).provide === 'function') { + const i18n = (parent as any).provide(props) + const rtl = provideRtl(i18n, parent.rtl, props) + const data = { ...i18n, ...rtl } + provide(LocaleSymbol, data) + return data + } + + // Inherit root messages and fallback, merge with any child-provided overrides + const parentData = parent as any as Partial + const parentMessages = parentData._messages ?? {} + const parentFallback = parentData._fallback ?? 'en' + const messages = props.messages + ? { ...parentMessages, [props.locale ?? parent.current.value]: props.messages } + : parentMessages + const fallback = props.fallback ?? parentFallback + + const v0Locale = createV0Locale({ + default: props.locale ?? parent.current.value, + fallback, + messages, + }) + + const locale = createLocaleInstance(v0Locale, props) + const rtl = provideRtl(locale, parent.rtl, props) + + const data = { ...locale, ...rtl, _messages: messages, _fallback: fallback } as LocaleInstance & RtlInstance & InternalLocaleData + provide(LocaleSymbol, data) + return data +} + +function provideRtl (locale: LocaleInstance, rtl: RtlInstance['rtl'], props: RtlProps): RtlInstance { const isRtl = computed(() => props.rtl ?? rtl.value[locale.current.value] ?? false) return { diff --git a/packages/vuetify/src/locale/adapters/vuetify.ts b/packages/vuetify/src/locale/adapters/vuetify.ts deleted file mode 100644 index ab860444064..00000000000 --- a/packages/vuetify/src/locale/adapters/vuetify.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Composables -import { useProxiedModel } from '@/composables/proxiedModel' - -// Utilities -import { ref, shallowRef, toRef, watch } from 'vue' -import { consoleError, consoleWarn, getObjectValueByPath } from '@/util' - -// Locales -import en from '@/locale/en' - -// Types -import type { Ref } from 'vue' -import type { LocaleInstance, LocaleMessages, LocaleOptions } from '@/composables/locale' - -const LANG_PREFIX = '$vuetify.' - -const replace = (str: string, params: unknown[]) => { - return str.replace(/\{(\d+)\}/g, (match: string, index: string) => { - return String(params[Number(index)]) - }) -} - -const createTranslateFunction = ( - current: Ref, - fallback: Ref, - messages: Ref, -) => { - return (key: string, ...params: unknown[]) => { - if (!key.startsWith(LANG_PREFIX)) { - return replace(key, params) - } - - const shortKey = key.replace(LANG_PREFIX, '') - const currentLocale = current.value && messages.value[current.value] - const fallbackLocale = fallback.value && messages.value[fallback.value] - - let str: string = getObjectValueByPath(currentLocale, shortKey, null) - - if (!str) { - consoleWarn(`Translation key "${key}" not found in "${current.value}", trying fallback locale`) - str = getObjectValueByPath(fallbackLocale, shortKey, null) - } - - if (!str) { - consoleError(`Translation key "${key}" not found in fallback`) - str = key - } - - if (typeof str !== 'string') { - consoleError(`Translation key "${key}" has a non-string value`) - str = key - } - - return replace(str, params) - } -} - -function createNumberFunction (current: Ref, fallback: Ref) { - return (value: number, options?: Intl.NumberFormatOptions) => { - const numberFormat = new Intl.NumberFormat([current.value, fallback.value], options) - - return numberFormat.format(value) - } -} - -function inferDecimalSeparator (current: Ref, fallback: Ref) { - const format = createNumberFunction(current, fallback) - return format(0.1).includes(',') ? ',' : '.' -} - -function useProvided (props: any, prop: string, provided: Ref) { - const internal = useProxiedModel(props, prop, props[prop] ?? provided.value) - - // TODO: Remove when defaultValue works - internal.value = props[prop] ?? provided.value - - watch(provided, v => { - if (props[prop] == null) { - internal.value = provided.value - } - }) - - return internal as Ref -} - -function createProvideFunction (state: { current: Ref, fallback: Ref, messages: Ref }) { - return (props: LocaleOptions): LocaleInstance => { - const current = useProvided(props, 'locale', state.current) - const fallback = useProvided(props, 'fallback', state.fallback) - const messages = useProvided(props, 'messages', state.messages) - - return { - name: 'vuetify', - current, - fallback, - messages, - decimalSeparator: toRef(() => inferDecimalSeparator(current, fallback)), - t: createTranslateFunction(current, fallback, messages), - n: createNumberFunction(current, fallback), - provide: createProvideFunction({ current, fallback, messages }), - } - } -} - -export function createVuetifyAdapter (options?: LocaleOptions): LocaleInstance { - const current = shallowRef(options?.locale ?? 'en') - const fallback = shallowRef(options?.fallback ?? 'en') - const messages = ref({ en, ...options?.messages }) - - return { - name: 'vuetify', - current, - fallback, - messages, - decimalSeparator: toRef(() => options?.decimalSeparator ?? inferDecimalSeparator(current, fallback)), - t: createTranslateFunction(current, fallback, messages), - n: createNumberFunction(current, fallback), - provide: createProvideFunction({ current, fallback, messages }), - } -} From 1a563ac668bff35e2a7abfade09599de3473f255 Mon Sep 17 00:00:00 2001 From: John Leider Date: Mon, 23 Mar 2026 21:47:36 -0500 Subject: [PATCH 04/14] refactor(locale): update vue-i18n adapter for LocaleInstance --- packages/vuetify/src/composables/locale.ts | 21 ++++++++++++------- .../vuetify/src/locale/adapters/vue-i18n.ts | 11 ++++++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/vuetify/src/composables/locale.ts b/packages/vuetify/src/composables/locale.ts index 678b37190a6..0e7adcabfc8 100644 --- a/packages/vuetify/src/composables/locale.ts +++ b/packages/vuetify/src/composables/locale.ts @@ -1,13 +1,13 @@ // Utilities -import { computed, inject, provide, ref, toRef, watch } from 'vue' import { createLocale as createV0Locale, createRtl as createV0Rtl } from '@vuetify/v0' +import { computed, inject, provide, ref, shallowRef, toRef, watch } from 'vue' // Locales import en from '@/locale/en' // Types -import type { InjectionKey, Ref } from 'vue' import type { LocaleContext, RtlContext } from '@vuetify/v0' +import type { InjectionKey, Ref } from 'vue' export interface LocaleMessages { [key: string]: LocaleMessages | string @@ -42,8 +42,8 @@ export interface RtlInstance { rtlClasses: Ref } -// Internal: carried on provided data so provideLocale can inherit context -interface InternalLocaleData { +/** @internal Carried on provided data so provideLocale can inherit context */ +export interface InternalLocaleData { _messages: Record _fallback: string } @@ -137,15 +137,20 @@ function createRtlInstance ( rtlMap: Ref>, v0Rtl: RtlContext ): RtlInstance { - // Bridge: when locale changes, update v0 RTL from per-locale map + // Local ref bridges v0's Ref (different @vue/reactivity copy) to Vuetify's + const isRtl = shallowRef(v0Rtl.isRtl.value) + + // Bridge: when locale changes, update RTL from per-locale map watch(() => locale.current.value, current => { - v0Rtl.isRtl.value = rtlMap.value[current] ?? false + const value = rtlMap.value[current] ?? false + v0Rtl.isRtl.value = value + isRtl.value = value }, { immediate: true }) return { - isRtl: v0Rtl.isRtl, + isRtl, rtl: rtlMap, - rtlClasses: toRef(() => `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}`), + rtlClasses: toRef(() => `v-locale--is-${isRtl.value ? 'rtl' : 'ltr'}`), } } diff --git a/packages/vuetify/src/locale/adapters/vue-i18n.ts b/packages/vuetify/src/locale/adapters/vue-i18n.ts index 762515d04fc..f9d22fdc810 100644 --- a/packages/vuetify/src/locale/adapters/vue-i18n.ts +++ b/packages/vuetify/src/locale/adapters/vue-i18n.ts @@ -9,6 +9,13 @@ import type { Ref } from 'vue' import type { I18n, useI18n } from 'vue-i18n' import type { LocaleInstance, LocaleMessages, LocaleOptions } from '@/composables/locale' +export interface VueI18nLocaleInstance extends LocaleInstance { + name: string + fallback: Ref + messages: Ref + provide: (props: LocaleOptions) => VueI18nLocaleInstance +} + type VueI18nAdapterParams = { i18n: I18n useI18n: typeof useI18n @@ -38,7 +45,7 @@ function createProvideFunction (data: { messages: Ref useI18n: typeof useI18n }) { - return (props: LocaleOptions): LocaleInstance => { + return (props: LocaleOptions): VueI18nLocaleInstance => { const current = useProvided(props, 'locale', data.current) const fallback = useProvided(props, 'fallback', data.fallback) const messages = useProvided(props, 'messages', data.messages) @@ -69,7 +76,7 @@ function createProvideFunction (data: { } } -export function createVueI18nAdapter ({ i18n, useI18n }: VueI18nAdapterParams): LocaleInstance { +export function createVueI18nAdapter ({ i18n, useI18n }: VueI18nAdapterParams): VueI18nLocaleInstance { const current = i18n.global.locale const fallback = i18n.global.fallbackLocale as Ref const messages = i18n.global.messages From 52f4e754a9d856b416b4242c9c3a058cb24fba75 Mon Sep 17 00:00:00 2001 From: John Leider Date: Mon, 23 Mar 2026 21:52:50 -0500 Subject: [PATCH 05/14] chore: bump @vuetify/v0 to ^0.1.10 Includes runtime locale/theme registration support via register({ id, messages }) and exported flatten() utility. --- packages/vuetify/package.json | 2 +- pnpm-lock.yaml | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/vuetify/package.json b/packages/vuetify/package.json index d2b440962ae..9173879e492 100755 --- a/packages/vuetify/package.json +++ b/packages/vuetify/package.json @@ -140,7 +140,7 @@ "lint:fix": "concurrently -n \"tsc,eslint\" \"tsgo -p tsconfig.checks.json --noEmit --pretty\" \"eslint --fix src\"" }, "dependencies": { - "@vuetify/v0": "^0.1.7" + "@vuetify/v0": "^0.1.10" }, "devDependencies": { "@date-io/core": "3.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff464d299e0..6fa4e8f2101 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -457,8 +457,8 @@ importers: packages/vuetify: dependencies: '@vuetify/v0': - specifier: ^0.1.7 - version: 0.1.7(vue@3.5.25(typescript@5.8.3)) + specifier: ^0.1.10 + version: 0.1.10(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) devDependencies: '@date-io/core': specifier: 3.2.0 @@ -3796,8 +3796,8 @@ packages: resolution: {integrity: sha512-EHJDH+O7eov7yJ3WPR52qELyHq8beQ/GmletheroKRvp4SxTL9IgOEeXqBfw4bOyd49VNH5coyoqvIIlnhavUQ==} engines: {node: '>=18'} - '@vuetify/v0@0.1.7': - resolution: {integrity: sha512-07QrP5Yp9CsdWTuyMiW38PDJs54Lg95iCmqI80WBm130nd89nLk3KXt+pY3vhfSRbM4AM4hFp+zGPj59nf3xyw==} + '@vuetify/v0@0.1.10': + resolution: {integrity: sha512-mHITfo9KjQ1jcIBQ3dpEsfNa863xHx/JxZM7Kjtf+mrFhyyDMk/NagT2cGGrWLdU65wv2BJg7cNZmWpSqk3l+A==} peerDependencies: '@adobe/leonardo-contrast-colors': '>=1.0.0' '@ant-design/colors': '>=7.0.0' @@ -3806,6 +3806,7 @@ packages: launchdarkly-js-client-sdk: ^3.0.0 posthog-js: ^1.0.0 vue: '>=3.5.0' + vue-i18n: '>=10.0.0' peerDependenciesMeta: '@adobe/leonardo-contrast-colors': optional: true @@ -3819,6 +3820,8 @@ packages: optional: true posthog-js: optional: true + vue-i18n: + optional: true '@vueuse/head@1.3.1': resolution: {integrity: sha512-XCcHGfDzkGlHS7KIPJVYN//L7jpfASLsN7MUE19ndHVQLnPIDxqFLDl7IROsY81PKzawVAUe4OYVWcGixseWxA==} @@ -9079,7 +9082,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -9105,7 +9108,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -9121,7 +9124,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.10 @@ -9182,7 +9185,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -9191,14 +9194,14 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -12390,11 +12393,12 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@vuetify/v0@0.1.7(vue@3.5.25(typescript@5.8.3))': + '@vuetify/v0@0.1.10(vue-i18n@11.2.1(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3))': dependencies: vue: 3.5.25(typescript@5.8.3) optionalDependencies: '@js-temporal/polyfill': 0.5.1 + vue-i18n: 11.2.1(vue@3.5.25(typescript@5.8.3)) '@vueuse/head@1.3.1(vue@3.5.25(typescript@5.8.3))': dependencies: @@ -15000,9 +15004,9 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.28.5 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.5) '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@jest/expect-utils': 30.2.0 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.2.0 From 8c876c3ff962de18cffbd7bf29e8f0bbcae3d5e7 Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 24 Mar 2026 11:07:28 -0500 Subject: [PATCH 06/14] chore: add docs/superpowers to gitignore --- .gitignore | 3 + .../plans/2026-03-23-v0-locale-integration.md | 542 ------------------ ...2026-03-16-v0-locale-integration-design.md | 276 --------- 3 files changed, 3 insertions(+), 818 deletions(-) delete mode 100644 docs/superpowers/plans/2026-03-23-v0-locale-integration.md delete mode 100644 docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md diff --git a/.gitignore b/.gitignore index f433d067efa..d2a67de2be5 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ __screenshots__/ .vercel .now + +# Superpowers +docs/superpowers/ diff --git a/docs/superpowers/plans/2026-03-23-v0-locale-integration.md b/docs/superpowers/plans/2026-03-23-v0-locale-integration.md deleted file mode 100644 index 8c28781ba55..00000000000 --- a/docs/superpowers/plans/2026-03-23-v0-locale-integration.md +++ /dev/null @@ -1,542 +0,0 @@ -# v0 Locale Integration Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Replace Vuetify's internal locale/RTL implementation with @vuetify/v0 composables, making v0 the runtime source of truth while Vuetify remains a thin configuration layer. - -**Architecture:** v0's `createLocale()` owns message storage (tokens), locale selection, translation (`t()`), number formatting (`n()`), and fallback resolution. v0's `createRtl()` owns reactive RTL boolean state. Vuetify wraps these with `$vuetify.` prefix stripping, per-locale RTL mapping, CSS class generation, and its existing `LocaleSymbol` provide/inject contract. The consumer-facing API (`t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator`) stays identical. - -**Tech Stack:** Vue 3, @vuetify/v0 ^0.1.9, TypeScript - -**Spec:** `docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md` - ---- - -## File Map - -| Action | File | Responsibility | -|--------|------|----------------| -| Rewrite | `packages/vuetify/src/composables/locale.ts` | Core composable — create, provide, use locale + RTL backed by v0 | -| Delete | `packages/vuetify/src/locale/adapters/vuetify.ts` | Replaced by v0's `Vuetify0LocaleAdapter` | -| Rewrite | `packages/vuetify/src/locale/adapters/vue-i18n.ts` | Thin wrapper that creates v0 locale with `VueI18nLocaleAdapter` | -| No change | `packages/vuetify/src/framework.ts` | `createLocale(options.locale)` call stays identical | -| No change | `packages/vuetify/src/components/VLocaleProvider/VLocaleProvider.tsx` | `provideLocale(props)` call stays identical | -| Modify | `packages/vuetify/src/types.ts` | Update exported types to match new interfaces | -| No change | `packages/vuetify/src/locale/*.ts` | Language pack files unchanged | -| No change | `packages/vuetify/src/locale/index.ts` | Re-exports unchanged | -| No change | All consumer components | `useLocale()` and `useRtl()` return types stay compatible | - ---- - -## Task 1: Rewrite the core locale composable - -**Files:** -- Rewrite: `packages/vuetify/src/composables/locale.ts` -- Delete: `packages/vuetify/src/locale/adapters/vuetify.ts` - -This is the central change. Replace `createLocale`, `provideLocale`, `useLocale`, `createRtl`, `provideRtl`, `useRtl` with v0-backed implementations. - -- [ ] **Step 1: Rewrite `packages/vuetify/src/composables/locale.ts`** - -The new file delegates to v0 for locale selection, token storage, translation, and RTL state. Vuetify adds: `$vuetify.` prefix stripping, per-locale RTL map, `rtlClasses` computation, `LocaleSymbol` DI contract. - -**Design notes:** -- Root `createLocale` stores the full messages ref on the provided data via `_messages` so `provideLocale` can inherit them for scoped contexts. Without this, `` would have zero tokens. -- `provideLocale` does NOT create a `v0Rtl` instance — `provideRtl` already computes RTL from the map. Only the root needs the v0 RTL bridge. -- v0 plugin installation (`createLocalePlugin`/`createRtlPlugin`) is deferred to a follow-up — not needed for the refactor since all Vuetify components use `LocaleSymbol` injection. - -```ts -// Utilities -import { computed, inject, provide, ref, shallowRef, toRef, watch } from 'vue' -import { createLocale as createV0Locale, createRtl as createV0Rtl } from '@vuetify/v0' - -// Locales -import en from '@/locale/en' - -// Types -import type { InjectionKey, Ref } from 'vue' -import type { LocaleContext, RtlContext } from '@vuetify/v0' - -export interface LocaleMessages { - [key: string]: LocaleMessages | string -} - -export interface LocaleOptions { - decimalSeparator?: string - messages?: Record - locale?: string - fallback?: string - adapter?: LocaleInstance -} - -export interface LocaleInstance { - current: Ref - t: (key: string, ...params: unknown[]) => string - n: (value: number) => string - decimalSeparator: Ref -} - -export interface RtlOptions { - rtl?: Record -} - -export interface RtlProps { - rtl?: boolean -} - -export interface RtlInstance { - isRtl: Ref - rtl: Ref> - rtlClasses: Ref -} - -// Internal: carried on provided data so provideLocale can inherit messages -interface InternalLocaleData { - _messages: Record -} - -export const LocaleSymbol: InjectionKey = Symbol.for('vuetify:locale') -export const RtlSymbol: InjectionKey = Symbol.for('vuetify:rtl') - -const LANG_PREFIX = '$vuetify.' - -function genDefaults () { - return { - af: false, - ar: true, - bg: false, - ca: false, - ckb: false, - cs: false, - de: false, - el: false, - en: false, - es: false, - et: false, - fa: true, - fi: false, - fr: false, - hr: false, - hu: false, - he: true, - id: false, - it: false, - ja: false, - km: false, - ko: false, - lv: false, - lt: false, - nl: false, - no: false, - pl: false, - pt: false, - ro: false, - ru: false, - sk: false, - sl: false, - srCyrl: false, - srLatn: false, - sv: false, - th: false, - tr: false, - az: false, - uk: false, - vi: false, - zhHans: false, - zhHant: false, - } -} - -function createLocaleInstance ( - v0Locale: LocaleContext, - options?: { decimalSeparator?: string } -): LocaleInstance { - const current = computed({ - get: () => v0Locale.selectedId.value ?? 'en', - set: v => v0Locale.select(v), - }) - - function t (key: string, ...params: unknown[]): string { - const stripped = key.startsWith(LANG_PREFIX) ? key.slice(LANG_PREFIX.length) : key - return v0Locale.t(stripped, ...params) - } - - function n (value: number): string { - return v0Locale.n(value) - } - - const decimalSeparator = toRef(() => { - if (options?.decimalSeparator) return options.decimalSeparator - const formatted = n(0.1) - return formatted.includes(',') ? ',' : '.' - }) - - return { - current, - t, - n, - decimalSeparator, - } -} - -function createRtlInstance ( - locale: LocaleInstance, - rtlMap: Ref>, - v0Rtl: RtlContext -): RtlInstance { - // Bridge: when locale changes, update v0 RTL from per-locale map - watch(() => locale.current.value, current => { - v0Rtl.isRtl.value = rtlMap.value[current] ?? false - }, { immediate: true }) - - return { - isRtl: v0Rtl.isRtl, - rtl: rtlMap, - rtlClasses: toRef(() => `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}`), - } -} - -// --- Public API --- - -export function createLocale (options?: LocaleOptions & RtlOptions) { - // If a custom adapter (e.g. vue-i18n) is passed, use it directly - if (options?.adapter) { - const rtl = createRtlFromAdapter(options.adapter, options) - return { ...options.adapter, ...rtl } - } - - const messages = { en, ...options?.messages } - const defaultLocale = options?.locale ?? 'en' - const fallback = options?.fallback ?? 'en' - - const v0Locale = createV0Locale({ - default: defaultLocale, - fallback, - messages, - }) - - const v0Rtl = createV0Rtl({ - default: false, - // Pass null to prevent v0 from managing document dir attribute — - // Vuetify manages this via VApp's rtlClasses - target: null, - }) - - const rtlMap = ref>(options?.rtl ?? genDefaults()) - const locale = createLocaleInstance(v0Locale, options) - const rtl = createRtlInstance(locale, rtlMap, v0Rtl) - - return { ...locale, ...rtl, _messages: messages } as LocaleInstance & RtlInstance & InternalLocaleData -} - -function createRtlFromAdapter (adapter: LocaleInstance, options?: RtlOptions): RtlInstance { - const rtl = ref>(options?.rtl ?? genDefaults()) - const isRtl = computed(() => rtl.value[adapter.current.value] ?? false) - - return { - isRtl, - rtl, - rtlClasses: toRef(() => `v-locale--is-${isRtl.value ? 'rtl' : 'ltr'}`), - } -} - -export function useLocale () { - const locale = inject(LocaleSymbol) - - if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') - - return locale -} - -export function provideLocale (props: LocaleOptions & RtlProps) { - const parent = inject(LocaleSymbol) - - if (!parent) throw new Error('[Vuetify] Could not find injected locale instance') - - // If the parent is using a custom adapter (e.g. vue-i18n), delegate to its provide - if ('provide' in parent && typeof (parent as any).provide === 'function') { - const i18n = (parent as any).provide(props) - const rtl = provideRtl(i18n, parent.rtl, props) - const data = { ...i18n, ...rtl } - provide(LocaleSymbol, data) - return data - } - - // Inherit root messages, merge with any child-provided overrides - const parentMessages = (parent as any)._messages ?? {} - const messages = props.messages - ? { ...parentMessages, [props.locale ?? parent.current.value]: props.messages } - : parentMessages - - const v0Locale = createV0Locale({ - default: props.locale ?? parent.current.value, - fallback: props.fallback ?? 'en', - messages, - }) - - const locale = createLocaleInstance(v0Locale, props) - const rtl = provideRtl(locale, parent.rtl, props) - - const data = { ...locale, ...rtl, _messages: messages } as LocaleInstance & RtlInstance & InternalLocaleData - provide(LocaleSymbol, data) - return data -} - -function provideRtl (locale: LocaleInstance, rtl: RtlInstance['rtl'], props: RtlProps): RtlInstance { - const isRtl = computed(() => props.rtl ?? rtl.value[locale.current.value] ?? false) - - return { - isRtl, - rtl, - rtlClasses: toRef(() => `v-locale--is-${isRtl.value ? 'rtl' : 'ltr'}`), - } -} - -export function useRtl () { - const locale = inject(LocaleSymbol) - - if (!locale) throw new Error('[Vuetify] Could not find injected rtl instance') - - return { isRtl: locale.isRtl, rtlClasses: locale.rtlClasses } -} -``` - -- [ ] **Step 2: Delete the old Vuetify adapter** - -```bash -rm packages/vuetify/src/locale/adapters/vuetify.ts -``` - -The `createVuetifyAdapter` function is no longer needed — v0's `Vuetify0LocaleAdapter` handles translation and number formatting internally. - -- [ ] **Step 3: Verify the build compiles** - -```bash -cd packages/vuetify && pnpm build:lib 2>&1 | head -20 -``` - -Expected: No import errors for `@/locale/adapters/vuetify`. All components importing `useLocale` / `useRtl` should compile since the return types are compatible. - -- [ ] **Step 4: Run the existing locale tests** - -```bash -cd packages/vuetify && pnpm test:unit -- --run src/locale/__tests__/index.spec.ts -``` - -Expected: PASS — these tests check language pack exports and structure, not the adapter. - -- [ ] **Step 5: Commit** - -```bash -git add packages/vuetify/src/composables/locale.ts -git add packages/vuetify/src/locale/adapters/vuetify.ts -git commit -m "refactor(locale): replace Vuetify adapter with v0 createLocale/createRtl - -Vuetify's locale composable now delegates to @vuetify/v0 for: -- Message storage (tokens) and translation (t()) -- Number formatting (n()) -- Fallback locale resolution -- RTL boolean state - -Vuetify remains responsible for: -- $vuetify. prefix stripping -- Per-locale RTL map (ar, fa, he → true) -- CSS class generation (v-locale--is-rtl/ltr) -- LocaleSymbol provide/inject contract - -BREAKING CHANGE: LocaleInstance.provide() and .name removed. -LocaleInstance.fallback and .messages removed from public type. -Nested contexts use provideLocale() or VLocaleProvider." -``` - ---- - -## Task 2: Update the vue-i18n adapter - -**Files:** -- Rewrite: `packages/vuetify/src/locale/adapters/vue-i18n.ts` - -The vue-i18n adapter needs to return a `LocaleInstance` that Vuetify's `createLocale` can consume via the `adapter` option. v0 ships its own `VueI18nLocaleAdapter`, but Vuetify's adapter wraps vue-i18n at a higher level — it needs to satisfy the `LocaleInstance` interface (`current`, `t`, `n`, `decimalSeparator`) plus extra properties (`provide`, `messages`, `fallback`, `name`) used for scope nesting. - -- [ ] **Step 1: Rewrite the vue-i18n adapter** - -The adapter continues to wrap vue-i18n directly (not via v0's adapter), since it needs to preserve vue-i18n's full feature set (pluralization, linked messages, datetime formatting) and its own scope nesting via `useI18n({ useScope: 'local' })`. - -```ts -// Composables -import { useProxiedModel } from '@/composables/proxiedModel' - -// Utilities -import { toRef, watch } from 'vue' - -// Types -import type { Ref } from 'vue' -import type { I18n, useI18n } from 'vue-i18n' -import type { LocaleInstance, LocaleMessages, LocaleOptions } from '@/composables/locale' - -type VueI18nAdapterParams = { - i18n: I18n - useI18n: typeof useI18n -} - -function useProvided (props: any, prop: string, provided: Ref) { - const internal = useProxiedModel(props, prop) - - internal.value = props[prop] ?? provided.value - - watch(provided, v => { - if (props[prop] == null) { - internal.value = v - } - }) - - return internal as Ref -} - -function inferDecimalSeparator (format: (v: number) => string) { - return format(0.1).includes(',') ? ',' : '.' -} - -function createProvideFunction (data: { - current: Ref - fallback: Ref - messages: Ref - useI18n: typeof useI18n -}) { - return (props: LocaleOptions): LocaleInstance => { - const current = useProvided(props, 'locale', data.current) - const fallback = useProvided(props, 'fallback', data.fallback) - const messages = useProvided(props, 'messages', data.messages) - - const i18n = data.useI18n({ - locale: current.value, - fallbackLocale: fallback.value, - messages: messages.value as any, - useScope: 'local', - legacy: false, - inheritLocale: false, - }) - - watch(current, v => { - i18n.locale.value = v - }) - - return { - name: 'vue-i18n', - current, - fallback, - messages, - decimalSeparator: toRef(() => props.decimalSeparator ?? inferDecimalSeparator(i18n.n)), - t: (key: string, ...params: unknown[]) => i18n.t(key, params), - n: i18n.n, - provide: createProvideFunction({ current, fallback, messages, useI18n: data.useI18n }), - } - } -} - -export function createVueI18nAdapter ({ i18n, useI18n }: VueI18nAdapterParams): LocaleInstance { - const current = i18n.global.locale - const fallback = i18n.global.fallbackLocale as Ref - const messages = i18n.global.messages - - return { - name: 'vue-i18n', - current, - fallback, - messages, - decimalSeparator: toRef(() => inferDecimalSeparator(i18n.global.n)), - t: (key: string, ...params: unknown[]) => i18n.global.t(key, params), - n: i18n.global.n, - provide: createProvideFunction({ current, fallback, messages, useI18n }), - } -} -``` - -**Key difference from current:** The `LocaleInstance` interface no longer requires `name`, `fallback`, `messages`, or `provide`. The vue-i18n adapter keeps these as extra properties since it uses them for scope nesting — the core `provideLocale` detects the `provide` method via duck-typing and delegates to it. - -- [ ] **Step 2: Verify build** - -```bash -cd packages/vuetify && pnpm build:lib 2>&1 | head -20 -``` - -- [ ] **Step 3: Commit** - -```bash -git add packages/vuetify/src/locale/adapters/vue-i18n.ts -git commit -m "refactor(locale): update vue-i18n adapter for new LocaleInstance interface" -``` - ---- - -## Task 3: Update exported types - -**Files:** -- Modify: `packages/vuetify/src/types.ts` - -- [ ] **Step 1: Update type exports** - -The `LocaleInstance` interface no longer has `messages`, `provide`, or `name` as required fields in the public type. Verify the export in `types.ts` still works: - -```ts -export type { LocaleInstance, LocaleMessages, RtlInstance, LocaleOptions, RtlOptions } from '@/composables/locale' -``` - -This line doesn't need to change — it re-exports from the composable which we've already updated. Just verify it compiles. - -- [ ] **Step 2: Run typecheck** - -```bash -cd packages/vuetify && pnpm tsc --noEmit --pretty 2>&1 | tail -20 -``` - -Expected: No type errors from consumers using `useLocale()` — the return type (`LocaleInstance & RtlInstance`) has `t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator` which all existing consumers destructure. - -- [ ] **Step 3: Commit if types.ts needed changes** - -Only commit if changes were required. Skip if no changes needed. - ---- - -## Task 4: Integration verification - -- [ ] **Step 1: Run the full unit test suite** - -```bash -cd packages/vuetify && pnpm test:unit -- --run 2>&1 | tail -30 -``` - -Check for failures related to locale, translation, RTL, or `VLocaleProvider`. - -- [ ] **Step 2: Run lint** - -```bash -cd packages/vuetify && pnpm lint 2>&1 | tail -20 -``` - -- [ ] **Step 3: Spot-check translation behavior** - -Verify that v0's token flattening correctly handles Vuetify's nested message format. The language packs use nested objects like: - -```ts -{ dataTable: { sortBy: 'Sort by' } } -``` - -v0's `createTokens` flattens these to `en.dataTable.sortBy`. The adapter looks up `${locale}.${key}` — so `t('dataTable.sortBy')` resolves to `en.dataTable.sortBy`. This should work, but verify by checking a component that uses `t()` with a nested key. - -- [ ] **Step 4: Verify VLocaleProvider still works** - -`VLocaleProvider` calls `provideLocale(props)` — unchanged. The new `provideLocale` creates a scoped v0 locale instance. Verify that nested locale providers correctly override the parent's locale. - -- [ ] **Step 5: Verify non-prefixed key behavior** - -Current behavior: keys without `$vuetify.` are treated as raw templates with positional param interpolation. The new code strips the prefix if present, then delegates to v0's `t()`. For non-prefixed keys, v0 will look them up as tokens (which won't exist) and return the key after interpolation. Verify this works correctly. - -- [ ] **Step 6: Final commit with any fixes** - -```bash -git add -u -git commit -m "fix(locale): address integration issues from v0 migration" -``` - -Only if fixes were needed. Skip if everything passed. diff --git a/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md b/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md deleted file mode 100644 index 2f81c6e66d7..00000000000 --- a/docs/superpowers/specs/2026-03-16-v0-locale-integration-design.md +++ /dev/null @@ -1,276 +0,0 @@ -# v0 Locale Integration Design - -## Overview - -Migrate Vuetify's locale and RTL system to use `@vuetify/v0` composables as the runtime baseline. Vuetify becomes a thin configuration layer providing language packs and per-locale RTL defaults, while v0 owns message resolution, RTL state, number formatting, decimal separators, and the adapter pattern. - -## Architecture - -### Layers - -**v0 (runtime — source of truth):** - -- `createLocale()` — message storage (tokens), locale selection (createSingle), translation (`t()`), number formatting (`n()`), decimal separator inference -- `createRtl()` — reactive boolean direction state, `dir` attribute management via adapter -- `createLocalePlugin()` / `createRtlPlugin()` — Vue plugin installation for DI context -- `LocaleAdapter` — adapter interface for i18n provider integration (including vue-i18n) -- `Locale` provider component — scoped locale + RTL context for subtrees - -**Vuetify (configuration + thin wrapper):** - -- 48 language packs registered as v0 token messages -- Per-locale RTL map (`ar: true`, `fa: true`, `he: true`, etc.) -- `$vuetify.` prefix stripping before delegating to v0's `t()` -- `rtlClasses` computed ref (`v-locale--is-rtl` / `v-locale--is-ltr`) -- Bridge: watches v0 locale selection, updates v0 RTL from per-locale map -- `LocaleSymbol` provide/inject for Vuetify component consumption -- `VLocaleProvider` wrapping v0's `Locale` component with Vuetify CSS classes - -**RTL layering:** v0 manages the document-level `dir` attribute via `Vuetify0RtlAdapter`. Vuetify manages component-level CSS classes (`v-locale--is-rtl` / `v-locale--is-ltr`). These two layers are intentionally independent — a `VLocaleProvider` subtree can have a different direction class than the document `dir` attribute. - -## Initialization - -In `createVuetify()`: - -1. Create v0 locale instance with Vuetify's message packs: - ```ts - const v0Locale = createLocale({ - default: options.locale ?? 'en', - messages: { en, ...options.messages }, - }) - ``` - - Vuetify stores the fallback locale separately (not part of v0's `LocaleOptions`): - ```ts - const fallbackLocale = shallowRef(options.fallback ?? 'en') - ``` - -2. Create v0 RTL instance: - ```ts - const v0Rtl = createRtl({ - default: rtlMap[options.locale ?? 'en'] ?? false, - }) - ``` - -3. Bridge locale → RTL with `immediate: true` to cover initial state: - ```ts - watch(v0Locale.selectedId, locale => { - v0Rtl.isRtl.value = rtlMap[locale] ?? false - }, { immediate: true }) - ``` - -4. Compute Vuetify CSS classes: - ```ts - const rtlClasses = toRef(() => - `v-locale--is-${v0Rtl.isRtl.value ? 'rtl' : 'ltr'}` - ) - ``` - -5. Provide combined object via `LocaleSymbol` - -6. Install v0 plugins (`createLocalePlugin`, `createRtlPlugin`) so v0's context is also available directly - -## Translation Signature Bridging - -### Problem - -Vuetify's `t()` uses positional params: `t(key: string, ...params: unknown[])` with `{0}`, `{1}` placeholders. - -v0's `t()` uses named params: `t(key: string, params?: Record, fallback?: string)`. - -Components call Vuetify's `t()` like: -```ts -t(props.pageText, startIndex + 1, stopIndex, itemsLength) -// where pageText = '$vuetify.dataFooter.pageText' → '{0}-{1} of {2}' -``` - -### Solution - -Vuetify's wrapper `t()` strips the prefix and passes positional args directly: - -```ts -function t(key: string, ...params: unknown[]): string { - const stripped = key.startsWith('$vuetify.') ? key.slice(9) : key - return v0Locale.t(stripped, params.length ? params : undefined) -} -``` - -v0's `t()` calls `toArray(params)` then spreads as `adapter.t(template, ...args)`. The adapter's positional regex (`/\{(\d+)\}/g`) matches `{0}`, `{1}`, etc. against the spread args. Passing an array of positional values works correctly at runtime. - -**Type note:** v0's `t()` type declares `params` as `Record`, but the runtime handles arrays via `toArray()`. v0 should widen the type to `Record | unknown[]` — listed as a v0 prerequisite. - -**Non-prefixed keys:** When a key doesn't start with `$vuetify.`, it's treated as a raw template string (current behavior preserved). The wrapper passes it through to v0's `t()` which interpolates params directly. - -## Fallback Locale Resolution - -### Problem - -v0's `createLocale()` does not implement automatic fallback locale lookup. When a token path like `fr.dataTable.itemsPerPageText` is missing, v0 returns the raw key. Vuetify relies on fallback to English for untranslated keys. - -### Solution — v0 Prerequisite - -v0 must implement fallback locale resolution: when `{current}.{key}` is not found, automatically try `{fallback}.{key}` before returning the raw key. This is listed as a v0 prerequisite. - -If v0's fallback implementation is not ready in time, Vuetify's wrapper can temporarily implement the fallback chain by doing a direct token lookup for the fallback locale (avoiding reactive side-effects from `select()`): - -```ts -function t(key: string, ...params: unknown[]): string { - const stripped = key.startsWith('$vuetify.') ? key.slice(9) : key - const args = params.length ? params : undefined - - const result = v0Locale.t(stripped, args) - - // If v0 returned the raw key (not found), try fallback via direct token lookup - if (result === stripped && fallbackLocale.value !== v0Locale.selectedId.value) { - const fallbackPath = `${fallbackLocale.value}.${stripped}` - const fallbackMessage = v0Tokens.get(fallbackPath)?.value - if (isString(fallbackMessage)) { - return v0Adapter.t(fallbackMessage, ...params) - } - } - - return result -} -``` - -This requires holding references to the v0 tokens and adapter instances, but avoids mutating `selectedId` (which would trigger watchers and reactivity side-effects). - -**Note:** v0's `resolve()` function already supports cross-locale token references (`{en.some.key}`), which could be leveraged in message templates as a partial workaround. The proper fix is v0 supporting fallback natively. - -## Message Resolution Flow - -``` -component calls t('$vuetify.dataTable.itemsPerPageText', 1, 10, 100) - → Vuetify wrapper strips '$vuetify.' prefix → 'dataTable.itemsPerPageText' - → Vuetify wrapper passes positional args → [1, 10, 100] - → v0 locale.t('dataTable.itemsPerPageText', [1, 10, 100]) - → v0 looks up token: '{currentLocale}.dataTable.itemsPerPageText' - → (if missing, v0 tries '{fallbackLocale}.dataTable.itemsPerPageText') - → v0 adapter interpolates {0}, {1}, {2} from positional args - → '1-10 of 100' returned -``` - -## Consumer Composables - -### useLocale() - -Thin wrapper — injects `LocaleSymbol`, returns combined locale + RTL context: - -```ts -function useLocale() { - const locale = inject(LocaleSymbol) - if (!locale) throw new Error('[Vuetify] Could not find injected locale instance') - return locale -} -``` - -**Consumer-facing API:** `const { t, n, current, isRtl, rtlClasses } = useLocale()` - -- `t` — Vuetify's wrapped `t()` with `$vuetify.` prefix handling and positional param conversion -- `n` — delegates to v0's `n()` -- `current` — writable computed aliased from v0's `selectedId`: - ```ts - const current = computed({ - get: () => v0Locale.selectedId.value, - set: v => v0Locale.select(v), - }) - ``` - v0's `selectedId` is a read-only computed, so writes go through `select()`. -- `isRtl` — from v0's RTL context -- `rtlClasses` — Vuetify-computed CSS class string -- `decimalSeparator` — from v0's locale context (or Vuetify-computed interim, see "Decimal Separator" section) - -### useRtl() - -Thin wrapper — injects `LocaleSymbol`, returns `{ isRtl, rtlClasses }`. - -### provideLocale() - -Retained as a thin wrapper. Creates a scoped v0 locale + RTL context pair, computes `rtlClasses`, and provides the combined object via `LocaleSymbol`. Used internally by `VLocaleProvider` and available for renderless composable patterns. Will migrate to wrap v0's `Locale` component when it ships. - -## vue-i18n Adapter - -Moves to v0 as a `LocaleAdapter` implementation. Vuetify re-exports or provides a thin wrapper for `$vuetify.` prefix handling. User-facing config stays the same: - -```ts -const vuetify = createVuetify({ - locale: { - adapter: createVueI18nAdapter({ i18n, useI18n }), - }, -}) -``` - -For initial implementation, Vuetify can keep a compatibility shim wrapping v0's adapter until the v0 adapter fully ships. - -## VLocaleProvider - -### Target state - -Migrates to v0 as a `Locale` provider component. Vuetify wraps it as `VLocaleProvider` adding: - -- `v-locale-provider` CSS class -- `v-locale--is-rtl` / `v-locale--is-ltr` class -- Per-locale RTL map lookup on locale prop change - -### Interim implementation - -Until v0 ships the `Locale` component, `VLocaleProvider` creates scoped v0 contexts directly: - -1. Creates a new `createLocale()` instance with the provider's props (locale, messages) -2. Creates a new `createRtl()` with direction from per-locale RTL map (or explicit `rtl` prop) -3. Watches scoped locale selection → updates scoped RTL from per-locale map -4. Provides both scoped v0 contexts and Vuetify's `LocaleSymbol` with the scoped values + `rtlClasses` - -Each `VLocaleProvider` instance maintains its own locale→RTL bridge watcher, independent of the root bridge. - -## Breaking Changes - -- `LocaleInstance` type changes internally (backed by v0 refs) -- `LocaleInstance.provide()` method removed — `provideLocale()` replaces this (see below) -- `provideLocale()` retained as thin wrapper — creates scoped v0 locale + RTL contexts, provides via `LocaleSymbol`. Used internally by `VLocaleProvider`. Will migrate to wrap v0's `Locale` component when it ships. -- `messages: Ref` removed from public API — messages are managed by v0's token system; consumers register messages via `createVuetify()` options -- `fallback: Ref` removed from public API — fallback is configured at creation time, not mutated at runtime -- `rtl: Ref>` (per-locale RTL map) removed from public API — the map is Vuetify-internal; consumers use `isRtl` (boolean) and `rtlClasses` (string). `VLocaleProvider` uses the internal map for locale→RTL lookup. -- `RtlSymbol` deprecated — RTL state is accessed via `LocaleSymbol` (current behavior) or v0's `useRtl()` directly -- `decimalSeparator` type changes from `ShallowRef` to `Ref` (computed) — unlikely to break consumers in practice -- `LocaleInstance.name` field (`'vuetify'` / `'vue-i18n'`) removed — adapter identity is a v0 concern -- vue-i18n adapter import path may change -- Consumer-facing destructured API (`t`, `n`, `current`, `isRtl`, `rtlClasses`, `decimalSeparator`) remains stable - -## Decimal Separator - -v0 must expose `decimalSeparator` on `LocaleContext` (inferred from `Intl.NumberFormat` based on current locale). Vuetify's wrapper passes it through: - -```ts -const { decimalSeparator } = useLocale() -``` - -Listed as a v0 prerequisite. If not ready, Vuetify's wrapper computes it temporarily: - -```ts -const decimalSeparator = toRef(() => { - const formatted = v0Locale.n(0.1) - return formatted.includes(',') ? ',' : '.' -}) -``` - -## v0 Prerequisites - -These must land in v0 before or alongside the Vuetify migration: - -1. `useRtl` composable — **done** (v0.1.7) -2. Fallback locale resolution in `createLocale` — needed (try fallback locale when key not found in current) -3. Widen `t()` params type from `Record` to `Record | unknown[]` — needed -4. Decimal separator inference on `LocaleContext` — needed -5. vue-i18n `LocaleAdapter` implementation — needed -6. `Locale` provider component — needed (interim: Vuetify creates scoped contexts directly) - -## Vuetify Responsibilities (post-migration) - -- Ship 48 language message files -- Map locale codes to RTL defaults -- Strip `$vuetify.` prefix and convert positional params in `t()` wrapper -- Generate `v-locale--is-rtl` / `v-locale--is-ltr` CSS classes -- Alias `current` from v0's `selectedId` -- Wrap v0's `Locale` as `VLocaleProvider` with Vuetify styling -- Bridge locale selection → RTL state via per-locale map From 543c8caae2a7c73ad1fa4eda06b9450f28019353 Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 24 Mar 2026 11:12:17 -0500 Subject: [PATCH 07/14] chore: clean up inline comments in locale composable --- packages/vuetify/src/composables/locale.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/vuetify/src/composables/locale.ts b/packages/vuetify/src/composables/locale.ts index 0e7adcabfc8..ea2388d6008 100644 --- a/packages/vuetify/src/composables/locale.ts +++ b/packages/vuetify/src/composables/locale.ts @@ -1,8 +1,6 @@ // Utilities import { createLocale as createV0Locale, createRtl as createV0Rtl } from '@vuetify/v0' import { computed, inject, provide, ref, shallowRef, toRef, watch } from 'vue' - -// Locales import en from '@/locale/en' // Types @@ -42,7 +40,7 @@ export interface RtlInstance { rtlClasses: Ref } -/** @internal Carried on provided data so provideLocale can inherit context */ +/** @internal */ export interface InternalLocaleData { _messages: Record _fallback: string @@ -137,10 +135,8 @@ function createRtlInstance ( rtlMap: Ref>, v0Rtl: RtlContext ): RtlInstance { - // Local ref bridges v0's Ref (different @vue/reactivity copy) to Vuetify's const isRtl = shallowRef(v0Rtl.isRtl.value) - // Bridge: when locale changes, update RTL from per-locale map watch(() => locale.current.value, current => { const value = rtlMap.value[current] ?? false v0Rtl.isRtl.value = value @@ -154,10 +150,7 @@ function createRtlInstance ( } } -// --- Public API --- - export function createLocale (options?: LocaleOptions & RtlOptions) { - // If a custom adapter (e.g. vue-i18n) is passed, use it directly if (options?.adapter) { const rtl = createRtlFromAdapter(options.adapter, options) return { ...options.adapter, ...rtl } @@ -175,8 +168,6 @@ export function createLocale (options?: LocaleOptions & RtlOptions) { const v0Rtl = createV0Rtl({ default: false, - // Pass null to prevent v0 from managing document dir attribute — - // Vuetify manages this via VApp's rtlClasses target: null, }) @@ -211,7 +202,6 @@ export function provideLocale (props: LocaleOptions & RtlProps) { if (!parent) throw new Error('[Vuetify] Could not find injected locale instance') - // If the parent is using a custom adapter (e.g. vue-i18n), delegate to its provide if ('provide' in parent && typeof (parent as any).provide === 'function') { const i18n = (parent as any).provide(props) const rtl = provideRtl(i18n, parent.rtl, props) @@ -220,7 +210,6 @@ export function provideLocale (props: LocaleOptions & RtlProps) { return data } - // Inherit root messages and fallback, merge with any child-provided overrides const parentData = parent as any as Partial const parentMessages = parentData._messages ?? {} const parentFallback = parentData._fallback ?? 'en' From 7d78e9325c578413f658bafa42dcf2947e268ea0 Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 24 Mar 2026 11:18:17 -0500 Subject: [PATCH 08/14] docs: add v5 upgrade guide with locale breaking changes --- .../en/getting-started/upgrade-guide-v5.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md diff --git a/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md b/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md new file mode 100644 index 00000000000..701033eea17 --- /dev/null +++ b/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md @@ -0,0 +1,62 @@ +--- +emphasized: true +meta: + nav: Upgrade to v5 + title: Upgrade to v5 + description: Detailed instructions on how to upgrade Vuetify from v4 to v5 + keywords: migration, upgrade, releases, upgrading vuetify, v5 +related: + - /introduction/roadmap/ + - /introduction/long-term-support/ + - /getting-started/upgrade-guide/ +--- + +# Upgrade Guide (v5) + +This page contains a detailed list of breaking changes and the steps required to upgrade your application from Vuetify 4 to Vuetify 5. + + + +---- + +## Locale + +Vuetify's locale system is now powered by `@vuetify/v0` under the hood. The consumer-facing API (`useLocale`, `useRtl`, `VLocaleProvider`) is unchanged, but some internal types and methods have been removed. + +### Removed: `LocaleInstance.provide()` + +The `provide()` method on `LocaleInstance` has been removed. Scoped locale contexts are created via `VLocaleProvider` or the `provideLocale()` composable. + +```diff +- const locale = useLocale() +- const scoped = locale.provide({ locale: 'fr' }) ++ // Use VLocaleProvider in templates or provideLocale() in setup +``` + +### Removed: `LocaleInstance.name` + +The `name` field (`'vuetify'` or `'vue-i18n'`) has been removed. Adapter identity is now a `@vuetify/v0` concern. + +### Removed: `LocaleInstance.messages` + +The `messages` ref is no longer exposed on the public type. Messages are managed internally by `@vuetify/v0`'s token system. Register messages at creation time via `createVuetify({ locale: { messages } })`. + +### Removed: `LocaleInstance.fallback` + +The `fallback` ref is no longer exposed on the public type. Configure the fallback locale at creation time via `createVuetify({ locale: { fallback: 'en' } })`. + +### vue-i18n adapter + +The vue-i18n adapter continues to work with the same import path and configuration: + +```ts +import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n' + +const vuetify = createVuetify({ + locale: { + adapter: createVueI18nAdapter({ i18n, useI18n }), + }, +}) +``` + +No changes are required for vue-i18n users. From 27e5b4b21563518872cc33d533263b7ef7d45bbc Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 24 Mar 2026 11:19:28 -0500 Subject: [PATCH 09/14] docs: update upgrade guide for v5 locale breaking changes --- .../en/getting-started/upgrade-guide-v5.md | 62 ------------------- .../pages/en/getting-started/upgrade-guide.md | 36 ++++++++++- 2 files changed, 33 insertions(+), 65 deletions(-) delete mode 100644 packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md diff --git a/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md b/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md deleted file mode 100644 index 701033eea17..00000000000 --- a/packages/docs/src/pages/en/getting-started/upgrade-guide-v5.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -emphasized: true -meta: - nav: Upgrade to v5 - title: Upgrade to v5 - description: Detailed instructions on how to upgrade Vuetify from v4 to v5 - keywords: migration, upgrade, releases, upgrading vuetify, v5 -related: - - /introduction/roadmap/ - - /introduction/long-term-support/ - - /getting-started/upgrade-guide/ ---- - -# Upgrade Guide (v5) - -This page contains a detailed list of breaking changes and the steps required to upgrade your application from Vuetify 4 to Vuetify 5. - - - ----- - -## Locale - -Vuetify's locale system is now powered by `@vuetify/v0` under the hood. The consumer-facing API (`useLocale`, `useRtl`, `VLocaleProvider`) is unchanged, but some internal types and methods have been removed. - -### Removed: `LocaleInstance.provide()` - -The `provide()` method on `LocaleInstance` has been removed. Scoped locale contexts are created via `VLocaleProvider` or the `provideLocale()` composable. - -```diff -- const locale = useLocale() -- const scoped = locale.provide({ locale: 'fr' }) -+ // Use VLocaleProvider in templates or provideLocale() in setup -``` - -### Removed: `LocaleInstance.name` - -The `name` field (`'vuetify'` or `'vue-i18n'`) has been removed. Adapter identity is now a `@vuetify/v0` concern. - -### Removed: `LocaleInstance.messages` - -The `messages` ref is no longer exposed on the public type. Messages are managed internally by `@vuetify/v0`'s token system. Register messages at creation time via `createVuetify({ locale: { messages } })`. - -### Removed: `LocaleInstance.fallback` - -The `fallback` ref is no longer exposed on the public type. Configure the fallback locale at creation time via `createVuetify({ locale: { fallback: 'en' } })`. - -### vue-i18n adapter - -The vue-i18n adapter continues to work with the same import path and configuration: - -```ts -import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n' - -const vuetify = createVuetify({ - locale: { - adapter: createVueI18nAdapter({ i18n, useI18n }), - }, -}) -``` - -No changes are required for vue-i18n users. diff --git a/packages/docs/src/pages/en/getting-started/upgrade-guide.md b/packages/docs/src/pages/en/getting-started/upgrade-guide.md index 4668a15ff1e..554eeb7c67a 100644 --- a/packages/docs/src/pages/en/getting-started/upgrade-guide.md +++ b/packages/docs/src/pages/en/getting-started/upgrade-guide.md @@ -3,8 +3,8 @@ emphasized: true meta: nav: Upgrade guide title: Upgrade guide - description: Detailed instruction on how to upgrade Vuetify to 4.0 - keywords: migration, upgrade, releases, upgrading vuetify, alpha, v4 + description: Detailed instructions on how to upgrade Vuetify from v4 to v5 + keywords: migration, upgrade, releases, upgrading vuetify, v5 related: - /introduction/roadmap/ - /introduction/long-term-support/ @@ -13,10 +13,40 @@ related: # Upgrade Guide -This page contains a detailed list of breaking changes and the steps required to upgrade your application to Vuetify 4 +This page contains a detailed list of breaking changes and the steps required to upgrade your application from Vuetify 4 to Vuetify 5. +## Locale + +Vuetify's locale system is now powered by `@vuetify/v0` under the hood. The consumer-facing API (`useLocale`, `useRtl`, `VLocaleProvider`) is unchanged, but some internal types and methods have been removed. + +### Removed: `LocaleInstance.provide()` + +The `provide()` method on `LocaleInstance` has been removed. Scoped locale contexts are created via `VLocaleProvider` or the `provideLocale()` composable. + +```diff +- const locale = useLocale() +- const scoped = locale.provide({ locale: 'fr' }) ++ // Use VLocaleProvider in templates or provideLocale() in setup +``` + +### Removed: `LocaleInstance.name` + +The `name` field (`'vuetify'` or `'vue-i18n'`) has been removed. Adapter identity is now a `@vuetify/v0` concern. + +### Removed: `LocaleInstance.messages` + +The `messages` ref is no longer exposed on the public type. Messages are managed internally by `@vuetify/v0`'s token system. Register messages at creation time via `createVuetify({ locale: { messages } })`. + +### Removed: `LocaleInstance.fallback` + +The `fallback` ref is no longer exposed on the public type. Configure the fallback locale at creation time via `createVuetify({ locale: { fallback: 'en' } })`. + +### vue-i18n adapter + +The vue-i18n adapter continues to work with the same import path and configuration. No changes required. + ## Quick Start with Vuetify MCP The fastest way to check your project for breaking changes is with [Vuetify MCP](https://github.com/vuetifyjs/mcp/). To get started, run the following in your terminal: From 4537b437ada953531ce5db2d7601d2870a42d0ed Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 24 Mar 2026 11:22:25 -0500 Subject: [PATCH 10/14] =?UTF-8?q?docs:=20strip=20v3=E2=86=92v4=20content?= =?UTF-8?q?=20from=20upgrade=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/en/getting-started/upgrade-guide.md | 578 ------------------ 1 file changed, 578 deletions(-) diff --git a/packages/docs/src/pages/en/getting-started/upgrade-guide.md b/packages/docs/src/pages/en/getting-started/upgrade-guide.md index 554eeb7c67a..7ba0edef19e 100644 --- a/packages/docs/src/pages/en/getting-started/upgrade-guide.md +++ b/packages/docs/src/pages/en/getting-started/upgrade-guide.md @@ -46,581 +46,3 @@ The `fallback` ref is no longer exposed on the public type. Configure the fallba ### vue-i18n adapter The vue-i18n adapter continues to work with the same import path and configuration. No changes required. - -## Quick Start with Vuetify MCP - -The fastest way to check your project for breaking changes is with [Vuetify MCP](https://github.com/vuetifyjs/mcp/). To get started, run the following in your terminal: - -```bash -# Claude Code -claude mcp add --transport http vuetify-mcp https://mcp.vuetifyjs.com/mcp - -# Configure for hosted remote server -npx -y @vuetify/mcp config --remote - -# Or configure for local installation -npx -y @vuetify/mcp config -``` - -Once the MCP server is set up and loaded you will gain access to new tools such as: - -- `get_upgrade_guide`: Get a list of all breaking changes in the upgrade guide. -- `get_v4_breaking_changes`: Get a list of all breaking changes in Vuetify 4. - -Now, prompt your agent with the following: - -```text -Using the vuetify-mcp server, scan this project for Vuetify 3 to 4 breaking changes. List each issue found with the file, line number, and recommended fix. -``` - -This will automatically analyze your codebase and provide a tailored list of changes you need to make. - -If you have any questions about the upgrade process, come visit us at [community.vuetifyjs.com](https://community.vuetifyjs.com/). - -## Multi-step migration - -Several breaking changes in Vuetify 4 can be temporarily reverted by pasting short CSS or configuration snippets — notably [CSS reset](#css-reset), [typography](#typography), [elevation](#elevation), and [grid](#grid-system-vrow-and-vcol). This means you can migrate incrementally: restore the legacy behavior first, then update each area at your own pace. - -Even though these migrations mostly come down to adjusting CSS classes, manually reviewing every affected template can be time-consuming without automated visual regression tests. For large projects (typically over 200 components), we recommend scanning your codebase for relevant usage before starting: - -- **HTML elements** — `

` through `

` (affected by CSS reset) -- **Grid usage** — `` and ``, with specific focus on ad-hoc spacing adjustments (i.e. classes like `mx-0`, `pa-0`) -- **Grid attributes** — `dense`, `align`, `justify`, `order`, `align-self` (affected by grid changes) -- **Shadows** — `elevation-*` classes and `elevation` attributes or CSS overrides (affected by elevation changes) -- **CSS classes** — `text-h1` … `text-h6`, `text-subtitle-1`, `text-body-2`, `text-caption`, `text-overline`, `elevation-*`, `offset-*` (affected by typography) - -Identify the areas with the highest usage first, apply the corresponding compatibility snippets, and then schedule the full class-by-class migration as a follow-up. - -[vuetify-codemods](https://www.npmjs.com/package/vuetify-codemods) can be used to automate many of these changes. - -## Styles - -### Style entry points - -There are now pre-compiled entry points for the most common style changes. If you have a Sass file that only sets `$color-pack: false` or `$utilities: false` you can replace it with `import 'vuetify/styles/core'`. See [Style entry points](/styles/entry-points) for more information. - -### CSS reset - -The CSS reset has been mostly removed, with style normalisation being moved to individual components instead. You can inspect the exact [changes](https://github.com/vuetifyjs/vuetify/pull/20960/changes#diff-87996fc432835581ad883bedbc1975ad3a3f44b5747b2b831e3fa03dfdabb91f) to learn more. Here is the high level overview: - -- global `* { padding: 0; margin: 0; }` is gone - no longer resets all elements -- `