mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-11 17:29:44 +00:00
Merge branch 'develop' into command-redesign
This commit is contained in:
commit
20ff7b1fad
@ -11,11 +11,9 @@
|
|||||||
"jarvis",
|
"jarvis",
|
||||||
"form-view",
|
"form-view",
|
||||||
"@kksh/desktop",
|
"@kksh/desktop",
|
||||||
"@kksh/supabase",
|
|
||||||
"@kksh/utils",
|
"@kksh/utils",
|
||||||
"@kksh/extension",
|
"@kksh/extension",
|
||||||
"@kksh/schema",
|
"@kksh/schema",
|
||||||
"@kksh/supabase",
|
|
||||||
"@kksh/ui"
|
"@kksh/ui"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
501
Cargo.lock
generated
501
Cargo.lock
generated
@ -479,6 +479,15 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atoi"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@ -705,7 +714,7 @@ dependencies = [
|
|||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"log",
|
"log",
|
||||||
@ -824,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"
|
||||||
@ -1774,6 +1808,20 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728"
|
checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dashmap"
|
||||||
|
version = "6.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"lock_api",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-url"
|
name = "data-url"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -1830,6 +1878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
|
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
|
"pem-rfc7468",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2042,6 +2091,12 @@ dependencies = [
|
|||||||
"const-random",
|
"const-random",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenvy"
|
||||||
|
version = "0.15.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "downcast-rs"
|
name = "downcast-rs"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -2127,6 +2182,9 @@ name = "either"
|
|||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elliptic-curve"
|
name = "elliptic-curve"
|
||||||
@ -2308,6 +2366,17 @@ version = "3.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etcetera"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"home",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event-listener"
|
name = "event-listener"
|
||||||
version = "5.3.1"
|
version = "5.3.1"
|
||||||
@ -2586,6 +2655,17 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-intrusive"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -3746,6 +3826,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
@ -3966,8 +4055,10 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tauri-plugin-shellx",
|
"tauri-plugin-shellx",
|
||||||
"tauri-plugin-single-instance",
|
"tauri-plugin-single-instance",
|
||||||
|
"tauri-plugin-sql",
|
||||||
"tauri-plugin-store",
|
"tauri-plugin-store",
|
||||||
"tauri-plugin-stronghold",
|
"tauri-plugin-stronghold",
|
||||||
|
"tauri-plugin-svelte",
|
||||||
"tauri-plugin-system-info",
|
"tauri-plugin-system-info",
|
||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-upload",
|
"tauri-plugin-upload",
|
||||||
@ -3983,6 +4074,9 @@ name = "lazy_static"
|
|||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazycell"
|
name = "lazycell"
|
||||||
@ -4090,6 +4184,12 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -4307,6 +4407,16 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-5"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdns-sd"
|
name = "mdns-sd"
|
||||||
version = "0.11.5"
|
version = "0.11.5"
|
||||||
@ -4685,6 +4795,23 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint-dig"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"lazy_static",
|
||||||
|
"libm",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"smallvec",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -4760,6 +4887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5278,6 +5406,15 @@ dependencies = [
|
|||||||
"hmac",
|
"hmac",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem-rfc7468"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -5471,6 +5608,17 @@ dependencies = [
|
|||||||
"futures-io",
|
"futures-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkcs1"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"pkcs8",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkcs8"
|
name = "pkcs8"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@ -5693,7 +5841,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -5713,7 +5861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
@ -5965,7 +6113,7 @@ dependencies = [
|
|||||||
"built",
|
"built",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"interpolate_name",
|
"interpolate_name",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"log",
|
"log",
|
||||||
@ -6312,6 +6460,26 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
|
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsa"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"digest",
|
||||||
|
"num-bigint-dig",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"pkcs1",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.31.0"
|
version = "0.31.0"
|
||||||
@ -6957,6 +7125,9 @@ name = "smallvec"
|
|||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
@ -7035,6 +7206,211 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlformat"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
"unicode_categories",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27144619c6e5802f1380337a209d2ac1c431002dd74c6e60aebff3c506dc4f0c"
|
||||||
|
dependencies = [
|
||||||
|
"sqlx-core",
|
||||||
|
"sqlx-macros",
|
||||||
|
"sqlx-mysql",
|
||||||
|
"sqlx-postgres",
|
||||||
|
"sqlx-sqlite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-core"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a999083c1af5b5d6c071d34a708a19ba3e02106ad82ef7bbd69f5e48266b613b"
|
||||||
|
dependencies = [
|
||||||
|
"atoi",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"crc",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"either",
|
||||||
|
"event-listener",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-intrusive",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"hashlink",
|
||||||
|
"hex",
|
||||||
|
"indexmap 2.6.0",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"paste",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"smallvec",
|
||||||
|
"sqlformat",
|
||||||
|
"thiserror 1.0.66",
|
||||||
|
"time",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-macros"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a23217eb7d86c584b8cbe0337b9eacf12ab76fe7673c513141ec42565698bb88"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"sqlx-core",
|
||||||
|
"sqlx-macros-core",
|
||||||
|
"syn 2.0.87",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-macros-core"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a099220ae541c5db479c6424bdf1b200987934033c2584f79a0e1693601e776"
|
||||||
|
dependencies = [
|
||||||
|
"dotenvy",
|
||||||
|
"either",
|
||||||
|
"heck 0.5.0",
|
||||||
|
"hex",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"sqlx-core",
|
||||||
|
"sqlx-mysql",
|
||||||
|
"sqlx-postgres",
|
||||||
|
"sqlx-sqlite",
|
||||||
|
"syn 2.0.87",
|
||||||
|
"tempfile",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-mysql"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5afe4c38a9b417b6a9a5eeffe7235d0a106716495536e7727d1c7f4b1ff3eba6"
|
||||||
|
dependencies = [
|
||||||
|
"atoi",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"crc",
|
||||||
|
"digest",
|
||||||
|
"dotenvy",
|
||||||
|
"either",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"generic-array",
|
||||||
|
"hex",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
|
"itoa 1.0.11",
|
||||||
|
"log",
|
||||||
|
"md-5",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"percent-encoding",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rsa",
|
||||||
|
"serde",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"smallvec",
|
||||||
|
"sqlx-core",
|
||||||
|
"stringprep",
|
||||||
|
"thiserror 1.0.66",
|
||||||
|
"time",
|
||||||
|
"tracing",
|
||||||
|
"whoami",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-postgres"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1dbb157e65f10dbe01f729339c06d239120221c9ad9fa0ba8408c4cc18ecf21"
|
||||||
|
dependencies = [
|
||||||
|
"atoi",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"byteorder",
|
||||||
|
"crc",
|
||||||
|
"dotenvy",
|
||||||
|
"etcetera",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
|
"home",
|
||||||
|
"itoa 1.0.11",
|
||||||
|
"log",
|
||||||
|
"md-5",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"smallvec",
|
||||||
|
"sqlx-core",
|
||||||
|
"stringprep",
|
||||||
|
"thiserror 1.0.66",
|
||||||
|
"time",
|
||||||
|
"tracing",
|
||||||
|
"whoami",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-sqlite"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b2cdd83c008a622d94499c0006d8ee5f821f36c89b7d625c900e5dc30b5c5ee"
|
||||||
|
dependencies = [
|
||||||
|
"atoi",
|
||||||
|
"flume",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-intrusive",
|
||||||
|
"futures-util",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"log",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sqlx-core",
|
||||||
|
"time",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -7090,6 +7466,17 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stringprep"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
"unicode-properties",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strip-ansi-escapes"
|
name = "strip-ansi-escapes"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -7940,6 +8327,25 @@ dependencies = [
|
|||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-sql"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6ccca89ded6bd2ff49fdad9a5b34bcd624aa223fdfddbab83c85706ee3a4948"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"indexmap 2.6.0",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sqlx",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"thiserror 2.0.3",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-store"
|
name = "tauri-plugin-store"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@ -7977,6 +8383,19 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-svelte"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17e96f88b3c614b98cea3afb5de6e2661d32f82c70423ae125c56a25d62017e6"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"tauri-store",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-system-info"
|
name = "tauri-plugin-system-info"
|
||||||
version = "2.0.9"
|
version = "2.0.9"
|
||||||
@ -8097,6 +8516,52 @@ dependencies = [
|
|||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-store"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a33c8afdf92c1b177296c0299f6d20116cbce0fa1e2264819fea8c80fd31774"
|
||||||
|
dependencies = [
|
||||||
|
"dashmap",
|
||||||
|
"futures",
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tauri",
|
||||||
|
"tauri-store-macros",
|
||||||
|
"tauri-store-utils",
|
||||||
|
"thiserror 2.0.3",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-store-macros"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8857e4240cf6dbabb15fc2d595e92abba404f0a5cce0f3abbfe9316cac4aa99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.87",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-store-utils"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14376c237a6632663991634d51a31f128b6b381b94d65e747db2419a513ae6d8"
|
||||||
|
dependencies = [
|
||||||
|
"bon",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tauri",
|
||||||
|
"thiserror 2.0.3",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
@ -8746,12 +9211,24 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-properties"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode_categories"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -8974,6 +9451,12 @@ dependencies = [
|
|||||||
"wit-bindgen-rt",
|
"wit-bindgen-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.95"
|
version = "0.2.95"
|
||||||
@ -9231,6 +9714,16 @@ dependencies = [
|
|||||||
"rustix",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "whoami"
|
||||||
|
version = "1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
||||||
|
dependencies = [
|
||||||
|
"redox_syscall",
|
||||||
|
"wasite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"inquirer": "^10.1.2",
|
"inquirer": "^10.1.2",
|
||||||
"valibot": "^1.0.0-beta.10"
|
"valibot": "^1.0.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"commander": "^12.1.0",
|
"commander": "^12.1.0",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"valibot": "^1.0.0-beta.10"
|
"valibot": "^1.0.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
13
apps/desktop/dev.ts
Normal file
13
apps/desktop/dev.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { IconType } from "@kksh/api/models"
|
||||||
|
import { getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
|
||||||
|
|
||||||
|
const latestPublish = await getExtensionsLatestPublishByIdentifier({
|
||||||
|
path: {
|
||||||
|
identifier: "RAG1"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log(latestPublish)
|
||||||
|
// latestPublish
|
||||||
|
|
||||||
|
// console.log(typeof IconEnum.Iconify)
|
||||||
|
console.log(IconType.options)
|
91
apps/desktop/messages/de.json
Normal file
91
apps/desktop/messages/de.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
|
|
||||||
|
"app_name": "KunKun",
|
||||||
|
"secondary_app_name": "KunKun",
|
||||||
|
|
||||||
|
"common_edit": "Bearbeiten",
|
||||||
|
"common_clear": "Löschen",
|
||||||
|
"common_check": "Prüfen",
|
||||||
|
"common_install": "Installieren",
|
||||||
|
|
||||||
|
"home_command_input_placeholder": "Suchen…",
|
||||||
|
"home_command_input_dropdown_quit": "Beenden",
|
||||||
|
"home_command_input_dropdown_developer_title": "Entwickler",
|
||||||
|
"home_command_input_dropdown_close_window": "Fenster schließen",
|
||||||
|
"home_command_input_dropdown_toggle_devtools": "Entwicklertools umschalten",
|
||||||
|
"home_command_input_dropdown_reload_window": "Fenster neu laden",
|
||||||
|
"home_command_input_dropdown_open_preference": "Einstellungen öffnen",
|
||||||
|
"home_command_input_dropdown_toggle_dev_extension_hmr": "Entwicklererweiterungen HMR umschalten",
|
||||||
|
|
||||||
|
"command_group_heading_dev_ext": "Entwicklererweiterungen",
|
||||||
|
"command_group_heading_ext": "Erweiterungen",
|
||||||
|
"command_group_heading_quick_links": "Quick Links",
|
||||||
|
|
||||||
|
"settings_menu_settings": "Einstellungen",
|
||||||
|
"settings_menu_general": "Allgemein",
|
||||||
|
"settings_menu_app_search_paths": "Verzeichnisse für Programme",
|
||||||
|
"settings_menu_developer": "Entwickler",
|
||||||
|
"settings_menu_extensions": "Erweiterungen",
|
||||||
|
"settings_menu_set_dev_ext": "Dev-Erweiterung festlegen",
|
||||||
|
"settings_menu_add_dev_ext": "Dev-Erweiterung hinzufügen",
|
||||||
|
"settings_menu_about": "Über",
|
||||||
|
|
||||||
|
"settings_general_launch_at_login": "Beim Systemstart öffnen",
|
||||||
|
"settings_general_hotkey": "Tastenkombination",
|
||||||
|
"settings_general_menu_bar_icon": "Menüleiste-Symbol",
|
||||||
|
"settings_general_hide_on_blur": "Automatisch ausblenden",
|
||||||
|
"settings_general_extension_auto_upgrade": "Erweiterungen automatisch aktualisieren",
|
||||||
|
"settings_general_dev_extension_hmr": "Entwicklererweiterungen HMR",
|
||||||
|
"settings_general_join_beta_updates": "Beta-Updates nutzen",
|
||||||
|
"settings_general_developer_mode": "Entwickler-Modus",
|
||||||
|
"settings_general_language": "Sprache",
|
||||||
|
"settings_general_loading_animation": "Ladeanimation",
|
||||||
|
|
||||||
|
"settings_app_search_paths_title": "Zusätzliche Verzeichnisse für die Programm-Suche",
|
||||||
|
"settings_app_search_paths_add_app_search_path": "Verzeichnis für Programm-Suche hinzufügen",
|
||||||
|
"settings_app_search_paths_table_col_search_path": "Suchpfad",
|
||||||
|
"settings_app_search_paths_table_col_depth": "Tiefe",
|
||||||
|
"settings_app_search_paths_table_col_actions": "Aktionen",
|
||||||
|
|
||||||
|
"settings_about_version": "Version",
|
||||||
|
"settings_about_author": "Autor",
|
||||||
|
"settings_about_source_code": "Quellcode",
|
||||||
|
"settings_about_extensions_source_code": "Quellcode für Erweiterungen",
|
||||||
|
"settings_about_check_for_updates": "Nach Updates suchen",
|
||||||
|
|
||||||
|
"settings_set_dev_ext_title": "Verzeichnis der Entwicklererweiterungen",
|
||||||
|
"settings_set_dev_ext_description": "Hier werden Entwicklererweiterungen installiert.",
|
||||||
|
"settings_set_dev_ext_enter_path": "Verzeichnis eingeben",
|
||||||
|
|
||||||
|
"settings_extensions_title": "Deine Erweiterungen",
|
||||||
|
"settings_extensions_table_col_name": "Name",
|
||||||
|
"settings_extensions_table_col_identifier": "Identifikator",
|
||||||
|
"settings_extensions_table_col_type": "Typ",
|
||||||
|
"settings_extensions_table_col_version": "Version",
|
||||||
|
"settings_extensions_table_col_uninstall": "Deinstallieren",
|
||||||
|
|
||||||
|
"settings_add_dev_ext_title": "Entwicklererweiterung hinzufügen",
|
||||||
|
"settings_add_dev_ext_description": "Es gibt vier Möglichkeiten, eine Erweiterung als Entwicklererweiterung zu installieren. Tarball-Archiv, lokales Verzeichnis, URL zu Tarball-Archiv oder NPM-Paketnamen.",
|
||||||
|
"settings_add_dev_ext_install_from_ext_folders": "Verzeichnis",
|
||||||
|
"settings_add_dev_ext_install_from_ext_files": "Tarball-Archiv",
|
||||||
|
"settings_add_dev_ext_drag_and_drop": "Drag and Drop",
|
||||||
|
"settings_add_dev_ext_drag_and_drop_strike": "Drag and Drop",
|
||||||
|
"settings_add_dev_ext_drag_and_drop2": "Verzeichnis oder Tarball-Archiv",
|
||||||
|
"settings_add_dev_ext_install_tarball_from_url": "Tarball-Archiv aus URL installieren",
|
||||||
|
|
||||||
|
"troubleshooters_sidebar_title": "Fehlerbehebung",
|
||||||
|
"troubleshooters_sidebar_extension_loading_title": "Ladevorgang",
|
||||||
|
"troubleshooters_sidebar_extension_window_title": "Darstellung",
|
||||||
|
"troubleshooters_sidebar_mdns_debugger_title": "MDNS-Debugger",
|
||||||
|
|
||||||
|
"troubleshooters_extension_window_title": "Fehlerbehebung für die Darstellung von Erweiterungen",
|
||||||
|
"troubleshooters_extension_window_refresh_every_second": "Jede Sekunde neu laden",
|
||||||
|
"troubleshooters_extension_window_refresh": "Neu laden",
|
||||||
|
"troubleshooters_extension_window_refreshed": "{count}x neu geladen",
|
||||||
|
|
||||||
|
"troubleshooters_extension_loading_title": "Fehlerbehebung für den Ladevorgang von Erweiterungen",
|
||||||
|
"troubleshooters_extension_loading_table_col_identifier": "Identifikator",
|
||||||
|
"troubleshooters_extension_loading_table_col_path": "Verzeichnis",
|
||||||
|
"troubleshooters_extension_loading_table_col_error": "Fehler"
|
||||||
|
}
|
@ -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.33",
|
"version": "0.1.37-beta.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -17,8 +17,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^0.8.2",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@inlang/paraglide-sveltekit": "0.16.0",
|
"@inlang/paraglide-sveltekit": "0.16.0",
|
||||||
|
"@kksh/drizzle": "workspace:*",
|
||||||
"@kksh/extension": "workspace:*",
|
"@kksh/extension": "workspace:*",
|
||||||
"@kksh/supabase": "workspace:*",
|
|
||||||
"@kksh/svelte5": "^0.1.15",
|
"@kksh/svelte5": "^0.1.15",
|
||||||
"@kksh/ui": "workspace:*",
|
"@kksh/ui": "workspace:*",
|
||||||
"@kksh/utils": "workspace:*",
|
"@kksh/utils": "workspace:*",
|
||||||
@ -28,20 +28,24 @@
|
|||||||
"@tauri-apps/api": "^2.3.0",
|
"@tauri-apps/api": "^2.3.0",
|
||||||
"@tauri-apps/plugin-autostart": "^2.2.0",
|
"@tauri-apps/plugin-autostart": "^2.2.0",
|
||||||
"@tauri-apps/plugin-shell": "^2.2.0",
|
"@tauri-apps/plugin-shell": "^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",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
"kkrpc": "^0.1.2",
|
"kkrpc": "^0.2.2",
|
||||||
"lz-string": "^1.5.0",
|
"lz-string": "^1.5.0",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
"semver": "^7.7.1",
|
"semver": "^7.7.1",
|
||||||
"svelte-inspect-value": "^0.3.0",
|
"svelte-inspect-value": "^0.5.0",
|
||||||
"svelte-sonner": "^0.3.28",
|
"svelte-sonner": "^0.3.28",
|
||||||
"sveltekit-superforms": "^2.23.1",
|
"sveltekit-superforms": "^2.23.1",
|
||||||
"tauri-plugin-clipboard-api": "^2.1.11",
|
"tauri-plugin-clipboard-api": "^2.1.11",
|
||||||
"tauri-plugin-shellx-api": "^2.0.16",
|
"tauri-plugin-shellx-api": "^2.0.16",
|
||||||
|
"tauri-plugin-svelte": "1.2.1",
|
||||||
"tauri-plugin-user-input-api": "workspace:*",
|
"tauri-plugin-user-input-api": "workspace:*",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
"$schema": "https://inlang.com/schema/project-settings",
|
||||||
"sourceLanguageTag": "en",
|
"sourceLanguageTag": "en",
|
||||||
"languageTags": ["en", "zh", "ru", "pt", "vi"],
|
"languageTags": ["en", "zh", "ru", "pt", "vi", "de"],
|
||||||
"modules": [
|
"modules": [
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
|
||||||
|
@ -58,6 +58,7 @@ uuid = "1.14.0"
|
|||||||
obfstr = { workspace = true }
|
obfstr = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
tauri-plugin-stronghold = "2.2.0"
|
tauri-plugin-stronghold = "2.2.0"
|
||||||
|
tauri-plugin-sql = "2"
|
||||||
|
|
||||||
|
|
||||||
[target."cfg(target_os = \"macos\")".dependencies]
|
[target."cfg(target_os = \"macos\")".dependencies]
|
||||||
@ -71,3 +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 = "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",
|
||||||
|
5
apps/desktop/src-tauri/capabilities/svelte.json
Normal file
5
apps/desktop/src-tauri/capabilities/svelte.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"identifier": "svelte",
|
||||||
|
"windows": ["*"],
|
||||||
|
"permissions": ["svelte:default", "core:event:default"]
|
||||||
|
}
|
@ -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 {
|
||||||
@ -108,10 +108,16 @@ pub fn run() {
|
|||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.plugin(tauri_plugin_cli::init())
|
.plugin(tauri_plugin_cli::init())
|
||||||
|
.plugin(
|
||||||
|
tauri_plugin_sql::Builder::default()
|
||||||
|
// .add_migrations("sqlite:mydatabase.db", migrations)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.plugin(tauri_plugin_user_input::init())
|
.plugin(tauri_plugin_user_input::init())
|
||||||
.plugin(tauri_plugin_deep_link::init())
|
.plugin(tauri_plugin_deep_link::init())
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.plugin(tauri_plugin_os::init())
|
.plugin(tauri_plugin_os::init())
|
||||||
|
.plugin(tauri_plugin_svelte::init())
|
||||||
.plugin(tauri_plugin_process::init())
|
.plugin(tauri_plugin_process::init())
|
||||||
.plugin(tauri_plugin_autostart::init(
|
.plugin(tauri_plugin_autostart::init(
|
||||||
MacosLauncher::LaunchAgent,
|
MacosLauncher::LaunchAgent,
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"identifier": "sh.kunkun.desktop",
|
"identifier": "sh.kunkun.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
"devUrl": "http://localhost:1420",
|
"devUrl": "http://localhost:1566",
|
||||||
"beforeBuildCommand": "pnpm build",
|
"beforeBuildCommand": "pnpm build",
|
||||||
"frontendDist": "../build"
|
"frontendDist": "../build"
|
||||||
},
|
},
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"url": "/app",
|
"url": "/app",
|
||||||
"title": "Kunkun",
|
"title": "Kunkun",
|
||||||
"width": 800,
|
"width": 800,
|
||||||
|
"label": "main",
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"height": 600,
|
"height": 600,
|
||||||
"decorations": true,
|
"decorations": true,
|
||||||
|
@ -242,6 +242,23 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
keywords: ["extension", "troubleshooter"]
|
keywords: ["extension", "troubleshooter"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ORM Troubleshooter",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "material-symbols:database"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
flags: {
|
||||||
|
developer: true,
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
function: async () => {
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
goto(i18n.resolveRoute("/app/troubleshooters/orm"))
|
||||||
|
},
|
||||||
|
keywords: ["extension", "troubleshooter", "database", "orm"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Create Quicklink",
|
name: "Create Quicklink",
|
||||||
icon: {
|
icon: {
|
||||||
@ -411,7 +428,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
visible: false
|
visible: false
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.show()
|
window.show().then(() => window.setFocus())
|
||||||
}, 2_000)
|
}, 2_000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,19 +3,22 @@ import { appState } from "@/stores"
|
|||||||
import { winExtMap } from "@/stores/winExtMap"
|
import { winExtMap } from "@/stores/winExtMap"
|
||||||
import { helperAPI } from "@/utils/helper"
|
import { helperAPI } from "@/utils/helper"
|
||||||
import { paste } from "@/utils/hotkey"
|
import { paste } from "@/utils/hotkey"
|
||||||
|
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"
|
||||||
import * as path from "@tauri-apps/api/path"
|
import * as path from "@tauri-apps/api/path"
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
import * as fs from "@tauri-apps/plugin-fs"
|
import * as fs from "@tauri-apps/plugin-fs"
|
||||||
|
import { info } from "@tauri-apps/plugin-log"
|
||||||
import { platform } from "@tauri-apps/plugin-os"
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
||||||
@ -85,6 +88,7 @@ export async function onHeadlessCmdSelect(
|
|||||||
const loadedExt = await loadExtensionManifestFromDisk(
|
const loadedExt = await loadExtensionManifestFromDisk(
|
||||||
await path.join(ext.extPath, "package.json")
|
await path.join(ext.extPath, "package.json")
|
||||||
)
|
)
|
||||||
|
|
||||||
const scriptPath = await path.join(loadedExt.extPath, cmd.main)
|
const scriptPath = await path.join(loadedExt.extPath, cmd.main)
|
||||||
const workerScript = await fs.readTextFile(scriptPath)
|
const workerScript = await fs.readTextFile(scriptPath)
|
||||||
const blob = new Blob([workerScript], { type: "application/javascript" })
|
const blob = new Blob([workerScript], { type: "application/javascript" })
|
||||||
@ -124,8 +128,15 @@ export async function onHeadlessCmdSelect(
|
|||||||
} satisfies IApp
|
} satisfies IApp
|
||||||
}
|
}
|
||||||
const io = new WorkerParentIO(worker)
|
const io = new WorkerParentIO(worker)
|
||||||
|
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
|
||||||
|
info(
|
||||||
|
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
|
||||||
|
)
|
||||||
const rpc = new RPCChannel<typeof serverAPI2, HeadlessCommand>(io, {
|
const rpc = new RPCChannel<typeof serverAPI2, HeadlessCommand>(io, {
|
||||||
expose: serverAPI2
|
expose: serverAPI2,
|
||||||
|
serialization: {
|
||||||
|
version: kkrpcSerialization
|
||||||
|
}
|
||||||
})
|
})
|
||||||
const workerAPI = rpc.getAPI()
|
const workerAPI = rpc.getAPI()
|
||||||
await workerAPI.load()
|
await workerAPI.load()
|
||||||
|
@ -12,7 +12,7 @@ import { onQuickLinkSelect } from "./quick-links"
|
|||||||
|
|
||||||
const onExtCmdSelect: OnExtCmdSelect = (
|
const onExtCmdSelect: OnExtCmdSelect = (
|
||||||
ext: ExtPackageJsonExtra,
|
ext: ExtPackageJsonExtra,
|
||||||
cmd: CustomUiCmd | TemplateUiCmd,
|
cmd: CustomUiCmd | TemplateUiCmd | HeadlessCmd,
|
||||||
{ isDev, hmr }: { isDev: boolean; hmr: boolean }
|
{ isDev, hmr }: { isDev: boolean; hmr: boolean }
|
||||||
) => {
|
) => {
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { appConfig, appState } from "@/stores"
|
||||||
|
import { cn } from "@/utils"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import { BorderBeam, Constants, Layouts, TauriLink } from "@kksh/ui"
|
||||||
|
import { goto } from "$app/navigation"
|
||||||
|
import { ArrowLeftIcon, LoaderCircleIcon } from "lucide-svelte"
|
||||||
|
import Dance from "../dance/dance.svelte"
|
||||||
|
|
||||||
|
let { class: className }: { class?: string } = $props()
|
||||||
|
|
||||||
|
function goHome() {
|
||||||
|
appState.setFullScreenLoading(false)
|
||||||
|
goto("/app")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Layouts.Center class={cn("flex h-screen flex-col items-center justify-center", className)}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onclick={goHome}
|
||||||
|
class={cn(Constants.CLASSNAMES.BACK_BUTTON, "absolute left-4 top-4")}
|
||||||
|
data-flip-id={Constants.CLASSNAMES.BACK_BUTTON}
|
||||||
|
>
|
||||||
|
<ArrowLeftIcon class="size-4" />
|
||||||
|
</Button>
|
||||||
|
{#if $appConfig.loadingAnimation === "kunkun-dancing"}
|
||||||
|
<!-- <DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
|
||||||
|
<Dance class="absolute z-50 h-screen opacity-20" />
|
||||||
|
{:else}
|
||||||
|
<!-- <LoadingAnimation delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
|
||||||
|
<LoaderCircleIcon class="h-24 w-24 animate-spin" />
|
||||||
|
<span class="font-mono">Loading</span>
|
||||||
|
{/if}
|
||||||
|
<BorderBeam size={150} duration={12} />
|
||||||
|
</Layouts.Center>
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { setAppConfigContext } from "@/context"
|
import { setAppConfigContext } from "@/context"
|
||||||
import { setAppStateContext } from "@/context/appState"
|
import { setAppStateContext } from "@/context/appState"
|
||||||
import type { AppConfig, AppState } from "@kksh/types"
|
import type { AppConfigState, AppState } from "@kksh/types"
|
||||||
import type { Snippet } from "svelte"
|
import { type Snippet } from "svelte"
|
||||||
import type { Writable } from "svelte/store"
|
import type { Writable } from "svelte/store"
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -10,7 +10,7 @@
|
|||||||
appState,
|
appState,
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
appConfig: Writable<AppConfig>
|
appConfig: Writable<AppConfigState>
|
||||||
appState: Writable<AppState>
|
appState: Writable<AppState>
|
||||||
children: Snippet<[]>
|
children: Snippet<[]>
|
||||||
} = $props()
|
} = $props()
|
||||||
|
@ -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>
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
* It's designed to allow all components to access a shared state.
|
* It's designed to allow all components to access a shared state.
|
||||||
* With context, we can avoid prop drilling, and avoid using stores which makes components hard to encapsulate.
|
* With context, we can avoid prop drilling, and avoid using stores which makes components hard to encapsulate.
|
||||||
*/
|
*/
|
||||||
import type { AppConfig } from "@/types/appConfig"
|
import type { AppConfigState } from "@kksh/types"
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import type { Writable } from "svelte/store"
|
import type { Writable } from "svelte/store"
|
||||||
|
|
||||||
export const APP_CONFIG_CONTEXT_KEY = Symbol("appConfig")
|
export const APP_CONFIG_CONTEXT_KEY = Symbol("appConfig")
|
||||||
|
|
||||||
export function getAppConfigContext(): Writable<AppConfig> {
|
export function getAppConfigContext(): Writable<AppConfigState> {
|
||||||
return getContext(APP_CONFIG_CONTEXT_KEY)
|
return getContext(APP_CONFIG_CONTEXT_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAppConfigContext(appConfig: Writable<AppConfig>) {
|
export function setAppConfigContext(appConfig: Writable<AppConfigState>) {
|
||||||
setContext(APP_CONFIG_CONTEXT_KEY, appConfig)
|
setContext(APP_CONFIG_CONTEXT_KEY, appConfig)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { AppState } from "@/types/appState"
|
import type { AppState } from "@kksh/types"
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import type { Writable } from "svelte/store"
|
import type { Writable } from "svelte/store"
|
||||||
|
|
||||||
|
60
apps/desktop/src/lib/orm/database.ts
Normal file
60
apps/desktop/src/lib/orm/database.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
// import { db as dbCmd } from "@kksh/api/commands"
|
||||||
|
// import * as schema from "@kksh/drizzle/schema"
|
||||||
|
// import { error } from "@tauri-apps/plugin-log"
|
||||||
|
// import { drizzle } from "drizzle-orm/sqlite-proxy"
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Loads the sqlite database via the Tauri Proxy.
|
||||||
|
// */
|
||||||
|
// // export const sqlite = await Database.load("sqlite:test.db");
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * The drizzle database instance.
|
||||||
|
// */
|
||||||
|
// export const db = drizzle<typeof schema>(
|
||||||
|
// async (sql, params, method) => {
|
||||||
|
// let rows: any = []
|
||||||
|
// let results = []
|
||||||
|
// console.log({
|
||||||
|
// sql,
|
||||||
|
// params,
|
||||||
|
// method
|
||||||
|
// })
|
||||||
|
// console.log(sql)
|
||||||
|
// // If the query is a SELECT, use the select method
|
||||||
|
// if (isSelectQuery(sql)) {
|
||||||
|
// rows = await dbCmd.select(sql, params).catch((e) => {
|
||||||
|
// error("SQL Error:", e)
|
||||||
|
// return []
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// // Otherwise, use the execute method
|
||||||
|
// rows = await dbCmd.execute(sql, params).catch((e) => {
|
||||||
|
// error("SQL Error:", e)
|
||||||
|
// return []
|
||||||
|
// })
|
||||||
|
// return { rows: [] }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// rows = rows.map((row: any) => {
|
||||||
|
// return Object.values(row)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // If the method is "all", return all rows
|
||||||
|
// results = method === "all" ? rows : rows[0]
|
||||||
|
// return { rows: results }
|
||||||
|
// },
|
||||||
|
// // Pass the schema to the drizzle instance
|
||||||
|
// { schema: schema, logger: true }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Checks if the given SQL query is a SELECT query.
|
||||||
|
// * @param sql The SQL query to check.
|
||||||
|
// * @returns True if the query is a SELECT query, false otherwise.
|
||||||
|
// */
|
||||||
|
// function isSelectQuery(sql: string): boolean {
|
||||||
|
// const selectRegex = /^\s*SELECT\b/i
|
||||||
|
// return selectRegex.test(sql)
|
||||||
|
// }
|
@ -1,16 +1,16 @@
|
|||||||
import { getExtensionsFolder } from "@/constants"
|
import { getExtensionsFolder } from "@/constants"
|
||||||
import { createTauriSyncStore, type WithSyncStore } from "@/utils/sync-store"
|
|
||||||
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 AppConfig } from "@kksh/types"
|
import { LoadingAnimation, PersistedAppConfig, type AppConfigState } from "@kksh/types"
|
||||||
import { debug, error } 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 { get, writable } from "svelte/store"
|
import { get, writable } from "svelte/store"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import { setLanguageTag } from "../paraglide/runtime"
|
|
||||||
|
|
||||||
export const defaultAppConfig: AppConfig = {
|
export const defaultAppConfig: AppConfigState = {
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
platform: "macos",
|
platform: "macos",
|
||||||
language: "en",
|
language: "en",
|
||||||
@ -29,14 +29,15 @@ export const defaultAppConfig: AppConfig = {
|
|||||||
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)
|
||||||
|
|
||||||
interface AppConfigAPI {
|
interface AppConfigAPI {
|
||||||
init: () => Promise<void>
|
init: () => Promise<void>
|
||||||
get: () => AppConfig
|
get: () => AppConfigState
|
||||||
setTheme: (theme: ThemeConfig) => void
|
setTheme: (theme: ThemeConfig) => void
|
||||||
setDevExtensionPath: (devExtensionPath: string | null) => void
|
setDevExtensionPath: (devExtensionPath: string | null) => void
|
||||||
setTriggerHotkey: (triggerHotkey: string[]) => void
|
setTriggerHotkey: (triggerHotkey: string[]) => void
|
||||||
@ -46,72 +47,63 @@ interface AppConfigAPI {
|
|||||||
removeAppSearchPath: (appSearchPath: SearchPath) => void
|
removeAppSearchPath: (appSearchPath: SearchPath) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAppConfig(): WithSyncStore<AppConfig & { language: string }> & AppConfigAPI {
|
class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI {
|
||||||
const store = createTauriSyncStore("app-config", defaultAppConfig)
|
constructor() {
|
||||||
|
super("app-config", defaultAppConfig, {
|
||||||
async function init() {
|
saveOnChange: true
|
||||||
debug("Initializing app config")
|
})
|
||||||
const persistStore = await load("kk-config.json", { autoSave: true })
|
this.start().catch((err) => {
|
||||||
let loadedConfig = await persistStore.get("config")
|
error("Failed to start app config store", err)
|
||||||
if (typeof loadedConfig === "object") {
|
toast.error("Failed to start app config store", { description: err.message })
|
||||||
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))
|
|
||||||
}
|
|
||||||
appConfigLoaded.set(true)
|
|
||||||
store.subscribe(async (config) => {
|
|
||||||
console.log("Saving app config", config)
|
|
||||||
await persistStore.set("config", config)
|
|
||||||
updateTheme(config.theme)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
async init() {
|
||||||
|
debug("Initializing app config")
|
||||||
|
const extensionsInstallDir = await getExtensionsFolder()
|
||||||
|
this.update((config) => ({
|
||||||
|
...config,
|
||||||
|
isInitialized: true,
|
||||||
|
platform: os.platform(),
|
||||||
|
extensionsInstallDir
|
||||||
|
}))
|
||||||
|
appConfigLoaded.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
get() {
|
||||||
...store,
|
return get(this)
|
||||||
get: () => get(store),
|
}
|
||||||
setTheme: (theme: ThemeConfig) => store.update((config) => ({ ...config, theme })),
|
setTheme(theme: ThemeConfig) {
|
||||||
setDevExtensionPath: (devExtensionPath: string | null) => {
|
this.update((config) => ({ ...config, theme }))
|
||||||
console.log("setDevExtensionPath", devExtensionPath)
|
}
|
||||||
store.update((config) => ({ ...config, devExtensionPath }))
|
setDevExtensionPath(devExtensionPath: string | null) {
|
||||||
},
|
info(`setDevExtensionPath ${devExtensionPath}`)
|
||||||
setTriggerHotkey: (triggerHotkey: string[]) => {
|
this.update((config) => ({ ...config, devExtensionPath }))
|
||||||
store.update((config) => ({ ...config, triggerHotkey }))
|
}
|
||||||
},
|
setTriggerHotkey(triggerHotkey: string[]) {
|
||||||
setOnBoarded: (onBoarded: boolean) => {
|
this.update((config) => ({ ...config, triggerHotkey }))
|
||||||
store.update((config) => ({ ...config, onBoarded }))
|
}
|
||||||
},
|
setOnBoarded(onBoarded: boolean) {
|
||||||
setLanguage: (language: string) => {
|
this.update((config) => ({ ...config, onBoarded }))
|
||||||
store.update((config) => ({ ...config, language }))
|
}
|
||||||
},
|
setLanguage(language: string) {
|
||||||
addAppSearchPath: (appSearchPath: SearchPath) => {
|
this.update((config) => ({ ...config, language }))
|
||||||
store.update((config) => ({
|
}
|
||||||
...config,
|
addAppSearchPath(appSearchPath: SearchPath) {
|
||||||
appSearchPaths: [...config.appSearchPaths, appSearchPath]
|
this.update((config) => ({
|
||||||
}))
|
...config,
|
||||||
},
|
appSearchPaths: [...config.appSearchPaths, appSearchPath]
|
||||||
removeAppSearchPath: (appSearchPath: SearchPath) => {
|
}))
|
||||||
store.update((config) => ({
|
}
|
||||||
...config,
|
removeAppSearchPath(appSearchPath: SearchPath) {
|
||||||
appSearchPaths: config.appSearchPaths.filter((path) => path.path !== appSearchPath.path)
|
this.update((config) => ({
|
||||||
}))
|
...config,
|
||||||
},
|
appSearchPaths: config.appSearchPaths.filter((path) => path.path !== appSearchPath.path)
|
||||||
init
|
}))
|
||||||
|
}
|
||||||
|
setLoadingAnimation(loadingAnimation: LoadingAnimation) {
|
||||||
|
this.update((config) => ({ ...config, loadingAnimation }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appConfig = createAppConfig()
|
// export const appConfig = createAppConfig()
|
||||||
|
export const appConfig = new AppConfigStore()
|
||||||
|
@ -8,7 +8,8 @@ export const defaultAppState: AppState = {
|
|||||||
loadingBar: false,
|
loadingBar: false,
|
||||||
defaultAction: "",
|
defaultAction: "",
|
||||||
actionPanel: undefined,
|
actionPanel: undefined,
|
||||||
lockHideOnBlur: false // when dialog is open, we don't hide the app, we lock the hide on blur and unlock when dialog is closed
|
lockHideOnBlur: false, // when dialog is open, we don't hide the app, we lock the hide on blur and unlock when dialog is closed
|
||||||
|
fullScreenLoading: false
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AppStateAPI {
|
interface AppStateAPI {
|
||||||
@ -18,6 +19,7 @@ interface AppStateAPI {
|
|||||||
setDefaultAction: (defaultAction: string | null) => void
|
setDefaultAction: (defaultAction: string | null) => void
|
||||||
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
|
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
|
||||||
setLockHideOnBlur: (lockHideOnBlur: boolean) => void
|
setLockHideOnBlur: (lockHideOnBlur: boolean) => void
|
||||||
|
setFullScreenLoading: (fullScreenLoading: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAppState(): Writable<AppState> & AppStateAPI {
|
function createAppState(): Writable<AppState> & AppStateAPI {
|
||||||
@ -40,6 +42,9 @@ function createAppState(): Writable<AppState> & AppStateAPI {
|
|||||||
},
|
},
|
||||||
setLockHideOnBlur: (lockHideOnBlur: boolean) => {
|
setLockHideOnBlur: (lockHideOnBlur: boolean) => {
|
||||||
store.update((state) => ({ ...state, lockHideOnBlur }))
|
store.update((state) => ({ ...state, lockHideOnBlur }))
|
||||||
|
},
|
||||||
|
setFullScreenLoading: (fullScreenLoading: boolean) => {
|
||||||
|
store.update((state) => ({ ...state, fullScreenLoading }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import Fuse from "fuse.js"
|
|||||||
import { derived, get, writable } from "svelte/store"
|
import { derived, get, writable } from "svelte/store"
|
||||||
import { appState } from "./appState"
|
import { appState } from "./appState"
|
||||||
|
|
||||||
export const fuse = new Fuse<AppInfo>([], {
|
const fuse = new Fuse<AppInfo>([], {
|
||||||
includeScore: true,
|
includeScore: true,
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
keys: ["name"]
|
keys: ["name"]
|
||||||
|
@ -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"
|
||||||
|
@ -6,7 +6,7 @@ import Fuse from "fuse.js"
|
|||||||
import { derived, get, writable, type Writable } from "svelte/store"
|
import { derived, get, writable, type Writable } from "svelte/store"
|
||||||
import { appState } from "./appState"
|
import { appState } from "./appState"
|
||||||
|
|
||||||
export const fuse = new Fuse<QuickLink>([], {
|
const fuse = new Fuse<QuickLink>([], {
|
||||||
includeScore: true,
|
includeScore: true,
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
keys: ["name"]
|
keys: ["name"]
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { SupabaseAPI } from "@kksh/supabase/api"
|
|
||||||
import type { Database } from "@kksh/supabase/types"
|
|
||||||
import * as sb from "@supabase/supabase-js"
|
import * as sb from "@supabase/supabase-js"
|
||||||
import { SUPABASE_ANON_KEY, SUPABASE_URL } from "./constants"
|
import { SUPABASE_ANON_KEY, SUPABASE_URL } from "./constants"
|
||||||
|
|
||||||
// export const supabase = createSB(SUPABASE_URL, SUPABASE_ANON_KEY)
|
// export const supabase = createSB(SUPABASE_URL, SUPABASE_ANON_KEY)
|
||||||
export const supabase: sb.SupabaseClient<Database> = sb.createClient<Database>(
|
export const supabase: sb.SupabaseClient = sb.createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
||||||
SUPABASE_URL,
|
auth: {
|
||||||
SUPABASE_ANON_KEY,
|
flowType: "pkce"
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
flowType: "pkce"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
|
|
||||||
export const storage = supabase.storage
|
export const storage = supabase.storage
|
||||||
export const supabaseExtensionsStorage = supabase.storage.from("extensions")
|
export const supabaseExtensionsStorage = supabase.storage.from("extensions")
|
||||||
export const supabaseAPI = new SupabaseAPI(supabase)
|
// export const supabaseAPI = new SupabaseAPI(supabase)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
apps/desktop/src/lib/utils/kkrpc.ts
Normal file
19
apps/desktop/src/lib/utils/kkrpc.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { parseAPIVersion } from "@kksh/extension/load"
|
||||||
|
import type { ExtPackageJsonExtra } from "@kunkunapi/src/models/manifest"
|
||||||
|
import semver from "semver"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide the serialization method for kkrpc based on the api version
|
||||||
|
* apiVersion is populated in loadExtensionManifestFromDisk, but we parse it again
|
||||||
|
* @param apiVersion - The version of the @kksh/api
|
||||||
|
* @returns "superjson" or "json"
|
||||||
|
*/
|
||||||
|
export function decideKkrpcSerialization(ext: ExtPackageJsonExtra): "superjson" | "json" {
|
||||||
|
const apiVersion = parseAPIVersion(ext.dependencies || {})
|
||||||
|
if (apiVersion && semver.lte(apiVersion, "0.1.5")) {
|
||||||
|
// 0.1.6 is the first version that supports superjson and default to use superjson
|
||||||
|
return "json"
|
||||||
|
}
|
||||||
|
// fallback default to use superjson, some extensions might not install @kksh/api
|
||||||
|
return "superjson"
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
import { extensions } from "@/stores"
|
import { extensions } from "@/stores"
|
||||||
import { supabaseAPI } from "@/supabase"
|
|
||||||
import { isCompatible } from "@kksh/api"
|
import { isCompatible } from "@kksh/api"
|
||||||
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
import { greaterThan } from "@std/semver"
|
import { getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
|
||||||
import { relaunch } from "@tauri-apps/plugin-process"
|
import { relaunch } from "@tauri-apps/plugin-process"
|
||||||
import { check } from "@tauri-apps/plugin-updater"
|
import { check } from "@tauri-apps/plugin-updater"
|
||||||
import { gt } from "semver"
|
import { gt } from "semver"
|
||||||
@ -32,11 +31,22 @@ export async function checkSingleExtensionUpdate(
|
|||||||
installedExt: ExtPackageJsonExtra,
|
installedExt: ExtPackageJsonExtra,
|
||||||
autoupgrade: boolean
|
autoupgrade: boolean
|
||||||
) {
|
) {
|
||||||
const { data: sbExt, error } = await supabaseAPI.getLatestExtPublish(
|
const {
|
||||||
installedExt.kunkun.identifier
|
data: sbExt,
|
||||||
)
|
error,
|
||||||
|
response
|
||||||
|
} = await getExtensionsLatestPublishByIdentifier({
|
||||||
|
path: {
|
||||||
|
identifier: "RAG"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// const { data: sbExt, error } = await supabaseAPI.getLatestExtPublish(
|
||||||
|
// installedExt.kunkun.identifier
|
||||||
|
// )
|
||||||
if (error) {
|
if (error) {
|
||||||
return toast.error(`Failed to check update for ${installedExt.kunkun.identifier}: ${error}`)
|
return toast.error(
|
||||||
|
`Failed to check update for ${installedExt.kunkun.identifier}: ${error} (${response.status})`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sbExt) {
|
if (!sbExt) {
|
||||||
@ -49,10 +59,7 @@ export async function checkSingleExtensionUpdate(
|
|||||||
) {
|
) {
|
||||||
if (autoupgrade) {
|
if (autoupgrade) {
|
||||||
await extensions
|
await extensions
|
||||||
.upgradeStoreExtension(
|
.upgradeStoreExtension(sbExt.identifier, sbExt.tarball_path)
|
||||||
sbExt.identifier,
|
|
||||||
supabaseAPI.translateExtensionFilePathToUrl(sbExt.tarball_path)
|
|
||||||
)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(`${sbExt.name} upgraded`, {
|
toast.success(`${sbExt.name} upgraded`, {
|
||||||
description: `From ${installedExt.version} to ${sbExt.version}`
|
description: `From ${installedExt.version} to ${sbExt.version}`
|
||||||
|
@ -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"
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
import { ParaglideJS } from "@inlang/paraglide-sveltekit"
|
import { ParaglideJS } from "@inlang/paraglide-sveltekit"
|
||||||
import { i18n } from "$lib/i18n"
|
import { i18n } from "$lib/i18n"
|
||||||
import "../app.css"
|
import "../app.css"
|
||||||
|
import FullScreenLoading from "@/components/common/FullScreenLoading.svelte"
|
||||||
|
import { appState } from "@/stores/appState"
|
||||||
import { ModeWatcher, ThemeWrapper } from "@kksh/svelte5"
|
import { ModeWatcher, ThemeWrapper } from "@kksh/svelte5"
|
||||||
import { Toaster } from "svelte-sonner"
|
import { Toaster } from "svelte-sonner"
|
||||||
|
|
||||||
@ -12,6 +14,9 @@
|
|||||||
<ModeWatcher />
|
<ModeWatcher />
|
||||||
<Toaster richColors closeButton />
|
<Toaster richColors closeButton />
|
||||||
<ThemeWrapper>
|
<ThemeWrapper>
|
||||||
|
{#if $appState.fullScreenLoading}
|
||||||
|
<FullScreenLoading class="bg-background absolute inset-0 z-50" />
|
||||||
|
{/if}
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</ThemeWrapper>
|
</ThemeWrapper>
|
||||||
</ParaglideJS>
|
</ParaglideJS>
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,22 @@
|
|||||||
SystemCmds
|
SystemCmds
|
||||||
} from "@kksh/ui/main"
|
} from "@kksh/ui/main"
|
||||||
import { cn } from "@kksh/ui/utils"
|
import { cn } from "@kksh/ui/utils"
|
||||||
|
import { Ext } from "@kunkunapi/src/models/extension"
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
|
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
|
||||||
import { platform } from "@tauri-apps/plugin-os"
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { exit } from "@tauri-apps/plugin-process"
|
import { exit } from "@tauri-apps/plugin-process"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
import {
|
||||||
|
ArrowBigUpIcon,
|
||||||
|
CircleXIcon,
|
||||||
|
EllipsisVerticalIcon,
|
||||||
|
RefreshCcwIcon,
|
||||||
|
SettingsIcon
|
||||||
|
} from "lucide-svelte"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { Inspect } from "svelte-inspect-value"
|
import { Inspect } from "svelte-inspect-value"
|
||||||
|
import * as v from "valibot"
|
||||||
|
|
||||||
const win = getCurrentWindow()
|
const win = getCurrentWindow()
|
||||||
let inputEle: HTMLInputElement | null = $state(null)
|
let inputEle: HTMLInputElement | null = $state(null)
|
||||||
@ -58,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
|
||||||
inputEle?.focus()
|
.show()
|
||||||
|
.then(() => win.setFocus())
|
||||||
|
.finally(() => {
|
||||||
|
inputEle?.focus()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
inputEle?.focus()
|
inputEle?.focus()
|
||||||
@ -71,6 +83,7 @@
|
|||||||
// wait for appConfig store to be loaded, it's async and saved to disk when changed, so we use another store appConfigLoaded
|
// wait for appConfig store to be loaded, it's async and saved to disk when changed, so we use another store appConfigLoaded
|
||||||
// to keep track of the loading status
|
// to keep track of the loading status
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
|
console.log("appConfig.get().onBoarded", appConfig.get().onBoarded)
|
||||||
if (!appConfig.get().onBoarded) {
|
if (!appConfig.get().onBoarded) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
goto(i18n.resolveRoute("/app/help/onboarding"))
|
goto(i18n.resolveRoute("/app/help/onboarding"))
|
||||||
@ -103,6 +116,7 @@
|
|||||||
<Inspect name="devStoreExtCmds" value={$devStoreExtCmds} />
|
<Inspect name="devStoreExtCmds" value={$devStoreExtCmds} />
|
||||||
<Inspect name="$appState.searchTerm" value={$appState.searchTerm} />
|
<Inspect name="$appState.searchTerm" value={$appState.searchTerm} />
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<Command.Root
|
<Command.Root
|
||||||
class={cn("h-screen rounded-lg shadow-md")}
|
class={cn("h-screen rounded-lg shadow-md")}
|
||||||
bind:value={$appState.highlightedCmd}
|
bind:value={$appState.highlightedCmd}
|
||||||
@ -178,8 +192,8 @@
|
|||||||
<span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+R </span>
|
<span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+R </span>
|
||||||
</DropdownMenu.Shortcut>
|
</DropdownMenu.Shortcut>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item onclick={() => location.reload()}>
|
<DropdownMenu.Item onclick={() => goto(i18n.resolveRoute("/app/settings"))}>
|
||||||
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
|
<SettingsIcon class="mr-2 h-4 w-4 text-green-500" />
|
||||||
{m.home_command_input_dropdown_open_preference()}
|
{m.home_command_input_dropdown_open_preference()}
|
||||||
<DropdownMenu.Shortcut>
|
<DropdownMenu.Shortcut>
|
||||||
{#if platform() === "macos"}
|
{#if platform() === "macos"}
|
||||||
|
@ -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
|
||||||
|
@ -2,14 +2,13 @@
|
|||||||
import { getExtensionsFolder } from "@/constants"
|
import { getExtensionsFolder } from "@/constants"
|
||||||
import { appState, extensions } from "@/stores"
|
import { appState, extensions } from "@/stores"
|
||||||
import { keys } from "@/stores/keys"
|
import { keys } from "@/stores/keys"
|
||||||
import { supabaseAPI } from "@/supabase"
|
|
||||||
import { goBackOnEscapeClearSearchTerm, goHomeOnEscapeClearSearchTerm } from "@/utils/key"
|
|
||||||
import { goBack, goHome } from "@/utils/route"
|
import { goBack, goHome } from "@/utils/route"
|
||||||
import { Action as ActionSchema } from "@kksh/api/models"
|
import { Action as ActionSchema, ExtensionStoreListItem, ExtPublish } from "@kksh/api/models"
|
||||||
import { Action } from "@kksh/api/ui"
|
import { Action } from "@kksh/api/ui"
|
||||||
import { SBExt } from "@kksh/supabase/models"
|
import {
|
||||||
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
getExtensionsLatestPublishByIdentifier,
|
||||||
import { type Tables } from "@kksh/supabase/types"
|
postExtensionsIncrementDownloads
|
||||||
|
} from "@kksh/sdk"
|
||||||
import { Button, Command } from "@kksh/svelte5"
|
import { Button, Command } from "@kksh/svelte5"
|
||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
import { ExtListItem } from "@kksh/ui/extension"
|
import { ExtListItem } from "@kksh/ui/extension"
|
||||||
@ -17,8 +16,9 @@
|
|||||||
import { platform } from "@tauri-apps/plugin-os"
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { ArrowLeft } from "lucide-svelte"
|
import { ArrowLeft } from "lucide-svelte"
|
||||||
import type { Snippet } from "svelte"
|
import { onMount, type Snippet } from "svelte"
|
||||||
import { toast } from "svelte-sonner"
|
import { toast } from "svelte-sonner"
|
||||||
|
import type { Action as SvelteAction } from "svelte/action"
|
||||||
import { getInstallExtras } from "./[identifier]/helper.js"
|
import { getInstallExtras } from "./[identifier]/helper.js"
|
||||||
|
|
||||||
let { data } = $props()
|
let { data } = $props()
|
||||||
@ -38,51 +38,89 @@
|
|||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function onExtItemSelected(ext: SBExt) {
|
onMount(() => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// console.log("focus", listviewInputRef)
|
||||||
|
// listviewInputRef?.focus()
|
||||||
|
// }, 3_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
const inputAction: SvelteAction = (node) => {
|
||||||
|
// the node has been mounted in the DOM
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
// setup goes here
|
||||||
|
console.log("inputAction", node)
|
||||||
|
listviewInputRef?.focus()
|
||||||
|
return () => {
|
||||||
|
// teardown goes here
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
function docKeyDown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "/") {
|
||||||
|
listviewInputRef?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", docKeyDown)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", docKeyDown)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function onExtItemSelected(ext: ExtensionStoreListItem) {
|
||||||
goto(`./store/${ext.identifier}`)
|
goto(`./store/${ext.identifier}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onExtItemUpgrade(ext: SBExt) {
|
async function onExtItemUpgrade(ext: ExtensionStoreListItem) {
|
||||||
const res = await supabaseAPI.getLatestExtPublish(ext.identifier)
|
const { data, error, response } = await getExtensionsLatestPublishByIdentifier({
|
||||||
if (res.error)
|
path: {
|
||||||
|
identifier: ext.identifier
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (error)
|
||||||
return toast.error("Fail to get latest extension", {
|
return toast.error("Fail to get latest extension", {
|
||||||
description: res.error.message
|
description: error.error
|
||||||
})
|
})
|
||||||
const tarballUrl = res.data.tarball_path.startsWith("http")
|
const installExtras = await getInstallExtras(data?.metadata)
|
||||||
? res.data.tarball_path
|
|
||||||
: supabaseAPI.translateExtensionFilePathToUrl(res.data.tarball_path)
|
|
||||||
const installExtras = await getInstallExtras(
|
|
||||||
res.data as Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
|
|
||||||
)
|
|
||||||
return extensions
|
return extensions
|
||||||
.upgradeStoreExtension(ext.identifier, tarballUrl, installExtras)
|
.upgradeStoreExtension(ext.identifier, data.tarball_path, installExtras)
|
||||||
.then((newExt) => {
|
.then((newExt) => {
|
||||||
toast.success(`${ext.name} Upgraded to ${newExt.version}`)
|
toast.success(`${ext.name} Upgraded to ${newExt.version}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onExtItemInstall(ext: SBExt) {
|
async function onExtItemInstall(ext: ExtensionStoreListItem) {
|
||||||
const res = await supabaseAPI.getLatestExtPublish(ext.identifier)
|
const { data, error, response } = await getExtensionsLatestPublishByIdentifier({
|
||||||
if (res.error)
|
path: {
|
||||||
|
identifier: ext.identifier
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (error)
|
||||||
return toast.error("Fail to get latest extension", {
|
return toast.error("Fail to get latest extension", {
|
||||||
description: res.error.message
|
description: error.error
|
||||||
})
|
})
|
||||||
|
|
||||||
const tarballUrl = res.data.tarball_path.startsWith("http")
|
const installExtras = await getInstallExtras(data?.metadata)
|
||||||
? res.data.tarball_path
|
|
||||||
: supabaseAPI.translateExtensionFilePathToUrl(res.data.tarball_path)
|
|
||||||
const installExtras = await getInstallExtras(
|
|
||||||
res.data as Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
|
|
||||||
)
|
|
||||||
const installDir = await getExtensionsFolder()
|
const installDir = await getExtensionsFolder()
|
||||||
return extensions
|
return extensions
|
||||||
.installFromTarballUrl(tarballUrl, installDir, installExtras)
|
.installFromTarballUrl(data.tarball_path, installDir, installExtras)
|
||||||
.then(() => toast.success(`Plugin ${ext.name} Installed`))
|
.then(() => toast.success(`Plugin ${ext.name} Installed`))
|
||||||
.then(() =>
|
.then(() =>
|
||||||
supabaseAPI.incrementDownloads({
|
postExtensionsIncrementDownloads({
|
||||||
identifier: ext.identifier,
|
body: {
|
||||||
version: ext.version
|
identifier: ext.identifier,
|
||||||
|
version: ext.version
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
.then(({ error }) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +164,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={onkeydown} />
|
<svelte:window {onkeydown} />
|
||||||
{#snippet leftSlot()}
|
{#snippet leftSlot()}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@ -142,7 +180,7 @@
|
|||||||
<CustomCommandInput
|
<CustomCommandInput
|
||||||
bind:ref={listviewInputRef}
|
bind:ref={listviewInputRef}
|
||||||
autofocus
|
autofocus
|
||||||
placeholder="Type a command or search..."
|
placeholder="Type / to focus"
|
||||||
leftSlot={leftSlot as Snippet}
|
leftSlot={leftSlot as Snippet}
|
||||||
bind:value={$appState.searchTerm}
|
bind:value={$appState.searchTerm}
|
||||||
onkeydown={(e) => {
|
onkeydown={(e) => {
|
||||||
|
@ -1,43 +1,47 @@
|
|||||||
import { appConfig, extensions, installedStoreExts } from "@/stores"
|
import { appConfig, appState, extensions, installedStoreExts } from "@/stores"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { goHome } from "@/utils/route"
|
||||||
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
// import { supabaseAPI } from "@/supabase"
|
||||||
|
import type { ExtensionStoreListItem, ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
import { isExtPathInDev, isUpgradable } from "@kksh/extension"
|
import { isExtPathInDev, isUpgradable } from "@kksh/extension"
|
||||||
import { SBExt } from "@kksh/supabase/models"
|
import { getExtensionsStoreList } from "@kksh/sdk"
|
||||||
import { error } from "@sveltejs/kit"
|
import { toast } from "svelte-sonner"
|
||||||
import { derived, get, type Readable } from "svelte/store"
|
import { derived, get, type Readable } from "svelte/store"
|
||||||
import type { PageLoad } from "./$types"
|
import type { PageLoad } from "./$types"
|
||||||
|
|
||||||
export const load: PageLoad = async (): Promise<{
|
export const load: PageLoad = (): Promise<{
|
||||||
storeExtList: SBExt[]
|
storeExtList: ExtensionStoreListItem[]
|
||||||
installedStoreExts: Readable<ExtPackageJsonExtra[]>
|
installedStoreExts: Readable<ExtPackageJsonExtra[]>
|
||||||
installedExtsMap: Readable<Record<string, string>>
|
installedExtsMap: Readable<Record<string, string>>
|
||||||
upgradableExpsMap: Readable<Record<string, boolean>>
|
upgradableExpsMap: Readable<Record<string, boolean>>
|
||||||
}> => {
|
}> => {
|
||||||
const storeExtList = await supabaseAPI.getExtList()
|
appState.setFullScreenLoading(true)
|
||||||
// map identifier to extItem
|
return getExtensionsStoreList()
|
||||||
const storeExtsMap = Object.fromEntries(storeExtList.map((ext) => [ext.identifier, ext]))
|
.then(({ data: storeExtList, error, response }) => {
|
||||||
const _appConfig = get(appConfig)
|
storeExtList = storeExtList ?? []
|
||||||
// const installedStoreExts = derived(extensions, ($extensions) => {
|
if (error) {
|
||||||
// if (!_appConfig.extensionPath) return []
|
toast.error(`Failed to load extension store: ${error} (${response.status})`)
|
||||||
// return $extensions.filter((ext) => !isExtPathInDev(_appConfig.extensionPath!, ext.extPath))
|
goHome()
|
||||||
// })
|
}
|
||||||
// map installed extension identifier to version
|
const storeExtsMap = Object.fromEntries(storeExtList.map((ext) => [ext.identifier, ext]))
|
||||||
const installedExtsMap = derived(installedStoreExts, ($exts) =>
|
const installedExtsMap = derived(installedStoreExts, ($exts) =>
|
||||||
Object.fromEntries($exts.map((ext) => [ext.kunkun.identifier, ext.version]))
|
Object.fromEntries($exts.map((ext) => [ext.kunkun.identifier, ext.version]))
|
||||||
)
|
)
|
||||||
const upgradableExpsMap = derived(installedStoreExts, ($exts) =>
|
const upgradableExpsMap = derived(installedStoreExts, ($exts) =>
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
$exts.map((ext) => {
|
$exts.map((ext) => {
|
||||||
const dbExt: SBExt | undefined = storeExtsMap[ext.kunkun.identifier]
|
const dbExt: ExtensionStoreListItem | undefined = storeExtsMap[ext.kunkun.identifier]
|
||||||
return [ext.kunkun.identifier, dbExt ? isUpgradable(dbExt, ext.version) : false]
|
return [ext.kunkun.identifier, dbExt ? isUpgradable(dbExt, ext.version) : false]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return {
|
||||||
return {
|
storeExtList,
|
||||||
storeExtList,
|
installedStoreExts,
|
||||||
installedStoreExts,
|
installedExtsMap,
|
||||||
installedExtsMap,
|
upgradableExpsMap
|
||||||
upgradableExpsMap
|
}
|
||||||
}
|
})
|
||||||
|
.finally(() => {
|
||||||
|
appState.setFullScreenLoading(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
import { getExtensionsFolder } from "@/constants.js"
|
import { getExtensionsFolder } from "@/constants.js"
|
||||||
import { i18n } from "@/i18n.js"
|
import { i18n } from "@/i18n.js"
|
||||||
import { extensions, installedStoreExts } from "@/stores/extensions.js"
|
import { extensions, installedStoreExts } from "@/stores/extensions.js"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { DBExtension, ExtensionStoreListItem, ExtPackageJson, ExtPublish } from "@kksh/api/models"
|
||||||
import { goBack } from "@/utils/route.js"
|
import { postExtensionsIncrementDownloads } from "@kksh/sdk"
|
||||||
import { ExtPackageJson } from "@kksh/api/models"
|
|
||||||
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
|
||||||
import type { Tables } from "@kksh/supabase/types"
|
|
||||||
import { Button } from "@kksh/svelte5"
|
import { Button } from "@kksh/svelte5"
|
||||||
import { cn } from "@kksh/svelte5/utils"
|
import { cn } from "@kksh/svelte5/utils"
|
||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
@ -22,10 +19,8 @@
|
|||||||
import { getInstallExtras } from "./helper"
|
import { getInstallExtras } from "./helper"
|
||||||
|
|
||||||
const { data } = $props()
|
const { data } = $props()
|
||||||
const extPublish: Tables<"ext_publish"> & { metadata: ExtPublishMetadata } = $derived(
|
const extPublish = $derived(data.extPublish)
|
||||||
data.extPublish
|
const ext = $derived(data.ext)
|
||||||
)
|
|
||||||
const ext: Tables<"extensions"> = $derived(data.ext)
|
|
||||||
const manifest = $derived(data.manifest)
|
const manifest = $derived(data.manifest)
|
||||||
const installedExt = storeDerived(installedStoreExts, ($e) => {
|
const installedExt = storeDerived(installedStoreExts, ($e) => {
|
||||||
return $e.find((e) => e.kunkun.identifier === extPublish.identifier)
|
return $e.find((e) => e.kunkun.identifier === extPublish.identifier)
|
||||||
@ -77,26 +72,29 @@
|
|||||||
}, 500)
|
}, 500)
|
||||||
})
|
})
|
||||||
|
|
||||||
const demoImages = $derived(
|
const demoImages = $derived(extPublish.demo_images)
|
||||||
extPublish.demo_images.map((src) => supabaseAPI.translateExtensionFilePathToUrl(src))
|
|
||||||
)
|
|
||||||
|
|
||||||
async function onInstallSelected() {
|
async function onInstallSelected() {
|
||||||
loading.install = true
|
loading.install = true
|
||||||
const tarballUrl = extPublish.tarball_path.startsWith("http")
|
const installExtras = await getInstallExtras(extPublish.metadata)
|
||||||
? extPublish.tarball_path
|
|
||||||
: supabaseAPI.translateExtensionFilePathToUrl(extPublish.tarball_path)
|
|
||||||
const installExtras = await getInstallExtras(extPublish)
|
|
||||||
const installDir = await getExtensionsFolder()
|
const installDir = await getExtensionsFolder()
|
||||||
return extensions
|
return extensions
|
||||||
.installFromTarballUrl(tarballUrl, installDir, installExtras)
|
.installFromTarballUrl(extPublish.tarball_path, installDir, installExtras)
|
||||||
.then(() => toast.success(`Plugin ${extPublish.name} Installed`))
|
.then(() => toast.success(`Plugin ${extPublish.name} Installed`))
|
||||||
.then((loadedExt) => {
|
.then((loadedExt) => {
|
||||||
info(`Successfully installed ${extPublish.name}`)
|
info(`Successfully installed ${extPublish.name}`)
|
||||||
supabaseAPI.incrementDownloads({
|
postExtensionsIncrementDownloads({
|
||||||
identifier: extPublish.identifier,
|
body: {
|
||||||
version: extPublish.version
|
identifier: extPublish.identifier,
|
||||||
|
version: extPublish.version
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
.then(({ error }) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
showBtn.install = false
|
showBtn.install = false
|
||||||
showBtn.uninstall = true
|
showBtn.uninstall = true
|
||||||
})
|
})
|
||||||
@ -111,9 +109,8 @@
|
|||||||
|
|
||||||
function onUpgradeSelected() {
|
function onUpgradeSelected() {
|
||||||
loading.upgrade = true
|
loading.upgrade = true
|
||||||
const tarballUrl = supabaseAPI.translateExtensionFilePathToUrl(extPublish.tarball_path)
|
|
||||||
return extensions
|
return extensions
|
||||||
.upgradeStoreExtension(extPublish.identifier, tarballUrl)
|
.upgradeStoreExtension(extPublish.identifier, extPublish.tarball_path)
|
||||||
.then((newExt) => {
|
.then((newExt) => {
|
||||||
toast.success(
|
toast.success(
|
||||||
`${extPublish.name} Upgraded from ${$installedExt?.version} to ${newExt.version}`
|
`${extPublish.name} Upgraded from ${$installedExt?.version} to ${newExt.version}`
|
||||||
|
@ -1,53 +1,57 @@
|
|||||||
import { extensions } from "@/stores"
|
import { appState } from "@/stores"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { DBExtension, ExtPublish, KunkunExtManifest } from "@kksh/api/models"
|
||||||
import { KunkunExtManifest, type ExtPackageJsonExtra } from "@kksh/api/models"
|
import { getExtensionsByIdentifier, getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
|
||||||
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
|
||||||
import type { Tables } from "@kksh/supabase/types"
|
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
import { toast } from "svelte-sonner"
|
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import type { PageLoad } from "./$types"
|
import type { PageLoad } from "./$types"
|
||||||
|
|
||||||
export const load: PageLoad = async ({
|
export const load: PageLoad = ({
|
||||||
params
|
params
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
extPublish: Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
|
extPublish: ExtPublish
|
||||||
ext: Tables<"extensions">
|
ext: DBExtension
|
||||||
manifest: KunkunExtManifest
|
manifest: KunkunExtManifest
|
||||||
params: {
|
params: {
|
||||||
identifier: string
|
identifier: string
|
||||||
}
|
}
|
||||||
}> => {
|
}> => {
|
||||||
const { error: dbError, data: extPublish } = await supabaseAPI.getLatestExtPublish(
|
appState.setFullScreenLoading(true)
|
||||||
params.identifier
|
return getExtensionsLatestPublishByIdentifier({
|
||||||
)
|
path: {
|
||||||
const metadataParse = v.safeParse(ExtPublishMetadata, extPublish?.metadata ?? {})
|
identifier: params.identifier
|
||||||
if (dbError) {
|
}
|
||||||
return error(400, {
|
})
|
||||||
message: dbError.message
|
.then(async ({ data: extPublish, error: err, response }) => {
|
||||||
|
if (err || !extPublish) {
|
||||||
|
return error(response.status, {
|
||||||
|
message: "Failed to get extension publish"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
data: ext,
|
||||||
|
error: extError,
|
||||||
|
response: extRes
|
||||||
|
} = await getExtensionsByIdentifier({
|
||||||
|
path: {
|
||||||
|
identifier: params.identifier
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (extError || !ext) {
|
||||||
|
console.error(extError)
|
||||||
|
return error(extRes.status, {
|
||||||
|
message: extError.error || "Failed to get extension"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
extPublish: v.parse(ExtPublish, extPublish),
|
||||||
|
ext,
|
||||||
|
manifest: v.parse(KunkunExtManifest, extPublish.manifest),
|
||||||
|
params
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
.finally(() => {
|
||||||
const metadata = metadataParse.success ? metadataParse.output : {}
|
appState.setFullScreenLoading(false)
|
||||||
const parseManifest = v.safeParse(KunkunExtManifest, extPublish.manifest)
|
|
||||||
if (!parseManifest.success) {
|
|
||||||
const errMsg = "Invalid extension manifest, you may need to upgrade your app."
|
|
||||||
toast.error(errMsg)
|
|
||||||
throw error(400, errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: ext, error: extError } = await supabaseAPI.getExtension(params.identifier)
|
|
||||||
if (extError) {
|
|
||||||
return error(400, {
|
|
||||||
message: extError.message
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
extPublish: { ...extPublish, metadata },
|
|
||||||
ext,
|
|
||||||
params,
|
|
||||||
manifest: parseManifest.output
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const csr = true
|
export const csr = true
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
import type { ExtPublishMetadata } from "@kunkunapi/src/models"
|
||||||
import type { Tables } from "@kksh/supabase/types"
|
|
||||||
|
|
||||||
export async function getInstallExtras(
|
export async function getInstallExtras(extMetadata?: {
|
||||||
ext: Tables<"ext_publish"> & { metadata?: ExtPublishMetadata }
|
sourceType?: string
|
||||||
): Promise<{ overwritePackageJson?: string }> {
|
source?: string
|
||||||
|
}): Promise<{ overwritePackageJson?: string }> {
|
||||||
const extras: { overwritePackageJson?: string } = {}
|
const extras: { overwritePackageJson?: string } = {}
|
||||||
if (ext.metadata?.sourceType) {
|
if (extMetadata?.sourceType) {
|
||||||
if (ext.metadata?.sourceType === "jsr") {
|
if (extMetadata?.sourceType === "jsr") {
|
||||||
if (ext.metadata?.source) {
|
if (extMetadata?.source) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${ext.metadata.source}/package.json`)
|
const res = await fetch(`${extMetadata.source}/package.json`)
|
||||||
const pkgJsonContent = await res.text()
|
const pkgJsonContent = await res.text()
|
||||||
extras.overwritePackageJson = pkgJsonContent
|
extras.overwritePackageJson = pkgJsonContent
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<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"
|
||||||
|
import { decideKkrpcSerialization } from "@/utils/kkrpc"
|
||||||
import { goHome } from "@/utils/route"
|
import { goHome } from "@/utils/route"
|
||||||
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,
|
||||||
@ -18,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"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
} from "@kunkunapi/src/events"
|
} from "@kunkunapi/src/events"
|
||||||
import { emitTo } from "@tauri-apps/api/event"
|
import { emitTo } from "@tauri-apps/api/event"
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
|
import { info } from "@tauri-apps/plugin-log"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { IframeParentIO, RPCChannel } from "kkrpc/browser"
|
import { IframeParentIO, RPCChannel } from "kkrpc/browser"
|
||||||
import { ArrowLeftIcon, MoveIcon, RefreshCcwIcon, XIcon } from "lucide-svelte"
|
import { ArrowLeftIcon, MoveIcon, RefreshCcwIcon, XIcon } from "lucide-svelte"
|
||||||
@ -36,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
|
||||||
@ -63,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 () => {
|
||||||
@ -129,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()
|
||||||
}
|
}
|
||||||
@ -153,7 +154,7 @@
|
|||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goHome()
|
goHome()
|
||||||
} else {
|
} else {
|
||||||
appWin.close()
|
data.win?.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,16 +162,27 @@
|
|||||||
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)
|
||||||
const rpc = new RPCChannel(io, { expose: serverAPI2 })
|
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
|
||||||
|
info(
|
||||||
|
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
|
||||||
|
)
|
||||||
|
const rpc = new RPCChannel(io, {
|
||||||
|
expose: serverAPI2,
|
||||||
|
serialization: {
|
||||||
|
version: kkrpcSerialization
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
toast.warning("iframeRef.contentWindow not available")
|
toast.warning("iframeRef.contentWindow not available")
|
||||||
}
|
}
|
||||||
@ -183,7 +195,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
winExtMap.unregisterExtensionFromWindow(appWin.label)
|
winExtMap.unregisterExtensionFromWindow(data.win?.label ?? "")
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -196,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" />
|
||||||
@ -227,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"
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import { winExtMap } from "@/stores/winExtMap.js"
|
import { winExtMap } from "@/stores/winExtMap.js"
|
||||||
import { helperAPI } from "@/utils/helper.js"
|
import { helperAPI } from "@/utils/helper.js"
|
||||||
import { paste } from "@/utils/hotkey"
|
import { paste } from "@/utils/hotkey"
|
||||||
|
import { decideKkrpcSerialization } from "@/utils/kkrpc.js"
|
||||||
import {
|
import {
|
||||||
emitReloadOneExtension,
|
emitReloadOneExtension,
|
||||||
listenToFileDrop,
|
listenToFileDrop,
|
||||||
@ -12,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,
|
||||||
@ -28,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"
|
||||||
@ -43,7 +44,7 @@
|
|||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
import * as fs from "@tauri-apps/plugin-fs"
|
import * as fs from "@tauri-apps/plugin-fs"
|
||||||
import { readTextFile } from "@tauri-apps/plugin-fs"
|
import { readTextFile } from "@tauri-apps/plugin-fs"
|
||||||
import { debug } from "@tauri-apps/plugin-log"
|
import { debug, info } from "@tauri-apps/plugin-log"
|
||||||
import { platform } from "@tauri-apps/plugin-os"
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
||||||
@ -270,8 +271,15 @@
|
|||||||
} satisfies IApp
|
} satisfies IApp
|
||||||
}
|
}
|
||||||
const io = new WorkerParentIO(worker)
|
const io = new WorkerParentIO(worker)
|
||||||
|
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
|
||||||
|
info(
|
||||||
|
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
|
||||||
|
)
|
||||||
const rpc = new RPCChannel<typeof serverAPI2, TemplateUiCommand>(io, {
|
const rpc = new RPCChannel<typeof serverAPI2, TemplateUiCommand>(io, {
|
||||||
expose: serverAPI2
|
expose: serverAPI2,
|
||||||
|
serialization: {
|
||||||
|
version: kkrpcSerialization
|
||||||
|
}
|
||||||
})
|
})
|
||||||
workerAPI = rpc.getAPI()
|
workerAPI = rpc.getAPI()
|
||||||
await workerAPI.load()
|
await workerAPI.load()
|
||||||
@ -302,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"
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { ArrowRightIcon } from "lucide-svelte"
|
import { ArrowRightIcon } from "lucide-svelte"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
import { Step } from "./steps"
|
import { Step } from "./steps"
|
||||||
|
|
||||||
@ -26,18 +25,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (step === Step.DenoInstall) {
|
if (step > Step.GeneralSettings) {
|
||||||
if (denoPath) {
|
|
||||||
step++
|
|
||||||
}
|
|
||||||
} else if (step === Step.FFmpegInstall) {
|
|
||||||
if (ffmpegPath) {
|
|
||||||
step++
|
|
||||||
}
|
|
||||||
} else if (step > Step.FFmpegInstall) {
|
|
||||||
appConfig.setOnBoarded(true)
|
appConfig.setOnBoarded(true)
|
||||||
goto(i18n.resolveRoute("/app"))
|
goto(i18n.resolveRoute("/app"))
|
||||||
}
|
}
|
||||||
|
// if (step === Step.DenoInstall) {
|
||||||
|
// if (denoPath) {
|
||||||
|
// step++
|
||||||
|
// }
|
||||||
|
// } else if (step === Step.FFmpegInstall) {
|
||||||
|
// if (ffmpegPath) {
|
||||||
|
// step++
|
||||||
|
// }
|
||||||
|
// } else if (step > Step.FFmpegInstall) {
|
||||||
|
// appConfig.setOnBoarded(true)
|
||||||
|
// goto(i18n.resolveRoute("/app"))
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
124
apps/desktop/src/routes/app/troubleshooters/orm/+page.svelte
Normal file
124
apps/desktop/src/routes/app/troubleshooters/orm/+page.svelte
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
getAllCmds,
|
||||||
|
getAllExtensions,
|
||||||
|
getExtensionDataById,
|
||||||
|
getUniqueExtensionByIdentifier,
|
||||||
|
getUniqueExtensionByPath,
|
||||||
|
searchExtensionData,
|
||||||
|
updateCmdByID
|
||||||
|
} from "@kksh/drizzle/api"
|
||||||
|
import * as schema from "@kksh/drizzle/schema"
|
||||||
|
import { Button, Input } from "@kksh/svelte5"
|
||||||
|
import { CmdTypeEnum, Ext } from "@kunkunapi/src/models/extension"
|
||||||
|
import { SearchModeEnum, SQLSortOrderEnum } from "@kunkunapi/src/models/sql"
|
||||||
|
// import * as orm from "drizzle-orm"
|
||||||
|
import { Inspect } from "svelte-inspect-value"
|
||||||
|
import { toast } from "svelte-sonner"
|
||||||
|
import * as v from "valibot"
|
||||||
|
|
||||||
|
let searchText = $state("")
|
||||||
|
/* eslint-disable */
|
||||||
|
let data: any = $state(null)
|
||||||
|
let inspectTitle = $state("")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="container space-y-2">
|
||||||
|
<Button
|
||||||
|
onclick={async () => {
|
||||||
|
getAllCmds()
|
||||||
|
.then((cmds) => {
|
||||||
|
console.log(cmds)
|
||||||
|
data = cmds
|
||||||
|
inspectTitle = "All Commands"
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
toast.error("Failed to get all commands", {
|
||||||
|
description: "See console for more details"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get All Commands
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
getAllExtensions()
|
||||||
|
.then((exts) => {
|
||||||
|
data = exts
|
||||||
|
inspectTitle = "All Extensions"
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
toast.error("Failed to get all extensions", {
|
||||||
|
description: "See console for more details"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get All Extensions
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onclick={async () => {
|
||||||
|
// get all extensions with path not null
|
||||||
|
const exts = await getAllExtensions()
|
||||||
|
for (const ext of exts) {
|
||||||
|
if (ext.path === null) continue
|
||||||
|
const _ext = await getUniqueExtensionByIdentifier(ext.identifier)
|
||||||
|
console.log(_ext)
|
||||||
|
if (ext.path) {
|
||||||
|
const __ext = await getUniqueExtensionByPath(ext.path)
|
||||||
|
console.log(__ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// data = exts
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get Unique Extension By Identifier and Path
|
||||||
|
</Button>
|
||||||
|
<!-- <Button
|
||||||
|
onclick={async () => {
|
||||||
|
updateCmdByID({
|
||||||
|
cmdId: 1,
|
||||||
|
name: "google",
|
||||||
|
cmdType: CmdTypeEnum.QuickLink,
|
||||||
|
data: `{"link":"https://google.com/search?query={argument}","icon":{"type":"remote-url","value":"https://google.com/favicon.ico","invert":false}}`,
|
||||||
|
enabled: true
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update Command By ID
|
||||||
|
</Button> -->
|
||||||
|
<Button
|
||||||
|
onclick={async () => {
|
||||||
|
const _data = await getExtensionDataById(1, ["search_text", "data"])
|
||||||
|
data = _data
|
||||||
|
inspectTitle = "Extension Data"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get Extension Data By ID
|
||||||
|
</Button>
|
||||||
|
<form
|
||||||
|
class="flex gap-1"
|
||||||
|
onsubmit={async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const _data = await searchExtensionData({
|
||||||
|
extId: 1,
|
||||||
|
searchMode: SearchModeEnum.FTS,
|
||||||
|
searchText: searchText,
|
||||||
|
orderByCreatedAt: SQLSortOrderEnum.Desc,
|
||||||
|
limit: 10,
|
||||||
|
fields: ["search_text", "data"]
|
||||||
|
})
|
||||||
|
console.log(_data)
|
||||||
|
data = _data
|
||||||
|
inspectTitle = "Search Results"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input class="" bind:value={searchText} placeholder="Search Text" />
|
||||||
|
<Button class="" type="submit">Search Extension Data</Button>
|
||||||
|
</form>
|
||||||
|
<Inspect name={inspectTitle} value={data} expandLevel={2} />
|
||||||
|
</main>
|
@ -6,6 +6,7 @@
|
|||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
import { ArrowLeftIcon } from "lucide-svelte"
|
import { ArrowLeftIcon } from "lucide-svelte"
|
||||||
import AppWindow from "lucide-svelte/icons/app-window"
|
import AppWindow from "lucide-svelte/icons/app-window"
|
||||||
|
import DB from "lucide-svelte/icons/database"
|
||||||
import Loader from "lucide-svelte/icons/loader"
|
import Loader from "lucide-svelte/icons/loader"
|
||||||
import Network from "lucide-svelte/icons/network"
|
import Network from "lucide-svelte/icons/network"
|
||||||
|
|
||||||
@ -25,6 +26,11 @@
|
|||||||
title: m.troubleshooters_sidebar_mdns_debugger_title(),
|
title: m.troubleshooters_sidebar_mdns_debugger_title(),
|
||||||
url: i18n.resolveRoute("/app/troubleshooters/mdns-debugger"),
|
url: i18n.resolveRoute("/app/troubleshooters/mdns-debugger"),
|
||||||
icon: Network
|
icon: Network
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ORM",
|
||||||
|
url: i18n.resolveRoute("/app/troubleshooters/orm"),
|
||||||
|
icon: DB
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
let currentItem = $state(items.find((item) => window.location.pathname === item.url))
|
let currentItem = $state(items.find((item) => window.location.pathname === item.url))
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -86,12 +86,18 @@ const config: Config = {
|
|||||||
"caret-blink": {
|
"caret-blink": {
|
||||||
"0%,70%,100%": { opacity: "1" },
|
"0%,70%,100%": { opacity: "1" },
|
||||||
"20%,50%": { opacity: "0" }
|
"20%,50%": { opacity: "0" }
|
||||||
|
},
|
||||||
|
"border-beam": {
|
||||||
|
"100%": {
|
||||||
|
"offset-distance": "100%"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
"accordion-up": "accordion-up 0.2s ease-out",
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
"caret-blink": "caret-blink 1.25s ease-out infinite"
|
"caret-blink": "caret-blink 1.25s ease-out infinite",
|
||||||
|
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"moduleResolution": "bundler"
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true
|
||||||
},
|
},
|
||||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||||
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
||||||
|
@ -14,7 +14,7 @@ export default defineConfig(async () => ({
|
|||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
// 2. tauri expects a fixed port, fail if that port is not available
|
// 2. tauri expects a fixed port, fail if that port is not available
|
||||||
server: {
|
server: {
|
||||||
port: 1420,
|
port: 1566,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
host: host || false,
|
host: host || false,
|
||||||
hmr: host
|
hmr: host
|
||||||
|
@ -22,13 +22,15 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@changesets/cli": "^2.28.1",
|
"@changesets/cli": "^2.28.1",
|
||||||
|
"@hey-api/client-fetch": "^0.8.3",
|
||||||
"@iconify/svelte": "^4.2.0",
|
"@iconify/svelte": "^4.2.0",
|
||||||
|
"@kksh/sdk": "^0.0.3",
|
||||||
"@supabase/supabase-js": "^2.49.1",
|
"@supabase/supabase-js": "^2.49.1",
|
||||||
"@tauri-apps/api": "^2.3.0",
|
"@tauri-apps/api": "^2.3.0",
|
||||||
"@tauri-apps/cli": "^2.3.1",
|
"@tauri-apps/cli": "^2.3.1",
|
||||||
@ -48,7 +50,7 @@
|
|||||||
"tauri-plugin-keyring-api": "workspace:*",
|
"tauri-plugin-keyring-api": "workspace:*",
|
||||||
"tauri-plugin-network-api": "workspace:*",
|
"tauri-plugin-network-api": "workspace:*",
|
||||||
"tauri-plugin-system-info-api": "workspace:*",
|
"tauri-plugin-system-info-api": "workspace:*",
|
||||||
"valibot": "^1.0.0-beta.11",
|
"valibot": "^1.0.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
@ -53,3 +53,16 @@
|
|||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
||||||
- More Icon Options
|
- More Icon Options
|
||||||
|
|
||||||
|
## 0.1.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Upgrade kkrpc to 0.2.1, which uses superjson for serialization
|
||||||
|
|
||||||
|
## 0.1.7
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Upgrade kkrpc to 0.2.2, supports both json and superjson serialization, for backward compatibility
|
||||||
|
- The previous version breaks extension compatibility.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kksh/api",
|
"name": "@kksh/api",
|
||||||
"version": "0.1.5",
|
"version": "0.1.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -65,17 +65,17 @@
|
|||||||
"@tauri-apps/plugin-store": "^2.2.0",
|
"@tauri-apps/plugin-store": "^2.2.0",
|
||||||
"@tauri-apps/plugin-updater": "^2.3.0",
|
"@tauri-apps/plugin-updater": "^2.3.0",
|
||||||
"@tauri-apps/plugin-upload": "^2.2.1",
|
"@tauri-apps/plugin-upload": "^2.2.1",
|
||||||
"kkrpc": "^0.1.1",
|
"kkrpc": "^0.2.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"svelte-sonner": "^0.3.28",
|
"svelte-sonner": "^0.3.28",
|
||||||
"tauri-api-adapter": "^0.3.23",
|
"tauri-api-adapter": "^0.3.27",
|
||||||
"tauri-plugin-network-api": "2.0.5",
|
"tauri-plugin-network-api": "2.0.5",
|
||||||
"tauri-plugin-shellx-api": "^2.0.16",
|
"tauri-plugin-shellx-api": "^2.0.16",
|
||||||
"tauri-plugin-system-info-api": "2.0.8",
|
"tauri-plugin-system-info-api": "2.0.8",
|
||||||
"valibot": "^1.0.0-beta.10"
|
"valibot": "^1.0.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
|
@ -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 {
|
||||||
|
@ -169,8 +169,8 @@ export function constructShellApi(
|
|||||||
const onEvent = new Channel<CommandEvent<O>>()
|
const onEvent = new Channel<CommandEvent<O>>()
|
||||||
onEvent.onmessage = cb
|
onEvent.onmessage = cb
|
||||||
return invoke<number>("plugin:shellx|spawn", {
|
return invoke<number>("plugin:shellx|spawn", {
|
||||||
program: "deno",
|
program,
|
||||||
args: ["run", "/Users/hk/Dev/kunkun/deno.ts"],
|
args,
|
||||||
options,
|
options,
|
||||||
onEvent
|
onEvent
|
||||||
})
|
})
|
||||||
|
@ -1,437 +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"
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* 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
|
||||||
|
})
|
||||||
|
}
|
@ -1,91 +1,79 @@
|
|||||||
import {
|
import * as v from "valibot"
|
||||||
any,
|
|
||||||
array,
|
|
||||||
boolean,
|
|
||||||
date,
|
|
||||||
enum_,
|
|
||||||
function_,
|
|
||||||
nullable,
|
|
||||||
number,
|
|
||||||
object,
|
|
||||||
optional,
|
|
||||||
record,
|
|
||||||
string,
|
|
||||||
type InferOutput
|
|
||||||
} from "valibot"
|
|
||||||
import { Icon } from "./icon"
|
import { Icon } from "./icon"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map window label to extension
|
* Map window label to extension
|
||||||
*/
|
*/
|
||||||
export const ExtensionLabelMap = record(
|
export const ExtensionLabelMap = v.record(
|
||||||
string("Window label"),
|
v.string("Window label"),
|
||||||
object({
|
v.object({
|
||||||
path: string("Path to the extension"),
|
path: v.string("Path to the extension"),
|
||||||
processes: array(number()),
|
processes: v.array(v.number()),
|
||||||
dist: optional(nullable(string()))
|
dist: v.optional(v.nullable(v.string()))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
export type ExtensionLabelMap = InferOutput<typeof ExtensionLabelMap>
|
export type ExtensionLabelMap = v.InferOutput<typeof ExtensionLabelMap>
|
||||||
|
|
||||||
export const Ext = object({
|
export const Ext = v.object({
|
||||||
extId: number(),
|
extId: v.number(),
|
||||||
identifier: string(),
|
identifier: v.string(),
|
||||||
version: string(),
|
version: v.string(),
|
||||||
enabled: boolean(),
|
enabled: v.boolean(),
|
||||||
installed_at: string(),
|
installedAt: v.string(),
|
||||||
path: nullable(string()),
|
path: v.optional(v.nullable(v.string())),
|
||||||
data: nullable(any())
|
data: v.optional(v.any())
|
||||||
})
|
})
|
||||||
export type Ext = InferOutput<typeof Ext>
|
export type Ext = v.InferOutput<typeof Ext>
|
||||||
|
|
||||||
export enum CmdTypeEnum {
|
export const CmdTypeEnum = {
|
||||||
HeadlessWorker = "headless_worker",
|
HeadlessWorker: "headless_worker",
|
||||||
Builtin = "builtin",
|
Builtin: "builtin",
|
||||||
System = "system",
|
System: "system",
|
||||||
UiWorker = "ui_worker",
|
UiWorker: "ui_worker",
|
||||||
UiIframe = "ui_iframe",
|
UiIframe: "ui_iframe",
|
||||||
QuickLink = "quick_link",
|
QuickLink: "quick_link",
|
||||||
Remote = "remote"
|
Remote: "remote"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CmdType = enum_(CmdTypeEnum)
|
export const CmdType = v.picklist(Object.values(CmdTypeEnum))
|
||||||
export type CmdType = InferOutput<typeof CmdType>
|
|
||||||
export const ExtCmd = object({
|
export type CmdType = v.InferOutput<typeof CmdType>
|
||||||
cmdId: number(),
|
export const ExtCmd = v.object({
|
||||||
extId: number(),
|
cmdId: v.number(),
|
||||||
name: string(),
|
extId: v.number(),
|
||||||
|
name: v.string(),
|
||||||
type: CmdType,
|
type: CmdType,
|
||||||
data: string(),
|
data: v.string(),
|
||||||
alias: nullable(optional(string())),
|
alias: v.optional(v.nullable(v.string())),
|
||||||
hotkey: nullable(optional(string())),
|
hotkey: v.optional(v.nullable(v.string())),
|
||||||
enabled: boolean()
|
enabled: v.boolean()
|
||||||
})
|
})
|
||||||
export type ExtCmd = InferOutput<typeof ExtCmd>
|
|
||||||
|
|
||||||
export const QuickLinkCmd = object({
|
export type ExtCmd = v.InferOutput<typeof ExtCmd>
|
||||||
|
|
||||||
|
export const QuickLinkCmd = v.object({
|
||||||
...ExtCmd.entries,
|
...ExtCmd.entries,
|
||||||
data: object({ link: string(), icon: Icon })
|
data: v.object({ link: v.string(), icon: Icon })
|
||||||
})
|
})
|
||||||
export type QuickLinkCmd = InferOutput<typeof QuickLinkCmd>
|
export type QuickLinkCmd = v.InferOutput<typeof QuickLinkCmd>
|
||||||
|
|
||||||
export const ExtData = object({
|
export const ExtData = v.object({
|
||||||
dataId: number(),
|
dataId: v.number(),
|
||||||
extId: number(),
|
extId: v.number(),
|
||||||
dataType: string(),
|
dataType: v.string(),
|
||||||
data: optional(string()),
|
data: v.optional(v.string()),
|
||||||
searchText: optional(string()),
|
searchText: v.optional(v.string()),
|
||||||
createdAt: date(),
|
createdAt: v.date(),
|
||||||
updatedAt: date()
|
updatedAt: v.date()
|
||||||
})
|
})
|
||||||
export type ExtData = InferOutput<typeof ExtData>
|
export type ExtData = v.InferOutput<typeof ExtData>
|
||||||
|
|
||||||
export const SysCommand = object({
|
export const SysCommand = v.object({
|
||||||
name: string(),
|
name: v.string(),
|
||||||
value: string(),
|
value: v.string(),
|
||||||
icon: nullable(Icon),
|
icon: v.optional(v.nullable(Icon)),
|
||||||
keywords: nullable(array(string())),
|
keywords: v.optional(v.nullable(v.array(v.string()))),
|
||||||
function: function_(),
|
function: v.function_(),
|
||||||
confirmRequired: boolean()
|
confirmRequired: v.boolean()
|
||||||
})
|
})
|
||||||
export type SysCommand = InferOutput<typeof SysCommand>
|
export type SysCommand = v.InferOutput<typeof SysCommand>
|
||||||
|
@ -4,14 +4,21 @@ import { NodeName, NodeNameEnum } from "./constants"
|
|||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Icon */
|
/* Icon */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
export enum IconEnum {
|
// export enum IconEnum {
|
||||||
Iconify = "iconify",
|
// Iconify = "iconify",
|
||||||
RemoteUrl = "remote-url",
|
// RemoteUrl = "remote-url",
|
||||||
Svg = "svg",
|
// Svg = "svg",
|
||||||
Base64PNG = "base64-png",
|
// Base64PNG = "base64-png",
|
||||||
Text = "text"
|
// Text = "text"
|
||||||
|
// }
|
||||||
|
export const IconEnum = {
|
||||||
|
Iconify: "iconify",
|
||||||
|
RemoteUrl: "remote-url",
|
||||||
|
Svg: "svg",
|
||||||
|
Base64PNG: "base64-png",
|
||||||
|
Text: "text"
|
||||||
}
|
}
|
||||||
export const IconType = v.enum_(IconEnum)
|
export const IconType = v.picklist(Object.values(IconEnum))
|
||||||
export type IconType = v.InferOutput<typeof IconType>
|
export type IconType = v.InferOutput<typeof IconType>
|
||||||
|
|
||||||
export type Icon = {
|
export type Icon = {
|
||||||
|
@ -13,3 +13,4 @@ export { Markdown as MarkdownSchema } from "../ui/template/schema/markdown"
|
|||||||
export * as ListSchema from "../ui/template/schema/list"
|
export * as ListSchema from "../ui/template/schema/list"
|
||||||
export * as FormSchema from "../ui/template/schema/form"
|
export * as FormSchema from "../ui/template/schema/form"
|
||||||
export * from "./file-transfer"
|
export * from "./file-transfer"
|
||||||
|
export * from "./server"
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { FsPermissionSchema } from "tauri-api-adapter/permissions"
|
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import {
|
import {
|
||||||
AllKunkunPermission,
|
AllKunkunPermission,
|
||||||
@ -8,29 +7,29 @@ import {
|
|||||||
OpenPermissionScopedSchema,
|
OpenPermissionScopedSchema,
|
||||||
ShellPermissionScopedSchema
|
ShellPermissionScopedSchema
|
||||||
} from "../permissions"
|
} from "../permissions"
|
||||||
import { CmdType } from "./extension"
|
import { CmdType, CmdTypeEnum } from "./extension"
|
||||||
import { Icon } from "./icon"
|
import { BaseIcon as Icon } from "./icon"
|
||||||
|
|
||||||
export enum OSPlatformEnum {
|
export const OSPlatformEnum = {
|
||||||
linux = "linux",
|
linux: "linux",
|
||||||
macos = "macos",
|
macos: "macos",
|
||||||
windows = "windows"
|
windows: "windows"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OSPlatform = v.enum_(OSPlatformEnum)
|
export const OSPlatform = v.picklist(Object.values(OSPlatformEnum))
|
||||||
export type OSPlatform = v.InferOutput<typeof OSPlatform>
|
export type OSPlatform = v.InferOutput<typeof OSPlatform>
|
||||||
const allPlatforms = Object.values(OSPlatformEnum)
|
|
||||||
export const TriggerCmd = v.object({
|
export const TriggerCmd = v.object({
|
||||||
type: v.union([v.literal("text"), v.literal("regex")]),
|
type: v.union([v.literal("text"), v.literal("regex")]),
|
||||||
value: v.string()
|
value: v.string()
|
||||||
})
|
})
|
||||||
export type TriggerCmd = v.InferOutput<typeof TriggerCmd>
|
export type TriggerCmd = v.InferOutput<typeof TriggerCmd>
|
||||||
export enum TitleBarStyleEnum {
|
export const TitleBarStyleEnum = {
|
||||||
"visible" = "visible",
|
visible: "visible",
|
||||||
"transparent" = "transparent",
|
transparent: "transparent",
|
||||||
"overlay" = "overlay"
|
overlay: "overlay"
|
||||||
}
|
}
|
||||||
export const TitleBarStyle = v.enum_(TitleBarStyleEnum)
|
export type TitleBarStyle = v.InferOutput<typeof TitleBarStyle>
|
||||||
|
export const TitleBarStyle = v.picklist(Object.values(TitleBarStyleEnum))
|
||||||
// JS new WebViewWindow only accepts lowercase, while manifest loaded from Rust is capitalized. I run toLowerCase() on the value before passing it to the WebViewWindow.
|
// JS new WebViewWindow only accepts lowercase, while manifest loaded from Rust is capitalized. I run toLowerCase() on the value before passing it to the WebViewWindow.
|
||||||
// This lowercase title bar style schema is used to validate and set the type so TypeScript won't complaint
|
// This lowercase title bar style schema is used to validate and set the type so TypeScript won't complaint
|
||||||
// export const TitleBarStyleAllLower = z.enum(["visible", "transparent", "overlay"]);
|
// export const TitleBarStyleAllLower = z.enum(["visible", "transparent", "overlay"]);
|
||||||
@ -71,37 +70,33 @@ export const WindowConfig = v.object({
|
|||||||
export type WindowConfig = v.InferOutput<typeof WindowConfig>
|
export type WindowConfig = v.InferOutput<typeof WindowConfig>
|
||||||
export const BaseCmd = v.object({
|
export const BaseCmd = v.object({
|
||||||
main: v.string("HTML file to load, e.g. dist/index.html"),
|
main: v.string("HTML file to load, e.g. dist/index.html"),
|
||||||
description: v.optional(v.nullable(v.string("Description of the Command"), ""), ""),
|
description: v.optional(v.nullable(v.string("Description of the Command"), "")),
|
||||||
name: v.string("Name of the command"),
|
name: v.string("Name of the command"),
|
||||||
cmds: v.array(TriggerCmd, "Commands to trigger the UI"),
|
cmds: v.array(TriggerCmd, "Commands to trigger the UI"),
|
||||||
icon: v.optional(Icon),
|
icon: v.optional(Icon),
|
||||||
platforms: v.optional(
|
platforms: v.optional(
|
||||||
v.nullable(
|
v.array(OSPlatform, "Platforms available on. Leave empty for all platforms.")
|
||||||
v.array(OSPlatform, "Platforms available on. Leave empty for all platforms."),
|
|
||||||
allPlatforms
|
|
||||||
),
|
|
||||||
allPlatforms
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
export const CustomUiCmd = v.object({
|
export const CustomUiCmd = v.object({
|
||||||
...BaseCmd.entries,
|
...BaseCmd.entries,
|
||||||
type: v.optional(CmdType, CmdType.enum.UiIframe),
|
|
||||||
dist: v.string("Dist folder to load, e.g. dist, build, out"),
|
dist: v.string("Dist folder to load, e.g. dist, build, out"),
|
||||||
devMain: v.string(
|
devMain: v.string(
|
||||||
"URL to load in development to support live reload, e.g. http://localhost:5173/"
|
"URL to load in development to support live reload, e.g. http://localhost:5173/"
|
||||||
),
|
),
|
||||||
window: v.optional(v.nullable(WindowConfig))
|
window: v.optional(v.nullable(WindowConfig)),
|
||||||
|
type: v.optional(CmdType, CmdTypeEnum.UiIframe)
|
||||||
})
|
})
|
||||||
export type CustomUiCmd = v.InferOutput<typeof CustomUiCmd>
|
export type CustomUiCmd = v.InferOutput<typeof CustomUiCmd>
|
||||||
|
|
||||||
export const TemplateUiCmd = v.object({
|
export const TemplateUiCmd = v.object({
|
||||||
...BaseCmd.entries,
|
...BaseCmd.entries,
|
||||||
type: v.optional(CmdType, CmdType.enum.UiWorker),
|
type: v.optional(CmdType, CmdTypeEnum.UiWorker),
|
||||||
window: v.optional(v.nullable(WindowConfig))
|
window: v.optional(v.nullable(WindowConfig))
|
||||||
})
|
})
|
||||||
export const HeadlessCmd = v.object({
|
export const HeadlessCmd = v.object({
|
||||||
...BaseCmd.entries,
|
...BaseCmd.entries,
|
||||||
type: v.optional(CmdType, CmdType.enum.HeadlessWorker)
|
type: v.optional(CmdType, CmdTypeEnum.HeadlessWorker)
|
||||||
})
|
})
|
||||||
export type HeadlessCmd = v.InferOutput<typeof HeadlessCmd>
|
export type HeadlessCmd = v.InferOutput<typeof HeadlessCmd>
|
||||||
export type TemplateUiCmd = v.InferOutput<typeof TemplateUiCmd>
|
export type TemplateUiCmd = v.InferOutput<typeof TemplateUiCmd>
|
||||||
@ -195,7 +190,8 @@ export const ExtPackageJsonExtra = v.object({
|
|||||||
...ExtPackageJson.entries,
|
...ExtPackageJson.entries,
|
||||||
...{
|
...{
|
||||||
extPath: v.string(),
|
extPath: v.string(),
|
||||||
extFolderName: v.string()
|
extFolderName: v.string(),
|
||||||
|
apiVersion: v.optional(v.string("API version of the extension"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
91
packages/api/src/models/server.ts
Normal file
91
packages/api/src/models/server.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import * as v from "valibot"
|
||||||
|
import { BaseIcon } from "./icon"
|
||||||
|
import { ExtPackageJson, KunkunExtManifest } from "./manifest"
|
||||||
|
|
||||||
|
export const ExtPublishSourceTypeEnum = {
|
||||||
|
jsr: "jsr",
|
||||||
|
npm: "npm"
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const ExtPublishMetadata = v.object({
|
||||||
|
source: v.optional(v.string("Source of the extension (e.g. url to package)")),
|
||||||
|
sourceType: v.optional(v.picklist(Object.values(ExtPublishSourceTypeEnum))),
|
||||||
|
rekorLogIndex: v.optional(v.string("Rekor log index of the extension")),
|
||||||
|
git: v.optional(
|
||||||
|
v.object({
|
||||||
|
githubActionInvocationId: v.string("GitHub action invocation ID"),
|
||||||
|
repo: v.string("GitHub repo of the extension"),
|
||||||
|
owner: v.string("GitHub owner of the extension"),
|
||||||
|
commit: v.string("Commit hash of the extension"),
|
||||||
|
workflowPath: v.string("Workflow path of the extension"),
|
||||||
|
repoNodeId: v.optional(
|
||||||
|
v.string("GitHub repo node ID of the extension (a string, not the number id)")
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
export type ExtPublishMetadata = v.InferOutput<typeof ExtPublishMetadata>
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Correspond to `extensions` table in supabase, missing a few fields
|
||||||
|
*/
|
||||||
|
export const ExtensionStoreListItem = v.object({
|
||||||
|
identifier: v.string(),
|
||||||
|
name: v.string(),
|
||||||
|
created_at: v.string(),
|
||||||
|
downloads: v.number(),
|
||||||
|
author_id: v.string(),
|
||||||
|
short_description: v.string(),
|
||||||
|
long_description: v.string(),
|
||||||
|
version: v.string(),
|
||||||
|
api_version: v.optional(v.string()),
|
||||||
|
icon: BaseIcon
|
||||||
|
})
|
||||||
|
|
||||||
|
export type ExtensionStoreListItem = v.InferOutput<typeof ExtensionStoreListItem>
|
||||||
|
|
||||||
|
export const PublishStateEnum = {
|
||||||
|
public: "public",
|
||||||
|
pending: "pending",
|
||||||
|
under_review: "under_review",
|
||||||
|
private: "private"
|
||||||
|
}
|
||||||
|
export const ExtensionPublishState = v.picklist(Object.values(PublishStateEnum))
|
||||||
|
|
||||||
|
export const ExtPublish = v.object({
|
||||||
|
id: v.number(),
|
||||||
|
name: v.string(),
|
||||||
|
tarball_path: v.string(),
|
||||||
|
created_at: v.string(),
|
||||||
|
version: v.string(),
|
||||||
|
manifest: KunkunExtManifest,
|
||||||
|
shasum: v.string(),
|
||||||
|
tarball_size: v.number(),
|
||||||
|
unpacked_size: v.nullable(v.number()),
|
||||||
|
cmd_count: v.number(),
|
||||||
|
identifier: v.string(),
|
||||||
|
downloads: v.number(),
|
||||||
|
demo_images: v.array(v.string()),
|
||||||
|
api_version: v.nullable(v.string()),
|
||||||
|
extension_state: ExtensionPublishState,
|
||||||
|
package_json: ExtPackageJson,
|
||||||
|
metadata: ExtPublishMetadata,
|
||||||
|
readme: v.nullable(v.string())
|
||||||
|
})
|
||||||
|
export type ExtPublish = v.InferOutput<typeof ExtPublish>
|
||||||
|
|
||||||
|
export const DBExtension = v.object({
|
||||||
|
api_version: v.string(),
|
||||||
|
author_id: v.string(),
|
||||||
|
created_at: v.string(),
|
||||||
|
downloads: v.number(),
|
||||||
|
icon: BaseIcon,
|
||||||
|
identifier: v.string(),
|
||||||
|
long_description: v.nullable(v.string()),
|
||||||
|
name: v.string(),
|
||||||
|
readme: v.nullable(v.string()),
|
||||||
|
short_description: v.string(),
|
||||||
|
tarball_size: v.nullable(v.number()),
|
||||||
|
version: v.string()
|
||||||
|
})
|
||||||
|
export type DBExtension = v.InferOutput<typeof DBExtension>
|
@ -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>
|
||||||
|
@ -2,7 +2,7 @@ import { DenoIo, RPCChannel } from "kkrpc"
|
|||||||
|
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
export function expose(api: Record<string, any>) {
|
export function expose(api: Record<string, any>) {
|
||||||
const stdio = new DenoIo(Deno.stdin.readable, Deno.stdout.writable)
|
const stdio = new DenoIo(Deno.stdin.readable)
|
||||||
const channel = new RPCChannel(stdio, { expose: api })
|
const channel = new RPCChannel(stdio, { expose: api })
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export const breakingChangesVersionCheckpoints = [
|
|||||||
const checkpointVersions = breakingChangesVersionCheckpoints.map((c) => c.version)
|
const checkpointVersions = breakingChangesVersionCheckpoints.map((c) => c.version)
|
||||||
const sortedCheckpointVersions = sort(checkpointVersions)
|
const sortedCheckpointVersions = sort(checkpointVersions)
|
||||||
|
|
||||||
export const version = "0.1.5"
|
export const version = "0.1.7"
|
||||||
|
|
||||||
export function isVersionBetween(v: string, start: string, end: string) {
|
export function isVersionBetween(v: string, start: string, end: string) {
|
||||||
const vCleaned = clean(v)
|
const vCleaned = clean(v)
|
||||||
|
@ -4,7 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rusqlite = { version = "0.31.0", features = ["bundled-sqlcipher-vendored-openssl"] }
|
rusqlite = { version = "0.31.0", features = [
|
||||||
|
"bundled-sqlcipher-vendored-openssl",
|
||||||
|
] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
|
@ -2,6 +2,7 @@ pub mod models;
|
|||||||
pub mod schema;
|
pub mod schema;
|
||||||
use models::{CmdType, ExtDataField, ExtDataSearchQuery, SearchMode};
|
use models::{CmdType, ExtDataField, ExtDataSearchQuery, SearchMode};
|
||||||
use rusqlite::{params, params_from_iter, Connection, Result, ToSql};
|
use rusqlite::{params, params_from_iter, Connection, Result, ToSql};
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -123,6 +124,97 @@ impl JarvisDB {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select(&self, query: String, values: Vec<JsonValue>) -> Result<Vec<JsonValue>> {
|
||||||
|
println!("DB selecting: {}", query);
|
||||||
|
println!("DB selecting values: {:?}", values);
|
||||||
|
let mut stmt = self.conn.prepare(&query)?;
|
||||||
|
|
||||||
|
// Convert JsonValue parameters to appropriate types for rusqlite
|
||||||
|
let mut params: Vec<Box<dyn ToSql>> = Vec::new();
|
||||||
|
for value in values {
|
||||||
|
if value.is_null() {
|
||||||
|
params.push(Box::new(Option::<String>::None));
|
||||||
|
} else if value.is_string() {
|
||||||
|
params.push(Box::new(value.as_str().unwrap().to_owned()));
|
||||||
|
} else if let Some(number) = value.as_number() {
|
||||||
|
if number.is_i64() {
|
||||||
|
params.push(Box::new(number.as_i64().unwrap()));
|
||||||
|
} else if number.is_u64() {
|
||||||
|
params.push(Box::new(number.as_u64().unwrap() as i64));
|
||||||
|
} else {
|
||||||
|
params.push(Box::new(number.as_f64().unwrap()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.push(Box::new(value.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get column names from statement
|
||||||
|
let column_names: Vec<String> = (0..stmt.column_count())
|
||||||
|
.map(|i| stmt.column_name(i).unwrap().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Execute the query with the converted parameters and map results
|
||||||
|
let rows = stmt.query_map(params_from_iter(params.iter().map(|p| p.as_ref())), |row| {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for i in 0..column_names.len() {
|
||||||
|
let value: Value = match row.get_ref(i)? {
|
||||||
|
rusqlite::types::ValueRef::Null => Value::Null,
|
||||||
|
rusqlite::types::ValueRef::Integer(i) => Value::Number(i.into()),
|
||||||
|
rusqlite::types::ValueRef::Real(r) => Value::Number(
|
||||||
|
serde_json::Number::from_f64(r).unwrap_or(serde_json::Number::from(0)),
|
||||||
|
),
|
||||||
|
rusqlite::types::ValueRef::Text(t) => {
|
||||||
|
Value::String(String::from_utf8_lossy(t).into_owned())
|
||||||
|
}
|
||||||
|
rusqlite::types::ValueRef::Blob(b) => {
|
||||||
|
Value::String(String::from_utf8_lossy(b).into_owned())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
result.push(value);
|
||||||
|
}
|
||||||
|
Ok(Value::Array(result))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for row in rows {
|
||||||
|
results.push(row?);
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(&self, query: &str, values: Vec<JsonValue>) -> Result<(u64, i64)> {
|
||||||
|
let mut stmt = self.conn.prepare(query)?;
|
||||||
|
|
||||||
|
// Convert JsonValue parameters to appropriate types for rusqlite
|
||||||
|
let mut params: Vec<Box<dyn ToSql>> = Vec::new();
|
||||||
|
for value in values {
|
||||||
|
if value.is_null() {
|
||||||
|
params.push(Box::new(Option::<String>::None));
|
||||||
|
} else if value.is_string() {
|
||||||
|
params.push(Box::new(value.as_str().unwrap().to_owned()));
|
||||||
|
} else if let Some(number) = value.as_number() {
|
||||||
|
if number.is_i64() {
|
||||||
|
params.push(Box::new(number.as_i64().unwrap()));
|
||||||
|
} else if number.is_u64() {
|
||||||
|
params.push(Box::new(number.as_u64().unwrap() as i64));
|
||||||
|
} else {
|
||||||
|
params.push(Box::new(number.as_f64().unwrap()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.push(Box::new(value.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the query with the converted parameters
|
||||||
|
let rows_affected = stmt.execute(params_from_iter(params.iter().map(|p| p.as_ref())))?;
|
||||||
|
|
||||||
|
// Get the last insert rowid
|
||||||
|
let last_insert_id = self.conn.last_insert_rowid();
|
||||||
|
|
||||||
|
Ok((rows_affected as u64, last_insert_id))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_all_extensions(&self) -> Result<Vec<models::Ext>> {
|
pub fn get_all_extensions(&self) -> Result<Vec<models::Ext>> {
|
||||||
let mut stmt = self.conn.prepare(
|
let mut stmt = self.conn.prepare(
|
||||||
"SELECT ext_id, identifier, path, data, version, enabled, installed_at FROM extensions",
|
"SELECT ext_id, identifier, path, data, version, enabled, installed_at FROM extensions",
|
||||||
@ -1092,59 +1184,69 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_command_aliases_crud() {
|
fn test_select_and_execute() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
let db_path = dir.path().join("test.db");
|
let db_path = dir.path().join("test.db");
|
||||||
let db = JarvisDB::new(&db_path, None).unwrap();
|
let db = JarvisDB::new(&db_path, None).unwrap();
|
||||||
db.init().unwrap();
|
db.init().unwrap();
|
||||||
|
|
||||||
// Create an extension and command first
|
// Create a simple todo table
|
||||||
db.create_extension("test", "0.1.0", true, None, None)
|
db.execute(
|
||||||
|
"CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT, completed BOOLEAN)",
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Test execute with INSERT
|
||||||
|
let (rows_affected, last_id) = db
|
||||||
|
.execute(
|
||||||
|
"INSERT INTO todos (title, completed) VALUES (?1, ?2)",
|
||||||
|
vec![json!("Buy groceries"), json!(false)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ext = db
|
assert_eq!(rows_affected, 1);
|
||||||
.get_unique_extension_by_identifier("test")
|
assert_eq!(last_id, 1);
|
||||||
.unwrap()
|
|
||||||
|
// Test select with basic query
|
||||||
|
let results = db
|
||||||
|
.select(
|
||||||
|
"SELECT title, completed FROM todos WHERE id = ?1".to_string(),
|
||||||
|
vec![json!(1)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.create_command(ext.ext_id, "test", CmdType::Iframe, "{}", true, None, None)
|
assert_eq!(results.len(), 1);
|
||||||
|
assert_eq!(results[0][0], "Buy groceries");
|
||||||
|
assert_eq!(results[0][1], "false");
|
||||||
|
|
||||||
|
// Test execute with UPDATE
|
||||||
|
let (rows_affected, _) = db
|
||||||
|
.execute(
|
||||||
|
"UPDATE todos SET completed = ?1 WHERE id = ?2",
|
||||||
|
vec![json!(true), json!(1)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cmd = db.get_command_by_id(1).unwrap().unwrap();
|
assert_eq!(rows_affected, 1);
|
||||||
|
|
||||||
// Test creating command alias
|
// Verify the update with select
|
||||||
db.create_command_alias(cmd.cmd_id, "test_alias").unwrap();
|
let results = db
|
||||||
db.create_command_alias(cmd.cmd_id, "another_alias")
|
.select(
|
||||||
|
"SELECT completed FROM todos WHERE id = ?1".to_string(),
|
||||||
|
vec![json!(1)],
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(results[0][0], "true");
|
||||||
|
|
||||||
// Test getting command aliases by cmd_id
|
// Test execute with DELETE
|
||||||
let aliases = db.get_command_aliases_by_cmd_id(cmd.cmd_id).unwrap();
|
let (rows_affected, _) = db
|
||||||
println!("aliases: {:#?}", aliases);
|
.execute("DELETE FROM todos WHERE id = ?1", vec![json!(1)])
|
||||||
assert_eq!(aliases.len(), 2);
|
|
||||||
assert_eq!(aliases[0].alias, "another_alias");
|
|
||||||
assert_eq!(aliases[1].alias, "test_alias");
|
|
||||||
|
|
||||||
// Test getting command alias by id
|
|
||||||
let alias = db
|
|
||||||
.get_command_alias_by_id(aliases[0].alias_id)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(alias.alias, "another_alias");
|
assert_eq!(rows_affected, 1);
|
||||||
|
|
||||||
// Test searching command aliases
|
// Verify the deletion
|
||||||
let search_results = db.search_command_aliases("test").unwrap();
|
let results = db
|
||||||
println!("search_results: {:#?}", search_results);
|
.select("SELECT COUNT(*) as count FROM todos".to_string(), vec![])
|
||||||
assert_eq!(search_results.len(), 1);
|
.unwrap();
|
||||||
assert_eq!(search_results[0].alias, "test_alias");
|
assert_eq!(results[0][0], 0);
|
||||||
|
|
||||||
// Test deleting command alias
|
|
||||||
db.delete_command_alias_by_id(aliases[0].alias_id).unwrap();
|
|
||||||
let aliases = db.get_command_aliases_by_cmd_id(cmd.cmd_id).unwrap();
|
|
||||||
assert_eq!(aliases.len(), 1);
|
|
||||||
assert_eq!(aliases[0].alias, "test_alias");
|
|
||||||
|
|
||||||
// Test command usage tracking
|
|
||||||
db.update_command_usage(cmd.cmd_id).unwrap();
|
|
||||||
let usage = db.get_command_usage(cmd.cmd_id).unwrap().unwrap();
|
|
||||||
assert_eq!(usage.0, 1); // usage_count should be 1
|
|
||||||
assert!(!usage.1.is_empty()); // last_used_at should be set
|
|
||||||
|
|
||||||
fs::remove_file(&db_path).unwrap();
|
fs::remove_file(&db_path).unwrap();
|
||||||
}
|
}
|
||||||
|
34
packages/drizzle/.gitignore
vendored
Normal file
34
packages/drizzle/.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
35
packages/drizzle/README.md
Normal file
35
packages/drizzle/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# drizzle
|
||||||
|
|
||||||
|
- Only use `pull` to generate the schema from existing database.
|
||||||
|
- Don't `migrate` or `push`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DB_FILE_NAME="~/Library/Application Support/sh.kunkun.desktop/kk.dev.sqlite"
|
||||||
|
bunx drizzle-kit pull
|
||||||
|
```
|
||||||
|
|
||||||
|
We are using sqlite with fts5, which drizzle doesn't support yet, so pushing the schema will destroy the existing schema.
|
||||||
|
|
||||||
|
We only use pulled schema to generate sql queries.
|
||||||
|
|
||||||
|
## Update Schema
|
||||||
|
|
||||||
|
After `drizzle-kit pull` the schema may have problem with JSON type and boolean type.
|
||||||
|
|
||||||
|
Will need to manually update the following
|
||||||
|
|
||||||
|
### JSON
|
||||||
|
|
||||||
|
```diff lang="ts"
|
||||||
|
+ data: text({ mode: "json" }).notNull(),
|
||||||
|
+ metadata: text({ mode: "json" }),
|
||||||
|
- data: numeric().notNull(),
|
||||||
|
- metadata: numeric(),
|
||||||
|
```
|
||||||
|
|
||||||
|
### Boolean
|
||||||
|
|
||||||
|
```diff lang="ts"
|
||||||
|
+ enabled: integer({ mode: "boolean" }),
|
||||||
|
- enabled: numeric().default(sql`(TRUE)`),
|
||||||
|
```
|
15
packages/drizzle/drizzle.config.ts
Normal file
15
packages/drizzle/drizzle.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import "dotenv/config"
|
||||||
|
import { defineConfig } from "drizzle-kit"
|
||||||
|
|
||||||
|
if (!process.env.DB_FILE_NAME) {
|
||||||
|
throw new Error("DB_FILE_NAME is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
out: "./drizzle",
|
||||||
|
// schema: "./src/db/schema.ts",
|
||||||
|
dialect: "sqlite",
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.DB_FILE_NAME
|
||||||
|
}
|
||||||
|
})
|
71
packages/drizzle/drizzle/0000_colossal_jocasta.sql
Normal file
71
packages/drizzle/drizzle/0000_colossal_jocasta.sql
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
-- Current sql file was generated after introspecting the database
|
||||||
|
-- If you want to run this migration please uncomment this code before executing migrations
|
||||||
|
/*
|
||||||
|
CREATE TABLE `schema_version` (
|
||||||
|
`version` integer NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extensions` (
|
||||||
|
`ext_id` integer PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`identifier` text NOT NULL,
|
||||||
|
`version` text NOT NULL,
|
||||||
|
`enabled` numeric DEFAULT (TRUE),
|
||||||
|
`path` text,
|
||||||
|
`data` numeric,
|
||||||
|
`installed_at` numeric DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `commands` (
|
||||||
|
`cmd_id` integer PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`ext_id` integer NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`enabled` numeric DEFAULT (TRUE),
|
||||||
|
`alias` text,
|
||||||
|
`hotkey` text,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
`data` numeric,
|
||||||
|
FOREIGN KEY (`ext_id`) REFERENCES `extensions`(`ext_id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data` (
|
||||||
|
`data_id` integer PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`ext_id` integer NOT NULL,
|
||||||
|
`data_type` text NOT NULL,
|
||||||
|
`data` numeric NOT NULL,
|
||||||
|
`metadata` numeric,
|
||||||
|
`search_text` text,
|
||||||
|
`created_at` numeric DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
`updated_at` numeric DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
FOREIGN KEY (`ext_id`) REFERENCES `extensions`(`ext_id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data_fts` (
|
||||||
|
`data_id` numeric,
|
||||||
|
`search_text` numeric,
|
||||||
|
`extension_data_fts` numeric,
|
||||||
|
`rank` numeric
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data_fts_data` (
|
||||||
|
`id` integer PRIMARY KEY,
|
||||||
|
`block` blob
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data_fts_idx` (
|
||||||
|
`segid` numeric NOT NULL,
|
||||||
|
`term` numeric NOT NULL,
|
||||||
|
`pgno` numeric,
|
||||||
|
PRIMARY KEY(`segid`, `term`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data_fts_docsize` (
|
||||||
|
`id` integer PRIMARY KEY,
|
||||||
|
`sz` blob
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `extension_data_fts_config` (
|
||||||
|
`k` numeric PRIMARY KEY NOT NULL,
|
||||||
|
`v` numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
*/
|
405
packages/drizzle/drizzle/meta/0000_snapshot.json
Normal file
405
packages/drizzle/drizzle/meta/0000_snapshot.json
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
{
|
||||||
|
"id": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"prevId": "",
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"tables": {
|
||||||
|
"schema_version": {
|
||||||
|
"name": "schema_version",
|
||||||
|
"columns": {
|
||||||
|
"version": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"name": "extensions",
|
||||||
|
"columns": {
|
||||||
|
"ext_id": {
|
||||||
|
"autoincrement": true,
|
||||||
|
"name": "ext_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"identifier": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "identifier",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "version",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"default": "(TRUE)",
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "path",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"installed_at": {
|
||||||
|
"default": "(CURRENT_TIMESTAMP)",
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "installed_at",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"name": "commands",
|
||||||
|
"columns": {
|
||||||
|
"cmd_id": {
|
||||||
|
"autoincrement": true,
|
||||||
|
"name": "cmd_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"ext_id": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "ext_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"default": "(TRUE)",
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "alias",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"hotkey": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "hotkey",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"commands_ext_id_extensions_ext_id_fk": {
|
||||||
|
"name": "commands_ext_id_extensions_ext_id_fk",
|
||||||
|
"tableFrom": "commands",
|
||||||
|
"tableTo": "extensions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"ext_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"ext_id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data": {
|
||||||
|
"name": "extension_data",
|
||||||
|
"columns": {
|
||||||
|
"data_id": {
|
||||||
|
"autoincrement": true,
|
||||||
|
"name": "data_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"ext_id": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "ext_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"data_type": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "data_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "data",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "metadata",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"search_text": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "search_text",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"default": "(CURRENT_TIMESTAMP)",
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"default": "(CURRENT_TIMESTAMP)",
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"extension_data_ext_id_extensions_ext_id_fk": {
|
||||||
|
"name": "extension_data_ext_id_extensions_ext_id_fk",
|
||||||
|
"tableFrom": "extension_data",
|
||||||
|
"tableTo": "extensions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"ext_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"ext_id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data_fts": {
|
||||||
|
"name": "extension_data_fts",
|
||||||
|
"columns": {
|
||||||
|
"data_id": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "data_id",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"search_text": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "search_text",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"extension_data_fts": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "extension_data_fts",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"rank": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "rank",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data_fts_data": {
|
||||||
|
"name": "extension_data_fts_data",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "block",
|
||||||
|
"type": "blob",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data_fts_idx": {
|
||||||
|
"name": "extension_data_fts_idx",
|
||||||
|
"columns": {
|
||||||
|
"segid": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "segid",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"term": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "term",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"pgno": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "pgno",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"extension_data_fts_idx_segid_term_pk": {
|
||||||
|
"columns": [
|
||||||
|
"segid",
|
||||||
|
"term"
|
||||||
|
],
|
||||||
|
"name": "extension_data_fts_idx_segid_term_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data_fts_docsize": {
|
||||||
|
"name": "extension_data_fts_docsize",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"sz": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "sz",
|
||||||
|
"type": "blob",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"extension_data_fts_config": {
|
||||||
|
"name": "extension_data_fts_config",
|
||||||
|
"columns": {
|
||||||
|
"k": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "k",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"v": {
|
||||||
|
"autoincrement": false,
|
||||||
|
"name": "v",
|
||||||
|
"type": "numeric",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
}
|
||||||
|
}
|
13
packages/drizzle/drizzle/meta/_journal.json
Normal file
13
packages/drizzle/drizzle/meta/_journal.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1742544246324,
|
||||||
|
"tag": "0000_colossal_jocasta",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
21
packages/drizzle/drizzle/relations.ts
Normal file
21
packages/drizzle/drizzle/relations.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { relations } from "drizzle-orm/relations"
|
||||||
|
import { commands, extensionData, extensions } from "./schema"
|
||||||
|
|
||||||
|
export const commandsRelations = relations(commands, ({ one }) => ({
|
||||||
|
extension: one(extensions, {
|
||||||
|
fields: [commands.extId],
|
||||||
|
references: [extensions.extId]
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const extensionsRelations = relations(extensions, ({ many }) => ({
|
||||||
|
commands: many(commands),
|
||||||
|
extensionData: many(extensionData)
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const extensionDataRelations = relations(extensionData, ({ one }) => ({
|
||||||
|
extension: one(extensions, {
|
||||||
|
fields: [extensionData.extId],
|
||||||
|
references: [extensions.extId]
|
||||||
|
})
|
||||||
|
}))
|
88
packages/drizzle/drizzle/schema.ts
Normal file
88
packages/drizzle/drizzle/schema.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { sql } from "drizzle-orm"
|
||||||
|
import {
|
||||||
|
blob,
|
||||||
|
foreignKey,
|
||||||
|
integer,
|
||||||
|
numeric,
|
||||||
|
primaryKey,
|
||||||
|
sqliteTable,
|
||||||
|
text
|
||||||
|
} from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
|
export const schemaVersion = sqliteTable("schema_version", {
|
||||||
|
version: integer().notNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensions = sqliteTable("extensions", {
|
||||||
|
extId: integer("ext_id").primaryKey({ autoIncrement: true }),
|
||||||
|
identifier: text().notNull(),
|
||||||
|
version: text().notNull(),
|
||||||
|
// enabled: numeric().default(sql`(TRUE)`),
|
||||||
|
enabled: integer({ mode: "boolean" }),
|
||||||
|
path: text(),
|
||||||
|
data: numeric(),
|
||||||
|
installedAt: numeric("installed_at").default(sql`(CURRENT_TIMESTAMP)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const commands = sqliteTable("commands", {
|
||||||
|
cmdId: integer("cmd_id").primaryKey({ autoIncrement: true }),
|
||||||
|
extId: integer("ext_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => extensions.extId, { onDelete: "cascade" }),
|
||||||
|
name: text().notNull(),
|
||||||
|
enabled: integer({ mode: "boolean" }),
|
||||||
|
// enabled: numeric().default(sql`(TRUE)`),
|
||||||
|
alias: text(),
|
||||||
|
hotkey: text(),
|
||||||
|
type: text().notNull(),
|
||||||
|
data: numeric()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensionData = sqliteTable("extension_data", {
|
||||||
|
dataId: integer("data_id").primaryKey({ autoIncrement: true }),
|
||||||
|
extId: integer("ext_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => extensions.extId, { onDelete: "cascade" }),
|
||||||
|
dataType: text("data_type").notNull(),
|
||||||
|
// data: text({ mode: "json" }).notNull(),
|
||||||
|
// metadata: text({ mode: "json" }),
|
||||||
|
data: text("data").notNull(),
|
||||||
|
metadata: text("metadata"),
|
||||||
|
searchText: text("search_text"),
|
||||||
|
createdAt: numeric("created_at").default(sql`(CURRENT_TIMESTAMP)`),
|
||||||
|
updatedAt: numeric("updated_at").default(sql`(CURRENT_TIMESTAMP)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensionDataFts = sqliteTable("extension_data_fts", {
|
||||||
|
dataId: numeric("data_id"),
|
||||||
|
searchText: numeric("search_text"),
|
||||||
|
extensionDataFts: numeric("extension_data_fts"),
|
||||||
|
rank: numeric()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensionDataFtsData = sqliteTable("extension_data_fts_data", {
|
||||||
|
id: integer().primaryKey(),
|
||||||
|
block: blob()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensionDataFtsIdx = sqliteTable(
|
||||||
|
"extension_data_fts_idx",
|
||||||
|
{
|
||||||
|
segid: numeric().notNull(),
|
||||||
|
term: numeric().notNull(),
|
||||||
|
pgno: numeric()
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
primaryKey({ columns: [table.segid, table.term], name: "extension_data_fts_idx_segid_term_pk" })
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
export const extensionDataFtsDocsize = sqliteTable("extension_data_fts_docsize", {
|
||||||
|
id: integer().primaryKey(),
|
||||||
|
sz: blob()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const extensionDataFtsConfig = sqliteTable("extension_data_fts_config", {
|
||||||
|
k: numeric().primaryKey().notNull(),
|
||||||
|
v: numeric()
|
||||||
|
})
|
3
packages/drizzle/index.ts
Normal file
3
packages/drizzle/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * as schema from "./drizzle/schema"
|
||||||
|
export * as relations from "./drizzle/relations"
|
||||||
|
export * as db from "./src/apis"
|
26
packages/drizzle/package.json
Normal file
26
packages/drizzle/package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@kksh/drizzle",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"exports": {
|
||||||
|
".": "./index.ts",
|
||||||
|
"./api": "./src/apis.ts",
|
||||||
|
"./schema": "./drizzle/schema.ts",
|
||||||
|
"./relations": "./drizzle/relations.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"drizzle-kit": "^0.30.5",
|
||||||
|
"tsx": "^4.19.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@kksh/api": "workspace:*",
|
||||||
|
"@libsql/client": "^0.15.1",
|
||||||
|
"@tauri-apps/plugin-log": "^2.3.1",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"drizzle-orm": "^0.41.0"
|
||||||
|
}
|
||||||
|
}
|
616
packages/drizzle/src/apis.ts
Normal file
616
packages/drizzle/src/apis.ts
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
import { KUNKUN_EXT_IDENTIFIER, type IDb, type IKV } from "@kksh/api"
|
||||||
|
import {
|
||||||
|
CmdType,
|
||||||
|
convertDateToSqliteString,
|
||||||
|
Ext,
|
||||||
|
ExtCmd,
|
||||||
|
ExtData,
|
||||||
|
SearchMode,
|
||||||
|
SearchModeEnum,
|
||||||
|
SQLSortOrder,
|
||||||
|
SQLSortOrderEnum
|
||||||
|
} from "@kksh/api/models"
|
||||||
|
import * as relations from "@kksh/drizzle/relations"
|
||||||
|
import * as orm from "drizzle-orm"
|
||||||
|
import type { SelectedFields } from "drizzle-orm/sqlite-core"
|
||||||
|
import * as v from "valibot"
|
||||||
|
import * as schema from "../drizzle/schema"
|
||||||
|
import { db } from "./proxy"
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Built-in Extensions */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export function getExtClipboard(): Promise<Ext> {
|
||||||
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_CLIPBOARD_EXT_IDENTIFIER)
|
||||||
|
}
|
||||||
|
export function getExtQuickLinks(): Promise<Ext> {
|
||||||
|
return getExtensionByIdentifierExpectExists(
|
||||||
|
KUNKUN_EXT_IDENTIFIER.KUNKUN_QUICK_LINKS_EXT_IDENTIFIER
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export function getExtRemote(): Promise<Ext> {
|
||||||
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_REMOTE_EXT_IDENTIFIER)
|
||||||
|
}
|
||||||
|
export function getExtScriptCmd(): Promise<Ext> {
|
||||||
|
return getExtensionByIdentifierExpectExists(
|
||||||
|
KUNKUN_EXT_IDENTIFIER.KUNKUN_SCRIPT_CMD_EXT_IDENTIFIER
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export function getExtDev(): Promise<Ext> {
|
||||||
|
return getExtensionByIdentifierExpectExists(KUNKUN_EXT_IDENTIFIER.KUNKUN_DEV_EXT_IDENTIFIER)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* 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> {
|
||||||
|
const ext = await db
|
||||||
|
.select()
|
||||||
|
.from(schema.extensions)
|
||||||
|
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||||
|
.get()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllExtensions(): Promise<Ext[]> {
|
||||||
|
const exts = await db.select().from(schema.extensions).all()
|
||||||
|
return v.parse(v.array(Ext), exts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There can be duplicate extensions with the same identifier. Store and Dev extensions can have the same identifier.
|
||||||
|
* But install path must be unique.
|
||||||
|
* @param path
|
||||||
|
*/
|
||||||
|
export async function getUniqueExtensionByPath(path: string) {
|
||||||
|
const ext = await db
|
||||||
|
.select()
|
||||||
|
.from(schema.extensions)
|
||||||
|
.where(orm.eq(schema.extensions.path, path))
|
||||||
|
.get()
|
||||||
|
return v.parse(Ext, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllExtensionsByIdentifier(identifier: string): Promise<Ext[]> {
|
||||||
|
return db
|
||||||
|
.select()
|
||||||
|
.from(schema.extensions)
|
||||||
|
.where(orm.eq(schema.extensions.identifier, identifier))
|
||||||
|
.all()
|
||||||
|
.then((exts) => v.parse(v.array(Ext), exts))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteExtensionByPath(path: string): Promise<void> {
|
||||||
|
return db
|
||||||
|
.delete(schema.extensions)
|
||||||
|
.where(orm.eq(schema.extensions.path, path))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteExtensionByExtId(extId: number): Promise<void> {
|
||||||
|
return db
|
||||||
|
.delete(schema.extensions)
|
||||||
|
.where(orm.eq(schema.extensions.extId, extId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Extension Command CRUD */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export function createCommand(data: {
|
||||||
|
extId: number
|
||||||
|
name: string
|
||||||
|
cmdType: CmdType
|
||||||
|
data: string
|
||||||
|
alias?: string
|
||||||
|
hotkey?: string
|
||||||
|
enabled?: boolean
|
||||||
|
}) {
|
||||||
|
return db
|
||||||
|
.insert(schema.commands)
|
||||||
|
.values({
|
||||||
|
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> {
|
||||||
|
const cmd = await db
|
||||||
|
.select()
|
||||||
|
.from(schema.commands)
|
||||||
|
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||||
|
.get()
|
||||||
|
return v.parse(ExtCmd, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllCmds(): Promise<ExtCmd[]> {
|
||||||
|
const cmds = await db.select().from(schema.commands).all()
|
||||||
|
return v.parse(v.array(ExtCmd), cmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommandsByExtId(extId: number) {
|
||||||
|
return db
|
||||||
|
.select()
|
||||||
|
.from(schema.commands)
|
||||||
|
.where(orm.eq(schema.commands.extId, extId))
|
||||||
|
.all()
|
||||||
|
.then((cmds) => v.parse(v.array(ExtCmd), cmds))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCmdById(cmdId: number) {
|
||||||
|
return db
|
||||||
|
.delete(schema.commands)
|
||||||
|
.where(orm.eq(schema.commands.cmdId, cmdId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCmdByID(data: {
|
||||||
|
cmdId: number
|
||||||
|
name: string
|
||||||
|
cmdType: CmdType
|
||||||
|
data: string
|
||||||
|
alias?: string
|
||||||
|
hotkey?: string
|
||||||
|
enabled: boolean
|
||||||
|
}) {
|
||||||
|
return db
|
||||||
|
.update(schema.commands)
|
||||||
|
.set({
|
||||||
|
name: data.name,
|
||||||
|
type: data.cmdType,
|
||||||
|
data: data.data,
|
||||||
|
alias: data.alias, // optional
|
||||||
|
hotkey: data.hotkey, // optional
|
||||||
|
enabled: data.enabled
|
||||||
|
// in drizzle schema, use integer({ mode: 'boolean' }) for boolean sqlite
|
||||||
|
// enabled: data.enabled ? String(data.enabled) : undefined
|
||||||
|
})
|
||||||
|
.where(orm.eq(schema.commands.cmdId, data.cmdId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Extension Data CRUD */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const ExtDataField = v.union([v.literal("data"), v.literal("search_text")])
|
||||||
|
export type ExtDataField = v.InferOutput<typeof ExtDataField>
|
||||||
|
|
||||||
|
function convertRawExtDataToExtData(rawData?: {
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
data: null | string
|
||||||
|
searchText?: null | string
|
||||||
|
dataId: number
|
||||||
|
extId: number
|
||||||
|
dataType: string
|
||||||
|
}): ExtData | undefined {
|
||||||
|
if (!rawData) {
|
||||||
|
return rawData
|
||||||
|
}
|
||||||
|
const parsedRes = v.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 db.insert(schema.extensionData).values(data).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExtensionDataById(dataId: number, fields?: ExtDataField[]) {
|
||||||
|
const _fields = fields ?? []
|
||||||
|
const selectQuery: SelectedFields = {
|
||||||
|
dataId: schema.extensionData.dataId,
|
||||||
|
extId: schema.extensionData.extId,
|
||||||
|
dataType: schema.extensionData.dataType,
|
||||||
|
metadata: schema.extensionData.metadata,
|
||||||
|
createdAt: schema.extensionData.createdAt,
|
||||||
|
updatedAt: schema.extensionData.updatedAt
|
||||||
|
// data: schema.extensionData.data,
|
||||||
|
// searchText: schema.extensionData.searchText
|
||||||
|
}
|
||||||
|
if (_fields.includes("data")) {
|
||||||
|
selectQuery["data"] = schema.extensionData.data
|
||||||
|
}
|
||||||
|
if (_fields.includes("search_text")) {
|
||||||
|
selectQuery["searchText"] = schema.extensionData.searchText
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
.select(selectQuery)
|
||||||
|
.from(schema.extensionData)
|
||||||
|
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||||
|
.get()
|
||||||
|
.then((rawData) => {
|
||||||
|
console.log("Raw Data", rawData)
|
||||||
|
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||||
|
return convertRawExtDataToExtData(rawData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = v.parse(v.optional(v.array(ExtDataField), []), searchParams.fields)
|
||||||
|
const _fields = fields ?? []
|
||||||
|
|
||||||
|
// Build the select query based on fields
|
||||||
|
const selectQuery: SelectedFields = {
|
||||||
|
dataId: schema.extensionData.dataId,
|
||||||
|
extId: schema.extensionData.extId,
|
||||||
|
dataType: schema.extensionData.dataType,
|
||||||
|
createdAt: schema.extensionData.createdAt,
|
||||||
|
updatedAt: schema.extensionData.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_fields.includes("data")) {
|
||||||
|
selectQuery["data"] = schema.extensionData.data
|
||||||
|
}
|
||||||
|
if (_fields.includes("search_text")) {
|
||||||
|
selectQuery["searchText"] = schema.extensionData.searchText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the query
|
||||||
|
let baseQuery = db.select(selectQuery).from(schema.extensionData)
|
||||||
|
|
||||||
|
// Add FTS join if needed
|
||||||
|
if (searchParams.searchMode === SearchModeEnum.FTS && searchParams.searchText) {
|
||||||
|
// @ts-expect-error - The join type is correct but TypeScript can't infer it properly
|
||||||
|
baseQuery = baseQuery.innerJoin(
|
||||||
|
schema.extensionDataFts,
|
||||||
|
orm.eq(schema.extensionData.dataId, schema.extensionDataFts.dataId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add conditions
|
||||||
|
const conditions = [orm.eq(schema.extensionData.extId, searchParams.extId)]
|
||||||
|
|
||||||
|
if (searchParams.dataId) {
|
||||||
|
conditions.push(orm.eq(schema.extensionData.dataId, searchParams.dataId))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParams.dataType) {
|
||||||
|
conditions.push(orm.eq(schema.extensionData.dataType, searchParams.dataType))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParams.searchText) {
|
||||||
|
switch (searchParams.searchMode) {
|
||||||
|
case SearchModeEnum.ExactMatch:
|
||||||
|
conditions.push(orm.eq(schema.extensionData.searchText, searchParams.searchText))
|
||||||
|
break
|
||||||
|
case SearchModeEnum.Like:
|
||||||
|
conditions.push(orm.like(schema.extensionData.searchText, `%${searchParams.searchText}%`))
|
||||||
|
break
|
||||||
|
case SearchModeEnum.FTS:
|
||||||
|
conditions.push(
|
||||||
|
orm.sql`${schema.extensionDataFts.searchText} MATCH ${searchParams.searchText}`
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParams.afterCreatedAt) {
|
||||||
|
conditions.push(orm.gt(schema.extensionData.createdAt, searchParams.afterCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParams.beforeCreatedAt) {
|
||||||
|
conditions.push(orm.lt(schema.extensionData.createdAt, searchParams.beforeCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the final query with all conditions and modifiers
|
||||||
|
const query = baseQuery
|
||||||
|
.where(orm.and(...conditions))
|
||||||
|
.orderBy(
|
||||||
|
searchParams.orderByCreatedAt
|
||||||
|
? searchParams.orderByCreatedAt === SQLSortOrderEnum.Asc
|
||||||
|
? orm.asc(schema.extensionData.createdAt)
|
||||||
|
: orm.desc(schema.extensionData.createdAt)
|
||||||
|
: searchParams.orderByUpdatedAt
|
||||||
|
? searchParams.orderByUpdatedAt === SQLSortOrderEnum.Asc
|
||||||
|
? orm.asc(schema.extensionData.updatedAt)
|
||||||
|
: orm.desc(schema.extensionData.updatedAt)
|
||||||
|
: orm.asc(schema.extensionData.createdAt) // Default ordering
|
||||||
|
)
|
||||||
|
.limit(searchParams.limit ?? 100) // Default limit
|
||||||
|
.offset(searchParams.offset ?? 0) // Default offset
|
||||||
|
|
||||||
|
// Execute query and convert results
|
||||||
|
const results = await query.all()
|
||||||
|
return results
|
||||||
|
.map((rawData) => {
|
||||||
|
// @ts-expect-error - rawData is unknown, but will be safe parsed with valibot
|
||||||
|
return convertRawExtDataToExtData(rawData)
|
||||||
|
})
|
||||||
|
.filter((item): item is ExtData => item !== undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteExtensionDataById(dataId: number) {
|
||||||
|
return db
|
||||||
|
.delete(schema.extensionData)
|
||||||
|
.where(orm.eq(schema.extensionData.dataId, dataId))
|
||||||
|
.run()
|
||||||
|
.then(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateExtensionDataById(data: {
|
||||||
|
dataId: number
|
||||||
|
data: string
|
||||||
|
searchText?: string
|
||||||
|
}): Promise<void> {
|
||||||
|
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
|
||||||
|
|
||||||
|
constructor(extId: number) {
|
||||||
|
this.extId = extId
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(data: { data: string; dataType?: string; searchText?: string }): Promise<void> {
|
||||||
|
return createExtensionData({
|
||||||
|
data: data.data,
|
||||||
|
dataType: data.dataType ?? "default",
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
55
packages/drizzle/src/proxy.ts
Normal file
55
packages/drizzle/src/proxy.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { sql } from "@kksh/api/commands"
|
||||||
|
import { error } from "@tauri-apps/plugin-log"
|
||||||
|
import { drizzle } from "drizzle-orm/sqlite-proxy"
|
||||||
|
import * as schema from "../drizzle/schema"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drizzle database instance.
|
||||||
|
*/
|
||||||
|
export const db = drizzle<typeof schema>(
|
||||||
|
async (sqlQuery, params, method) => {
|
||||||
|
let rows: any = []
|
||||||
|
let results = []
|
||||||
|
console.log({
|
||||||
|
sql: sqlQuery,
|
||||||
|
params,
|
||||||
|
method
|
||||||
|
})
|
||||||
|
console.log(sqlQuery)
|
||||||
|
// If the query is a SELECT, use the select method
|
||||||
|
if (isSelectQuery(sqlQuery)) {
|
||||||
|
rows = await sql.select(sqlQuery, params).catch((e) => {
|
||||||
|
error("SQL Error:", e)
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Otherwise, use the execute method
|
||||||
|
rows = await sql.execute(sqlQuery, params).catch((e) => {
|
||||||
|
error("SQL Error:", e)
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
return { rows: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
rows = rows.map((row: any) => {
|
||||||
|
return Object.values(row)
|
||||||
|
})
|
||||||
|
|
||||||
|
// If the method is "all", return all rows
|
||||||
|
results = method === "all" ? rows : rows[0]
|
||||||
|
return { rows: results }
|
||||||
|
},
|
||||||
|
// Pass the schema to the drizzle instance
|
||||||
|
{ schema: schema, logger: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given SQL query is a SELECT query.
|
||||||
|
* @param sql The SQL query to check.
|
||||||
|
* @returns True if the query is a SELECT query, false otherwise.
|
||||||
|
*/
|
||||||
|
function isSelectQuery(sql: string): boolean {
|
||||||
|
const selectRegex = /^\s*SELECT\b/i
|
||||||
|
return selectRegex.test(sql)
|
||||||
|
}
|
28
packages/drizzle/tsconfig.json
Normal file
28
packages/drizzle/tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["esnext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,16 @@
|
|||||||
"./install": "./src/install.ts"
|
"./install": "./src/install.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"@types/bun": "latest",
|
||||||
|
"@types/semver": "^7.5.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kksh/api": "workspace:*",
|
"@kksh/api": "workspace:*",
|
||||||
"@kksh/supabase": "workspace:*",
|
"@kksh/drizzle": "workspace:*",
|
||||||
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
|
"@std/semver": "npm:@jsr/std__semver@^1.0.4",
|
||||||
"@tauri-apps/plugin-upload": "^2.2.1",
|
"@tauri-apps/plugin-upload": "^2.2.1",
|
||||||
"uuid": "^11.0.3"
|
"semver": "^7.7.1",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user