diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index 52a83a5e22..e285f2162e 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -84,7 +84,7 @@ function ServerKey(props: ParentProps) { ) } -export function AppInterface(props: { defaultUrl?: string, root?: (props: ParentProps) => JSX.Element }) { +export function AppInterface(props: { defaultUrl?: string; root?: (props: ParentProps) => JSX.Element }) { const platform = usePlatform() const stored = (() => { @@ -105,6 +105,8 @@ export function AppInterface(props: { defaultUrl?: string, root?: (props: Parent return window.location.origin } + console.log("interface") + return ( @@ -119,7 +121,7 @@ export function AppInterface(props: { defaultUrl?: string, root?: (props: Parent - {props.root?.(rootProps) ?? rootProps.children} + {rootProps.children} diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index ad3d124b2c..00d04e36f1 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -1079,6 +1079,9 @@ const GlobalSyncContext = createContext>() export function GlobalSyncProvider(props: ParentProps) { const value = createGlobalSync() + createEffect(() => { + console.log({ ...value }) + }) return ( diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index 2d94befcf0..4e44d82039 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -1977,9 +1977,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png 0.17.16", @@ -2994,14 +2994,14 @@ dependencies = [ "listeners", "objc2", "objc2-web-kit", - "reqwest", + "reqwest 0.12.28", "semver", "serde", "serde_json", - "specta 2.0.0-rc.22 (git+https://github.com/specta-rs/specta?rev=5409c179845bce2dc4a7315870c83c8d80c0dee4)", + "specta", "specta-typescript", "tauri", - "tauri-build", + "tauri-build 2.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "tauri-plugin-clipboard-manager", "tauri-plugin-decorum", "tauri-plugin-deep-link", @@ -3895,6 +3895,40 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "reqwest" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + [[package]] name = "rfd" version = "0.16.0" @@ -4442,25 +4476,17 @@ dependencies = [ [[package]] name = "specta" version = "2.0.0-rc.22" -source = "git+https://github.com/specta-rs/specta?rev=5409c179845bce2dc4a7315870c83c8d80c0dee4#5409c179845bce2dc4a7315870c83c8d80c0dee4" +source = "git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0#f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" dependencies = [ "paste", "rustc_version", "specta-macros", ] -[[package]] -name = "specta" -version = "2.0.0-rc.22" -source = "git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0#f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" -dependencies = [ - "rustc_version", -] - [[package]] name = "specta-macros" version = "2.0.0-rc.18" -source = "git+https://github.com/specta-rs/specta?rev=5409c179845bce2dc4a7315870c83c8d80c0dee4#5409c179845bce2dc4a7315870c83c8d80c0dee4" +source = "git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0#f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" dependencies = [ "Inflector", "proc-macro2", @@ -4473,7 +4499,7 @@ name = "specta-serde" version = "0.0.9" source = "git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0#f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" dependencies = [ - "specta 2.0.0-rc.22 (git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0)", + "specta", ] [[package]] @@ -4481,7 +4507,7 @@ name = "specta-typescript" version = "0.0.9" source = "git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0#f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" dependencies = [ - "specta 2.0.0-rc.22 (git+https://github.com/specta-rs/specta?rev=f1e55f8c774a9506e3ca6bec1265ce5dda6831a0)", + "specta", "specta-serde", ] @@ -4695,8 +4721,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" version = "2.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3868da5508446a7cd08956d523ac3edf0a8bc20bf7e4038f9a95c2800d2033" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "anyhow", "bytes", @@ -4722,18 +4747,18 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.1", "serde", "serde_json", "serde_repr", "serialize-to-javascript", - "specta 2.0.0-rc.22 (git+https://github.com/specta-rs/specta?rev=5409c179845bce2dc4a7315870c83c8d80c0dee4)", + "specta", "swift-rs", - "tauri-build", + "tauri-build 2.5.3 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "tauri-macros", "tauri-runtime", "tauri-runtime-wry", - "tauri-utils", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "thiserror 2.0.18", "tokio", "tray-icon", @@ -4760,7 +4785,28 @@ dependencies = [ "semver", "serde", "serde_json", - "tauri-utils", + "tauri-utils 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tauri-winres", + "toml 0.9.11+spec-1.1.0", + "walkdir", +] + +[[package]] +name = "tauri-build" +version = "2.5.3" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "tauri-winres", "toml 0.9.11+spec-1.1.0", "walkdir", @@ -4769,8 +4815,7 @@ dependencies = [ [[package]] name = "tauri-codegen" version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa9844cefcf99554a16e0a278156ae73b0d8680bbc0e2ad1e4287aadd8489cf" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "base64 0.22.1", "brotli", @@ -4785,7 +4830,7 @@ dependencies = [ "serde_json", "sha2", "syn 2.0.114", - "tauri-utils", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "thiserror 2.0.18", "time", "url", @@ -4796,15 +4841,14 @@ dependencies = [ [[package]] name = "tauri-macros" version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3764a12f886d8245e66b7ee9b43ccc47883399be2019a61d80cf0f4117446fde" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.114", "tauri-codegen", - "tauri-utils", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", ] [[package]] @@ -4819,7 +4863,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "tauri-utils", + "tauri-utils 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.9.11+spec-1.1.0", "walkdir", ] @@ -4869,7 +4913,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "tauri-utils", + "tauri-utils 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", "tracing", "url", @@ -4911,7 +4955,7 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "tauri-utils", + "tauri-utils 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", "toml 0.9.11+spec-1.1.0", "url", @@ -4928,7 +4972,7 @@ dependencies = [ "data-url", "http", "regex", - "reqwest", + "reqwest 0.12.28", "schemars 0.8.22", "serde", "serde_json", @@ -5079,7 +5123,7 @@ dependencies = [ "minisign-verify", "osakit", "percent-encoding", - "reqwest", + "reqwest 0.12.28", "semver", "serde", "serde_json", @@ -5113,8 +5157,7 @@ dependencies = [ [[package]] name = "tauri-runtime" version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f766fe9f3d1efc4b59b17e7a891ad5ed195fa8d23582abb02e6c9a01137892" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "cookie", "dpi", @@ -5127,7 +5170,7 @@ dependencies = [ "raw-window-handle", "serde", "serde_json", - "tauri-utils", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "thiserror 2.0.18", "url", "webkit2gtk", @@ -5138,8 +5181,7 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" version = "2.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187a3f26f681bdf028f796ccf57cf478c1ee422c50128e5a0a6ebeb3f5910065" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "gtk", "http", @@ -5154,7 +5196,7 @@ dependencies = [ "softbuffer", "tao", "tauri-runtime", - "tauri-utils", + "tauri-utils 2.8.1 (git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee)", "url", "webkit2gtk", "webview2-com", @@ -5170,7 +5212,7 @@ dependencies = [ "heck 0.5.0", "serde", "serde_json", - "specta 2.0.0-rc.22 (git+https://github.com/specta-rs/specta?rev=5409c179845bce2dc4a7315870c83c8d80c0dee4)", + "specta", "specta-typescript", "tauri", "tauri-specta-macros", @@ -5194,6 +5236,42 @@ name = "tauri-utils" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a423c51176eb3616ee9b516a9fa67fed5f0e78baaba680e44eb5dd2cc37490" +dependencies = [ + "anyhow", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever", + "http", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 0.9.11+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-utils" +version = "2.8.1" +source = "git+https://github.com/tauri-apps/tauri?rev=4d5d78daf636feaac20c5bc48a6071491c4291ee#4d5d78daf636feaac20c5bc48a6071491c4291ee" dependencies = [ "anyhow", "brotli", @@ -6041,9 +6119,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -6065,9 +6143,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -6726,9 +6804,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.53.5" +version = "0.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2" +checksum = "5ed1a195b0375491dd15a7066a10251be217ce743cf4bbbbdcf5391d6473bee0" dependencies = [ "base64 0.22.1", "block2", diff --git a/packages/desktop/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml index 57a1556874..c7337f1d43 100644 --- a/packages/desktop/src-tauri/Cargo.toml +++ b/packages/desktop/src-tauri/Cargo.toml @@ -44,13 +44,13 @@ uuid = { version = "1.19.0", features = ["v4"] } tauri-plugin-decorum = "1.1.1" comrak = { version = "0.50", default-features = false } url = "2" -specta = { git = "https://github.com/specta-rs/specta", rev = "5409c179845bce2dc4a7315870c83c8d80c0dee4" } -specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" } -tauri-specta = { git = "https://github.com/specta-rs/tauri-specta", rev = "c0cd3c58e29191845e2bd2b30238246469903727", features = ["derive", "typescript"] } +specta = "=2.0.0-rc.22" +specta-typescript = "0.0.9" +tauri-specta = { version = "=2.0.0-rc.21", features = ["derive", "typescript"] } [target.'cfg(target_os = "linux")'.dependencies] gtk = "0.18.2" -webkit2gtk = "=2.0.1" +webkit2gtk = "=2.0.2" [target.'cfg(target_os = "macos")'.dependencies] objc2 = "0.6" @@ -65,6 +65,8 @@ windows = { version = "0.61", features = [ ] } [patch.crates-io] -specta = { git = "https://github.com/specta-rs/specta", rev = "5409c179845bce2dc4a7315870c83c8d80c0dee4" } -specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "f1e55f8c774a9506e3ca6bec1265ce5dda6831a0" } -tauri-specta = { git = "https://github.com/specta-rs/tauri-specta", rev = "c0cd3c58e29191845e2bd2b30238246469903727" } +specta = { git = "https://github.com/specta-rs/specta", rev = "106425eac4964d8ff34d3a02f1612e33117b08bb" } +specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "106425eac4964d8ff34d3a02f1612e33117b08bb" } +tauri-specta = { git = "https://github.com/specta-rs/tauri-specta", rev = "52c122a2110385857cbfc5a970016fa424007170" } +# TODO: https://github.com/tauri-apps/tauri/pull/14812 +# tauri = { git = "https://github.com/tauri-apps/tauri", rev = "4d5d78daf636feaac20c5bc48a6071491c4291ee" } diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index cb4ba3826b..0a2b9c9729 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -7,8 +7,8 @@ mod window_customizer; use cli::{install_cli, sync_cli}; use futures::channel::mpsc; +use futures::future; use futures::{FutureExt, StreamExt}; -use futures::{SinkExt, future}; #[cfg(windows)] use job_object::*; use specta_typescript::Typescript; @@ -27,7 +27,7 @@ use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult}; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; use tauri_plugin_store::StoreExt; -use tauri_specta::{Builder, collect_commands}; +use tauri_specta::{Builder, Event, collect_commands, collect_events}; use tokio::sync::oneshot; use crate::deep_link::DeepLinkAction; @@ -258,6 +258,20 @@ async fn check_server_health(url: &str, password: Option<&str>) -> bool { .unwrap_or(false) } +struct AppState { + ready_tx: tokio::sync::Mutex>>, +} + +#[tauri::command] +#[specta::specta] +async fn notify_ready(state: tauri::State<'_, AppState>) -> Result<(), ()> { + if let Some(ready_tx) = state.ready_tx.lock().await.take() { + let _ = ready_tx.send(()); + } + + Ok(()) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let updater_enabled = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some(); @@ -267,7 +281,7 @@ pub fn run() { .arg("opencode-cli") .output(); - let mut builder = Builder::::new() + let builder = Builder::::new() // Then register them (separated by a comma) .commands(collect_commands![ kill_sidecar, @@ -275,8 +289,10 @@ pub fn run() { ensure_server_ready, get_default_server_url, set_default_server_url, - markdown::parse_markdown_command - ]); + markdown::parse_markdown_command, + notify_ready + ]) + .events(collect_events![DeepLinkAction]); #[cfg(debug_assertions)] // <- Only export on non-release builds builder @@ -327,6 +343,11 @@ pub fn run() { #[cfg(windows)] app.manage(JobObjectState::new()); + let (ready_tx, ready_rx) = oneshot::channel::<()>(); + app.manage(AppState { + ready_tx: tokio::sync::Mutex::new(Some(ready_tx)) + }); + let primary_monitor = app.primary_monitor().ok().flatten(); let size = primary_monitor .map(|m| m.size().to_logical(m.scale_factor())) @@ -386,8 +407,6 @@ pub fn run() { } }); - let (ready_tx, ready_rx) = oneshot::channel::<()>(); - { let app = app.clone(); tauri::async_runtime::spawn(async move { @@ -444,7 +463,7 @@ pub fn run() { tauri::async_runtime::spawn(deeplink_rx.for_each(move |url| { if let Some(action) = DeepLinkAction::from_url(url) && let Some(window) = app.get_webview_window("main") { - let _ = window.emit("opencode:deep-link", action); + let _ = action.emit(&window); } future::ready(()) diff --git a/packages/desktop/src/bindings.ts b/packages/desktop/src/bindings.ts index 30c98f674d..efd157f756 100644 --- a/packages/desktop/src/bindings.ts +++ b/packages/desktop/src/bindings.ts @@ -1,123 +1,62 @@ +// This file has been generated by Tauri Specta. Do not edit this file manually. -// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. - -/** user-defined commands **/ - +import { invoke as __TAURI_INVOKE, Channel } from "@tauri-apps/api/core" +import * as __TAURI_EVENT from "@tauri-apps/api/event" +/** Commands */ export const commands = { -async killSidecar() : Promise { - await TAURI_INVOKE("kill_sidecar"); -}, -async installCli() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("install_cli") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} -}, -async ensureServerReady() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("ensure_server_ready") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} -}, -async getDefaultServerUrl() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_default_server_url") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} -}, -async setDefaultServerUrl(url: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("set_default_server_url", { url }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} -}, -async parseMarkdownCommand(markdown: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("parse_markdown_command", { markdown }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} -} + killSidecar: () => __TAURI_INVOKE("kill_sidecar"), + installCli: () => typedError(__TAURI_INVOKE("install_cli")), + ensureServerReady: () => typedError(__TAURI_INVOKE("ensure_server_ready")), + getDefaultServerUrl: () => typedError(__TAURI_INVOKE("get_default_server_url")), + setDefaultServerUrl: (url: string | null) => + typedError(__TAURI_INVOKE("set_default_server_url", { url })), + parseMarkdownCommand: (markdown: string) => + typedError(__TAURI_INVOKE("parse_markdown_command", { markdown })), + notifyReady: () => typedError(__TAURI_INVOKE("notify_ready")), } -/** user-defined events **/ - - - -/** user-defined constants **/ - - - -/** user-defined types **/ - -export type ServerReadyData = { url: string; password: string | null } - -/** tauri-specta globals **/ - -import { - invoke as TAURI_INVOKE, - Channel as TAURI_CHANNEL, -} from "@tauri-apps/api/core"; -import * as TAURI_API_EVENT from "@tauri-apps/api/event"; -import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow"; - -type __EventObj__ = { - listen: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - once: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; -}; - -export type Result = - | { status: "ok"; data: T } - | { status: "error"; error: E }; - -function __makeEvents__>( - mappings: Record, -) { - return new Proxy( - {} as unknown as { - [K in keyof T]: __EventObj__ & { - (handle: __WebviewWindow__): __EventObj__; - }; - }, - { - get: (_, event) => { - const name = mappings[event as keyof T]; - - return new Proxy((() => {}) as any, { - apply: (_, __, [window]: [__WebviewWindow__]) => ({ - listen: (arg: any) => window.listen(name, arg), - once: (arg: any) => window.once(name, arg), - emit: (arg: any) => window.emit(name, arg), - }), - get: (_, command: keyof __EventObj__) => { - switch (command) { - case "listen": - return (arg: any) => TAURI_API_EVENT.listen(name, arg); - case "once": - return (arg: any) => TAURI_API_EVENT.once(name, arg); - case "emit": - return (arg: any) => TAURI_API_EVENT.emit(name, arg); - } - }, - }); - }, - }, - ); +/** Events */ +export const events = { + deepLinkAction: makeEvent("deep-link-action"), +} + +/* Types */ +export type DeepLinkAction = { OpenProject: { directory: string; session: string | null } } + +export type ServerReadyData = { + url: string + password: string | null +} + +/* Tauri Specta runtime */ +async function typedError( + result: Promise, +): Promise<{ status: "ok"; data: T } | { status: "error"; error: E }> { + try { + return { status: "ok", data: await result } + } catch (e) { + if (e instanceof Error) throw e + return { status: "error", error: e as any } + } +} + +function makeEvent(name: string) { + const base = { + listen: (cb: __TAURI_EVENT.EventCallback) => __TAURI_EVENT.listen(name, cb), + once: (cb: __TAURI_EVENT.EventCallback) => __TAURI_EVENT.once(name, cb), + emit: (payload: T) => + __TAURI_EVENT.emit(name, payload) as unknown as T extends null + ? () => Promise + : (payload: T) => Promise, + } + + const fn = (target: import("@tauri-apps/api/webview").Webview | import("@tauri-apps/api/window").Window) => ({ + listen: (cb: __TAURI_EVENT.EventCallback) => target.listen(name, cb), + once: (cb: __TAURI_EVENT.EventCallback) => target.once(name, cb), + emit: (payload: T) => + target.emit(name, payload) as unknown as T extends null ? () => Promise : (payload: T) => Promise, + }) + + return Object.assign(fn, base) } diff --git a/packages/desktop/src/cli.ts b/packages/desktop/src/cli.ts index 5a8875cf89..cf2c6cf45b 100644 --- a/packages/desktop/src/cli.ts +++ b/packages/desktop/src/cli.ts @@ -1,15 +1,17 @@ -import { invoke } from "@tauri-apps/api/core" import { message } from "@tauri-apps/plugin-dialog" import { initI18n, t } from "./i18n" +import { commands } from "./bindings" export async function installCli(): Promise { await initI18n() - try { - const path = await invoke("install_cli") - await message(t("desktop.cli.installed.message", { path }), { title: t("desktop.cli.installed.title") }) - } catch (e) { - await message(t("desktop.cli.failed.message", { error: String(e) }), { title: t("desktop.cli.failed.title") }) + const res = await commands.installCli() + if (res.status === "ok") { + await message(t("desktop.cli.installed.message", { path: res.data }), { title: t("desktop.cli.installed.title") }) + } else { + await message(t("desktop.cli.failed.message", { error: String(res.error) }), { + title: t("desktop.cli.failed.title"), + }) } } diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 2e7ca136ac..ce10b75cda 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -3,11 +3,9 @@ import "./webview-zoom" import { render } from "solid-js/web" import { AppBaseProviders, AppInterface, PlatformProvider, Platform } from "@opencode-ai/app" import { open, save } from "@tauri-apps/plugin-dialog" -import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link" import { open as shellOpen } from "@tauri-apps/plugin-shell" import { type as ostype } from "@tauri-apps/plugin-os" import { check, Update } from "@tauri-apps/plugin-updater" -import { invoke } from "@tauri-apps/api/core" import { getCurrentWindow } from "@tauri-apps/api/window" import { isPermissionGranted, requestPermission } from "@tauri-apps/plugin-notification" import { relaunch } from "@tauri-apps/plugin-process" @@ -22,6 +20,7 @@ import { createMenu } from "./menu" import { initI18n, t } from "./i18n" import pkg from "../package.json" import "./styles.css" +import { commands, events } from "./bindings" const root = document.getElementById("root") if (import.meta.env.DEV && !(root instanceof HTMLElement)) { @@ -43,21 +42,21 @@ window.getComputedStyle = ((elt: Element, pseudoElt?: string | null) => { let update: Update | null = null -const deepLinkEvent = "opencode:deep-link" +// const deepLinkEvent = "opencode:deep-link" -const emitDeepLinks = (urls: string[]) => { - if (urls.length === 0) return - window.__OPENCODE__ ??= {} - const pending = window.__OPENCODE__.deepLinks ?? [] - window.__OPENCODE__.deepLinks = [...pending, ...urls] - window.dispatchEvent(new CustomEvent(deepLinkEvent, { detail: { urls } })) -} +// const emitDeepLinks = (urls: string[]) => { +// if (urls.length === 0) return +// window.__OPENCODE__ ??= {} +// const pending = window.__OPENCODE__.deepLinks ?? [] +// window.__OPENCODE__.deepLinks = [...pending, ...urls] +// window.dispatchEvent(new CustomEvent(deepLinkEvent, { detail: { urls } })) +// } -const listenForDeepLinks = async () => { - const startUrls = await getCurrent().catch(() => null) - if (startUrls?.length) emitDeepLinks(startUrls) - await onOpenUrl((urls) => emitDeepLinks(urls)).catch(() => undefined) -} +// const listenForDeepLinks = async () => { +// const startUrls = await getCurrent().catch(() => null) +// if (startUrls?.length) emitDeepLinks(startUrls) +// await onOpenUrl((urls) => emitDeepLinks(urls)).catch(() => undefined) +// } const createPlatform = (password: Accessor): Platform => ({ platform: "desktop", @@ -274,12 +273,12 @@ const createPlatform = (password: Accessor): Platform => ({ update: async () => { if (!UPDATER_ENABLED || !update) return - if (ostype() === "windows") await invoke("kill_sidecar").catch(() => undefined) + if (ostype() === "windows") await commands.killSidecar().catch(() => undefined) await update.install().catch(() => undefined) }, restart: async () => { - await invoke("kill_sidecar").catch(() => undefined) + await commands.killSidecar().catch(() => undefined) await relaunch() }, @@ -334,22 +333,21 @@ const createPlatform = (password: Accessor): Platform => ({ } }, - getDefaultServerUrl: async () => { - const result = await invoke("get_default_server_url").catch(() => null) - return result - }, + getDefaultServerUrl: () => commands.getDefaultServerUrl().then((v) => (v.status === "ok" ? v.data : null)), setDefaultServerUrl: async (url: string | null) => { - await invoke("set_default_server_url", { url }) + await commands.setDefaultServerUrl(url) }, - parseMarkdown: async (markdown: string) => { - return invoke("parse_markdown_command", { markdown }) - }, + parseMarkdown: (markdown) => + commands.parseMarkdownCommand(markdown).then((v) => { + if (v.status === "ok") return v.data + throw new Error(v.error) + }), }) createMenu() -void listenForDeepLinks() +// void listenForDeepLinks() render(() => { const [serverPassword, setServerPassword] = createSignal(null) @@ -379,6 +377,14 @@ render(() => { window.__OPENCODE__ ??= {} window.__OPENCODE__.serverPassword = data().password ?? undefined + onMount(() => { + commands.notifyReady() + }) + + events.deepLinkAction.listen((deepLink) => { + console.log({ deepLink }) + }) + return }} @@ -392,8 +398,9 @@ type ServerReadyData = { url: string; password: string | null } // Gate component that waits for the server to be ready function ServerGate(props: { children: (data: Accessor) => JSX.Element }) { const [serverData] = createResource(() => - invoke("ensure_server_ready").then((v) => { - return new Promise((res) => setTimeout(() => res(v as ServerReadyData), 2000)) + commands.ensureServerReady().then((v) => { + if (v.status === "ok") return v.data + throw new Error(v.error) }), ) @@ -406,7 +413,7 @@ function ServerGate(props: { children: (data: Accessor) => JSX. } const restartApp = async () => { - await invoke("kill_sidecar").catch(() => undefined) + await commands.killSidecar().catch(() => undefined) await relaunch().catch(() => undefined) } diff --git a/packages/desktop/src/menu.ts b/packages/desktop/src/menu.ts index 2edeff42b2..d410844042 100644 --- a/packages/desktop/src/menu.ts +++ b/packages/desktop/src/menu.ts @@ -1,11 +1,11 @@ import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu" import { type as ostype } from "@tauri-apps/plugin-os" -import { invoke } from "@tauri-apps/api/core" import { relaunch } from "@tauri-apps/plugin-process" import { runUpdater, UPDATER_ENABLED } from "./updater" import { installCli } from "./cli" import { initI18n, t } from "./i18n" +import { commands } from "./bindings" export async function createMenu() { if (ostype() !== "macos") return @@ -35,7 +35,7 @@ export async function createMenu() { }), await MenuItem.new({ action: async () => { - await invoke("kill_sidecar").catch(() => undefined) + await commands.killSidecar().catch(() => undefined) await relaunch().catch(() => undefined) }, text: t("desktop.menu.restart"), diff --git a/packages/desktop/src/updater.ts b/packages/desktop/src/updater.ts index b48bb6be02..7326696338 100644 --- a/packages/desktop/src/updater.ts +++ b/packages/desktop/src/updater.ts @@ -1,10 +1,10 @@ import { check } from "@tauri-apps/plugin-updater" import { relaunch } from "@tauri-apps/plugin-process" import { ask, message } from "@tauri-apps/plugin-dialog" -import { invoke } from "@tauri-apps/api/core" import { type as ostype } from "@tauri-apps/plugin-os" import { initI18n, t } from "./i18n" +import { commands } from "./bindings" export const UPDATER_ENABLED = window.__OPENCODE__?.updaterEnabled ?? false @@ -39,13 +39,13 @@ export async function runUpdater({ alertOnFail }: { alertOnFail: boolean }) { if (!shouldUpdate) return try { - if (ostype() === "windows") await invoke("kill_sidecar") + if (ostype() === "windows") await commands.killSidecar() await update.install() } catch { await message(t("desktop.updater.installFailed.message"), { title: t("desktop.updater.installFailed.title") }) return } - await invoke("kill_sidecar") + await commands.killSidecar() await relaunch() }