mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-05-24 21:55:04 +00:00
Compare commits
6 Commits
5455ad77c8
...
3596f0b8a4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3596f0b8a4 | ||
![]() |
8fd3223b66 | ||
![]() |
92c69430ff | ||
![]() |
555a0594e3 | ||
![]() |
bf51fdadbc | ||
![]() |
9cf06b1835 |
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -833,6 +833,31 @@ dependencies = [
|
|||||||
"piper",
|
"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]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -8360,9 +8385,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-svelte"
|
name = "tauri-plugin-svelte"
|
||||||
version = "1.2.1"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dab0a4d739af1108c6572e6249113190135c66a45586d0f8f93b3ee532e6176f"
|
checksum = "17e96f88b3c614b98cea3afb5de6e2661d32f82c70423ae125c56a25d62017e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"tauri",
|
"tauri",
|
||||||
@ -8493,9 +8518,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-store"
|
name = "tauri-store"
|
||||||
version = "0.8.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb4e7c0776d9f8b54fd4788f4e471ce83b4c6cf62079799830a3735582a51fc4"
|
checksum = "5a33c8afdf92c1b177296c0299f6d20116cbce0fa1e2264819fea8c80fd31774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"futures",
|
"futures",
|
||||||
@ -8512,9 +8537,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-store-macros"
|
name = "tauri-store-macros"
|
||||||
version = "0.8.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fabed02238bc887f75887b315c6a14d9571ab463c1a188cc27ec2f7e917b06c3"
|
checksum = "c8857e4240cf6dbabb15fc2d595e92abba404f0a5cce0f3abbfe9316cac4aa99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -8523,11 +8548,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-store-utils"
|
name = "tauri-store-utils"
|
||||||
version = "0.2.2"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b983a259b22d622ce74b957140efa161bd75c6bfd47b7bf621c98dd05b1a2474"
|
checksum = "14376c237a6632663991634d51a31f128b6b381b94d65e747db2419a513ae6d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"bon",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"thiserror 2.0.3",
|
"thiserror 2.0.3",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"settings_general_join_beta_updates": "Beta-Updates nutzen",
|
"settings_general_join_beta_updates": "Beta-Updates nutzen",
|
||||||
"settings_general_developer_mode": "Entwickler-Modus",
|
"settings_general_developer_mode": "Entwickler-Modus",
|
||||||
"settings_general_language": "Sprache",
|
"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_title": "Zusätzliche Verzeichnisse für die Programm-Suche",
|
||||||
"settings_app_search_paths_add_app_search_path": "Verzeichnis für Programm-Suche hinzufügen",
|
"settings_app_search_paths_add_app_search_path": "Verzeichnis für Programm-Suche hinzufügen",
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"settings_general_join_beta_updates": "Join Beta Updates",
|
"settings_general_join_beta_updates": "Join Beta Updates",
|
||||||
"settings_general_developer_mode": "Developer Mode",
|
"settings_general_developer_mode": "Developer Mode",
|
||||||
"settings_general_language": "Language",
|
"settings_general_language": "Language",
|
||||||
|
"settings_general_loading_animation": "Loading Animation",
|
||||||
|
|
||||||
"settings_app_search_paths_title": "Extra App Search Paths",
|
"settings_app_search_paths_title": "Extra App Search Paths",
|
||||||
"settings_app_search_paths_add_app_search_path": "Add App Search Path",
|
"settings_app_search_paths_add_app_search_path": "Add App Search Path",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"settings_general_join_beta_updates": "Participar das Atualizações Beta",
|
"settings_general_join_beta_updates": "Participar das Atualizações Beta",
|
||||||
"settings_general_developer_mode": "Modo Desenvolvedor",
|
"settings_general_developer_mode": "Modo Desenvolvedor",
|
||||||
"settings_general_language": "Idioma",
|
"settings_general_language": "Idioma",
|
||||||
|
"settings_general_loading_animation": "Animação de Carregamento",
|
||||||
|
|
||||||
"settings_about_version": "Versão",
|
"settings_about_version": "Versão",
|
||||||
"settings_about_author": "Autor",
|
"settings_about_author": "Autor",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"settings_general_join_beta_updates": "Получать бета-обновления",
|
"settings_general_join_beta_updates": "Получать бета-обновления",
|
||||||
"settings_general_developer_mode": "Режим разработчика",
|
"settings_general_developer_mode": "Режим разработчика",
|
||||||
"settings_general_language": "Язык",
|
"settings_general_language": "Язык",
|
||||||
|
"settings_general_loading_animation": "Анимация загрузки",
|
||||||
|
|
||||||
"settings_about_version": "Версия",
|
"settings_about_version": "Версия",
|
||||||
"settings_about_author": "Автор",
|
"settings_about_author": "Автор",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"settings_general_join_beta_updates": "Cài đặt cập nhật thử nghiệm (beta)",
|
"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_developer_mode": "Chế độ nhà phát triển",
|
||||||
"settings_general_language": "Ngôn ngữ",
|
"settings_general_language": "Ngôn ngữ",
|
||||||
|
"settings_general_loading_animation": "Hình ảnh tải",
|
||||||
|
|
||||||
"settings_about_version": "Phiên bản",
|
"settings_about_version": "Phiên bản",
|
||||||
"settings_about_author": "Tác giả",
|
"settings_about_author": "Tác giả",
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"settings_general_join_beta_updates": "加入 Beta 更新",
|
"settings_general_join_beta_updates": "加入 Beta 更新",
|
||||||
"settings_general_developer_mode": "开发者模式",
|
"settings_general_developer_mode": "开发者模式",
|
||||||
"settings_general_language": "语言",
|
"settings_general_language": "语言",
|
||||||
|
"settings_general_loading_animation": "加载动画",
|
||||||
|
|
||||||
"settings_app_search_paths_title": "额外应用搜索路径",
|
"settings_app_search_paths_title": "额外应用搜索路径",
|
||||||
"settings_app_search_paths_add_app_search_path": "添加应用搜索路径",
|
"settings_app_search_paths_add_app_search_path": "添加应用搜索路径",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kksh/desktop",
|
"name": "@kksh/desktop",
|
||||||
"version": "0.1.36",
|
"version": "0.1.37-beta.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -30,6 +30,7 @@
|
|||||||
"@tauri-apps/plugin-shell": "^2.2.0",
|
"@tauri-apps/plugin-shell": "^2.2.0",
|
||||||
"@tauri-apps/plugin-sql": "^2.2.0",
|
"@tauri-apps/plugin-sql": "^2.2.0",
|
||||||
"@tauri-apps/plugin-stronghold": "^2.2.0",
|
"@tauri-apps/plugin-stronghold": "^2.2.0",
|
||||||
|
"@tauri-store/svelte": "^2.1.1",
|
||||||
"dompurify": "^3.2.4",
|
"dompurify": "^3.2.4",
|
||||||
"drizzle-orm": "^0.40.1",
|
"drizzle-orm": "^0.40.1",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
|
@ -72,4 +72,4 @@ tauri-plugin-cli = "2"
|
|||||||
tauri-plugin-global-shortcut = "2.0.1"
|
tauri-plugin-global-shortcut = "2.0.1"
|
||||||
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
|
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
|
||||||
tauri-plugin-updater = "2.0.2"
|
tauri-plugin-updater = "2.0.2"
|
||||||
tauri-plugin-svelte = "1.2.1"
|
tauri-plugin-svelte = "2.1.1"
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"core:event:default",
|
"core:event:default",
|
||||||
"core:window:default",
|
"core:window:default",
|
||||||
"core:window:allow-set-size",
|
"core:window:allow-set-size",
|
||||||
|
"core:window:allow-set-enabled",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-set-focus",
|
"core:window:allow-set-focus",
|
||||||
"core:window:allow-toggle-maximize",
|
"core:window:allow-toggle-maximize",
|
||||||
|
@ -27,7 +27,7 @@ use utils::server::tauri_file_server;
|
|||||||
pub fn run() {
|
pub fn run() {
|
||||||
let context = tauri::generate_context!();
|
let context = tauri::generate_context!();
|
||||||
let mut builder = tauri::Builder::default();
|
let mut builder = tauri::Builder::default();
|
||||||
|
// let app_data_path = tauri::path::PathResolver::app_data_dir().unwrap();
|
||||||
// let db_key = if cfg!(debug_assertions) {
|
// let db_key = if cfg!(debug_assertions) {
|
||||||
// None
|
// None
|
||||||
// } else {
|
// } else {
|
||||||
|
@ -428,7 +428,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
visible: false
|
visible: false
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.show()
|
window.show().then(() => window.setFocus())
|
||||||
}, 2_000)
|
}, 2_000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -7,10 +7,11 @@ import { decideKkrpcSerialization } from "@/utils/kkrpc"
|
|||||||
import { sleep } from "@/utils/time"
|
import { sleep } from "@/utils/time"
|
||||||
import { trimSlash } from "@/utils/url"
|
import { trimSlash } from "@/utils/url"
|
||||||
import { constructExtensionSupportDir } from "@kksh/api"
|
import { constructExtensionSupportDir } from "@kksh/api"
|
||||||
import { db, spawnExtensionFileServer } from "@kksh/api/commands"
|
import { spawnExtensionFileServer } from "@kksh/api/commands"
|
||||||
import type { HeadlessCommand } from "@kksh/api/headless"
|
import type { HeadlessCommand } from "@kksh/api/headless"
|
||||||
import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
||||||
import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui"
|
import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension"
|
import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||||
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
import { convertFileSrc } from "@tauri-apps/api/core"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { appState } from "@/stores"
|
import { appConfig, appState } from "@/stores"
|
||||||
import { cn } from "@/utils"
|
import { cn } from "@/utils"
|
||||||
import { Button } from "@kksh/svelte5"
|
import { Button } from "@kksh/svelte5"
|
||||||
import { BorderBeam, Constants, Layouts, TauriLink } from "@kksh/ui"
|
import { BorderBeam, Constants, Layouts, TauriLink } from "@kksh/ui"
|
||||||
@ -25,8 +25,13 @@
|
|||||||
>
|
>
|
||||||
<ArrowLeftIcon class="size-4" />
|
<ArrowLeftIcon class="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
{#if $appConfig.loadingAnimation === "kunkun-dancing"}
|
||||||
|
<!-- <DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
|
||||||
<Dance class="absolute z-50 h-screen opacity-20" />
|
<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" />
|
<LoaderCircleIcon class="h-24 w-24 animate-spin" />
|
||||||
<span class="font-mono">Loading</span>
|
<span class="font-mono">Loading</span>
|
||||||
|
{/if}
|
||||||
<BorderBeam size={150} duration={12} />
|
<BorderBeam size={150} duration={12} />
|
||||||
</Layouts.Center>
|
</Layouts.Center>
|
||||||
|
@ -11,20 +11,24 @@
|
|||||||
} from "@/paraglide/runtime"
|
} from "@/paraglide/runtime"
|
||||||
import { appConfig } from "@/stores"
|
import { appConfig } from "@/stores"
|
||||||
import { Select, Switch } from "@kksh/svelte5"
|
import { Select, Switch } from "@kksh/svelte5"
|
||||||
|
import type { LoadingAnimation } from "@kksh/types"
|
||||||
import * as autoStart from "@tauri-apps/plugin-autostart"
|
import * as autoStart from "@tauri-apps/plugin-autostart"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { toast } from "svelte-sonner"
|
import { toast } from "svelte-sonner"
|
||||||
|
|
||||||
const languages = availableLanguageTags.map((lang) => ({
|
const languages = availableLanguageTags.map((lang) => ({
|
||||||
value: 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 launchAtLogin = $state(false)
|
||||||
let language = $state(languageTag())
|
let language = $state(languageTag())
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
autoStart.isEnabled().then((enabled) => {
|
autoStart.isEnabled().then((enabled) => {
|
||||||
launchAtLogin = enabled
|
launchAtLogin = enabled
|
||||||
})
|
})
|
||||||
|
loadingAnimation = $appConfig.loadingAnimation
|
||||||
})
|
})
|
||||||
const triggerContent = $derived(languages.find((f) => f.value === language)?.label ?? "Language")
|
const triggerContent = $derived(languages.find((f) => f.value === language)?.label ?? "Language")
|
||||||
</script>
|
</script>
|
||||||
@ -101,6 +105,31 @@
|
|||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
</li>
|
</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>
|
</ul>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { getExtensionsFolder } from "@/constants"
|
import { getExtensionsFolder } from "@/constants"
|
||||||
import type { SearchPath } from "@kksh/api/models"
|
import type { SearchPath } from "@kksh/api/models"
|
||||||
import { updateTheme, type ThemeConfig } from "@kksh/svelte5"
|
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 { debug, error, info } from "@tauri-apps/plugin-log"
|
||||||
import * as os from "@tauri-apps/plugin-os"
|
import * as os from "@tauri-apps/plugin-os"
|
||||||
import { load } from "@tauri-apps/plugin-store"
|
import { load } from "@tauri-apps/plugin-store"
|
||||||
|
import { Store } from "@tauri-store/svelte"
|
||||||
import { toast } from "svelte-sonner"
|
import { toast } from "svelte-sonner"
|
||||||
import { get, writable } from "svelte/store"
|
import { get, writable } from "svelte/store"
|
||||||
import { Store } from "tauri-plugin-svelte"
|
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
export const defaultAppConfig: AppConfigState = {
|
export const defaultAppConfig: AppConfigState = {
|
||||||
@ -29,7 +29,8 @@ export const defaultAppConfig: AppConfigState = {
|
|||||||
joinBetaProgram: false,
|
joinBetaProgram: false,
|
||||||
onBoarded: false,
|
onBoarded: false,
|
||||||
developerMode: false,
|
developerMode: false,
|
||||||
appSearchPaths: []
|
appSearchPaths: [],
|
||||||
|
loadingAnimation: "kunkun-dancing"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appConfigLoaded = writable(false)
|
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)
|
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 = createAppConfig()
|
||||||
export const appConfig = new AppConfigStore()
|
export const appConfig = new AppConfigStore()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { db } from "@kksh/api/commands"
|
|
||||||
import type { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
import type { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import * as extAPI from "@kksh/extension"
|
import * as extAPI from "@kksh/extension"
|
||||||
import * as path from "@tauri-apps/api/path"
|
import * as path from "@tauri-apps/api/path"
|
||||||
import Fuse from "fuse.js"
|
import Fuse from "fuse.js"
|
||||||
|
@ -48,8 +48,7 @@ export async function registerAppHotkey(hotkeyStr: string) {
|
|||||||
mainWin.setFocus()
|
mainWin.setFocus()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mainWin.show()
|
mainWin.show().then(() => mainWin.setFocus())
|
||||||
mainWin.setFocus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -97,7 +97,7 @@ export async function globalKeyDownHandler(e: KeyboardEvent) {
|
|||||||
await appWin.hide()
|
await appWin.hide()
|
||||||
location.reload()
|
location.reload()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
appWin.show()
|
appWin.show().then(() => appWin.setFocus())
|
||||||
}, 1_000)
|
}, 1_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<svelte:window on:keydown={handleKeyDown} />
|
<svelte:window on:keydown={handleKeyDown} />
|
||||||
|
|
||||||
|
<div class="fixed h-12 w-full" data-tauri-drag-region></div>
|
||||||
<Layouts.Center class="min-h-screen py-5">
|
<Layouts.Center class="min-h-screen py-5">
|
||||||
<Error.RawErrorJSONPreset
|
<Error.RawErrorJSONPreset
|
||||||
title="Error"
|
title="Error"
|
||||||
|
@ -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
|
// Tauri doesn't have a Node.js server to do proper SSR
|
||||||
// so we will use adapter-static to prerender the app (SSG)
|
// so we will use adapter-static to prerender the app (SSG)
|
||||||
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
||||||
export const prerender = true
|
export const prerender = true
|
||||||
export const ssr = false
|
export const ssr = false
|
||||||
|
|
||||||
|
export const load = () => {
|
||||||
|
if (browser) {
|
||||||
|
const win = getCurrentWebviewWindow()
|
||||||
|
return { win }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -46,7 +46,8 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let { children } = $props()
|
let { children, data } = $props()
|
||||||
|
|
||||||
const unlisteners: UnlistenFn[] = []
|
const unlisteners: UnlistenFn[] = []
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
unlisteners.forEach((unlistener) => unlistener())
|
unlisteners.forEach((unlistener) => unlistener())
|
||||||
@ -100,7 +101,7 @@
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
getCurrentWebviewWindow().show()
|
data.win?.show().then(() => data.win?.setFocus())
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
|
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
|
||||||
|
import * as path from "@tauri-apps/api/path"
|
||||||
import { error } from "@tauri-apps/plugin-log"
|
import { error } from "@tauri-apps/plugin-log"
|
||||||
|
import { setStoreCollectionPath } from "@tauri-store/svelte"
|
||||||
import type { LayoutLoad } from "./$types"
|
import type { LayoutLoad } from "./$types"
|
||||||
|
|
||||||
export const load: LayoutLoad = async () => {
|
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 }
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
import { systemCommands, systemCommandsFiltered } from "@/cmds/system"
|
import { systemCommands, systemCommandsFiltered } from "@/cmds/system"
|
||||||
import AppsCmds from "@/components/main/AppsCmds.svelte"
|
import AppsCmds from "@/components/main/AppsCmds.svelte"
|
||||||
import { i18n } from "@/i18n"
|
import { i18n } from "@/i18n"
|
||||||
import { getUniqueExtensionByIdentifier } from "@/orm/cmds"
|
|
||||||
import { db } from "@/orm/database"
|
|
||||||
import * as m from "@/paraglide/messages"
|
import * as m from "@/paraglide/messages"
|
||||||
import {
|
import {
|
||||||
appConfig,
|
appConfig,
|
||||||
@ -68,12 +66,16 @@
|
|||||||
if (splashscreenWin) {
|
if (splashscreenWin) {
|
||||||
splashscreenWin.close()
|
splashscreenWin.close()
|
||||||
}
|
}
|
||||||
win.show()
|
win.show().then(() => win.setFocus())
|
||||||
})
|
})
|
||||||
win.onFocusChanged(({ payload: focused }) => {
|
win.onFocusChanged(({ payload: focused }) => {
|
||||||
if (focused) {
|
if (focused) {
|
||||||
win.show()
|
win
|
||||||
|
.show()
|
||||||
|
.then(() => win.setFocus())
|
||||||
|
.finally(() => {
|
||||||
inputEle?.focus()
|
inputEle?.focus()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
inputEle?.focus()
|
inputEle?.focus()
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
import { goHome } from "@/utils/route"
|
import { goHome } from "@/utils/route"
|
||||||
import { listenToNewClipboardItem, listenToWindowFocus } from "@/utils/tauri-events"
|
import { listenToNewClipboardItem, listenToWindowFocus } from "@/utils/tauri-events"
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
import { ClipboardContentType, db } from "@kksh/api/commands"
|
import { ClipboardContentType } from "@kksh/api/commands"
|
||||||
import { SearchModeEnum, SQLSortOrderEnum, type ExtData } from "@kksh/api/models"
|
import { SearchModeEnum, SQLSortOrderEnum, type ExtData } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { Button, Command, Resizable } from "@kksh/svelte5"
|
import { Button, Command, Resizable } from "@kksh/svelte5"
|
||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
import { CustomCommandInput, GlobalCommandPaletteFooter } from "@kksh/ui/main"
|
import { CustomCommandInput, GlobalCommandPaletteFooter } from "@kksh/ui/main"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { cn } from "@/utils"
|
import { cn } from "@/utils"
|
||||||
import { db } from "@kksh/api/commands"
|
|
||||||
import type { ExtData } from "@kksh/api/models"
|
import type { ExtData } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { Resizable, Separator } from "@kksh/svelte5"
|
import { Resizable, Separator } from "@kksh/svelte5"
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
import { convertFileSrc } from "@tauri-apps/api/core"
|
||||||
import DOMPurify from "dompurify"
|
import DOMPurify from "dompurify"
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
import * as userInput from "tauri-plugin-user-input-api"
|
import * as userInput from "tauri-plugin-user-input-api"
|
||||||
import { type InputEvent } from "tauri-plugin-user-input-api"
|
import { type InputEvent } from "tauri-plugin-user-input-api"
|
||||||
|
|
||||||
|
let { data } = $props()
|
||||||
|
|
||||||
const SymbolMap = {
|
const SymbolMap = {
|
||||||
Alt: "⎇",
|
Alt: "⎇",
|
||||||
AltGr: "⌥",
|
AltGr: "⌥",
|
||||||
@ -97,10 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const win = getCurrentWebviewWindow()
|
data.win?.show().then(() => data.win?.setFocus())
|
||||||
if (win) {
|
|
||||||
win.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
userInput.setEventTypes([userInput.EventTypeEnum.KeyPress, userInput.EventTypeEnum.KeyRelease])
|
userInput.setEventTypes([userInput.EventTypeEnum.KeyPress, userInput.EventTypeEnum.KeyRelease])
|
||||||
userInput.startListening((evt: InputEvent) => {
|
userInput.startListening((evt: InputEvent) => {
|
||||||
|
@ -8,23 +8,24 @@
|
|||||||
import * as clipboard from "tauri-plugin-clipboard-api"
|
import * as clipboard from "tauri-plugin-clipboard-api"
|
||||||
|
|
||||||
let image = $state<string | null>(null)
|
let image = $state<string | null>(null)
|
||||||
const appWin = getCurrentWebviewWindow()
|
let { data } = $props()
|
||||||
let originalSize = $state<{ width: number; height: number } | null>(null)
|
let originalSize = $state<{ width: number; height: number } | null>(null)
|
||||||
let originalScaleFactor = $state<number | null>(null)
|
let originalScaleFactor = $state<number | null>(null)
|
||||||
let scale = $state<number>(1)
|
let scale = $state<number>(1)
|
||||||
let currentSize = $derived(
|
let currentSize = $derived(
|
||||||
originalSize ? { width: originalSize.width * scale, height: originalSize.height * scale } : null
|
originalSize ? { width: originalSize.width * scale, height: originalSize.height * scale } : null
|
||||||
)
|
)
|
||||||
|
const win = getCurrentWebviewWindow()
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (currentSize) {
|
if (currentSize) {
|
||||||
appWin.setSize(new LogicalSize(currentSize.width, currentSize.height))
|
win.setSize(new LogicalSize(currentSize.width, currentSize.height))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getWindowSize() {
|
async function getWindowSize() {
|
||||||
const size = await appWin.outerSize()
|
const size = await win.outerSize()
|
||||||
const scaleFactor = originalScaleFactor ?? (await appWin.scaleFactor())
|
const scaleFactor = originalScaleFactor ?? (await win.scaleFactor())
|
||||||
const logicalSize = size.toLogical(scaleFactor)
|
const logicalSize = size.toLogical(scaleFactor)
|
||||||
return { logicalSize, scaleFactor }
|
return { logicalSize, scaleFactor }
|
||||||
}
|
}
|
||||||
@ -36,7 +37,7 @@
|
|||||||
image = b64
|
image = b64
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
appWin.show()
|
data.win?.show().then(() => data.win?.setFocus())
|
||||||
})
|
})
|
||||||
const { logicalSize, scaleFactor } = await getWindowSize()
|
const { logicalSize, scaleFactor } = await getWindowSize()
|
||||||
originalSize = { width: logicalSize.width, height: logicalSize.height }
|
originalSize = { width: logicalSize.width, height: logicalSize.height }
|
||||||
@ -67,13 +68,13 @@
|
|||||||
<svelte:window
|
<svelte:window
|
||||||
on:keydown={(e) => {
|
on:keydown={(e) => {
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
appWin.close()
|
win.close()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => appWin.close()}
|
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => win.close()}>
|
||||||
><CircleX /></Button
|
<CircleX />
|
||||||
>
|
</Button>
|
||||||
<main class="z-50 h-screen w-screen overflow-hidden" data-tauri-drag-region>
|
<main class="z-50 h-screen w-screen overflow-hidden" data-tauri-drag-region>
|
||||||
{#if image}
|
{#if image}
|
||||||
<img
|
<img
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import DanceTransition from "@/components/dance/dance-transition.svelte"
|
import DanceTransition from "@/components/dance/dance-transition.svelte"
|
||||||
import { i18n } from "@/i18n"
|
import { i18n } from "@/i18n"
|
||||||
import { appConfig, winExtMap } from "@/stores"
|
import { appConfig, appState, winExtMap } from "@/stores"
|
||||||
import { helperAPI } from "@/utils/helper"
|
import { helperAPI } from "@/utils/helper"
|
||||||
import { paste } from "@/utils/hotkey"
|
import { paste } from "@/utils/hotkey"
|
||||||
import { goBackOnEscape } from "@/utils/key"
|
import { goBackOnEscape } from "@/utils/key"
|
||||||
@ -10,7 +10,6 @@
|
|||||||
import { positionToCssStyleString, positionToTailwindClasses } from "@/utils/style"
|
import { positionToCssStyleString, positionToTailwindClasses } from "@/utils/style"
|
||||||
import { sleep } from "@/utils/time"
|
import { sleep } from "@/utils/time"
|
||||||
import { isInMainWindow } from "@/utils/window"
|
import { isInMainWindow } from "@/utils/window"
|
||||||
import { db } from "@kksh/api/commands"
|
|
||||||
import { CustomPosition, ThemeColor, type Position } from "@kksh/api/models"
|
import { CustomPosition, ThemeColor, type Position } from "@kksh/api/models"
|
||||||
import {
|
import {
|
||||||
constructJarvisServerAPIWithPermissions,
|
constructJarvisServerAPIWithPermissions,
|
||||||
@ -19,6 +18,7 @@
|
|||||||
type IUiCustom
|
type IUiCustom
|
||||||
} from "@kksh/api/ui"
|
} from "@kksh/api/ui"
|
||||||
import { toast, type IUiCustomServer1, type IUiCustomServer2 } from "@kksh/api/ui/custom"
|
import { toast, type IUiCustomServer1, type IUiCustomServer2 } from "@kksh/api/ui/custom"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { Button } from "@kksh/svelte5"
|
import { Button } from "@kksh/svelte5"
|
||||||
import { cn } from "@kksh/ui/utils"
|
import { cn } from "@kksh/ui/utils"
|
||||||
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
||||||
@ -38,7 +38,6 @@
|
|||||||
let { data }: { data: PageData } = $props()
|
let { data }: { data: PageData } = $props()
|
||||||
const { loadedExt, url, extPath, extInfoInDB } = data
|
const { loadedExt, url, extPath, extInfoInDB } = data
|
||||||
let extSpawnedProcesses = $state<number[]>([])
|
let extSpawnedProcesses = $state<number[]>([])
|
||||||
const appWin = getCurrentWindow()
|
|
||||||
let iframeRef: HTMLIFrameElement
|
let iframeRef: HTMLIFrameElement
|
||||||
let uiControl = $state<{
|
let uiControl = $state<{
|
||||||
iframeLoaded: boolean
|
iframeLoaded: boolean
|
||||||
@ -65,7 +64,7 @@
|
|||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goto(i18n.resolveRoute("/app/"))
|
goto(i18n.resolveRoute("/app/"))
|
||||||
} else {
|
} else {
|
||||||
appWin.close()
|
data.win?.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideBackButton: async () => {
|
hideBackButton: async () => {
|
||||||
@ -131,7 +130,7 @@
|
|||||||
},
|
},
|
||||||
getSpawnedProcesses: () => Promise.resolve(extSpawnedProcesses),
|
getSpawnedProcesses: () => Promise.resolve(extSpawnedProcesses),
|
||||||
paste: async () => {
|
paste: async () => {
|
||||||
await appWin.hide()
|
await data.win?.hide()
|
||||||
await sleep(200)
|
await sleep(200)
|
||||||
return paste()
|
return paste()
|
||||||
}
|
}
|
||||||
@ -155,7 +154,7 @@
|
|||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goHome()
|
goHome()
|
||||||
} else {
|
} else {
|
||||||
appWin.close()
|
data.win?.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,12 +162,14 @@
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
iframeRef.focus()
|
iframeRef.focus()
|
||||||
uiControl.iframeLoaded = true
|
uiControl.iframeLoaded = true
|
||||||
|
appState.setFullScreenLoading(false)
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
appState.setFullScreenLoading(true)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
appWin.show()
|
data.win?.setFocus()
|
||||||
}, 200)
|
}, 200)
|
||||||
if (iframeRef?.contentWindow) {
|
if (iframeRef?.contentWindow) {
|
||||||
const io = new IframeParentIO(iframeRef.contentWindow)
|
const io = new IframeParentIO(iframeRef.contentWindow)
|
||||||
@ -194,7 +195,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
winExtMap.unregisterExtensionFromWindow(appWin.label)
|
winExtMap.unregisterExtensionFromWindow(data.win?.label ?? "")
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -207,7 +208,7 @@
|
|||||||
onclick={onBackBtnClicked}
|
onclick={onBackBtnClicked}
|
||||||
style={`${positionToCssStyleString(uiControl.backBtnPosition)}`}
|
style={`${positionToCssStyleString(uiControl.backBtnPosition)}`}
|
||||||
>
|
>
|
||||||
{#if appWin.label === "main"}
|
{#if data.win?.label === "main"}
|
||||||
<ArrowLeftIcon class="w-4" />
|
<ArrowLeftIcon class="w-4" />
|
||||||
{:else}
|
{:else}
|
||||||
<XIcon class="w-4" />
|
<XIcon class="w-4" />
|
||||||
@ -238,7 +239,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<main class="h-screen">
|
<main class="h-screen">
|
||||||
<DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} />
|
|
||||||
<iframe
|
<iframe
|
||||||
bind:this={iframeRef}
|
bind:this={iframeRef}
|
||||||
class={cn("h-full", {
|
class={cn("h-full", {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { KunkunIframeExtParams } from "@/cmds/ext"
|
import { KunkunIframeExtParams } from "@/cmds/ext"
|
||||||
import { i18n } from "@/i18n"
|
import { i18n } from "@/i18n"
|
||||||
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
|
|
||||||
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
|
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||||
import { error as svError } from "@sveltejs/kit"
|
import { error as svError } from "@sveltejs/kit"
|
||||||
import { join } from "@tauri-apps/api/path"
|
import { join } from "@tauri-apps/api/path"
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
} from "@/utils/tauri-events.js"
|
} from "@/utils/tauri-events.js"
|
||||||
import { sleep } from "@/utils/time.js"
|
import { sleep } from "@/utils/time.js"
|
||||||
import { isInMainWindow } from "@/utils/window.js"
|
import { isInMainWindow } from "@/utils/window.js"
|
||||||
import { db } from "@kksh/api/commands"
|
|
||||||
import {
|
import {
|
||||||
constructJarvisServerAPIWithPermissions,
|
constructJarvisServerAPIWithPermissions,
|
||||||
type IApp,
|
type IApp,
|
||||||
@ -29,6 +28,7 @@
|
|||||||
type IComponent,
|
type IComponent,
|
||||||
type TemplateUiCommand
|
type TemplateUiCommand
|
||||||
} from "@kksh/api/ui/template"
|
} from "@kksh/api/ui/template"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { Button, Form } from "@kksh/svelte5"
|
import { Button, Form } from "@kksh/svelte5"
|
||||||
import { LoadingBar } from "@kksh/ui"
|
import { LoadingBar } from "@kksh/ui"
|
||||||
import { Templates } from "@kksh/ui/extension"
|
import { Templates } from "@kksh/ui/extension"
|
||||||
@ -310,7 +310,7 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
appState.setLoadingBar(true)
|
appState.setLoadingBar(true)
|
||||||
appWin.show()
|
appWin.show().then(() => appWin.setFocus())
|
||||||
}, 100)
|
}, 100)
|
||||||
unlistenRefreshWorkerExt = await listenToRefreshDevExt(() => {
|
unlistenRefreshWorkerExt = await listenToRefreshDevExt(() => {
|
||||||
debug("Refreshing Worker Extension")
|
debug("Refreshing Worker Extension")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { KunkunTemplateExtParams } from "@/cmds/ext"
|
import { KunkunTemplateExtParams } from "@/cmds/ext"
|
||||||
import { i18n } from "@/i18n"
|
import { i18n } from "@/i18n"
|
||||||
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
|
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
|
import { db } from "@kksh/drizzle"
|
||||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||||
import { error as sbError, error as svError } from "@sveltejs/kit"
|
import { error as sbError, error as svError } from "@sveltejs/kit"
|
||||||
import { join } from "@tauri-apps/api/path"
|
import { join } from "@tauri-apps/api/path"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as m from "@/paraglide/messages"
|
import * as m from "@/paraglide/messages"
|
||||||
import { db } from "@kksh/api/commands"
|
import { db } from "@kksh/drizzle"
|
||||||
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
import { loadExtensionManifestFromDisk } from "@kksh/extension"
|
||||||
import { Button, Dialog, Table } from "@kksh/svelte5"
|
import { Button, Dialog, Table } from "@kksh/svelte5"
|
||||||
import { join } from "@tauri-apps/api/path"
|
import { join } from "@tauri-apps/api/path"
|
||||||
|
@ -7,13 +7,12 @@
|
|||||||
getUniqueExtensionByPath,
|
getUniqueExtensionByPath,
|
||||||
searchExtensionData,
|
searchExtensionData,
|
||||||
updateCmdByID
|
updateCmdByID
|
||||||
} from "@/orm/cmds"
|
} from "@kksh/drizzle/api"
|
||||||
import * as schema from "@kksh/drizzle/schema"
|
import * as schema from "@kksh/drizzle/schema"
|
||||||
import { Button, Input } from "@kksh/svelte5"
|
import { Button, Input } from "@kksh/svelte5"
|
||||||
import { CmdTypeEnum, Ext } from "@kunkunapi/src/models/extension"
|
import { CmdTypeEnum, Ext } from "@kunkunapi/src/models/extension"
|
||||||
import { SearchModeEnum, SQLSortOrderEnum } from "@kunkunapi/src/models/sql"
|
import { SearchModeEnum, SQLSortOrderEnum } from "@kunkunapi/src/models/sql"
|
||||||
import { db } from "$lib/orm/database"
|
// import * as orm from "drizzle-orm"
|
||||||
import * as orm from "drizzle-orm"
|
|
||||||
import { Inspect } from "svelte-inspect-value"
|
import { Inspect } from "svelte-inspect-value"
|
||||||
import { toast } from "svelte-sonner"
|
import { toast } from "svelte-sonner"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Layouts } from "@kksh/ui"
|
import { Layouts } from "@kksh/ui"
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
onMount(async () => {
|
let { data } = $props()
|
||||||
const mainWin = await getCurrentWindow()
|
|
||||||
mainWin.show()
|
onMount(() => {
|
||||||
|
data.win?.show().then(() => data.win?.setFocus())
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"verify-package-export": "^0.0.3"
|
"verify-package-export": "^0.0.3"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.2",
|
"packageManager": "pnpm@10.7.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
|
@ -20,9 +20,10 @@ import type {
|
|||||||
IPath as ITauriPath
|
IPath as ITauriPath
|
||||||
} from "tauri-api-adapter"
|
} from "tauri-api-adapter"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import { KV, type JarvisExtDB } from "../commands/db"
|
|
||||||
import type { fileSearch } from "../commands/fileSearch"
|
import type { fileSearch } from "../commands/fileSearch"
|
||||||
import { type AppInfo } from "../models/apps"
|
import { type AppInfo } from "../models/apps"
|
||||||
|
import { type ExtData } from "../models/extension"
|
||||||
|
import { ExtDataField, SearchMode, SQLSortOrder } from "../models/sql"
|
||||||
import type { LightMode, Position, Radius, ThemeColor } from "../models/styles"
|
import type { LightMode, Position, Radius, ThemeColor } from "../models/styles"
|
||||||
import type { DenoSysOptions } from "../permissions/schema"
|
import type { DenoSysOptions } from "../permissions/schema"
|
||||||
|
|
||||||
@ -154,23 +155,34 @@ export interface IUiCustom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IDb {
|
export interface IDb {
|
||||||
add: typeof JarvisExtDB.prototype.add
|
add: (data: { data: string; dataType?: string; searchText?: string }) => Promise<void>
|
||||||
delete: typeof JarvisExtDB.prototype.delete
|
delete: (dataId: number) => Promise<void>
|
||||||
search: typeof JarvisExtDB.prototype.search
|
search: (searchParams: {
|
||||||
retrieveAll: typeof JarvisExtDB.prototype.retrieveAll
|
dataId?: number
|
||||||
retrieveAllByType: typeof JarvisExtDB.prototype.retrieveAllByType
|
searchMode?: SearchMode
|
||||||
deleteAll: typeof JarvisExtDB.prototype.deleteAll
|
dataType?: string
|
||||||
update: typeof JarvisExtDB.prototype.update
|
searchText?: string
|
||||||
|
afterCreatedAt?: Date
|
||||||
|
beforeCreatedAt?: Date
|
||||||
|
limit?: number
|
||||||
|
orderByCreatedAt?: SQLSortOrder
|
||||||
|
orderByUpdatedAt?: SQLSortOrder
|
||||||
|
fields?: ExtDataField[]
|
||||||
|
}) => Promise<ExtData[]>
|
||||||
|
retrieveAll: (options: { fields?: ExtDataField[] }) => Promise<ExtData[]>
|
||||||
|
retrieveAllByType: (dataType: string) => Promise<ExtData[]>
|
||||||
|
deleteAll: () => Promise<void>
|
||||||
|
update: (data: { dataId: number; data: string; searchText?: string }) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A key-value store built on top of the Database API (based on sqlite)
|
* A key-value store built on top of the Database API (based on sqlite)
|
||||||
*/
|
*/
|
||||||
export interface IKV {
|
export interface IKV {
|
||||||
get: typeof KV.prototype.get
|
get: <T = string>(key: string) => Promise<T | null | undefined>
|
||||||
set: typeof KV.prototype.set
|
set: (key: string, value: string) => Promise<void>
|
||||||
exists: typeof KV.prototype.exists
|
exists: (key: string) => Promise<boolean>
|
||||||
delete: typeof KV.prototype.delete
|
delete: (key: string) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFs {
|
export interface IFs {
|
||||||
|
@ -1,465 +0,0 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core"
|
|
||||||
import { array, literal, optional, parse, safeParse, union, type InferOutput } from "valibot"
|
|
||||||
import { KUNKUN_EXT_IDENTIFIER } from "../constants"
|
|
||||||
import { CmdType, Ext, ExtCmd, ExtData } from "../models/extension"
|
|
||||||
import { convertDateToSqliteString, SearchMode, SearchModeEnum, SQLSortOrder } from "../models/sql"
|
|
||||||
import { generateJarvisPluginCommand } from "./common"
|
|
||||||
|
|
||||||
export interface QueryResult {
|
|
||||||
/** The number of rows affected by the query. */
|
|
||||||
rowsAffected: number
|
|
||||||
/**
|
|
||||||
* The last inserted `id`.
|
|
||||||
*
|
|
||||||
* This value is not set for Postgres databases. If the
|
|
||||||
* last inserted id is required on Postgres, the `select` function
|
|
||||||
* must be used, with a `RETURNING` clause
|
|
||||||
* (`INSERT INTO todos (title) VALUES ($1) RETURNING id`).
|
|
||||||
*/
|
|
||||||
lastInsertId?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function select(query: string, values: any[]) {
|
|
||||||
return invoke<any[]>(generateJarvisPluginCommand("select"), {
|
|
||||||
query,
|
|
||||||
values
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function execute(query: string, values: any[]) {
|
|
||||||
return invoke<QueryResult>(generateJarvisPluginCommand("execute"), {
|
|
||||||
query,
|
|
||||||
values
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
export function createExtension(ext: {
|
|
||||||
identifier: string
|
|
||||||
version: string
|
|
||||||
enabled?: boolean
|
|
||||||
path?: string
|
|
||||||
data?: any
|
|
||||||
}) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("create_extension"), ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllExtensions() {
|
|
||||||
return invoke<Ext[]>(generateJarvisPluginCommand("get_all_extensions"))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUniqueExtensionByIdentifier(identifier: string) {
|
|
||||||
return invoke<Ext | undefined>(
|
|
||||||
generateJarvisPluginCommand("get_unique_extension_by_identifier"),
|
|
||||||
{
|
|
||||||
identifier
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUniqueExtensionByPath(path: string) {
|
|
||||||
return invoke<Ext | undefined>(generateJarvisPluginCommand("get_unique_extension_by_path"), {
|
|
||||||
path
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllExtensionsByIdentifier(identifier: string) {
|
|
||||||
return invoke<Ext[]>(generateJarvisPluginCommand("get_all_extensions_by_identifier"), {
|
|
||||||
identifier
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this function when you expect the extension to exist. Such as builtin extensions.
|
|
||||||
* @param identifier
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getExtensionByIdentifierExpectExists(identifier: string): Promise<Ext> {
|
|
||||||
return getUniqueExtensionByIdentifier(identifier).then((ext) => {
|
|
||||||
if (!ext) {
|
|
||||||
throw new Error(`Unexpexted Error: Extension ${identifier} not found`)
|
|
||||||
}
|
|
||||||
return ext
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: clean this up
|
|
||||||
// export function deleteExtensionByIdentifier(identifier: string) {
|
|
||||||
// return invoke<void>(generateJarvisPluginCommand("delete_extension_by_identifier"), { identifier })
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function deleteExtensionByPath(path: string) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_by_path"), {
|
|
||||||
path
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteExtensionByExtId(extId: string) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_by_ext_id"), { extId })
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension Command CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
export function createCommand(data: {
|
|
||||||
extId: number
|
|
||||||
name: string
|
|
||||||
cmdType: CmdType
|
|
||||||
data: string
|
|
||||||
alias?: string
|
|
||||||
hotkey?: string
|
|
||||||
enabled?: boolean
|
|
||||||
}) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("create_command"), {
|
|
||||||
...data,
|
|
||||||
enabled: data.enabled ?? false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCommandById(cmdId: number) {
|
|
||||||
return invoke<ExtCmd | undefined>(generateJarvisPluginCommand("get_command_by_id"), { cmdId })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCommandsByExtId(extId: number) {
|
|
||||||
return invoke<ExtCmd[]>(generateJarvisPluginCommand("get_commands_by_ext_id"), { extId })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteCommandById(cmdId: number) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("delete_command_by_id"), {
|
|
||||||
cmdId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateCommandById(data: {
|
|
||||||
cmdId: number
|
|
||||||
name: string
|
|
||||||
cmdType: CmdType
|
|
||||||
data: string
|
|
||||||
alias?: string
|
|
||||||
hotkey?: string
|
|
||||||
enabled: boolean
|
|
||||||
}) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("update_command_by_id"), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension Data CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
export const ExtDataField = union([literal("data"), literal("search_text")])
|
|
||||||
export type ExtDataField = InferOutput<typeof ExtDataField>
|
|
||||||
|
|
||||||
function convertRawExtDataToExtData(rawData?: {
|
|
||||||
createdAt: string
|
|
||||||
updatedAt: string
|
|
||||||
data: null | string
|
|
||||||
searchText: null | string
|
|
||||||
}): ExtData | undefined {
|
|
||||||
if (!rawData) {
|
|
||||||
return rawData
|
|
||||||
}
|
|
||||||
const parsedRes = safeParse(ExtData, {
|
|
||||||
...rawData,
|
|
||||||
createdAt: new Date(rawData.createdAt),
|
|
||||||
updatedAt: new Date(rawData.updatedAt),
|
|
||||||
data: rawData.data ?? undefined,
|
|
||||||
searchText: rawData.searchText ?? undefined
|
|
||||||
})
|
|
||||||
if (parsedRes.success) {
|
|
||||||
return parsedRes.output
|
|
||||||
} else {
|
|
||||||
console.error("Extension Data Parse Failure", parsedRes.issues)
|
|
||||||
throw new Error("Fail to parse extension data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createExtensionData(data: {
|
|
||||||
extId: number
|
|
||||||
dataType: string
|
|
||||||
data: string
|
|
||||||
searchText?: string
|
|
||||||
}) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("create_extension_data"), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getExtensionDataById(dataId: number, fields?: ExtDataField[]) {
|
|
||||||
return invoke<
|
|
||||||
| (ExtData & {
|
|
||||||
createdAt: string
|
|
||||||
updatedAt: string
|
|
||||||
data: null | string
|
|
||||||
searchText: null | string
|
|
||||||
})
|
|
||||||
| undefined
|
|
||||||
>(generateJarvisPluginCommand("get_extension_data_by_id"), {
|
|
||||||
dataId,
|
|
||||||
fields
|
|
||||||
}).then(convertRawExtDataToExtData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fields option can be used to select optional fields. By default, if left empty, data and searchText are not returned.
|
|
||||||
* This is because data and searchText can be large and we don't want to return them by default.
|
|
||||||
* If you just want to get data ids in order to delete them, retrieving all data is not necessary.
|
|
||||||
* @param searchParams
|
|
||||||
*/
|
|
||||||
export async function searchExtensionData(searchParams: {
|
|
||||||
extId: number
|
|
||||||
searchMode: SearchMode
|
|
||||||
dataId?: number
|
|
||||||
dataType?: string
|
|
||||||
searchText?: string
|
|
||||||
afterCreatedAt?: string
|
|
||||||
beforeCreatedAt?: string
|
|
||||||
limit?: number
|
|
||||||
offset?: number
|
|
||||||
orderByCreatedAt?: SQLSortOrder
|
|
||||||
orderByUpdatedAt?: SQLSortOrder
|
|
||||||
fields?: ExtDataField[]
|
|
||||||
}): Promise<ExtData[]> {
|
|
||||||
const fields = parse(optional(array(ExtDataField), []), searchParams.fields)
|
|
||||||
let items = await invoke<
|
|
||||||
(ExtData & {
|
|
||||||
createdAt: string
|
|
||||||
updatedAt: string
|
|
||||||
data: null | string
|
|
||||||
searchText: null | string
|
|
||||||
})[]
|
|
||||||
>(generateJarvisPluginCommand("search_extension_data"), {
|
|
||||||
searchQuery: {
|
|
||||||
...searchParams,
|
|
||||||
fields
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return items.map(convertRawExtDataToExtData).filter((item) => item) as ExtData[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteExtensionDataById(dataId: number) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("delete_extension_data_by_id"), { dataId })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateExtensionDataById(data: {
|
|
||||||
dataId: number
|
|
||||||
data: string
|
|
||||||
searchText?: string
|
|
||||||
}) {
|
|
||||||
return invoke<void>(generateJarvisPluginCommand("update_extension_data_by_id"), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Built-in Extensions */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
export function getExtClipboard() {
|
|
||||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
|
||||||
}
|
|
||||||
export function getExtQuickLinks() {
|
|
||||||
return getExtensionByIdentifierExpectExists(
|
|
||||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export function getExtRemote() {
|
|
||||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
|
||||||
}
|
|
||||||
export function getExtScriptCmd() {
|
|
||||||
return getExtensionByIdentifierExpectExists(
|
|
||||||
KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export function getExtDev() {
|
|
||||||
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Database API for extensions.
|
|
||||||
* Extensions shouldn't have full access to the database, they can only access their own data.
|
|
||||||
* When an extension is loaded, the main thread will create an instance of this class and
|
|
||||||
* expose it to the extension.
|
|
||||||
*/
|
|
||||||
export class JarvisExtDB {
|
|
||||||
extId: number
|
|
||||||
|
|
||||||
constructor(extId: number) {
|
|
||||||
this.extId = extId
|
|
||||||
}
|
|
||||||
|
|
||||||
async add(data: { data: string; dataType?: string; searchText?: string }) {
|
|
||||||
return createExtensionData({
|
|
||||||
data: data.data,
|
|
||||||
dataType: data.dataType ?? "default",
|
|
||||||
searchText: data.searchText,
|
|
||||||
extId: this.extId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete(dataId: number): Promise<void> {
|
|
||||||
// Verify if this data belongs to this extension
|
|
||||||
const d = await getExtensionDataById(dataId)
|
|
||||||
if (!d || d.extId !== this.extId) {
|
|
||||||
throw new Error("Extension Data not found")
|
|
||||||
}
|
|
||||||
return await deleteExtensionDataById(dataId)
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(searchParams: {
|
|
||||||
dataId?: number
|
|
||||||
searchMode?: SearchMode
|
|
||||||
dataType?: string
|
|
||||||
searchText?: string
|
|
||||||
afterCreatedAt?: Date
|
|
||||||
beforeCreatedAt?: Date
|
|
||||||
limit?: number
|
|
||||||
orderByCreatedAt?: SQLSortOrder
|
|
||||||
orderByUpdatedAt?: SQLSortOrder
|
|
||||||
fields?: ExtDataField[]
|
|
||||||
}): Promise<ExtData[]> {
|
|
||||||
const beforeCreatedAt = searchParams.beforeCreatedAt
|
|
||||||
? convertDateToSqliteString(searchParams.beforeCreatedAt)
|
|
||||||
: undefined
|
|
||||||
const afterCreatedAt = searchParams.afterCreatedAt
|
|
||||||
? convertDateToSqliteString(searchParams.afterCreatedAt)
|
|
||||||
: undefined
|
|
||||||
return searchExtensionData({
|
|
||||||
...searchParams,
|
|
||||||
searchMode: searchParams.searchMode ?? SearchModeEnum.FTS,
|
|
||||||
extId: this.extId,
|
|
||||||
beforeCreatedAt,
|
|
||||||
afterCreatedAt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all data of this extension.
|
|
||||||
* Use `search()` method for more advanced search.
|
|
||||||
* @param options optional fields to retrieve. By default, data and searchText are not returned.
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
retrieveAll(options: { fields?: ExtDataField[] }): Promise<ExtData[]> {
|
|
||||||
return this.search({ fields: options.fields })
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all data of this extension by type.
|
|
||||||
* Use `search()` method for more advanced search.
|
|
||||||
* @param dataType
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
retrieveAllByType(dataType: string): Promise<ExtData[]> {
|
|
||||||
return this.search({ dataType })
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all data of this extension.
|
|
||||||
*/
|
|
||||||
deleteAll(): Promise<void> {
|
|
||||||
return this.search({})
|
|
||||||
.then((items) => {
|
|
||||||
return Promise.all(items.map((item) => this.delete(item.dataId)))
|
|
||||||
})
|
|
||||||
.then(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update data and searchText of this extension.
|
|
||||||
* @param dataId unique id of the data
|
|
||||||
* @param data
|
|
||||||
* @param searchText
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async update(data: { dataId: number; data: string; searchText?: string }): Promise<void> {
|
|
||||||
const d = await getExtensionDataById(data.dataId)
|
|
||||||
if (!d || d.extId !== this.extId) {
|
|
||||||
throw new Error("Extension Data not found")
|
|
||||||
}
|
|
||||||
return updateExtensionDataById(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class KV {
|
|
||||||
extId: number
|
|
||||||
db: JarvisExtDB
|
|
||||||
private DataType: string = "kunkun_kv"
|
|
||||||
|
|
||||||
constructor(extId: number) {
|
|
||||||
this.extId = extId
|
|
||||||
this.db = new JarvisExtDB(extId)
|
|
||||||
}
|
|
||||||
|
|
||||||
get<T = string>(key: string): Promise<T | null | undefined> {
|
|
||||||
return this.db
|
|
||||||
.search({
|
|
||||||
dataType: this.DataType,
|
|
||||||
searchText: key,
|
|
||||||
searchMode: SearchModeEnum.ExactMatch,
|
|
||||||
fields: ["search_text", "data"]
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return null
|
|
||||||
} else if (items.length > 1) {
|
|
||||||
throw new Error("Multiple KVs with the same key")
|
|
||||||
}
|
|
||||||
return items[0].data ? (JSON.parse(items[0].data).value as T) : null
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.warn(err)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: string, value: string): Promise<void> {
|
|
||||||
return this.db
|
|
||||||
.search({
|
|
||||||
dataType: this.DataType,
|
|
||||||
searchText: key,
|
|
||||||
searchMode: SearchModeEnum.ExactMatch
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return this.db.add({
|
|
||||||
data: JSON.stringify({ value: value }),
|
|
||||||
dataType: this.DataType,
|
|
||||||
searchText: key
|
|
||||||
})
|
|
||||||
} else if (items.length === 1) {
|
|
||||||
return this.db.update({
|
|
||||||
dataId: items[0].dataId,
|
|
||||||
data: JSON.stringify({ value: value }),
|
|
||||||
searchText: key
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
|
||||||
Promise.resolve()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(key: string): Promise<void> {
|
|
||||||
return this.db
|
|
||||||
.search({
|
|
||||||
dataType: this.DataType,
|
|
||||||
searchText: key,
|
|
||||||
searchMode: SearchModeEnum.ExactMatch
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
|
||||||
Promise.resolve()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exists(key: string): Promise<boolean> {
|
|
||||||
return this.db
|
|
||||||
.search({
|
|
||||||
dataType: this.DataType,
|
|
||||||
searchText: key,
|
|
||||||
searchMode: SearchModeEnum.ExactMatch,
|
|
||||||
fields: []
|
|
||||||
})
|
|
||||||
.then((items) => {
|
|
||||||
return items.length > 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,8 +5,7 @@ export * from "./tools"
|
|||||||
export * from "./extension"
|
export * from "./extension"
|
||||||
export * from "./system"
|
export * from "./system"
|
||||||
export * from "./store"
|
export * from "./store"
|
||||||
export * as db from "./db"
|
export * as sql from "./sql"
|
||||||
export { JarvisExtDB } from "./db"
|
|
||||||
export * from "./clipboard"
|
export * from "./clipboard"
|
||||||
export * from "./fileSearch"
|
export * from "./fileSearch"
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
|
30
packages/api/src/commands/sql.ts
Normal file
30
packages/api/src/commands/sql.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
|
import { generateJarvisPluginCommand } from "./common"
|
||||||
|
|
||||||
|
export interface QueryResult {
|
||||||
|
/** The number of rows affected by the query. */
|
||||||
|
rowsAffected: number
|
||||||
|
/**
|
||||||
|
* The last inserted `id`.
|
||||||
|
*
|
||||||
|
* This value is not set for Postgres databases. If the
|
||||||
|
* last inserted id is required on Postgres, the `select` function
|
||||||
|
* must be used, with a `RETURNING` clause
|
||||||
|
* (`INSERT INTO todos (title) VALUES ($1) RETURNING id`).
|
||||||
|
*/
|
||||||
|
lastInsertId?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function select(query: string, values: any[]) {
|
||||||
|
return invoke<any[]>(generateJarvisPluginCommand("select"), {
|
||||||
|
query,
|
||||||
|
values
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function execute(query: string, values: any[]) {
|
||||||
|
return invoke<QueryResult>(generateJarvisPluginCommand("execute"), {
|
||||||
|
query,
|
||||||
|
values
|
||||||
|
})
|
||||||
|
}
|
@ -18,10 +18,7 @@ export const Ext = v.object({
|
|||||||
extId: v.number(),
|
extId: v.number(),
|
||||||
identifier: v.string(),
|
identifier: v.string(),
|
||||||
version: v.string(),
|
version: v.string(),
|
||||||
enabled: v.pipe(
|
enabled: v.boolean(),
|
||||||
v.number(),
|
|
||||||
v.transform((input) => Boolean(input))
|
|
||||||
),
|
|
||||||
installedAt: v.string(),
|
installedAt: v.string(),
|
||||||
path: v.optional(v.nullable(v.string())),
|
path: v.optional(v.nullable(v.string())),
|
||||||
data: v.optional(v.any())
|
data: v.optional(v.any())
|
||||||
@ -49,11 +46,9 @@ export const ExtCmd = v.object({
|
|||||||
data: v.string(),
|
data: v.string(),
|
||||||
alias: v.optional(v.nullable(v.string())),
|
alias: v.optional(v.nullable(v.string())),
|
||||||
hotkey: v.optional(v.nullable(v.string())),
|
hotkey: v.optional(v.nullable(v.string())),
|
||||||
enabled: v.pipe(
|
enabled: v.boolean()
|
||||||
v.number(),
|
|
||||||
v.transform((input) => Boolean(input))
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export type ExtCmd = v.InferOutput<typeof ExtCmd>
|
export type ExtCmd = v.InferOutput<typeof ExtCmd>
|
||||||
|
|
||||||
export const QuickLinkCmd = v.object({
|
export const QuickLinkCmd = v.object({
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { enum_, type InferOutput } from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
export enum SQLSortOrderEnum {
|
export enum SQLSortOrderEnum {
|
||||||
Asc = "ASC",
|
Asc = "ASC",
|
||||||
Desc = "DESC"
|
Desc = "DESC"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SQLSortOrder = enum_(SQLSortOrderEnum)
|
export const SQLSortOrder = v.enum_(SQLSortOrderEnum)
|
||||||
export type SQLSortOrder = InferOutput<typeof SQLSortOrder>
|
export type SQLSortOrder = v.InferOutput<typeof SQLSortOrder>
|
||||||
|
|
||||||
export enum SearchModeEnum {
|
export enum SearchModeEnum {
|
||||||
ExactMatch = "exact_match",
|
ExactMatch = "exact_match",
|
||||||
@ -14,8 +14,8 @@ export enum SearchModeEnum {
|
|||||||
FTS = "fts"
|
FTS = "fts"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchMode = enum_(SearchModeEnum)
|
export const SearchMode = v.enum_(SearchModeEnum)
|
||||||
export type SearchMode = InferOutput<typeof SearchMode>
|
export type SearchMode = v.InferOutput<typeof SearchMode>
|
||||||
|
|
||||||
export function convertDateToSqliteString(date: Date) {
|
export function convertDateToSqliteString(date: Date) {
|
||||||
const pad = (num: number) => num.toString().padStart(2, "0")
|
const pad = (num: number) => num.toString().padStart(2, "0")
|
||||||
@ -29,3 +29,6 @@ export function convertDateToSqliteString(date: Date) {
|
|||||||
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ExtDataField = v.union([v.literal("data"), v.literal("search_text")])
|
||||||
|
export type ExtDataField = v.InferOutput<typeof ExtDataField>
|
||||||
|
@ -17,7 +17,8 @@ export const extensions = sqliteTable("extensions", {
|
|||||||
extId: integer("ext_id").primaryKey({ autoIncrement: true }),
|
extId: integer("ext_id").primaryKey({ autoIncrement: true }),
|
||||||
identifier: text().notNull(),
|
identifier: text().notNull(),
|
||||||
version: text().notNull(),
|
version: text().notNull(),
|
||||||
enabled: numeric().default(sql`(TRUE)`),
|
// enabled: numeric().default(sql`(TRUE)`),
|
||||||
|
enabled: integer({ mode: "boolean" }),
|
||||||
path: text(),
|
path: text(),
|
||||||
data: numeric(),
|
data: numeric(),
|
||||||
installedAt: numeric("installed_at").default(sql`(CURRENT_TIMESTAMP)`)
|
installedAt: numeric("installed_at").default(sql`(CURRENT_TIMESTAMP)`)
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * as schema from "./drizzle/schema"
|
export * as schema from "./drizzle/schema"
|
||||||
export * as relations from "./drizzle/relations"
|
export * as relations from "./drizzle/relations"
|
||||||
|
export * as db from "./src/apis"
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.ts",
|
".": "./index.ts",
|
||||||
|
"./api": "./src/apis.ts",
|
||||||
"./schema": "./drizzle/schema.ts",
|
"./schema": "./drizzle/schema.ts",
|
||||||
"./relations": "./drizzle/relations.ts"
|
"./relations": "./drizzle/relations.ts"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as relations from "@kksh/drizzle/relations"
|
import { KUNKUN_EXT_IDENTIFIER, type IDb, type IKV } from "@kksh/api"
|
||||||
import * as schema from "../drizzle/schema"
|
|
||||||
import {
|
import {
|
||||||
CmdType,
|
CmdType,
|
||||||
|
convertDateToSqliteString,
|
||||||
Ext,
|
Ext,
|
||||||
ExtCmd,
|
ExtCmd,
|
||||||
ExtData,
|
ExtData,
|
||||||
@ -10,44 +10,71 @@ import {
|
|||||||
SQLSortOrder,
|
SQLSortOrder,
|
||||||
SQLSortOrderEnum
|
SQLSortOrderEnum
|
||||||
} from "@kksh/api/models"
|
} from "@kksh/api/models"
|
||||||
|
import * as relations from "@kksh/drizzle/relations"
|
||||||
import * as orm from "drizzle-orm"
|
import * as orm from "drizzle-orm"
|
||||||
import type { SelectedFields } from "drizzle-orm/sqlite-core"
|
import type { SelectedFields } from "drizzle-orm/sqlite-core"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
import * as schema from "../drizzle/schema"
|
||||||
import { db } from "./proxy"
|
import { db } from "./proxy"
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Built-in Extensions */
|
/* Built-in Extensions */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
export function getExtClipboard() {
|
export function getExtClipboard(): Promise<Ext> {
|
||||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
||||||
}
|
}
|
||||||
export function getExtQuickLinks() {
|
export function getExtQuickLinks(): Promise<Ext> {
|
||||||
// return getExtensionByIdentifierExpectExists(
|
return getExtensionByIdentifierExpectExists(
|
||||||
// KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
||||||
// )
|
)
|
||||||
}
|
}
|
||||||
export function getExtRemote() {
|
export function getExtRemote(): Promise<Ext> {
|
||||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
||||||
}
|
}
|
||||||
export function getExtScriptCmd() {
|
export function getExtScriptCmd(): Promise<Ext> {
|
||||||
// return getExtensionByIdentifierExpectExists(
|
return getExtensionByIdentifierExpectExists(
|
||||||
// KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
||||||
// )
|
)
|
||||||
}
|
}
|
||||||
export function getExtDev() {
|
export function getExtDev(): Promise<Ext> {
|
||||||
// return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Extension CRUD */
|
/* Extension CRUD */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export function createExtension(ext: {
|
||||||
|
identifier: string
|
||||||
|
version: string
|
||||||
|
enabled?: boolean
|
||||||
|
path?: string
|
||||||
|
data?: any
|
||||||
|
}) {
|
||||||
|
return db
|
||||||
|
.insert(schema.extensions)
|
||||||
|
.values({
|
||||||
|
identifier: ext.identifier,
|
||||||
|
version: ext.version,
|
||||||
|
enabled: ext.enabled,
|
||||||
|
path: ext.path,
|
||||||
|
data: ext.data
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
|
||||||
export async function getUniqueExtensionByIdentifier(identifier: string): Promise<Ext | undefined> {
|
export async function getUniqueExtensionByIdentifier(identifier: string): Promise<Ext | undefined> {
|
||||||
const ext = await db
|
const ext = await db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.extensions)
|
.from(schema.extensions)
|
||||||
.where(orm.eq(schema.extensions.identifier, identifier))
|
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||||
.get()
|
.get()
|
||||||
return v.parse(v.optional(Ext), ext)
|
const result = v.safeParse(v.optional(Ext), ext)
|
||||||
|
if (!result.success) {
|
||||||
|
console.error("Failed to parse extension:", v.flatten(result.issues))
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const parsed = result.output
|
||||||
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,20 +138,29 @@ export function deleteExtensionByExtId(extId: number): Promise<void> {
|
|||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Extension Command CRUD */
|
/* Extension Command CRUD */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export function createCommand(data: {
|
||||||
// export async function getExtensionWithCmdsByIdentifier(identifier: string): Promise<ExtWithCmds> {
|
extId: number
|
||||||
// const ext = await db
|
name: string
|
||||||
// .select({
|
cmdType: CmdType
|
||||||
// ...schema.extensions,
|
data: string
|
||||||
// commands: relations.commandsRelations
|
alias?: string
|
||||||
// })
|
hotkey?: string
|
||||||
// .from(schema.extensions)
|
enabled?: boolean
|
||||||
// .leftJoin(schema.commands, orm.eq(schema.extensions.extId, schema.commands.extId))
|
}) {
|
||||||
// .where(orm.eq(schema.extensions.identifier, identifier))
|
return db
|
||||||
// .get()
|
.insert(schema.commands)
|
||||||
|
.values({
|
||||||
// // return v.parse(v.nullable(ExtWithCmds), ext);
|
extId: data.extId,
|
||||||
// }
|
name: data.name,
|
||||||
|
type: data.cmdType,
|
||||||
|
data: data.data,
|
||||||
|
alias: data.alias,
|
||||||
|
hotkey: data.hotkey,
|
||||||
|
enabled: data.enabled ?? true
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
export async function getCmdById(cmdId: number): Promise<ExtCmd> {
|
export async function getCmdById(cmdId: number): Promise<ExtCmd> {
|
||||||
const cmd = await db
|
const cmd = await db
|
||||||
@ -362,28 +398,219 @@ export async function searchExtensionData(searchParams: {
|
|||||||
.filter((item): item is ExtData => item !== undefined)
|
.filter((item): item is ExtData => item !== undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function deleteExtensionDataById(dataId: number) {
|
export function deleteExtensionDataById(dataId: number) {
|
||||||
|
return db
|
||||||
// return invoke<void>(generateJarvisPluginCommand("delete_extension_data_by_id"), { dataId })
|
.delete(schema.extensionData)
|
||||||
|
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateExtensionDataById(data: {
|
export function updateExtensionDataById(data: {
|
||||||
dataId: number
|
dataId: number
|
||||||
data: string
|
data: string
|
||||||
searchText?: string
|
searchText?: string
|
||||||
}) {
|
}): Promise<void> {
|
||||||
// return invoke<void>(generateJarvisPluginCommand("update_extension_data_by_id"), data)
|
return db
|
||||||
|
.update(schema.extensionData)
|
||||||
|
.set({
|
||||||
|
data: data.data,
|
||||||
|
searchText: data.searchText
|
||||||
|
})
|
||||||
|
.where(orm.eq(schema.extensionData.dataId, data.dataId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database API for extensions.
|
||||||
|
* Extensions shouldn't have full access to the database, they can only access their own data.
|
||||||
|
* When an extension is loaded, the main thread will create an instance of this class and
|
||||||
|
* expose it to the extension.
|
||||||
|
*/
|
||||||
|
export class JarvisExtDB implements IDb {
|
||||||
|
extId: number
|
||||||
|
|
||||||
// export async function getNCommands(n: number):
|
constructor(extId: number) {
|
||||||
// export function createExtension(ext: {
|
this.extId = extId
|
||||||
// identifier: string
|
}
|
||||||
// version: string
|
|
||||||
// enabled?: boolean
|
async add(data: { data: string; dataType?: string; searchText?: string }): Promise<void> {
|
||||||
// path?: string
|
return createExtensionData({
|
||||||
// data?: any
|
data: data.data,
|
||||||
// }) {
|
dataType: data.dataType ?? "default",
|
||||||
// return invoke<void>(generateJarvisPluginCommand("create_extension"), ext)
|
searchText: data.searchText,
|
||||||
// }
|
extId: this.extId
|
||||||
|
}).then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(dataId: number): Promise<void> {
|
||||||
|
// Verify if this data belongs to this extension
|
||||||
|
const d = await getExtensionDataById(dataId)
|
||||||
|
if (!d || d.extId !== this.extId) {
|
||||||
|
throw new Error("Extension Data not found")
|
||||||
|
}
|
||||||
|
return await deleteExtensionDataById(dataId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(searchParams: {
|
||||||
|
dataId?: number
|
||||||
|
searchMode?: SearchMode
|
||||||
|
dataType?: string
|
||||||
|
searchText?: string
|
||||||
|
afterCreatedAt?: Date
|
||||||
|
beforeCreatedAt?: Date
|
||||||
|
limit?: number
|
||||||
|
orderByCreatedAt?: SQLSortOrder
|
||||||
|
orderByUpdatedAt?: SQLSortOrder
|
||||||
|
fields?: ExtDataField[]
|
||||||
|
}): Promise<ExtData[]> {
|
||||||
|
const beforeCreatedAt = searchParams.beforeCreatedAt
|
||||||
|
? convertDateToSqliteString(searchParams.beforeCreatedAt)
|
||||||
|
: undefined
|
||||||
|
const afterCreatedAt = searchParams.afterCreatedAt
|
||||||
|
? convertDateToSqliteString(searchParams.afterCreatedAt)
|
||||||
|
: undefined
|
||||||
|
return searchExtensionData({
|
||||||
|
...searchParams,
|
||||||
|
searchMode: searchParams.searchMode ?? SearchModeEnum.FTS,
|
||||||
|
extId: this.extId,
|
||||||
|
beforeCreatedAt,
|
||||||
|
afterCreatedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all data of this extension.
|
||||||
|
* Use `search()` method for more advanced search.
|
||||||
|
* @param options optional fields to retrieve. By default, data and searchText are not returned.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
retrieveAll(options: { fields?: ExtDataField[] }): Promise<ExtData[]> {
|
||||||
|
return this.search({ fields: options.fields })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all data of this extension by type.
|
||||||
|
* Use `search()` method for more advanced search.
|
||||||
|
* @param dataType
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
retrieveAllByType(dataType: string): Promise<ExtData[]> {
|
||||||
|
return this.search({ dataType })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all data of this extension.
|
||||||
|
*/
|
||||||
|
deleteAll(): Promise<void> {
|
||||||
|
return this.search({})
|
||||||
|
.then((items) => {
|
||||||
|
return Promise.all(items.map((item) => this.delete(item.dataId)))
|
||||||
|
})
|
||||||
|
.then(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update data and searchText of this extension.
|
||||||
|
* @param dataId unique id of the data
|
||||||
|
* @param data
|
||||||
|
* @param searchText
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async update(data: { dataId: number; data: string; searchText?: string }): Promise<void> {
|
||||||
|
const d = await getExtensionDataById(data.dataId)
|
||||||
|
if (!d || d.extId !== this.extId) {
|
||||||
|
throw new Error("Extension Data not found")
|
||||||
|
}
|
||||||
|
return updateExtensionDataById(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KV implements IKV {
|
||||||
|
extId: number
|
||||||
|
db: JarvisExtDB
|
||||||
|
private DataType: string = "kunkun_kv"
|
||||||
|
|
||||||
|
constructor(extId: number) {
|
||||||
|
this.extId = extId
|
||||||
|
this.db = new JarvisExtDB(extId)
|
||||||
|
}
|
||||||
|
|
||||||
|
get<T = string>(key: string): Promise<T | null | undefined> {
|
||||||
|
return this.db
|
||||||
|
.search({
|
||||||
|
dataType: this.DataType,
|
||||||
|
searchText: key,
|
||||||
|
searchMode: SearchModeEnum.ExactMatch,
|
||||||
|
fields: ["search_text", "data"]
|
||||||
|
})
|
||||||
|
.then((items) => {
|
||||||
|
if (items.length === 0) {
|
||||||
|
return null
|
||||||
|
} else if (items.length > 1) {
|
||||||
|
throw new Error("Multiple KVs with the same key")
|
||||||
|
}
|
||||||
|
return items[0]?.data ? (JSON.parse(items[0].data).value as T) : null
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn(err)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: string, value: string): Promise<void> {
|
||||||
|
return this.db
|
||||||
|
.search({
|
||||||
|
dataType: this.DataType,
|
||||||
|
searchText: key,
|
||||||
|
searchMode: SearchModeEnum.ExactMatch
|
||||||
|
})
|
||||||
|
.then((items) => {
|
||||||
|
if (items.length === 0) {
|
||||||
|
return this.db.add({
|
||||||
|
data: JSON.stringify({ value: value }),
|
||||||
|
dataType: this.DataType,
|
||||||
|
searchText: key
|
||||||
|
})
|
||||||
|
} else if (items.length === 1) {
|
||||||
|
return this.db.update({
|
||||||
|
dataId: items[0]!.dataId,
|
||||||
|
data: JSON.stringify({ value: value }),
|
||||||
|
searchText: key
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key: string): Promise<void> {
|
||||||
|
return this.db
|
||||||
|
.search({
|
||||||
|
dataType: this.DataType,
|
||||||
|
searchText: key,
|
||||||
|
searchMode: SearchModeEnum.ExactMatch
|
||||||
|
})
|
||||||
|
.then((items) => {
|
||||||
|
return Promise.all(items.map((item) => this.db.delete(item.dataId))).then(() =>
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(key: string): Promise<boolean> {
|
||||||
|
return this.db
|
||||||
|
.search({
|
||||||
|
dataType: this.DataType,
|
||||||
|
searchText: key,
|
||||||
|
searchMode: SearchModeEnum.ExactMatch,
|
||||||
|
fields: []
|
||||||
|
})
|
||||||
|
.then((items) => {
|
||||||
|
return items.length > 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,36 +1,31 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { db as dbCmd } from "@kksh/api/commands"
|
import { sql } from "@kksh/api/commands"
|
||||||
import * as schema from "../drizzle/schema"
|
|
||||||
import { error } from "@tauri-apps/plugin-log"
|
import { error } from "@tauri-apps/plugin-log"
|
||||||
import { drizzle } from "drizzle-orm/sqlite-proxy"
|
import { drizzle } from "drizzle-orm/sqlite-proxy"
|
||||||
|
import * as schema from "../drizzle/schema"
|
||||||
/**
|
|
||||||
* Loads the sqlite database via the Tauri Proxy.
|
|
||||||
*/
|
|
||||||
// export const sqlite = await Database.load("sqlite:test.db");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The drizzle database instance.
|
* The drizzle database instance.
|
||||||
*/
|
*/
|
||||||
export const db = drizzle<typeof schema>(
|
export const db = drizzle<typeof schema>(
|
||||||
async (sql, params, method) => {
|
async (sqlQuery, params, method) => {
|
||||||
let rows: any = []
|
let rows: any = []
|
||||||
let results = []
|
let results = []
|
||||||
console.log({
|
console.log({
|
||||||
sql,
|
sql: sqlQuery,
|
||||||
params,
|
params,
|
||||||
method
|
method
|
||||||
})
|
})
|
||||||
console.log(sql)
|
console.log(sqlQuery)
|
||||||
// If the query is a SELECT, use the select method
|
// If the query is a SELECT, use the select method
|
||||||
if (isSelectQuery(sql)) {
|
if (isSelectQuery(sqlQuery)) {
|
||||||
rows = await dbCmd.select(sql, params).catch((e) => {
|
rows = await sql.select(sqlQuery, params).catch((e) => {
|
||||||
error("SQL Error:", e)
|
error("SQL Error:", e)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use the execute method
|
// Otherwise, use the execute method
|
||||||
rows = await dbCmd.execute(sql, params).catch((e) => {
|
rows = await sql.execute(sqlQuery, params).catch((e) => {
|
||||||
error("SQL Error:", e)
|
error("SQL Error:", e)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kksh/api": "workspace:*",
|
"@kksh/api": "workspace:*",
|
||||||
|
"@kksh/drizzle": "workspace:*",
|
||||||
"@std/semver": "npm:@jsr/std__semver@^1.0.4",
|
"@std/semver": "npm:@jsr/std__semver@^1.0.4",
|
||||||
"@tauri-apps/plugin-upload": "^2.2.1",
|
"@tauri-apps/plugin-upload": "^2.2.1",
|
||||||
"semver": "^7.7.1",
|
"semver": "^7.7.1",
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
import { db } from "@kksh/api/commands"
|
import { CmdTypeEnum, ExtPackageJson, Icon, QuickLinkCmd } from "@kksh/api/models"
|
||||||
import {
|
import { db } from "@kksh/drizzle"
|
||||||
CmdTypeEnum,
|
|
||||||
ExtCmd,
|
|
||||||
ExtPackageJson,
|
|
||||||
ExtPackageJsonExtra,
|
|
||||||
Icon,
|
|
||||||
QuickLinkCmd
|
|
||||||
} from "@kksh/api/models"
|
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import { isExtPathInDev } from "./utils"
|
|
||||||
|
|
||||||
export async function upsertExtension(extPkgJson: ExtPackageJson, extFullPath: string) {
|
export async function upsertExtension(extPkgJson: ExtPackageJson, extFullPath: string) {
|
||||||
const extInDb = await db.getUniqueExtensionByIdentifier(extPkgJson.kunkun.identifier)
|
const extInDb = await db.getUniqueExtensionByIdentifier(extPkgJson.kunkun.identifier)
|
||||||
@ -39,7 +31,7 @@ export async function createQuickLinkCommand(name: string, link: string, icon: I
|
|||||||
export async function getAllQuickLinkCommands(): Promise<QuickLinkCmd[]> {
|
export async function getAllQuickLinkCommands(): Promise<QuickLinkCmd[]> {
|
||||||
const extension = await db.getExtQuickLinks()
|
const extension = await db.getExtQuickLinks()
|
||||||
const cmds = await db.getCommandsByExtId(extension.extId)
|
const cmds = await db.getCommandsByExtId(extension.extId)
|
||||||
return cmds
|
const parsedCmds = cmds
|
||||||
.map((cmd) => {
|
.map((cmd) => {
|
||||||
try {
|
try {
|
||||||
cmd.data = JSON.parse(cmd.data)
|
cmd.data = JSON.parse(cmd.data)
|
||||||
@ -55,4 +47,5 @@ export async function getAllQuickLinkCommands(): Promise<QuickLinkCmd[]> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter((cmd) => cmd !== null)
|
.filter((cmd) => cmd !== null)
|
||||||
|
return parsedCmds
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
* including install, uninstall, upgrade, check app-extension compatibility, etc.
|
* including install, uninstall, upgrade, check app-extension compatibility, etc.
|
||||||
*/
|
*/
|
||||||
import { isCompatible } from "@kksh/api"
|
import { isCompatible } from "@kksh/api"
|
||||||
import { copy_dir_all, db, decompressTarball } from "@kksh/api/commands"
|
import { copy_dir_all, decompressTarball } from "@kksh/api/commands"
|
||||||
import type { ExtensionStoreListItem, ExtPackageJsonExtra } from "@kksh/api/models"
|
import type { ExtensionStoreListItem, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { greaterThan, parse as parseSemver } from "@std/semver"
|
import { greaterThan, parse as parseSemver } from "@std/semver"
|
||||||
import * as path from "@tauri-apps/api/path"
|
import * as path from "@tauri-apps/api/path"
|
||||||
import * as dialog from "@tauri-apps/plugin-dialog"
|
import * as dialog from "@tauri-apps/plugin-dialog"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { db } from "@kksh/api/commands"
|
|
||||||
import { ExtPackageJson, ExtPackageJsonExtra, License } from "@kksh/api/models"
|
import { ExtPackageJson, ExtPackageJsonExtra, License } from "@kksh/api/models"
|
||||||
|
import { db } from "@kksh/drizzle"
|
||||||
import { basename, dirname, join } from "@tauri-apps/api/path"
|
import { basename, dirname, join } from "@tauri-apps/api/path"
|
||||||
import { readDir, readTextFile } from "@tauri-apps/plugin-fs"
|
import { readDir, readTextFile } from "@tauri-apps/plugin-fs"
|
||||||
import { debug, error } from "@tauri-apps/plugin-log"
|
import { debug, error } from "@tauri-apps/plugin-log"
|
||||||
@ -77,7 +77,7 @@ export function loadAllExtensionsFromDisk(
|
|||||||
* @returns loaded extensions
|
* @returns loaded extensions
|
||||||
*/
|
*/
|
||||||
export async function loadAllExtensionsFromDb(): Promise<ExtPackageJsonExtra[]> {
|
export async function loadAllExtensionsFromDb(): Promise<ExtPackageJsonExtra[]> {
|
||||||
const allDbExts = await (await db.getAllExtensions()).filter((ext) => ext.path)
|
const allDbExts = (await db.getAllExtensions()).filter((ext) => ext.path)
|
||||||
const results: ExtPackageJsonExtra[] = []
|
const results: ExtPackageJsonExtra[] = []
|
||||||
for (const ext of allDbExts) {
|
for (const ext of allDbExts) {
|
||||||
if (!ext.path) continue
|
if (!ext.path) continue
|
||||||
|
@ -81,23 +81,23 @@ const COMMANDS: &[&str] = &[
|
|||||||
// "ext_store_wrapper_save",
|
// "ext_store_wrapper_save",
|
||||||
"get_server_port",
|
"get_server_port",
|
||||||
/* ----------------------------- sqlite database ---------------------------- */
|
/* ----------------------------- sqlite database ---------------------------- */
|
||||||
"create_extension",
|
// "create_extension",
|
||||||
"get_all_extensions",
|
// "get_all_extensions",
|
||||||
"get_unique_extension_by_identifier",
|
// "get_unique_extension_by_identifier",
|
||||||
"get_unique_extension_by_path",
|
// "get_unique_extension_by_path",
|
||||||
"get_all_extensions_by_identifier",
|
// "get_all_extensions_by_identifier",
|
||||||
"delete_extension_by_path",
|
// "delete_extension_by_path",
|
||||||
"delete_extension_by_ext_id",
|
// "delete_extension_by_ext_id",
|
||||||
"create_command",
|
// "create_command",
|
||||||
"get_command_by_id",
|
// "get_command_by_id",
|
||||||
"get_commands_by_ext_id",
|
// "get_commands_by_ext_id",
|
||||||
"delete_command_by_id",
|
// "delete_command_by_id",
|
||||||
"update_command_by_id",
|
// "update_command_by_id",
|
||||||
"create_extension_data",
|
// "create_extension_data",
|
||||||
"get_extension_data_by_id",
|
// "get_extension_data_by_id",
|
||||||
"search_extension_data",
|
// "search_extension_data",
|
||||||
"delete_extension_data_by_id",
|
// "delete_extension_data_by_id",
|
||||||
"update_extension_data_by_id",
|
// "update_extension_data_by_id",
|
||||||
"select",
|
"select",
|
||||||
"execute",
|
"execute",
|
||||||
/* -------------------------------- Clipboard ------------------------------- */
|
/* -------------------------------- Clipboard ------------------------------- */
|
||||||
|
@ -22,231 +22,6 @@ impl DBState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn create_extension(
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
identifier: &str,
|
|
||||||
version: &str,
|
|
||||||
enabled: Option<bool>,
|
|
||||||
path: Option<&str>,
|
|
||||||
data: Option<&str>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.create_extension(identifier, version, enabled.unwrap_or(true), path, data)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_all_extensions(db: State<'_, DBState>) -> Result<Vec<Ext>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_all_extensions()
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_all_extensions_by_identifier(
|
|
||||||
identifier: &str,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<Vec<Ext>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_all_extensions_by_identifier(identifier)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_unique_extension_by_identifier(
|
|
||||||
identifier: &str,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<Option<Ext>, String> {
|
|
||||||
let ext = db
|
|
||||||
.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_unique_extension_by_identifier(identifier)
|
|
||||||
.map_err(|err| err.to_string())?;
|
|
||||||
Ok(ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_unique_extension_by_path(
|
|
||||||
path: &str,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<Option<Ext>, String> {
|
|
||||||
let ext = db
|
|
||||||
.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_unique_extension_by_path(path)
|
|
||||||
.map_err(|err| err.to_string())?;
|
|
||||||
Ok(ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn delete_extension_by_path(path: &str, db: State<'_, DBState>) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.delete_extension_by_path(path)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn delete_extension_by_ext_id(ext_id: i32, db: State<'_, DBState>) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.delete_extension_by_ext_id(ext_id)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension Command CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn create_command(
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
ext_id: i32,
|
|
||||||
name: &str,
|
|
||||||
cmd_type: CmdType,
|
|
||||||
data: &str,
|
|
||||||
enabled: bool,
|
|
||||||
alias: Option<&str>,
|
|
||||||
hotkey: Option<&str>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.create_command(ext_id, name, cmd_type, data, enabled, alias, hotkey)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_command_by_id(db: State<'_, DBState>, cmd_id: i32) -> Result<Option<Cmd>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_command_by_id(cmd_id)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_commands_by_ext_id(
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
ext_id: i32,
|
|
||||||
) -> Result<Vec<Cmd>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_commands_by_ext_id(ext_id)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn delete_command_by_id(db: State<'_, DBState>, cmd_id: i32) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.delete_command_by_id(cmd_id)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn update_command_by_id(
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
cmd_id: i32,
|
|
||||||
name: &str,
|
|
||||||
cmd_type: CmdType,
|
|
||||||
data: &str,
|
|
||||||
enabled: bool,
|
|
||||||
alias: Option<&str>,
|
|
||||||
hotkey: Option<&str>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.update_command_by_id(cmd_id, name, cmd_type, data, enabled, alias, hotkey)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Extension Data CRUD */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn create_extension_data(
|
|
||||||
ext_id: i32,
|
|
||||||
data_type: &str,
|
|
||||||
data: &str,
|
|
||||||
search_text: Option<&str>,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.create_extension_data(ext_id, data_type, data, search_text, None)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_extension_data_by_id(
|
|
||||||
data_id: i32,
|
|
||||||
fields: Option<Vec<ExtDataField>>,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<Option<ExtData>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get_extension_data_by_id(data_id, fields)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn search_extension_data(
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
search_query: ExtDataSearchQuery,
|
|
||||||
) -> Result<Vec<ExtData>, String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.search_extension_data(search_query)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn delete_extension_data_by_id(
|
|
||||||
data_id: i32,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.delete_extension_data_by_id(data_id)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn update_extension_data_by_id(
|
|
||||||
data_id: i32,
|
|
||||||
data: &str,
|
|
||||||
search_text: Option<&str>,
|
|
||||||
db: State<'_, DBState>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
db.db
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.update_extension_data_by_id(data_id, data, search_text)
|
|
||||||
.map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn select(
|
pub async fn select(
|
||||||
db: State<'_, DBState>,
|
db: State<'_, DBState>,
|
||||||
|
@ -142,24 +142,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
/* -------------------------------- database -------------------------------- */
|
/* -------------------------------- database -------------------------------- */
|
||||||
commands::db::select,
|
commands::db::select,
|
||||||
commands::db::execute,
|
commands::db::execute,
|
||||||
commands::db::create_extension,
|
|
||||||
commands::db::create_extension,
|
|
||||||
commands::db::get_all_extensions,
|
|
||||||
commands::db::get_unique_extension_by_identifier,
|
|
||||||
commands::db::get_unique_extension_by_path,
|
|
||||||
commands::db::get_all_extensions_by_identifier,
|
|
||||||
commands::db::delete_extension_by_path,
|
|
||||||
commands::db::delete_extension_by_ext_id,
|
|
||||||
commands::db::create_command,
|
|
||||||
commands::db::get_command_by_id,
|
|
||||||
commands::db::get_commands_by_ext_id,
|
|
||||||
commands::db::delete_command_by_id,
|
|
||||||
commands::db::update_command_by_id,
|
|
||||||
commands::db::create_extension_data,
|
|
||||||
commands::db::get_extension_data_by_id,
|
|
||||||
commands::db::search_extension_data,
|
|
||||||
commands::db::delete_extension_data_by_id,
|
|
||||||
commands::db::update_extension_data_by_id,
|
|
||||||
/* -------------------------------- Clipboard ------------------------------- */
|
/* -------------------------------- Clipboard ------------------------------- */
|
||||||
commands::clipboard::get_history,
|
commands::clipboard::get_history,
|
||||||
commands::clipboard::add_to_history,
|
commands::clipboard::add_to_history,
|
||||||
|
@ -2,6 +2,9 @@ import { LightMode, SearchPath } from "@kksh/api/models"
|
|||||||
import type { Platform } from "@tauri-apps/plugin-os"
|
import type { Platform } from "@tauri-apps/plugin-os"
|
||||||
import * as v from "valibot"
|
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({
|
export const PersistedAppConfig = v.object({
|
||||||
theme: v.object({
|
theme: v.object({
|
||||||
theme: v.string(),
|
theme: v.string(),
|
||||||
@ -18,7 +21,8 @@ export const PersistedAppConfig = v.object({
|
|||||||
joinBetaProgram: v.boolean(),
|
joinBetaProgram: v.boolean(),
|
||||||
onBoarded: v.boolean(),
|
onBoarded: v.boolean(),
|
||||||
developerMode: v.boolean(),
|
developerMode: v.boolean(),
|
||||||
appSearchPaths: v.array(SearchPath)
|
appSearchPaths: v.array(SearchPath),
|
||||||
|
loadingAnimation: LoadingAnimation
|
||||||
})
|
})
|
||||||
|
|
||||||
export type PersistedAppConfig = v.InferOutput<typeof PersistedAppConfig>
|
export type PersistedAppConfig = v.InferOutput<typeof PersistedAppConfig>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import autoAnimate from "@formkit/auto-animate"
|
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
import { Button, ButtonModule, Collapsible, ScrollArea } from "@kksh/svelte5"
|
import { Button, ButtonModule, Collapsible, ScrollArea } from "@kksh/svelte5"
|
||||||
import { Error, Layouts, Shiki } from "@kksh/ui"
|
import { Error, Layouts, Shiki } from "@kksh/ui"
|
||||||
import { ChevronsUpDown } from "lucide-svelte"
|
import { ChevronsUpDown } from "lucide-svelte"
|
||||||
import { type Snippet } from "svelte"
|
import { type Snippet } from "svelte"
|
||||||
import { fade, slide } from "svelte/transition"
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
|
29
pnpm-lock.yaml
generated
29
pnpm-lock.yaml
generated
@ -251,6 +251,9 @@ importers:
|
|||||||
'@tauri-apps/plugin-stronghold':
|
'@tauri-apps/plugin-stronghold':
|
||||||
specifier: ^2.2.0
|
specifier: ^2.2.0
|
||||||
version: 2.2.0
|
version: 2.2.0
|
||||||
|
'@tauri-store/svelte':
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1
|
||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.2.4
|
specifier: ^3.2.4
|
||||||
version: 3.2.4
|
version: 3.2.4
|
||||||
@ -585,6 +588,9 @@ importers:
|
|||||||
'@kksh/api':
|
'@kksh/api':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../api
|
version: link:../api
|
||||||
|
'@kksh/drizzle':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../drizzle
|
||||||
'@std/semver':
|
'@std/semver':
|
||||||
specifier: npm:@jsr/std__semver@^1.0.4
|
specifier: npm:@jsr/std__semver@^1.0.4
|
||||||
version: '@jsr/std__semver@1.0.3'
|
version: '@jsr/std__semver@1.0.3'
|
||||||
@ -5685,6 +5691,12 @@ packages:
|
|||||||
'@tauri-store/shared@0.6.0':
|
'@tauri-store/shared@0.6.0':
|
||||||
resolution: {integrity: sha512-2KBezqqkw68HvvXHEtbbpxyQHDjymBUZl10YuAsNRI8DHFIA0n18WE7NRyQ93+H7IzDP1/B41m2/rcMDHBSiKw==}
|
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':
|
'@trysound/sax@0.2.0':
|
||||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@ -8027,6 +8039,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
|
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-toolkit@1.34.1:
|
||||||
|
resolution: {integrity: sha512-OA6cd94fJV9bm8dWhIySkWq4xV+rAQnBZUr2dnpXam0QJ8c+hurLbKA8/QooL9Mx4WCAxvIDsiEkid5KPQ5xgQ==}
|
||||||
|
|
||||||
esbuild-register@3.6.0:
|
esbuild-register@3.6.0:
|
||||||
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
|
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -9449,7 +9464,6 @@ packages:
|
|||||||
|
|
||||||
libsql@0.5.3:
|
libsql@0.5.3:
|
||||||
resolution: {integrity: sha512-S3WR8WNCJV1VXraBFUKjDA6+8LcNDJMLm+83qohm1O3YM1iVqV2+/XN3SXOxpxVjuL4g/rLrjO5kzygkPefCFQ==}
|
resolution: {integrity: sha512-S3WR8WNCJV1VXraBFUKjDA6+8LcNDJMLm+83qohm1O3YM1iVqV2+/XN3SXOxpxVjuL4g/rLrjO5kzygkPefCFQ==}
|
||||||
cpu: [x64, arm64, wasm32]
|
|
||||||
os: [darwin, linux, win32]
|
os: [darwin, linux, win32]
|
||||||
|
|
||||||
lilconfig@2.1.0:
|
lilconfig@2.1.0:
|
||||||
@ -18251,6 +18265,17 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.3.0
|
'@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': {}
|
'@trysound/sax@0.2.0': {}
|
||||||
|
|
||||||
'@ts-graphviz/adapter@2.0.5':
|
'@ts-graphviz/adapter@2.0.5':
|
||||||
@ -20945,6 +20970,8 @@ snapshots:
|
|||||||
is-date-object: 1.0.5
|
is-date-object: 1.0.5
|
||||||
is-symbol: 1.0.4
|
is-symbol: 1.0.4
|
||||||
|
|
||||||
|
es-toolkit@1.34.1: {}
|
||||||
|
|
||||||
esbuild-register@3.6.0(esbuild@0.19.12):
|
esbuild-register@3.6.0(esbuild@0.19.12):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.0(supports-color@9.4.0)
|
debug: 4.4.0(supports-color@9.4.0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user