mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-21 19:43:32 +00:00
feat(desktop): optimize cold development startup (#42722)
This commit is contained in:
parent
e2d9376614
commit
2a7d0729d0
113 changed files with 2479 additions and 2656 deletions
|
|
@ -15,6 +15,7 @@
|
|||
"dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
|
||||
"dev:www": "bun run --cwd packages/www dev",
|
||||
"dev:storybook": "bun --cwd packages/storybook storybook",
|
||||
"bench:devex": "bun run --cwd packages/app test:bench:devex",
|
||||
"lint": "oxlint",
|
||||
"lint:effect-patterns": "ast-grep scan -c script/ast-grep/sgconfig.yml packages/util/src packages/core/src packages/server/src packages/protocol/src packages/cli/src",
|
||||
"test:lint-rules": "ast-grep test -c script/ast-grep/sgconfig.yml",
|
||||
|
|
|
|||
|
|
@ -2,12 +2,34 @@
|
|||
|
||||
The app's high-volume performance diagnostics live under `packages/app/e2e/performance` and are excluded from normal local and CI Playwright discovery. The benchmark config builds the app and serves the production bundle before running scenarios serially.
|
||||
|
||||
The `devex` category is the explicit exception to the production-build rule. It measures development commands from submission through a user-visible ready state and has its own Playwright configuration.
|
||||
|
||||
Run the suite explicitly from `packages/app`:
|
||||
|
||||
```sh
|
||||
bun run test:bench
|
||||
```
|
||||
|
||||
Run the desktop development startup benchmark from the repository root:
|
||||
|
||||
```sh
|
||||
bun run bench:devex
|
||||
```
|
||||
|
||||
It runs five serial samples of the exact `bun dev:desktop` command. Each sample uses a fresh desktop profile, database, service configuration, service registration, and service process; the desktop selects an isolated ephemeral loopback endpoint. It removes desktop build output and the desktop Vite cache before every run; dependencies, Bun's package cache, and Electron remain installed. The harness stops only that sample's service; it does not stop or change the elected global OpenCode service. The measured endpoint is a visible Home page whose empty-state controls pass Playwright actionability checks. The command's Electron installation check remains inside the measured interval.
|
||||
|
||||
Set `DESKTOP_STARTUP_RUNS` only for focused diagnostics:
|
||||
|
||||
```sh
|
||||
DESKTOP_STARTUP_RUNS=1 bun run bench:devex
|
||||
```
|
||||
|
||||
Set `OPENCODE_PERFORMANCE_TRACE_DIR` to capture the renderer's CDP trace from attachment through actionable Home:
|
||||
|
||||
```sh
|
||||
DESKTOP_STARTUP_RUNS=1 OPENCODE_PERFORMANCE_TRACE_DIR=/tmp/opencode-desktop-traces bun run bench:devex
|
||||
```
|
||||
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ const categories = [
|
|||
"disabled-by-default-v8.cpu_profiler",
|
||||
]
|
||||
|
||||
export async function startChromeTrace(page: Page, name: string) {
|
||||
export async function startChromeTrace(page: Page, name: string): Promise<undefined | (() => Promise<string>)> {
|
||||
const directory = process.env.OPENCODE_PERFORMANCE_TRACE_DIR
|
||||
if (!directory) return
|
||||
if (!directory) return undefined
|
||||
|
||||
const selectors = process.env.OPENCODE_PERFORMANCE_SELECTOR_TRACE === "1"
|
||||
const file = await prepareChromeTrace(directory, name, selectors)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import { benchmark } from "../benchmark"
|
||||
import {
|
||||
desktopBenchmarkContext,
|
||||
runDesktopStartup,
|
||||
summarizeDesktopStartup,
|
||||
type DesktopStartupSample,
|
||||
} from "./desktop-startup"
|
||||
|
||||
benchmark.describe("devex: desktop startup", () => {
|
||||
benchmark("opens a cold desktop on Home", async ({ report }, testInfo) => {
|
||||
benchmark.setTimeout(15 * 60_000)
|
||||
const runs = Number(process.env.DESKTOP_STARTUP_RUNS ?? 5)
|
||||
if (!Number.isSafeInteger(runs) || runs < 1) throw new Error("DESKTOP_STARTUP_RUNS must be a positive integer")
|
||||
|
||||
const samples: DesktopStartupSample[] = []
|
||||
const context = await desktopBenchmarkContext(runs)
|
||||
for (let run = 1; run <= runs; run++) {
|
||||
const sample = await runDesktopStartup(run, testInfo).catch((error) => {
|
||||
report(samples.length ? { samples, summary: summarizeDesktopStartup(samples) } : { samples }, context)
|
||||
throw error
|
||||
})
|
||||
samples.push(sample)
|
||||
}
|
||||
report({ samples, summary: summarizeDesktopStartup(samples) }, context)
|
||||
})
|
||||
})
|
||||
526
packages/app/e2e/performance/devex/desktop-startup.ts
Normal file
526
packages/app/e2e/performance/devex/desktop-startup.ts
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
import { Service } from "@opencode-ai/client/service"
|
||||
import { chromium, expect, type Browser, type Page, type TestInfo } from "@playwright/test"
|
||||
import { spawn, spawnSync, type ChildProcess } from "node:child_process"
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join, resolve } from "node:path"
|
||||
import { startChromeTrace } from "../chrome-trace"
|
||||
|
||||
const repository = resolve(import.meta.dirname, "../../../../..")
|
||||
const milestones = [
|
||||
"bunRootScript",
|
||||
"bunDesktopScript",
|
||||
"desktopPrepared",
|
||||
"mainBundleReady",
|
||||
"preloadBundleReady",
|
||||
"rendererDevServerReady",
|
||||
"electronSpawnStarted",
|
||||
"debugEndpointReady",
|
||||
"electronStarted",
|
||||
"serviceEnsureStarted",
|
||||
"serviceSpawnRequested",
|
||||
"serviceReady",
|
||||
"backgroundLoadingReady",
|
||||
"rendererViteConnected",
|
||||
"rendererInitializationStarted",
|
||||
"rendererInitializationReady",
|
||||
"windowVisible",
|
||||
"homeReady",
|
||||
] as const
|
||||
const phases = [
|
||||
"desktopPreparation",
|
||||
"viteMainBundle",
|
||||
"vitePreloadBundle",
|
||||
"rendererServerStartup",
|
||||
"electronStartup",
|
||||
"serviceSpawnWait",
|
||||
"serviceProcessStartup",
|
||||
"rendererStartup",
|
||||
"visibleWindowToHome",
|
||||
] as const
|
||||
|
||||
type Milestone = (typeof milestones)[number]
|
||||
type Phase = (typeof phases)[number]
|
||||
type ServiceInfo = { id: string; version: string; url: string; pid: number }
|
||||
|
||||
export type DesktopStartupSample = {
|
||||
run: number
|
||||
commandToHomeReadyMs: number
|
||||
milestonesMs: Record<Milestone, number>
|
||||
phasesMs: Record<Phase, number>
|
||||
service: Omit<ServiceInfo, "id">
|
||||
}
|
||||
|
||||
export async function runDesktopStartup(run: number, testInfo: TestInfo) {
|
||||
const profile = await createColdProfile()
|
||||
const desktop = await Promise.resolve()
|
||||
.then(() => startDesktop(profile))
|
||||
.catch(async (error) => {
|
||||
await rm(profile.root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
|
||||
throw error
|
||||
})
|
||||
try {
|
||||
const page = await desktop.open()
|
||||
const stopTrace = await startChromeTrace(page, `desktop-startup-${run}`)
|
||||
try {
|
||||
await startThemeObservation(page)
|
||||
await waitForHome(page, desktop.mark)
|
||||
await requireStableTheme(page)
|
||||
return await desktop.result(run)
|
||||
} finally {
|
||||
await stopTrace?.()
|
||||
}
|
||||
} finally {
|
||||
await desktop.close(testInfo, run)
|
||||
}
|
||||
}
|
||||
|
||||
export async function desktopBenchmarkContext(runs: number) {
|
||||
const pkg = JSON.parse(await readFile(join(repository, "packages/desktop/package.json"), "utf8"))
|
||||
const revision = spawnSync("git", ["rev-parse", "HEAD"], { cwd: repository })
|
||||
if (revision.status !== 0) throw new Error("Failed to read the benchmark Git revision")
|
||||
const status = spawnSync("git", ["status", "--porcelain"], { cwd: repository })
|
||||
if (status.status !== 0) throw new Error("Failed to read the benchmark Git status")
|
||||
const bun = spawnSync("bun", ["--version"], { cwd: repository })
|
||||
if (bun.status !== 0) throw new Error("Failed to read the benchmark Bun version")
|
||||
return {
|
||||
arch: process.arch,
|
||||
command: "bun dev:desktop",
|
||||
runs,
|
||||
profile: "fresh",
|
||||
service: "isolated-cold",
|
||||
install: "complete",
|
||||
viteCache: "cold",
|
||||
electronInstall: "present",
|
||||
bunVersion: bun.stdout.toString().trim(),
|
||||
electronVersion: pkg.devDependencies.electron,
|
||||
electronViteVersionRange: pkg.devDependencies["electron-vite"],
|
||||
gitCommit: revision.stdout.toString().trim(),
|
||||
gitDirty: status.stdout.length > 0,
|
||||
trace: Boolean(process.env.OPENCODE_PERFORMANCE_TRACE_DIR),
|
||||
}
|
||||
}
|
||||
|
||||
export function summarizeDesktopStartup(samples: DesktopStartupSample[]) {
|
||||
return {
|
||||
commandToHomeReadyMs: statistics(samples.map((sample) => sample.commandToHomeReadyMs)),
|
||||
milestonesMs: Object.fromEntries(
|
||||
milestones.map((name) => [name, statistics(samples.map((sample) => sample.milestonesMs[name]))]),
|
||||
),
|
||||
phasesMs: Object.fromEntries(
|
||||
phases.map((name) => [name, statistics(samples.map((sample) => sample.phasesMs[name]))]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function milestoneForLine(line: string): Milestone | undefined {
|
||||
const text = stripAnsi(line)
|
||||
return milestonePatterns.find((item) => text.includes(item.text))?.name
|
||||
}
|
||||
|
||||
const milestonePatterns: ReadonlyArray<{ name: Milestone; text: string }> = [
|
||||
{ name: "bunRootScript", text: "$ bun --cwd packages/desktop dev" },
|
||||
{ name: "bunDesktopScript", text: "$ bun ./scripts/dev.ts" },
|
||||
{ name: "desktopPrepared", text: "Copied dev icons from" },
|
||||
{ name: "mainBundleReady", text: "electron main process built successfully" },
|
||||
{ name: "preloadBundleReady", text: "electron preload scripts built successfully" },
|
||||
{ name: "rendererDevServerReady", text: "dev server running for the electron renderer process at:" },
|
||||
{ name: "electronSpawnStarted", text: "starting electron app..." },
|
||||
{ name: "debugEndpointReady", text: "DevTools listening on ws://" },
|
||||
{ name: "electronStarted", text: "app starting" },
|
||||
{ name: "serviceEnsureStarted", text: "starting v2 background service" },
|
||||
{ name: "serviceSpawnRequested", text: "v2 CLI background service starting" },
|
||||
{ name: "serviceReady", text: "v2 CLI background service ready" },
|
||||
{ name: "backgroundLoadingReady", text: "loading task finished" },
|
||||
{ name: "rendererViteConnected", text: "[vite] connected." },
|
||||
{ name: "rendererInitializationStarted", text: "awaiting server ready" },
|
||||
{ name: "rendererInitializationReady", text: "server ready" },
|
||||
{ name: "windowVisible", text: "main window visible" },
|
||||
]
|
||||
|
||||
async function createColdProfile() {
|
||||
await Promise.all(
|
||||
["packages/desktop/node_modules/.vite", "packages/desktop/out"].map((path) =>
|
||||
rm(join(repository, path), { recursive: true, force: true }),
|
||||
),
|
||||
)
|
||||
const root = await mkdtemp(join(tmpdir(), "opencode-desktop-startup-"))
|
||||
return initializeColdProfile(root).catch(async (error) => {
|
||||
await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
|
||||
throw error
|
||||
})
|
||||
}
|
||||
|
||||
async function initializeColdProfile(root: string) {
|
||||
await Promise.all(
|
||||
["data", "config", "cache", "state", "desktop", "session", "home"].map((dir) =>
|
||||
mkdir(join(root, dir), { recursive: true }),
|
||||
),
|
||||
)
|
||||
await Promise.all([
|
||||
writeFile(
|
||||
join(root, "desktop", "opencode.settings"),
|
||||
JSON.stringify({ firstLaunchOnboardingComplete: true }),
|
||||
),
|
||||
writeFile(join(root, "desktop", "opencode.global.dat"), JSON.stringify({ language: '{"locale":"en"}' })),
|
||||
])
|
||||
const registration = join(root, "desktop", "opencode", "service-local.json")
|
||||
await Service.stop({ file: registration })
|
||||
return { root, registration }
|
||||
}
|
||||
|
||||
function startDesktop(profile: Awaited<ReturnType<typeof createColdProfile>>) {
|
||||
const started = performance.now()
|
||||
const child = spawn("bun", ["dev:desktop"], {
|
||||
cwd: repository,
|
||||
detached: process.platform !== "win32",
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCODE_CONFIG_DIR: join(profile.root, "config"),
|
||||
OPENCODE_DB: join(profile.root, "data", "opencode.db"),
|
||||
OPENCODE_TEST_HOME: join(profile.root, "home"),
|
||||
OPENCODE_TEST_ONBOARDING: "0",
|
||||
OPENCODE_DESKTOP_TEST_ROOT: profile.root,
|
||||
OPENCODE_DESKTOP_REMOTE_DEBUGGING_PORT: "0",
|
||||
OPENCODE_DESKTOP_DISABLE_PROTOCOL_REGISTRATION: "1",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
})
|
||||
if (!child.pid || !child.stdout || !child.stderr) throw new Error("Failed to start the desktop command")
|
||||
const exited = childExit(child)
|
||||
const observed: Partial<Record<Milestone, number>> = {}
|
||||
const endpoint = Promise.withResolvers<string>()
|
||||
const pageErrors: string[] = []
|
||||
let browser: Browser | undefined
|
||||
let service: ServiceInfo | undefined
|
||||
const mark = (name: Milestone) => {
|
||||
observed[name] ??= elapsed(started)
|
||||
}
|
||||
const record = (line: string) => {
|
||||
const milestone = milestoneForLine(line)
|
||||
if (milestone) mark(milestone)
|
||||
const match = stripAnsi(line).match(/DevTools listening on (ws:\/\/\S+)/)
|
||||
if (match?.[1]) endpoint.resolve(match[1])
|
||||
}
|
||||
const stdout = observeOutput(child.stdout, record)
|
||||
const stderr = observeOutput(child.stderr, record)
|
||||
|
||||
return {
|
||||
mark,
|
||||
async open() {
|
||||
const url = await Promise.race([
|
||||
endpoint.promise,
|
||||
exited.then((code) => {
|
||||
throw new Error(`Desktop command exited with code ${code} before opening its debug endpoint`)
|
||||
}),
|
||||
sleep(120_000).then(() => {
|
||||
throw new Error("Timed out waiting for the desktop debug endpoint")
|
||||
}),
|
||||
])
|
||||
browser = await chromium.connectOverCDP(url, { timeout: 120_000 })
|
||||
const context = browser.contexts()[0]
|
||||
if (!context) throw new Error("Electron did not expose a browser context")
|
||||
await expect.poll(() => context.pages().length, { timeout: 120_000 }).toBeGreaterThan(0)
|
||||
const page = context.pages()[0]
|
||||
if (!page) throw new Error("Electron did not expose a renderer page")
|
||||
page.on("pageerror", (error) => pageErrors.push(error.stack ?? error.message))
|
||||
return page
|
||||
},
|
||||
async result(run: number): Promise<DesktopStartupSample> {
|
||||
if (pageErrors.length) throw new Error(`Desktop renderer reported errors:\n\n${pageErrors.join("\n\n")}`)
|
||||
service = await readService(profile)
|
||||
const milestonesMs = requireMilestones(observed)
|
||||
return {
|
||||
run,
|
||||
commandToHomeReadyMs: milestonesMs.homeReady,
|
||||
milestonesMs,
|
||||
phasesMs: calculatePhases(milestonesMs),
|
||||
service: {
|
||||
version: service.version,
|
||||
url: service.url,
|
||||
pid: service.pid,
|
||||
},
|
||||
}
|
||||
},
|
||||
async close(testInfo: TestInfo, run: number) {
|
||||
const errors: unknown[] = []
|
||||
await browser?.close().catch(() => undefined)
|
||||
await stopProcessTree(child, exited).catch((error) => {
|
||||
errors.push(error)
|
||||
child.stdout?.destroy()
|
||||
child.stderr?.destroy()
|
||||
})
|
||||
const [stdoutText, stderrText] = await Promise.all([stdout, stderr]).catch((error) => {
|
||||
errors.push(error)
|
||||
return ["", ""]
|
||||
})
|
||||
await Promise.all([
|
||||
testInfo.attach(`desktop-startup-${run}-stdout`, { body: stdoutText, contentType: "text/plain" }),
|
||||
testInfo.attach(`desktop-startup-${run}-stderr`, { body: stderrText, contentType: "text/plain" }),
|
||||
pageErrors.length
|
||||
? testInfo.attach(`desktop-startup-${run}-page-errors`, {
|
||||
body: pageErrors.join("\n\n"),
|
||||
contentType: "text/plain",
|
||||
})
|
||||
: Promise.resolve(),
|
||||
]).catch((error) => errors.push(error))
|
||||
await Service.stop({ file: profile.registration }).catch((error) => errors.push(error))
|
||||
if (service && processAlive(service.pid))
|
||||
errors.push(new Error(`Desktop service process ${service.pid} did not stop`))
|
||||
await rm(profile.root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch((error) =>
|
||||
errors.push(error),
|
||||
)
|
||||
if (errors.length) throw new AggregateError(errors, "Desktop benchmark cleanup failed")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForHome(page: Page, mark: (name: Milestone) => void) {
|
||||
await expect.poll(() => page.evaluate(() => document.visibilityState), { timeout: 120_000 }).toBe("visible")
|
||||
|
||||
const projects = page.getByRole("complementary", { name: "Projects", exact: true })
|
||||
const sessions = page.getByRole("region", { name: "Recent sessions", exact: true })
|
||||
const search = page.getByRole("textbox", { name: "Search sessions", exact: true })
|
||||
const addProject = projects.locator('button[data-action="home-add-project-row"]')
|
||||
await expect(projects).toBeVisible({ timeout: 120_000 })
|
||||
await expect(sessions).toBeVisible()
|
||||
await expect(search).toBeEditable()
|
||||
await expect(sessions.getByText("Nothing here yet", { exact: true })).toBeVisible()
|
||||
await expect(addProject).toHaveCount(1)
|
||||
await addProject.click({ trial: true })
|
||||
mark("homeReady")
|
||||
}
|
||||
|
||||
type ThemeWindow = Window & {
|
||||
__OPENCODE_THEME_STATES__?: string[]
|
||||
__OPENCODE_THEME_OBSERVER__?: MutationObserver
|
||||
}
|
||||
|
||||
async function startThemeObservation(page: Page) {
|
||||
await page.addInitScript(installThemeObservation)
|
||||
await page.evaluate(installThemeObservation)
|
||||
}
|
||||
|
||||
async function requireStableTheme(page: Page) {
|
||||
const states = await page.evaluate(() => {
|
||||
const target = window as ThemeWindow
|
||||
target.__OPENCODE_THEME_OBSERVER__?.disconnect()
|
||||
return target.__OPENCODE_THEME_STATES__ ?? []
|
||||
})
|
||||
if (states.length !== 1) throw new Error(`Desktop theme changed during startup: ${states.join(" -> ")}`)
|
||||
}
|
||||
|
||||
function installThemeObservation() {
|
||||
const target = window as ThemeWindow
|
||||
const observeRoot = () => {
|
||||
const root = document.documentElement
|
||||
if (!root) return false
|
||||
const state = () => {
|
||||
const theme = root.dataset.theme
|
||||
const scheme = root.dataset.colorScheme
|
||||
return theme && scheme ? `${theme}:${scheme}` : undefined
|
||||
}
|
||||
const initial = state()
|
||||
target.__OPENCODE_THEME_STATES__ = initial ? [initial] : []
|
||||
target.__OPENCODE_THEME_OBSERVER__ = new MutationObserver(() => {
|
||||
const next = state()
|
||||
if (!next) return
|
||||
if (target.__OPENCODE_THEME_STATES__?.at(-1) !== next) target.__OPENCODE_THEME_STATES__?.push(next)
|
||||
})
|
||||
target.__OPENCODE_THEME_OBSERVER__.observe(root, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "data-color-scheme"],
|
||||
})
|
||||
return true
|
||||
}
|
||||
if (observeRoot()) return
|
||||
const documentObserver = new MutationObserver(() => {
|
||||
if (!observeRoot()) return
|
||||
documentObserver.disconnect()
|
||||
})
|
||||
target.__OPENCODE_THEME_OBSERVER__ = documentObserver
|
||||
documentObserver.observe(document, { childList: true })
|
||||
}
|
||||
|
||||
async function observeOutput(stream: NodeJS.ReadableStream, record: (line: string) => void) {
|
||||
const decoder = new TextDecoder()
|
||||
const output: string[] = []
|
||||
let pending = ""
|
||||
for await (const chunk of stream) {
|
||||
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true })
|
||||
output.push(text)
|
||||
pending += text
|
||||
const lines = pending.split(/\r?\n/)
|
||||
pending = lines.pop() ?? ""
|
||||
lines.forEach(record)
|
||||
}
|
||||
const final = decoder.decode()
|
||||
output.push(final)
|
||||
pending += final
|
||||
if (pending) record(pending)
|
||||
return output.join("")
|
||||
}
|
||||
|
||||
async function readService(profile: Awaited<ReturnType<typeof createColdProfile>>) {
|
||||
const value: unknown = JSON.parse(await readFile(profile.registration, "utf8"))
|
||||
if (!isServiceInfo(value)) throw new Error("Desktop service registration is invalid")
|
||||
const url = new URL(value.url)
|
||||
const port = Number(url.port)
|
||||
if (url.hostname !== "127.0.0.1" || !Number.isInteger(port) || port <= 0)
|
||||
throw new Error(`Desktop service used unexpected endpoint ${value.url}`)
|
||||
if (!value.version.startsWith("2.0.0-local-"))
|
||||
throw new Error(`Desktop service used unexpected version ${value.version}`)
|
||||
return value
|
||||
}
|
||||
|
||||
function isServiceInfo(value: unknown): value is ServiceInfo {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
"id" in value &&
|
||||
typeof value.id === "string" &&
|
||||
"version" in value &&
|
||||
typeof value.version === "string" &&
|
||||
"url" in value &&
|
||||
typeof value.url === "string" &&
|
||||
"pid" in value &&
|
||||
typeof value.pid === "number"
|
||||
)
|
||||
}
|
||||
|
||||
function requireMilestones(observed: Partial<Record<Milestone, number>>) {
|
||||
const get = (name: Milestone) => {
|
||||
const value = observed[name]
|
||||
if (value === undefined) throw new Error(`Desktop startup did not report milestone: ${name}`)
|
||||
return round(value)
|
||||
}
|
||||
return {
|
||||
bunRootScript: get("bunRootScript"),
|
||||
bunDesktopScript: get("bunDesktopScript"),
|
||||
desktopPrepared: get("desktopPrepared"),
|
||||
mainBundleReady: get("mainBundleReady"),
|
||||
preloadBundleReady: get("preloadBundleReady"),
|
||||
rendererDevServerReady: get("rendererDevServerReady"),
|
||||
electronSpawnStarted: get("electronSpawnStarted"),
|
||||
debugEndpointReady: get("debugEndpointReady"),
|
||||
electronStarted: get("electronStarted"),
|
||||
serviceEnsureStarted: get("serviceEnsureStarted"),
|
||||
serviceSpawnRequested: get("serviceSpawnRequested"),
|
||||
serviceReady: get("serviceReady"),
|
||||
backgroundLoadingReady: get("backgroundLoadingReady"),
|
||||
rendererViteConnected: get("rendererViteConnected"),
|
||||
rendererInitializationStarted: get("rendererInitializationStarted"),
|
||||
rendererInitializationReady: get("rendererInitializationReady"),
|
||||
windowVisible: get("windowVisible"),
|
||||
homeReady: get("homeReady"),
|
||||
}
|
||||
}
|
||||
|
||||
function calculatePhases(value: Record<Milestone, number>): Record<Phase, number> {
|
||||
return {
|
||||
desktopPreparation: value.desktopPrepared,
|
||||
viteMainBundle: round(value.mainBundleReady - value.desktopPrepared),
|
||||
vitePreloadBundle: round(value.preloadBundleReady - value.mainBundleReady),
|
||||
rendererServerStartup: round(value.rendererDevServerReady - value.preloadBundleReady),
|
||||
electronStartup: round(value.electronStarted - value.electronSpawnStarted),
|
||||
serviceSpawnWait: round(value.serviceSpawnRequested - value.serviceEnsureStarted),
|
||||
serviceProcessStartup: round(value.serviceReady - value.serviceSpawnRequested),
|
||||
rendererStartup: round(value.homeReady - value.rendererViteConnected),
|
||||
visibleWindowToHome: round(value.homeReady - value.windowVisible),
|
||||
}
|
||||
}
|
||||
|
||||
function statistics(values: number[]) {
|
||||
if (!values.length) throw new Error("Cannot summarize an empty benchmark")
|
||||
const sorted = values.toSorted((left, right) => left - right)
|
||||
const median = medianOf(sorted)
|
||||
return {
|
||||
min: round(sorted[0]),
|
||||
median: round(median),
|
||||
max: round(sorted.at(-1)!),
|
||||
medianAbsoluteDeviation: round(medianOf(sorted.map((value) => Math.abs(value - median)).toSorted((a, b) => a - b))),
|
||||
}
|
||||
}
|
||||
|
||||
function medianOf(sorted: number[]) {
|
||||
const middle = Math.floor(sorted.length / 2)
|
||||
if (sorted.length % 2) return sorted[middle]
|
||||
return (sorted[middle - 1] + sorted[middle]) / 2
|
||||
}
|
||||
|
||||
async function stopProcessTree(child: ChildProcess, exited: Promise<number | null>) {
|
||||
if (!child.pid) throw new Error("Desktop command has no process ID")
|
||||
if (process.platform !== "win32") return stopProcessGroup(child.pid, exited)
|
||||
if (child.exitCode !== null || (await exitsWithin(child, exited, 2_000))) return
|
||||
const kill = spawn("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
})
|
||||
await childExit(kill)
|
||||
if (await exitsWithin(child, exited, 10_000)) return
|
||||
if (!(await exitsWithin(child, exited, 5_000))) throw new Error(`Desktop command process ${child.pid} did not stop`)
|
||||
}
|
||||
|
||||
async function stopProcessGroup(pid: number, exited: Promise<number | null>) {
|
||||
await Promise.race([exited, sleep(2_000)])
|
||||
if (!processGroupAlive(pid)) return
|
||||
process.kill(-pid, "SIGTERM")
|
||||
if (await processGroupStopsWithin(pid, 10_000)) return
|
||||
process.kill(-pid, "SIGKILL")
|
||||
if (!(await processGroupStopsWithin(pid, 5_000))) throw new Error(`Desktop command process group ${pid} did not stop`)
|
||||
}
|
||||
|
||||
async function processGroupStopsWithin(pid: number, timeout: number) {
|
||||
const deadline = Date.now() + timeout
|
||||
while (Date.now() < deadline) {
|
||||
if (!processGroupAlive(pid)) return true
|
||||
await sleep(50)
|
||||
}
|
||||
return !processGroupAlive(pid)
|
||||
}
|
||||
|
||||
function processGroupAlive(pid: number) {
|
||||
try {
|
||||
process.kill(-pid, 0)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function exitsWithin(child: ChildProcess, exited: Promise<number | null>, timeout: number) {
|
||||
if (child.exitCode !== null) return true
|
||||
const result = await Promise.race([exited.then(() => true), sleep(timeout).then(() => false)])
|
||||
return result
|
||||
}
|
||||
|
||||
function childExit(child: ChildProcess) {
|
||||
return new Promise<number | null>((resolve, reject) => {
|
||||
child.once("error", reject)
|
||||
child.once("exit", (code) => resolve(code))
|
||||
})
|
||||
}
|
||||
|
||||
function sleep(milliseconds: number) {
|
||||
return new Promise<void>((resolve) => setTimeout(resolve, milliseconds))
|
||||
}
|
||||
|
||||
function processAlive(pid: number) {
|
||||
try {
|
||||
process.kill(pid, 0)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function stripAnsi(value: string) {
|
||||
return value.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
||||
}
|
||||
|
||||
function elapsed(started: number) {
|
||||
return round(performance.now() - started)
|
||||
}
|
||||
|
||||
function round(value: number) {
|
||||
return Math.round(value * 100) / 100
|
||||
}
|
||||
17
packages/app/e2e/performance/devex/playwright.config.ts
Normal file
17
packages/app/e2e/performance/devex/playwright.config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { defineConfig } from "@playwright/test"
|
||||
|
||||
process.env.OPENCODE_PERFORMANCE_RUN_ID ??= `${new Date().toISOString().replace(/[:.]/g, "-")}-${process.pid}`
|
||||
|
||||
export default defineConfig({
|
||||
testDir: ".",
|
||||
testMatch: "desktop-startup-benchmark.spec.ts",
|
||||
outputDir: "../../test-results/performance-devex",
|
||||
timeout: 15 * 60_000,
|
||||
expect: {
|
||||
timeout: 120_000,
|
||||
},
|
||||
fullyParallel: false,
|
||||
workers: 1,
|
||||
reporter: [["html", { outputFolder: "../../playwright-report/performance-devex", open: "never" }], ["line"]],
|
||||
projects: [{ name: "desktop" }],
|
||||
})
|
||||
|
|
@ -7,7 +7,7 @@ process.env.OPENCODE_PERFORMANCE_RUN_ID ??= `${new Date().toISOString().replace(
|
|||
export default {
|
||||
...config,
|
||||
testDir: ".",
|
||||
testIgnore: "unit/**",
|
||||
testIgnore: ["unit/**", "devex/**"],
|
||||
outputDir: "../test-results/performance",
|
||||
fullyParallel: false,
|
||||
workers: 1,
|
||||
|
|
|
|||
88
packages/app/e2e/performance/unit/desktop-startup.test.ts
Normal file
88
packages/app/e2e/performance/unit/desktop-startup.test.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { inlineThemePreload } from "../../../vite.js"
|
||||
import { milestoneForLine, summarizeDesktopStartup, type DesktopStartupSample } from "../devex/desktop-startup"
|
||||
|
||||
describe("desktop startup benchmark", () => {
|
||||
test.each(["/oc-theme-preload.js", "./oc-theme-preload.js"])("inlines %s before the renderer runs", (path) => {
|
||||
const html = inlineThemePreload(`<script id="oc-theme-preload-script" src="${path}"></script>`)
|
||||
expect(html).not.toContain(" src=")
|
||||
expect(html).toContain("opencode-color-scheme")
|
||||
})
|
||||
|
||||
test("recognizes startup milestones in colored output", () => {
|
||||
const cases = [
|
||||
["bunRootScript", "$ bun --cwd packages/desktop dev"],
|
||||
["bunDesktopScript", "$ bun ./scripts/dev.ts"],
|
||||
["desktopPrepared", "Copied dev icons from"],
|
||||
["mainBundleReady", "electron main process built successfully"],
|
||||
["preloadBundleReady", "electron preload scripts built successfully"],
|
||||
["rendererDevServerReady", "dev server running for the electron renderer process at:"],
|
||||
["electronSpawnStarted", "starting electron app..."],
|
||||
["debugEndpointReady", "DevTools listening on ws://"],
|
||||
["electronStarted", "app starting"],
|
||||
["serviceEnsureStarted", "starting v2 background service"],
|
||||
["serviceSpawnRequested", "v2 CLI background service starting"],
|
||||
["serviceReady", "v2 CLI background service ready"],
|
||||
["backgroundLoadingReady", "loading task finished"],
|
||||
["rendererViteConnected", "[vite] connected."],
|
||||
["rendererInitializationStarted", "awaiting server ready"],
|
||||
["rendererInitializationReady", "server ready"],
|
||||
["windowVisible", "main window visible"],
|
||||
] as const
|
||||
cases.forEach(([milestone, line]) => {
|
||||
expect(milestoneForLine(`\u001b[32m${line}\u001b[39m`)).toBe(milestone)
|
||||
})
|
||||
expect(milestoneForLine("12:30:00.000 › v2 CLI background service ready {")).toBe("serviceReady")
|
||||
expect(milestoneForLine("unrelated output")).toBeUndefined()
|
||||
})
|
||||
|
||||
test("keeps raw samples and reports median absolute deviation", () => {
|
||||
const samples = [24, 20, 22, 28, 26].map((commandToHomeReadyMs, index) => sample(index + 1, commandToHomeReadyMs))
|
||||
expect(summarizeDesktopStartup(samples).commandToHomeReadyMs).toEqual({
|
||||
min: 20,
|
||||
median: 24,
|
||||
max: 28,
|
||||
medianAbsoluteDeviation: 2,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sample(run: number, commandToHomeReadyMs: number): DesktopStartupSample {
|
||||
const milestonesMs = {
|
||||
bunRootScript: 1,
|
||||
bunDesktopScript: 2,
|
||||
desktopPrepared: 3,
|
||||
mainBundleReady: 4,
|
||||
preloadBundleReady: 5,
|
||||
rendererDevServerReady: 6,
|
||||
electronSpawnStarted: 7,
|
||||
debugEndpointReady: 8,
|
||||
electronStarted: 9,
|
||||
serviceEnsureStarted: 10,
|
||||
serviceSpawnRequested: 11,
|
||||
serviceReady: 12,
|
||||
backgroundLoadingReady: 13,
|
||||
rendererViteConnected: 14,
|
||||
rendererInitializationStarted: 15,
|
||||
rendererInitializationReady: 16,
|
||||
windowVisible: 17,
|
||||
homeReady: commandToHomeReadyMs,
|
||||
}
|
||||
return {
|
||||
run,
|
||||
commandToHomeReadyMs,
|
||||
milestonesMs,
|
||||
phasesMs: {
|
||||
desktopPreparation: 3,
|
||||
viteMainBundle: 1,
|
||||
vitePreloadBundle: 1,
|
||||
rendererServerStartup: 1,
|
||||
electronStartup: 2,
|
||||
serviceSpawnWait: 1,
|
||||
serviceProcessStartup: 1,
|
||||
rendererStartup: commandToHomeReadyMs - 14,
|
||||
visibleWindowToHome: commandToHomeReadyMs - 17,
|
||||
},
|
||||
service: { version: "2.0.0-local-test", url: "http://127.0.0.1:3000", pid: run },
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,9 @@ test("preserves the draft when a populated command menu triggers a built-in", as
|
|||
await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
|
||||
const composer = page.locator('[data-component="prompt-input-v2"]')
|
||||
const input = composer.locator('[data-component="prompt-input"]')
|
||||
await expect.poll(() => input.evaluate((element) => getComputedStyle(element, "::before").content)).toBe(
|
||||
`"${String.fromCodePoint(0x200b)}"`,
|
||||
)
|
||||
await expectAppVisible(composer)
|
||||
|
||||
await input.fill("keep me")
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ test("session settings use the remote server context", async ({ page }) => {
|
|||
await configureServers(page)
|
||||
|
||||
await page.goto(`/server/${base64Encode(serverB)}/session/${sessionB.id}`)
|
||||
await expect(page.getByText(sessionB.title).first()).toBeVisible()
|
||||
await expect(page.getByRole("heading", { name: sessionB.title, exact: true })).toBeVisible()
|
||||
await page.keyboard.press("Control+,")
|
||||
|
||||
const dialog = page.locator(".settings-v2-dialog")
|
||||
|
|
@ -61,7 +61,7 @@ test("auto-accept responds for an unfocused server session", async ({ page }) =>
|
|||
|
||||
const hrefB = `/server/${base64Encode(serverB)}/session/${sessionB.id}`
|
||||
await page.goto(`/server/${base64Encode(serverA)}/session/${sessionA.id}`)
|
||||
await expect(page.getByText(sessionA.title).first()).toBeVisible()
|
||||
await expect(page.getByRole("heading", { name: sessionA.title, exact: true })).toBeVisible()
|
||||
await page.keyboard.press("Control+,")
|
||||
const autoAccept = page.locator(".settings-v2-dialog").locator('[data-action="settings-auto-accept-permissions"]')
|
||||
await autoAccept.locator('[data-slot="switch-control"]').click()
|
||||
|
|
@ -78,7 +78,7 @@ test("auto-accept responds for an unfocused server session", async ({ page }) =>
|
|||
|
||||
await page.locator(`[data-titlebar-tab-slot]:has(a[href="${hrefB}"])`).click()
|
||||
await expect(page).toHaveURL(new RegExp(`${hrefB.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
|
||||
await expect(page.getByText(sessionB.title).first()).toBeVisible()
|
||||
await expect(page.getByRole("heading", { name: sessionB.title, exact: true })).toBeVisible()
|
||||
await transport.waitForConnection()
|
||||
|
||||
await transport.send({
|
||||
|
|
@ -208,7 +208,7 @@ async function mockServers(page: Page, permissionRequests: string[], permissionR
|
|||
return json(route, [
|
||||
{
|
||||
id: remote ? sessionB.projectID : "project-server-a",
|
||||
worktree: directory,
|
||||
canonical: directory,
|
||||
vcs: "git",
|
||||
time: { created: 1, updated: 1 },
|
||||
sandboxes: [],
|
||||
|
|
@ -216,7 +216,7 @@ async function mockServers(page: Page, permissionRequests: string[], permissionR
|
|||
])
|
||||
}
|
||||
if (url.pathname === "/api/project/current")
|
||||
return json(route, { id: remote ? sessionB.projectID : "project-server-a", directory })
|
||||
return json(route, { id: remote ? sessionB.projectID : "project-server-a", directory, canonical: directory })
|
||||
if (url.pathname === "/api/session")
|
||||
return json(route, { data: sessions.map((session) => currentSession(session)), cursor: {} })
|
||||
if (url.pathname === "/api/session/active") return json(route, { data: {} })
|
||||
|
|
@ -237,7 +237,7 @@ function session(id: string, directory: string, title: string) {
|
|||
id,
|
||||
slug: id,
|
||||
projectID: `project-${id}`,
|
||||
directory,
|
||||
location: { directory },
|
||||
title,
|
||||
version: "dev",
|
||||
time: { created: 1, updated: 1 },
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./desktop": "./src/desktop.ts",
|
||||
"./desktop-menu": "./src/desktop-menu.ts",
|
||||
"./i18n/desktop-native": "./src/i18n/desktop-native.ts",
|
||||
"./updater": "./src/updater.ts",
|
||||
|
|
@ -28,14 +29,15 @@
|
|||
"test:e2e:ui": "playwright test --ui",
|
||||
"test:e2e:report": "playwright show-report e2e/playwright-report",
|
||||
"test:stability": "bun test ./e2e/performance/unit/visual-stability.test.ts && playwright test --config e2e/performance/timeline-stability/playwright.config.ts",
|
||||
"test:bench": "bun test ./e2e/performance/unit && playwright test --config e2e/performance/playwright.config.ts"
|
||||
"test:bench": "bun test ./e2e/performance/unit && playwright test --config e2e/performance/playwright.config.ts",
|
||||
"test:bench:devex": "bun test ./e2e/performance/unit/desktop-startup.test.ts && playwright test --config e2e/performance/devex/playwright.config.ts"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@happy-dom/global-registrator": "20.0.11",
|
||||
"@playwright/test": "catalog:",
|
||||
"@sentry/vite-plugin": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@tailwindcss/vite": "4.3.3",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/luxon": "catalog:",
|
||||
|
|
@ -44,9 +46,9 @@
|
|||
"happy-dom": "20.11.1",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite": "8.2.1",
|
||||
"vite-plugin-icons-spritesheet": "3.0.1",
|
||||
"vite-plugin-solid": "catalog:"
|
||||
"vite-plugin-solid": "2.11.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
|
|
@ -85,6 +87,6 @@
|
|||
"solid-js": "catalog:",
|
||||
"solid-list": "catalog:",
|
||||
"solid-presence": "0.2.0",
|
||||
"tailwindcss": "catalog:"
|
||||
"tailwindcss": "4.3.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import "@/index.css"
|
||||
import * as Sentry from "@sentry/solid"
|
||||
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
|
||||
import { FileComponentProvider } from "@opencode-ai/ui/context/file"
|
||||
import { File } from "@opencode-ai/session-ui/file"
|
||||
import { Font } from "@opencode-ai/ui/font"
|
||||
import { ThemeProvider } from "@opencode-ai/ui/theme/context"
|
||||
import { MetaProvider } from "@solidjs/meta"
|
||||
import { type BaseRouterProps, Navigate, Route, Router, useParams, useSearchParams } from "@solidjs/router"
|
||||
import { type BaseRouterProps, Route, Router, useParams } from "@solidjs/router"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
|
||||
import {
|
||||
type Component,
|
||||
|
|
@ -20,30 +18,37 @@ import {
|
|||
} from "solid-js"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
import { CommandProvider, useCommand, type CommandOption } from "@/context/command"
|
||||
import { CommentsProvider } from "@/context/comments"
|
||||
import { FileProvider } from "@/context/file"
|
||||
import { GlobalProvider, useGlobal } from "@/context/global"
|
||||
import { HighlightsProvider } from "@/context/highlights"
|
||||
import { LanguageProvider, UiI18nBridge, type Locale, useLanguage } from "@/context/language"
|
||||
import { LayoutProvider } from "@/context/layout"
|
||||
import { ModelsProvider } from "@/context/models"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { PromptProvider } from "@/context/prompt"
|
||||
import { ServerConnection, ServersProvider } from "@/context/servers"
|
||||
import { SettingsProvider } from "@/context/settings"
|
||||
import { TabsProvider, useTabs, type DraftTab } from "@/context/tabs"
|
||||
import { LocationProvider } from "@/context/location"
|
||||
import { TabsProvider } from "@/context/tabs"
|
||||
import { WslServersProvider } from "@/wsl/context"
|
||||
import { SessionUIProvider } from "@/pages/directory-layout"
|
||||
import Layout from "@/pages/layout"
|
||||
import { ErrorPage } from "./pages/error"
|
||||
import { requireServerKey } from "./utils/session-route"
|
||||
|
||||
import { TargetSessionRouteContent } from "@/pages/session"
|
||||
import { Home } from "@/pages/home"
|
||||
import { ServerProvider } from "./context/server"
|
||||
|
||||
const NewSession = lazy(() => import("@/pages/new-session"))
|
||||
const File = lazy(() => import("@opencode-ai/session-ui/file").then((module) => ({ default: module.File })))
|
||||
const loadDraftRoute = () => Promise.all([import("@/pages/draft-route"), File.preload()]).then(([module]) => module)
|
||||
const loadSessionRoute = () => Promise.all([import("@/pages/session"), File.preload()]).then(([module]) => module)
|
||||
const DraftRoute = lazy(() => loadDraftRoute().then((module) => ({ default: module.DraftRoute })))
|
||||
const TargetSessionRouteContent = lazy(() =>
|
||||
loadSessionRoute().then((module) => ({ default: module.TargetSessionRouteContent })),
|
||||
)
|
||||
|
||||
export function preloadRoute(url: string) {
|
||||
const pathname = url.split(/[?#]/, 1)[0]
|
||||
if (pathname === "/new-session") return DraftRoute.preload().then(() => undefined)
|
||||
if (/^\/server\/[^/]+\/session\/[^/]+$/.test(pathname))
|
||||
return TargetSessionRouteContent.preload().then(() => undefined)
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function TargetServerRoute(props: ParentProps) {
|
||||
const params = useParams<{ serverKey: string }>()
|
||||
|
|
@ -60,45 +65,6 @@ function TargetServerRoute(props: ParentProps) {
|
|||
)
|
||||
}
|
||||
|
||||
function DraftRoute() {
|
||||
const [search] = useSearchParams<{ draftId?: string }>()
|
||||
const tabs = useTabs()
|
||||
return (
|
||||
<Show
|
||||
when={tabs.store.find((tab): tab is DraftTab => tab.type === "draft" && tab.draftID === search.draftId)}
|
||||
keyed
|
||||
fallback={tabs.ready() && <Navigate href="/" />}
|
||||
>
|
||||
{(draft) => <ResolvedDraftRoute draft={draft} />}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
function ResolvedDraftRoute(props: { draft: DraftTab }) {
|
||||
const global = useGlobal()
|
||||
const conn = createMemo(() => global.servers.list().find((item) => ServerConnection.key(item) === props.draft.server))
|
||||
|
||||
return (
|
||||
<Show when={`${props.draft.server}\0${props.draft.directory}`} keyed>
|
||||
<Show when={conn()} keyed>
|
||||
{(conn) => (
|
||||
<ServerProvider conn={conn}>
|
||||
<ModelsProvider directory={props.draft.directory}>
|
||||
<LocationProvider directory={props.draft.directory}>
|
||||
<SessionUIProvider directory={props.draft.directory} server={props.draft.server}>
|
||||
<DraftProviders>
|
||||
<NewSession draftId={props.draft.draftID} />
|
||||
</DraftProviders>
|
||||
</SessionUIProvider>
|
||||
</LocationProvider>
|
||||
</ModelsProvider>
|
||||
</ServerProvider>
|
||||
)}
|
||||
</Show>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__OPENCODE__?: {
|
||||
|
|
@ -167,22 +133,11 @@ function AppLayout(props: ParentProps) {
|
|||
)
|
||||
}
|
||||
|
||||
// The draft page only renders the prompt composer, so it drops TerminalProvider.
|
||||
// FileProvider and CommentsProvider stay because PromptInput uses file search and comment context.
|
||||
function DraftProviders(props: ParentProps) {
|
||||
return (
|
||||
<FileProvider>
|
||||
<PromptProvider>
|
||||
<CommentsProvider>{props.children}</CommentsProvider>
|
||||
</PromptProvider>
|
||||
</FileProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export function AppBaseProviders(
|
||||
props: ParentProps<{
|
||||
locale?: Locale
|
||||
onNativeTranslations?: Parameters<typeof LanguageProvider>[0]["onNativeTranslations"]
|
||||
onThemeApplied?: () => void
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
|
|
@ -191,13 +146,14 @@ export function AppBaseProviders(
|
|||
<ThemeProvider
|
||||
onThemeApplied={(_, mode, scheme) => {
|
||||
void window.api?.setTitlebar?.({ mode, scheme })
|
||||
props.onThemeApplied?.()
|
||||
}}
|
||||
>
|
||||
<LanguageProvider locale={props.locale} onNativeTranslations={props.onNativeTranslations}>
|
||||
<UiI18nBridge>
|
||||
<ErrorBoundary
|
||||
fallback={(error) => {
|
||||
Sentry.captureException(error)
|
||||
void import("@sentry/solid").then(({ captureException }) => captureException(error))
|
||||
return <ErrorPage error={error} />
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -241,7 +241,11 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
|||
sessionId: activeSession.id,
|
||||
}
|
||||
const model = tabs.stateValue<PromptSession>(sessionTab, "prompt")?.model.current()
|
||||
tabs.newDraft({ server: sessionTab.server, directory: activeSession.location.directory }, "", model)
|
||||
void tabs.newDraft(
|
||||
{ server: sessionTab.server, directory: activeSession.location.directory },
|
||||
"",
|
||||
model,
|
||||
)
|
||||
return
|
||||
}
|
||||
case "draft": {
|
||||
|
|
@ -249,7 +253,7 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
|||
if (activeTab?.type !== "draft") return
|
||||
|
||||
const model = tabs.stateValue<PromptSession>(activeTab, "prompt")?.model.current()
|
||||
tabs.newDraft({ server: activeTab.server, directory: activeTab.directory }, "", model)
|
||||
void tabs.newDraft({ server: activeTab.server, directory: activeTab.directory }, "", model)
|
||||
return
|
||||
}
|
||||
case "home": {
|
||||
|
|
@ -263,7 +267,7 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
|||
projects?.list().find((item) => item.worktree === projects.last()) ??
|
||||
projects?.list()[0]
|
||||
if (conn && project) {
|
||||
tabs.newDraft({ server: ServerConnection.key(conn), directory: project.worktree }, "")
|
||||
void tabs.newDraft({ server: ServerConnection.key(conn), directory: project.worktree }, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -467,11 +471,14 @@ function ChannelIndicator(props: { debugTools?: { visible: boolean; toggle: () =
|
|||
)
|
||||
}
|
||||
|
||||
const label = channel && ["local", "beta", "dev"].includes(channel) ? channel.toUpperCase() : undefined
|
||||
return (
|
||||
<Show when={["local", "beta", "dev"].includes(channel)}>
|
||||
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono">
|
||||
{channel.toUpperCase()}
|
||||
</div>
|
||||
<Show when={label}>
|
||||
{(value) => (
|
||||
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono">
|
||||
{value()}
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
9
packages/app/src/desktop.ts
Normal file
9
packages/app/src/desktop.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export { AppBaseProviders, AppInterface, preloadRoute } from "./app"
|
||||
export { ACCEPTED_FILE_EXTENSIONS } from "./constants/file-picker"
|
||||
export { useCommand } from "./context/command"
|
||||
export { loadLocaleDict, normalizeLocale, type Locale, useLanguage } from "./context/language"
|
||||
export { type Platform, PlatformProvider } from "./context/platform"
|
||||
export { ServerConnection, useServers } from "./context/servers"
|
||||
export { useTabs } from "./context/tabs"
|
||||
export { createDraftStore } from "./utils/draft-store"
|
||||
export { useWslServers } from "./wsl/context"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
export { AppBaseProviders, AppInterface } from "./app"
|
||||
export { AppBaseProviders, AppInterface, preloadRoute } from "./app"
|
||||
export { useLayout } from "./context/layout"
|
||||
export { useServerSDK } from "./context/server-sdk"
|
||||
export { useServers as useServers } from "./context/servers"
|
||||
|
|
|
|||
64
packages/app/src/pages/draft-route.tsx
Normal file
64
packages/app/src/pages/draft-route.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { Navigate, useSearchParams } from "@solidjs/router"
|
||||
import { createMemo, Show, type ParentProps } from "solid-js"
|
||||
import { CommentsProvider } from "@/context/comments"
|
||||
import { FileProvider } from "@/context/file"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { LocationProvider } from "@/context/location"
|
||||
import { ModelsProvider } from "@/context/models"
|
||||
import { PromptProvider } from "@/context/prompt"
|
||||
import { ServerProvider } from "@/context/server"
|
||||
import { ServerConnection } from "@/context/servers"
|
||||
import { useTabs, type DraftTab } from "@/context/tabs"
|
||||
import { SessionUIProvider } from "@/pages/directory-layout"
|
||||
import NewSession from "@/pages/new-session"
|
||||
|
||||
export function DraftRoute() {
|
||||
const [search] = useSearchParams<{ draftId?: string }>()
|
||||
const tabs = useTabs()
|
||||
return (
|
||||
<Show
|
||||
when={tabs.store.find((tab): tab is DraftTab => tab.type === "draft" && tab.draftID === search.draftId)}
|
||||
keyed
|
||||
fallback={tabs.ready() && <Navigate href="/" />}
|
||||
>
|
||||
{(draft) => <ResolvedDraftRoute draft={draft} />}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
function ResolvedDraftRoute(props: { draft: DraftTab }) {
|
||||
const global = useGlobal()
|
||||
const conn = createMemo(() => global.servers.list().find((item) => ServerConnection.key(item) === props.draft.server))
|
||||
|
||||
return (
|
||||
<Show when={`${props.draft.server}\0${props.draft.directory}`} keyed>
|
||||
<Show when={conn()} keyed>
|
||||
{(conn) => (
|
||||
<ServerProvider conn={conn}>
|
||||
<ModelsProvider directory={props.draft.directory}>
|
||||
<LocationProvider directory={props.draft.directory}>
|
||||
<SessionUIProvider directory={props.draft.directory} server={props.draft.server}>
|
||||
<DraftProviders>
|
||||
<NewSession draftId={props.draft.draftID} />
|
||||
</DraftProviders>
|
||||
</SessionUIProvider>
|
||||
</LocationProvider>
|
||||
</ModelsProvider>
|
||||
</ServerProvider>
|
||||
)}
|
||||
</Show>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
// The draft page only renders the prompt composer, so it drops TerminalProvider.
|
||||
// FileProvider and CommentsProvider stay because PromptInput uses file search and comment context.
|
||||
function DraftProviders(props: ParentProps) {
|
||||
return (
|
||||
<FileProvider>
|
||||
<PromptProvider>
|
||||
<CommentsProvider>{props.children}</CommentsProvider>
|
||||
</PromptProvider>
|
||||
</FileProvider>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
import { useDirectoryPicker } from "@/components/directory-picker"
|
||||
import { useServerActionsController } from "@/components/server/server-management-controller"
|
||||
import { useSettingsCommand } from "@/components/settings-dialog"
|
||||
import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
|
||||
import { type LocalProject } from "@/context/layout"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useNotification } from "@/context/notification"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { ServerConnection } from "@/context/servers"
|
||||
import { closeHomeProject, errorMessage, homeProjectDirectories } from "@/pages/layout/helpers"
|
||||
|
|
@ -63,7 +61,11 @@ export function createHomeProjectsController(home: HomeController) {
|
|||
serverManagement.defaults.set(conn ? ServerConnection.key(conn) : null),
|
||||
canRemove: (conn: ServerConnection.Any) => serverManagement.connection.canRemove(ServerConnection.key(conn)),
|
||||
remove: (conn: ServerConnection.Any) => serverManagement.connection.remove(ServerConnection.key(conn)),
|
||||
edit: (conn: ServerConnection.Http) => dialog.show(() => <DialogServerV2 mode="edit" server={conn} />),
|
||||
edit: (conn: ServerConnection.Http) => {
|
||||
void import("@/components/settings-v2/dialog-server-v2").then(({ DialogServerV2 }) => {
|
||||
void dialog.show(() => <DialogServerV2 mode="edit" server={conn} />)
|
||||
})
|
||||
},
|
||||
focus: home.selection.focusServer,
|
||||
},
|
||||
project: {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { Show, Suspense, type ParentProps } from "solid-js"
|
||||
import { lazy, Show, Suspense, type ParentProps } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { DebugBar } from "@/components/debug-bar"
|
||||
import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { ToastRegion } from "@/utils/toast"
|
||||
|
||||
const DebugBar = lazy(() => import("@/components/debug-bar").then((module) => ({ default: module.DebugBar })))
|
||||
|
||||
export default function Layout(props: ParentProps) {
|
||||
const platform = usePlatform()
|
||||
const [state, setState] = createStore({ debugTools: true })
|
||||
const [state, setState] = createStore({ debugTools: false })
|
||||
|
||||
const update: TitlebarUpdate = {
|
||||
get version() {
|
||||
|
|
@ -41,7 +42,9 @@ export default function Layout(props: ParentProps) {
|
|||
<Suspense>{props.children}</Suspense>
|
||||
</main>
|
||||
<Show when={import.meta.env.DEV && state.debugTools}>
|
||||
<DebugBar inline />
|
||||
<Suspense>
|
||||
<DebugBar inline />
|
||||
</Suspense>
|
||||
</Show>
|
||||
<ToastRegion />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,22 +3,56 @@ import { beforeEach, describe, expect, test } from "bun:test"
|
|||
const src = await Bun.file(new URL("../public/oc-theme-preload.js", import.meta.url)).text()
|
||||
|
||||
const run = () => Function(src)()
|
||||
const setSystemDark = (matches: boolean) =>
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
value: () => ({ matches }) as MediaQueryList,
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
document.head.innerHTML = ""
|
||||
document.documentElement.removeAttribute("data-theme")
|
||||
document.documentElement.removeAttribute("data-color-scheme")
|
||||
document.documentElement.style.removeProperty("background-color")
|
||||
localStorage.clear()
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
value: () =>
|
||||
({
|
||||
matches: false,
|
||||
}) as MediaQueryList,
|
||||
configurable: true,
|
||||
})
|
||||
setSystemDark(false)
|
||||
})
|
||||
|
||||
describe("theme preload", () => {
|
||||
test("uses default theme and system light mode when settings are absent", () => {
|
||||
run()
|
||||
|
||||
expect(document.documentElement.dataset.theme).toBe("oc-2")
|
||||
expect(document.documentElement.dataset.colorScheme).toBe("light")
|
||||
expect(document.documentElement.style.backgroundColor).toBe("#fafafa")
|
||||
})
|
||||
|
||||
test("restores explicit dark mode on a light system", () => {
|
||||
localStorage.setItem("opencode-color-scheme", "dark")
|
||||
run()
|
||||
|
||||
expect(document.documentElement.dataset.colorScheme).toBe("dark")
|
||||
expect(document.documentElement.style.backgroundColor).toBe("#080808")
|
||||
})
|
||||
|
||||
test("restores explicit light mode on a dark system", () => {
|
||||
setSystemDark(true)
|
||||
localStorage.setItem("opencode-color-scheme", "light")
|
||||
run()
|
||||
|
||||
expect(document.documentElement.dataset.colorScheme).toBe("light")
|
||||
expect(document.documentElement.style.backgroundColor).toBe("#fafafa")
|
||||
})
|
||||
|
||||
test("resolves persisted system mode before paint", () => {
|
||||
setSystemDark(true)
|
||||
localStorage.setItem("opencode-color-scheme", "system")
|
||||
run()
|
||||
|
||||
expect(document.documentElement.dataset.colorScheme).toBe("dark")
|
||||
expect(document.documentElement.style.backgroundColor).toBe("#080808")
|
||||
})
|
||||
|
||||
test("keeps cached css for non-default themes", () => {
|
||||
localStorage.setItem("opencode-theme-id", "nightowl")
|
||||
localStorage.setItem("opencode-theme-css-light", "--background-base:#fff;")
|
||||
|
|
@ -28,4 +62,15 @@ describe("theme preload", () => {
|
|||
expect(document.documentElement.dataset.theme).toBe("nightowl")
|
||||
expect(document.getElementById("oc-theme-preload")?.textContent).toContain("--background-base:#fff;")
|
||||
})
|
||||
|
||||
test("restores the cached variant for a persisted custom dark theme", () => {
|
||||
localStorage.setItem("opencode-theme-id", "nightowl")
|
||||
localStorage.setItem("opencode-color-scheme", "dark")
|
||||
localStorage.setItem("opencode-theme-css-dark", "--background-base:#010203;")
|
||||
run()
|
||||
|
||||
expect(document.documentElement.dataset.theme).toBe("nightowl")
|
||||
expect(document.documentElement.dataset.colorScheme).toBe("dark")
|
||||
expect(document.getElementById("oc-theme-preload")?.textContent).toContain("--background-base:#010203;")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { sentryVitePlugin } from "@sentry/vite-plugin"
|
||||
import { defineConfig } from "vite"
|
||||
import desktopPlugin from "./vite"
|
||||
import desktopPlugin from "./vite.js"
|
||||
|
||||
const sentry =
|
||||
process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@ import tailwindcss from "@tailwindcss/vite"
|
|||
import { fileURLToPath } from "url"
|
||||
|
||||
const theme = fileURLToPath(new URL("./public/oc-theme-preload.js", import.meta.url))
|
||||
const themeScript = readFileSync(theme, "utf8")
|
||||
const tailwind = tailwindcss()
|
||||
const tailwindGenerate = tailwind.find((plugin) => plugin.name === "@tailwindcss/vite:generate:serve")
|
||||
const tailwindHotUpdate = tailwindGenerate?.hotUpdate
|
||||
|
||||
// Tailwind 4.3.3 expects a server that Vite's bundled dev hook does not provide.
|
||||
if (tailwindGenerate && typeof tailwindHotUpdate === "function") {
|
||||
tailwindGenerate.hotUpdate = function (context) {
|
||||
if (!context.server) return
|
||||
return tailwindHotUpdate.call(this, context)
|
||||
}
|
||||
}
|
||||
|
||||
const channel = (() => {
|
||||
const raw = process.env.OPENCODE_CHANNEL
|
||||
|
|
@ -40,13 +52,18 @@ export default [
|
|||
},
|
||||
{
|
||||
name: "opencode-desktop:theme-preload",
|
||||
transformIndexHtml(html) {
|
||||
return html.replace(
|
||||
'<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>',
|
||||
`<script id="oc-theme-preload-script">${readFileSync(theme, "utf8")}</script>`,
|
||||
)
|
||||
transformIndexHtml: {
|
||||
order: "pre",
|
||||
handler: inlineThemePreload,
|
||||
},
|
||||
},
|
||||
tailwindcss(),
|
||||
...tailwind,
|
||||
solidPlugin(),
|
||||
]
|
||||
|
||||
export function inlineThemePreload(html) {
|
||||
return html.replace(
|
||||
/<script id="oc-theme-preload-script" src="(?:\.\/|\/)oc-theme-preload\.js"><\/script>/,
|
||||
`<script id="oc-theme-preload-script">${themeScript}</script>`,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ export type EnsureTiming = {
|
|||
const timings = new WeakMap<object, EnsureTiming>()
|
||||
|
||||
export const defaultEnsureTiming: EnsureTiming = {
|
||||
pollInterval: 1_000,
|
||||
attempts: 120,
|
||||
pollInterval: 100,
|
||||
attempts: 1_200,
|
||||
requestTimeout: 2_000,
|
||||
spawnDelay: 5_000,
|
||||
maxSpawnDelay: 30_000,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { withEnsureTiming } from "../../src/service-timing"
|
|||
|
||||
const timing = {
|
||||
pollInterval: 20,
|
||||
attempts: 120,
|
||||
requestTimeout: 100,
|
||||
spawnDelay: 200,
|
||||
maxSpawnDelay: 1_200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { sentryVitePlugin } from "@sentry/vite-plugin"
|
||||
import { defineConfig } from "electron-vite"
|
||||
import appPlugin from "@opencode-ai/app/vite"
|
||||
|
||||
const channel = (() => {
|
||||
const raw = process.env.OPENCODE_CHANNEL
|
||||
|
|
@ -11,9 +9,10 @@ const channel = (() => {
|
|||
|
||||
const nodePtyPkg = `@lydell/node-pty-${process.platform}-${process.arch}`
|
||||
|
||||
const appPlugin = (await import("@opencode-ai/app/vite")).default
|
||||
const sentry =
|
||||
process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT
|
||||
? sentryVitePlugin({
|
||||
? (await import("@sentry/vite-plugin")).sentryVitePlugin({
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
org: process.env.SENTRY_ORG,
|
||||
project: process.env.SENTRY_PROJECT,
|
||||
|
|
@ -34,11 +33,12 @@ export default defineConfig({
|
|||
"import.meta.env.OPENCODE_CHANNEL": JSON.stringify(channel),
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
rolldownOptions: {
|
||||
input: { index: "src/main/index.ts" },
|
||||
// Keep this identical to electron-vite's Node 20.11+ shim. Its regex insertion can
|
||||
// corrupt bundled TypeScript, while a Rollup banner places the shim safely.
|
||||
// corrupt bundled TypeScript, while an output banner places the shim safely.
|
||||
output: {
|
||||
format: "es",
|
||||
banner: `
|
||||
// -- CommonJS Shims --
|
||||
import __cjs_mod__ from 'node:module';
|
||||
|
|
@ -56,13 +56,14 @@ const require = __cjs_mod__.createRequire(import.meta.url);
|
|||
enforce: "pre",
|
||||
resolveId(s) {
|
||||
if (s === "@lydell/node-pty") return nodePtyPkg
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
preload: {
|
||||
build: {
|
||||
rollupOptions: {
|
||||
rolldownOptions: {
|
||||
input: { index: "src/preload/index.ts" },
|
||||
output: {
|
||||
format: "cjs",
|
||||
|
|
@ -72,6 +73,9 @@ const require = __cjs_mod__.createRequire(import.meta.url);
|
|||
},
|
||||
},
|
||||
renderer: {
|
||||
experimental: {
|
||||
bundledDev: true,
|
||||
},
|
||||
define: {
|
||||
"import.meta.env.OPENCODE_VERSION": JSON.stringify(process.env.OPENCODE_VERSION),
|
||||
"import.meta.env.VITE_OPENCODE_CHANNEL": JSON.stringify(channel),
|
||||
|
|
@ -81,7 +85,7 @@ const require = __cjs_mod__.createRequire(import.meta.url);
|
|||
root: "src/renderer",
|
||||
build: {
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
rolldownOptions: {
|
||||
input: {
|
||||
main: "src/renderer/index.html",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
"@opencode-ai/ui": "workspace:*",
|
||||
"@sentry/solid": "catalog:",
|
||||
"@sentry/vite-plugin": "catalog:",
|
||||
"@solid-primitives/i18n": "2.2.1",
|
||||
"@solid-primitives/storage": "catalog:",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@solidjs/router": "0.15.4",
|
||||
|
|
@ -50,11 +49,11 @@
|
|||
"@valibot/to-json-schema": "1.6.0",
|
||||
"electron": "42.3.3",
|
||||
"electron-builder": "26.15.2",
|
||||
"electron-vite": "^5",
|
||||
"electron-vite": "6.0.0-beta.1",
|
||||
"solid-js": "catalog:",
|
||||
"sury": "11.0.0-alpha.4",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "catalog:",
|
||||
"vite": "8.2.1",
|
||||
"zod-openapi": "5.4.6"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
|
|
|||
|
|
@ -11,14 +11,17 @@ async function main() {
|
|||
process.env.OPENCODE_DISABLE_CHANNEL_DB = "0"
|
||||
const options = selectOptions()
|
||||
if (options.server.type === "build") process.env.OPENCODE_DESKTOP_SERVER_CHANNEL = "local"
|
||||
process.env.OPENCODE_DESKTOP_ISOLATED_SERVER = "1"
|
||||
await prepareDesktop()
|
||||
await prepareServer(options.server)
|
||||
await startDesktop(options.electron)
|
||||
}
|
||||
|
||||
async function prepareDesktop() {
|
||||
await $`bun run install-electron`
|
||||
await $`bun ./scripts/copy-icons.ts ${process.env.OPENCODE_CHANNEL ?? "dev"}`
|
||||
await Promise.all([
|
||||
$`bun run install-electron`,
|
||||
$`bun ./scripts/copy-icons.ts ${process.env.OPENCODE_CHANNEL ?? "dev"}`,
|
||||
])
|
||||
}
|
||||
|
||||
function selectOptions(): DevOptions {
|
||||
|
|
@ -46,7 +49,6 @@ async function prepareServer(source: ServerSource) {
|
|||
}
|
||||
|
||||
async function startDesktop(args: string[]) {
|
||||
process.env.OPENCODE_DESKTOP_ISOLATED_SERVER = "1"
|
||||
await $`electron-vite dev ${args}`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ import { app } from "electron"
|
|||
import { Deferred, Effect, Fiber } from "effect"
|
||||
import type { ServerReadyData } from "../shared/ipc-contract"
|
||||
import { checkAppExists, resolveAppPath } from "./files/apps"
|
||||
import { registerIpcHandlers, registerUpdaterIpcHandlers, registerWslIpcHandlers } from "./ipc"
|
||||
import {
|
||||
registerIpcHandlers,
|
||||
registerUpdaterIpcHandlers,
|
||||
registerWslInitialization,
|
||||
registerWslIpcHandlers,
|
||||
} from "./ipc"
|
||||
import {
|
||||
acquireApplicationLock,
|
||||
configureApplication,
|
||||
|
|
@ -26,13 +31,17 @@ const main = Effect.gen(function* () {
|
|||
const logger = configureApplication()
|
||||
if (!acquireApplicationLock()) return
|
||||
preferApplicationEnvironment(logger)
|
||||
loadProxyEnvironment(logger)
|
||||
const lifecycle = createApplicationLifecycle(logger)
|
||||
const serverReady = Deferred.makeUnsafe<ServerReadyData, unknown>()
|
||||
const wslReady = Promise.withResolvers<void>()
|
||||
logger.log("starting v2 background service")
|
||||
const backgroundTask = yield* Effect.promise(() => startBackgroundCli(logger)).pipe(Effect.forkChild)
|
||||
|
||||
yield* Effect.promise(() => app.whenReady())
|
||||
yield* prepareDesktop(logger)
|
||||
|
||||
const updater = setupAutoUpdater(lifecycle.prepareToRestart)
|
||||
const updater = yield* Effect.promise(() => setupAutoUpdater(lifecycle.prepareToRestart))
|
||||
const menu = {
|
||||
trigger: (id: string) => {
|
||||
const win = getLastFocusedWindow()
|
||||
|
|
@ -68,27 +77,35 @@ const main = Effect.gen(function* () {
|
|||
},
|
||||
})
|
||||
registerUpdaterIpcHandlers(createUpdaterIpc(updater))
|
||||
registerWslInitialization(wslReady.promise)
|
||||
startAutoUpdater(updater)
|
||||
yield* Effect.promise(() => startNetworkLogging())
|
||||
|
||||
const loadingTask = yield* Effect.gen(function* () {
|
||||
loadProxyEnvironment(logger)
|
||||
logger.log("starting v2 background service")
|
||||
const background = yield* Effect.promise(() => startBackgroundCli(logger))
|
||||
const wsl = yield* Effect.promise(() => startWsl(background, logger))
|
||||
registerWslIpcHandlers(wsl.ipc)
|
||||
wsl.start()
|
||||
lifecycle.setWslShutdown(wsl.stop)
|
||||
const background = yield* Fiber.join(backgroundTask)
|
||||
yield* Deferred.succeed(serverReady, {
|
||||
url: background.url,
|
||||
username: background.username,
|
||||
password: background.password,
|
||||
})
|
||||
logger.log("loading task finished")
|
||||
|
||||
void startWsl(background, logger).then(
|
||||
(wsl) => {
|
||||
registerWslIpcHandlers(wsl.ipc)
|
||||
lifecycle.setWslShutdown(wsl.stop)
|
||||
wsl.start()
|
||||
wslReady.resolve()
|
||||
},
|
||||
(error) => {
|
||||
logger.error("failed to start WSL manager", { error })
|
||||
wslReady.reject(error)
|
||||
},
|
||||
)
|
||||
}).pipe(forwardInitializationFailure(serverReady), Effect.forkChild)
|
||||
|
||||
yield* Fiber.await(loadingTask)
|
||||
if (lifecycle.restoreWindows().length) createMenu(menu)
|
||||
yield* Fiber.await(loadingTask)
|
||||
})
|
||||
|
||||
Effect.runFork(main)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,14 @@ import { createFileCapabilities, openExternalURL, openLocalFileURL } from "./fil
|
|||
import { setForceFocus } from "./native/debug"
|
||||
import { runDesktopMenuAction } from "./native/menu-actions"
|
||||
import { createDesktopStorage } from "./storage"
|
||||
import { getPinchZoomEnabled, getWindowID, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
|
||||
import {
|
||||
getPinchZoomEnabled,
|
||||
getWindowID,
|
||||
setPinchZoomEnabled,
|
||||
setTitlebar,
|
||||
setWindowThemeReady,
|
||||
updateTitlebar,
|
||||
} from "./windows"
|
||||
import type { UpdaterIpc } from "./updater"
|
||||
import type { WslIpc } from "./wsl/ipc"
|
||||
|
||||
|
|
@ -112,6 +119,12 @@ export function registerIpcHandlers(deps: Deps) {
|
|||
return id
|
||||
})
|
||||
|
||||
handle(Ipc.window.themeReady, (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
if (!win) throw new Error("Window not found")
|
||||
setWindowThemeReady(win)
|
||||
})
|
||||
|
||||
handle(Ipc.window.getFocused, (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
return win?.isFocused() ?? false
|
||||
|
|
@ -167,6 +180,10 @@ export function registerUpdaterIpcHandlers(updater: UpdaterIpc) {
|
|||
handle(Ipc.updater.install, () => updater.install())
|
||||
}
|
||||
|
||||
export function registerWslInitialization(ready: Promise<void>) {
|
||||
handle(Ipc.wsl.awaitInitialization, () => ready)
|
||||
}
|
||||
|
||||
export function registerWslIpcHandlers(wsl: WslIpc) {
|
||||
handle(Ipc.wsl.subscribe, (event) => wsl.subscribe(event.sender))
|
||||
handle(Ipc.wsl.unsubscribe, (event) => wsl.unsubscribe(event.sender.id))
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ export function configureApplication() {
|
|||
process.env.OPENCODE_DISABLE_EMBEDDED_WEB_UI = "true"
|
||||
|
||||
const appID = app.isPackaged ? appIDs[CHANNEL] : "ai.opencode.desktop.dev"
|
||||
const onboardingRoot = createOnboardingTestRoot()
|
||||
const testRoot = createTestRoot()
|
||||
app.setName(app.isPackaged ? appNames[CHANNEL] : "OpenCode Dev")
|
||||
app.setAppUserModelId(appID)
|
||||
app.setPath("userData", onboardingRoot ? join(onboardingRoot, "desktop") : join(app.getPath("appData"), appID))
|
||||
if (onboardingRoot) app.setPath("sessionData", join(onboardingRoot, "session"))
|
||||
app.setPath("userData", testRoot ? join(testRoot, "desktop") : join(app.getPath("appData"), appID))
|
||||
if (testRoot) app.setPath("sessionData", join(testRoot, "session"))
|
||||
|
||||
initializeFirstLaunchOnboarding(app.getPath("userData"))
|
||||
const logger = initLogging()
|
||||
|
|
@ -48,14 +48,15 @@ export function configureApplication() {
|
|||
logger.log("app starting", {
|
||||
version: VERSION,
|
||||
packaged: app.isPackaged,
|
||||
onboardingTest: Boolean(onboardingRoot),
|
||||
onboardingTest: testOnboarding,
|
||||
})
|
||||
|
||||
loadProxyEnvironment(logger)
|
||||
app.commandLine.appendSwitch("proxy-bypass-list", "<-loopback>")
|
||||
const features = app.commandLine.getSwitchValue("enable-features")
|
||||
app.commandLine.appendSwitch("enable-features", features ? `${jsCallStackFeature},${features}` : jsCallStackFeature)
|
||||
if (!app.isPackaged) app.commandLine.appendSwitch("remote-debugging-port", "9222")
|
||||
if (!app.isPackaged)
|
||||
app.commandLine.appendSwitch("remote-debugging-port", process.env.OPENCODE_DESKTOP_REMOTE_DEBUGGING_PORT ?? "9222")
|
||||
return logger
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,8 @@ export function prepareDesktop(logger: DesktopLogger) {
|
|||
),
|
||||
Effect.catch((error) => Effect.sync(() => logger.warn("failed to clean scoped store files", error))),
|
||||
)
|
||||
app.setAsDefaultProtocolClient("opencode")
|
||||
if (app.isPackaged || process.env.OPENCODE_DESKTOP_DISABLE_PROTOCOL_REGISTRATION !== "1")
|
||||
app.setAsDefaultProtocolClient("opencode")
|
||||
registerRendererProtocol()
|
||||
setDockIcon()
|
||||
})
|
||||
|
|
@ -105,14 +107,18 @@ export function loadProxyEnvironment(logger: DesktopLogger) {
|
|||
}
|
||||
}
|
||||
|
||||
function createOnboardingTestRoot() {
|
||||
if (!testOnboarding) return undefined
|
||||
const root = join(tmpdir(), `opencode-onboarding-${randomUUID()}`)
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
function createTestRoot() {
|
||||
const root = testOnboarding
|
||||
? join(tmpdir(), `opencode-onboarding-${randomUUID()}`)
|
||||
: app.isPackaged
|
||||
? undefined
|
||||
: process.env.OPENCODE_DESKTOP_TEST_ROOT
|
||||
if (!root) return undefined
|
||||
if (testOnboarding) rmSync(root, { recursive: true, force: true })
|
||||
;["data", "config", "cache", "state", "desktop", "session"].forEach((dir) =>
|
||||
mkdirSync(join(root, dir), { recursive: true }),
|
||||
)
|
||||
process.env.OPENCODE_DB = ":memory:"
|
||||
if (testOnboarding) process.env.OPENCODE_DB = ":memory:"
|
||||
process.env.XDG_DATA_HOME = join(root, "data")
|
||||
process.env.XDG_CONFIG_HOME = join(root, "config")
|
||||
process.env.XDG_CACHE_HOME = join(root, "cache")
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { MainLogger } from "electron-log"
|
|||
import log from "electron-log/main.js"
|
||||
import { app, crashReporter, netLog, shell } from "electron"
|
||||
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs"
|
||||
import { ZipWriter, BlobWriter, BlobReader } from "@zip.js/zip.js"
|
||||
import { dirname, join } from "node:path"
|
||||
import { homedir } from "node:os"
|
||||
import { VERSION } from "../constants"
|
||||
|
|
@ -185,6 +184,7 @@ function collect(dir: string, prefix: string): Entry[] {
|
|||
}
|
||||
|
||||
async function writeZip(output: string, entries: Entry[]) {
|
||||
const { BlobReader, BlobWriter, ZipWriter } = await import("@zip.js/zip.js")
|
||||
const writer = new ZipWriter(new BlobWriter("application/zip"))
|
||||
for (const entry of entries) {
|
||||
const data = entry.data ?? readFileSync(entry.path!)
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl"
|
|||
export const FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY = "firstLaunchOnboardingComplete"
|
||||
export const WSL_SERVERS_KEY = "wslServers"
|
||||
export const PINCH_ZOOM_ENABLED_KEY = "pinchZoomEnabled"
|
||||
export const BACKGROUND_COLOR_KEY = "backgroundColor"
|
||||
export const WINDOW_IDS_KEY = "windowIds"
|
||||
|
|
|
|||
|
|
@ -6,21 +6,22 @@ import { getLogger } from "../native/logging"
|
|||
import { nativeT } from "../native/translations"
|
||||
import { getStore } from "../storage/store"
|
||||
import { createUpdaterController, type UpdaterController, type UpdaterReadyRecord } from "./controller"
|
||||
import { createUpdaterPlatform } from "./platform"
|
||||
|
||||
const key = "ready"
|
||||
|
||||
export function setupAutoUpdater(prepareToRestart: () => Promise<void>) {
|
||||
export async function setupAutoUpdater(prepareToRestart: () => Promise<void>) {
|
||||
const logger = getLogger()
|
||||
const store = getStore("opencode.updater")
|
||||
const platform = UPDATER_ENABLED ? (await import("./platform")).createUpdaterPlatform(logger) : undefined
|
||||
return createUpdaterController({
|
||||
currentVersion: app.getVersion(),
|
||||
platform: UPDATER_ENABLED ? createUpdaterPlatform(logger) : undefined,
|
||||
platform,
|
||||
lifecycle: { prepareToRestart },
|
||||
persistence: {
|
||||
get() {
|
||||
const value = store.get(key)
|
||||
if (!value || typeof value !== "object" || !("version" in value) || typeof value.version !== "string") return
|
||||
if (!value || typeof value !== "object" || !("version" in value) || typeof value.version !== "string")
|
||||
return undefined
|
||||
return { version: value.version } satisfies UpdaterReadyRecord
|
||||
},
|
||||
set: (value) => store.set(key, value),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { app, BrowserWindow, nativeImage, nativeTheme } from "electron"
|
|||
import { join } from "node:path"
|
||||
import { Ipc, sendIpcEvent, type TitlebarTheme } from "../../shared/ipc-contract"
|
||||
import { developmentResourcesRoot, preloadPath } from "../paths"
|
||||
import { PINCH_ZOOM_ENABLED_KEY } from "../storage/keys"
|
||||
import { BACKGROUND_COLOR_KEY, PINCH_ZOOM_ENABLED_KEY } from "../storage/keys"
|
||||
import { getStore } from "../storage/store"
|
||||
|
||||
const oc2Theme = oc2ThemeJson as DesktopTheme
|
||||
|
|
@ -22,10 +22,12 @@ let backgroundColor: string | undefined
|
|||
|
||||
export function windowAppearance() {
|
||||
const mode = tone()
|
||||
const storedBackground = getStore().get(BACKGROUND_COLOR_KEY)
|
||||
return {
|
||||
title: "OpenCode",
|
||||
icon: iconPath(),
|
||||
backgroundColor: backgroundColor ?? oc2Background[mode],
|
||||
backgroundColor:
|
||||
backgroundColor ?? (typeof storedBackground === "string" ? storedBackground : undefined) ?? oc2Background[mode],
|
||||
...(process.platform === "darwin"
|
||||
? {
|
||||
titleBarStyle: "hidden" as const,
|
||||
|
|
@ -56,6 +58,7 @@ export function setDockIcon() {
|
|||
|
||||
export function setBackgroundColor(color: string) {
|
||||
backgroundColor = color
|
||||
getStore().set(BACKGROUND_COLOR_KEY, color)
|
||||
BrowserWindow.getAllWindows().forEach((win) => {
|
||||
win.setBackgroundColor(color)
|
||||
if (process.platform === "darwin") win.invalidateShadow()
|
||||
|
|
@ -63,7 +66,8 @@ export function setBackgroundColor(color: string) {
|
|||
}
|
||||
|
||||
export function getBackgroundColor() {
|
||||
return backgroundColor
|
||||
const stored = getStore().get(BACKGROUND_COLOR_KEY)
|
||||
return backgroundColor ?? (typeof stored === "string" ? stored : undefined)
|
||||
}
|
||||
|
||||
export function setTitlebar(win: BrowserWindow, theme: Partial<TitlebarTheme> = {}) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto"
|
|||
import { rmSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import { app, BrowserWindow } from "electron"
|
||||
import { writeLog } from "../native/logging"
|
||||
import { removeStoreFile, getStore } from "../storage/store"
|
||||
import { WINDOW_IDS_KEY } from "../storage/keys"
|
||||
import {
|
||||
|
|
@ -23,6 +24,7 @@ import { wireWindowRecovery } from "./recovery"
|
|||
import { allowRendererPermissions, wireNavigationPolicy, wireRendererHeaders } from "./security"
|
||||
|
||||
const windowIDs = new WeakMap<BrowserWindow, string>()
|
||||
const themeReady = new WeakMap<BrowserWindow, () => void>()
|
||||
const registry = createWindowRegistry<BrowserWindow>({
|
||||
read: () => getStore().get(WINDOW_IDS_KEY),
|
||||
write: (ids) => getStore().set(WINDOW_IDS_KEY, ids),
|
||||
|
|
@ -68,6 +70,10 @@ export function getLastFocusedWindow() {
|
|||
return win
|
||||
}
|
||||
|
||||
export function setWindowThemeReady(win: BrowserWindow) {
|
||||
themeReady.get(win)?.()
|
||||
}
|
||||
|
||||
export function restoreMainWindows() {
|
||||
const ids = registry.persisted()
|
||||
return (ids.length ? ids : [randomUUID()]).map((id) => createMainWindow(id))
|
||||
|
|
@ -92,16 +98,28 @@ export function createMainWindow(id: string = randomUUID()) {
|
|||
state.manage(win)
|
||||
registerWindow(win, id)
|
||||
wireFullscreen(win)
|
||||
loadWindow(win, "index.html")
|
||||
wireZoom(win)
|
||||
let contentReady = false
|
||||
let appliedTheme = false
|
||||
let revealed = false
|
||||
const reveal = () => {
|
||||
if (revealed || win.isDestroyed()) return
|
||||
if (!contentReady || !appliedTheme || revealed || win.isDestroyed()) return
|
||||
revealed = true
|
||||
win.show()
|
||||
writeLog("window", "main window visible", { window: id })
|
||||
}
|
||||
win.once("ready-to-show", reveal)
|
||||
if (process.platform === "linux") win.webContents.once("did-finish-load", reveal)
|
||||
const ready = () => {
|
||||
contentReady = true
|
||||
reveal()
|
||||
}
|
||||
themeReady.set(win, () => {
|
||||
appliedTheme = true
|
||||
reveal()
|
||||
})
|
||||
win.once("ready-to-show", ready)
|
||||
if (process.platform === "linux") win.webContents.once("did-finish-load", ready)
|
||||
win.once("closed", () => themeReady.delete(win))
|
||||
loadWindow(win, "index.html")
|
||||
return win
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,29 +34,36 @@ const updaterHandler = (state: UpdaterState) => {
|
|||
updaterState = state
|
||||
updaterCallbacks.forEach((callback) => callback(state))
|
||||
}
|
||||
type WslInvoke = Exclude<
|
||||
(typeof Ipc.wsl)[keyof typeof Ipc.wsl],
|
||||
typeof Ipc.wsl.awaitInitialization | typeof Ipc.wsl.event
|
||||
>
|
||||
function invokeWsl<Channel extends WslInvoke>(channel: Channel, ...args: IpcInvokeArgs<Channel>) {
|
||||
return invoke(Ipc.wsl.awaitInitialization).then(() => invoke(channel, ...args))
|
||||
}
|
||||
|
||||
const api: ElectronAPI = {
|
||||
awaitInitialization: () => invoke(Ipc.app.awaitInitialization),
|
||||
wslServers: {
|
||||
getState: () => invoke(Ipc.wsl.getState),
|
||||
getState: () => invokeWsl(Ipc.wsl.getState),
|
||||
subscribe: (cb) => {
|
||||
const dispose = listen(Ipc.wsl.event, cb)
|
||||
void invoke(Ipc.wsl.subscribe)
|
||||
const subscribed = invokeWsl(Ipc.wsl.subscribe)
|
||||
return () => {
|
||||
dispose()
|
||||
void invoke(Ipc.wsl.unsubscribe)
|
||||
void subscribed.then(() => invokeWsl(Ipc.wsl.unsubscribe))
|
||||
}
|
||||
},
|
||||
probeRuntime: () => invoke(Ipc.wsl.probeRuntime),
|
||||
refreshDistros: () => invoke(Ipc.wsl.refreshDistros),
|
||||
installWsl: () => invoke(Ipc.wsl.installWsl),
|
||||
installDistro: (name) => invoke(Ipc.wsl.installDistro, name),
|
||||
probeAddable: (distros) => invoke(Ipc.wsl.probeAddable, distros),
|
||||
installOpencode: (name) => invoke(Ipc.wsl.installOpencode, name),
|
||||
openTerminal: (name) => invoke(Ipc.wsl.openTerminal, name),
|
||||
addServer: (distro) => invoke(Ipc.wsl.addServer, distro),
|
||||
removeServer: (id) => invoke(Ipc.wsl.removeServer, id),
|
||||
startServer: (id) => invoke(Ipc.wsl.startServer, id),
|
||||
probeRuntime: () => invokeWsl(Ipc.wsl.probeRuntime),
|
||||
refreshDistros: () => invokeWsl(Ipc.wsl.refreshDistros),
|
||||
installWsl: () => invokeWsl(Ipc.wsl.installWsl),
|
||||
installDistro: (name) => invokeWsl(Ipc.wsl.installDistro, name),
|
||||
probeAddable: (distros) => invokeWsl(Ipc.wsl.probeAddable, distros),
|
||||
installOpencode: (name) => invokeWsl(Ipc.wsl.installOpencode, name),
|
||||
openTerminal: (name) => invokeWsl(Ipc.wsl.openTerminal, name),
|
||||
addServer: (distro) => invokeWsl(Ipc.wsl.addServer, distro),
|
||||
removeServer: (id) => invokeWsl(Ipc.wsl.removeServer, id),
|
||||
startServer: (id) => invokeWsl(Ipc.wsl.startServer, id),
|
||||
},
|
||||
updater: {
|
||||
subscribe: async (cb) => {
|
||||
|
|
@ -100,6 +107,7 @@ const api: ElectronAPI = {
|
|||
draftBlobGet: (id) => invoke(Ipc.drafts.getBlob, id),
|
||||
|
||||
getWindowID: () => invoke(Ipc.window.getId),
|
||||
themeReady: () => invoke(Ipc.window.themeReady),
|
||||
onMenuCommand: (cb) => listen(Ipc.menu.command, cb),
|
||||
onDeepLink: (cb) => listen(Ipc.app.deepLink, cb),
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export type ElectronAPI = {
|
|||
draftBlobGet: IpcInvokeMethod<typeof Ipc.drafts.getBlob>
|
||||
|
||||
getWindowID: IpcInvokeMethod<typeof Ipc.window.getId>
|
||||
themeReady: IpcInvokeMethod<typeof Ipc.window.themeReady>
|
||||
onMenuCommand: IpcEventSubscription<typeof Ipc.menu.command>
|
||||
onDeepLink: IpcEventSubscription<typeof Ipc.app.deepLink>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
AppBaseProviders,
|
||||
AppInterface,
|
||||
PlatformProvider,
|
||||
preloadRoute,
|
||||
ServerConnection,
|
||||
useCommand,
|
||||
useLanguage,
|
||||
|
|
@ -13,9 +14,8 @@ import {
|
|||
} from "@opencode-ai/app"
|
||||
import { useTheme } from "@opencode-ai/ui/theme/context"
|
||||
import type { BaseRouterProps } from "@solidjs/router"
|
||||
import { createEffect, createMemo, createResource, Show } from "solid-js"
|
||||
import { createEffect, createMemo, createResource, lazy, Show, Suspense } from "solid-js"
|
||||
import type { ElectronAPI } from "../preload/types"
|
||||
import { MigrationStatus } from "./migration-status"
|
||||
import { DesktopFirstLaunchOnboarding } from "./onboarding"
|
||||
import { createDesktopPlatform, type DesktopWindowState } from "./platform"
|
||||
import { bindDesktopMenu } from "./platform/menu"
|
||||
|
|
@ -26,6 +26,8 @@ import { getLastActiveUrl } from "./window/route-storage"
|
|||
import { DesktopMemoryRouter } from "./window/router"
|
||||
import { availableStartupServer, readyWslConnections } from "./wsl/connections"
|
||||
|
||||
const MigrationStatus = lazy(() => import("./migration-status").then((module) => ({ default: module.MigrationStatus })))
|
||||
|
||||
export function DesktopApp(props: { api: ElectronAPI; updater: UpdaterPlatform; version: string }) {
|
||||
const [windowState] = createResource(() => props.api.getWindowID().then((id) => ({ id, version: props.version })))
|
||||
return (
|
||||
|
|
@ -37,9 +39,11 @@ export function DesktopApp(props: { api: ElectronAPI; updater: UpdaterPlatform;
|
|||
|
||||
function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; windowState: DesktopWindowState }) {
|
||||
const platform = createDesktopPlatform(props.api, props.windowState, props.updater)
|
||||
const initialUrl = getLastActiveUrl(props.windowState.id)
|
||||
const [sidecar] = createResource(() => props.api.awaitInitialization())
|
||||
const [defaultServer] = createResource(() => platform.getDefaultServer?.())
|
||||
const [locale] = createResource(() => preloadStoredLocale(platform))
|
||||
const [route] = createResource(() => preloadRoute(initialUrl))
|
||||
const router = (routerProps: BaseRouterProps) => (
|
||||
<DesktopMemoryRouter {...routerProps} windowID={props.windowState.id} />
|
||||
)
|
||||
|
|
@ -47,9 +51,7 @@ function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; wind
|
|||
function ReadyApp() {
|
||||
const wslServers = useWslServers()
|
||||
const language = useLanguage()
|
||||
const ready = createMemo(
|
||||
() => !defaultServer.loading && !sidecar.loading && !locale.loading && !wslServers.isLoading,
|
||||
)
|
||||
const ready = createMemo(() => !defaultServer.loading && !sidecar.loading && !locale.loading && !route.loading)
|
||||
const servers = createMemo(() => {
|
||||
const data = initializationData(sidecar)
|
||||
const list: ServerConnection.Any[] = []
|
||||
|
|
@ -79,13 +81,15 @@ function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; wind
|
|||
<AppInterface defaultServer={key} servers={servers()} router={router}>
|
||||
<DesktopFirstLaunchOnboarding
|
||||
api={props.api}
|
||||
initialUrl={getLastActiveUrl(props.windowState.id)}
|
||||
initialUrl={initialUrl}
|
||||
serverKey={key}
|
||||
/>
|
||||
<DesktopEffects api={props.api} />
|
||||
<Show when={initializationData(sidecar)} keyed>
|
||||
{(server) => <MigrationStatus server={server} />}
|
||||
</Show>
|
||||
<Suspense fallback={null}>
|
||||
<Show when={initializationData(sidecar)} keyed>
|
||||
{(server) => <MigrationStatus server={server} />}
|
||||
</Show>
|
||||
</Suspense>
|
||||
</AppInterface>
|
||||
)}
|
||||
</Show>
|
||||
|
|
@ -98,6 +102,7 @@ function DesktopWindow(props: { api: ElectronAPI; updater: UpdaterPlatform; wind
|
|||
<AppBaseProviders
|
||||
locale={locale.latest}
|
||||
onNativeTranslations={(bundle) => void props.api.setNativeTranslations(bundle).catch(() => undefined)}
|
||||
onThemeApplied={() => void props.api.themeReady()}
|
||||
>
|
||||
<Show when={true}>{(_) => <ReadyApp />}</Show>
|
||||
</AppBaseProviders>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "ዝማኔዎችን ይመልከቱ...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"ሥርወ አካል አልተገኘም። ወደ የእርስዎ index.html ማከልን ረስተዋል? ወይም የመታወቂያ ባህሪው የተሳሳተ ፊደል ተጽፎ ሊሆን ይችላል?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "التحقق من وجود تحديثات...",
|
||||
"desktop.menu.reloadWebview": "إعادة تحميل عرض الويب",
|
||||
"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.error.dev.rootNotFound":
|
||||
"لم يتم العثور على العنصر الجذري. هل نسيت إضافته إلى index.html؟ أو ربما تمت كتابة سمة id بشكل خاطئ؟",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Yeniləmələri yoxla...",
|
||||
"desktop.menu.reloadWebview": "Webview-u yenidən yüklə",
|
||||
"desktop.menu.restart": "Yenidən başlat",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Qovluq seçin",
|
||||
"desktop.dialog.chooseFile": "Fayl seçin",
|
||||
"desktop.dialog.saveFile": "Faylı saxla",
|
||||
|
||||
"desktop.updater.checkFailed.title": "Yeniləmə yoxlaması uğursuz oldu",
|
||||
"desktop.updater.checkFailed.message": "Yeniləmələr yoxlana bilmədi",
|
||||
"desktop.updater.none.title": "Yeniləmə mövcud deyil",
|
||||
"desktop.updater.none.message": "Artıq OpenCode-un ən son versiyasından istifadə edirsiniz",
|
||||
"desktop.updater.downloadFailed.title": "Yeniləmə uğursuz oldu",
|
||||
"desktop.updater.downloadFailed.message": "Yeniləmə yüklənə bilmədi",
|
||||
"desktop.updater.downloaded.title": "Yeniləmə yükləndi",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode-un {{version}} versiyası yüklənib. Onu quraşdırıb tətbiqi yenidən başlatmaq istəyirsiniz?",
|
||||
"desktop.updater.installFailed.title": "Yeniləmə uğursuz oldu",
|
||||
"desktop.updater.installFailed.message": "Yeniləmə quraşdırıla bilmədi",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Kök element tapılmadı. index.html-ə əlavə etməyi unutmusunuz? Yoxsa id atributu səhv yazılıb?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Проверете за актуализации...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Основният елемент не е намерен. Забравихте ли да го добавите към вашия index.html? Или може би атрибутът id е изписан неправилно?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict: Record<string, string> = {
|
||||
"desktop.menu.checkForUpdates": "আপডেটের জন্য চেক করুন...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"মূল উপাদান পাওয়া যায়নি. আপনি কি আপনার index.html এ যোগ করতে ভুলে গেছেন? অথবা হয়তো আইডি অ্যাট্রিবিউট ভুল বানান হয়েছে?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Verificar atualizações...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Elemento raiz não encontrado. Você esqueceu de adicioná-lo ao seu index.html? Ou talvez o atributo id foi escrito incorretamente?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Provjeri ažuriranja...",
|
||||
"desktop.menu.reloadWebview": "Ponovo učitaj Webview",
|
||||
"desktop.menu.restart": "Ponovo pokreni",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Odaberi fasciklu",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Korijenski element nije pronađen. Da li si zaboravio da ga dodaš u index.html? Ili je možda id atribut pogrešno napisan?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Comproveu si hi ha actualitzacions...",
|
||||
"desktop.menu.reloadWebview": "Torna a carregar Webview",
|
||||
"desktop.menu.restart": "Reinicia",
|
||||
"desktop.dialog.chooseFolder": "Trieu una carpeta",
|
||||
"desktop.dialog.chooseFile": "Trieu un fitxer",
|
||||
"desktop.dialog.saveFile": "Desa el fitxer",
|
||||
"desktop.updater.checkFailed.title": "La comprovació d'actualització ha fallat",
|
||||
"desktop.updater.checkFailed.message": "No s'ha pogut comprovar si hi ha actualitzacions",
|
||||
"desktop.updater.none.title": "No hi ha cap actualització disponible",
|
||||
"desktop.updater.none.message": "Ja utilitzeu la versió més recent d'OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "L'actualització ha fallat",
|
||||
"desktop.updater.downloadFailed.message": "No s'ha pogut descarregar l'actualització",
|
||||
"desktop.updater.downloaded.title": "Actualització baixada",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"S'ha baixat la versió {{version}} d'OpenCode. Voleu instal·lar-la i reiniciar l'aplicació?",
|
||||
"desktop.updater.installFailed.title": "L'actualització ha fallat",
|
||||
"desktop.updater.installFailed.message": "No s'ha pogut instal·lar l'actualització",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"No s'ha trobat l'element arrel. T'has oblidat d'afegir-lo al teu index.html? O potser l'atribut id s'ha escrit malament?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Zkontrolovat aktualizace...",
|
||||
"desktop.menu.reloadWebview": "Znovu načíst Webview",
|
||||
"desktop.menu.restart": "Restartovat",
|
||||
"desktop.dialog.chooseFolder": "Vyberte složku",
|
||||
"desktop.dialog.chooseFile": "Vyberte soubor",
|
||||
"desktop.dialog.saveFile": "Uložit soubor",
|
||||
"desktop.updater.checkFailed.title": "Kontrola aktualizace se nezdařila",
|
||||
"desktop.updater.checkFailed.message": "Kontrola aktualizací se nezdařila",
|
||||
"desktop.updater.none.title": "Není k dispozici žádná aktualizace",
|
||||
"desktop.updater.none.message": "Již používáte nejnovější verzi OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Aktualizace se nezdařila",
|
||||
"desktop.updater.downloadFailed.message": "Stažení aktualizace se nezdařilo",
|
||||
"desktop.updater.downloaded.title": "Aktualizace stažena",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Byla stažena verze {{version}} aplikace OpenCode. Chcete ji nainstalovat a aplikaci znovu spustit?",
|
||||
"desktop.updater.installFailed.title": "Aktualizace se nezdařila",
|
||||
"desktop.updater.installFailed.message": "Aktualizaci se nepodařilo nainstalovat",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Kořenový prvek nenalezen. Zapomněli jste to přidat do index.html? Nebo je možná chyba v atributu id?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Tjek for opdateringer...",
|
||||
"desktop.menu.reloadWebview": "Genindlæs webvisning",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Rodelement ikke fundet. Har du glemt at tilføje det til din index.html? Eller måske er id-attributten stavet forkert?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Nach Updates suchen…",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das ID-Attribut falsch geschrieben?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "އަޕްޑޭޓްސް އަށް ޗެކް ކޮށްލައްވާ...",
|
||||
"desktop.menu.reloadWebview": "ވެބްވިއު ރީލޯޑް ކުރާށެވެ",
|
||||
"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.error.dev.rootNotFound":
|
||||
"ރޫޓް އެލިމެންޓް ނުފެނެއެވެ. ތިބާގެ index.html އަށް އެޑް ކުރަން ހަނދާން ނެތުނީ ހެއްޔެވެ؟ ނުވަތަ id އެޓްރިބިއުޓް ގޯސްކޮށް އިމްތިހާނު ވެދާނެ ހެއްޔެވެ؟",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict: Record<string, string> = {
|
||||
"desktop.menu.checkForUpdates": "དུས་མཐུན་ཚུ་གི་དོན་ལུ་ཞིབ་དཔྱད་འབད།",
|
||||
"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.error.dev.rootNotFound":
|
||||
"རྩ་བའི་ཆ་ཤས་འཚོལ་མ་ཐོབ། ཁྱོད་ཀྱི་ index.html ལུ་ཁ་སྐོང་འབད་ནི་བརྗེད་སོང་ག? ཡང་ན་ id ཁྱད་ཆོས་འདི་ཡིག་སྡེབ་འཛོལ་བ་འོང་ག?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Έλεγχος για ενημερώσεις...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Το στοιχείο ρίζας δεν βρέθηκε. Ξεχάσατε να το προσθέσετε στο index.html; Ή μήπως το χαρακτηριστικό id γράφτηκε λάθος;",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Check for Updates...",
|
||||
"desktop.menu.reloadWebview": "Reload Webview",
|
||||
"desktop.menu.restart": "Restart",
|
||||
|
||||
"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.error.dev.rootNotFound":
|
||||
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Buscar actualizaciones...",
|
||||
"desktop.menu.reloadWebview": "Recargar vista web",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Elemento raíz no encontrado. ¿Olvidaste añadirlo a tu index.html? ¿O tal vez el atributo id está mal escrito?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Kontrolli värskendusi...",
|
||||
"desktop.menu.reloadWebview": "Laadi veebivaade uuesti",
|
||||
"desktop.menu.restart": "Taaskäivita",
|
||||
"desktop.dialog.chooseFolder": "Valige kaust",
|
||||
"desktop.dialog.chooseFile": "Valige fail",
|
||||
"desktop.dialog.saveFile": "Salvesta fail",
|
||||
"desktop.updater.checkFailed.title": "Värskenduskontroll ebaõnnestus",
|
||||
"desktop.updater.checkFailed.message": "Värskenduste kontrollimine ebaõnnestus",
|
||||
"desktop.updater.none.title": "Värskendus pole saadaval",
|
||||
"desktop.updater.none.message": "Kasutate juba rakenduse OpenCode uusimat versiooni",
|
||||
"desktop.updater.downloadFailed.title": "Värskendus ebaõnnestus",
|
||||
"desktop.updater.downloadFailed.message": "Värskenduse allalaadimine ebaõnnestus",
|
||||
"desktop.updater.downloaded.title": "Värskendus alla laaditud",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode'i versioon {{version}} on alla laaditud. Kas soovite selle installida ja rakenduse taaskäivitada?",
|
||||
"desktop.updater.installFailed.title": "Värskendus ebaõnnestus",
|
||||
"desktop.updater.installFailed.message": "Värskenduse installimine ebaõnnestus",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Juurelementi ei leitud. Kas unustasite selle lisada oma loendisse index.html? Või äkki on id-atribuut valesti kirjutatud?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "بررسی به روز رسانی...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"عنصر ریشه یافت نشد. آیا فراموش کرده اید که آن را به index.html خود اضافه کنید؟ یا شاید ویژگی id اشتباه املایی داشته باشد؟",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Tarkista päivitykset...",
|
||||
"desktop.menu.reloadWebview": "Lataa verkkonäkymä uudelleen",
|
||||
"desktop.menu.restart": "Käynnistä uudelleen",
|
||||
"desktop.dialog.chooseFolder": "Valitse kansio",
|
||||
"desktop.dialog.chooseFile": "Valitse tiedosto",
|
||||
"desktop.dialog.saveFile": "Tallenna tiedosto",
|
||||
"desktop.updater.checkFailed.title": "Päivitystarkistus epäonnistui",
|
||||
"desktop.updater.checkFailed.message": "Päivitysten tarkistaminen epäonnistui",
|
||||
"desktop.updater.none.title": "Päivitystä ei ole saatavilla",
|
||||
"desktop.updater.none.message": "Käytät jo OpenCoden uusinta versiota",
|
||||
"desktop.updater.downloadFailed.title": "Päivitys epäonnistui",
|
||||
"desktop.updater.downloadFailed.message": "Päivityksen lataaminen epäonnistui",
|
||||
"desktop.updater.downloaded.title": "Päivitys ladattu",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCoden versio {{version}} on ladattu. Haluatko asentaa sen ja käynnistää OpenCoden uudelleen?",
|
||||
"desktop.updater.installFailed.title": "Päivitys epäonnistui",
|
||||
"desktop.updater.installFailed.message": "Päivityksen asentaminen epäonnistui",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Juurielementtiä ei löydy. Unohditko lisätä sen index.html-tiedostoosi? Tai ehkä id-attribuutti on kirjoitettu väärin?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Kanna fyri dagføringum...",
|
||||
"desktop.menu.reloadWebview": "Endurlesa vevvísing",
|
||||
"desktop.menu.restart": "Endurbyrja",
|
||||
"desktop.dialog.chooseFolder": "Vel eina mappu",
|
||||
"desktop.dialog.chooseFile": "Vel eina fílu",
|
||||
"desktop.dialog.saveFile": "Goym fílu",
|
||||
"desktop.updater.checkFailed.title": "Dagføringarkanningin miseydnaðist",
|
||||
"desktop.updater.checkFailed.message": "Tað eydnaðist ikki at kanna fyri dagføringum",
|
||||
"desktop.updater.none.title": "Eingin dagføring er tøk",
|
||||
"desktop.updater.none.message": "Tú brúkar longu nýggjastu útgávuna av OpenCode.",
|
||||
"desktop.updater.downloadFailed.title": "Dagføring miseydnaðist",
|
||||
"desktop.updater.downloadFailed.message": "Tað eydnaðist ikki at heinta dagføring",
|
||||
"desktop.updater.downloaded.title": "Dagføring heintað",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Útgáva {{version}} av OpenCode er heintað, vilt tú seta hana upp og seta hana í gongd aftur?",
|
||||
"desktop.updater.installFailed.title": "Dagføring miseydnaðist",
|
||||
"desktop.updater.installFailed.message": "Tað eydnaðist ikki at seta upp dagføring",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Rótarevni ikki funnið. Gloymdi tú at leggja tað til títt index.html? Ella kanska fekk id eginleikin skeivt stavað?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Rechercher des mises à jour...",
|
||||
"desktop.menu.reloadWebview": "Recharger la vue Web",
|
||||
"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 relancer l'application ?",
|
||||
"desktop.updater.installFailed.title": "Échec de la mise à jour",
|
||||
"desktop.updater.installFailed.message": "Impossible d'installer la mise à jour",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Élément racine introuvable. Avez-vous oublié de l'ajouter à votre index.html ? Ou peut-être que l'attribut id est mal orthographié ?",
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "בדוק אם יש עדכונים...",
|
||||
"desktop.menu.reloadWebview": "טען מחדש את תצוגת האינטרנט",
|
||||
"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.error.dev.rootNotFound":
|
||||
"רכיב השורש לא נמצא. האם שכחת להוסיף אותו ל-index.html, או שיש טעות בשם מאפיין ה-id?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "अद्यतन के लिए जाँच...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"मूल तत्व नहीं मिला. क्या आप इसे अपने index.html में जोड़ना भूल गए? या हो सकता है कि आईडी विशेषता गलत वर्तनी हो गई हो?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Provjeri ažuriranja...",
|
||||
"desktop.menu.reloadWebview": "Ponovno učitaj Webview",
|
||||
"desktop.menu.restart": "Ponovno pokreni",
|
||||
"desktop.dialog.chooseFolder": "Odaberite mapu",
|
||||
"desktop.dialog.chooseFile": "Odaberite datoteku",
|
||||
"desktop.dialog.saveFile": "Spremi datoteku",
|
||||
"desktop.updater.checkFailed.title": "Provjera ažuriranja nije uspjela",
|
||||
"desktop.updater.checkFailed.message": "Provjera ažuriranja nije uspjela",
|
||||
"desktop.updater.none.title": "Nema dostupnih ažuriranja",
|
||||
"desktop.updater.none.message": "Već koristite najnoviju verziju OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Ažuriranje nije uspjelo",
|
||||
"desktop.updater.downloadFailed.message": "Preuzimanje ažuriranja nije uspjelo",
|
||||
"desktop.updater.downloaded.title": "Ažuriranje preuzeto",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Preuzeta je verzija {{version}} aplikacije OpenCode. Želite li je instalirati i ponovno pokrenuti aplikaciju?",
|
||||
"desktop.updater.installFailed.title": "Ažuriranje nije uspjelo",
|
||||
"desktop.updater.installFailed.message": "Instalacija ažuriranja nije uspjela",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Korijenski element nije pronađen. Jeste li ga zaboravili dodati u svoj index.html? Ili je možda atribut id-a pogrešno napisan?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Frissítések keresése...",
|
||||
"desktop.menu.reloadWebview": "A Webview újratöltése",
|
||||
"desktop.menu.restart": "Újraindítás",
|
||||
"desktop.dialog.chooseFolder": "Válasszon egy mappát",
|
||||
"desktop.dialog.chooseFile": "Válasszon egy fájlt",
|
||||
"desktop.dialog.saveFile": "Fájl mentése",
|
||||
"desktop.updater.checkFailed.title": "Frissítés ellenőrzése sikertelen",
|
||||
"desktop.updater.checkFailed.message": "Nem sikerült ellenőrizni a frissítéseket",
|
||||
"desktop.updater.none.title": "Nem érhető el frissítés",
|
||||
"desktop.updater.none.message": "Már az OpenCode legújabb verzióját használja",
|
||||
"desktop.updater.downloadFailed.title": "Frissítés sikertelen",
|
||||
"desktop.updater.downloadFailed.message": "Nem sikerült letölteni a frissítést",
|
||||
"desktop.updater.downloaded.title": "Frissítés letöltve",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Az OpenCode {{version}} verziója letöltődött. Szeretné telepíteni és újraindítani az alkalmazást?",
|
||||
"desktop.updater.installFailed.title": "Frissítés sikertelen",
|
||||
"desktop.updater.installFailed.message": "Nem sikerült telepíteni a frissítést",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"A gyökérelem nem található. Elfelejtette hozzáadni az index.html-hez? Vagy lehet, hogy az id attribútumot rosszul írták?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Ստուգեք թարմացումների համար...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Արմատային տարրը չի գտնվել։ Մոռացե՞լ եք այն ավելացնել ձեր index.html-ում: Կամ գուցե id հատկանիշը սխալ է գրվել:",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Periksa pembaruan...",
|
||||
"desktop.menu.reloadWebview": "Muat ulang WebView",
|
||||
"desktop.menu.restart": "Mulai ulang",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Pilih folder",
|
||||
"desktop.dialog.chooseFile": "Pilih berkas",
|
||||
"desktop.dialog.saveFile": "Simpan berkas",
|
||||
|
||||
"desktop.updater.checkFailed.title": "Pemeriksaan pembaruan gagal",
|
||||
"desktop.updater.checkFailed.message": "Gagal memeriksa pembaruan",
|
||||
"desktop.updater.none.title": "Tidak ada pembaruan",
|
||||
"desktop.updater.none.message": "Anda sudah menggunakan versi terbaru OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Pembaruan gagal",
|
||||
"desktop.updater.downloadFailed.message": "Gagal mengunduh pembaruan",
|
||||
"desktop.updater.downloaded.title": "Pembaruan diunduh",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode versi {{version}} telah diunduh. Apakah Anda ingin menginstalnya dan menjalankan ulang aplikasi?",
|
||||
"desktop.updater.installFailed.title": "Pembaruan gagal",
|
||||
"desktop.updater.installFailed.message": "Gagal menginstal pembaruan",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Elemen root tidak ditemukan. Apakah Anda lupa menambahkannya ke index.html? Atau mungkin atribut id salah eja?",
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
import * as i18n from "@solid-primitives/i18n"
|
||||
import {
|
||||
DESKTOP_NATIVE_LOCALES,
|
||||
detectDesktopNativeLocale,
|
||||
type DesktopNativeLocale,
|
||||
} from "../../../../app/src/i18n/desktop-native"
|
||||
|
||||
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 desktopUk } from "./uk"
|
||||
import { dict as desktopAr } from "./ar"
|
||||
import { dict as desktopHe } from "./he"
|
||||
import { dict as desktopNo } from "./no"
|
||||
import { dict as desktopBr } from "./br"
|
||||
import { dict as desktopBs } from "./bs"
|
||||
import { dict as desktopTr } from "./tr"
|
||||
import { dict as desktopHi } from "./hi"
|
||||
import { dict as desktopNl } from "./nl"
|
||||
import { dict as desktopId } from "./id"
|
||||
import { dict as desktopVi } from "./vi"
|
||||
import { dict as desktopIt } from "./it"
|
||||
import { dict as desktopUr } from "./ur"
|
||||
import { dict as desktopPa } from "./pa"
|
||||
import { dict as desktopAz } from "./az"
|
||||
import { dict as desktopFi } from "./fi"
|
||||
import { dict as desktopSv } from "./sv"
|
||||
import { dict as desktopTh } from "./th"
|
||||
|
||||
import { dict as desktopAm } from "./am"
|
||||
import { dict as desktopBg } from "./bg"
|
||||
import { dict as desktopBn } from "./bn"
|
||||
import { dict as desktopCa } from "./ca"
|
||||
import { dict as desktopCs } from "./cs"
|
||||
import { dict as desktopDv } from "./dv"
|
||||
import { dict as desktopDz } from "./dz"
|
||||
import { dict as desktopEl } from "./el"
|
||||
import { dict as desktopEt } from "./et"
|
||||
import { dict as desktopFa } from "./fa"
|
||||
import { dict as desktopFo } from "./fo"
|
||||
import { dict as desktopHr } from "./hr"
|
||||
import { dict as desktopHu } from "./hu"
|
||||
import { dict as desktopHy } from "./hy"
|
||||
import { dict as desktopIs } from "./is"
|
||||
import { dict as desktopKa } from "./ka"
|
||||
import { dict as desktopKm } from "./km"
|
||||
import { dict as desktopLo } from "./lo"
|
||||
import { dict as desktopLt } from "./lt"
|
||||
import { dict as desktopLv } from "./lv"
|
||||
import { dict as desktopMk } from "./mk"
|
||||
import { dict as desktopMn } from "./mn"
|
||||
import { dict as desktopMs } from "./ms"
|
||||
import { dict as desktopMy } from "./my"
|
||||
import { dict as desktopNe } from "./ne"
|
||||
import { dict as desktopRo } from "./ro"
|
||||
import { dict as desktopSi } from "./si"
|
||||
import { dict as desktopSk } from "./sk"
|
||||
import { dict as desktopSl } from "./sl"
|
||||
import { dict as desktopSq } from "./sq"
|
||||
import { dict as desktopSr } from "./sr"
|
||||
import { dict as desktopTg } from "./tg"
|
||||
import { dict as desktopTk } from "./tk"
|
||||
import { dict as desktopUz } from "./uz"
|
||||
|
||||
export type Locale = DesktopNativeLocale
|
||||
|
||||
type RawDictionary = typeof desktopEn
|
||||
type Dictionary = Record<keyof i18n.Flatten<RawDictionary>, string>
|
||||
|
||||
function detectLocale(): Locale {
|
||||
if (typeof navigator !== "object") return "en"
|
||||
return detectDesktopNativeLocale(navigator.languages?.length ? navigator.languages : [navigator.language])
|
||||
}
|
||||
|
||||
function parseLocale(value: unknown): Locale | null {
|
||||
if (!value) return null
|
||||
if (typeof value !== "string") return null
|
||||
if ((DESKTOP_NATIVE_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(desktopEn)
|
||||
|
||||
function build(locale: Locale): Dictionary {
|
||||
if (locale === "en") return base
|
||||
if (locale === "zh") return { ...base, ...i18n.flatten(desktopZh) }
|
||||
if (locale === "zht") return { ...base, ...i18n.flatten(desktopZht) }
|
||||
if (locale === "de") return { ...base, ...i18n.flatten(desktopDe) }
|
||||
if (locale === "es") return { ...base, ...i18n.flatten(desktopEs) }
|
||||
if (locale === "fr") return { ...base, ...i18n.flatten(desktopFr) }
|
||||
if (locale === "da") return { ...base, ...i18n.flatten(desktopDa) }
|
||||
if (locale === "ja") return { ...base, ...i18n.flatten(desktopJa) }
|
||||
if (locale === "pl") return { ...base, ...i18n.flatten(desktopPl) }
|
||||
if (locale === "ru") return { ...base, ...i18n.flatten(desktopRu) }
|
||||
if (locale === "uk") return { ...base, ...i18n.flatten(desktopUk) }
|
||||
if (locale === "ar") return { ...base, ...i18n.flatten(desktopAr) }
|
||||
if (locale === "he") return { ...base, ...i18n.flatten(desktopHe) }
|
||||
if (locale === "no") return { ...base, ...i18n.flatten(desktopNo) }
|
||||
if (locale === "br") return { ...base, ...i18n.flatten(desktopBr) }
|
||||
if (locale === "bs") return { ...base, ...i18n.flatten(desktopBs) }
|
||||
if (locale === "tr") return { ...base, ...i18n.flatten(desktopTr) }
|
||||
if (locale === "hi") return { ...base, ...i18n.flatten(desktopHi) }
|
||||
if (locale === "nl") return { ...base, ...i18n.flatten(desktopNl) }
|
||||
if (locale === "id") return { ...base, ...i18n.flatten(desktopId) }
|
||||
if (locale === "vi") return { ...base, ...i18n.flatten(desktopVi) }
|
||||
if (locale === "it") return { ...base, ...i18n.flatten(desktopIt) }
|
||||
if (locale === "ur") return { ...base, ...i18n.flatten(desktopUr) }
|
||||
if (locale === "pa") return { ...base, ...i18n.flatten(desktopPa) }
|
||||
if (locale === "az") return { ...base, ...i18n.flatten(desktopAz) }
|
||||
if (locale === "fi") return { ...base, ...i18n.flatten(desktopFi) }
|
||||
if (locale === "sv") return { ...base, ...i18n.flatten(desktopSv) }
|
||||
if (locale === "th") return { ...base, ...i18n.flatten(desktopTh) }
|
||||
if (locale === "am") return { ...base, ...i18n.flatten(desktopAm) }
|
||||
if (locale === "bg") return { ...base, ...i18n.flatten(desktopBg) }
|
||||
if (locale === "bn") return { ...base, ...i18n.flatten(desktopBn) }
|
||||
if (locale === "ca") return { ...base, ...i18n.flatten(desktopCa) }
|
||||
if (locale === "cs") return { ...base, ...i18n.flatten(desktopCs) }
|
||||
if (locale === "dv") return { ...base, ...i18n.flatten(desktopDv) }
|
||||
if (locale === "dz") return { ...base, ...i18n.flatten(desktopDz) }
|
||||
if (locale === "el") return { ...base, ...i18n.flatten(desktopEl) }
|
||||
if (locale === "et") return { ...base, ...i18n.flatten(desktopEt) }
|
||||
if (locale === "fa") return { ...base, ...i18n.flatten(desktopFa) }
|
||||
if (locale === "fo") return { ...base, ...i18n.flatten(desktopFo) }
|
||||
if (locale === "hr") return { ...base, ...i18n.flatten(desktopHr) }
|
||||
if (locale === "hu") return { ...base, ...i18n.flatten(desktopHu) }
|
||||
if (locale === "hy") return { ...base, ...i18n.flatten(desktopHy) }
|
||||
if (locale === "is") return { ...base, ...i18n.flatten(desktopIs) }
|
||||
if (locale === "ka") return { ...base, ...i18n.flatten(desktopKa) }
|
||||
if (locale === "km") return { ...base, ...i18n.flatten(desktopKm) }
|
||||
if (locale === "lo") return { ...base, ...i18n.flatten(desktopLo) }
|
||||
if (locale === "lt") return { ...base, ...i18n.flatten(desktopLt) }
|
||||
if (locale === "lv") return { ...base, ...i18n.flatten(desktopLv) }
|
||||
if (locale === "mk") return { ...base, ...i18n.flatten(desktopMk) }
|
||||
if (locale === "mn") return { ...base, ...i18n.flatten(desktopMn) }
|
||||
if (locale === "ms") return { ...base, ...i18n.flatten(desktopMs) }
|
||||
if (locale === "my") return { ...base, ...i18n.flatten(desktopMy) }
|
||||
if (locale === "ne") return { ...base, ...i18n.flatten(desktopNe) }
|
||||
if (locale === "ro") return { ...base, ...i18n.flatten(desktopRo) }
|
||||
if (locale === "si") return { ...base, ...i18n.flatten(desktopSi) }
|
||||
if (locale === "sk") return { ...base, ...i18n.flatten(desktopSk) }
|
||||
if (locale === "sl") return { ...base, ...i18n.flatten(desktopSl) }
|
||||
if (locale === "sq") return { ...base, ...i18n.flatten(desktopSq) }
|
||||
if (locale === "sr") return { ...base, ...i18n.flatten(desktopSr) }
|
||||
if (locale === "tg") return { ...base, ...i18n.flatten(desktopTg) }
|
||||
if (locale === "tk") return { ...base, ...i18n.flatten(desktopTk) }
|
||||
if (locale === "uz") return { ...base, ...i18n.flatten(desktopUz) }
|
||||
return { ...base, ...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 raw = await window.api.storeGet("opencode.global.dat", "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
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Leita að uppfærslum...",
|
||||
"desktop.menu.reloadWebview": "Endurhlaða Webview",
|
||||
"desktop.menu.restart": "Endurræsa",
|
||||
"desktop.dialog.chooseFolder": "Veldu möppu",
|
||||
"desktop.dialog.chooseFile": "Veldu skrá",
|
||||
"desktop.dialog.saveFile": "Vista skrá",
|
||||
"desktop.updater.checkFailed.title": "Uppfærsluathugun mistókst",
|
||||
"desktop.updater.checkFailed.message": "Mistókst að leita að uppfærslum",
|
||||
"desktop.updater.none.title": "Engin uppfærsla í boði",
|
||||
"desktop.updater.none.message": "Þú ert nú þegar að nota nýjustu útgáfuna af OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Uppfærsla mistókst",
|
||||
"desktop.updater.downloadFailed.message": "Mistókst að hlaða niður uppfærslu",
|
||||
"desktop.updater.downloaded.title": "Uppfærslu hlaðið niður",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Útgáfu {{version}} af OpenCode hefur verið hlaðið niður. Viltu setja hana upp og endurræsa?",
|
||||
"desktop.updater.installFailed.title": "Uppfærsla mistókst",
|
||||
"desktop.updater.installFailed.message": "Mistókst að setja upp uppfærslu",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Rótarþáttur fannst ekki. Gleymdirðu að bæta því við index.html þinn? Eða er id eigindin kannski rangt stafsett?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Controlla gli aggiornamenti...",
|
||||
"desktop.menu.reloadWebview": "Ricarica la webview",
|
||||
"desktop.menu.restart": "Riavvia",
|
||||
"desktop.dialog.chooseFolder": "Scegli una cartella",
|
||||
"desktop.dialog.chooseFile": "Scegli un file",
|
||||
"desktop.dialog.saveFile": "Salva file",
|
||||
"desktop.updater.checkFailed.title": "Controllo degli aggiornamenti non riuscito",
|
||||
"desktop.updater.checkFailed.message": "Impossibile controllare gli aggiornamenti",
|
||||
"desktop.updater.none.title": "Nessun aggiornamento disponibile",
|
||||
"desktop.updater.none.message": "Stai già utilizzando l'ultima versione di OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Aggiornamento non riuscito",
|
||||
"desktop.updater.downloadFailed.message": "Impossibile scaricare l'aggiornamento",
|
||||
"desktop.updater.downloaded.title": "Aggiornamento scaricato",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"La versione {{version}} di OpenCode è stata scaricata. Vuoi installarla e riavviare l'app?",
|
||||
"desktop.updater.installFailed.title": "Aggiornamento non riuscito",
|
||||
"desktop.updater.installFailed.message": "Impossibile installare l'aggiornamento",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Elemento radice non trovato. Hai dimenticato di aggiungerlo al tuo index.html? O forse l'attributo id è stato scritto in modo errato?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "アップデートを確認...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"ルート要素が見つかりません。index.htmlに追加するのを忘れていませんか?またはid属性のスペルが間違っていませんか?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "შეამოწმეთ განახლებები...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"ძირის ელემენტი ვერ მოიძებნა. დაგავიწყდათ მისი დამატება თქვენს index.html-ში? ან იქნებ id ატრიბუტი არასწორად არის დაწერილი?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "ពិនិត្យមើលបច្ចុប្បន្នភាព...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"រកមិនឃើញធាតុឫសទេ។ តើអ្នកភ្លេចបន្ថែមវាទៅ index.html របស់អ្នកទេ? ឬប្រហែលជាគុណលក្ខណៈលេខសម្គាល់ត្រូវបានសរសេរខុស?",
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "업데이트 확인...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"루트 요소를 찾을 수 없습니다. index.html에 추가하는 것을 잊으셨나요? 또는 id 속성의 철자가 틀렸을 수 있습니다.",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "ກວດເບິ່ງການອັບເດດ...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"ບໍ່ພົບອົງປະກອບຮາກ. ທ່ານລືມເພີ່ມມັນໃສ່ index.html ຂອງທ່ານບໍ? ຫຼືບາງທີຄຸນສົມບັດ id ມີການສະກົດຜິດ?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Tikrinti, ar yra naujinimų...",
|
||||
"desktop.menu.reloadWebview": "Iš naujo įkelti Webview",
|
||||
"desktop.menu.restart": "Paleisti iš naujo",
|
||||
"desktop.dialog.chooseFolder": "Pasirinkite aplanką",
|
||||
"desktop.dialog.chooseFile": "Pasirinkite failą",
|
||||
"desktop.dialog.saveFile": "Išsaugoti failą",
|
||||
"desktop.updater.checkFailed.title": "Naujinių patikrinti nepavyko",
|
||||
"desktop.updater.checkFailed.message": "Nepavyko patikrinti, ar nėra naujinimų",
|
||||
"desktop.updater.none.title": "Naujinių nėra",
|
||||
"desktop.updater.none.message": "Jau naudojate naujausią OpenCode versiją",
|
||||
"desktop.updater.downloadFailed.title": "Nepavyko atnaujinti",
|
||||
"desktop.updater.downloadFailed.message": "Nepavyko atsisiųsti naujinimo",
|
||||
"desktop.updater.downloaded.title": "Naujinimas parsiųstas",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode versija {{version}} atsisiųsta, ar norėtumėte ją įdiegti ir paleisti iš naujo?",
|
||||
"desktop.updater.installFailed.title": "Nepavyko atnaujinti",
|
||||
"desktop.updater.installFailed.message": "Nepavyko įdiegti naujinimo",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Šakninis elementas nerastas. Ar pamiršote jį įtraukti į index.html? O gal id atributas buvo neteisingai parašytas?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Pārbaudīt atjauninājumus...",
|
||||
"desktop.menu.reloadWebview": "Pārlādēt tīmekļa skatu",
|
||||
"desktop.menu.restart": "Restartēt",
|
||||
"desktop.dialog.chooseFolder": "Izvēlieties mapi",
|
||||
"desktop.dialog.chooseFile": "Izvēlieties failu",
|
||||
"desktop.dialog.saveFile": "Saglabāt failu",
|
||||
"desktop.updater.checkFailed.title": "Atjauninājumu pārbaude neizdevās",
|
||||
"desktop.updater.checkFailed.message": "Neizdevās pārbaudīt atjauninājumus",
|
||||
"desktop.updater.none.title": "Atjauninājumu nav",
|
||||
"desktop.updater.none.message": "Jūs jau izmantojat jaunāko OpenCode versiju",
|
||||
"desktop.updater.downloadFailed.title": "Atjaunināšana neizdevās",
|
||||
"desktop.updater.downloadFailed.message": "Neizdevās lejupielādēt atjauninājumu",
|
||||
"desktop.updater.downloaded.title": "Atjauninājums lejupielādēts",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode versija {{version}} ir lejupielādēta. Vai vēlaties to instalēt un palaist no jauna?",
|
||||
"desktop.updater.installFailed.title": "Atjaunināšana neizdevās",
|
||||
"desktop.updater.installFailed.message": "Neizdevās instalēt atjauninājumu",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Saknes elements nav atrasts. Vai aizmirsāt to pievienot index.html? Vai arī id atribūts ir kļūdaini uzrakstīts?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Проверете дали има ажурирања...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Не е пронајден корен елемент. Дали заборавивте да го додадете во вашата index.html? Или можеби атрибутот id е погрешно напишан?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Шинэчлэлтүүдийг шалгана уу...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Үндэс элемент олдсонгүй. Та үүнийг index.html дээрээ нэмэхээ мартсан уу? Эсвэл id атрибутыг буруу бичсэн байж магадгүй юм уу?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Semak Kemas Kini...",
|
||||
"desktop.menu.reloadWebview": "Muat Semula Paparan Web",
|
||||
"desktop.menu.restart": "Mulakan Semula",
|
||||
"desktop.dialog.chooseFolder": "Pilih folder",
|
||||
"desktop.dialog.chooseFile": "Pilih fail",
|
||||
"desktop.dialog.saveFile": "Simpan fail",
|
||||
"desktop.updater.checkFailed.title": "Semakan Kemas Kini Gagal",
|
||||
"desktop.updater.checkFailed.message": "Gagal menyemak kemas kini",
|
||||
"desktop.updater.none.title": "Tiada Kemas Kini Tersedia",
|
||||
"desktop.updater.none.message": "Anda sudah menggunakan versi terkini OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Kemas Kini Gagal",
|
||||
"desktop.updater.downloadFailed.message": "Gagal memuat turun kemas kini",
|
||||
"desktop.updater.downloaded.title": "Kemas Kini Dimuat Turun",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Versi {{version}} OpenCode telah dimuat turun, adakah anda ingin memasangnya dan melancarkan semula?",
|
||||
"desktop.updater.installFailed.title": "Kemas Kini Gagal",
|
||||
"desktop.updater.installFailed.message": "Gagal memasang kemas kini",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Elemen root tidak ditemui. Adakah anda terlupa menambahnya ke index.html anda? Atau mungkin atribut id telah tersalah eja?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "အပ်ဒိတ်များအတွက် စစ်ဆေးပါ...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"အမြစ်ဒြပ်စင်ကို ရှာမတွေ့ပါ။ ၎င်းကို သင်၏ index.html တွင် ထည့်ရန် မေ့သွားပါသလား။ ဒါမှမဟုတ် id attribute က စာလုံးပေါင်းမှားနေသလား။",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict: Record<string, string> = {
|
||||
"desktop.menu.checkForUpdates": "अपडेटहरूको लागि जाँच गर्नुहोस्...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"मूल तत्व फेला परेन। के तपाईंले यसलाई आफ्नो index.html मा थप्न बिर्सनुभयो? वा हुनसक्छ आईडी विशेषता गलत हिज्जे भयो?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Controleren op updates...",
|
||||
"desktop.menu.reloadWebview": "Webview opnieuw laden",
|
||||
"desktop.menu.restart": "Opnieuw opstarten",
|
||||
"desktop.dialog.chooseFolder": "Kies een map",
|
||||
"desktop.dialog.chooseFile": "Kies een bestand",
|
||||
"desktop.dialog.saveFile": "Bestand opslaan",
|
||||
"desktop.updater.checkFailed.title": "Updatecontrole mislukt",
|
||||
"desktop.updater.checkFailed.message": "Controleren op updates is mislukt",
|
||||
"desktop.updater.none.title": "Geen update beschikbaar",
|
||||
"desktop.updater.none.message": "Je gebruikt al de nieuwste versie van OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Update mislukt",
|
||||
"desktop.updater.downloadFailed.message": "Downloaden van update is mislukt",
|
||||
"desktop.updater.downloaded.title": "Update gedownload",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Versie {{version}} van OpenCode is gedownload. Wil je deze installeren en OpenCode opnieuw starten?",
|
||||
"desktop.updater.installFailed.title": "Update mislukt",
|
||||
"desktop.updater.installFailed.message": "Installeren van update is mislukt",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Root-element niet gevonden. Ben je vergeten het toe te voegen aan je index.html? Of is het id-attribuut misschien verkeerd gespeld?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Se etter oppdateringer...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Rotelement ikke funnet. Glemte du å legge det til i index.html? Eller kanskje id-attributtet er feilstavet?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "اپ ڈیٹس لئی چیک کرو...",
|
||||
"desktop.menu.reloadWebview": "ویب ویو دوبارہ لوڈ کرو",
|
||||
"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.error.dev.rootNotFound":
|
||||
"عنصر نئیں لبیا۔ کی تسی ایہنوں اپنے index.html چ شامل کرنا بھل گئے او؟ یا شاید id وصف غلط ہجے ہو گیا اے؟",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Sprawdź aktualizacje...",
|
||||
"desktop.menu.reloadWebview": "Załaduj ponownie WebView",
|
||||
"desktop.menu.restart": "Uruchom ponownie",
|
||||
|
||||
"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.error.dev.rootNotFound":
|
||||
"Nie znaleziono elementu głównego. Czy zapomniałeś dodać go do swojego index.html? A może atrybut id został błędnie wpisany?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Caută actualizări...",
|
||||
"desktop.menu.reloadWebview": "Reîncarcă webview",
|
||||
"desktop.menu.restart": "Repornește",
|
||||
"desktop.dialog.chooseFolder": "Alege un folder",
|
||||
"desktop.dialog.chooseFile": "Alege un fișier",
|
||||
"desktop.dialog.saveFile": "Salvează fișierul",
|
||||
"desktop.updater.checkFailed.title": "Verificarea actualizărilor a eșuat",
|
||||
"desktop.updater.checkFailed.message": "Nu s-au putut verifica actualizările",
|
||||
"desktop.updater.none.title": "Nicio actualizare disponibilă",
|
||||
"desktop.updater.none.message": "Folosești deja cea mai recentă versiune OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Actualizarea a eșuat",
|
||||
"desktop.updater.downloadFailed.message": "Nu s-a putut descărca actualizarea",
|
||||
"desktop.updater.downloaded.title": "Actualizare descărcată",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Versiunea {{version}} OpenCode a fost descărcată. Vrei să o instalezi și să repornești?",
|
||||
"desktop.updater.installFailed.title": "Actualizarea a eșuat",
|
||||
"desktop.updater.installFailed.message": "Nu s-a putut instala actualizarea",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Elementul root nu a fost găsit. L-ai adăugat în index.html? Sau poate atributul id este scris greșit?",
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Проверить обновления...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Корневой элемент не найден. Вы забыли добавить его в index.html? Или, может быть, атрибут id был написан неправильно?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict: Record<string, string> = {
|
||||
"desktop.menu.checkForUpdates": "යාවත්කාලීන සඳහා පරීක්ෂා කරන්න...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"මූල මූලද්රව්යය හමු නොවීය. ඔබට එය ඔබගේ index.html වෙත එක් කිරීමට අමතකද? එසේත් නැතිනම් හැඳුනුම්පත වැරදි ලෙස සටහන් වී තිබේද?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Skontrolovať aktualizácie...",
|
||||
"desktop.menu.reloadWebview": "Obnoviť webové zobrazenie",
|
||||
"desktop.menu.restart": "Reštartovať",
|
||||
"desktop.dialog.chooseFolder": "Vybrať priečinok",
|
||||
"desktop.dialog.chooseFile": "Vybrať súbor",
|
||||
"desktop.dialog.saveFile": "Uložiť súbor",
|
||||
"desktop.updater.checkFailed.title": "Kontrola aktualizácií zlyhala",
|
||||
"desktop.updater.checkFailed.message": "Nepodarilo sa skontrolovať aktualizácie",
|
||||
"desktop.updater.none.title": "Žiadna aktualizácia nie je k dispozícii",
|
||||
"desktop.updater.none.message": "Používate najnovšiu verziu OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Aktualizácia zlyhala",
|
||||
"desktop.updater.downloadFailed.message": "Nepodarilo sa stiahnuť aktualizáciu",
|
||||
"desktop.updater.downloaded.title": "Aktualizácia stiahnutá",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Verzia {{version}} OpenCode bola stiahnutá. Chcete ju nainštalovať a reštartovať?",
|
||||
"desktop.updater.installFailed.title": "Aktualizácia zlyhala",
|
||||
"desktop.updater.installFailed.message": "Nepodarilo sa nainštalovať aktualizáciu",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Koreňový prvok sa nenašiel. Pridali ste ho do index.html? Alebo je atribút id napísaný nesprávne?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Preverite posodobitve ...",
|
||||
"desktop.menu.reloadWebview": "Ponovno naloži Webview",
|
||||
"desktop.menu.restart": "Znova zaženite",
|
||||
"desktop.dialog.chooseFolder": "Izberite mapo",
|
||||
"desktop.dialog.chooseFile": "Izberite datoteko",
|
||||
"desktop.dialog.saveFile": "Shrani datoteko",
|
||||
"desktop.updater.checkFailed.title": "Preverjanje posodobitve ni uspelo",
|
||||
"desktop.updater.checkFailed.message": "Preverjanje posodobitev ni uspelo",
|
||||
"desktop.updater.none.title": "Posodobitev ni na voljo",
|
||||
"desktop.updater.none.message": "Že uporabljate najnovejšo različico OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Posodobitev ni uspela",
|
||||
"desktop.updater.downloadFailed.message": "Prenos posodobitve ni uspel",
|
||||
"desktop.updater.downloaded.title": "Posodobitev prenesena",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Različica {{version}} OpenCode je bila prenesena, jo želite namestiti in znova zagnati?",
|
||||
"desktop.updater.installFailed.title": "Posodobitev ni uspela",
|
||||
"desktop.updater.installFailed.message": "Namestitev posodobitve ni uspela",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Korenski element ni bil najden. Ste ga pozabili dodati v svoj index.html? Ali pa je morda atribut id narobe črkovan?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Kontrollo për përditësime...",
|
||||
"desktop.menu.reloadWebview": "Rifresko pamjen e internetit",
|
||||
"desktop.menu.restart": "Rinis",
|
||||
"desktop.dialog.chooseFolder": "Zgjidhni një dosje",
|
||||
"desktop.dialog.chooseFile": "Zgjidhni një skedar",
|
||||
"desktop.dialog.saveFile": "Ruaj skedarin",
|
||||
"desktop.updater.checkFailed.title": "Kontrolli i përditësimit dështoi",
|
||||
"desktop.updater.checkFailed.message": "Kontrolli për përditësime dështoi",
|
||||
"desktop.updater.none.title": "Nuk ka përditësim të disponueshëm",
|
||||
"desktop.updater.none.message": "Ju tashmë po përdorni versionin më të fundit të OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Përditësimi dështoi",
|
||||
"desktop.updater.downloadFailed.message": "Shkarkimi i përditësimit dështoi",
|
||||
"desktop.updater.downloaded.title": "Përditësimi u shkarkua",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Versioni {{version}} i OpenCode është shkarkuar, dëshironi ta instaloni dhe rinisni?",
|
||||
"desktop.updater.installFailed.title": "Përditësimi dështoi",
|
||||
"desktop.updater.installFailed.message": "Instalimi i përditësimit dështoi",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Elementi rrënjë nuk u gjet. A keni harruar ta shtoni atë në index.html tuaj? Apo ndoshta atributi id është shkruar gabim?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Проверите да ли постоје ажурирања...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Основни елемент није пронађен. Да ли сте заборавили да га додате у свој index.html? Или је можда атрибут ид погрешно написан?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Sök efter uppdateringar...",
|
||||
"desktop.menu.reloadWebview": "Ladda om webbvyn",
|
||||
"desktop.menu.restart": "Starta om",
|
||||
"desktop.dialog.chooseFolder": "Välj en mapp",
|
||||
"desktop.dialog.chooseFile": "Välj en fil",
|
||||
"desktop.dialog.saveFile": "Spara filen",
|
||||
"desktop.updater.checkFailed.title": "Uppdateringskontrollen misslyckades",
|
||||
"desktop.updater.checkFailed.message": "Det gick inte att söka efter uppdateringar",
|
||||
"desktop.updater.none.title": "Ingen uppdatering tillgänglig",
|
||||
"desktop.updater.none.message": "Du använder redan den senaste versionen av OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Uppdateringen misslyckades",
|
||||
"desktop.updater.downloadFailed.message": "Det gick inte att ladda ned uppdateringen",
|
||||
"desktop.updater.downloaded.title": "Uppdatering nedladdad",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Version {{version}} av OpenCode har laddats ned. Vill du installera den och starta om programmet?",
|
||||
"desktop.updater.installFailed.title": "Uppdateringen misslyckades",
|
||||
"desktop.updater.installFailed.message": "Det gick inte att installera uppdateringen",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Rotelementet hittades inte. Har du glömt att lägga till det i din index.html? Eller kanske id-attributet är felstavat?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Навсозиро санҷед...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Элементи решавӣ ёфт нашуд. Оё шумо онро ба index.html илова карданро фаромӯш кардаед? Ё шояд атрибути id хато навишта шудааст?",
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "ตรวจหาการอัปเดต...",
|
||||
"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.error.dev.rootNotFound": "ไม่พบองค์ประกอบรูท คุณลืมเพิ่มใน index.html หรือบางทีแอตทริบิวต์ id อาจสะกดผิด?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Täzelenmeleri barlaň ...",
|
||||
"desktop.menu.reloadWebview": "Web sahypasyny täzeden ýükläň",
|
||||
"desktop.menu.restart": "Gaýtadan açyň",
|
||||
"desktop.dialog.chooseFolder": "Papka saýlaň",
|
||||
"desktop.dialog.chooseFile": "Faýl saýlaň",
|
||||
"desktop.dialog.saveFile": "Faýly ýazdyryň",
|
||||
"desktop.updater.checkFailed.title": "Täzelenme şowsuz",
|
||||
"desktop.updater.checkFailed.message": "Täzelenmeleri barlap bilmedi",
|
||||
"desktop.updater.none.title": "Täzelenme ýok",
|
||||
"desktop.updater.none.message": "OpenCode-iň iň soňky wersiýasyny eýýäm ulanýarsyňyz",
|
||||
"desktop.updater.downloadFailed.title": "Täzelenme şowsuz",
|
||||
"desktop.updater.downloadFailed.message": "Täzelenmäni göçürip alyp bilmedi",
|
||||
"desktop.updater.downloaded.title": "Täzelenme ýüklendi",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode-iň {{version}} wersiýasy göçürildi, ony gurup täzeden işletmek isleýärsiňizmi?",
|
||||
"desktop.updater.installFailed.title": "Täzelenme şowsuz",
|
||||
"desktop.updater.installFailed.message": "Täzelenmäni gurup bilmedi",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Kök elementi tapylmady index.html-e goşmagy ýatdan çykardyňyzmy? Ora-da id atributynyň ýalňyş ýazylan bolmagy mümkin?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Güncellemeleri kontrol et...",
|
||||
"desktop.menu.reloadWebview": "Web görünümünü yeniden yükle",
|
||||
"desktop.menu.restart": "Yeniden başlat",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Bir klasör seçin",
|
||||
"desktop.dialog.chooseFile": "Bir dosya seçin",
|
||||
"desktop.dialog.saveFile": "Dosyayı kaydedin",
|
||||
|
||||
"desktop.updater.checkFailed.title": "Güncelleme kontrolü başarısız oldu",
|
||||
"desktop.updater.checkFailed.message": "Güncellemeler kontrol edilemedi",
|
||||
"desktop.updater.none.title": "Güncelleme yok",
|
||||
"desktop.updater.none.message": "OpenCode'un en son sürümünü zaten kullanıyorsunuz",
|
||||
"desktop.updater.downloadFailed.title": "Güncelleme başarısız oldu",
|
||||
"desktop.updater.downloadFailed.message": "Güncelleme indirilemedi",
|
||||
"desktop.updater.downloaded.title": "Güncelleme indirildi",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCode'un {{version}} sürümü indirildi. Şimdi yükleyip yeniden başlatmak ister misiniz?",
|
||||
"desktop.updater.installFailed.title": "Güncelleme başarısız oldu",
|
||||
"desktop.updater.installFailed.message": "Güncelleme yüklenemedi",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Kök eleman bulunamadı. index.html dosyanıza eklemeyi unuttunuz mu? Ya da id özelliği yanlış mı yazıldı?",
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Перевірити оновлення...",
|
||||
"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.error.dev.rootNotFound":
|
||||
"Кореневий елемент не знайдено. Ви забули додати його до index.html? Або, можливо, атрибут id було написано з помилкою?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "اپ ڈیٹس کے لیے چیک کریں...",
|
||||
"desktop.menu.reloadWebview": "ویب ویو کو دوبارہ لوڈ کریں۔",
|
||||
"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.error.dev.rootNotFound":
|
||||
"روٹ عنصر نہیں ملا۔ کیا آپ اسے اپنے index.html میں شامل کرنا بھول گئے؟ یا ہو سکتا ہے کہ آئی ڈی وصف کی ہجے غلط ہو گئی ہو؟",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Yangilanishlarni tekshiring...",
|
||||
"desktop.menu.reloadWebview": "Veb koʻrinishini qayta yuklash",
|
||||
"desktop.menu.restart": "Qayta ishga tushirish",
|
||||
"desktop.dialog.chooseFolder": "Jildni tanlang",
|
||||
"desktop.dialog.chooseFile": "Faylni tanlang",
|
||||
"desktop.dialog.saveFile": "Faylni saqlash",
|
||||
"desktop.updater.checkFailed.title": "Yangilanish tekshiruvi amalga oshmadi",
|
||||
"desktop.updater.checkFailed.message": "Yangilanishlarni tekshirib boʻlmadi",
|
||||
"desktop.updater.none.title": "Yangilanish mavjud emas",
|
||||
"desktop.updater.none.message": "Siz allaqachon OpenCode ning oxirgi versiyasidan foydalanyapsiz",
|
||||
"desktop.updater.downloadFailed.title": "Yangilash amalga oshmadi",
|
||||
"desktop.updater.downloadFailed.message": "Yangilanishni yuklab boʻlmadi",
|
||||
"desktop.updater.downloaded.title": "Yangilanish yuklab olindi",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"OpenCodening {{version}} versiyasi yuklab olindi, uni oʻrnatib, qayta ishga tushirmoqchimisiz?",
|
||||
"desktop.updater.installFailed.title": "Yangilash amalga oshmadi",
|
||||
"desktop.updater.installFailed.message": "Yangilanishni oʻrnatib boʻlmadi",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Ildiz element topilmadi. Uni index.html-ga qo'shishni unutdingizmi? Yoki id atributi noto'g'ri yozilgandir?",
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
export const dict = {
|
||||
"desktop.menu.checkForUpdates": "Kiểm tra cập nhật...",
|
||||
"desktop.menu.reloadWebview": "Tải lại Webview",
|
||||
"desktop.menu.restart": "Khởi động lại",
|
||||
"desktop.dialog.chooseFolder": "Chọn một thư mục",
|
||||
"desktop.dialog.chooseFile": "Chọn một tệp",
|
||||
"desktop.dialog.saveFile": "Lưu tệp",
|
||||
"desktop.updater.checkFailed.title": "Kiểm tra cập nhật không thành công",
|
||||
"desktop.updater.checkFailed.message": "Không thể kiểm tra cập nhật",
|
||||
"desktop.updater.none.title": "Không có bản cập nhật nào",
|
||||
"desktop.updater.none.message": "Bạn đang sử dụng phiên bản mới nhất của OpenCode",
|
||||
"desktop.updater.downloadFailed.title": "Cập nhật không thành công",
|
||||
"desktop.updater.downloadFailed.message": "Không tải được bản cập nhật xuống",
|
||||
"desktop.updater.downloaded.title": "Đã tải xuống bản cập nhật",
|
||||
"desktop.updater.downloaded.prompt":
|
||||
"Phiên bản {{version}} của OpenCode đã được tải xuống, bạn có muốn cài đặt và khởi chạy lại không?",
|
||||
"desktop.updater.installFailed.title": "Cập nhật không thành công",
|
||||
"desktop.updater.installFailed.message": "Không cài đặt được bản cập nhật",
|
||||
|
||||
"desktop.error.dev.rootNotFound":
|
||||
"Không tìm thấy phần tử gốc. Bạn đã quên thêm nó vào index.html của mình? Hoặc có thể thuộc tính id bị sai chính tả?",
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue