mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-09 10:24:11 +00:00
Compare commits
2 Commits
sqlite2
...
brendan/sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bef9938c0a | ||
|
|
f69ea40e55 |
@@ -30,7 +30,7 @@ import { HighlightsProvider } from "@/context/highlights"
|
||||
import Layout from "@/pages/layout"
|
||||
import DirectoryLayout from "@/pages/directory-layout"
|
||||
import { ErrorPage } from "./pages/error"
|
||||
import { Suspense } from "solid-js"
|
||||
import { Suspense, JSX } from "solid-js"
|
||||
|
||||
const Home = lazy(() => import("@/pages/home"))
|
||||
const Session = lazy(() => import("@/pages/session"))
|
||||
@@ -84,7 +84,7 @@ function ServerKey(props: ParentProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function AppInterface(props: { defaultUrl?: string }) {
|
||||
export function AppInterface(props: { defaultUrl?: string; root?: (props: ParentProps) => JSX.Element }) {
|
||||
const platform = usePlatform()
|
||||
|
||||
const stored = (() => {
|
||||
@@ -105,13 +105,15 @@ export function AppInterface(props: { defaultUrl?: string }) {
|
||||
return window.location.origin
|
||||
}
|
||||
|
||||
console.log("interface")
|
||||
|
||||
return (
|
||||
<ServerProvider defaultUrl={defaultServerUrl()}>
|
||||
<ServerKey>
|
||||
<GlobalSDKProvider>
|
||||
<GlobalSyncProvider>
|
||||
<Router
|
||||
root={(props) => (
|
||||
root={(rootProps) => (
|
||||
<SettingsProvider>
|
||||
<PermissionProvider>
|
||||
<LayoutProvider>
|
||||
@@ -119,7 +121,7 @@ export function AppInterface(props: { defaultUrl?: string }) {
|
||||
<ModelsProvider>
|
||||
<CommandProvider>
|
||||
<HighlightsProvider>
|
||||
<Layout>{props.children}</Layout>
|
||||
<Layout>{rootProps.children}</Layout>
|
||||
</HighlightsProvider>
|
||||
</CommandProvider>
|
||||
</ModelsProvider>
|
||||
|
||||
@@ -1079,6 +1079,9 @@ const GlobalSyncContext = createContext<ReturnType<typeof createGlobalSync>>()
|
||||
|
||||
export function GlobalSyncProvider(props: ParentProps) {
|
||||
const value = createGlobalSync()
|
||||
createEffect(() => {
|
||||
console.log({ ...value })
|
||||
})
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={value.ready}>
|
||||
|
||||
1223
packages/desktop/src-tauri/Cargo.lock
generated
1223
packages/desktop/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -43,10 +43,14 @@ reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"
|
||||
uuid = { version = "1.19.0", features = ["v4"] }
|
||||
tauri-plugin-decorum = "1.1.1"
|
||||
comrak = { version = "0.50", default-features = false }
|
||||
url = "2"
|
||||
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"
|
||||
@@ -59,3 +63,10 @@ windows = { version = "0.61", features = [
|
||||
"Win32_System_Threading",
|
||||
"Win32_Security"
|
||||
] }
|
||||
|
||||
[patch.crates-io]
|
||||
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" }
|
||||
|
||||
@@ -51,6 +51,7 @@ fn is_cli_installed() -> bool {
|
||||
const INSTALL_SCRIPT: &str = include_str!("../../../../install");
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn install_cli(app: tauri::AppHandle) -> Result<String, String> {
|
||||
if cfg!(not(unix)) {
|
||||
return Err("CLI installation is only supported on macOS & Linux".to_string());
|
||||
|
||||
28
packages/desktop/src-tauri/src/deep_link.rs
Normal file
28
packages/desktop/src-tauri/src/deep_link.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(serde::Serialize, Clone, tauri_specta::Event, specta::Type)]
|
||||
pub enum DeepLinkAction {
|
||||
OpenProject {
|
||||
directory: String,
|
||||
session: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl DeepLinkAction {
|
||||
pub fn from_url(url: url::Url) -> Option<Self> {
|
||||
if url.scheme() != "opencode" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let action = url.path().trim_start_matches('/');
|
||||
let mut query_pairs: BTreeMap<_, _> = url.query_pairs().collect();
|
||||
|
||||
match action {
|
||||
"open-project" => Some(DeepLinkAction::OpenProject {
|
||||
directory: query_pairs.remove("directory")?.to_string(),
|
||||
session: query_pairs.remove("session").map(|v| v.to_string()),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,42 @@
|
||||
mod cli;
|
||||
mod deep_link;
|
||||
#[cfg(windows)]
|
||||
mod job_object;
|
||||
mod markdown;
|
||||
mod window_customizer;
|
||||
|
||||
use cli::{install_cli, sync_cli};
|
||||
use futures::FutureExt;
|
||||
use futures::channel::mpsc;
|
||||
use futures::future;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
#[cfg(windows)]
|
||||
use job_object::*;
|
||||
use specta_typescript::Typescript;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
net::TcpListener,
|
||||
sync::{Arc, Mutex},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tauri::{AppHandle, LogicalSize, Manager, RunEvent, State, WebviewWindowBuilder};
|
||||
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri::{AppHandle, Emitter, LogicalSize, Manager, RunEvent, State, WebviewWindowBuilder};
|
||||
#[cfg(windows)]
|
||||
use tauri_plugin_decorum::WebviewWindowExt;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
|
||||
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, Event, collect_commands, collect_events};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::deep_link::DeepLinkAction;
|
||||
use crate::window_customizer::PinchZoomDisablePlugin;
|
||||
|
||||
const SETTINGS_STORE: &str = "opencode.settings.dat";
|
||||
const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
#[derive(Clone, serde::Serialize, specta::Type)]
|
||||
struct ServerReadyData {
|
||||
url: String,
|
||||
password: Option<String>,
|
||||
@@ -64,6 +70,7 @@ struct LogState(Arc<Mutex<VecDeque<String>>>);
|
||||
const MAX_LOG_ENTRIES: usize = 200;
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
fn kill_sidecar(app: AppHandle) {
|
||||
let Some(server_state) = app.try_state::<ServerState>() else {
|
||||
println!("Server not running");
|
||||
@@ -97,6 +104,7 @@ async fn get_logs(app: AppHandle) -> Result<String, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
async fn ensure_server_ready(state: State<'_, ServerState>) -> Result<ServerReadyData, String> {
|
||||
state
|
||||
.status
|
||||
@@ -106,6 +114,7 @@ async fn ensure_server_ready(state: State<'_, ServerState>) -> Result<ServerRead
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
fn get_default_server_url(app: AppHandle) -> Result<Option<String>, String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
@@ -119,6 +128,7 @@ fn get_default_server_url(app: AppHandle) -> Result<Option<String>, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
async fn set_default_server_url(app: AppHandle, url: Option<String>) -> Result<(), String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
@@ -248,6 +258,20 @@ async fn check_server_health(url: &str, password: Option<&str>) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
ready_tx: tokio::sync::Mutex<Option<oneshot::Sender<()>>>,
|
||||
}
|
||||
|
||||
#[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();
|
||||
@@ -257,6 +281,24 @@ pub fn run() {
|
||||
.arg("opencode-cli")
|
||||
.output();
|
||||
|
||||
let builder = Builder::<tauri::Wry>::new()
|
||||
// Then register them (separated by a comma)
|
||||
.commands(collect_commands![
|
||||
kill_sidecar,
|
||||
install_cli,
|
||||
ensure_server_ready,
|
||||
get_default_server_url,
|
||||
set_default_server_url,
|
||||
markdown::parse_markdown_command,
|
||||
notify_ready
|
||||
])
|
||||
.events(collect_events![DeepLinkAction]);
|
||||
|
||||
#[cfg(debug_assertions)] // <- Only export on non-release builds
|
||||
builder
|
||||
.export(Typescript::default(), "../src/bindings.ts")
|
||||
.expect("Failed to export typescript bindings");
|
||||
|
||||
let mut builder = tauri::Builder::default()
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
// Focus existing window when another instance is launched
|
||||
@@ -285,15 +327,11 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(PinchZoomDisablePlugin)
|
||||
.plugin(tauri_plugin_decorum::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
kill_sidecar,
|
||||
install_cli,
|
||||
ensure_server_ready,
|
||||
get_default_server_url,
|
||||
set_default_server_url,
|
||||
markdown::parse_markdown_command
|
||||
])
|
||||
.invoke_handler(builder.invoke_handler())
|
||||
.setup(move |app| {
|
||||
// This is also required if you want to use events
|
||||
builder.mount_events(app);
|
||||
|
||||
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
|
||||
app.deep_link().register_all().ok();
|
||||
|
||||
@@ -305,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()))
|
||||
@@ -344,14 +387,26 @@ pub fn run() {
|
||||
)
|
||||
.decorations(false);
|
||||
|
||||
let window = window_builder.build().expect("Failed to create window");
|
||||
let _window = window_builder.build().expect("Failed to create window");
|
||||
|
||||
#[cfg(windows)]
|
||||
let _ = window.create_overlay_titlebar();
|
||||
let _ = _window.create_overlay_titlebar();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
app.manage(ServerState::new(None, rx));
|
||||
|
||||
let (deeplink_tx, deeplink_rx) = mpsc::unbounded();
|
||||
|
||||
for url in app.deep_link().get_current().ok().flatten().unwrap_or_default() {
|
||||
let _ = deeplink_tx.unbounded_send(url);
|
||||
}
|
||||
|
||||
app.deep_link().on_open_url(move |e| {
|
||||
for url in e.urls() {
|
||||
let _ = deeplink_tx.unbounded_send(url);
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
let app = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
@@ -398,6 +453,25 @@ pub fn run() {
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let app = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let Ok(_) = ready_rx.await else {
|
||||
return
|
||||
};
|
||||
|
||||
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 _ = action.emit(&window);
|
||||
}
|
||||
|
||||
future::ready(())
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use comrak::{create_formatter, parse_document, Arena, Options, html::ChildRendering, nodes::NodeValue};
|
||||
use comrak::{
|
||||
Arena, Options, create_formatter, html::ChildRendering, nodes::NodeValue, parse_document,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
|
||||
create_formatter!(ExternalLinkFormatter, {
|
||||
@@ -55,6 +57,7 @@ pub fn parse_markdown(input: &str) -> String {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn parse_markdown_command(markdown: String) -> Result<String, String> {
|
||||
Ok(parse_markdown(&markdown))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use tauri::{plugin::Plugin, Manager, Runtime, Window};
|
||||
use tauri::{Manager, Runtime, Window, plugin::Plugin};
|
||||
|
||||
pub struct PinchZoomDisablePlugin;
|
||||
|
||||
@@ -21,8 +21,8 @@ impl<R: Runtime> Plugin<R> for PinchZoomDisablePlugin {
|
||||
let _ = webview_window.with_webview(|_webview| {
|
||||
#[cfg(target_os = "linux")]
|
||||
unsafe {
|
||||
use gtk::glib::ObjectExt;
|
||||
use gtk::GestureZoom;
|
||||
use gtk::glib::ObjectExt;
|
||||
use webkit2gtk::glib::gobject_ffi;
|
||||
|
||||
if let Some(data) = _webview.inner().data::<GestureZoom>("wk-view-zoom-gesture") {
|
||||
|
||||
62
packages/desktop/src/bindings.ts
Normal file
62
packages/desktop/src/bindings.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
// This file has been generated by Tauri Specta. Do not edit this file manually.
|
||||
|
||||
import { invoke as __TAURI_INVOKE, Channel } from "@tauri-apps/api/core"
|
||||
import * as __TAURI_EVENT from "@tauri-apps/api/event"
|
||||
|
||||
/** Commands */
|
||||
export const commands = {
|
||||
killSidecar: () => __TAURI_INVOKE<void>("kill_sidecar"),
|
||||
installCli: () => typedError<string, string>(__TAURI_INVOKE("install_cli")),
|
||||
ensureServerReady: () => typedError<ServerReadyData, string>(__TAURI_INVOKE("ensure_server_ready")),
|
||||
getDefaultServerUrl: () => typedError<string | null, string>(__TAURI_INVOKE("get_default_server_url")),
|
||||
setDefaultServerUrl: (url: string | null) =>
|
||||
typedError<null, string>(__TAURI_INVOKE("set_default_server_url", { url })),
|
||||
parseMarkdownCommand: (markdown: string) =>
|
||||
typedError<string, string>(__TAURI_INVOKE("parse_markdown_command", { markdown })),
|
||||
notifyReady: () => typedError<null, null>(__TAURI_INVOKE("notify_ready")),
|
||||
}
|
||||
|
||||
/** Events */
|
||||
export const events = {
|
||||
deepLinkAction: makeEvent<DeepLinkAction>("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<T, E>(
|
||||
result: Promise<T>,
|
||||
): 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<T>(name: string) {
|
||||
const base = {
|
||||
listen: (cb: __TAURI_EVENT.EventCallback<T>) => __TAURI_EVENT.listen(name, cb),
|
||||
once: (cb: __TAURI_EVENT.EventCallback<T>) => __TAURI_EVENT.once(name, cb),
|
||||
emit: (payload: T) =>
|
||||
__TAURI_EVENT.emit(name, payload) as unknown as T extends null
|
||||
? () => Promise<void>
|
||||
: (payload: T) => Promise<void>,
|
||||
}
|
||||
|
||||
const fn = (target: import("@tauri-apps/api/webview").Webview | import("@tauri-apps/api/window").Window) => ({
|
||||
listen: (cb: __TAURI_EVENT.EventCallback<T>) => target.listen(name, cb),
|
||||
once: (cb: __TAURI_EVENT.EventCallback<T>) => target.once(name, cb),
|
||||
emit: (payload: T) =>
|
||||
target.emit(name, payload) as unknown as T extends null ? () => Promise<void> : (payload: T) => Promise<void>,
|
||||
})
|
||||
|
||||
return Object.assign(fn, base)
|
||||
}
|
||||
@@ -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<void> {
|
||||
await initI18n()
|
||||
|
||||
try {
|
||||
const path = await invoke<string>("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"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string | null>): Platform => ({
|
||||
platform: "desktop",
|
||||
@@ -274,12 +273,12 @@ const createPlatform = (password: Accessor<string | null>): 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<string | null>): Platform => ({
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultServerUrl: async () => {
|
||||
const result = await invoke<string | null>("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<string>("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<string | null>(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 <AppInterface defaultUrl={data().url} />
|
||||
}}
|
||||
</ServerGate>
|
||||
@@ -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<ServerReadyData>) => JSX.Element }) {
|
||||
const [serverData] = createResource<ServerReadyData>(() =>
|
||||
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<ServerReadyData>) => JSX.
|
||||
}
|
||||
|
||||
const restartApp = async () => {
|
||||
await invoke("kill_sidecar").catch(() => undefined)
|
||||
await commands.killSidecar().catch(() => undefined)
|
||||
await relaunch().catch(() => undefined)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user