Merge branch 'develop' into feature/drizzle

This commit is contained in:
Huakun Shen 2025-04-01 03:45:03 -04:00
commit 555a0594e3
No known key found for this signature in database
29 changed files with 175 additions and 125 deletions

46
Cargo.lock generated
View File

@ -833,6 +833,31 @@ dependencies = [
"piper",
]
[[package]]
name = "bon"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65268237be94042665b92034f979c42d431d2fd998b49809543afe3e66abad1c"
dependencies = [
"bon-macros",
"rustversion",
]
[[package]]
name = "bon-macros"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803c95b2ecf650eb10b5f87dda6b9f6a1b758cee53245e2b7b825c9b3803a443"
dependencies = [
"darling",
"ident_case",
"prettyplease",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.87",
]
[[package]]
name = "borsh"
version = "1.3.0"
@ -8360,9 +8385,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-svelte"
version = "1.2.1"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dab0a4d739af1108c6572e6249113190135c66a45586d0f8f93b3ee532e6176f"
checksum = "17e96f88b3c614b98cea3afb5de6e2661d32f82c70423ae125c56a25d62017e6"
dependencies = [
"serde",
"tauri",
@ -8493,9 +8518,9 @@ dependencies = [
[[package]]
name = "tauri-store"
version = "0.8.1"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4e7c0776d9f8b54fd4788f4e471ce83b4c6cf62079799830a3735582a51fc4"
checksum = "5a33c8afdf92c1b177296c0299f6d20116cbce0fa1e2264819fea8c80fd31774"
dependencies = [
"dashmap",
"futures",
@ -8512,9 +8537,9 @@ dependencies = [
[[package]]
name = "tauri-store-macros"
version = "0.8.1"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabed02238bc887f75887b315c6a14d9571ab463c1a188cc27ec2f7e917b06c3"
checksum = "c8857e4240cf6dbabb15fc2d595e92abba404f0a5cce0f3abbfe9316cac4aa99"
dependencies = [
"proc-macro2",
"quote",
@ -8523,11 +8548,14 @@ dependencies = [
[[package]]
name = "tauri-store-utils"
version = "0.2.2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b983a259b22d622ce74b957140efa161bd75c6bfd47b7bf621c98dd05b1a2474"
checksum = "14376c237a6632663991634d51a31f128b6b381b94d65e747db2419a513ae6d8"
dependencies = [
"futures",
"bon",
"semver",
"serde",
"serde_json",
"tauri",
"thiserror 2.0.3",
"tokio",

View File

@ -40,6 +40,7 @@
"settings_general_join_beta_updates": "Beta-Updates nutzen",
"settings_general_developer_mode": "Entwickler-Modus",
"settings_general_language": "Sprache",
"settings_general_loading_animation": "Ladeanimation",
"settings_app_search_paths_title": "Zusätzliche Verzeichnisse für die Programm-Suche",
"settings_app_search_paths_add_app_search_path": "Verzeichnis für Programm-Suche hinzufügen",

View File

@ -40,6 +40,7 @@
"settings_general_join_beta_updates": "Join Beta Updates",
"settings_general_developer_mode": "Developer Mode",
"settings_general_language": "Language",
"settings_general_loading_animation": "Loading Animation",
"settings_app_search_paths_title": "Extra App Search Paths",
"settings_app_search_paths_add_app_search_path": "Add App Search Path",

View File

@ -39,6 +39,7 @@
"settings_general_join_beta_updates": "Participar das Atualizações Beta",
"settings_general_developer_mode": "Modo Desenvolvedor",
"settings_general_language": "Idioma",
"settings_general_loading_animation": "Animação de Carregamento",
"settings_about_version": "Versão",
"settings_about_author": "Autor",

View File

@ -39,6 +39,7 @@
"settings_general_join_beta_updates": "Получать бета-обновления",
"settings_general_developer_mode": "Режим разработчика",
"settings_general_language": "Язык",
"settings_general_loading_animation": "Анимация загрузки",
"settings_about_version": "Версия",
"settings_about_author": "Автор",

View File

@ -39,6 +39,7 @@
"settings_general_join_beta_updates": "Cài đặt cập nhật thử nghiệm (beta)",
"settings_general_developer_mode": "Chế độ nhà phát triển",
"settings_general_language": "Ngôn ngữ",
"settings_general_loading_animation": "Hình ảnh tải",
"settings_about_version": "Phiên bản",
"settings_about_author": "Tác giả",

View File

@ -40,6 +40,7 @@
"settings_general_join_beta_updates": "加入 Beta 更新",
"settings_general_developer_mode": "开发者模式",
"settings_general_language": "语言",
"settings_general_loading_animation": "加载动画",
"settings_app_search_paths_title": "额外应用搜索路径",
"settings_app_search_paths_add_app_search_path": "添加应用搜索路径",

View File

@ -1,6 +1,6 @@
{
"name": "@kksh/desktop",
"version": "0.1.36",
"version": "0.1.37-beta.1",
"description": "",
"type": "module",
"scripts": {
@ -30,6 +30,7 @@
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-sql": "^2.2.0",
"@tauri-apps/plugin-stronghold": "^2.2.0",
"@tauri-store/svelte": "^2.1.1",
"dompurify": "^3.2.4",
"drizzle-orm": "^0.40.1",
"eslint": "^9.21.0",

View File

@ -72,4 +72,4 @@ tauri-plugin-cli = "2"
tauri-plugin-global-shortcut = "2.0.1"
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
tauri-plugin-updater = "2.0.2"
tauri-plugin-svelte = "1.2.1"
tauri-plugin-svelte = "2.1.1"

View File

@ -24,6 +24,7 @@
"core:event:default",
"core:window:default",
"core:window:allow-set-size",
"core:window:allow-set-enabled",
"core:window:allow-start-dragging",
"core:window:allow-set-focus",
"core:window:allow-toggle-maximize",

View File

@ -27,7 +27,7 @@ use utils::server::tauri_file_server;
pub fn run() {
let context = tauri::generate_context!();
let mut builder = tauri::Builder::default();
// let app_data_path = tauri::path::PathResolver::app_data_dir().unwrap();
// let db_key = if cfg!(debug_assertions) {
// None
// } else {

View File

@ -428,7 +428,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
visible: false
})
setTimeout(() => {
window.show()
window.show().then(() => window.setFocus())
}, 2_000)
}
},

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { appState } from "@/stores"
import { appConfig, appState } from "@/stores"
import { cn } from "@/utils"
import { Button } from "@kksh/svelte5"
import { BorderBeam, Constants, Layouts, TauriLink } from "@kksh/ui"
@ -25,8 +25,13 @@
>
<ArrowLeftIcon class="size-4" />
</Button>
<Dance class="absolute z-50 h-screen opacity-20" />
<LoaderCircleIcon class="h-24 w-24 animate-spin" />
<span class="font-mono">Loading</span>
{#if $appConfig.loadingAnimation === "kunkun-dancing"}
<!-- <DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
<Dance class="absolute z-50 h-screen opacity-20" />
{:else}
<!-- <LoadingAnimation delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
<LoaderCircleIcon class="h-24 w-24 animate-spin" />
<span class="font-mono">Loading</span>
{/if}
<BorderBeam size={150} duration={12} />
</Layouts.Center>

View File

@ -11,20 +11,24 @@
} from "@/paraglide/runtime"
import { appConfig } from "@/stores"
import { Select, Switch } from "@kksh/svelte5"
import type { LoadingAnimation } from "@kksh/types"
import * as autoStart from "@tauri-apps/plugin-autostart"
import { onMount } from "svelte"
import { toast } from "svelte-sonner"
const languages = availableLanguageTags.map((lang) => ({
value: lang,
label: LanguageMap[lang] ?? lang
label: LanguageMap[lang as keyof typeof LanguageMap] ?? lang
}))
let loadingAnimation = $state<LoadingAnimation>("spinning-circle")
const loadingAnimations = ["spinning-circle", "kunkun-dancing"] as const
let launchAtLogin = $state(false)
let language = $state(languageTag())
onMount(() => {
autoStart.isEnabled().then((enabled) => {
launchAtLogin = enabled
})
loadingAnimation = $appConfig.loadingAnimation
})
const triggerContent = $derived(languages.find((f) => f.value === language)?.label ?? "Language")
</script>
@ -101,6 +105,31 @@
</Select.Content>
</Select.Root>
</li>
<li>
<span>{m.settings_general_loading_animation()}</span>
<Select.Root type="single" name="loadingAnimation" bind:value={loadingAnimation}>
<Select.Trigger class="w-fit">
{loadingAnimation}
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.GroupHeading>Loading Animation</Select.GroupHeading>
{#each loadingAnimations as anim}
<Select.Item
onclick={() => {
appConfig.setLoadingAnimation(anim)
}}
value={anim}
label={anim}
>
{anim}
</Select.Item>
{/each}
</Select.Group>
</Select.Content>
</Select.Root>
</li>
</ul>
<style scoped>

View File

@ -1,13 +1,13 @@
import { getExtensionsFolder } from "@/constants"
import type { SearchPath } from "@kksh/api/models"
import { updateTheme, type ThemeConfig } from "@kksh/svelte5"
import { PersistedAppConfig, type AppConfigState } from "@kksh/types"
import { LoadingAnimation, PersistedAppConfig, type AppConfigState } from "@kksh/types"
import { debug, error, info } from "@tauri-apps/plugin-log"
import * as os from "@tauri-apps/plugin-os"
import { load } from "@tauri-apps/plugin-store"
import { Store } from "@tauri-store/svelte"
import { toast } from "svelte-sonner"
import { get, writable } from "svelte/store"
import { Store } from "tauri-plugin-svelte"
import * as v from "valibot"
export const defaultAppConfig: AppConfigState = {
@ -29,7 +29,8 @@ export const defaultAppConfig: AppConfigState = {
joinBetaProgram: false,
onBoarded: false,
developerMode: false,
appSearchPaths: []
appSearchPaths: [],
loadingAnimation: "kunkun-dancing"
}
export const appConfigLoaded = writable(false)
@ -99,74 +100,10 @@ class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI {
appSearchPaths: config.appSearchPaths.filter((path) => path.path !== appSearchPath.path)
}))
}
setLoadingAnimation(loadingAnimation: LoadingAnimation) {
this.update((config) => ({ ...config, loadingAnimation }))
}
}
// function createAppConfig(): WithSyncStore<AppConfigState & { language: string }> & AppConfigAPI {
// const store = createTauriSyncStore("app-config", defaultAppConfig)
// async function init() {
// debug("Initializing app config")
// const persistStore = await load("kk-config.json", { autoSave: true })
// let loadedConfig = await persistStore.get("config")
// if (typeof loadedConfig === "object") {
// loadedConfig = { ...defaultAppConfig, ...loadedConfig }
// }
// const parseRes = v.safeParse(PersistedAppConfig, loadedConfig)
// if (parseRes.success) {
// console.log("Parse Persisted App Config Success", parseRes.output)
// const extensionsInstallDir = await getExtensionsFolder()
// store.update((config) => ({
// ...config,
// ...parseRes.output,
// isInitialized: true,
// extensionsInstallDir,
// platform: os.platform()
// }))
// } else {
// error("Failed to parse app config, going to remove it and reinitialize")
// console.error(v.flatten<typeof PersistedAppConfig>(parseRes.issues))
// await persistStore.clear()
// await persistStore.set("config", v.parse(PersistedAppConfig, defaultAppConfig))
// }
// store.subscribe(async (config) => {
// console.log("Saving app config", config)
// await persistStore.set("config", config)
// updateTheme(config.theme)
// })
// }
// return {
// ...store,
// get: () => get(store),
// setTheme: (theme: ThemeConfig) => store.update((config) => ({ ...config, theme })),
// setDevExtensionPath: (devExtensionPath: string | null) => {
// console.log("setDevExtensionPath", devExtensionPath)
// store.update((config) => ({ ...config, devExtensionPath }))
// },
// setTriggerHotkey: (triggerHotkey: string[]) => {
// store.update((config) => ({ ...config, triggerHotkey }))
// },
// setOnBoarded: (onBoarded: boolean) => {
// store.update((config) => ({ ...config, onBoarded }))
// },
// setLanguage: (language: string) => {
// store.update((config) => ({ ...config, language }))
// },
// addAppSearchPath: (appSearchPath: SearchPath) => {
// store.update((config) => ({
// ...config,
// appSearchPaths: [...config.appSearchPaths, appSearchPath]
// }))
// },
// removeAppSearchPath: (appSearchPath: SearchPath) => {
// store.update((config) => ({
// ...config,
// appSearchPaths: config.appSearchPaths.filter((path) => path.path !== appSearchPath.path)
// }))
// },
// init
// }
// }
// export const appConfig = createAppConfig()
export const appConfig = new AppConfigStore()

View File

@ -48,8 +48,7 @@ export async function registerAppHotkey(hotkeyStr: string) {
mainWin.setFocus()
}
} else {
mainWin.show()
mainWin.setFocus()
mainWin.show().then(() => mainWin.setFocus())
}
}
})

View File

@ -97,7 +97,7 @@ export async function globalKeyDownHandler(e: KeyboardEvent) {
await appWin.hide()
location.reload()
setTimeout(() => {
appWin.show()
appWin.show().then(() => appWin.setFocus())
}, 1_000)
}
}

View File

@ -1,5 +1,15 @@
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { browser } from "$app/environment"
// Tauri doesn't have a Node.js server to do proper SSR
// so we will use adapter-static to prerender the app (SSG)
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
export const prerender = true
export const ssr = false
export const load = () => {
if (browser) {
const win = getCurrentWebviewWindow()
return { win }
}
}

View File

@ -46,7 +46,8 @@
})
})
let { children } = $props()
let { children, data } = $props()
const unlisteners: UnlistenFn[] = []
onDestroy(() => {
unlisteners.forEach((unlistener) => unlistener())
@ -100,7 +101,7 @@
})
)
}
getCurrentWebviewWindow().show()
data.win?.show().then(() => data.win?.setFocus())
})
</script>

View File

@ -1,7 +1,11 @@
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
import * as path from "@tauri-apps/api/path"
import { error } from "@tauri-apps/plugin-log"
import { setStoreCollectionPath } from "@tauri-store/svelte"
import type { LayoutLoad } from "./$types"
export const load: LayoutLoad = async () => {
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "" }
const appDataPath = await path.appDataDir()
await setStoreCollectionPath(await path.join(appDataPath, "kk-config"))
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "", appDataPath }
}

View File

@ -68,12 +68,16 @@
if (splashscreenWin) {
splashscreenWin.close()
}
win.show()
win.show().then(() => win.setFocus())
})
win.onFocusChanged(({ payload: focused }) => {
if (focused) {
win.show()
inputEle?.focus()
win
.show()
.then(() => win.setFocus())
.finally(() => {
inputEle?.focus()
})
}
})
inputEle?.focus()

View File

@ -8,6 +8,8 @@
import * as userInput from "tauri-plugin-user-input-api"
import { type InputEvent } from "tauri-plugin-user-input-api"
let { data } = $props()
const SymbolMap = {
Alt: "⎇",
AltGr: "⌥",
@ -97,10 +99,7 @@
}
$effect(() => {
const win = getCurrentWebviewWindow()
if (win) {
win.show()
}
data.win?.show().then(() => data.win?.setFocus())
userInput.setEventTypes([userInput.EventTypeEnum.KeyPress, userInput.EventTypeEnum.KeyRelease])
userInput.startListening((evt: InputEvent) => {

View File

@ -8,23 +8,24 @@
import * as clipboard from "tauri-plugin-clipboard-api"
let image = $state<string | null>(null)
const appWin = getCurrentWebviewWindow()
let { data } = $props()
let originalSize = $state<{ width: number; height: number } | null>(null)
let originalScaleFactor = $state<number | null>(null)
let scale = $state<number>(1)
let currentSize = $derived(
originalSize ? { width: originalSize.width * scale, height: originalSize.height * scale } : null
)
const win = getCurrentWebviewWindow()
$effect(() => {
if (currentSize) {
appWin.setSize(new LogicalSize(currentSize.width, currentSize.height))
win.setSize(new LogicalSize(currentSize.width, currentSize.height))
}
})
async function getWindowSize() {
const size = await appWin.outerSize()
const scaleFactor = originalScaleFactor ?? (await appWin.scaleFactor())
const size = await win.outerSize()
const scaleFactor = originalScaleFactor ?? (await win.scaleFactor())
const logicalSize = size.toLogical(scaleFactor)
return { logicalSize, scaleFactor }
}
@ -36,7 +37,7 @@
image = b64
})
.finally(() => {
appWin.show()
data.win?.show().then(() => data.win?.setFocus())
})
const { logicalSize, scaleFactor } = await getWindowSize()
originalSize = { width: logicalSize.width, height: logicalSize.height }
@ -67,13 +68,13 @@
<svelte:window
on:keydown={(e) => {
if (e.key === "Escape") {
appWin.close()
win.close()
}
}}
/>
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => appWin.close()}
><CircleX /></Button
>
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => win.close()}>
<CircleX />
</Button>
<main class="z-50 h-screen w-screen overflow-hidden" data-tauri-drag-region>
{#if image}
<img

View File

@ -1,7 +1,7 @@
<script lang="ts">
import DanceTransition from "@/components/dance/dance-transition.svelte"
import { i18n } from "@/i18n"
import { appConfig, winExtMap } from "@/stores"
import { appConfig, appState, winExtMap } from "@/stores"
import { helperAPI } from "@/utils/helper"
import { paste } from "@/utils/hotkey"
import { goBackOnEscape } from "@/utils/key"
@ -38,7 +38,6 @@
let { data }: { data: PageData } = $props()
const { loadedExt, url, extPath, extInfoInDB } = data
let extSpawnedProcesses = $state<number[]>([])
const appWin = getCurrentWindow()
let iframeRef: HTMLIFrameElement
let uiControl = $state<{
iframeLoaded: boolean
@ -65,7 +64,7 @@
if (isInMainWindow()) {
goto(i18n.resolveRoute("/app/"))
} else {
appWin.close()
data.win?.close()
}
},
hideBackButton: async () => {
@ -131,7 +130,7 @@
},
getSpawnedProcesses: () => Promise.resolve(extSpawnedProcesses),
paste: async () => {
await appWin.hide()
await data.win?.hide()
await sleep(200)
return paste()
}
@ -155,7 +154,7 @@
if (isInMainWindow()) {
goHome()
} else {
appWin.close()
data.win?.close()
}
}
@ -163,12 +162,14 @@
setTimeout(() => {
iframeRef.focus()
uiControl.iframeLoaded = true
appState.setFullScreenLoading(false)
}, 300)
}
onMount(() => {
appState.setFullScreenLoading(true)
setTimeout(() => {
appWin.show()
data.win?.setFocus()
}, 200)
if (iframeRef?.contentWindow) {
const io = new IframeParentIO(iframeRef.contentWindow)
@ -194,7 +195,7 @@
})
onDestroy(() => {
winExtMap.unregisterExtensionFromWindow(appWin.label)
winExtMap.unregisterExtensionFromWindow(data.win?.label ?? "")
})
</script>
@ -207,7 +208,7 @@
onclick={onBackBtnClicked}
style={`${positionToCssStyleString(uiControl.backBtnPosition)}`}
>
{#if appWin.label === "main"}
{#if data.win?.label === "main"}
<ArrowLeftIcon class="w-4" />
{:else}
<XIcon class="w-4" />
@ -238,7 +239,6 @@
{/if}
<main class="h-screen">
<DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} />
<iframe
bind:this={iframeRef}
class={cn("h-full", {

View File

@ -1,5 +1,6 @@
import { KunkunIframeExtParams } from "@/cmds/ext"
import { i18n } from "@/i18n"
import { appState } from "@/stores/appState"
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
import { loadExtensionManifestFromDisk } from "@kksh/extension"

View File

@ -310,7 +310,7 @@
onMount(async () => {
setTimeout(() => {
appState.setLoadingBar(true)
appWin.show()
appWin.show().then(() => appWin.setFocus())
}, 100)
unlistenRefreshWorkerExt = await listenToRefreshDevExt(() => {
debug("Refreshing Worker Extension")

View File

@ -1,11 +1,11 @@
<script lang="ts">
import { Layouts } from "@kksh/ui"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { onMount } from "svelte"
onMount(async () => {
const mainWin = await getCurrentWindow()
mainWin.show()
let { data } = $props()
onMount(() => {
data.win?.show().then(() => data.win?.setFocus())
})
</script>

View File

@ -2,6 +2,9 @@ import { LightMode, SearchPath } from "@kksh/api/models"
import type { Platform } from "@tauri-apps/plugin-os"
import * as v from "valibot"
export const LoadingAnimation = v.union([v.literal("spinning-circle"), v.literal("kunkun-dancing")])
export type LoadingAnimation = v.InferOutput<typeof LoadingAnimation>
export const PersistedAppConfig = v.object({
theme: v.object({
theme: v.string(),
@ -18,7 +21,8 @@ export const PersistedAppConfig = v.object({
joinBetaProgram: v.boolean(),
onBoarded: v.boolean(),
developerMode: v.boolean(),
appSearchPaths: v.array(SearchPath)
appSearchPaths: v.array(SearchPath),
loadingAnimation: LoadingAnimation
})
export type PersistedAppConfig = v.InferOutput<typeof PersistedAppConfig>

20
pnpm-lock.yaml generated
View File

@ -251,6 +251,9 @@ importers:
'@tauri-apps/plugin-stronghold':
specifier: ^2.2.0
version: 2.2.0
'@tauri-store/svelte':
specifier: ^2.1.1
version: 2.1.1
dompurify:
specifier: ^3.2.4
version: 3.2.4
@ -5685,6 +5688,12 @@ packages:
'@tauri-store/shared@0.6.0':
resolution: {integrity: sha512-2KBezqqkw68HvvXHEtbbpxyQHDjymBUZl10YuAsNRI8DHFIA0n18WE7NRyQ93+H7IzDP1/B41m2/rcMDHBSiKw==}
'@tauri-store/shared@0.7.2':
resolution: {integrity: sha512-42nprNNeU+tjpCvYnaBsu3kYwAy8gP6KyXX9zeaIvpMys1tIhQdEiUAXo3KChHq/jCkSralXRLN4zfjCfBJFrw==}
'@tauri-store/svelte@2.1.1':
resolution: {integrity: sha512-exGvgEM6zcXZq6KRnG2b2JDXogyarRaJdjrblD27Q4IU1vhSTY8TxvDMCPGfD31kbOcf/aR4A6zT8OX0DFuxjg==}
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
@ -18251,6 +18260,17 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.3.0
'@tauri-store/shared@0.7.2':
dependencies:
'@tauri-apps/api': 2.3.0
es-toolkit: 1.34.1
'@tauri-store/svelte@2.1.1':
dependencies:
'@tauri-apps/api': 2.3.0
'@tauri-store/shared': 0.7.2
svelte: 5.20.5
'@trysound/sax@0.2.0': {}
'@ts-graphviz/adapter@2.0.5':