chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
// 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: () => __TAURI_INVOKE<string>("install_cli"),
awaitInitialization: (events: Channel) => __TAURI_INVOKE<ServerReadyData>("await_initialization", { events }),
getDefaultServerUrl: () => __TAURI_INVOKE<string | null>("get_default_server_url"),
setDefaultServerUrl: (url: string | null) => __TAURI_INVOKE<null>("set_default_server_url", { url }),
getWslConfig: () => __TAURI_INVOKE<WslConfig>("get_wsl_config"),
setWslConfig: (config: WslConfig) => __TAURI_INVOKE<null>("set_wsl_config", { config }),
getDisplayBackend: () => __TAURI_INVOKE<"wayland" | "auto" | null>("get_display_backend"),
setDisplayBackend: (backend: LinuxDisplayBackend) => __TAURI_INVOKE<null>("set_display_backend", { backend }),
parseMarkdownCommand: (markdown: string) => __TAURI_INVOKE<string>("parse_markdown_command", { markdown }),
checkAppExists: (appName: string) => __TAURI_INVOKE<boolean>("check_app_exists", { appName }),
wslPath: (path: string, mode: "windows" | "linux" | null) => __TAURI_INVOKE<string>("wsl_path", { path, mode }),
resolveAppPath: (appName: string) => __TAURI_INVOKE<string | null>("resolve_app_path", { appName }),
openPath: (path: string, appName: string | null) => __TAURI_INVOKE<null>("open_path", { path, appName }),
};
/** Events */
export const events = {
loadingWindowComplete: makeEvent<LoadingWindowComplete>("loading-window-complete"),
sqliteMigrationProgress: makeEvent<SqliteMigrationProgress>("sqlite-migration-progress"),
};
/* Types */
export type InitStep = { phase: "server_waiting" } | { phase: "sqlite_waiting" } | { phase: "done" };
export type LinuxDisplayBackend = "wayland" | "auto";
export type LoadingWindowComplete = null;
export type ServerReadyData = {
url: string,
username: string | null,
password: string | null,
};
export type SqliteMigrationProgress = { type: "InProgress"; value: number } | { type: "Done" };
export type WslConfig = {
enabled: boolean,
};
export type WslPathMode = "windows" | "linux";
/* Tauri Specta runtime */
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);
}

View File

@@ -0,0 +1,43 @@
import { message } from "@tauri-apps/plugin-dialog"
import { initI18n, t } from "./i18n"
import { commands } from "./bindings"
function installError(error: unknown) {
const text = String(error)
if (text.includes("CLI installation is only supported on macOS & Linux")) {
return t("desktop.cli.error.unsupportedPlatform")
}
if (text.includes("Sidecar binary not found")) {
return t("desktop.cli.error.sidecarMissing")
}
if (text.includes("Failed to write install script")) {
return t("desktop.cli.error.scriptWriteFailed")
}
if (text.includes("Failed to set script permissions")) {
return t("desktop.cli.error.scriptPermissionFailed")
}
if (text.includes("Failed to run install script")) {
return t("desktop.cli.error.scriptRunFailed")
}
if (text.includes("Install script failed")) {
return t("desktop.cli.error.scriptFailed")
}
if (text.includes("Could not determine install path")) {
return t("desktop.cli.error.installPathUnknown")
}
return text || t("desktop.cli.error.unknown")
}
export async function installCli(): Promise<void> {
await initI18n()
try {
const path = await commands.installCli()
await message(t("desktop.cli.installed.message", { path }), { title: t("desktop.cli.installed.title") })
} catch (e) {
await message(t("desktop.cli.failed.message", { error: installError(e) }), {
title: t("desktop.cli.failed.title"),
})
}
}

View File

@@ -0,0 +1,5 @@
if (location.pathname === "/loading") {
void import("./loading")
} else {
void import("./")
}

View File

@@ -0,0 +1,59 @@
export const dict = {
"desktop.menu.checkForUpdates": "التحقق من وجود تحديثات...",
"desktop.menu.installCli": "تثبيت CLI...",
"desktop.menu.reloadWebview": "إعادة تحميل Webview",
"desktop.menu.restart": "إعادة تشغيل",
"desktop.dialog.chooseFolder": "اختر مجلدًا",
"desktop.dialog.chooseFile": "اختر ملفًا",
"desktop.dialog.saveFile": "حفظ ملف",
"desktop.updater.checkFailed.title": "فشل التحقق من التحديثات",
"desktop.updater.checkFailed.message": "فشل التحقق من وجود تحديثات",
"desktop.updater.none.title": "لا توجد تحديثات متاحة",
"desktop.updater.none.message": "أنت تستخدم بالفعل أحدث إصدار من OpenCode",
"desktop.updater.downloadFailed.title": "فشل التحديث",
"desktop.updater.downloadFailed.message": "فشل تنزيل التحديث",
"desktop.updater.downloaded.title": "تم تنزيل التحديث",
"desktop.updater.downloaded.prompt": "تم تنزيل إصدار {{version}} من OpenCode، هل ترغب في تثبيته وإعادة تشغيله؟",
"desktop.updater.installFailed.title": "فشل التحديث",
"desktop.updater.installFailed.message": "فشل تثبيت التحديث",
"desktop.cli.installed.title": "تم تثبيت CLI",
"desktop.cli.installed.message": "تم تثبيت CLI في {{path}}\n\nأعد تشغيل الطرفية لاستخدام الأمر 'opencode'.",
"desktop.cli.failed.title": "فشل التثبيت",
"desktop.cli.failed.message": "فشل تثبيت CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "ملف",
"desktop.menu.edit": "تعديل",
"desktop.menu.view": "عرض",
"desktop.menu.help": "مساعدة",
"desktop.menu.file.newSession": "جلسة جديدة",
"desktop.menu.file.openProject": "فتح مشروع...",
"desktop.menu.view.toggleSidebar": "تبديل الشريط الجانبي",
"desktop.menu.view.toggleTerminal": "تبديل الطرفية",
"desktop.menu.view.toggleFileTree": "تبديل شجرة الملفات",
"desktop.menu.view.back": "رجوع",
"desktop.menu.view.forward": "تقدم",
"desktop.menu.view.previousSession": "الجلسة السابقة",
"desktop.menu.view.nextSession": "الجلسة التالية",
"desktop.menu.help.documentation": "وثائق OpenCode",
"desktop.menu.help.supportForum": "منتدى الدعم",
"desktop.menu.help.shareFeedback": "مشاركة التعليقات",
"desktop.menu.help.reportBug": "الإبلاغ عن خطأ",
"desktop.cli.error.unsupportedPlatform": "تثبيت CLI مدعوم فقط على macOS و Linux.",
"desktop.cli.error.sidecarMissing": "ملف OpenCode CLI الثنائي مفقود. حاول إعادة تثبيت تطبيق سطح المكتب.",
"desktop.cli.error.scriptWriteFailed": "فشل تحضير برنامج تثبيت CLI.",
"desktop.cli.error.scriptPermissionFailed": "فشل جعل برنامج تثبيت CLI قابلاً للتنفيذ.",
"desktop.cli.error.scriptRunFailed": "فشل تشغيل برنامج تثبيت CLI.",
"desktop.cli.error.scriptFailed": "فشل برنامج تثبيت CLI.",
"desktop.cli.error.installPathUnknown": "تعذر تحديد مكان تثبيت CLI.",
"desktop.cli.error.unknown": "خطأ تثبيت غير معروف",
"desktop.loading.status.initial": "لحظة من فضلك...",
"desktop.loading.status.done": "تم الانتهاء",
"desktop.loading.status.migrating": "جارٍ ترحيل قاعدة البيانات الخاصة بك",
"desktop.loading.status.waiting": "قد يستغرق هذا بضع دقائق",
"desktop.loading.progressAria": "تقدم ترحيل قاعدة البيانات",
"desktop.server.local": "خادم محلي",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Verificar atualizações...",
"desktop.menu.installCli": "Instalar CLI...",
"desktop.menu.reloadWebview": "Recarregar Webview",
"desktop.menu.restart": "Reiniciar",
"desktop.dialog.chooseFolder": "Escolher uma pasta",
"desktop.dialog.chooseFile": "Escolher um arquivo",
"desktop.dialog.saveFile": "Salvar arquivo",
"desktop.updater.checkFailed.title": "Falha ao verificar atualizações",
"desktop.updater.checkFailed.message": "Falha ao verificar atualizações",
"desktop.updater.none.title": "Nenhuma atualização disponível",
"desktop.updater.none.message": "Você já está usando a versão mais recente do OpenCode",
"desktop.updater.downloadFailed.title": "Falha na atualização",
"desktop.updater.downloadFailed.message": "Falha ao baixar a atualização",
"desktop.updater.downloaded.title": "Atualização baixada",
"desktop.updater.downloaded.prompt":
"A versão {{version}} do OpenCode foi baixada. Você gostaria de instalá-la e reiniciar?",
"desktop.updater.installFailed.title": "Falha na atualização",
"desktop.updater.installFailed.message": "Falha ao instalar a atualização",
"desktop.cli.installed.title": "CLI instalada",
"desktop.cli.installed.message": "CLI instalada em {{path}}\n\nReinicie seu terminal para usar o comando 'opencode'.",
"desktop.cli.failed.title": "Falha na instalação",
"desktop.cli.failed.message": "Falha ao instalar a CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Arquivo",
"desktop.menu.edit": "Editar",
"desktop.menu.view": "Visualizar",
"desktop.menu.help": "Ajuda",
"desktop.menu.file.newSession": "Nova Sessão",
"desktop.menu.file.openProject": "Abrir Projeto...",
"desktop.menu.view.toggleSidebar": "Alternar Barra Lateral",
"desktop.menu.view.toggleTerminal": "Alternar Terminal",
"desktop.menu.view.toggleFileTree": "Alternar Árvore de Arquivos",
"desktop.menu.view.back": "Voltar",
"desktop.menu.view.forward": "Avançar",
"desktop.menu.view.previousSession": "Sessão Anterior",
"desktop.menu.view.nextSession": "Próxima Sessão",
"desktop.menu.help.documentation": "Documentação do OpenCode",
"desktop.menu.help.supportForum": "Fórum de Suporte",
"desktop.menu.help.shareFeedback": "Compartilhar Feedback",
"desktop.menu.help.reportBug": "Relatar um Bug",
"desktop.cli.error.unsupportedPlatform": "A instalação da CLI é suportada apenas no macOS e Linux.",
"desktop.cli.error.sidecarMissing":
"O binário da CLI do OpenCode está ausente. Tente reinstalar o aplicativo de desktop.",
"desktop.cli.error.scriptWriteFailed": "Falha ao preparar o script de instalação da CLI.",
"desktop.cli.error.scriptPermissionFailed": "Falha ao tornar o script de instalação da CLI executável.",
"desktop.cli.error.scriptRunFailed": "Falha ao executar o script de instalação da CLI.",
"desktop.cli.error.scriptFailed": "O instalador da CLI falhou.",
"desktop.cli.error.installPathUnknown": "Não foi possível determinar onde a CLI foi instalada.",
"desktop.cli.error.unknown": "Erro de instalação desconhecido",
"desktop.loading.status.initial": "Só um momento...",
"desktop.loading.status.done": "Tudo pronto",
"desktop.loading.status.migrating": "Migrando seu banco de dados",
"desktop.loading.status.waiting": "Isso pode levar alguns minutos",
"desktop.loading.progressAria": "Progresso da migração do banco de dados",
"desktop.server.local": "Servidor Local",
}

View File

@@ -0,0 +1,62 @@
export const dict = {
"desktop.menu.checkForUpdates": "Provjeri ažuriranja...",
"desktop.menu.installCli": "Instaliraj CLI...",
"desktop.menu.reloadWebview": "Ponovo učitavanje webview-a",
"desktop.menu.restart": "Restartuj",
"desktop.dialog.chooseFolder": "Odaberi folder",
"desktop.dialog.chooseFile": "Odaberi datoteku",
"desktop.dialog.saveFile": "Sačuvaj datoteku",
"desktop.updater.checkFailed.title": "Provjera ažuriranja nije uspjela",
"desktop.updater.checkFailed.message": "Nije moguće provjeriti ažuriranja",
"desktop.updater.none.title": "Nema dostupnog ažuriranja",
"desktop.updater.none.message": "Već koristiš najnoviju verziju OpenCode-a",
"desktop.updater.downloadFailed.title": "Ažuriranje nije uspjelo",
"desktop.updater.downloadFailed.message": "Neuspjelo preuzimanje ažuriranja",
"desktop.updater.downloaded.title": "Ažuriranje preuzeto",
"desktop.updater.downloaded.prompt":
"Verzija {{version}} OpenCode-a je preuzeta. Želiš li da je instaliraš i ponovo pokreneš aplikaciju?",
"desktop.updater.installFailed.title": "Ažuriranje nije uspjelo",
"desktop.updater.installFailed.message": "Neuspjela instalacija ažuriranja",
"desktop.cli.installed.title": "CLI instaliran",
"desktop.cli.installed.message":
"CLI je instaliran u {{path}}\n\nRestartuj terminal da bi koristio komandu 'opencode'.",
"desktop.cli.failed.title": "Instalacija nije uspjela",
"desktop.cli.failed.message": "Neuspjela instalacija CLI-a: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Datoteka",
"desktop.menu.edit": "Uredi",
"desktop.menu.view": "Prikaz",
"desktop.menu.help": "Pomoć",
"desktop.menu.file.newSession": "Nova sesija",
"desktop.menu.file.openProject": "Otvori projekat...",
"desktop.menu.view.toggleSidebar": "Prebaci bočnu traku",
"desktop.menu.view.toggleTerminal": "Prebaci terminal",
"desktop.menu.view.toggleFileTree": "Prebaci stablo datoteka",
"desktop.menu.view.back": "Nazad",
"desktop.menu.view.forward": "Naprijed",
"desktop.menu.view.previousSession": "Prethodna sesija",
"desktop.menu.view.nextSession": "Sljedeća sesija",
"desktop.menu.help.documentation": "OpenCode Dokumentacija",
"desktop.menu.help.supportForum": "Forum za podršku",
"desktop.menu.help.shareFeedback": "Podijeli povratne informacije",
"desktop.menu.help.reportBug": "Prijavi grešku",
"desktop.cli.error.unsupportedPlatform": "Instalacija CLI-a je podržana samo na macOS-u i Linux-u.",
"desktop.cli.error.sidecarMissing":
"Nedostaje binarna datoteka OpenCode CLI-a. Pokušaj ponovo instalirati desktop aplikaciju.",
"desktop.cli.error.scriptWriteFailed": "Nije uspjela priprema skripte za instalaciju CLI-a.",
"desktop.cli.error.scriptPermissionFailed": "Nije uspjelo postavljanje izvršnih dozvola za instalaciju CLI-a.",
"desktop.cli.error.scriptRunFailed": "Nije uspjelo pokretanje skripte za instalaciju CLI-a.",
"desktop.cli.error.scriptFailed": "Instalacija CLI-a nije uspjela.",
"desktop.cli.error.installPathUnknown": "Nije bilo moguće utvrditi gdje je instaliran CLI.",
"desktop.cli.error.unknown": "Nepoznata greška pri instalaciji",
"desktop.loading.status.initial": "Samo trenutak...",
"desktop.loading.status.done": "Sve je gotovo",
"desktop.loading.status.migrating": "Migracija baze podataka u toku",
"desktop.loading.status.waiting": "Ovo može potrajati nekoliko minuta",
"desktop.loading.progressAria": "Napredak migracije baze podataka",
"desktop.server.local": "Lokalni server",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Tjek for opdateringer...",
"desktop.menu.installCli": "Installer CLI...",
"desktop.menu.reloadWebview": "Genindlæs Webview",
"desktop.menu.restart": "Genstart",
"desktop.dialog.chooseFolder": "Vælg en mappe",
"desktop.dialog.chooseFile": "Vælg en fil",
"desktop.dialog.saveFile": "Gem fil",
"desktop.updater.checkFailed.title": "Opdateringstjek mislykkedes",
"desktop.updater.checkFailed.message": "Kunne ikke tjekke for opdateringer",
"desktop.updater.none.title": "Ingen opdatering tilgængelig",
"desktop.updater.none.message": "Du bruger allerede den nyeste version af OpenCode",
"desktop.updater.downloadFailed.title": "Opdatering mislykkedes",
"desktop.updater.downloadFailed.message": "Kunne ikke downloade opdateringen",
"desktop.updater.downloaded.title": "Opdatering downloadet",
"desktop.updater.downloaded.prompt":
"Version {{version}} af OpenCode er blevet downloadet. Vil du installere den og genstarte?",
"desktop.updater.installFailed.title": "Opdatering mislykkedes",
"desktop.updater.installFailed.message": "Kunne ikke installere opdateringen",
"desktop.cli.installed.title": "CLI installeret",
"desktop.cli.installed.message":
"CLI installeret i {{path}}\n\nGenstart din terminal for at bruge 'opencode'-kommandoen.",
"desktop.cli.failed.title": "Installation mislykkedes",
"desktop.cli.failed.message": "Kunne ikke installere CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Filer",
"desktop.menu.edit": "Rediger",
"desktop.menu.view": "Vis",
"desktop.menu.help": "Hjælp",
"desktop.menu.file.newSession": "Ny session",
"desktop.menu.file.openProject": "Åbn projekt...",
"desktop.menu.view.toggleSidebar": "Slå sidepanel til/fra",
"desktop.menu.view.toggleTerminal": "Slå terminal til/fra",
"desktop.menu.view.toggleFileTree": "Slå filoversigt til/fra",
"desktop.menu.view.back": "Tilbage",
"desktop.menu.view.forward": "Fremad",
"desktop.menu.view.previousSession": "Forrige session",
"desktop.menu.view.nextSession": "Næste session",
"desktop.menu.help.documentation": "OpenCode Dokumentation",
"desktop.menu.help.supportForum": "Supportforum",
"desktop.menu.help.shareFeedback": "Del feedback",
"desktop.menu.help.reportBug": "Rapporter en fejl",
"desktop.cli.error.unsupportedPlatform": "CLI-installation understøttes kun på macOS og Linux.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI-binærfil mangler. Prøv at geninstallere desktop-appen.",
"desktop.cli.error.scriptWriteFailed": "Kunne ikke forberede CLI-installationsscriptet.",
"desktop.cli.error.scriptPermissionFailed": "Kunne ikke gøre CLI-installationsscriptet eksekverbart.",
"desktop.cli.error.scriptRunFailed": "Kunne ikke køre CLI-installationsscriptet.",
"desktop.cli.error.scriptFailed": "CLI-installationsprogrammet mislykkedes.",
"desktop.cli.error.installPathUnknown": "Kunne ikke fastslå, hvor CLI'en blev installeret.",
"desktop.cli.error.unknown": "Ukendt installationsfejl",
"desktop.loading.status.initial": "Lige et øjeblik...",
"desktop.loading.status.done": "Helt færdig",
"desktop.loading.status.migrating": "Migrerer din database",
"desktop.loading.status.waiting": "Dette kan tage et par minutter",
"desktop.loading.progressAria": "Status for databasemigrering",
"desktop.server.local": "Lokal server",
}

View File

@@ -0,0 +1,62 @@
export const dict = {
"desktop.menu.checkForUpdates": "Nach Updates suchen...",
"desktop.menu.installCli": "CLI installieren...",
"desktop.menu.reloadWebview": "Webview neu laden",
"desktop.menu.restart": "Neustart",
"desktop.dialog.chooseFolder": "Ordner auswählen",
"desktop.dialog.chooseFile": "Datei auswählen",
"desktop.dialog.saveFile": "Datei speichern",
"desktop.updater.checkFailed.title": "Updateprüfung fehlgeschlagen",
"desktop.updater.checkFailed.message": "Updates konnten nicht geprüft werden",
"desktop.updater.none.title": "Kein Update verfügbar",
"desktop.updater.none.message": "Sie verwenden bereits die neueste Version von OpenCode",
"desktop.updater.downloadFailed.title": "Update fehlgeschlagen",
"desktop.updater.downloadFailed.message": "Update konnte nicht heruntergeladen werden",
"desktop.updater.downloaded.title": "Update heruntergeladen",
"desktop.updater.downloaded.prompt":
"Version {{version}} von OpenCode wurde heruntergeladen. Möchten Sie sie installieren und neu starten?",
"desktop.updater.installFailed.title": "Update fehlgeschlagen",
"desktop.updater.installFailed.message": "Update konnte nicht installiert werden",
"desktop.cli.installed.title": "CLI installiert",
"desktop.cli.installed.message":
"CLI wurde in {{path}} installiert\n\nStarten Sie Ihr Terminal neu, um den Befehl 'opencode' zu verwenden.",
"desktop.cli.failed.title": "Installation fehlgeschlagen",
"desktop.cli.failed.message": "CLI konnte nicht installiert werden: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Datei",
"desktop.menu.edit": "Bearbeiten",
"desktop.menu.view": "Ansicht",
"desktop.menu.help": "Hilfe",
"desktop.menu.file.newSession": "Neue Sitzung",
"desktop.menu.file.openProject": "Projekt öffnen...",
"desktop.menu.view.toggleSidebar": "Seitenleiste umschalten",
"desktop.menu.view.toggleTerminal": "Terminal umschalten",
"desktop.menu.view.toggleFileTree": "Dateibaum umschalten",
"desktop.menu.view.back": "Zurück",
"desktop.menu.view.forward": "Vorwärts",
"desktop.menu.view.previousSession": "Vorherige Sitzung",
"desktop.menu.view.nextSession": "Nächste Sitzung",
"desktop.menu.help.documentation": "OpenCode-Dokumentation",
"desktop.menu.help.supportForum": "Support-Forum",
"desktop.menu.help.shareFeedback": "Feedback teilen",
"desktop.menu.help.reportBug": "Einen Fehler melden",
"desktop.cli.error.unsupportedPlatform": "Die CLI-Installation wird nur unter macOS und Linux unterstützt.",
"desktop.cli.error.sidecarMissing":
"Das OpenCode CLI-Binary fehlt. Versuchen Sie, die Desktop-App neu zu installieren.",
"desktop.cli.error.scriptWriteFailed": "Das CLI-Installationsskript konnte nicht vorbereitet werden.",
"desktop.cli.error.scriptPermissionFailed": "Das CLI-Installationsskript konnte nicht ausführbar gemacht werden.",
"desktop.cli.error.scriptRunFailed": "Das CLI-Installationsskript konnte nicht ausgeführt werden.",
"desktop.cli.error.scriptFailed": "Das CLI-Installationsprogramm ist fehlgeschlagen.",
"desktop.cli.error.installPathUnknown": "Es konnte nicht ermittelt werden, wo die CLI installiert wurde.",
"desktop.cli.error.unknown": "Unbekannter Installationsfehler",
"desktop.loading.status.initial": "Einen Moment bitte...",
"desktop.loading.status.done": "Alles erledigt",
"desktop.loading.status.migrating": "Ihre Datenbank wird migriert",
"desktop.loading.status.waiting": "Dies kann einige Minuten dauern",
"desktop.loading.progressAria": "Fortschritt der Datenbankmigration",
"desktop.server.local": "Lokaler Server",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Check for Updates...",
"desktop.menu.installCli": "Install CLI...",
"desktop.menu.reloadWebview": "Reload Webview",
"desktop.menu.restart": "Restart",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "File",
"desktop.menu.edit": "Edit",
"desktop.menu.view": "View",
"desktop.menu.help": "Help",
"desktop.menu.file.newSession": "New Session",
"desktop.menu.file.openProject": "Open Project...",
"desktop.menu.view.toggleSidebar": "Toggle Sidebar",
"desktop.menu.view.toggleTerminal": "Toggle Terminal",
"desktop.menu.view.toggleFileTree": "Toggle File Tree",
"desktop.menu.view.back": "Back",
"desktop.menu.view.forward": "Forward",
"desktop.menu.view.previousSession": "Previous Session",
"desktop.menu.view.nextSession": "Next Session",
"desktop.menu.help.documentation": "OpenCode Documentation",
"desktop.menu.help.supportForum": "Support Forum",
"desktop.menu.help.shareFeedback": "Share Feedback",
"desktop.menu.help.reportBug": "Report a Bug",
"desktop.dialog.chooseFolder": "Choose a folder",
"desktop.dialog.chooseFile": "Choose a file",
"desktop.dialog.saveFile": "Save file",
"desktop.updater.checkFailed.title": "Update Check Failed",
"desktop.updater.checkFailed.message": "Failed to check for updates",
"desktop.updater.none.title": "No Update Available",
"desktop.updater.none.message": "You are already using the latest version of OpenCode",
"desktop.updater.downloadFailed.title": "Update Failed",
"desktop.updater.downloadFailed.message": "Failed to download update",
"desktop.updater.downloaded.title": "Update Downloaded",
"desktop.updater.downloaded.prompt":
"Version {{version}} of OpenCode has been downloaded, would you like to install it and relaunch?",
"desktop.updater.installFailed.title": "Update Failed",
"desktop.updater.installFailed.message": "Failed to install update",
"desktop.cli.installed.title": "CLI Installed",
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode' command.",
"desktop.cli.failed.title": "Installation Failed",
"desktop.cli.failed.message": "Failed to install CLI: {{error}}",
"desktop.cli.error.unsupportedPlatform": "CLI installation is only supported on macOS and Linux.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI binary is missing. Try reinstalling the desktop app.",
"desktop.cli.error.scriptWriteFailed": "Failed to prepare CLI installer script.",
"desktop.cli.error.scriptPermissionFailed": "Failed to make CLI installer executable.",
"desktop.cli.error.scriptRunFailed": "Failed to run CLI installer script.",
"desktop.cli.error.scriptFailed": "CLI installer failed.",
"desktop.cli.error.installPathUnknown": "Could not determine where the CLI was installed.",
"desktop.cli.error.unknown": "Unknown installation error",
"desktop.loading.status.initial": "Just a moment...",
"desktop.loading.status.done": "All done",
"desktop.loading.status.migrating": "Migrating your database",
"desktop.loading.status.waiting": "This may take a couple of minutes",
"desktop.loading.progressAria": "Database migration progress",
"desktop.server.local": "Local Server",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Buscar actualizaciones...",
"desktop.menu.installCli": "Instalar CLI...",
"desktop.menu.reloadWebview": "Recargar Webview",
"desktop.menu.restart": "Reiniciar",
"desktop.dialog.chooseFolder": "Elegir una carpeta",
"desktop.dialog.chooseFile": "Elegir un archivo",
"desktop.dialog.saveFile": "Guardar archivo",
"desktop.updater.checkFailed.title": "Comprobación de actualizaciones fallida",
"desktop.updater.checkFailed.message": "No se pudieron buscar actualizaciones",
"desktop.updater.none.title": "No hay actualizaciones disponibles",
"desktop.updater.none.message": "Ya estás usando la versión más reciente de OpenCode",
"desktop.updater.downloadFailed.title": "Actualización fallida",
"desktop.updater.downloadFailed.message": "No se pudo descargar la actualización",
"desktop.updater.downloaded.title": "Actualización descargada",
"desktop.updater.downloaded.prompt":
"Se ha descargado la versión {{version}} de OpenCode. ¿Quieres instalarla y reiniciar?",
"desktop.updater.installFailed.title": "Actualización fallida",
"desktop.updater.installFailed.message": "No se pudo instalar la actualización",
"desktop.cli.installed.title": "CLI instalada",
"desktop.cli.installed.message": "CLI instalada en {{path}}\n\nReinicia tu terminal para usar el comando 'opencode'.",
"desktop.cli.failed.title": "Instalación fallida",
"desktop.cli.failed.message": "No se pudo instalar la CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Archivo",
"desktop.menu.edit": "Editar",
"desktop.menu.view": "Ver",
"desktop.menu.help": "Ayuda",
"desktop.menu.file.newSession": "Nueva sesión",
"desktop.menu.file.openProject": "Abrir proyecto...",
"desktop.menu.view.toggleSidebar": "Alternar barra lateral",
"desktop.menu.view.toggleTerminal": "Alternar terminal",
"desktop.menu.view.toggleFileTree": "Alternar árbol de archivos",
"desktop.menu.view.back": "Atrás",
"desktop.menu.view.forward": "Adelante",
"desktop.menu.view.previousSession": "Sesión anterior",
"desktop.menu.view.nextSession": "Siguiente sesión",
"desktop.menu.help.documentation": "Documentación de OpenCode",
"desktop.menu.help.supportForum": "Foro de soporte",
"desktop.menu.help.shareFeedback": "Compartir comentarios",
"desktop.menu.help.reportBug": "Informar de un error",
"desktop.cli.error.unsupportedPlatform": "La instalación de la CLI solo es compatible con macOS y Linux.",
"desktop.cli.error.sidecarMissing":
"Falta el binario de la CLI de OpenCode. Intenta reinstalar la aplicación de escritorio.",
"desktop.cli.error.scriptWriteFailed": "No se pudo preparar el script del instalador de la CLI.",
"desktop.cli.error.scriptPermissionFailed": "No se pudo hacer ejecutable el script del instalador de la CLI.",
"desktop.cli.error.scriptRunFailed": "No se pudo ejecutar el script del instalador de la CLI.",
"desktop.cli.error.scriptFailed": "El instalador de la CLI falló.",
"desktop.cli.error.installPathUnknown": "No se pudo determinar dónde se instaló la CLI.",
"desktop.cli.error.unknown": "Error de instalación desconocido",
"desktop.loading.status.initial": "Un momento...",
"desktop.loading.status.done": "Todo listo",
"desktop.loading.status.migrating": "Migrando tu base de datos",
"desktop.loading.status.waiting": "Esto puede tardar unos minutos",
"desktop.loading.progressAria": "Progreso de migración de la base de datos",
"desktop.server.local": "Servidor local",
}

View File

@@ -0,0 +1,62 @@
export const dict = {
"desktop.menu.checkForUpdates": "Vérifier les mises à jour...",
"desktop.menu.installCli": "Installer la CLI...",
"desktop.menu.reloadWebview": "Recharger la Webview",
"desktop.menu.restart": "Redémarrer",
"desktop.dialog.chooseFolder": "Choisir un dossier",
"desktop.dialog.chooseFile": "Choisir un fichier",
"desktop.dialog.saveFile": "Enregistrer le fichier",
"desktop.updater.checkFailed.title": "Échec de la vérification des mises à jour",
"desktop.updater.checkFailed.message": "Impossible de vérifier les mises à jour",
"desktop.updater.none.title": "Aucune mise à jour disponible",
"desktop.updater.none.message": "Vous utilisez déjà la dernière version d'OpenCode",
"desktop.updater.downloadFailed.title": "Échec de la mise à jour",
"desktop.updater.downloadFailed.message": "Impossible de télécharger la mise à jour",
"desktop.updater.downloaded.title": "Mise à jour téléchargée",
"desktop.updater.downloaded.prompt":
"La version {{version}} d'OpenCode a été téléchargée. Voulez-vous l'installer et redémarrer ?",
"desktop.updater.installFailed.title": "Échec de la mise à jour",
"desktop.updater.installFailed.message": "Impossible d'installer la mise à jour",
"desktop.cli.installed.title": "CLI installée",
"desktop.cli.installed.message":
"CLI installée dans {{path}}\n\nRedémarrez votre terminal pour utiliser la commande 'opencode'.",
"desktop.cli.failed.title": "Échec de l'installation",
"desktop.cli.failed.message": "Impossible d'installer la CLI : {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Fichier",
"desktop.menu.edit": "Édition",
"desktop.menu.view": "Affichage",
"desktop.menu.help": "Aide",
"desktop.menu.file.newSession": "Nouvelle session",
"desktop.menu.file.openProject": "Ouvrir un projet...",
"desktop.menu.view.toggleSidebar": "Basculer la barre latérale",
"desktop.menu.view.toggleTerminal": "Basculer le terminal",
"desktop.menu.view.toggleFileTree": "Basculer l'arborescence des fichiers",
"desktop.menu.view.back": "Retour",
"desktop.menu.view.forward": "Suivant",
"desktop.menu.view.previousSession": "Session précédente",
"desktop.menu.view.nextSession": "Session suivante",
"desktop.menu.help.documentation": "Documentation d'OpenCode",
"desktop.menu.help.supportForum": "Forum d'assistance",
"desktop.menu.help.shareFeedback": "Partager des commentaires",
"desktop.menu.help.reportBug": "Signaler un bug",
"desktop.cli.error.unsupportedPlatform": "L'installation de la CLI n'est prise en charge que sur macOS et Linux.",
"desktop.cli.error.sidecarMissing":
"Le binaire de la CLI OpenCode est manquant. Essayez de réinstaller l'application de bureau.",
"desktop.cli.error.scriptWriteFailed": "Impossible de préparer le script d'installation de la CLI.",
"desktop.cli.error.scriptPermissionFailed": "Impossible de rendre le script d'installation de la CLI exécutable.",
"desktop.cli.error.scriptRunFailed": "Impossible d'exécuter le script d'installation de la CLI.",
"desktop.cli.error.scriptFailed": "L'installateur de la CLI a échoué.",
"desktop.cli.error.installPathUnknown": "Impossible de déterminer où la CLI a été installée.",
"desktop.cli.error.unknown": "Erreur d'installation inconnue",
"desktop.loading.status.initial": "Un instant...",
"desktop.loading.status.done": "Terminé",
"desktop.loading.status.migrating": "Migration de votre base de données",
"desktop.loading.status.waiting": "Cela peut prendre quelques minutes",
"desktop.loading.progressAria": "Progression de la migration de la base de données",
"desktop.server.local": "Serveur local",
}

View File

@@ -0,0 +1,192 @@
import * as i18n from "@solid-primitives/i18n"
import { Store } from "@tauri-apps/plugin-store"
import { dict as desktopEn } from "./en"
import { dict as desktopZh } from "./zh"
import { dict as desktopZht } from "./zht"
import { dict as desktopKo } from "./ko"
import { dict as desktopDe } from "./de"
import { dict as desktopEs } from "./es"
import { dict as desktopFr } from "./fr"
import { dict as desktopDa } from "./da"
import { dict as desktopJa } from "./ja"
import { dict as desktopPl } from "./pl"
import { dict as desktopRu } from "./ru"
import { dict as desktopAr } from "./ar"
import { dict as desktopNo } from "./no"
import { dict as desktopBr } from "./br"
import { dict as desktopBs } from "./bs"
import { dict as appEn } from "../../../app/src/i18n/en"
import { dict as appZh } from "../../../app/src/i18n/zh"
import { dict as appZht } from "../../../app/src/i18n/zht"
import { dict as appKo } from "../../../app/src/i18n/ko"
import { dict as appDe } from "../../../app/src/i18n/de"
import { dict as appEs } from "../../../app/src/i18n/es"
import { dict as appFr } from "../../../app/src/i18n/fr"
import { dict as appDa } from "../../../app/src/i18n/da"
import { dict as appJa } from "../../../app/src/i18n/ja"
import { dict as appPl } from "../../../app/src/i18n/pl"
import { dict as appRu } from "../../../app/src/i18n/ru"
import { dict as appAr } from "../../../app/src/i18n/ar"
import { dict as appNo } from "../../../app/src/i18n/no"
import { dict as appBr } from "../../../app/src/i18n/br"
import { dict as appBs } from "../../../app/src/i18n/bs"
export type Locale =
| "en"
| "zh"
| "zht"
| "ko"
| "de"
| "es"
| "fr"
| "da"
| "ja"
| "pl"
| "ru"
| "ar"
| "no"
| "br"
| "bs"
type RawDictionary = typeof appEn & typeof desktopEn
type Dictionary = i18n.Flatten<RawDictionary>
const LOCALES: readonly Locale[] = [
"en",
"zh",
"zht",
"ko",
"de",
"es",
"fr",
"da",
"ja",
"pl",
"ru",
"bs",
"ar",
"no",
"br",
]
function detectLocale(): Locale {
if (typeof navigator !== "object") return "en"
const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
for (const language of languages) {
if (!language) continue
if (language.toLowerCase().startsWith("en")) return "en"
if (language.toLowerCase().startsWith("zh")) {
if (language.toLowerCase().includes("hant")) return "zht"
return "zh"
}
if (language.toLowerCase().startsWith("ko")) return "ko"
if (language.toLowerCase().startsWith("de")) return "de"
if (language.toLowerCase().startsWith("es")) return "es"
if (language.toLowerCase().startsWith("fr")) return "fr"
if (language.toLowerCase().startsWith("da")) return "da"
if (language.toLowerCase().startsWith("ja")) return "ja"
if (language.toLowerCase().startsWith("pl")) return "pl"
if (language.toLowerCase().startsWith("ru")) return "ru"
if (language.toLowerCase().startsWith("ar")) return "ar"
if (
language.toLowerCase().startsWith("no") ||
language.toLowerCase().startsWith("nb") ||
language.toLowerCase().startsWith("nn")
)
return "no"
if (language.toLowerCase().startsWith("pt")) return "br"
if (language.toLowerCase().startsWith("bs")) return "bs"
}
return "en"
}
function parseLocale(value: unknown): Locale | null {
if (!value) return null
if (typeof value !== "string") return null
if ((LOCALES as readonly string[]).includes(value)) return value as Locale
return null
}
function parseRecord(value: unknown) {
if (!value || typeof value !== "object") return null
if (Array.isArray(value)) return null
return value as Record<string, unknown>
}
function parseStored(value: unknown) {
if (typeof value !== "string") return value
try {
return JSON.parse(value) as unknown
} catch {
return value
}
}
function pickLocale(value: unknown): Locale | null {
const direct = parseLocale(value)
if (direct) return direct
const record = parseRecord(value)
if (!record) return null
return parseLocale(record.locale)
}
const base = i18n.flatten({ ...appEn, ...desktopEn })
function build(locale: Locale): Dictionary {
if (locale === "en") return base
if (locale === "zh") return { ...base, ...i18n.flatten(appZh), ...i18n.flatten(desktopZh) }
if (locale === "zht") return { ...base, ...i18n.flatten(appZht), ...i18n.flatten(desktopZht) }
if (locale === "de") return { ...base, ...i18n.flatten(appDe), ...i18n.flatten(desktopDe) }
if (locale === "es") return { ...base, ...i18n.flatten(appEs), ...i18n.flatten(desktopEs) }
if (locale === "fr") return { ...base, ...i18n.flatten(appFr), ...i18n.flatten(desktopFr) }
if (locale === "da") return { ...base, ...i18n.flatten(appDa), ...i18n.flatten(desktopDa) }
if (locale === "ja") return { ...base, ...i18n.flatten(appJa), ...i18n.flatten(desktopJa) }
if (locale === "pl") return { ...base, ...i18n.flatten(appPl), ...i18n.flatten(desktopPl) }
if (locale === "ru") return { ...base, ...i18n.flatten(appRu), ...i18n.flatten(desktopRu) }
if (locale === "ar") return { ...base, ...i18n.flatten(appAr), ...i18n.flatten(desktopAr) }
if (locale === "no") return { ...base, ...i18n.flatten(appNo), ...i18n.flatten(desktopNo) }
if (locale === "br") return { ...base, ...i18n.flatten(appBr), ...i18n.flatten(desktopBr) }
if (locale === "bs") return { ...base, ...i18n.flatten(appBs), ...i18n.flatten(desktopBs) }
return { ...base, ...i18n.flatten(appKo), ...i18n.flatten(desktopKo) }
}
const state = {
locale: detectLocale(),
dict: base as Dictionary,
init: undefined as Promise<Locale> | undefined,
}
state.dict = build(state.locale)
const translate = i18n.translator(() => state.dict, i18n.resolveTemplate)
export function t(key: keyof Dictionary, params?: Record<string, string | number>) {
return translate(key, params)
}
export function initI18n(): Promise<Locale> {
const cached = state.init
if (cached) return cached
const promise = (async () => {
const store = await Store.load("opencode.global.dat").catch(() => null)
if (!store) return state.locale
const raw = await store.get("language").catch(() => null)
const value = parseStored(raw)
const next = pickLocale(value) ?? state.locale
state.locale = next
state.dict = build(next)
return next
})().catch(() => state.locale)
state.init = promise
return promise
}

View File

@@ -0,0 +1,62 @@
export const dict = {
"desktop.menu.checkForUpdates": "アップデートを確認...",
"desktop.menu.installCli": "CLI をインストール...",
"desktop.menu.reloadWebview": "Webview を再読み込み",
"desktop.menu.restart": "再起動",
"desktop.dialog.chooseFolder": "フォルダーを選択",
"desktop.dialog.chooseFile": "ファイルを選択",
"desktop.dialog.saveFile": "ファイルを保存",
"desktop.updater.checkFailed.title": "アップデートの確認に失敗しました",
"desktop.updater.checkFailed.message": "アップデートを確認できませんでした",
"desktop.updater.none.title": "利用可能なアップデートはありません",
"desktop.updater.none.message": "すでに最新バージョンの OpenCode を使用しています",
"desktop.updater.downloadFailed.title": "アップデートに失敗しました",
"desktop.updater.downloadFailed.message": "アップデートをダウンロードできませんでした",
"desktop.updater.downloaded.title": "アップデートをダウンロードしました",
"desktop.updater.downloaded.prompt":
"OpenCode のバージョン {{version}} がダウンロードされました。インストールして再起動しますか?",
"desktop.updater.installFailed.title": "アップデートに失敗しました",
"desktop.updater.installFailed.message": "アップデートをインストールできませんでした",
"desktop.cli.installed.title": "CLI をインストールしました",
"desktop.cli.installed.message":
"CLI を {{path}} にインストールしました\n\nターミナルを再起動して 'opencode' コマンドを使用してください。",
"desktop.cli.failed.title": "インストールに失敗しました",
"desktop.cli.failed.message": "CLI のインストールに失敗しました: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "ファイル",
"desktop.menu.edit": "編集",
"desktop.menu.view": "表示",
"desktop.menu.help": "ヘルプ",
"desktop.menu.file.newSession": "新しいセッション",
"desktop.menu.file.openProject": "プロジェクトを開く...",
"desktop.menu.view.toggleSidebar": "サイドバーの切り替え",
"desktop.menu.view.toggleTerminal": "ターミナルの切り替え",
"desktop.menu.view.toggleFileTree": "ファイルツリーの切り替え",
"desktop.menu.view.back": "戻る",
"desktop.menu.view.forward": "進む",
"desktop.menu.view.previousSession": "前のセッション",
"desktop.menu.view.nextSession": "次のセッション",
"desktop.menu.help.documentation": "OpenCode ドキュメント",
"desktop.menu.help.supportForum": "サポートフォーラム",
"desktop.menu.help.shareFeedback": "フィードバックを共有",
"desktop.menu.help.reportBug": "バグを報告",
"desktop.cli.error.unsupportedPlatform": "CLI のインストールは macOS と Linux のみでサポートされています。",
"desktop.cli.error.sidecarMissing":
"OpenCode CLI のバイナリが見つかりません。デスクトップアプリを再インストールしてみてください。",
"desktop.cli.error.scriptWriteFailed": "CLI インストーラースクリプトの準備に失敗しました。",
"desktop.cli.error.scriptPermissionFailed": "CLI インストーラースクリプトに実行権限を付与できませんでした。",
"desktop.cli.error.scriptRunFailed": "CLI インストーラースクリプトの実行に失敗しました。",
"desktop.cli.error.scriptFailed": "CLI インストーラーが失敗しました。",
"desktop.cli.error.installPathUnknown": "CLI がどこにインストールされたか特定できませんでした。",
"desktop.cli.error.unknown": "不明なインストールエラー",
"desktop.loading.status.initial": "少々お待ちください...",
"desktop.loading.status.done": "完了しました",
"desktop.loading.status.migrating": "データベースを移行しています",
"desktop.loading.status.waiting": "これには数分かかる場合があります",
"desktop.loading.progressAria": "データベース移行の進行状況",
"desktop.server.local": "ローカルサーバー",
}

View File

@@ -0,0 +1,60 @@
export const dict = {
"desktop.menu.checkForUpdates": "업데이트 확인...",
"desktop.menu.installCli": "CLI 설치...",
"desktop.menu.reloadWebview": "Webview 새로고침",
"desktop.menu.restart": "다시 시작",
"desktop.dialog.chooseFolder": "폴더 선택",
"desktop.dialog.chooseFile": "파일 선택",
"desktop.dialog.saveFile": "파일 저장",
"desktop.updater.checkFailed.title": "업데이트 확인 실패",
"desktop.updater.checkFailed.message": "업데이트를 확인하지 못했습니다",
"desktop.updater.none.title": "사용 가능한 업데이트 없음",
"desktop.updater.none.message": "이미 최신 버전의 OpenCode를 사용하고 있습니다",
"desktop.updater.downloadFailed.title": "업데이트 실패",
"desktop.updater.downloadFailed.message": "업데이트를 다운로드하지 못했습니다",
"desktop.updater.downloaded.title": "업데이트 다운로드 완료",
"desktop.updater.downloaded.prompt": "OpenCode {{version}} 버전을 다운로드했습니다. 설치하고 다시 실행할까요?",
"desktop.updater.installFailed.title": "업데이트 실패",
"desktop.updater.installFailed.message": "업데이트를 설치하지 못했습니다",
"desktop.cli.installed.title": "CLI 설치됨",
"desktop.cli.installed.message":
"CLI가 {{path}}에 설치되었습니다\n\n터미널을 다시 시작하여 'opencode' 명령을 사용하세요.",
"desktop.cli.failed.title": "설치 실패",
"desktop.cli.failed.message": "CLI 설치 실패: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "파일",
"desktop.menu.edit": "편집",
"desktop.menu.view": "보기",
"desktop.menu.help": "도움말",
"desktop.menu.file.newSession": "새 세션",
"desktop.menu.file.openProject": "프로젝트 열기...",
"desktop.menu.view.toggleSidebar": "사이드바 전환",
"desktop.menu.view.toggleTerminal": "터미널 전환",
"desktop.menu.view.toggleFileTree": "파일 트리 전환",
"desktop.menu.view.back": "뒤로",
"desktop.menu.view.forward": "앞으로",
"desktop.menu.view.previousSession": "이전 세션",
"desktop.menu.view.nextSession": "다음 세션",
"desktop.menu.help.documentation": "OpenCode 문서",
"desktop.menu.help.supportForum": "지원 포럼",
"desktop.menu.help.shareFeedback": "피드백 공유",
"desktop.menu.help.reportBug": "버그 신고",
"desktop.cli.error.unsupportedPlatform": "CLI 설치는 macOS 및 Linux에서만 지원됩니다.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI 바이너리가 누락되었습니다. 데스크톱 앱을 다시 설치해 보세요.",
"desktop.cli.error.scriptWriteFailed": "CLI 설치 스크립트를 준비하지 못했습니다.",
"desktop.cli.error.scriptPermissionFailed": "CLI 설치 스크립트를 실행 가능하게 만들지 못했습니다.",
"desktop.cli.error.scriptRunFailed": "CLI 설치 스크립트를 실행하지 못했습니다.",
"desktop.cli.error.scriptFailed": "CLI 설치 프로그램이 실패했습니다.",
"desktop.cli.error.installPathUnknown": "CLI가 어디에 설치되었는지 확인할 수 없습니다.",
"desktop.cli.error.unknown": "알 수 없는 설치 오류",
"desktop.loading.status.initial": "잠시만 기다려 주세요...",
"desktop.loading.status.done": "모두 완료되었습니다",
"desktop.loading.status.migrating": "데이터베이스 마이그레이션 중",
"desktop.loading.status.waiting": "이 작업은 몇 분 정도 걸릴 수 있습니다",
"desktop.loading.progressAria": "데이터베이스 마이그레이션 진행률",
"desktop.server.local": "로컬 서버",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Se etter oppdateringer...",
"desktop.menu.installCli": "Installer CLI...",
"desktop.menu.reloadWebview": "Last inn Webview på nytt",
"desktop.menu.restart": "Start på nytt",
"desktop.dialog.chooseFolder": "Velg en mappe",
"desktop.dialog.chooseFile": "Velg en fil",
"desktop.dialog.saveFile": "Lagre fil",
"desktop.updater.checkFailed.title": "Oppdateringssjekk mislyktes",
"desktop.updater.checkFailed.message": "Kunne ikke se etter oppdateringer",
"desktop.updater.none.title": "Ingen oppdatering tilgjengelig",
"desktop.updater.none.message": "Du bruker allerede den nyeste versjonen av OpenCode",
"desktop.updater.downloadFailed.title": "Oppdatering mislyktes",
"desktop.updater.downloadFailed.message": "Kunne ikke laste ned oppdateringen",
"desktop.updater.downloaded.title": "Oppdatering lastet ned",
"desktop.updater.downloaded.prompt":
"Versjon {{version}} av OpenCode er lastet ned. Vil du installere den og starte på nytt?",
"desktop.updater.installFailed.title": "Oppdatering mislyktes",
"desktop.updater.installFailed.message": "Kunne ikke installere oppdateringen",
"desktop.cli.installed.title": "CLI installert",
"desktop.cli.installed.message":
"CLI installert til {{path}}\n\nStart terminalen på nytt for å bruke 'opencode'-kommandoen.",
"desktop.cli.failed.title": "Installasjon mislyktes",
"desktop.cli.failed.message": "Kunne ikke installere CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Fil",
"desktop.menu.edit": "Rediger",
"desktop.menu.view": "Vis",
"desktop.menu.help": "Hjelp",
"desktop.menu.file.newSession": "Ny sesjon",
"desktop.menu.file.openProject": "Åpne prosjekt...",
"desktop.menu.view.toggleSidebar": "Vis/skjul sidefelt",
"desktop.menu.view.toggleTerminal": "Vis/skjul terminal",
"desktop.menu.view.toggleFileTree": "Vis/skjul filtre",
"desktop.menu.view.back": "Tilbake",
"desktop.menu.view.forward": "Frem",
"desktop.menu.view.previousSession": "Forrige sesjon",
"desktop.menu.view.nextSession": "Neste sesjon",
"desktop.menu.help.documentation": "OpenCode Dokumentasjon",
"desktop.menu.help.supportForum": "Støtteforum",
"desktop.menu.help.shareFeedback": "Del tilbakemelding",
"desktop.menu.help.reportBug": "Rapporter en feil",
"desktop.cli.error.unsupportedPlatform": "CLI-installasjon støttes kun på macOS og Linux.",
"desktop.cli.error.sidecarMissing": "OpenCode CLI-binærfil mangler. Prøv å installere skrivebordsappen på nytt.",
"desktop.cli.error.scriptWriteFailed": "Kunne ikke klargjøre CLI-installasjonsskriptet.",
"desktop.cli.error.scriptPermissionFailed": "Kunne ikke gjøre CLI-installasjonsskriptet kjørbart.",
"desktop.cli.error.scriptRunFailed": "Kunne ikke kjøre CLI-installasjonsskriptet.",
"desktop.cli.error.scriptFailed": "CLI-installasjonsprogrammet mislyktes.",
"desktop.cli.error.installPathUnknown": "Kunne ikke avgjøre hvor CLI ble installert.",
"desktop.cli.error.unknown": "Ukjent installasjonsfeil",
"desktop.loading.status.initial": "Et øyeblikk...",
"desktop.loading.status.done": "Alt ferdig",
"desktop.loading.status.migrating": "Migrerer databasen din",
"desktop.loading.status.waiting": "Dette kan ta et par minutter",
"desktop.loading.progressAria": "Fremdrift for databasemigrering",
"desktop.server.local": "Lokal server",
}

View File

@@ -0,0 +1,62 @@
export const dict = {
"desktop.menu.checkForUpdates": "Sprawdź aktualizacje...",
"desktop.menu.installCli": "Zainstaluj CLI...",
"desktop.menu.reloadWebview": "Przeładuj Webview",
"desktop.menu.restart": "Restartuj",
"desktop.dialog.chooseFolder": "Wybierz folder",
"desktop.dialog.chooseFile": "Wybierz plik",
"desktop.dialog.saveFile": "Zapisz plik",
"desktop.updater.checkFailed.title": "Nie udało się sprawdzić aktualizacji",
"desktop.updater.checkFailed.message": "Nie udało się sprawdzić aktualizacji",
"desktop.updater.none.title": "Brak dostępnych aktualizacji",
"desktop.updater.none.message": "Korzystasz już z najnowszej wersji OpenCode",
"desktop.updater.downloadFailed.title": "Aktualizacja nie powiodła się",
"desktop.updater.downloadFailed.message": "Nie udało się pobrać aktualizacji",
"desktop.updater.downloaded.title": "Aktualizacja pobrana",
"desktop.updater.downloaded.prompt":
"Pobrano wersję {{version}} OpenCode. Czy chcesz ją zainstalować i uruchomić ponownie?",
"desktop.updater.installFailed.title": "Aktualizacja nie powiodła się",
"desktop.updater.installFailed.message": "Nie udało się zainstalować aktualizacji",
"desktop.cli.installed.title": "CLI zainstalowane",
"desktop.cli.installed.message":
"CLI zainstalowane w {{path}}\n\nUruchom ponownie terminal, aby użyć polecenia 'opencode'.",
"desktop.cli.failed.title": "Instalacja nie powiodła się",
"desktop.cli.failed.message": "Nie udało się zainstalować CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Plik",
"desktop.menu.edit": "Edycja",
"desktop.menu.view": "Widok",
"desktop.menu.help": "Pomoc",
"desktop.menu.file.newSession": "Nowa sesja",
"desktop.menu.file.openProject": "Otwórz projekt...",
"desktop.menu.view.toggleSidebar": "Przełącz pasek boczny",
"desktop.menu.view.toggleTerminal": "Przełącz terminal",
"desktop.menu.view.toggleFileTree": "Przełącz drzewo plików",
"desktop.menu.view.back": "Wstecz",
"desktop.menu.view.forward": "Dalej",
"desktop.menu.view.previousSession": "Poprzednia sesja",
"desktop.menu.view.nextSession": "Następna sesja",
"desktop.menu.help.documentation": "Dokumentacja OpenCode",
"desktop.menu.help.supportForum": "Forum wsparcia",
"desktop.menu.help.shareFeedback": "Prześlij opinię",
"desktop.menu.help.reportBug": "Zgłoś błąd",
"desktop.cli.error.unsupportedPlatform": "Instalacja CLI jest obsługiwana tylko na macOS i Linux.",
"desktop.cli.error.sidecarMissing":
"Brakuje pliku binarnego OpenCode CLI. Spróbuj ponownie zainstalować aplikację na komputer.",
"desktop.cli.error.scriptWriteFailed": "Nie udało się przygotować skryptu instalatora CLI.",
"desktop.cli.error.scriptPermissionFailed": "Nie udało się nadać uprawnień do wykonania skryptu instalatora CLI.",
"desktop.cli.error.scriptRunFailed": "Nie udało się uruchomić skryptu instalatora CLI.",
"desktop.cli.error.scriptFailed": "Instalator CLI nie powiódł się.",
"desktop.cli.error.installPathUnknown": "Nie udało się ustalić, gdzie zostało zainstalowane CLI.",
"desktop.cli.error.unknown": "Nieznany błąd instalacji",
"desktop.loading.status.initial": "Chwileczkę...",
"desktop.loading.status.done": "Gotowe",
"desktop.loading.status.migrating": "Migrowanie bazy danych",
"desktop.loading.status.waiting": "Może to potrwać kilka minut",
"desktop.loading.progressAria": "Postęp migracji bazy danych",
"desktop.server.local": "Serwer lokalny",
}

View File

@@ -0,0 +1,61 @@
export const dict = {
"desktop.menu.checkForUpdates": "Проверить обновления...",
"desktop.menu.installCli": "Установить CLI...",
"desktop.menu.reloadWebview": "Перезагрузить Webview",
"desktop.menu.restart": "Перезапустить",
"desktop.dialog.chooseFolder": "Выберите папку",
"desktop.dialog.chooseFile": "Выберите файл",
"desktop.dialog.saveFile": "Сохранить файл",
"desktop.updater.checkFailed.title": "Не удалось проверить обновления",
"desktop.updater.checkFailed.message": "Не удалось проверить обновления",
"desktop.updater.none.title": "Обновлений нет",
"desktop.updater.none.message": "Вы уже используете последнюю версию OpenCode",
"desktop.updater.downloadFailed.title": "Обновление не удалось",
"desktop.updater.downloadFailed.message": "Не удалось скачать обновление",
"desktop.updater.downloaded.title": "Обновление загружено",
"desktop.updater.downloaded.prompt": "Версия OpenCode {{version}} загружена. Хотите установить и перезапустить?",
"desktop.updater.installFailed.title": "Обновление не удалось",
"desktop.updater.installFailed.message": "Не удалось установить обновление",
"desktop.cli.installed.title": "CLI установлен",
"desktop.cli.installed.message":
"CLI установлен в {{path}}\n\nПерезапустите терминал, чтобы использовать команду 'opencode'.",
"desktop.cli.failed.title": "Ошибка установки",
"desktop.cli.failed.message": "Не удалось установить CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "Файл",
"desktop.menu.edit": "Правка",
"desktop.menu.view": "Вид",
"desktop.menu.help": "Справка",
"desktop.menu.file.newSession": "Новая сессия",
"desktop.menu.file.openProject": "Открыть проект...",
"desktop.menu.view.toggleSidebar": "Переключить боковую панель",
"desktop.menu.view.toggleTerminal": "Переключить терминал",
"desktop.menu.view.toggleFileTree": "Переключить дерево файлов",
"desktop.menu.view.back": "Назад",
"desktop.menu.view.forward": "Вперед",
"desktop.menu.view.previousSession": "Предыдущая сессия",
"desktop.menu.view.nextSession": "Следующая сессия",
"desktop.menu.help.documentation": "Документация OpenCode",
"desktop.menu.help.supportForum": "Форум поддержки",
"desktop.menu.help.shareFeedback": "Поделиться отзывом",
"desktop.menu.help.reportBug": "Сообщить об ошибке",
"desktop.cli.error.unsupportedPlatform": "Установка CLI поддерживается только в macOS и Linux.",
"desktop.cli.error.sidecarMissing":
"Отсутствует бинарный файл OpenCode CLI. Попробуйте переустановить настольное приложение.",
"desktop.cli.error.scriptWriteFailed": "Не удалось подготовить скрипт установщика CLI.",
"desktop.cli.error.scriptPermissionFailed": "Не удалось сделать скрипт установщика CLI исполняемым.",
"desktop.cli.error.scriptRunFailed": "Не удалось запустить скрипт установщика CLI.",
"desktop.cli.error.scriptFailed": "Ошибка установщика CLI.",
"desktop.cli.error.installPathUnknown": "Не удалось определить, куда был установлен CLI.",
"desktop.cli.error.unknown": "Неизвестная ошибка установки",
"desktop.loading.status.initial": "Минуточку...",
"desktop.loading.status.done": "Всё готово",
"desktop.loading.status.migrating": "Миграция вашей базы данных",
"desktop.loading.status.waiting": "Это может занять пару минут",
"desktop.loading.progressAria": "Прогресс миграции базы данных",
"desktop.server.local": "Локальный сервер",
}

View File

@@ -0,0 +1,59 @@
export const dict = {
"desktop.menu.checkForUpdates": "检查更新...",
"desktop.menu.installCli": "安装 CLI...",
"desktop.menu.reloadWebview": "重新加载 Webview",
"desktop.menu.restart": "重启",
"desktop.dialog.chooseFolder": "选择文件夹",
"desktop.dialog.chooseFile": "选择文件",
"desktop.dialog.saveFile": "保存文件",
"desktop.updater.checkFailed.title": "检查更新失败",
"desktop.updater.checkFailed.message": "无法检查更新",
"desktop.updater.none.title": "没有可用更新",
"desktop.updater.none.message": "你已经在使用最新版本的 OpenCode",
"desktop.updater.downloadFailed.title": "更新失败",
"desktop.updater.downloadFailed.message": "无法下载更新",
"desktop.updater.downloaded.title": "更新已下载",
"desktop.updater.downloaded.prompt": "已下载 OpenCode {{version}} 版本,是否安装并重启?",
"desktop.updater.installFailed.title": "更新失败",
"desktop.updater.installFailed.message": "无法安装更新",
"desktop.cli.installed.title": "CLI 已安装",
"desktop.cli.installed.message": "CLI 已安装到 {{path}}\n\n重启终端以使用 'opencode' 命令。",
"desktop.cli.failed.title": "安装失败",
"desktop.cli.failed.message": "无法安装 CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "文件",
"desktop.menu.edit": "编辑",
"desktop.menu.view": "查看",
"desktop.menu.help": "帮助",
"desktop.menu.file.newSession": "新会话",
"desktop.menu.file.openProject": "打开项目...",
"desktop.menu.view.toggleSidebar": "切换侧边栏",
"desktop.menu.view.toggleTerminal": "切换终端",
"desktop.menu.view.toggleFileTree": "切换文件树",
"desktop.menu.view.back": "后退",
"desktop.menu.view.forward": "前进",
"desktop.menu.view.previousSession": "上一个会话",
"desktop.menu.view.nextSession": "下一个会话",
"desktop.menu.help.documentation": "OpenCode 文档",
"desktop.menu.help.supportForum": "支持论坛",
"desktop.menu.help.shareFeedback": "分享反馈",
"desktop.menu.help.reportBug": "报告错误",
"desktop.cli.error.unsupportedPlatform": "CLI 安装仅在 macOS 和 Linux 上受支持。",
"desktop.cli.error.sidecarMissing": "OpenCode CLI 二进制文件缺失。请尝试重新安装桌面应用程序。",
"desktop.cli.error.scriptWriteFailed": "无法准备 CLI 安装脚本。",
"desktop.cli.error.scriptPermissionFailed": "无法使 CLI 安装脚本可执行。",
"desktop.cli.error.scriptRunFailed": "无法运行 CLI 安装脚本。",
"desktop.cli.error.scriptFailed": "CLI 安装程序失败。",
"desktop.cli.error.installPathUnknown": "无法确定 CLI 的安装位置。",
"desktop.cli.error.unknown": "未知的安装错误",
"desktop.loading.status.initial": "稍等片刻...",
"desktop.loading.status.done": "全部完成",
"desktop.loading.status.migrating": "正在迁移您的数据库",
"desktop.loading.status.waiting": "这可能需要几分钟",
"desktop.loading.progressAria": "数据库迁移进度",
"desktop.server.local": "本地服务器",
}

View File

@@ -0,0 +1,59 @@
export const dict = {
"desktop.menu.checkForUpdates": "檢查更新...",
"desktop.menu.installCli": "安裝 CLI...",
"desktop.menu.reloadWebview": "重新載入 Webview",
"desktop.menu.restart": "重新啟動",
"desktop.dialog.chooseFolder": "選擇資料夾",
"desktop.dialog.chooseFile": "選擇檔案",
"desktop.dialog.saveFile": "儲存檔案",
"desktop.updater.checkFailed.title": "檢查更新失敗",
"desktop.updater.checkFailed.message": "無法檢查更新",
"desktop.updater.none.title": "沒有可用更新",
"desktop.updater.none.message": "你已在使用最新版的 OpenCode",
"desktop.updater.downloadFailed.title": "更新失敗",
"desktop.updater.downloadFailed.message": "無法下載更新",
"desktop.updater.downloaded.title": "更新已下載",
"desktop.updater.downloaded.prompt": "已下載 OpenCode {{version}} 版本,是否安裝並重新啟動?",
"desktop.updater.installFailed.title": "更新失敗",
"desktop.updater.installFailed.message": "無法安裝更新",
"desktop.cli.installed.title": "CLI 已安裝",
"desktop.cli.installed.message": "CLI 已安裝到 {{path}}\n\n重新啟動終端機以使用 'opencode' 命令。",
"desktop.cli.failed.title": "安裝失敗",
"desktop.cli.failed.message": "無法安裝 CLI: {{error}}",
"desktop.menu.app": "OpenCode",
"desktop.menu.file": "檔案",
"desktop.menu.edit": "編輯",
"desktop.menu.view": "檢視",
"desktop.menu.help": "說明",
"desktop.menu.file.newSession": "新工作階段",
"desktop.menu.file.openProject": "開啟專案...",
"desktop.menu.view.toggleSidebar": "切換側邊欄",
"desktop.menu.view.toggleTerminal": "切換終端機",
"desktop.menu.view.toggleFileTree": "切換檔案樹",
"desktop.menu.view.back": "上一步",
"desktop.menu.view.forward": "下一步",
"desktop.menu.view.previousSession": "上一個工作階段",
"desktop.menu.view.nextSession": "下一個工作階段",
"desktop.menu.help.documentation": "OpenCode 文件",
"desktop.menu.help.supportForum": "支援論壇",
"desktop.menu.help.shareFeedback": "分享意見回饋",
"desktop.menu.help.reportBug": "回報錯誤",
"desktop.cli.error.unsupportedPlatform": "CLI 安裝僅支援 macOS 與 Linux。",
"desktop.cli.error.sidecarMissing": "OpenCode CLI 執行檔遺失。請嘗試重新安裝桌面應用程式。",
"desktop.cli.error.scriptWriteFailed": "無法準備 CLI 安裝指令碼。",
"desktop.cli.error.scriptPermissionFailed": "無法將 CLI 安裝指令碼設為可執行。",
"desktop.cli.error.scriptRunFailed": "無法執行 CLI 安裝指令碼。",
"desktop.cli.error.scriptFailed": "CLI 安裝程式失敗。",
"desktop.cli.error.installPathUnknown": "無法確定 CLI 的安裝位置。",
"desktop.cli.error.unknown": "未知的安裝錯誤",
"desktop.loading.status.initial": "稍等片刻...",
"desktop.loading.status.done": "全部完成",
"desktop.loading.status.migrating": "正在移轉您的資料庫",
"desktop.loading.status.waiting": "這可能需要幾分鐘",
"desktop.loading.progressAria": "資料庫移轉進度",
"desktop.server.local": "本機伺服器",
}

View File

@@ -0,0 +1,504 @@
// @refresh reload
import {
ACCEPTED_FILE_EXTENSIONS,
filePickerFilters,
AppBaseProviders,
AppInterface,
handleNotificationClick,
loadLocaleDict,
normalizeLocale,
type Locale,
type Platform,
PlatformProvider,
ServerConnection,
useCommand,
} from "@opencode-ai/app"
import type { AsyncStorage } from "@solid-primitives/storage"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { readImage } from "@tauri-apps/plugin-clipboard-manager"
import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
import { open, save } from "@tauri-apps/plugin-dialog"
import { fetch as tauriFetch } from "@tauri-apps/plugin-http"
import { isPermissionGranted, requestPermission } from "@tauri-apps/plugin-notification"
import { type as ostype } from "@tauri-apps/plugin-os"
import { relaunch } from "@tauri-apps/plugin-process"
import { open as shellOpen } from "@tauri-apps/plugin-shell"
import { Store } from "@tauri-apps/plugin-store"
import { check, type Update } from "@tauri-apps/plugin-updater"
import { createResource, onCleanup, onMount, Show } from "solid-js"
import { render } from "solid-js/web"
import pkg from "../package.json"
import { initI18n, t } from "./i18n"
import { UPDATER_ENABLED } from "./updater"
import { webviewZoom } from "./webview-zoom"
import "./styles.css"
import { Channel } from "@tauri-apps/api/core"
import { commands, type InitStep } from "./bindings"
import { createMenu } from "./menu"
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(t("error.dev.rootNotFound"))
}
void initI18n()
let update: Update | null = null
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 listenForDeepLinks = async () => {
const startUrls = await getCurrent().catch(() => null)
if (startUrls?.length) emitDeepLinks(startUrls)
await onOpenUrl((urls) => emitDeepLinks(urls)).catch(() => undefined)
}
const createPlatform = (): Platform => {
const os = (() => {
const type = ostype()
if (type === "macos" || type === "windows" || type === "linux") return type
return undefined
})()
const wslHome = async () => {
if (os !== "windows" || !window.__OPENCODE__?.wsl) return undefined
return commands.wslPath("~", "windows").catch(() => undefined)
}
const handleWslPicker = async <T extends string | string[]>(result: T | null): Promise<T | null> => {
if (!result || !window.__OPENCODE__?.wsl) return result
if (Array.isArray(result)) {
return Promise.all(result.map((path) => commands.wslPath(path, "linux").catch(() => path))) as any
}
return commands.wslPath(result, "linux").catch(() => result) as any
}
return {
platform: "desktop",
os,
version: pkg.version,
async openDirectoryPickerDialog(opts) {
const defaultPath = await wslHome()
const result = await open({
directory: true,
multiple: opts?.multiple ?? false,
title: opts?.title ?? t("desktop.dialog.chooseFolder"),
defaultPath,
})
return await handleWslPicker(result)
},
async openFilePickerDialog(opts) {
const result = await open({
directory: false,
multiple: opts?.multiple ?? false,
title: opts?.title ?? t("desktop.dialog.chooseFile"),
filters: filePickerFilters(opts?.extensions ?? ACCEPTED_FILE_EXTENSIONS),
})
return handleWslPicker(result)
},
async saveFilePickerDialog(opts) {
const result = await save({
title: opts?.title ?? t("desktop.dialog.saveFile"),
defaultPath: opts?.defaultPath,
})
return handleWslPicker(result)
},
openLink(url: string) {
void shellOpen(url).catch(() => undefined)
},
async openPath(path: string, app?: string) {
await commands.openPath(path, app ?? null)
},
back() {
window.history.back()
},
forward() {
window.history.forward()
},
storage: (() => {
type StoreLike = {
get(key: string): Promise<string | null | undefined>
set(key: string, value: string): Promise<unknown>
delete(key: string): Promise<unknown>
clear(): Promise<unknown>
keys(): Promise<string[]>
length(): Promise<number>
}
const WRITE_DEBOUNCE_MS = 250
const storeCache = new Map<string, Promise<StoreLike>>()
const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>()
const memoryCache = new Map<string, StoreLike>()
const flushAll = async () => {
const apis = Array.from(apiCache.values())
await Promise.all(apis.map((api) => api.flush().catch(() => undefined)))
}
if ("addEventListener" in globalThis) {
const handleVisibility = () => {
if (document.visibilityState !== "hidden") return
void flushAll()
}
window.addEventListener("pagehide", () => void flushAll())
document.addEventListener("visibilitychange", handleVisibility)
}
const createMemoryStore = () => {
const data = new Map<string, string>()
const store: StoreLike = {
get: async (key) => data.get(key),
set: async (key, value) => {
data.set(key, value)
},
delete: async (key) => {
data.delete(key)
},
clear: async () => {
data.clear()
},
keys: async () => Array.from(data.keys()),
length: async () => data.size,
}
return store
}
const getStore = (name: string) => {
const cached = storeCache.get(name)
if (cached) return cached
const store = Store.load(name).catch(() => {
const cached = memoryCache.get(name)
if (cached) return cached
const memory = createMemoryStore()
memoryCache.set(name, memory)
return memory
})
storeCache.set(name, store)
return store
}
const createStorage = (name: string) => {
const pending = new Map<string, string | null>()
let timer: ReturnType<typeof setTimeout> | undefined
let flushing: Promise<void> | undefined
const flush = async () => {
if (flushing) return flushing
flushing = (async () => {
const store = await getStore(name)
while (pending.size > 0) {
const batch = Array.from(pending.entries())
pending.clear()
for (const [key, value] of batch) {
if (value === null) {
await store.delete(key).catch(() => undefined)
} else {
await store.set(key, value).catch(() => undefined)
}
}
}
})().finally(() => {
flushing = undefined
})
return flushing
}
const schedule = () => {
if (timer) return
timer = setTimeout(() => {
timer = undefined
void flush()
}, WRITE_DEBOUNCE_MS)
}
const api: AsyncStorage & { flush: () => Promise<void> } = {
flush,
getItem: async (key: string) => {
const next = pending.get(key)
if (next !== undefined) return next
const store = await getStore(name)
const value = await store.get(key).catch(() => null)
if (value === undefined) return null
return value
},
setItem: async (key: string, value: string) => {
pending.set(key, value)
schedule()
},
removeItem: async (key: string) => {
pending.set(key, null)
schedule()
},
clear: async () => {
pending.clear()
const store = await getStore(name)
await store.clear().catch(() => undefined)
},
key: async (index: number) => {
const store = await getStore(name)
return (await store.keys().catch(() => []))[index]
},
getLength: async () => {
const store = await getStore(name)
return await store.length().catch(() => 0)
},
get length() {
return api.getLength()
},
}
return api
}
return (name = "default.dat") => {
const cached = apiCache.get(name)
if (cached) return cached
const api = createStorage(name)
apiCache.set(name, api)
return api
}
})(),
checkUpdate: async () => {
if (!UPDATER_ENABLED) return { updateAvailable: false }
const next = await check().catch(() => null)
if (!next) return { updateAvailable: false }
const ok = await next
.download()
.then(() => true)
.catch(() => false)
if (!ok) return { updateAvailable: false }
update = next
return { updateAvailable: true, version: next.version }
},
updateAndRestart: async () => {
if (!UPDATER_ENABLED || !update) return
if (ostype() === "windows") await commands.killSidecar().catch(() => undefined)
const installed = await update
.install()
.then(() => true)
.catch(() => false)
if (!installed) return
await relaunch()
},
restart: async () => {
await commands.killSidecar().catch(() => undefined)
await relaunch()
},
notify: async (title, description, href) => {
const granted = await isPermissionGranted().catch(() => false)
const permission = granted ? "granted" : await requestPermission().catch(() => "denied")
if (permission !== "granted") return
const win = getCurrentWindow()
const focused = await win.isFocused().catch(() => document.hasFocus())
if (focused) return
await Promise.resolve()
.then(() => {
const notification = new Notification(title, {
body: description ?? "",
icon: "https://opencode.ai/favicon-96x96-v3.png",
})
notification.onclick = () => {
const win = getCurrentWindow()
void win.show().catch(() => undefined)
void win.unminimize().catch(() => undefined)
void win.setFocus().catch(() => undefined)
handleNotificationClick(href)
notification.close()
}
})
.catch(() => undefined)
},
fetch: (input, init) => {
if (input instanceof Request) {
return tauriFetch(input)
} else {
return tauriFetch(input, init)
}
},
getWslEnabled: async () => {
const next = await commands.getWslConfig().catch(() => null)
if (next) return next.enabled
return window.__OPENCODE__!.wsl ?? false
},
setWslEnabled: async (enabled) => {
await commands.setWslConfig({ enabled })
},
getDefaultServer: async () => {
const url = await commands.getDefaultServerUrl().catch(() => null)
if (!url) return null
return ServerConnection.Key.make(url)
},
setDefaultServer: async (url: string | null) => {
await commands.setDefaultServerUrl(url)
},
getDisplayBackend: async () => {
const result = await commands.getDisplayBackend().catch(() => null)
return result
},
setDisplayBackend: async (backend) => {
await commands.setDisplayBackend(backend)
},
parseMarkdown: (markdown: string) => commands.parseMarkdownCommand(markdown),
webviewZoom,
checkAppExists: async (appName: string) => {
return commands.checkAppExists(appName)
},
async readClipboardImage() {
const image = await readImage().catch(() => null)
if (!image) return null
const bytes = await image.rgba().catch(() => null)
if (!bytes || bytes.length === 0) return null
const size = await image.size().catch(() => null)
if (!size) return null
const canvas = document.createElement("canvas")
canvas.width = size.width
canvas.height = size.height
const ctx = canvas.getContext("2d")
if (!ctx) return null
const imageData = ctx.createImageData(size.width, size.height)
imageData.data.set(bytes)
ctx.putImageData(imageData, 0, 0)
return new Promise<File | null>((resolve) => {
canvas.toBlob((blob) => {
if (!blob) return resolve(null)
resolve(
new File([blob], `pasted-image-${Date.now()}.png`, {
type: "image/png",
}),
)
}, "image/png")
})
},
}
}
let menuTrigger = null as null | ((id: string) => void)
void createMenu((id) => {
menuTrigger?.(id)
})
void listenForDeepLinks()
render(() => {
const platform = createPlatform()
const loadLocale = async () => {
const current = await platform.storage?.("opencode.global.dat").getItem("language")
const legacy = current ? undefined : await platform.storage?.().getItem("language.v1")
const raw = current ?? legacy
if (!raw) return
const locale = raw.match(/"locale"\s*:\s*"([^"]+)"/)?.[1]
if (!locale) return
const next = normalizeLocale(locale)
if (next !== "en") await loadLocaleDict(next)
return next satisfies Locale
}
// Fetch sidecar credentials from Rust (available immediately, before health check)
const [sidecar] = createResource(() => commands.awaitInitialization(new Channel<InitStep>() as any))
const [defaultServer] = createResource(() =>
platform.getDefaultServer?.().then((url) => {
if (url) return ServerConnection.key({ type: "http", http: { url } })
}),
)
const [locale] = createResource(loadLocale)
// Build the sidecar server connection once credentials arrive
const servers = () => {
const data = sidecar()
if (!data) return []
const http = {
url: data.url,
username: data.username ?? undefined,
password: data.password ?? undefined,
}
const server: ServerConnection.Sidecar = {
displayName: t("desktop.server.local"),
type: "sidecar",
variant: "base",
http,
}
return [server] as ServerConnection.Any[]
}
function handleClick(e: MouseEvent) {
const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null
if (link?.href) {
e.preventDefault()
platform.openLink(link.href)
}
}
function Inner() {
const cmd = useCommand()
menuTrigger = (id) => cmd.trigger(id)
return null
}
onMount(() => {
document.addEventListener("click", handleClick)
onCleanup(() => {
document.removeEventListener("click", handleClick)
})
})
return (
<PlatformProvider value={platform}>
<AppBaseProviders locale={locale.latest}>
<Show when={!defaultServer.loading && !sidecar.loading && !locale.loading}>
{(_) => {
return (
<AppInterface
defaultServer={defaultServer.latest ?? ServerConnection.Key.make("sidecar")}
servers={servers()}
>
<Inner />
</AppInterface>
)
}}
</Show>
</AppBaseProviders>
</PlatformProvider>
)
}, root!)

View File

@@ -0,0 +1,90 @@
import { render } from "solid-js/web"
import { MetaProvider } from "@solidjs/meta"
import "@opencode-ai/app/index.css"
import { Font } from "@opencode-ai/ui/font"
import { Splash } from "@opencode-ai/ui/logo"
import { Progress } from "@opencode-ai/ui/progress"
import "./styles.css"
import { createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js"
import { commands, events, InitStep } from "./bindings"
import { Channel } from "@tauri-apps/api/core"
import { initI18n, t } from "./i18n"
const root = document.getElementById("root")!
const lines = [
t("desktop.loading.status.initial"),
t("desktop.loading.status.migrating"),
t("desktop.loading.status.waiting"),
]
const delays = [3000, 9000]
void initI18n()
render(() => {
const [step, setStep] = createSignal<InitStep | null>(null)
const [line, setLine] = createSignal(0)
const [percent, setPercent] = createSignal(0)
const phase = createMemo(() => step()?.phase)
const value = createMemo(() => {
if (phase() === "done") return 100
return Math.max(25, Math.min(100, percent()))
})
const channel = new Channel<InitStep>()
channel.onmessage = (next) => setStep(next)
commands.awaitInitialization(channel as any).catch(() => undefined)
onMount(() => {
setLine(0)
setPercent(0)
const timers = delays.map((ms, i) => setTimeout(() => setLine(i + 1), ms))
const listener = events.sqliteMigrationProgress.listen((e) => {
if (e.payload.type === "InProgress") setPercent(Math.max(0, Math.min(100, e.payload.value)))
if (e.payload.type === "Done") setPercent(100)
})
onCleanup(() => {
void listener.then((cb) => cb())
timers.forEach(clearTimeout)
})
})
createEffect(() => {
if (phase() !== "done") return
const timer = setTimeout(() => events.loadingWindowComplete.emit(null), 1000)
onCleanup(() => clearTimeout(timer))
})
const status = createMemo(() => {
if (phase() === "done") return t("desktop.loading.status.done")
if (phase() === "sqlite_waiting") return lines[line()]
return t("desktop.loading.status.initial")
})
return (
<MetaProvider>
<div class="w-screen h-screen bg-background-base flex items-center justify-center">
<Font />
<div class="flex flex-col items-center gap-11">
<Splash class="w-20 h-25 opacity-15" />
<div class="w-60 flex flex-col items-center gap-4" aria-live="polite">
<span class="w-full overflow-hidden text-center text-ellipsis whitespace-nowrap text-text-strong text-14-normal">
{status()}
</span>
<Progress
value={value()}
class="w-20 [&_[data-slot='progress-track']]:h-1 [&_[data-slot='progress-track']]:border-0 [&_[data-slot='progress-track']]:rounded-none [&_[data-slot='progress-track']]:bg-surface-weak [&_[data-slot='progress-fill']]:rounded-none [&_[data-slot='progress-fill']]:bg-icon-warning-base"
aria-label={t("desktop.loading.progressAria")}
getValueLabel={({ value }) => `${Math.round(value)}%`}
/>
</div>
</div>
</div>
</MetaProvider>
)
}, root)

View File

@@ -0,0 +1,190 @@
import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu"
import { openUrl } from "@tauri-apps/plugin-opener"
import { type as ostype } from "@tauri-apps/plugin-os"
import { relaunch } from "@tauri-apps/plugin-process"
import { commands } from "./bindings"
import { installCli } from "./cli"
import { initI18n, t } from "./i18n"
import { runUpdater, UPDATER_ENABLED } from "./updater"
export async function createMenu(trigger: (id: string) => void) {
if (ostype() !== "macos") return
await initI18n()
const menu = await Menu.new({
items: [
await Submenu.new({
text: t("desktop.menu.app"),
items: [
await PredefinedMenuItem.new({
item: { About: null },
}),
await MenuItem.new({
enabled: UPDATER_ENABLED,
action: () => runUpdater({ alertOnFail: true }),
text: t("desktop.menu.checkForUpdates"),
}),
await MenuItem.new({
action: () => installCli(),
text: t("desktop.menu.installCli"),
}),
await MenuItem.new({
action: async () => window.location.reload(),
text: t("desktop.menu.reloadWebview"),
}),
await MenuItem.new({
action: async () => {
await commands.killSidecar().catch(() => undefined)
await relaunch().catch(() => undefined)
},
text: t("desktop.menu.restart"),
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await PredefinedMenuItem.new({
item: "Hide",
}),
await PredefinedMenuItem.new({
item: "HideOthers",
}),
await PredefinedMenuItem.new({
item: "ShowAll",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await PredefinedMenuItem.new({
item: "Quit",
}),
].filter(Boolean),
}),
await Submenu.new({
text: t("desktop.menu.file"),
items: [
await MenuItem.new({
text: t("desktop.menu.file.newSession"),
accelerator: "Shift+Cmd+S",
action: () => trigger("session.new"),
}),
await MenuItem.new({
text: t("desktop.menu.file.openProject"),
accelerator: "Cmd+O",
action: () => trigger("project.open"),
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await PredefinedMenuItem.new({
item: "CloseWindow",
}),
],
}),
await Submenu.new({
text: t("desktop.menu.edit"),
items: [
await PredefinedMenuItem.new({
item: "Undo",
}),
await PredefinedMenuItem.new({
item: "Redo",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await PredefinedMenuItem.new({
item: "Cut",
}),
await PredefinedMenuItem.new({
item: "Copy",
}),
await PredefinedMenuItem.new({
item: "Paste",
}),
await PredefinedMenuItem.new({
item: "SelectAll",
}),
],
}),
await Submenu.new({
text: t("desktop.menu.view"),
items: [
await MenuItem.new({
action: () => trigger("sidebar.toggle"),
text: t("desktop.menu.view.toggleSidebar"),
accelerator: "Cmd+B",
}),
await MenuItem.new({
action: () => trigger("terminal.toggle"),
text: t("desktop.menu.view.toggleTerminal"),
accelerator: "Ctrl+`",
}),
await MenuItem.new({
action: () => trigger("fileTree.toggle"),
text: t("desktop.menu.view.toggleFileTree"),
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => trigger("common.goBack"),
text: t("desktop.menu.view.back"),
}),
await MenuItem.new({
action: () => trigger("common.goForward"),
text: t("desktop.menu.view.forward"),
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => trigger("session.previous"),
text: t("desktop.menu.view.previousSession"),
accelerator: "Option+ArrowUp",
}),
await MenuItem.new({
action: () => trigger("session.next"),
text: t("desktop.menu.view.nextSession"),
accelerator: "Option+ArrowDown",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
],
}),
await Submenu.new({
text: t("desktop.menu.help"),
items: [
// missing native macos search
await MenuItem.new({
action: () => openUrl("https://opencode.ai/docs"),
text: t("desktop.menu.help.documentation"),
}),
await MenuItem.new({
action: () => openUrl("https://discord.com/invite/opencode"),
text: t("desktop.menu.help.supportForum"),
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
// await MenuItem.new({
// text: "Release Notes",
// }),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => openUrl("https://github.com/anomalyco/opencode/issues/new?template=feature_request.yml"),
text: t("desktop.menu.help.shareFeedback"),
}),
await MenuItem.new({
action: () => openUrl("https://github.com/anomalyco/opencode/issues/new?template=bug_report.yml"),
text: t("desktop.menu.help.reportBug"),
}),
],
}),
],
})
void menu.setAsAppMenu()
}

View File

@@ -0,0 +1,7 @@
button.decorum-tb-btn,
button#decorum-tb-minimize,
button#decorum-tb-maximize,
button#decorum-tb-close,
div[data-tauri-decorum-tb] {
height: calc(var(--spacing) * 10) !important;
}

View File

@@ -0,0 +1,51 @@
import { check } from "@tauri-apps/plugin-updater"
import { relaunch } from "@tauri-apps/plugin-process"
import { ask, message } from "@tauri-apps/plugin-dialog"
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
export async function runUpdater({ alertOnFail }: { alertOnFail: boolean }) {
await initI18n()
let update
try {
update = await check()
} catch {
if (alertOnFail)
await message(t("desktop.updater.checkFailed.message"), { title: t("desktop.updater.checkFailed.title") })
return
}
if (!update) {
if (alertOnFail) await message(t("desktop.updater.none.message"), { title: t("desktop.updater.none.title") })
return
}
try {
await update.download()
} catch {
if (alertOnFail)
await message(t("desktop.updater.downloadFailed.message"), { title: t("desktop.updater.downloadFailed.title") })
return
}
const shouldUpdate = await ask(t("desktop.updater.downloaded.prompt", { version: update.version }), {
title: t("desktop.updater.downloaded.title"),
})
if (!shouldUpdate) return
try {
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 commands.killSidecar()
await relaunch()
}

View File

@@ -0,0 +1,37 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { invoke } from "@tauri-apps/api/core"
import { type as ostype } from "@tauri-apps/plugin-os"
import { createSignal } from "solid-js"
const OS_NAME = ostype()
const [webviewZoom, setWebviewZoom] = createSignal(1)
const MAX_ZOOM_LEVEL = 10
const MIN_ZOOM_LEVEL = 0.2
const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL)
const applyZoom = (next: number) => {
setWebviewZoom(next)
void invoke("plugin:webview|set_webview_zoom", {
value: next,
})
}
window.addEventListener("keydown", (event) => {
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
let newZoom = webviewZoom()
if (event.key === "-") newZoom -= 0.2
if (event.key === "=" || event.key === "+") newZoom += 0.2
if (event.key === "0") newZoom = 1
applyZoom(clamp(newZoom))
})
export { webviewZoom }