mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-24 06:45:22 +00:00
basic wsl impl
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Component, createMemo, type JSX } from "solid-js"
|
||||
import { Component, createEffect, createMemo, type JSX, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { Select } from "@opencode-ai/ui/select"
|
||||
@@ -36,8 +36,12 @@ export const SettingsGeneral: Component = () => {
|
||||
const platform = usePlatform()
|
||||
const settings = useSettings()
|
||||
|
||||
type BackendMode = "native" | "wsl"
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
checking: false,
|
||||
backend: "native" as BackendMode,
|
||||
backendReady: false,
|
||||
})
|
||||
|
||||
const check = () => {
|
||||
@@ -111,6 +115,34 @@ export const SettingsGeneral: Component = () => {
|
||||
})),
|
||||
)
|
||||
|
||||
const backendOptions = createMemo(() => [
|
||||
{ value: "native" as const, label: language.t("settings.desktop.backend.option.native") },
|
||||
{ value: "wsl" as const, label: language.t("settings.desktop.backend.option.wsl") },
|
||||
])
|
||||
|
||||
const showBackend = () =>
|
||||
platform.platform === "desktop" &&
|
||||
platform.os === "windows" &&
|
||||
!!platform.getBackendConfig &&
|
||||
!!platform.setBackendConfig
|
||||
|
||||
createEffect(() => {
|
||||
if (!showBackend()) return
|
||||
if (store.backendReady) return
|
||||
const get = platform.getBackendConfig
|
||||
if (!get) {
|
||||
setStore("backendReady", true)
|
||||
return
|
||||
}
|
||||
|
||||
void Promise.resolve(get())
|
||||
.then((config) => {
|
||||
const mode = config?.mode === "wsl" ? "wsl" : "native"
|
||||
setStore({ backend: mode, backendReady: true })
|
||||
})
|
||||
.catch(() => setStore("backendReady", true))
|
||||
})
|
||||
|
||||
const fontOptions = [
|
||||
{ value: "ibm-plex-mono", label: "font.option.ibmPlexMono" },
|
||||
{ value: "cascadia-code", label: "font.option.cascadiaCode" },
|
||||
@@ -363,6 +395,38 @@ export const SettingsGeneral: Component = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={showBackend()}>
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.desktop.section.backend")}</h3>
|
||||
|
||||
<div class="bg-surface-raised-base px-4 rounded-lg">
|
||||
<SettingsRow
|
||||
title={language.t("settings.desktop.backend.title")}
|
||||
description={language.t("settings.desktop.backend.description")}
|
||||
>
|
||||
<Select
|
||||
data-action="settings-backend"
|
||||
options={backendOptions()}
|
||||
current={backendOptions().find((o) => o.value === store.backend)}
|
||||
value={(option) => option.value}
|
||||
label={(option) => option.label}
|
||||
onSelect={(option) => {
|
||||
if (!option) return
|
||||
const setBackend = platform.setBackendConfig
|
||||
if (!setBackend) return
|
||||
setStore("backend", option.value)
|
||||
void Promise.resolve(setBackend({ mode: option.value }))
|
||||
}}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
disabled={!store.backendReady}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Updates Section */}
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.updates")}</h3>
|
||||
|
||||
@@ -33,6 +33,9 @@ export type Platform = {
|
||||
/** Open directory picker dialog (native on Tauri, server-backed on web) */
|
||||
openDirectoryPickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
|
||||
|
||||
/** Whether native pickers should be used (desktop only) */
|
||||
supportsNativePickers?(): boolean
|
||||
|
||||
/** Open native file picker dialog (Tauri only) */
|
||||
openFilePickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
|
||||
|
||||
@@ -57,6 +60,12 @@ export type Platform = {
|
||||
/** Set the default server URL to use on app startup (platform-specific) */
|
||||
setDefaultServerUrl?(url: string | null): Promise<void> | void
|
||||
|
||||
/** Get the configured backend mode (desktop only) */
|
||||
getBackendConfig?(): Promise<{ mode: "native" | "wsl" } | null> | { mode: "native" | "wsl" } | null
|
||||
|
||||
/** Set the configured backend mode (desktop only) */
|
||||
setBackendConfig?(config: { mode: "native" | "wsl" }): Promise<void> | void
|
||||
|
||||
/** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
|
||||
parseMarkdown?(markdown: string): Promise<string>
|
||||
|
||||
|
||||
@@ -508,6 +508,11 @@ export const dict = {
|
||||
"settings.section.server": "الخادم",
|
||||
"settings.tab.general": "عام",
|
||||
"settings.tab.shortcuts": "اختصارات",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "المظهر",
|
||||
"settings.general.section.notifications": "إشعارات النظام",
|
||||
|
||||
@@ -512,6 +512,11 @@ export const dict = {
|
||||
"settings.section.server": "Servidor",
|
||||
"settings.tab.general": "Geral",
|
||||
"settings.tab.shortcuts": "Atalhos",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Aparência",
|
||||
"settings.general.section.notifications": "Notificações do sistema",
|
||||
|
||||
@@ -539,6 +539,11 @@ export const dict = {
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Opšte",
|
||||
"settings.tab.shortcuts": "Prečice",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Izgled",
|
||||
"settings.general.section.notifications": "Sistemske obavijesti",
|
||||
|
||||
@@ -512,6 +512,11 @@ export const dict = {
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Generelt",
|
||||
"settings.tab.shortcuts": "Genveje",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Udseende",
|
||||
"settings.general.section.notifications": "Systemmeddelelser",
|
||||
|
||||
@@ -556,6 +556,11 @@ export const dict = {
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Allgemein",
|
||||
"settings.tab.shortcuts": "Tastenkombinationen",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Erscheinungsbild",
|
||||
"settings.general.section.notifications": "Systembenachrichtigungen",
|
||||
|
||||
@@ -583,6 +583,11 @@ export const dict = {
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "General",
|
||||
"settings.tab.shortcuts": "Shortcuts",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Appearance",
|
||||
"settings.general.section.notifications": "System notifications",
|
||||
|
||||
@@ -515,6 +515,11 @@ export const dict = {
|
||||
"settings.section.server": "Servidor",
|
||||
"settings.tab.general": "General",
|
||||
"settings.tab.shortcuts": "Atajos",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Apariencia",
|
||||
"settings.general.section.notifications": "Notificaciones del sistema",
|
||||
|
||||
@@ -522,6 +522,11 @@ export const dict = {
|
||||
"settings.section.server": "Serveur",
|
||||
"settings.tab.general": "Général",
|
||||
"settings.tab.shortcuts": "Raccourcis",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Apparence",
|
||||
"settings.general.section.notifications": "Notifications système",
|
||||
|
||||
@@ -507,6 +507,11 @@ export const dict = {
|
||||
"settings.section.server": "サーバー",
|
||||
"settings.tab.general": "一般",
|
||||
"settings.tab.shortcuts": "ショートカット",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "外観",
|
||||
"settings.general.section.notifications": "システム通知",
|
||||
|
||||
@@ -513,6 +513,11 @@ export const dict = {
|
||||
"settings.section.server": "서버",
|
||||
"settings.tab.general": "일반",
|
||||
"settings.tab.shortcuts": "단축키",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "모양",
|
||||
"settings.general.section.notifications": "시스템 알림",
|
||||
|
||||
@@ -515,6 +515,11 @@ export const dict = {
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Generelt",
|
||||
"settings.tab.shortcuts": "Snarveier",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Utseende",
|
||||
"settings.general.section.notifications": "Systemvarsler",
|
||||
|
||||
@@ -514,6 +514,11 @@ export const dict = {
|
||||
"settings.section.server": "Serwer",
|
||||
"settings.tab.general": "Ogólne",
|
||||
"settings.tab.shortcuts": "Skróty",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Wygląd",
|
||||
"settings.general.section.notifications": "Powiadomienia systemowe",
|
||||
|
||||
@@ -517,6 +517,11 @@ export const dict = {
|
||||
"settings.section.server": "Сервер",
|
||||
"settings.tab.general": "Основные",
|
||||
"settings.tab.shortcuts": "Горячие клавиши",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "Внешний вид",
|
||||
"settings.general.section.notifications": "Системные уведомления",
|
||||
|
||||
@@ -516,6 +516,11 @@ export const dict = {
|
||||
"settings.section.server": "เซิร์ฟเวอร์",
|
||||
"settings.tab.general": "ทั่วไป",
|
||||
"settings.tab.shortcuts": "ทางลัด",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "รูปลักษณ์",
|
||||
"settings.general.section.notifications": "การแจ้งเตือนระบบ",
|
||||
|
||||
@@ -548,6 +548,11 @@ export const dict = {
|
||||
"settings.section.server": "服务器",
|
||||
"settings.tab.general": "通用",
|
||||
"settings.tab.shortcuts": "快捷键",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "外观",
|
||||
"settings.general.section.notifications": "系统通知",
|
||||
|
||||
@@ -545,6 +545,11 @@ export const dict = {
|
||||
"settings.section.server": "伺服器",
|
||||
"settings.tab.general": "一般",
|
||||
"settings.tab.shortcuts": "快速鍵",
|
||||
"settings.desktop.section.backend": "Backend",
|
||||
"settings.desktop.backend.title": "Server backend",
|
||||
"settings.desktop.backend.description": "Choose where the OpenCode server runs.",
|
||||
"settings.desktop.backend.option.native": "Native (Windows)",
|
||||
"settings.desktop.backend.option.wsl": "WSL (Linux)",
|
||||
|
||||
"settings.general.section.appearance": "外觀",
|
||||
"settings.general.section.notifications": "系統通知",
|
||||
|
||||
@@ -46,7 +46,8 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal()) {
|
||||
const allowNative = platform.supportsNativePickers?.() !== false
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal() && allowNative) {
|
||||
const result = await platform.openDirectoryPickerDialog?.({
|
||||
title: language.t("command.project.open"),
|
||||
multiple: true,
|
||||
|
||||
@@ -1182,7 +1182,8 @@ export default function Layout(props: ParentProps) {
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal()) {
|
||||
const allowNative = platform.supportsNativePickers?.() !== false
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal() && allowNative) {
|
||||
const result = await platform.openDirectoryPickerDialog?.({
|
||||
title: language.t("command.project.open"),
|
||||
multiple: true,
|
||||
|
||||
@@ -3,8 +3,9 @@ use tauri_plugin_shell::{
|
||||
ShellExt,
|
||||
process::{Command, CommandChild, CommandEvent},
|
||||
};
|
||||
use tauri_plugin_store::StoreExt;
|
||||
|
||||
use crate::{LogState, constants::MAX_LOG_ENTRIES};
|
||||
use crate::{LogState, constants::{BACKEND_MODE_KEY, MAX_LOG_ENTRIES, SETTINGS_STORE}};
|
||||
|
||||
const CLI_INSTALL_DIR: &str = ".opencode/bin";
|
||||
const CLI_BINARY_NAME: &str = "opencode";
|
||||
@@ -21,7 +22,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
pub async fn get_config(app: &AppHandle) -> Option<Config> {
|
||||
create_command(app, "debug config")
|
||||
create_command(app, "debug config", &[])
|
||||
.output()
|
||||
.await
|
||||
.inspect_err(|e| eprintln!("Failed to read OC config: {e}"))
|
||||
@@ -150,22 +151,95 @@ fn get_user_shell() -> String {
|
||||
std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())
|
||||
}
|
||||
|
||||
pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command {
|
||||
fn is_wsl_backend(app: &tauri::AppHandle) -> bool {
|
||||
let Ok(store) = app.store(SETTINGS_STORE) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
store
|
||||
.get(BACKEND_MODE_KEY)
|
||||
.and_then(|value| value.as_str())
|
||||
.is_some_and(|value| value == "wsl")
|
||||
}
|
||||
|
||||
fn shell_escape(input: &str) -> String {
|
||||
if input.is_empty() {
|
||||
return "''".to_string();
|
||||
}
|
||||
|
||||
let mut escaped = String::from("'");
|
||||
escaped.push_str(&input.replace("'", "'\"'\"'"));
|
||||
escaped.push('\'');
|
||||
escaped
|
||||
}
|
||||
|
||||
pub fn create_command(app: &tauri::AppHandle, args: &str, extra_env: &[(&str, String)]) -> Command {
|
||||
let state_dir = app
|
||||
.path()
|
||||
.resolve("", BaseDirectory::AppLocalData)
|
||||
.expect("Failed to resolve app local data dir");
|
||||
|
||||
let mut envs = vec![
|
||||
("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY".to_string(), "true".to_string()),
|
||||
("OPENCODE_EXPERIMENTAL_FILEWATCHER".to_string(), "true".to_string()),
|
||||
("OPENCODE_CLIENT".to_string(), "desktop".to_string()),
|
||||
("XDG_STATE_HOME".to_string(), state_dir.to_string_lossy().to_string()),
|
||||
];
|
||||
envs.extend(extra_env.iter().map(|(key, value)| (key.to_string(), value.clone())));
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return app
|
||||
if is_wsl_backend(app) {
|
||||
let version = app.package_info().version.to_string();
|
||||
let mut script = vec![
|
||||
"set -e".to_string(),
|
||||
"BIN=\"$HOME/.opencode/bin/opencode\"".to_string(),
|
||||
"if [ ! -x \"$BIN\" ]; then".to_string(),
|
||||
format!(
|
||||
" curl -fsSL https://opencode.ai/install | bash -s -- --version {} --no-modify-path",
|
||||
shell_escape(&version)
|
||||
),
|
||||
"fi".to_string(),
|
||||
];
|
||||
|
||||
let mut env_prefix = vec![
|
||||
"OPENCODE_EXPERIMENTAL_ICON_DISCOVERY=true".to_string(),
|
||||
"OPENCODE_EXPERIMENTAL_FILEWATCHER=true".to_string(),
|
||||
"OPENCODE_CLIENT=desktop".to_string(),
|
||||
"XDG_STATE_HOME=\"$HOME/.local/state\"".to_string(),
|
||||
];
|
||||
env_prefix.extend(
|
||||
envs
|
||||
.iter()
|
||||
.filter(|(key, _)| key != "OPENCODE_EXPERIMENTAL_ICON_DISCOVERY")
|
||||
.filter(|(key, _)| key != "OPENCODE_EXPERIMENTAL_FILEWATCHER")
|
||||
.filter(|(key, _)| key != "OPENCODE_CLIENT")
|
||||
.filter(|(key, _)| key != "XDG_STATE_HOME")
|
||||
.map(|(key, value)| format!("{}={}", key, shell_escape(value))),
|
||||
);
|
||||
|
||||
script.push(format!(
|
||||
"{} exec \"$BIN\" {}",
|
||||
env_prefix.join(" "),
|
||||
args
|
||||
));
|
||||
|
||||
return app
|
||||
.shell()
|
||||
.command("wsl")
|
||||
.args(["-e", "bash", "-lc", &script.join("\n")]);
|
||||
}
|
||||
|
||||
let mut cmd = app
|
||||
.shell()
|
||||
.sidecar("opencode-cli")
|
||||
.unwrap()
|
||||
.args(args.split_whitespace())
|
||||
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
||||
.env("OPENCODE_EXPERIMENTAL_FILEWATCHER", "true")
|
||||
.env("OPENCODE_CLIENT", "desktop")
|
||||
.env("XDG_STATE_HOME", &state_dir);
|
||||
.args(args.split_whitespace());
|
||||
|
||||
for (key, value) in envs {
|
||||
cmd = cmd.env(key, value);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return {
|
||||
@@ -178,13 +252,15 @@ pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command {
|
||||
format!("\"{}\" {}", sidecar.display(), args)
|
||||
};
|
||||
|
||||
app.shell()
|
||||
let mut cmd = app.shell()
|
||||
.command(&shell)
|
||||
.env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true")
|
||||
.env("OPENCODE_EXPERIMENTAL_FILEWATCHER", "true")
|
||||
.env("OPENCODE_CLIENT", "desktop")
|
||||
.env("XDG_STATE_HOME", &state_dir)
|
||||
.args(["-il", "-c", &cmd])
|
||||
.args(["-il", "-c", &cmd]);
|
||||
|
||||
for (key, value) in envs {
|
||||
cmd = cmd.env(key, value);
|
||||
}
|
||||
|
||||
cmd
|
||||
};
|
||||
}
|
||||
|
||||
@@ -194,12 +270,16 @@ pub fn serve(app: &AppHandle, hostname: &str, port: u32, password: &str) -> Comm
|
||||
|
||||
println!("spawning sidecar on port {port}");
|
||||
|
||||
let envs = [
|
||||
("OPENCODE_SERVER_USERNAME", "opencode".to_string()),
|
||||
("OPENCODE_SERVER_PASSWORD", password.to_string()),
|
||||
];
|
||||
|
||||
let (mut rx, child) = create_command(
|
||||
app,
|
||||
format!("serve --hostname {hostname} --port {port}").as_str(),
|
||||
&envs,
|
||||
)
|
||||
.env("OPENCODE_SERVER_USERNAME", "opencode")
|
||||
.env("OPENCODE_SERVER_PASSWORD", password)
|
||||
.spawn()
|
||||
.expect("Failed to spawn opencode");
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use tauri_plugin_window_state::StateFlags;
|
||||
|
||||
pub const SETTINGS_STORE: &str = "opencode.settings.dat";
|
||||
pub const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
|
||||
pub const BACKEND_MODE_KEY: &str = "serverBackendMode";
|
||||
pub const UPDATER_ENABLED: bool = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some();
|
||||
pub const MAX_LOG_ENTRIES: usize = 200;
|
||||
|
||||
|
||||
@@ -209,6 +209,8 @@ pub fn run() {
|
||||
await_initialization,
|
||||
server::get_default_server_url,
|
||||
server::set_default_server_url,
|
||||
server::get_backend_config,
|
||||
server::set_backend_config,
|
||||
markdown::parse_markdown_command,
|
||||
check_app_exists
|
||||
])
|
||||
|
||||
@@ -8,9 +8,38 @@ use tokio::task::JoinHandle;
|
||||
|
||||
use crate::{
|
||||
cli,
|
||||
constants::{DEFAULT_SERVER_URL_KEY, SETTINGS_STORE},
|
||||
constants::{BACKEND_MODE_KEY, DEFAULT_SERVER_URL_KEY, SETTINGS_STORE},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, specta::Type, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BackendMode {
|
||||
Native,
|
||||
Wsl,
|
||||
}
|
||||
|
||||
impl BackendMode {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
BackendMode::Native => "native",
|
||||
BackendMode::Wsl => "wsl",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, specta::Type, Debug)]
|
||||
pub struct BackendConfig {
|
||||
pub mode: BackendMode,
|
||||
}
|
||||
|
||||
impl Default for BackendConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: BackendMode::Native,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn get_default_server_url(app: AppHandle) -> Result<Option<String>, String> {
|
||||
@@ -48,6 +77,44 @@ pub async fn set_default_server_url(app: AppHandle, url: Option<String>) -> Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn get_backend_config(app: AppHandle) -> Result<BackendConfig, String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||
|
||||
let mode = store
|
||||
.get(BACKEND_MODE_KEY)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|v| match v {
|
||||
"wsl" => BackendMode::Wsl,
|
||||
_ => BackendMode::Native,
|
||||
})
|
||||
.unwrap_or(BackendMode::Native);
|
||||
|
||||
Ok(BackendConfig { mode })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn set_backend_config(app: AppHandle, config: BackendConfig) -> Result<(), String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||
|
||||
store.set(
|
||||
BACKEND_MODE_KEY,
|
||||
serde_json::Value::String(config.mode.as_str().to_string()),
|
||||
);
|
||||
|
||||
store
|
||||
.save()
|
||||
.map_err(|e| format!("Failed to save settings: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_saved_server_url(app: &tauri::AppHandle) -> Option<String> {
|
||||
if let Some(url) = get_default_server_url(app.clone()).ok().flatten() {
|
||||
println!("Using desktop-specific custom URL: {url}");
|
||||
|
||||
@@ -23,7 +23,7 @@ import { initI18n, t } from "./i18n"
|
||||
import pkg from "../package.json"
|
||||
import "./styles.css"
|
||||
import { commands, InitStep } from "./bindings"
|
||||
import { Channel } from "@tauri-apps/api/core"
|
||||
import { Channel, invoke } from "@tauri-apps/api/core"
|
||||
import { createMenu } from "./menu"
|
||||
|
||||
const root = document.getElementById("root")
|
||||
@@ -51,7 +51,18 @@ const listenForDeepLinks = async () => {
|
||||
await onOpenUrl((urls) => emitDeepLinks(urls)).catch(() => undefined)
|
||||
}
|
||||
|
||||
const createPlatform = (password: Accessor<string | null>): Platform => ({
|
||||
type BackendConfig = {
|
||||
mode: "native" | "wsl"
|
||||
}
|
||||
|
||||
const defaultBackend: BackendConfig = { mode: "native" }
|
||||
|
||||
const createPlatform = (
|
||||
password: Accessor<string | null>,
|
||||
backend: Accessor<BackendConfig>,
|
||||
setBackend: (next: BackendConfig) => void,
|
||||
fetchBackend: () => Promise<BackendConfig | null>,
|
||||
): Platform => ({
|
||||
platform: "desktop",
|
||||
os: (() => {
|
||||
const type = ostype()
|
||||
@@ -61,6 +72,7 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
|
||||
version: pkg.version,
|
||||
|
||||
async openDirectoryPickerDialog(opts) {
|
||||
if (backend().mode === "wsl") return null
|
||||
const result = await open({
|
||||
directory: true,
|
||||
multiple: opts?.multiple ?? false,
|
||||
@@ -338,6 +350,19 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
|
||||
await commands.setDefaultServerUrl(url)
|
||||
},
|
||||
|
||||
getBackendConfig: async () => {
|
||||
const next = await fetchBackend()
|
||||
if (next) return next
|
||||
return backend()
|
||||
},
|
||||
|
||||
setBackendConfig: async (config: BackendConfig) => {
|
||||
setBackend(config)
|
||||
await invoke("set_backend_config", { config }).catch(() => undefined)
|
||||
},
|
||||
|
||||
supportsNativePickers: () => backend().mode !== "wsl",
|
||||
|
||||
parseMarkdown: (markdown: string) => commands.parseMarkdownCommand(markdown),
|
||||
|
||||
webviewZoom,
|
||||
@@ -378,7 +403,16 @@ void listenForDeepLinks()
|
||||
|
||||
render(() => {
|
||||
const [serverPassword, setServerPassword] = createSignal<string | null>(null)
|
||||
const platform = createPlatform(() => serverPassword())
|
||||
const [backend, setBackend] = createSignal<BackendConfig>(defaultBackend)
|
||||
|
||||
const fetchBackend = async () => {
|
||||
const next = await invoke<BackendConfig>("get_backend_config").catch(() => null)
|
||||
if (!next) return null
|
||||
setBackend(next)
|
||||
return next
|
||||
}
|
||||
|
||||
const platform = createPlatform(() => serverPassword(), backend, setBackend, fetchBackend)
|
||||
|
||||
function handleClick(e: MouseEvent) {
|
||||
const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null
|
||||
@@ -390,6 +424,7 @@ render(() => {
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener("click", handleClick)
|
||||
void fetchBackend()
|
||||
onCleanup(() => {
|
||||
document.removeEventListener("click", handleClick)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user