From 6db260fcba491d656691605b13adfd699c2c27a9 Mon Sep 17 00:00:00 2001 From: RainbowBird Date: Fri, 9 Jan 2026 18:28:40 +0800 Subject: [PATCH] feat(stage-ui): local-first storage (#922) --- packages/stage-ui/package.json | 2 + .../src/database/repos/characters.repo.ts | 30 +++++ .../src/database/repos/providers.repo.ts | 32 +++++ packages/stage-ui/src/database/storage.ts | 11 ++ packages/stage-ui/src/stores/characters.ts | 20 ++- .../stage-ui/src/stores/provider-catalog.ts | 32 +++-- pnpm-lock.yaml | 122 +++++++++++++++++- pnpm-workspace.yaml | 2 + 8 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 packages/stage-ui/src/database/repos/characters.repo.ts create mode 100644 packages/stage-ui/src/database/repos/providers.repo.ts create mode 100644 packages/stage-ui/src/database/storage.ts diff --git a/packages/stage-ui/package.json b/packages/stage-ui/package.json index 91791a6ec..005fea1da 100644 --- a/packages/stage-ui/package.json +++ b/packages/stage-ui/package.json @@ -94,6 +94,7 @@ "gpuu": "^1.0.6", "hono": "catalog:", "html2canvas": "^1.4.1", + "idb-keyval": "catalog:", "localforage": "^1.10.0", "mediabunny": "^1.27.2", "nanoid": "^5.1.6", @@ -114,6 +115,7 @@ "unist-builder": "^4.0.0", "unist-util-visit": "^5.0.0", "unspeech": "catalog:xsai", + "unstorage": "catalog:", "uuid": "^13.0.0", "valibot": "catalog:", "vaul-vue": "^0.4.1", diff --git a/packages/stage-ui/src/database/repos/characters.repo.ts b/packages/stage-ui/src/database/repos/characters.repo.ts new file mode 100644 index 000000000..688025284 --- /dev/null +++ b/packages/stage-ui/src/database/repos/characters.repo.ts @@ -0,0 +1,30 @@ +import type { Character } from '../../types/character' + +import { storage } from '../storage' + +export const charactersRepo = { + async getAll() { + return await storage.getItem('local:characters') || [] + }, + + async saveAll(characters: Character[]) { + await storage.setItem('local:characters', characters) + }, + + async upsert(character: Character) { + const all = await this.getAll() + const index = all.findIndex(c => c.id === character.id) + if (index > -1) { + all[index] = character + } + else { + all.push(character) + } + await this.saveAll(all) + }, + + async remove(id: string) { + const all = await this.getAll() + await this.saveAll(all.filter(c => c.id !== id)) + }, +} diff --git a/packages/stage-ui/src/database/repos/providers.repo.ts b/packages/stage-ui/src/database/repos/providers.repo.ts new file mode 100644 index 000000000..a5fa284d5 --- /dev/null +++ b/packages/stage-ui/src/database/repos/providers.repo.ts @@ -0,0 +1,32 @@ +import { storage } from '../storage' + +export interface ProviderCatalogProvider { + id: string + definitionId: string + name: string + config: Record + validated: boolean + validationBypassed: boolean +} + +export const providersRepo = { + async getAll() { + return await storage.getItem>('local:providers') || {} + }, + + async saveAll(providers: Record) { + await storage.setItem('local:providers', providers) + }, + + async upsert(provider: ProviderCatalogProvider) { + const all = await this.getAll() + all[provider.id] = provider + await this.saveAll(all) + }, + + async remove(id: string) { + const all = await this.getAll() + delete all[id] + await this.saveAll(all) + }, +} diff --git a/packages/stage-ui/src/database/storage.ts b/packages/stage-ui/src/database/storage.ts new file mode 100644 index 000000000..cf0b194a5 --- /dev/null +++ b/packages/stage-ui/src/database/storage.ts @@ -0,0 +1,11 @@ +import indexedDbDriver from 'unstorage/drivers/indexedb' +import memoryDriver from 'unstorage/drivers/memory' + +import { createStorage } from 'unstorage' + +export const storage = createStorage({ + driver: memoryDriver(), +}) + +storage.mount('local', indexedDbDriver({ base: 'airi-local' })) +storage.mount('outbox', indexedDbDriver({ base: 'airi-sync-queue' })) diff --git a/packages/stage-ui/src/stores/characters.ts b/packages/stage-ui/src/stores/characters.ts index f59f83ea1..e8fec8b7a 100644 --- a/packages/stage-ui/src/stores/characters.ts +++ b/packages/stage-ui/src/stores/characters.ts @@ -6,12 +6,22 @@ import { ref } from 'vue' import { client } from '../composables/api' import { useAsyncState } from '../composables/use-async-state' +import { charactersRepo } from '../database/repos/characters.repo' import { CharacterWithRelationsSchema } from '../types/character' export const useCharacterStore = defineStore('characters', () => { const characters = ref>(new Map()) async function fetchList(all: boolean = false) { + // Load from storage immediately + const cached = await charactersRepo.getAll() + if (cached.length > 0) { + characters.value.clear() + for (const char of cached) { + characters.value.set(char.id, char) + } + } + return useAsyncState(async () => { const res = await client.api.characters.$get({ query: { all: String(all) }, @@ -22,9 +32,13 @@ export const useCharacterStore = defineStore('characters', () => { const data = await res.json() characters.value.clear() + const parsedData: Character[] = [] for (const char of data) { - characters.value.set(char.id, parse(CharacterWithRelationsSchema, char)) + const parsed = parse(CharacterWithRelationsSchema, char) + characters.value.set(char.id, parsed) + parsedData.push(parsed) } + await charactersRepo.saveAll(parsedData) }, { immediate: true }) } @@ -40,6 +54,7 @@ export const useCharacterStore = defineStore('characters', () => { const character = parse(CharacterWithRelationsSchema, data) characters.value.set(character.id, character) + await charactersRepo.upsert(character) return character }, { immediate: true }) } @@ -56,6 +71,7 @@ export const useCharacterStore = defineStore('characters', () => { const character = parse(CharacterWithRelationsSchema, data) characters.value.set(character.id, character) + await charactersRepo.upsert(character) return character }, { immediate: true }) } @@ -74,6 +90,7 @@ export const useCharacterStore = defineStore('characters', () => { const character = parse(CharacterWithRelationsSchema, data) characters.value.set(character.id, character) + await charactersRepo.upsert(character) return character }, { immediate: true }) } @@ -88,6 +105,7 @@ export const useCharacterStore = defineStore('characters', () => { } characters.value.delete(id) + await charactersRepo.remove(id) }, { immediate: true }) } diff --git a/packages/stage-ui/src/stores/provider-catalog.ts b/packages/stage-ui/src/stores/provider-catalog.ts index 3de8dce1a..587abb257 100644 --- a/packages/stage-ui/src/stores/provider-catalog.ts +++ b/packages/stage-ui/src/stores/provider-catalog.ts @@ -1,24 +1,24 @@ +import type { ProviderCatalogProvider } from '../database/repos/providers.repo' + import { defineStore } from 'pinia' import { computed, ref } from 'vue' import { client } from '../composables/api' import { useAsyncState } from '../composables/use-async-state' +import { providersRepo } from '../database/repos/providers.repo' import { getDefinedProvider, listProviders } from '../libs/providers/providers' -interface ProviderCatalogProvider { - id: string - definitionId: string - name: string - config: Record - validated: boolean - validationBypassed: boolean -} - export const useProviderCatalogStore = defineStore('provider-catalog', () => { const defs = computed(() => listProviders()) const configs = ref>({}) async function fetchList() { + // Load from storage immediately + const cached = await providersRepo.getAll() + if (Object.keys(cached).length > 0) { + configs.value = cached + } + return useAsyncState(async () => { const res = await client.api.providers.$get() if (!res.ok) { @@ -38,6 +38,7 @@ export const useProviderCatalogStore = defineStore('provider-catalog', () => { } } configs.value = newConfigs + await providersRepo.saveAll(newConfigs) }, { immediate: true }) } @@ -61,7 +62,7 @@ export const useProviderCatalogStore = defineStore('provider-catalog', () => { } const item = await res.json() - configs.value[item.id] = { + const provider: ProviderCatalogProvider = { id: item.id, definitionId: item.definitionId, name: item.name, @@ -69,6 +70,8 @@ export const useProviderCatalogStore = defineStore('provider-catalog', () => { validated: item.validated, validationBypassed: item.validationBypassed, } + configs.value[item.id] = provider + await providersRepo.upsert(provider) return item }, { immediate: true }) } @@ -82,6 +85,7 @@ export const useProviderCatalogStore = defineStore('provider-catalog', () => { throw new Error('Failed to remove provider') } delete configs.value[providerId] + await providersRepo.remove(providerId) }, { immediate: true }) } @@ -105,9 +109,11 @@ export const useProviderCatalogStore = defineStore('provider-catalog', () => { } const item = await res.json() - configs.value[providerId].config = { ...item.config as Record } - configs.value[providerId].validated = item.validated - configs.value[providerId].validationBypassed = item.validationBypassed + const provider = configs.value[providerId] + provider.config = { ...item.config as Record } + provider.validated = item.validated + provider.validationBypassed = item.validationBypassed + await providersRepo.upsert(provider) }, { immediate: true }) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee7163c0a..190b71879 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ catalogs: hono: specifier: 4.11.3 version: 4.11.3 + idb-keyval: + specifier: ^6.2.2 + version: 6.2.2 injeca: specifier: ^0.1.5 version: 0.1.5 @@ -141,6 +144,9 @@ catalogs: unplugin-info: specifier: ^1.2.4 version: 1.2.4 + unstorage: + specifier: ^1.17.3 + version: 1.17.3 valibot: specifier: 1.2.0 version: 1.2.0 @@ -1726,7 +1732,7 @@ importers: version: 3.0.7(@nuxt/kit@3.20.2(magicast@0.5.1))(esbuild@0.27.2)(rolldown@1.0.0-beta.53)(rollup@4.54.0)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) vitepress: specifier: ^2.0.0-alpha.15 - version: 2.0.0-alpha.15(@algolia/client-search@5.46.2)(@types/node@24.10.4)(change-case@5.4.4)(fuse.js@7.1.0)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(nprogress@0.2.0)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + version: 2.0.0-alpha.15(@algolia/client-search@5.46.2)(@types/node@24.10.4)(change-case@5.4.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(nprogress@0.2.0)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) vue-tsc: specifier: ^3.1.8 version: 3.2.1(typescript@5.9.3) @@ -2274,6 +2280,9 @@ importers: html2canvas: specifier: ^1.4.1 version: 1.4.1 + idb-keyval: + specifier: 'catalog:' + version: 6.2.2 localforage: specifier: ^1.10.0 version: 1.10.0 @@ -2334,6 +2343,9 @@ importers: unspeech: specifier: catalog:xsai version: 0.1.11 + unstorage: + specifier: 'catalog:' + version: 1.17.3(idb-keyval@6.2.2) uuid: specifier: ^13.0.0 version: 13.0.0 @@ -2884,7 +2896,7 @@ importers: version: 66.5.11 '@wxt-dev/module-vue': specifier: ^1.0.3 - version: 1.0.3(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 1.0.3(vite@8.0.0-beta.5(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) vue-tsc: specifier: ^3.1.8 version: 3.2.1(typescript@5.9.3) @@ -9154,6 +9166,10 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + apache-arrow@17.0.0: resolution: {integrity: sha512-X0p7auzdnGuhYMVKYINdQssS4EcKec9TCXyez/qtJt32DrIMGbzqiaMiQ0X6fQlQpw8Fl0Qygcv4dfRAr5Gu9Q==} hasBin: true @@ -11854,6 +11870,9 @@ packages: resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -15569,6 +15588,68 @@ packages: unspeech@0.1.11: resolution: {integrity: sha512-leRXPcR3ou4F1Jxio0ePyOv+/+mm++huuLHMN4f63/My/s6tiFPbZ8EwQcgxw0FVpiaH4auad5qkhWUD3xxzrQ==} + unstorage@1.17.3: + resolution: {integrity: sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} @@ -22111,6 +22192,12 @@ snapshots: vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.25(typescript@5.9.3) + '@vitejs/plugin-vue@6.0.3(vite@8.0.0-beta.5(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.53 + vite: 8.0.0-beta.5(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vue: 3.5.25(typescript@5.9.3) + '@vitest/browser-playwright@4.0.16(bufferutil@4.1.0)(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.16)': dependencies: '@vitest/browser': 4.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.16) @@ -22785,7 +22872,7 @@ snapshots: '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) vue: 3.5.25(typescript@5.9.3) - '@vueuse/integrations@14.1.0(change-case@5.4.4)(focus-trap@7.7.0)(fuse.js@7.1.0)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3))': + '@vueuse/integrations@14.1.0(change-case@5.4.4)(focus-trap@7.7.0)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3))': dependencies: '@vueuse/core': 14.1.0(vue@3.5.25(typescript@5.9.3)) '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) @@ -22794,6 +22881,7 @@ snapshots: change-case: 5.4.4 focus-trap: 7.7.0 fuse.js: 7.1.0 + idb-keyval: 6.2.2 nprogress: 0.2.0 '@vueuse/metadata@10.11.1': {} @@ -22856,9 +22944,9 @@ snapshots: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 - '@wxt-dev/module-vue@1.0.3(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@wxt-dev/module-vue@1.0.3(vite@8.0.0-beta.5(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@vitejs/plugin-vue': 6.0.3(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.3(vite@8.0.0-beta.5(@types/node@24.10.4)(esbuild@0.25.12)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) wxt: 0.20.13(@types/node@24.10.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - vite @@ -23126,6 +23214,11 @@ snapshots: any-promise@1.3.0: {} + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + apache-arrow@17.0.0: dependencies: '@swc/helpers': 0.5.18 @@ -26227,6 +26320,8 @@ snapshots: dependencies: safer-buffer: '@nolyfill/safer-buffer@1.0.44' + idb-keyval@6.2.2: {} + idb@7.1.1: {} ieee754@1.2.1: {} @@ -30753,6 +30848,19 @@ snapshots: '@xsai-ext/providers': 0.4.0 '@xsai/shared': 0.4.0 + unstorage@1.17.3(idb-keyval@6.2.2): + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.4 + lru-cache: 10.4.3 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.1 + optionalDependencies: + idb-keyval: 6.2.2 + untildify@4.0.0: {} untun@0.1.3: @@ -31118,7 +31226,7 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitepress@2.0.0-alpha.15(@algolia/client-search@5.46.2)(@types/node@24.10.4)(change-case@5.4.4)(fuse.js@7.1.0)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(nprogress@0.2.0)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): + vitepress@2.0.0-alpha.15(@algolia/client-search@5.46.2)(@types/node@24.10.4)(change-case@5.4.4)(fuse.js@7.1.0)(idb-keyval@6.2.2)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(nprogress@0.2.0)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: '@docsearch/css': 4.4.0 '@docsearch/js': 4.4.0(@algolia/client-search@5.46.2)(react@19.2.3)(search-insights@2.17.3) @@ -31131,7 +31239,7 @@ snapshots: '@vue/devtools-api': 8.0.5 '@vue/shared': 3.5.26 '@vueuse/core': 14.1.0(vue@3.5.25(typescript@5.9.3)) - '@vueuse/integrations': 14.1.0(change-case@5.4.4)(focus-trap@7.7.0)(fuse.js@7.1.0)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': 14.1.0(change-case@5.4.4)(focus-trap@7.7.0)(fuse.js@7.1.0)(idb-keyval@6.2.2)(nprogress@0.2.0)(vue@3.5.25(typescript@5.9.3)) focus-trap: 7.7.0 mark.js: 8.11.1 minisearch: 7.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d1583e690..4592f3b35 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -58,6 +58,7 @@ catalog: embla-carousel-vue: ^8.6.0 es-toolkit: 1.43.0 hono: 4.11.3 + idb-keyval: ^6.2.2 injeca: ^0.1.5 is-network-error: ^1.3.0 nano-staged: ^0.9.0 @@ -68,6 +69,7 @@ catalog: tsx: ^4.21.0 uncrypto: ^0.1.3 unplugin-info: ^1.2.4 + unstorage: ^1.17.3 valibot: 1.2.0 vite-plugin-mkcert: ^1.17.9 vue-sonner: 2.0.9