mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
refactor(view extension): load HTML/resources via local HTTP server (#973)
Previously, View extensions loaded their HTML files directly from disk. Now, Coco starts a lightweight local HTTP server to serve the static files, and the extension loads them via HTTP instead. This refactoring is needed because Tauri is not allowed to load local files directly, we have to call convertFileSrc() to do the URL conversion to make it work. In previous implementations, we did such conversions to all the paths specified in the HTML file, but we realized that there are paths in JS/CSS files as well, and it is impossible to convert them all. So we have to change the way how view extensions load their files.
This commit is contained in:
@@ -52,6 +52,7 @@ refactor: procedure that convert_pages() into a func #934
|
|||||||
refactor(post-search): collect at least 2 documents from each query source #948
|
refactor(post-search): collect at least 2 documents from each query source #948
|
||||||
refactor: custom_version_comparator() now compares semantic versions #941
|
refactor: custom_version_comparator() now compares semantic versions #941
|
||||||
chore: center the main window vertically #959
|
chore: center the main window vertically #959
|
||||||
|
refactor(view extension): load HTML/resources via local HTTP server #973
|
||||||
|
|
||||||
## 0.8.0 (2025-09-28)
|
## 0.8.0 (2025-09-28)
|
||||||
|
|
||||||
|
|||||||
327
src-tauri/Cargo.lock
generated
327
src-tauri/Cargo.lock
generated
@@ -2,6 +2,212 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-codec"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.4",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-files"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c0d87f10d70e2948ad40e8edea79c8e77c6c66e0250a4c1f09b690465199576"
|
||||||
|
dependencies = [
|
||||||
|
"actix-http",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"bitflags 2.9.4",
|
||||||
|
"bytes",
|
||||||
|
"derive_more 2.0.1",
|
||||||
|
"futures-core",
|
||||||
|
"http-range",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"v_htmlescape",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-http"
|
||||||
|
version = "3.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49"
|
||||||
|
dependencies = [
|
||||||
|
"actix-codec",
|
||||||
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bitflags 2.9.4",
|
||||||
|
"brotli",
|
||||||
|
"bytes",
|
||||||
|
"bytestring",
|
||||||
|
"derive_more 2.0.1",
|
||||||
|
"encoding_rs",
|
||||||
|
"flate2",
|
||||||
|
"foldhash",
|
||||||
|
"futures-core",
|
||||||
|
"h2 0.3.27",
|
||||||
|
"http 0.2.12",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa",
|
||||||
|
"language-tags",
|
||||||
|
"local-channel",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand 0.9.2",
|
||||||
|
"sha1",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-macros"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-router"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
|
||||||
|
dependencies = [
|
||||||
|
"bytestring",
|
||||||
|
"cfg-if",
|
||||||
|
"http 0.2.12",
|
||||||
|
"regex",
|
||||||
|
"regex-lite",
|
||||||
|
"serde",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-rt"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-server"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
|
||||||
|
dependencies = [
|
||||||
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"mio 1.0.4",
|
||||||
|
"socket2 0.5.10",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-service"
|
||||||
|
version = "2.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-utils"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
|
||||||
|
dependencies = [
|
||||||
|
"local-waker",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web"
|
||||||
|
version = "4.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea"
|
||||||
|
dependencies = [
|
||||||
|
"actix-codec",
|
||||||
|
"actix-http",
|
||||||
|
"actix-macros",
|
||||||
|
"actix-router",
|
||||||
|
"actix-rt",
|
||||||
|
"actix-server",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web-codegen",
|
||||||
|
"bytes",
|
||||||
|
"bytestring",
|
||||||
|
"cfg-if",
|
||||||
|
"cookie 0.16.2",
|
||||||
|
"derive_more 2.0.1",
|
||||||
|
"encoding_rs",
|
||||||
|
"foldhash",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"impl-more",
|
||||||
|
"itoa",
|
||||||
|
"language-tags",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"once_cell",
|
||||||
|
"pin-project-lite",
|
||||||
|
"regex",
|
||||||
|
"regex-lite",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"smallvec",
|
||||||
|
"socket2 0.5.10",
|
||||||
|
"time",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-web-codegen"
|
||||||
|
version = "4.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8"
|
||||||
|
dependencies = [
|
||||||
|
"actix-router",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.24.2"
|
version = "0.24.2"
|
||||||
@@ -668,6 +874,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytestring"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2"
|
name = "bzip2"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -871,6 +1086,8 @@ dependencies = [
|
|||||||
name = "coco"
|
name = "coco"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actix-files",
|
||||||
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"applications",
|
"applications",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -1058,6 +1275,17 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@@ -1075,7 +1303,7 @@ version = "0.21.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie",
|
"cookie 0.18.1",
|
||||||
"document-features",
|
"document-features",
|
||||||
"idna",
|
"idna",
|
||||||
"log",
|
"log",
|
||||||
@@ -1899,6 +2127,12 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2649,6 +2883,25 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-util",
|
||||||
|
"http 0.2.12",
|
||||||
|
"indexmap 2.11.4",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -2879,7 +3132,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"h2",
|
"h2 0.4.12",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"httparse",
|
"httparse",
|
||||||
@@ -2942,7 +3195,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -3137,6 +3390,12 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "impl-more"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -3438,6 +3697,12 @@ dependencies = [
|
|||||||
"selectors 0.24.0",
|
"selectors 0.24.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "language-tags"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -3597,6 +3862,23 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "local-channel"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"local-waker",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "local-waker"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -5187,7 +5469,7 @@ dependencies = [
|
|||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.16",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -5224,7 +5506,7 @@ dependencies = [
|
|||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
@@ -5510,6 +5792,12 @@ dependencies = [
|
|||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-lite"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
@@ -5533,12 +5821,12 @@ checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cookie",
|
"cookie 0.18.1",
|
||||||
"cookie_store",
|
"cookie_store",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2 0.4.12",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
@@ -6234,6 +6522,16 @@ version = "1.15.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -6579,7 +6877,7 @@ checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cookie",
|
"cookie 0.18.1",
|
||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
"dunce",
|
"dunce",
|
||||||
"embed_plist",
|
"embed_plist",
|
||||||
@@ -7091,7 +7389,7 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie",
|
"cookie 0.18.1",
|
||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
@@ -7345,7 +7643,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@@ -7567,6 +7865,7 @@ version = "0.1.41"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
@@ -7855,6 +8154,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v_htmlescape"
|
||||||
|
version = "0.15.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "value-bag"
|
name = "value-bag"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
@@ -8978,7 +9283,7 @@ checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"block2 0.6.1",
|
"block2 0.6.1",
|
||||||
"cookie",
|
"cookie 0.18.1",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
"dpi",
|
"dpi",
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ urlencoding = "2.1.3"
|
|||||||
scraper = "0.17"
|
scraper = "0.17"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
path-clean = "1.0.1"
|
path-clean = "1.0.1"
|
||||||
|
actix-files = "0.6.8"
|
||||||
|
actix-web = "4.11.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.23.0"
|
tempfile = "3.23.0"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::extension::built_in::window_management::actions::Action;
|
use crate::extension::built_in::window_management::actions::Action;
|
||||||
|
use crate::extension::view_extension::serve_files_in;
|
||||||
use crate::extension::{ExtensionPermission, ExtensionSettings, ViewExtensionUISettings};
|
use crate::extension::{ExtensionPermission, ExtensionSettings, ViewExtensionUISettings};
|
||||||
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as Json;
|
use serde_json::Value as Json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -86,6 +88,10 @@ pub(crate) enum ExtensionOnOpenedType {
|
|||||||
open_with: Option<String>,
|
open_with: Option<String>,
|
||||||
},
|
},
|
||||||
View {
|
View {
|
||||||
|
/// Extension name
|
||||||
|
name: String,
|
||||||
|
// An absolute path to the extension icon or a font code.
|
||||||
|
icon: String,
|
||||||
/// Path to the HTML file that coco will load and render.
|
/// Path to the HTML file that coco will load and render.
|
||||||
///
|
///
|
||||||
/// It should be an absolute path or Tauri cannot open it.
|
/// It should be an absolute path or Tauri cannot open it.
|
||||||
@@ -120,7 +126,12 @@ impl OnOpened {
|
|||||||
// The URL of a quicklink is nearly useless without such dynamic user
|
// The URL of a quicklink is nearly useless without such dynamic user
|
||||||
// inputs, so until we have dynamic URL support, we just use "N/A".
|
// inputs, so until we have dynamic URL support, we just use "N/A".
|
||||||
ExtensionOnOpenedType::Quicklink { .. } => String::from("N/A"),
|
ExtensionOnOpenedType::Quicklink { .. } => String::from("N/A"),
|
||||||
ExtensionOnOpenedType::View { page: _, ui: _ } => {
|
ExtensionOnOpenedType::View {
|
||||||
|
name: _,
|
||||||
|
icon: _,
|
||||||
|
page: _,
|
||||||
|
ui: _,
|
||||||
|
} => {
|
||||||
// We currently don't have URL for this kind of extension.
|
// We currently don't have URL for this kind of extension.
|
||||||
String::from("N/A")
|
String::from("N/A")
|
||||||
}
|
}
|
||||||
@@ -233,32 +244,49 @@ pub(crate) async fn open(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExtensionOnOpenedType::View { page, ui } => {
|
ExtensionOnOpenedType::View {
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
page,
|
||||||
|
ui,
|
||||||
|
} => {
|
||||||
|
let page_path = Utf8Path::new(&page);
|
||||||
|
let directory = page_path.parent().unwrap_or_else(|| {
|
||||||
|
panic!("View extension page path should have a parent, i.e., it should be under a directory, but [{}] does not", page);
|
||||||
|
});
|
||||||
|
let mut url = serve_files_in(directory.as_ref()).await;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Emit an event to let the frontend code open this extension.
|
* Emit an event to let the frontend code open this extension.
|
||||||
*
|
*
|
||||||
* Payload `page_and_permission` contains the information needed
|
* Payload `view_extension_opened` contains the information needed
|
||||||
* to do that.
|
* to do that.
|
||||||
*
|
*
|
||||||
* See "src/pages/main/index.tsx" for more info.
|
* See "src/pages/main/index.tsx" for more info.
|
||||||
*/
|
*/
|
||||||
|
use camino::Utf8Path;
|
||||||
use serde_json::Value as Json;
|
use serde_json::Value as Json;
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
|
|
||||||
let mut extra_args =
|
let html_filename = page_path
|
||||||
extra_args.expect("extra_args is needed to open() a view extension");
|
.file_name()
|
||||||
let document = extra_args.remove("document").expect(
|
.unwrap_or_else(|| {
|
||||||
"extra argument [document] should be provided to open a view extension",
|
panic!("View extension page path should have a file name, but [{}] does not have one", page);
|
||||||
);
|
}).to_string();
|
||||||
|
url.push('/');
|
||||||
|
url.push_str(&html_filename);
|
||||||
|
|
||||||
let page_and_permission: [Json; 4] = [
|
let html_file_url = url;
|
||||||
Json::String(page),
|
debug!("View extension listening on: {}", html_file_url);
|
||||||
|
let view_extension_opened: [Json; 5] = [
|
||||||
|
Json::String(name),
|
||||||
|
Json::String(icon),
|
||||||
|
Json::String(html_file_url),
|
||||||
to_value(permission).unwrap(),
|
to_value(permission).unwrap(),
|
||||||
to_value(ui).unwrap(),
|
to_value(ui).unwrap(),
|
||||||
document,
|
|
||||||
];
|
];
|
||||||
tauri_app_handle
|
tauri_app_handle
|
||||||
.emit("open_view_extension", page_and_permission)
|
.emit("open_view_extension", view_extension_opened)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub(crate) mod api;
|
pub(crate) mod api;
|
||||||
pub(crate) mod built_in;
|
pub(crate) mod built_in;
|
||||||
pub(crate) mod third_party;
|
pub(crate) mod third_party;
|
||||||
|
pub(crate) mod view_extension;
|
||||||
|
|
||||||
use crate::common::document::ExtensionOnOpened;
|
use crate::common::document::ExtensionOnOpened;
|
||||||
use crate::common::document::ExtensionOnOpenedType;
|
use crate::common::document::ExtensionOnOpenedType;
|
||||||
@@ -292,12 +293,19 @@ impl Extension {
|
|||||||
ExtensionType::Script => todo!("not supported yet"),
|
ExtensionType::Script => todo!("not supported yet"),
|
||||||
ExtensionType::Setting => todo!("not supported yet"),
|
ExtensionType::Setting => todo!("not supported yet"),
|
||||||
ExtensionType::View => {
|
ExtensionType::View => {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let icon = self.icon.clone();
|
||||||
let page = self.page.as_ref().unwrap_or_else(|| {
|
let page = self.page.as_ref().unwrap_or_else(|| {
|
||||||
panic!("View extension [{}]'s [page] field is not set, something wrong with your extension validity check", self.id);
|
panic!("View extension [{}]'s [page] field is not set, something wrong with your extension validity check", self.id);
|
||||||
}).clone();
|
}).clone();
|
||||||
let ui = self.ui.clone();
|
let ui = self.ui.clone();
|
||||||
|
|
||||||
let extension_on_opened_type = ExtensionOnOpenedType::View { page, ui };
|
let extension_on_opened_type = ExtensionOnOpenedType::View {
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
page,
|
||||||
|
ui,
|
||||||
|
};
|
||||||
let extension_on_opened = ExtensionOnOpened {
|
let extension_on_opened = ExtensionOnOpened {
|
||||||
ty: extension_on_opened_type,
|
ty: extension_on_opened_type,
|
||||||
settings,
|
settings,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::check_compatibility_via_mcv;
|
|||||||
use crate::extension::PLUGIN_JSON_FILE_NAME;
|
use crate::extension::PLUGIN_JSON_FILE_NAME;
|
||||||
use crate::extension::third_party::check::general_check;
|
use crate::extension::third_party::check::general_check;
|
||||||
use crate::extension::third_party::install::{
|
use crate::extension::third_party::install::{
|
||||||
filter_out_incompatible_sub_extensions, is_extension_installed, view_extension_convert_pages,
|
filter_out_incompatible_sub_extensions, is_extension_installed,
|
||||||
};
|
};
|
||||||
use crate::extension::third_party::{
|
use crate::extension::third_party::{
|
||||||
THIRD_PARTY_EXTENSIONS_SEARCH_SOURCE, get_third_party_extension_directory,
|
THIRD_PARTY_EXTENSIONS_SEARCH_SOURCE, get_third_party_extension_directory,
|
||||||
@@ -226,14 +226,6 @@ pub(crate) async fn install_local_extension(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
/*
|
|
||||||
* Call convert_page() to update the page files. This has to be done after
|
|
||||||
* writing the extension files because we will edit them.
|
|
||||||
*
|
|
||||||
* HTTP links will be skipped.
|
|
||||||
*/
|
|
||||||
view_extension_convert_pages(&extension, &dest_dir).await?;
|
|
||||||
|
|
||||||
// Canonicalize relative icon and page paths
|
// Canonicalize relative icon and page paths
|
||||||
canonicalize_relative_icon_path(&dest_dir, &mut extension)?;
|
canonicalize_relative_icon_path(&dest_dir, &mut extension)?;
|
||||||
canonicalize_relative_page_path(&dest_dir, &mut extension)?;
|
canonicalize_relative_page_path(&dest_dir, &mut extension)?;
|
||||||
|
|||||||
435
src-tauri/src/extension/third_party/install/mod.rs
vendored
435
src-tauri/src/extension/third_party/install/mod.rs
vendored
@@ -33,31 +33,23 @@
|
|||||||
//! * plugin.json file
|
//! * plugin.json file
|
||||||
//! * View pages if exist
|
//! * View pages if exist
|
||||||
//!
|
//!
|
||||||
//! 6. If this extension contains any View extensions, call `convert_page()`
|
//! 6. Canonicalize `Extension.icon` and `Extension.page` fields if they are
|
||||||
//! on them to make them loadable by Tauri/webview.
|
|
||||||
//!
|
|
||||||
//! See `convert_page()` for more info.
|
|
||||||
//!
|
|
||||||
//! 7. Canonicalize `Extension.icon` and `Extension.page` fields if they are
|
|
||||||
//! relative paths
|
//! relative paths
|
||||||
//!
|
//!
|
||||||
//! * icon: relative to the `assets` directory
|
//! * icon: relative to the `assets` directory
|
||||||
//! * page: relative to the extension root directory
|
//! * page: relative to the extension root directory
|
||||||
//!
|
//!
|
||||||
//! 8. Add the extension to the in-memory extension list.
|
//! 7. Add the extension to the in-memory extension list.
|
||||||
|
|
||||||
pub(crate) mod local_extension;
|
pub(crate) mod local_extension;
|
||||||
pub(crate) mod store;
|
pub(crate) mod store;
|
||||||
|
|
||||||
use crate::extension::Extension;
|
use crate::extension::Extension;
|
||||||
use crate::extension::ExtensionType;
|
|
||||||
use crate::extension::PLUGIN_JSON_FIELD_MINIMUM_COCO_VERSION;
|
use crate::extension::PLUGIN_JSON_FIELD_MINIMUM_COCO_VERSION;
|
||||||
use crate::util::platform::Platform;
|
use crate::util::platform::Platform;
|
||||||
use crate::util::version::{COCO_VERSION, parse_coco_semver};
|
use crate::util::version::{COCO_VERSION, parse_coco_semver};
|
||||||
use serde_json::Value as Json;
|
use serde_json::Value as Json;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use super::THIRD_PARTY_EXTENSIONS_SEARCH_SOURCE;
|
use super::THIRD_PARTY_EXTENSIONS_SEARCH_SOURCE;
|
||||||
|
|
||||||
@@ -129,176 +121,6 @@ pub(crate) fn filter_out_incompatible_sub_extensions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the page file to make it loadable by the Tauri/Webview.
|
|
||||||
pub(crate) async fn convert_page(absolute_page_path: &Path) -> Result<(), String> {
|
|
||||||
assert!(absolute_page_path.is_absolute());
|
|
||||||
|
|
||||||
let page_content = tokio::fs::read_to_string(absolute_page_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let new_page_content = _convert_page(&page_content, absolute_page_path)?;
|
|
||||||
|
|
||||||
// overwrite it
|
|
||||||
tokio::fs::write(absolute_page_path, new_page_content)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NOTE: There is no Rust implementation of `convertFileSrc()` in Tauri. Our
|
|
||||||
/// impl here is based on [comment](https://github.com/tauri-apps/tauri/issues/12022#issuecomment-2572879115)
|
|
||||||
fn convert_file_src(path: &Path) -> Result<String, String> {
|
|
||||||
#[cfg(any(windows, target_os = "android"))]
|
|
||||||
let base = "http://asset.localhost/";
|
|
||||||
#[cfg(not(any(windows, target_os = "android")))]
|
|
||||||
let base = "asset://localhost/";
|
|
||||||
|
|
||||||
let path =
|
|
||||||
dunce::canonicalize(path).map_err(|e| format!("Failed to canonicalize path: {}", e))?;
|
|
||||||
let path_str = path.to_string_lossy();
|
|
||||||
let encoded = urlencoding::encode(&path_str);
|
|
||||||
|
|
||||||
Ok(format!("{base}{encoded}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tauri cannot directly access the file system, to make a file loadable, we
|
|
||||||
/// have to `canonicalize()` and `convertFileSrc()` its path before passing it
|
|
||||||
/// to Tauri.
|
|
||||||
///
|
|
||||||
/// View extension's page is a HTML file that Coco (Tauri) will load, we need
|
|
||||||
/// to process all `<PATH>` tags:
|
|
||||||
///
|
|
||||||
/// 1. `<script type="xxx" crossorigin src="<PATH>"></script>`
|
|
||||||
/// 2. `<a href="<PATH>">xxx</a>`
|
|
||||||
/// 3. `<link rel="xxx" href="<PATH>"/>`
|
|
||||||
/// 4. `<img class="xxx" src="<PATH>" alt="xxx"/>`
|
|
||||||
fn _convert_page(page_content: &str, absolute_page_path: &Path) -> Result<String, String> {
|
|
||||||
use scraper::{Html, Selector};
|
|
||||||
|
|
||||||
/// Helper function.
|
|
||||||
///
|
|
||||||
/// Search `document` for the tag attributes specified by `tag_with_attribute`
|
|
||||||
/// and `tag_attribute`, call `convert_file_src()`, then update the attribute
|
|
||||||
/// value with the function return value.
|
|
||||||
fn modify_tag_attributes(
|
|
||||||
document: &Html,
|
|
||||||
modified_html: &mut String,
|
|
||||||
base_dir: &Path,
|
|
||||||
tag_with_attribute: &str,
|
|
||||||
tag_attribute: &str,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let script_selector = Selector::parse(tag_with_attribute).unwrap();
|
|
||||||
for element in document.select(&script_selector) {
|
|
||||||
if let Some(src) = element.value().attr(tag_attribute) {
|
|
||||||
if !src.starts_with("http://")
|
|
||||||
&& !src.starts_with("https://")
|
|
||||||
&& !src.starts_with("asset://")
|
|
||||||
&& !src.starts_with("http://asset.localhost/")
|
|
||||||
{
|
|
||||||
// It could be a path like "/assets/index-41be3ec9.js", but it
|
|
||||||
// is still a relative path. We need to remove the starting /
|
|
||||||
// or path.join() will think it is an absolute path and does nothing
|
|
||||||
let corrected_src = if src.starts_with('/') { &src[1..] } else { src };
|
|
||||||
|
|
||||||
let full_path = base_dir.join(corrected_src);
|
|
||||||
|
|
||||||
let converted_path = convert_file_src(full_path.as_path())?;
|
|
||||||
*modified_html = modified_html.replace(
|
|
||||||
&format!("{}=\"{}\"", tag_attribute, src),
|
|
||||||
&format!("{}=\"{}\"", tag_attribute, converted_path),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
let base_dir = absolute_page_path
|
|
||||||
.parent()
|
|
||||||
.ok_or_else(|| format!("page path is invalid, it should have a parent path"))?;
|
|
||||||
let document: Html = Html::parse_document(page_content);
|
|
||||||
let mut modified_html: String = page_content.to_string();
|
|
||||||
|
|
||||||
modify_tag_attributes(
|
|
||||||
&document,
|
|
||||||
&mut modified_html,
|
|
||||||
base_dir,
|
|
||||||
"script[src]",
|
|
||||||
"src",
|
|
||||||
)?;
|
|
||||||
modify_tag_attributes(&document, &mut modified_html, base_dir, "a[href]", "href")?;
|
|
||||||
modify_tag_attributes(
|
|
||||||
&document,
|
|
||||||
&mut modified_html,
|
|
||||||
base_dir,
|
|
||||||
"link[href]",
|
|
||||||
"href",
|
|
||||||
)?;
|
|
||||||
modify_tag_attributes(&document, &mut modified_html, base_dir, "img[src]", "src")?;
|
|
||||||
|
|
||||||
Ok(modified_html)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn view_extension_convert_pages(
|
|
||||||
extension: &Extension,
|
|
||||||
extension_directory: &Path,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let pages: Vec<&str> = {
|
|
||||||
if extension.r#type == ExtensionType::View {
|
|
||||||
let page = extension
|
|
||||||
.page
|
|
||||||
.as_ref()
|
|
||||||
.expect("View extension should set its page field");
|
|
||||||
|
|
||||||
vec![page.as_str()]
|
|
||||||
} else if extension.r#type.contains_sub_items()
|
|
||||||
&& let Some(ref views) = extension.views
|
|
||||||
{
|
|
||||||
let mut pages = Vec::with_capacity(views.len());
|
|
||||||
|
|
||||||
for view in views.iter() {
|
|
||||||
let page = view
|
|
||||||
.page
|
|
||||||
.as_ref()
|
|
||||||
.expect("View extension should set its page field");
|
|
||||||
|
|
||||||
pages.push(page.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
pages
|
|
||||||
} else {
|
|
||||||
// No pages in this extension
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fn canonicalize_page_path(page_path: &Path, extension_root: &Path) -> PathBuf {
|
|
||||||
if page_path.is_relative() {
|
|
||||||
// It is relative to the extension root directory
|
|
||||||
extension_root.join(page_path)
|
|
||||||
} else {
|
|
||||||
page_path.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for page in pages {
|
|
||||||
/*
|
|
||||||
* Skip HTTP links
|
|
||||||
*/
|
|
||||||
if let Ok(url) = url::Url::parse(page)
|
|
||||||
&& ["http", "https"].contains(&url.scheme())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = canonicalize_page_path(Path::new(page), &extension_directory);
|
|
||||||
convert_page(&path).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inspect the "minimum_coco_version" field and see if this extension is
|
/// Inspect the "minimum_coco_version" field and see if this extension is
|
||||||
/// compatible with the current Coco app.
|
/// compatible with the current Coco app.
|
||||||
fn check_compatibility_via_mcv(plugin_json: &Json) -> Result<bool, String> {
|
fn check_compatibility_via_mcv(plugin_json: &Json) -> Result<bool, String> {
|
||||||
@@ -528,257 +350,4 @@ mod tests {
|
|||||||
filter_out_incompatible_sub_extensions(&mut main_extension, Platform::Macos);
|
filter_out_incompatible_sub_extensions(&mut main_extension, Platform::Macos);
|
||||||
assert_eq!(main_extension.views.unwrap().len(), 1);
|
assert_eq!(main_extension.views.unwrap().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_script_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><script src="main.js"></script></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><script src=\"{}\"></script></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_script_tag_with_a_root_char() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><script src="/main.js"></script></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><script src=\"{}\"></script></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_a_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><a href="main.js">foo</a></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!("<html><body><a href=\"{}\">foo</a></body></html>", path);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_a_tag_with_a_root_char() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><a href="/main.js">foo</a></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!("<html><body><a href=\"{}\">foo</a></body></html>", path);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_link_href_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let css_file = temp_dir.path().join("main.css");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><link rel="stylesheet" href="main.css"/></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&css_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&css_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><link rel=\"stylesheet\" href=\"{}\"/></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_link_href_tag_with_a_root_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let css_file = temp_dir.path().join("main.css");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><link rel="stylesheet" href="/main.css"/></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&css_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&css_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><link rel=\"stylesheet\" href=\"{}\"/></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_img_src_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let png_file = temp_dir.path().join("main.png");
|
|
||||||
|
|
||||||
let html_content =
|
|
||||||
r#"<html><body> <img class="fit-picture" src="main.png" alt="xxx" /></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&png_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&png_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body> <img class=\"fit-picture\" src=\"{}\" alt=\"xxx\" /></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_img_src_tag_with_a_root_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let png_file = temp_dir.path().join("main.png");
|
|
||||||
|
|
||||||
let html_content =
|
|
||||||
r#"<html><body> <img class="fit-picture" src="/main.png" alt="xxx" /></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&png_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&png_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body> <img class=\"fit-picture\" src=\"{}\" alt=\"xxx\" /></body></html>",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_contain_both_script_and_a_tags() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content =
|
|
||||||
r#"<html><body><a href="main.js">foo</a><script src="main.js"></script></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><a href=\"{}\">foo</a><script src=\"{}\"></script></body></html>",
|
|
||||||
path, path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_contain_both_script_and_a_tags_with_root_char() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
let js_file = temp_dir.path().join("main.js");
|
|
||||||
|
|
||||||
let html_content = r#"<html><body><a href="/main.js">foo</a><script src="/main.js"></script></body></html>"#;
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
std::fs::write(&js_file, "").unwrap();
|
|
||||||
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
|
|
||||||
let path = convert_file_src(&js_file).unwrap();
|
|
||||||
let expected = format!(
|
|
||||||
"<html><body><a href=\"{}\">foo</a><script src=\"{}\"></script></body></html>",
|
|
||||||
path, path
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_empty_html() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
|
|
||||||
let html_content = "";
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
assert!(result.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_convert_page_only_html_tag() {
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let html_file = temp_dir.path().join("test.html");
|
|
||||||
|
|
||||||
let html_content = "<html></html>";
|
|
||||||
std::fs::write(&html_file, html_content).unwrap();
|
|
||||||
let result = _convert_page(html_content, &html_file).unwrap();
|
|
||||||
assert_eq!(result, html_content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use crate::extension::canonicalize_relative_page_path;
|
|||||||
use crate::extension::third_party::check::general_check;
|
use crate::extension::third_party::check::general_check;
|
||||||
use crate::extension::third_party::get_third_party_extension_directory;
|
use crate::extension::third_party::get_third_party_extension_directory;
|
||||||
use crate::extension::third_party::install::filter_out_incompatible_sub_extensions;
|
use crate::extension::third_party::install::filter_out_incompatible_sub_extensions;
|
||||||
use crate::extension::third_party::install::view_extension_convert_pages;
|
|
||||||
use crate::server::http_client::HttpClient;
|
use crate::server::http_client::HttpClient;
|
||||||
use crate::util::platform::Platform;
|
use crate::util::platform::Platform;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -401,14 +400,6 @@ pub(crate) async fn install_extension_from_store(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
/*
|
|
||||||
* Call convert_page() to update the page files. This has to be done after
|
|
||||||
* writing the extension files because we will edit them.
|
|
||||||
*
|
|
||||||
* HTTP links will be skipped.
|
|
||||||
*/
|
|
||||||
view_extension_convert_pages(&extension, &extension_directory).await?;
|
|
||||||
|
|
||||||
// Canonicalize relative icon and page paths
|
// Canonicalize relative icon and page paths
|
||||||
canonicalize_relative_icon_path(&extension_directory, &mut extension)?;
|
canonicalize_relative_icon_path(&extension_directory, &mut extension)?;
|
||||||
canonicalize_relative_page_path(&extension_directory, &mut extension)?;
|
canonicalize_relative_page_path(&extension_directory, &mut extension)?;
|
||||||
|
|||||||
56
src-tauri/src/extension/third_party/mod.rs
vendored
56
src-tauri/src/extension/third_party/mod.rs
vendored
@@ -29,7 +29,6 @@ use check::general_check;
|
|||||||
use function_name::named;
|
use function_name::named;
|
||||||
use semver::Version as SemVer;
|
use semver::Version as SemVer;
|
||||||
use serde_json::Value as Json;
|
use serde_json::Value as Json;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -403,25 +402,6 @@ impl ThirdPartyExtensionsSearchSource {
|
|||||||
if extension.supports_alias_hotkey() {
|
if extension.supports_alias_hotkey() {
|
||||||
if let Some(ref hotkey) = extension.hotkey {
|
if let Some(ref hotkey) = extension.hotkey {
|
||||||
let on_opened = extension.on_opened().unwrap_or_else(|| panic!( "extension has hotkey, but on_open() returns None, extension ID [{}], extension type [{:?}]", extension.id, extension.r#type));
|
let on_opened = extension.on_opened().unwrap_or_else(|| panic!( "extension has hotkey, but on_open() returns None, extension ID [{}], extension type [{:?}]", extension.id, extension.r#type));
|
||||||
let url = on_opened.url();
|
|
||||||
let extension_type_string = extension.r#type.to_string();
|
|
||||||
let document = Document {
|
|
||||||
id: extension.id.clone(),
|
|
||||||
title: Some(extension.name.clone()),
|
|
||||||
icon: Some(extension.icon.clone()),
|
|
||||||
on_opened: Some(on_opened.clone()),
|
|
||||||
url: Some(url),
|
|
||||||
category: Some(extension_type_string.clone()),
|
|
||||||
source: Some(DataSourceReference {
|
|
||||||
id: Some(extension_type_string.clone()),
|
|
||||||
name: Some(extension_type_string.clone()),
|
|
||||||
icon: None,
|
|
||||||
r#type: Some(extension_type_string),
|
|
||||||
}),
|
|
||||||
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let extension_id_clone = extension.id.clone();
|
let extension_id_clone = extension.id.clone();
|
||||||
|
|
||||||
tauri_app_handle
|
tauri_app_handle
|
||||||
@@ -431,16 +411,9 @@ impl ThirdPartyExtensionsSearchSource {
|
|||||||
let extension_id_clone = extension_id_clone.clone();
|
let extension_id_clone = extension_id_clone.clone();
|
||||||
let app_handle_clone = tauri_app_handle.clone();
|
let app_handle_clone = tauri_app_handle.clone();
|
||||||
|
|
||||||
let mut args = HashMap::new();
|
|
||||||
args.insert(
|
|
||||||
String::from("document"),
|
|
||||||
serde_json::to_value(&document).unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if event.state() == ShortcutState::Pressed {
|
if event.state() == ShortcutState::Pressed {
|
||||||
async_runtime::spawn(async move {
|
async_runtime::spawn(async move {
|
||||||
let result =
|
let result = open(app_handle_clone, on_opened_clone, None).await;
|
||||||
open(app_handle_clone, on_opened_clone, Some(args)).await;
|
|
||||||
if let Err(msg) = result {
|
if let Err(msg) = result {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"failed to open extension [{}], error [{}]",
|
"failed to open extension [{}], error [{}]",
|
||||||
@@ -705,24 +678,6 @@ impl ThirdPartyExtensionsSearchSource {
|
|||||||
let on_opened = extension.on_opened().unwrap_or_else(|| panic!(
|
let on_opened = extension.on_opened().unwrap_or_else(|| panic!(
|
||||||
"setting hotkey for an extension that cannot be opened, extension ID [{:?}], extension type [{:?}]", bundle_id, extension.r#type,
|
"setting hotkey for an extension that cannot be opened, extension ID [{:?}], extension type [{:?}]", bundle_id, extension.r#type,
|
||||||
));
|
));
|
||||||
let url = on_opened.url();
|
|
||||||
let extension_type_string = extension.r#type.to_string();
|
|
||||||
let document = Document {
|
|
||||||
id: extension.id.clone(),
|
|
||||||
title: Some(extension.name.clone()),
|
|
||||||
icon: Some(extension.icon.clone()),
|
|
||||||
on_opened: Some(on_opened.clone()),
|
|
||||||
url: Some(url),
|
|
||||||
category: Some(extension_type_string.clone()),
|
|
||||||
source: Some(DataSourceReference {
|
|
||||||
id: Some(extension_type_string.clone()),
|
|
||||||
name: Some(extension_type_string.clone()),
|
|
||||||
icon: None,
|
|
||||||
r#type: Some(extension_type_string),
|
|
||||||
}),
|
|
||||||
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let bundle_id_owned = bundle_id.to_owned();
|
let bundle_id_owned = bundle_id.to_owned();
|
||||||
tauri_app_handle
|
tauri_app_handle
|
||||||
@@ -732,16 +687,9 @@ impl ThirdPartyExtensionsSearchSource {
|
|||||||
let bundle_id_clone = bundle_id_owned.clone();
|
let bundle_id_clone = bundle_id_owned.clone();
|
||||||
let app_handle_clone = tauri_app_handle.clone();
|
let app_handle_clone = tauri_app_handle.clone();
|
||||||
|
|
||||||
let document_clone = document.clone();
|
|
||||||
if event.state() == ShortcutState::Pressed {
|
if event.state() == ShortcutState::Pressed {
|
||||||
async_runtime::spawn(async move {
|
async_runtime::spawn(async move {
|
||||||
let mut args = HashMap::new();
|
let result = open(app_handle_clone, on_opened_clone, None).await;
|
||||||
args.insert(
|
|
||||||
String::from("document"),
|
|
||||||
serde_json::to_value(&document_clone).unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = open(app_handle_clone, on_opened_clone, Some(args)).await;
|
|
||||||
if let Err(msg) = result {
|
if let Err(msg) = result {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"failed to open extension [{:?}], error [{}]",
|
"failed to open extension [{:?}], error [{}]",
|
||||||
|
|||||||
38
src-tauri/src/extension/view_extension.rs
Normal file
38
src-tauri/src/extension/view_extension.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//! View extension-related stuff
|
||||||
|
|
||||||
|
use actix_files::Files;
|
||||||
|
use actix_web::{App, HttpServer, dev::ServerHandle};
|
||||||
|
use std::path::Path;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
static FILE_SERVER_HANDLE: Mutex<Option<ServerHandle>> = Mutex::const_new(None);
|
||||||
|
|
||||||
|
/// Start a static HTTP file server serving the directory specified by `path`.
|
||||||
|
/// Return the URL of the server.
|
||||||
|
pub(crate) async fn serve_files_in(path: &Path) -> String {
|
||||||
|
const ADDR: &str = "127.0.0.1";
|
||||||
|
|
||||||
|
let mut guard = FILE_SERVER_HANDLE.lock().await;
|
||||||
|
if let Some(prev_server_handle) = guard.take() {
|
||||||
|
prev_server_handle.stop(true).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = path.to_path_buf();
|
||||||
|
let http_server =
|
||||||
|
HttpServer::new(move || App::new().service(Files::new("/", &path).show_files_listing()))
|
||||||
|
// Set port to 0 and let OS assign a port to us
|
||||||
|
.bind((ADDR, 0))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let assigned_port = http_server.addrs()[0].port();
|
||||||
|
|
||||||
|
let server = http_server.disable_signals().workers(1).run();
|
||||||
|
|
||||||
|
let new_handle = server.handle();
|
||||||
|
|
||||||
|
tokio::spawn(server);
|
||||||
|
|
||||||
|
*guard = Some(new_handle);
|
||||||
|
|
||||||
|
format!("http://{}:{}", ADDR, assigned_port)
|
||||||
|
}
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null,
|
"csp": "default-src blob: data: filesystem: ws: wss: http://127.0.0.1:* http://localhost:* 'unsafe-inline' 'unsafe-eval';",
|
||||||
"dangerousDisableAssetCspModification": true,
|
"dangerousDisableAssetCspModification": true,
|
||||||
"assetProtocol": {
|
"assetProtocol": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
|
|||||||
@@ -187,17 +187,8 @@ export function useAssistantManager({
|
|||||||
const onOpened = selectedSearchContent?.on_opened;
|
const onOpened = selectedSearchContent?.on_opened;
|
||||||
|
|
||||||
if (onOpened?.Extension?.ty?.View) {
|
if (onOpened?.Extension?.ty?.View) {
|
||||||
const { setViewExtensionOpened } = useSearchStore.getState();
|
|
||||||
const viewData = onOpened.Extension.ty.View;
|
|
||||||
const extensionPermission = onOpened.Extension.permission;
|
|
||||||
|
|
||||||
clearSearchValue();
|
clearSearchValue();
|
||||||
return setViewExtensionOpened([
|
return platformAdapter.invokeBackend("open", { onOpened: onOpened, extraArgs: null });
|
||||||
viewData.page,
|
|
||||||
extensionPermission,
|
|
||||||
viewData.ui,
|
|
||||||
selectedSearchContent as any,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,11 +106,12 @@ export default function SearchIcons({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (viewExtensionOpened) {
|
if (viewExtensionOpened) {
|
||||||
const { title, icon } = viewExtensionOpened[3];
|
const name = viewExtensionOpened[0];
|
||||||
|
const icon = viewExtensionOpened[1];
|
||||||
|
|
||||||
const iconPath = icon ? platformAdapter.convertFileSrc(icon) : void 0;
|
const iconPath = icon ? platformAdapter.convertFileSrc(icon) : void 0;
|
||||||
|
|
||||||
return <MultilevelWrapper title={title} icon={iconPath} />;
|
return <MultilevelWrapper title={name} icon={iconPath} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { useShortcutsStore } from "@/stores/shortcutsStore";
|
|||||||
|
|
||||||
const ViewExtension: React.FC = () => {
|
const ViewExtension: React.FC = () => {
|
||||||
const { viewExtensionOpened } = useSearchStore();
|
const { viewExtensionOpened } = useSearchStore();
|
||||||
const [page, setPage] = useState<string>("");
|
|
||||||
// Complete list of the backend APIs, grouped by their category.
|
// Complete list of the backend APIs, grouped by their category.
|
||||||
const [apis, setApis] = useState<Map<string, string[]> | null>(null);
|
const [apis, setApis] = useState<Map<string, string[]> | null>(null);
|
||||||
const { setModifierKeyPressed } = useShortcutsStore();
|
const { setModifierKeyPressed } = useShortcutsStore();
|
||||||
@@ -23,24 +22,6 @@ const ViewExtension: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tauri/webview is not allowed to access local files directly,
|
|
||||||
// use convertFileSrc to work around the issue.
|
|
||||||
useEffect(() => {
|
|
||||||
const setupFileUrl = async () => {
|
|
||||||
// The check above ensures viewExtensionOpened is not null here.
|
|
||||||
const page = viewExtensionOpened[0];
|
|
||||||
|
|
||||||
// Only convert to file source if it's a local file path, not a URL
|
|
||||||
if (page.startsWith("http://") || page.startsWith("https://")) {
|
|
||||||
setPage(page);
|
|
||||||
} else {
|
|
||||||
setPage(platformAdapter.convertFileSrc(page));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setupFileUrl();
|
|
||||||
}, [viewExtensionOpened]);
|
|
||||||
|
|
||||||
// invoke `apis()` and set the state
|
// invoke `apis()` and set the state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModifierKeyPressed(false);
|
setModifierKeyPressed(false);
|
||||||
@@ -60,7 +41,7 @@ const ViewExtension: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// White list of the permission entries
|
// White list of the permission entries
|
||||||
const permission = viewExtensionOpened[1];
|
const permission = viewExtensionOpened[3];
|
||||||
|
|
||||||
// apis is in format {"category": ["api1", "api2"]}, to make the permission check
|
// apis is in format {"category": ["api1", "api2"]}, to make the permission check
|
||||||
// easier, reverse the map key values: {"api1": "category", "api2": "category"}
|
// easier, reverse the map key values: {"api1": "category", "api2": "category"}
|
||||||
@@ -182,9 +163,11 @@ const ViewExtension: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [reversedApis, permission]); // Add apiPermissions as dependency
|
}, [reversedApis, permission]); // Add apiPermissions as dependency
|
||||||
|
|
||||||
|
const fileUrl = viewExtensionOpened[2];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
src={page}
|
src={fileUrl}
|
||||||
className="w-full h-full border-0"
|
className="w-full h-full border-0"
|
||||||
onLoad={(event) => {
|
onLoad={(event) => {
|
||||||
event.currentTarget.focus();
|
event.currentTarget.focus();
|
||||||
|
|||||||
@@ -3,15 +3,18 @@ import {
|
|||||||
ExtensionPermission,
|
ExtensionPermission,
|
||||||
ViewExtensionUISettings,
|
ViewExtensionUISettings,
|
||||||
} from "@/components/Settings/Extensions";
|
} from "@/components/Settings/Extensions";
|
||||||
import { SearchDocument } from "@/types/search";
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
export type ViewExtensionOpened = [
|
export type ViewExtensionOpened = [
|
||||||
|
// Extension name
|
||||||
|
string,
|
||||||
|
// An absolute path to the extension icon or a font code.
|
||||||
|
string,
|
||||||
|
// HTML file URL
|
||||||
string,
|
string,
|
||||||
ExtensionPermission | null,
|
ExtensionPermission | null,
|
||||||
ViewExtensionUISettings | null,
|
ViewExtensionUISettings | null,
|
||||||
SearchDocument
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export type ISearchStore = {
|
export type ISearchStore = {
|
||||||
@@ -55,12 +58,6 @@ export type ISearchStore = {
|
|||||||
setVisibleExtensionDetail: (visibleExtensionDetail: boolean) => void;
|
setVisibleExtensionDetail: (visibleExtensionDetail: boolean) => void;
|
||||||
|
|
||||||
// When we open a View extension, we set this to a non-null value.
|
// When we open a View extension, we set this to a non-null value.
|
||||||
//
|
|
||||||
// Arguments
|
|
||||||
//
|
|
||||||
// The first array element is the path to the page that we should load
|
|
||||||
// The second element is the permission that this extension requires.
|
|
||||||
// The third argument is the UI Settings
|
|
||||||
viewExtensionOpened?: ViewExtensionOpened;
|
viewExtensionOpened?: ViewExtensionOpened;
|
||||||
setViewExtensionOpened: (showViewExtension?: ViewExtensionOpened) => void;
|
setViewExtensionOpened: (showViewExtension?: ViewExtensionOpened) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ export const visibleSearchBar = () => {
|
|||||||
|
|
||||||
if (isNil(viewExtensionOpened)) return true;
|
if (isNil(viewExtensionOpened)) return true;
|
||||||
|
|
||||||
const [, , ui] = viewExtensionOpened;
|
const ui = viewExtensionOpened[4];
|
||||||
|
|
||||||
return ui?.search_bar ?? true;
|
return ui?.search_bar ?? true;
|
||||||
};
|
};
|
||||||
@@ -312,7 +312,7 @@ export const visibleFilterBar = () => {
|
|||||||
|
|
||||||
if (isNil(viewExtensionOpened)) return true;
|
if (isNil(viewExtensionOpened)) return true;
|
||||||
|
|
||||||
const [, , ui] = viewExtensionOpened;
|
const ui = viewExtensionOpened[4];
|
||||||
|
|
||||||
return ui?.filter_bar ?? true;
|
return ui?.filter_bar ?? true;
|
||||||
};
|
};
|
||||||
@@ -322,7 +322,7 @@ export const visibleFooterBar = () => {
|
|||||||
|
|
||||||
if (isNil(viewExtensionOpened)) return true;
|
if (isNil(viewExtensionOpened)) return true;
|
||||||
|
|
||||||
const [, , ui] = viewExtensionOpened;
|
const ui = viewExtensionOpened[4];
|
||||||
|
|
||||||
return ui?.footer ?? true;
|
return ui?.footer ?? true;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user