chore: initialize qiming workspace repository
This commit is contained in:
67
qimingcode/packages/desktop/src/bindings.ts
Normal file
67
qimingcode/packages/desktop/src/bindings.ts
Normal 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);
|
||||
}
|
||||
|
||||
43
qimingcode/packages/desktop/src/cli.ts
Normal file
43
qimingcode/packages/desktop/src/cli.ts
Normal 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"),
|
||||
})
|
||||
}
|
||||
}
|
||||
5
qimingcode/packages/desktop/src/entry.tsx
Normal file
5
qimingcode/packages/desktop/src/entry.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
if (location.pathname === "/loading") {
|
||||
void import("./loading")
|
||||
} else {
|
||||
void import("./")
|
||||
}
|
||||
59
qimingcode/packages/desktop/src/i18n/ar.ts
Normal file
59
qimingcode/packages/desktop/src/i18n/ar.ts
Normal 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": "خادم محلي",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/br.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/br.ts
Normal 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",
|
||||
}
|
||||
62
qimingcode/packages/desktop/src/i18n/bs.ts
Normal file
62
qimingcode/packages/desktop/src/i18n/bs.ts
Normal 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",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/da.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/da.ts
Normal 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",
|
||||
}
|
||||
62
qimingcode/packages/desktop/src/i18n/de.ts
Normal file
62
qimingcode/packages/desktop/src/i18n/de.ts
Normal 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",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/en.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/en.ts
Normal 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",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/es.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/es.ts
Normal 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",
|
||||
}
|
||||
62
qimingcode/packages/desktop/src/i18n/fr.ts
Normal file
62
qimingcode/packages/desktop/src/i18n/fr.ts
Normal 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",
|
||||
}
|
||||
192
qimingcode/packages/desktop/src/i18n/index.ts
Normal file
192
qimingcode/packages/desktop/src/i18n/index.ts
Normal 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
|
||||
}
|
||||
62
qimingcode/packages/desktop/src/i18n/ja.ts
Normal file
62
qimingcode/packages/desktop/src/i18n/ja.ts
Normal 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": "ローカルサーバー",
|
||||
}
|
||||
60
qimingcode/packages/desktop/src/i18n/ko.ts
Normal file
60
qimingcode/packages/desktop/src/i18n/ko.ts
Normal 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": "로컬 서버",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/no.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/no.ts
Normal 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",
|
||||
}
|
||||
62
qimingcode/packages/desktop/src/i18n/pl.ts
Normal file
62
qimingcode/packages/desktop/src/i18n/pl.ts
Normal 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",
|
||||
}
|
||||
61
qimingcode/packages/desktop/src/i18n/ru.ts
Normal file
61
qimingcode/packages/desktop/src/i18n/ru.ts
Normal 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": "Локальный сервер",
|
||||
}
|
||||
59
qimingcode/packages/desktop/src/i18n/zh.ts
Normal file
59
qimingcode/packages/desktop/src/i18n/zh.ts
Normal 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": "本地服务器",
|
||||
}
|
||||
59
qimingcode/packages/desktop/src/i18n/zht.ts
Normal file
59
qimingcode/packages/desktop/src/i18n/zht.ts
Normal 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": "本機伺服器",
|
||||
}
|
||||
504
qimingcode/packages/desktop/src/index.tsx
Normal file
504
qimingcode/packages/desktop/src/index.tsx
Normal 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!)
|
||||
90
qimingcode/packages/desktop/src/loading.tsx
Normal file
90
qimingcode/packages/desktop/src/loading.tsx
Normal 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)
|
||||
190
qimingcode/packages/desktop/src/menu.ts
Normal file
190
qimingcode/packages/desktop/src/menu.ts
Normal 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()
|
||||
}
|
||||
7
qimingcode/packages/desktop/src/styles.css
Normal file
7
qimingcode/packages/desktop/src/styles.css
Normal 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;
|
||||
}
|
||||
51
qimingcode/packages/desktop/src/updater.ts
Normal file
51
qimingcode/packages/desktop/src/updater.ts
Normal 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()
|
||||
}
|
||||
37
qimingcode/packages/desktop/src/webview-zoom.ts
Normal file
37
qimingcode/packages/desktop/src/webview-zoom.ts
Normal 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 }
|
||||
Reference in New Issue
Block a user