mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-09 22:58:28 +00:00
chore: merge v2 into promise-combinators
This commit is contained in:
commit
02ce4a330f
794 changed files with 33863 additions and 20365 deletions
|
|
@ -19,6 +19,8 @@ Valid types are `feat`, `fix`, `docs`, `chore`, `refactor`, and `test`. Scopes a
|
|||
|
||||
Examples: `fix(tui): simplify thinking toggle styling`, `docs: update contributing guide`, `chore(sdk): regenerate types`.
|
||||
|
||||
Never bypass Git hooks. Do not use `--no-verify` or otherwise disable, skip, or circumvent commit or push hooks. If a hook fails, fix the failure or stop and report it to the user.
|
||||
|
||||
## Style Guide
|
||||
|
||||
### General Principles
|
||||
|
|
|
|||
68
bun.lock
68
bun.lock
|
|
@ -30,7 +30,7 @@
|
|||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
"@dnd-kit/dom": "0.5.0",
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"bin": {
|
||||
"opencode2": "./bin/opencode2.cjs",
|
||||
},
|
||||
|
|
@ -114,10 +114,12 @@
|
|||
"effect": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
"jsonc-parser": "3.3.1",
|
||||
"open": "10.1.2",
|
||||
"opentui-spinner": "catalog:",
|
||||
"semver": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
"uqr": "0.1.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
|
|
@ -151,7 +153,7 @@
|
|||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
|
|
@ -165,7 +167,7 @@
|
|||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
|
@ -201,7 +203,7 @@
|
|||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
|
|
@ -228,7 +230,7 @@
|
|||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
|
|
@ -250,7 +252,7 @@
|
|||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
@ -274,7 +276,7 @@
|
|||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
|
|
@ -294,7 +296,7 @@
|
|||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -390,7 +392,7 @@
|
|||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"effect": "catalog:",
|
||||
|
|
@ -450,7 +452,7 @@
|
|||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
|
|
@ -464,7 +466,7 @@
|
|||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
|
|
@ -476,7 +478,7 @@
|
|||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -508,7 +510,7 @@
|
|||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
|
|
@ -524,12 +526,12 @@
|
|||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "4.0.0-beta.83",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.83",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
|
|
@ -538,7 +540,7 @@
|
|||
"typescript": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"effect": "4.0.0-beta.83",
|
||||
"effect": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/httpapi-codegen": {
|
||||
|
|
@ -556,7 +558,7 @@
|
|||
},
|
||||
"packages/llm": {
|
||||
"name": "@opencode-ai/llm",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
|
|
@ -575,7 +577,7 @@
|
|||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -708,7 +710,7 @@
|
|||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/client": "workspace:*",
|
||||
|
|
@ -795,7 +797,7 @@
|
|||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
|
|
@ -810,7 +812,7 @@
|
|||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -827,7 +829,7 @@
|
|||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -873,6 +875,7 @@
|
|||
"name": "@opencode-ai/simulation",
|
||||
"version": "1.17.13",
|
||||
"dependencies": {
|
||||
"@fontsource/adwaita-mono": "5.2.1",
|
||||
"@napi-rs/canvas": "1.0.2",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/llm": "workspace:*",
|
||||
|
|
@ -887,7 +890,7 @@
|
|||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
|
|
@ -900,7 +903,7 @@
|
|||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@opencode-ai/stats-core": "workspace:*",
|
||||
|
|
@ -933,7 +936,7 @@
|
|||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
|
|
@ -952,7 +955,7 @@
|
|||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
|
|
@ -993,7 +996,7 @@
|
|||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/client": "workspace:*",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
|
|
@ -1014,6 +1017,7 @@
|
|||
"remeda": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
"uqr": "0.1.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/bun": "catalog:",
|
||||
|
|
@ -1023,7 +1027,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
|
|
@ -1074,7 +1078,7 @@
|
|||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
|
@ -1724,6 +1728,8 @@
|
|||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
|
||||
|
||||
"@fontsource/adwaita-mono": ["@fontsource/adwaita-mono@5.2.1", "", {}, "sha512-6+Q1UIvklJ9REijs6kv7YlRNt6yktRj0iW8H69YIugdD9P2h3eIX1AB8/9ICMfpVyVeywlsrCXg82y/LfRrjyg=="],
|
||||
|
||||
"@fontsource/ibm-plex-mono": ["@fontsource/ibm-plex-mono@5.2.5", "", {}, "sha512-G09N3GfuT9qj3Ax2FDZvKqZttzM3v+cco2l8uXamhKyXLdmlaUDH5o88/C3vtTHj2oT7yRKsvxz9F+BXbWKMYA=="],
|
||||
|
||||
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
|
||||
|
|
@ -5946,6 +5952,8 @@
|
|||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"uqr": ["uqr@0.1.3", "", {}, "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"urijs": ["urijs@1.19.11", "", {}, "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="],
|
||||
|
|
|
|||
|
|
@ -495,7 +495,6 @@ async function subscribeSessionEvents() {
|
|||
console.log("Subscribing to session events...")
|
||||
|
||||
const TOOL: Record<string, [string, string]> = {
|
||||
todowrite: ["Todo", "\x1b[33m\x1b[1m"],
|
||||
bash: ["Bash", "\x1b[31m\x1b[1m"],
|
||||
edit: ["Edit", "\x1b[32m\x1b[1m"],
|
||||
glob: ["Glob", "\x1b[34m\x1b[1m"],
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"prepare": "husky",
|
||||
"random": "echo 'Random script'",
|
||||
"sso": "aws sso login --sso-session=opencode --no-browser",
|
||||
"translate:app": "bun run script/translate-app.ts",
|
||||
"test": "echo 'do not run tests from root' && exit 1"
|
||||
},
|
||||
"workspaces": {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const profiles = [
|
|||
{ name: "edit", tool: "edit", input: { filePath: "src/edit.ts" } },
|
||||
{
|
||||
name: "multi patch",
|
||||
tool: "apply_patch",
|
||||
tool: "patch",
|
||||
input: { files: ["src/a.ts", "src/b.ts", "src/old.ts", "src/moved.ts"] },
|
||||
},
|
||||
] as const
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
|
|||
userMessage(),
|
||||
assistantMessage(
|
||||
[
|
||||
toolPart(patchID, "apply_patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
|
||||
toolPart(patchID, "patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
|
||||
textPart(followingID, "Following incremental patch"),
|
||||
],
|
||||
{ completed: false },
|
||||
|
|
@ -49,7 +49,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
|
|||
partUpdated(
|
||||
toolPart(
|
||||
patchID,
|
||||
"apply_patch",
|
||||
"patch",
|
||||
"running",
|
||||
{ files: [first.filePath, second.filePath] },
|
||||
{ metadata: { files: [first, second] } },
|
||||
|
|
@ -61,7 +61,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
|
|||
partUpdated(
|
||||
toolPart(
|
||||
patchID,
|
||||
"apply_patch",
|
||||
"patch",
|
||||
"completed",
|
||||
{ files: [first.filePath, second.filePath, third.filePath] },
|
||||
{ metadata: { files: [first, second, third] } },
|
||||
|
|
|
|||
|
|
@ -33,11 +33,9 @@ test.describe("timeline tool state stability", () => {
|
|||
}
|
||||
const names = { webfetch: "webfetch", websearch: "websearch", task: "task", skill: "skill", custom: "mcp_probe" }
|
||||
const questionID = "prt_state_question"
|
||||
const todoID = "prt_state_todo"
|
||||
const initial = [
|
||||
...ids.map((id) => toolPart(`prt_state_${id}`, names[id], "pending", inputs[id])),
|
||||
toolPart(questionID, "question", "pending", questionInput()),
|
||||
toolPart(todoID, "todowrite", "pending", { todos: [{ content: "Hidden", status: "pending" }] }),
|
||||
textPart("prt_state_following", "Following lightweight tools"),
|
||||
]
|
||||
const childID = "ses_timeline_child"
|
||||
|
|
@ -49,7 +47,6 @@ test.describe("timeline tool state stability", () => {
|
|||
await timeline.send(status("busy"), 120)
|
||||
for (const id of ids) await timeline.waitForPart(`prt_state_${id}`)
|
||||
await expect(page.locator(`[data-timeline-part-id="${questionID}"]`)).toHaveCount(0)
|
||||
await expect(page.locator(`[data-timeline-part-id="${todoID}"]`)).toHaveCount(0)
|
||||
|
||||
const regionIDs = [
|
||||
"prt_state_webfetch",
|
||||
|
|
@ -105,7 +102,6 @@ test.describe("timeline tool state stability", () => {
|
|||
]),
|
||||
)
|
||||
await expect(page.locator(`[data-timeline-part-id="${questionID}"]`)).toContainText("Keep it stable")
|
||||
await expect(page.locator(`[data-timeline-part-id="${todoID}"]`)).toHaveCount(0)
|
||||
await expect(
|
||||
page.locator(`a[href$="/session/${childID}"]`, { has: page.locator('[data-component="task-tool-card"]') }),
|
||||
).toBeVisible()
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ function performanceTurn(index: number) {
|
|||
messageID: assistantID,
|
||||
type: "tool",
|
||||
callID: `call_0000_${suffix}_patch`,
|
||||
tool: "apply_patch",
|
||||
tool: "patch",
|
||||
state: {
|
||||
status: "completed",
|
||||
input: { patchText: realisticPatch(index) },
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ function toolPart(
|
|||
): MessagePart {
|
||||
const metadata =
|
||||
metadataOverride ??
|
||||
(tool === "apply_patch"
|
||||
(tool === "patch"
|
||||
? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
|
||||
: tool === "edit" || tool === "write"
|
||||
? {
|
||||
|
|
@ -219,7 +219,7 @@ function turn(index: number): Message[] {
|
|||
? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
|
||||
: []),
|
||||
...(index % 8 === 0
|
||||
? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
|
||||
? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
|
||||
: []),
|
||||
...(index % 7 === 0
|
||||
? [toolPart(index, 4, "bash", { command: "bun typecheck", description: "Verify generated output" }, 620)]
|
||||
|
|
@ -269,7 +269,6 @@ const childMessages = Array.from({ length: 4 }, (_, index) => [
|
|||
]).flat()
|
||||
|
||||
function renderable(part: MessagePart) {
|
||||
if (part.type === "tool" && part.tool === "todowrite") return false
|
||||
if (part.type === "text") return !!part.text.trim()
|
||||
if (part.type === "reasoning") return !!part.text.trim()
|
||||
return part.type !== "step-start" && part.type !== "step-finish" && part.type !== "patch"
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ async function mockServers(page: Page, requests: string[]) {
|
|||
if (url.pathname === `/session/${current.id}`) return json(route, current)
|
||||
if (/^\/session\/[^/]+$/.test(url.pathname)) return json(route, { name: "NotFoundError" }, 404)
|
||||
if (url.pathname === `/session/${current.id}/message`) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|todo|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/diff"].includes(url.pathname))
|
||||
return json(route, [])
|
||||
if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(url.pathname))
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ async function mockServers(page: Page) {
|
|||
if (url.pathname === `/session/${current.id}`) return json(route, current)
|
||||
if (/^\/session\/[^/]+$/.test(url.pathname)) return json(route, { name: "NotFoundError" }, 404)
|
||||
if (url.pathname === `/session/${current.id}/message`) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|todo|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/diff"].includes(url.pathname))
|
||||
return json(route, [])
|
||||
if (["/global/config", "/config", "/provider/auth", "/mcp"].includes(url.pathname)) return json(route, {})
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ test("renders a completed single-file patch", async ({ page }) => {
|
|||
assistantMessage([
|
||||
toolPart(
|
||||
id,
|
||||
"apply_patch",
|
||||
"patch",
|
||||
"completed",
|
||||
{ files: ["src/a.ts"] },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ test("preserves nested patch file state through outer collapse and reopen", asyn
|
|||
assistantMessage([
|
||||
toolPart(
|
||||
patchID,
|
||||
"apply_patch",
|
||||
"patch",
|
||||
"completed",
|
||||
{ files: files.map((file) => file.filePath) },
|
||||
{ metadata: { files } },
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ test.describe("session timeline projection", () => {
|
|||
editPart("prt_edit"),
|
||||
toolPart("prt_write", "write", "completed", { filePath: "src/new.ts", content: "export const stable = true\n" }),
|
||||
patchPart("prt_patch"),
|
||||
toolPart("prt_todo", "todowrite", "completed", { todos: [{ content: "Hidden", status: "pending" }] }),
|
||||
toolPart(
|
||||
"prt_question",
|
||||
"question",
|
||||
|
|
@ -65,7 +64,6 @@ test.describe("session timeline projection", () => {
|
|||
]) {
|
||||
await expect(page.locator(`[data-timeline-part-id="${id}"]`).first(), id).toBeVisible()
|
||||
}
|
||||
await expect(page.locator('[data-timeline-part-id="prt_todo"]')).toHaveCount(0)
|
||||
})
|
||||
|
||||
test("projects gaps, dividers, assistant parts, and errors together", async ({ page }) => {
|
||||
|
|
@ -246,7 +244,7 @@ function editPart(id: string) {
|
|||
function patchPart(id: string) {
|
||||
return toolPart(
|
||||
id,
|
||||
"apply_patch",
|
||||
"patch",
|
||||
"completed",
|
||||
{ files: ["src/a.ts", "src/b.ts"] },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "../performance/timeline-stability/fixture"
|
||||
|
||||
test("renders every tool error outcome without leaking hidden tools", async ({ page }) => {
|
||||
const ordinary = ["bash", "edit", "write", "apply_patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
|
||||
const ordinary = ["bash", "edit", "write", "patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
|
||||
const parts = ordinary.map((tool, index) =>
|
||||
toolPart(`prt_error_${index}`, tool, "error", errorInput(tool), { error: `${tool} failed visibly` }),
|
||||
)
|
||||
|
|
@ -17,13 +17,11 @@ test("renders every tool error outcome without leaking hidden tools", async ({ p
|
|||
error: "The user dismissed this question",
|
||||
}),
|
||||
toolPart("prt_question_error", "question", "error", questionInput(), { error: "Question transport failed" }),
|
||||
toolPart("prt_todo_error", "todowrite", "error", { todos: [] }, { error: "Hidden todo failure" }),
|
||||
)
|
||||
await setupTimeline(page, { messages: [userMessage(), assistantMessage(parts)] })
|
||||
|
||||
await expect(page.locator('[data-kind="tool-error-card"]')).toHaveCount(ordinary.length + 1)
|
||||
await expect(page.getByText(/dismissed/i)).toBeVisible()
|
||||
await expect(page.locator('[data-timeline-part-id="prt_todo_error"]')).toHaveCount(0)
|
||||
for (let index = 0; index < ordinary.length; index++) {
|
||||
await expect(page.locator(`[data-timeline-part-id="prt_error_${index}"]`)).toBeVisible()
|
||||
}
|
||||
|
|
@ -90,7 +88,7 @@ function questionInput() {
|
|||
function errorInput(tool: string) {
|
||||
if (tool === "bash") return { command: "exit 1" }
|
||||
if (["edit", "write"].includes(tool)) return { filePath: "src/error.ts", content: "" }
|
||||
if (tool === "apply_patch") return { files: ["src/error.ts"] }
|
||||
if (tool === "patch") return { files: ["src/error.ts"] }
|
||||
if (tool === "webfetch") return { url: "https://example.com" }
|
||||
if (tool === "websearch") return { query: "failure" }
|
||||
if (tool === "task") return { description: "Fail task", subagent_type: "explore" }
|
||||
|
|
|
|||
|
|
@ -1,186 +0,0 @@
|
|||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { expect, test, type Page } from "@playwright/test"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { expectSessionTitle } from "../utils/waits"
|
||||
|
||||
const directory = "C:/OpenCode/TodoDockNavigation"
|
||||
const projectID = "proj_todo_dock_navigation"
|
||||
const sourceID = "ses_todo_dock_source"
|
||||
const otherID = "ses_todo_dock_other"
|
||||
const sourceTitle = "Todo dock animation"
|
||||
const otherTitle = "Separate session"
|
||||
|
||||
const activeTodos = [
|
||||
{ id: "todo-1", content: "Receive todos in the active session", status: "completed", priority: "high" },
|
||||
{ id: "todo-2", content: "Keep the dock visible across tabs", status: "completed", priority: "high" },
|
||||
{ id: "todo-3", content: "Close after the final todo", status: "in_progress", priority: "high" },
|
||||
]
|
||||
|
||||
type EventPayload = {
|
||||
directory: string
|
||||
payload: Record<string, unknown>
|
||||
}
|
||||
|
||||
test.use({ viewport: { width: 1440, height: 900 }, reducedMotion: "no-preference" })
|
||||
|
||||
test("animates todo lifecycle without replaying it across session tabs", async ({ page }) => {
|
||||
test.setTimeout(90_000)
|
||||
const events: EventPayload[] = []
|
||||
const todos: Record<string, typeof activeTodos> = { [sourceID]: [], [otherID]: [] }
|
||||
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
id: projectID,
|
||||
worktree: directory,
|
||||
vcs: "git",
|
||||
name: "todo-dock-navigation",
|
||||
time: { created: 1700000000000, updated: 1700000000000 },
|
||||
sandboxes: [],
|
||||
},
|
||||
provider: {
|
||||
all: [
|
||||
{
|
||||
id: "opencode",
|
||||
name: "OpenCode",
|
||||
models: {
|
||||
"claude-opus-4-6": {
|
||||
id: "claude-opus-4-6",
|
||||
name: "Claude Opus 4.6",
|
||||
limit: { context: 200_000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connected: ["opencode"],
|
||||
default: { providerID: "opencode", modelID: "claude-opus-4-6" },
|
||||
},
|
||||
sessions: [session(sourceID, sourceTitle, 1700000000000), session(otherID, otherTitle, 1700000001000)],
|
||||
pageMessages: () => ({ items: [] }),
|
||||
events: () => events.splice(0, 1),
|
||||
eventRetry: 16,
|
||||
todos: (sessionID) => todos[sessionID] ?? [],
|
||||
})
|
||||
await configurePage(page)
|
||||
|
||||
await page.goto(sessionHref(sourceID))
|
||||
await expectSessionTitle(page, sourceTitle)
|
||||
const dock = page.locator('[data-component="session-todo-dock"]')
|
||||
await expect(dock).toHaveCount(0)
|
||||
|
||||
events.push(statusEvent(sourceID, "busy"))
|
||||
await expect(page.getByRole("button", { name: "Stop" })).toBeVisible()
|
||||
|
||||
await page.waitForTimeout(700)
|
||||
const opening = sampleDock(page, 1_000)
|
||||
todos[sourceID] = activeTodos
|
||||
events.push(todoEvent(sourceID, activeTodos))
|
||||
await expect(dock).toBeVisible()
|
||||
await expect(dock.locator('[data-state="in_progress"]')).toHaveCount(1)
|
||||
expect((await opening).some((sample) => sample.opacity > 0.05 && sample.opacity < 0.95)).toBe(true)
|
||||
|
||||
await switchSession(page, otherID, otherTitle)
|
||||
await expect(dock).toHaveCount(0)
|
||||
|
||||
const returningOpen = sampleDock(page, 700)
|
||||
await switchSession(page, sourceID, sourceTitle)
|
||||
const openSamples = (await returningOpen).filter((sample) => sample.present)
|
||||
expect(openSamples.length).toBeGreaterThan(0)
|
||||
expect(openSamples[0]!.opacity).toBeGreaterThan(0.98)
|
||||
expect(openSamples[0]!.height).toBeGreaterThan(70)
|
||||
await expect(dock.locator('[data-state="in_progress"]')).toHaveCount(1)
|
||||
|
||||
const completedTodos = activeTodos.map((todo) => ({ ...todo, status: "completed" }))
|
||||
const closing = sampleDock(page, 1_000)
|
||||
todos[sourceID] = completedTodos
|
||||
events.push(todoEvent(sourceID, completedTodos))
|
||||
await expect(dock).toHaveCount(0)
|
||||
expect((await closing).some((sample) => sample.opacity > 0.05 && sample.opacity < 0.95)).toBe(true)
|
||||
todos[sourceID] = []
|
||||
events.push(todoEvent(sourceID, []))
|
||||
|
||||
await switchSession(page, otherID, otherTitle)
|
||||
const returningEmpty = sampleDock(page, 700)
|
||||
await switchSession(page, sourceID, sourceTitle)
|
||||
await expect(dock).toHaveCount(0)
|
||||
expect((await returningEmpty).every((sample) => !sample.present)).toBe(true)
|
||||
})
|
||||
|
||||
function session(id: string, title: string, created: number) {
|
||||
return {
|
||||
id,
|
||||
slug: id,
|
||||
projectID,
|
||||
directory,
|
||||
title,
|
||||
version: "dev",
|
||||
time: { created, updated: created },
|
||||
}
|
||||
}
|
||||
|
||||
function statusEvent(sessionID: string, type: "busy" | "idle"): EventPayload {
|
||||
return {
|
||||
directory,
|
||||
payload: { type: "session.status", properties: { sessionID, status: { type } } },
|
||||
}
|
||||
}
|
||||
|
||||
function todoEvent(sessionID: string, next: typeof activeTodos): EventPayload {
|
||||
return {
|
||||
directory,
|
||||
payload: { type: "todo.updated", properties: { sessionID, todos: next } },
|
||||
}
|
||||
}
|
||||
|
||||
async function configurePage(page: Page) {
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
await page.addInitScript(
|
||||
({ directory, dirBase64, server, sessionIDs }) => {
|
||||
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
|
||||
localStorage.setItem(
|
||||
"opencode.global.dat:server",
|
||||
JSON.stringify({
|
||||
projects: { local: [{ worktree: directory, expanded: true }] },
|
||||
lastProject: { local: directory },
|
||||
}),
|
||||
)
|
||||
localStorage.setItem(
|
||||
"opencode.window.browser.dat:tabs",
|
||||
JSON.stringify(sessionIDs.map((sessionId) => ({ type: "session", server, dirBase64, sessionId }))),
|
||||
)
|
||||
},
|
||||
{ directory, dirBase64: base64Encode(directory), server, sessionIDs: [sourceID, otherID] },
|
||||
)
|
||||
}
|
||||
|
||||
function sessionHref(sessionID: string) {
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
return `/server/${base64Encode(server)}/session/${sessionID}`
|
||||
}
|
||||
|
||||
async function switchSession(page: Page, sessionID: string, title: string) {
|
||||
const href = sessionHref(sessionID)
|
||||
const tab = page.locator(`[data-slot="titlebar-tabs"] a[href="${href}"]`).first()
|
||||
await expect(tab).toBeVisible()
|
||||
await tab.click()
|
||||
await expectSessionTitle(page, title)
|
||||
}
|
||||
|
||||
function sampleDock(page: Page, duration: number) {
|
||||
return page.evaluate(async (duration) => {
|
||||
const samples: { present: boolean; height: number; opacity: number }[] = []
|
||||
const start = performance.now()
|
||||
while (performance.now() - start < duration) {
|
||||
const dock = document.querySelector<HTMLElement>('[data-component="session-todo-dock"]')
|
||||
const clip = dock?.parentElement?.parentElement
|
||||
const label = dock?.querySelector<HTMLElement>('[data-action="session-todo-toggle"] span[aria-label]')
|
||||
samples.push({
|
||||
present: !!dock,
|
||||
height: clip?.getBoundingClientRect().height ?? 0,
|
||||
opacity: label ? Number.parseFloat(getComputedStyle(label).opacity) : 0,
|
||||
})
|
||||
await new Promise(requestAnimationFrame)
|
||||
}
|
||||
return samples
|
||||
}, duration)
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ async function mockServer(page: Page) {
|
|||
if (byId) return json(route, byId)
|
||||
if (/^\/session\/[^/]+$/.test(url.pathname)) return json(route, { name: "NotFoundError" }, 404)
|
||||
if (/^\/session\/[^/]+\/message$/.test(url.pathname)) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|todo|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (/^\/session\/[^/]+\/(children|diff)$/.test(url.pathname)) return json(route, [])
|
||||
if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/diff"].includes(url.pathname))
|
||||
return json(route, [])
|
||||
if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(url.pathname))
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ function toolPart(
|
|||
outputLength = 160,
|
||||
): MessagePart {
|
||||
const metadata =
|
||||
tool === "apply_patch"
|
||||
tool === "patch"
|
||||
? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
|
||||
: tool === "edit" || tool === "write"
|
||||
? {
|
||||
|
|
@ -199,7 +199,7 @@ function turn(index: number): Message[] {
|
|||
? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
|
||||
: []),
|
||||
...(index % 8 === 0
|
||||
? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
|
||||
? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
|
||||
: []),
|
||||
...(index % 7 === 0 ? [toolPart(index, 4, "bash", { command: "bun typecheck" }, 620)] : []),
|
||||
...(index % 10 === 0 ? [toolPart(index, 9, "webfetch", { url: "https://example.com/docs/sample" }, 120)] : []),
|
||||
|
|
@ -229,7 +229,6 @@ const sourceMessages = Array.from({ length: 12 }, (_, index) => [
|
|||
]).flat()
|
||||
|
||||
function renderable(part: MessagePart) {
|
||||
if (part.type === "tool" && part.tool === "todowrite") return false
|
||||
if (part.type === "text") return !!part.text.trim()
|
||||
if (part.type === "reasoning") return !!part.text.trim()
|
||||
return part.type !== "step-start" && part.type !== "step-finish" && part.type !== "patch"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ export interface MockServerConfig {
|
|||
onMessage?: (input: { sessionID: string; messageID: string }) => void
|
||||
events?: () => unknown[]
|
||||
eventRetry?: number
|
||||
todos?: (sessionID: string) => unknown[]
|
||||
permissions?: unknown[] | (() => unknown[])
|
||||
questions?: unknown[] | (() => unknown[])
|
||||
fileList?: (path: string) => unknown | Promise<unknown>
|
||||
|
|
@ -55,7 +54,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
|
|||
const path = url.pathname
|
||||
if (path === "/global/event" || path === "/event") return sse(route, config.events?.(), config.eventRetry)
|
||||
if (path === "/global/health") return json(route, { healthy: true })
|
||||
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: false })
|
||||
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: true })
|
||||
if (path === "/permission")
|
||||
return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
|
||||
if (path === "/question")
|
||||
|
|
@ -96,8 +95,6 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
|
|||
return json(route, message)
|
||||
}
|
||||
|
||||
const todoMatch = path.match(/^\/session\/([^/]+)\/todo$/)
|
||||
if (todoMatch) return json(route, config.todos?.(todoMatch[1]!) ?? [])
|
||||
if (/^\/session\/[^/]+\/(children|diff)$/.test(path)) return json(route, [])
|
||||
|
||||
const messagesMatch = path.match(/^\/session\/([^/]+)\/message$/)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { Font } from "@opencode-ai/ui/font"
|
|||
import { Splash } from "@opencode-ai/ui/logo"
|
||||
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, Navigate, Route, Router, useNavigate, useParams, useSearchParams } from "@solidjs/router"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
|
||||
import { Effect } from "effect"
|
||||
import {
|
||||
|
|
@ -32,7 +32,7 @@ import { CommandProvider, useCommand, type CommandOption } from "@/context/comma
|
|||
import { CommentsProvider } from "@/context/comments"
|
||||
import { FileProvider } from "@/context/file"
|
||||
import { ServerSDKProvider } from "@/context/server-sdk"
|
||||
import { ServerSyncProvider } from "@/context/server-sync"
|
||||
import { ServerSyncProvider, useServerSync } from "@/context/server-sync"
|
||||
import { GlobalProvider, useGlobal } from "@/context/global"
|
||||
import { HighlightsProvider } from "@/context/highlights"
|
||||
import { LanguageProvider, type Locale, useLanguage } from "@/context/language"
|
||||
|
|
@ -52,9 +52,10 @@ import LegacyLayout from "@/pages/layout"
|
|||
import NewLayout from "@/pages/layout-new"
|
||||
import { ErrorPage } from "./pages/error"
|
||||
import { useCheckServerHealth } from "./utils/server-health"
|
||||
import { legacySessionServer, requireServerKey, sessionHref } from "./utils/session-route"
|
||||
import { legacySessionHref, legacySessionServer, requireServerKey, sessionHref } from "./utils/session-route"
|
||||
import { createSessionLineage } from "@/pages/session/session-lineage"
|
||||
|
||||
import { SessionPage, TargetSessionRouteContent } from "@/pages/session"
|
||||
import { SessionPage, SessionRouteErrorBoundary, TargetSessionRouteContent } from "@/pages/session"
|
||||
import { NewHome, LegacyHome } from "@/pages/home"
|
||||
|
||||
const NewSession = lazy(() => import("@/pages/new-session"))
|
||||
|
|
@ -88,10 +89,14 @@ const SessionRoute = () => {
|
|||
tabs.newDraft({ server: server.key, directory: sdk().directory }, search.prompt)
|
||||
})
|
||||
|
||||
return <SessionPage />
|
||||
return (
|
||||
<SessionRouteErrorBoundary sessionID={params.id}>
|
||||
<SessionPage />
|
||||
</SessionRouteErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
const TargetSessionRoute = () => {
|
||||
function TargetServerRoute(props: ParentProps) {
|
||||
const params = useParams<{ serverKey: string; id: string }>()
|
||||
const global = useGlobal()
|
||||
const conn = createMemo(() => {
|
||||
|
|
@ -105,14 +110,47 @@ const TargetSessionRoute = () => {
|
|||
// re-resolves reactively instead); both rely on this key for server changes.
|
||||
<Show when={requireServerKey(params.serverKey)} keyed>
|
||||
<ServerSDKProvider server={conn}>
|
||||
<ServerSyncProvider server={conn}>
|
||||
<TargetSessionRouteContent />
|
||||
</ServerSyncProvider>
|
||||
<ServerSyncProvider server={conn}>{props.children}</ServerSyncProvider>
|
||||
</ServerSDKProvider>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
const TargetSessionRoute = () => (
|
||||
<TargetServerRoute>
|
||||
<TargetSessionRouteContent />
|
||||
</TargetServerRoute>
|
||||
)
|
||||
|
||||
function LegacyTargetSessionRoute() {
|
||||
const params = useParams<{ serverKey: string; id: string }>()
|
||||
return (
|
||||
<TargetServerRoute>
|
||||
<SessionRouteErrorBoundary sessionID={params.id} serverKey={requireServerKey(params.serverKey)}>
|
||||
<LegacyTargetSessionRedirect />
|
||||
</SessionRouteErrorBoundary>
|
||||
</TargetServerRoute>
|
||||
)
|
||||
}
|
||||
|
||||
function LegacyTargetSessionRedirect() {
|
||||
const params = useParams<{ id: string }>()
|
||||
const navigate = useNavigate()
|
||||
const sync = useServerSync()
|
||||
const current = createSessionLineage(
|
||||
() => params.id,
|
||||
() => sync().session.lineage,
|
||||
)
|
||||
|
||||
createEffect(() => {
|
||||
const directory = current()?.session.directory
|
||||
if (!directory) return
|
||||
navigate(legacySessionHref(directory, params.id), { replace: true })
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// Wraps the non-draft routes. They are gated on (and keyed to) the globally selected
|
||||
// server via ServerKey, then provide the server-scoped shell (Permission/Layout/
|
||||
// Notification/Models + the visual Layout) for that server.
|
||||
|
|
@ -186,7 +224,7 @@ declare global {
|
|||
deepLinks?: string[]
|
||||
}
|
||||
api?: {
|
||||
setTitlebar?: (theme: { mode: "light" | "dark" }) => Promise<void>
|
||||
setTitlebar?: (theme: { mode: "light" | "dark"; scheme?: "system" | "light" | "dark" }) => Promise<void>
|
||||
exportDebugLogs?: () => Promise<string>
|
||||
}
|
||||
}
|
||||
|
|
@ -320,8 +358,8 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
|
|||
<MetaProvider>
|
||||
<Font />
|
||||
<ThemeProvider
|
||||
onThemeApplied={(_, mode) => {
|
||||
void window.api?.setTitlebar?.({ mode })
|
||||
onThemeApplied={(_, mode, scheme) => {
|
||||
void window.api?.setTitlebar?.({ mode, scheme })
|
||||
}}
|
||||
>
|
||||
<LanguageProvider locale={props.locale}>
|
||||
|
|
@ -542,7 +580,14 @@ function Routes(props: { serverScoped?: JSX.Element }) {
|
|||
<LegacyServerLayout serverScoped={props.serverScoped}>{routeProps.children}</LegacyServerLayout>
|
||||
)}
|
||||
>
|
||||
<Show when={!settings.general.newLayoutDesigns()}>{<Route path="/" component={LegacyHome} />}</Show>
|
||||
<Show when={!settings.general.newLayoutDesigns()}>
|
||||
{
|
||||
<>
|
||||
<Route path="/" component={LegacyHome} />
|
||||
<Route path="/server/:serverKey/session/:id" component={LegacyTargetSessionRoute} />
|
||||
</>
|
||||
}
|
||||
</Show>
|
||||
<Route path="/:dir" component={DirectoryLayout}>
|
||||
<Route path="/" component={() => <Navigate href="session" />} />
|
||||
<Route path="/session/:id?" component={SessionRoute} />
|
||||
|
|
@ -550,15 +595,15 @@ function Routes(props: { serverScoped?: JSX.Element }) {
|
|||
</Route>
|
||||
<Show when={settings.general.newLayoutDesigns()}>
|
||||
<Route path="/" component={NewHome} />
|
||||
<Route path="/:dir/session/:id" component={LegacyTargetSessionRoute} />
|
||||
<Route path="/:dir/session/:id" component={NewLayoutLegacySessionRedirect} />
|
||||
<Route path="/server/:serverKey/session/:id" component={TargetSessionRoute} />
|
||||
</Show>
|
||||
<Route path="/new-session" component={DraftRoute} />
|
||||
<Route path="/server/:serverKey/session/:id" component={TargetSessionRoute} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function LegacyTargetSessionRoute() {
|
||||
function NewLayoutLegacySessionRedirect() {
|
||||
const server = useServer()
|
||||
const tabs = useTabs()
|
||||
const params = useParams<{ id: string }>()
|
||||
|
|
|
|||
325
packages/app/src/components/command-palette.ts
Normal file
325
packages/app/src/components/command-palette.ts
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { getFilename } from "@opencode-ai/core/util/path"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { createMemo, onCleanup } from "solid-js"
|
||||
import { useCommand, type CommandOption } from "@/context/command"
|
||||
import { useFile } from "@/context/file"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useLayout } from "@/context/layout"
|
||||
import { useServerSDK, type ServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { createSessionTabs } from "@/pages/session/helpers"
|
||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
|
||||
export type CommandPaletteEntry = {
|
||||
id: string
|
||||
type: "command" | "file" | "session"
|
||||
title: string
|
||||
description?: string
|
||||
keybind?: string
|
||||
category: string
|
||||
option?: CommandOption
|
||||
path?: string
|
||||
directory?: string
|
||||
sessionID?: string
|
||||
archived?: number
|
||||
updated?: number
|
||||
}
|
||||
|
||||
const ENTRY_LIMIT = 5
|
||||
const COMMON_COMMAND_IDS = [
|
||||
"session.new",
|
||||
"workspace.new",
|
||||
"session.previous",
|
||||
"session.next",
|
||||
"terminal.toggle",
|
||||
"review.toggle",
|
||||
] as const
|
||||
|
||||
export function uniqueCommandPaletteEntries(items: CommandPaletteEntry[]) {
|
||||
const seen = new Set<string>()
|
||||
return items.filter((item) => {
|
||||
if (seen.has(item.id)) return false
|
||||
seen.add(item.id)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
export function createCommandPaletteFileEntry(path: string, category: string): CommandPaletteEntry {
|
||||
return {
|
||||
id: "file:" + path,
|
||||
type: "file",
|
||||
title: path,
|
||||
category,
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
export function createCommandPaletteFileOpener(onOpenFile?: (path: string) => void) {
|
||||
const file = useFile()
|
||||
const layout = useLayout()
|
||||
const { tabs, view } = useSessionLayout()
|
||||
|
||||
return (path: string) => {
|
||||
const value = file.tab(path)
|
||||
void tabs().open(value)
|
||||
void file.load(path)
|
||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
||||
layout.fileTree.setTab("all")
|
||||
onOpenFile?.(path)
|
||||
tabs().setActive(value)
|
||||
}
|
||||
}
|
||||
|
||||
export function createCommandPaletteModel(props: { filesOnly?: () => boolean; onOpenFile?: (path: string) => void }) {
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
const layout = useLayout()
|
||||
const file = useFile()
|
||||
const dialog = useDialog()
|
||||
const navigate = useNavigate()
|
||||
const serverSDK = useServerSDK()()
|
||||
const serverSync = useServerSync()
|
||||
const { params, tabs } = useSessionLayout()
|
||||
const openFile = createCommandPaletteFileOpener(props.onOpenFile)
|
||||
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||
const filesOnly = () => props.filesOnly?.() ?? false
|
||||
|
||||
const allowedCommands = createMemo(() => {
|
||||
if (filesOnly()) return []
|
||||
return command.options.filter(
|
||||
(option) =>
|
||||
!option.disabled && !option.hidden && !option.id.startsWith("suggested.") && option.id !== "file.open",
|
||||
)
|
||||
})
|
||||
const commandEntries = createMemo(() => {
|
||||
const category = language.t("palette.group.commands")
|
||||
return allowedCommands().map((option) => createCommandEntry(option, category))
|
||||
})
|
||||
const preferredCommandEntries = createMemo(() => {
|
||||
const all = allowedCommands()
|
||||
const order = new Map<string, number>(COMMON_COMMAND_IDS.map((id, index) => [id, index]))
|
||||
const picked = all.filter((option) => order.has(option.id))
|
||||
const base = picked.length ? picked : all.slice(0, ENTRY_LIMIT)
|
||||
const sorted = picked.length ? [...base].sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)) : base
|
||||
const category = language.t("palette.group.commands")
|
||||
return sorted.map((option) => createCommandEntry(option, category))
|
||||
})
|
||||
|
||||
const tabState = createSessionTabs({
|
||||
tabs,
|
||||
pathFromTab: file.pathFromTab,
|
||||
normalizeTab: (tab) => (tab.startsWith("file://") ? file.tab(tab) : tab),
|
||||
})
|
||||
const recentFileEntries = createMemo(() => {
|
||||
const all = tabState.openedTabs()
|
||||
const active = tabState.activeFileTab()
|
||||
const order = active ? [active, ...all.filter((item) => item !== active)] : all
|
||||
const seen = new Set<string>()
|
||||
const category = language.t("palette.group.files")
|
||||
return order
|
||||
.map((item) => file.pathFromTab(item))
|
||||
.filter((path): path is string => {
|
||||
if (!path || seen.has(path)) return false
|
||||
seen.add(path)
|
||||
return true
|
||||
})
|
||||
.slice(0, ENTRY_LIMIT)
|
||||
.map((path) => createCommandPaletteFileEntry(path, category))
|
||||
})
|
||||
const rootFileEntries = createMemo(() => {
|
||||
const category = language.t("palette.group.files")
|
||||
return file.tree
|
||||
.children("")
|
||||
.filter((node) => node.type === "file")
|
||||
.map((node) => node.path)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.slice(0, ENTRY_LIMIT)
|
||||
.map((path) => createCommandPaletteFileEntry(path, category))
|
||||
})
|
||||
|
||||
const projectDirectory = createMemo(() => decode64(params.dir) ?? "")
|
||||
const project = createMemo(() => {
|
||||
const directory = projectDirectory()
|
||||
if (!directory) return undefined
|
||||
return layout.projects.list().find((item) => item.worktree === directory || item.sandboxes?.includes(directory))
|
||||
})
|
||||
const workspaces = createMemo(() => {
|
||||
const directory = projectDirectory()
|
||||
const current = project()
|
||||
if (!current) return directory ? [directory] : []
|
||||
const dirs = [current.worktree, ...(current.sandboxes ?? [])]
|
||||
if (directory && !dirs.includes(directory)) return [...dirs, directory]
|
||||
return dirs
|
||||
})
|
||||
const homedir = createMemo(() => serverSync().data.path.home)
|
||||
const sessions = createSessionEntries({
|
||||
workspaces,
|
||||
label: (directory) => {
|
||||
const current = project()
|
||||
const kind =
|
||||
current && directory === current.worktree
|
||||
? language.t("workspace.type.local")
|
||||
: language.t("workspace.type.sandbox")
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
const home = homedir()
|
||||
const path = home ? directory.replace(home, "~") : directory
|
||||
const name = store.vcs?.branch ?? getFilename(directory)
|
||||
return `${kind} : ${name || path}`
|
||||
},
|
||||
load: (directory) => serverSDK.client.session.list({ directory, roots: true }),
|
||||
untitled: () => language.t("command.session.new"),
|
||||
category: () => language.t("command.category.session"),
|
||||
})
|
||||
|
||||
const highlight = (item: CommandPaletteEntry | undefined) => {
|
||||
state.cleanup?.()
|
||||
state.cleanup = undefined
|
||||
if (item?.type !== "command") return
|
||||
state.cleanup = item.option?.onHighlight?.()
|
||||
}
|
||||
|
||||
const select = (item: CommandPaletteEntry | undefined) => {
|
||||
if (!item) return
|
||||
state.committed = true
|
||||
state.cleanup = undefined
|
||||
dialog.close()
|
||||
if (item.type === "command") {
|
||||
item.option?.onSelect?.("palette")
|
||||
return
|
||||
}
|
||||
if (item.type === "session") {
|
||||
if (!item.directory || !item.sessionID) return
|
||||
navigate(`/${base64Encode(item.directory)}/session/${item.sessionID}`)
|
||||
return
|
||||
}
|
||||
if (!item.path) return
|
||||
openFile(item.path)
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
if (state.committed) return
|
||||
state.cleanup?.()
|
||||
})
|
||||
|
||||
return {
|
||||
language,
|
||||
file,
|
||||
commandEntries,
|
||||
preferredCommandEntries,
|
||||
recentFileEntries,
|
||||
rootFileEntries,
|
||||
sessions,
|
||||
highlight,
|
||||
select,
|
||||
close: () => dialog.close(),
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandEntry(option: CommandOption, category: string): CommandPaletteEntry {
|
||||
return {
|
||||
id: "command:" + option.id,
|
||||
type: "command",
|
||||
title: option.title,
|
||||
description: option.description,
|
||||
keybind: option.keybind,
|
||||
category,
|
||||
option,
|
||||
}
|
||||
}
|
||||
|
||||
function createSessionEntries(props: {
|
||||
workspaces: () => string[]
|
||||
label: (directory: string) => string
|
||||
load: (directory: string) => ReturnType<ServerSDK["client"]["session"]["list"]>
|
||||
untitled: () => string
|
||||
category: () => string
|
||||
}) {
|
||||
const state: {
|
||||
token: number
|
||||
inflight: Promise<CommandPaletteEntry[]> | undefined
|
||||
cached: CommandPaletteEntry[] | undefined
|
||||
} = { token: 0, inflight: undefined, cached: undefined }
|
||||
|
||||
return (text: string) => {
|
||||
if (!text.trim()) {
|
||||
state.token += 1
|
||||
state.inflight = undefined
|
||||
state.cached = undefined
|
||||
return [] as CommandPaletteEntry[]
|
||||
}
|
||||
if (state.cached) return state.cached
|
||||
if (state.inflight) return state.inflight
|
||||
|
||||
const current = state.token
|
||||
const dirs = props.workspaces()
|
||||
if (dirs.length === 0) return [] as CommandPaletteEntry[]
|
||||
|
||||
state.inflight = Promise.all(
|
||||
dirs.map((directory) => {
|
||||
const description = props.label(directory)
|
||||
return props
|
||||
.load(directory)
|
||||
.then((result) =>
|
||||
(result.data ?? [])
|
||||
.filter((session) => !!session?.id)
|
||||
.map((session) => ({
|
||||
id: session.id,
|
||||
title: session.title ?? props.untitled(),
|
||||
description,
|
||||
directory,
|
||||
archived: session.time?.archived,
|
||||
updated: session.time?.updated,
|
||||
})),
|
||||
)
|
||||
.catch(() => [] as SessionEntryInput[])
|
||||
}),
|
||||
)
|
||||
.then((results) => {
|
||||
if (state.token !== current) return [] as CommandPaletteEntry[]
|
||||
const seen = new Set<string>()
|
||||
const next = results
|
||||
.flat()
|
||||
.filter((item) => {
|
||||
const key = `${item.directory}:${item.id}`
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
.map((item) => createSessionEntry(item, props.category()))
|
||||
state.cached = next
|
||||
return next
|
||||
})
|
||||
.catch(() => [] as CommandPaletteEntry[])
|
||||
.finally(() => {
|
||||
state.inflight = undefined
|
||||
})
|
||||
|
||||
return state.inflight
|
||||
}
|
||||
}
|
||||
|
||||
type SessionEntryInput = {
|
||||
directory: string
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
archived?: number
|
||||
updated?: number
|
||||
}
|
||||
|
||||
function createSessionEntry(input: SessionEntryInput, category: string): CommandPaletteEntry {
|
||||
return {
|
||||
id: `session:${input.directory}:${input.id}`,
|
||||
type: "session",
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
category,
|
||||
directory: input.directory,
|
||||
sessionID: input.id,
|
||||
archived: input.archived,
|
||||
updated: input.updated,
|
||||
}
|
||||
}
|
||||
220
packages/app/src/components/dialog-command-palette-v2.css
Normal file
220
packages/app/src/components/dialog-command-palette-v2.css
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
.command-palette-v2 {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Anchor to the top edge of where a centered 480px-tall dialog would sit, so the
|
||||
top stays put while the content-driven height grows and shrinks. */
|
||||
[data-component="dialog-v2"]:has(.command-palette-v2) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
[data-component="dialog-v2"]:has(.command-palette-v2) [data-slot="dialog-container"] {
|
||||
width: min(calc(100vw - 24px), 640px);
|
||||
height: auto;
|
||||
min-height: 280px;
|
||||
max-height: min(calc(100vh - 96px), 480px);
|
||||
margin-top: max(48px, calc((100vh - 480px) / 2));
|
||||
border-radius: 12px;
|
||||
background: var(--v2-background-bg-base);
|
||||
box-shadow: var(--v2-elevation-floating);
|
||||
}
|
||||
|
||||
.command-palette-v2-body {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.command-palette-v2-search {
|
||||
flex-shrink: 0;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.command-palette-v2-search [data-component="text-input-v2"] {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
outline: 0;
|
||||
background: color-mix(in srgb, var(--v2-background-bg-layer-02) 60%, transparent);
|
||||
box-shadow: none;
|
||||
transition:
|
||||
background-color 120ms ease-in-out,
|
||||
box-shadow 120ms ease-in-out;
|
||||
}
|
||||
|
||||
.command-palette-v2-search [data-component="text-input-v2"]:where(:hover):not([data-disabled], [data-invalid]),
|
||||
.command-palette-v2-search [data-component="text-input-v2"]:where(:focus-within):not([data-disabled], [data-invalid]) {
|
||||
background: var(--v2-background-bg-layer-02);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.command-palette-v2-search [data-component="text-input-v2"] [data-slot="text-input-v2-value"] {
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.command-palette-v2-search [data-component="text-input-v2"] [data-slot="text-input-v2-leading-icon"] {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.command-palette-v2-search [data-component="text-input-v2"] [data-slot="text-input-v2-input"] {
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.command-palette-v2-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.command-palette-v2-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 6px 6px 8px;
|
||||
}
|
||||
|
||||
.command-palette-v2-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.command-palette-v2-group-title {
|
||||
margin: 6px 0;
|
||||
padding: 0 12px;
|
||||
color: var(--v2-text-text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.command-palette-v2-row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--v2-text-text-base);
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
scroll-margin: 6px 0;
|
||||
}
|
||||
|
||||
.command-palette-v2-row[data-active] {
|
||||
background: var(--v2-overlay-simple-overlay-hover);
|
||||
}
|
||||
|
||||
.command-palette-v2-row:focus-visible {
|
||||
background: var(--v2-overlay-simple-overlay-hover);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.command-palette-v2-row-main {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.command-palette-v2-row-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--v2-icon-icon-muted);
|
||||
}
|
||||
|
||||
.command-palette-v2-row-text {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.command-palette-v2-title {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: var(--v2-text-text-base);
|
||||
font-size: 13px;
|
||||
font-weight: 530;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.command-palette-v2-description,
|
||||
.command-palette-v2-meta {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: var(--v2-text-text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.command-palette-v2-meta {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.command-palette-v2-file-path {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: baseline;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
}
|
||||
|
||||
.command-palette-v2-file-dir {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: var(--v2-text-text-muted);
|
||||
font-weight: 440;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.command-palette-v2-file-name {
|
||||
flex-shrink: 0;
|
||||
color: var(--v2-text-text-base);
|
||||
font-weight: 530;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.command-palette-v2-state {
|
||||
display: grid;
|
||||
min-height: 120px;
|
||||
place-items: center;
|
||||
color: var(--v2-text-text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.04px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.command-palette-v2-row-text {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.command-palette-v2-description {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
221
packages/app/src/components/dialog-command-palette-v2.tsx
Normal file
221
packages/app/src/components/dialog-command-palette-v2.tsx
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
||||
import { Dialog, DialogBody } from "@opencode-ai/ui/v2/dialog-v2"
|
||||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { createEffect, createMemo, createResource, createSignal, For, Match, Show, Switch } from "solid-js"
|
||||
import { formatKeybindParts } from "@/context/command"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { getRelativeTime } from "@/utils/time"
|
||||
import {
|
||||
createCommandPaletteFileEntry,
|
||||
createCommandPaletteModel,
|
||||
uniqueCommandPaletteEntries,
|
||||
type CommandPaletteEntry,
|
||||
} from "./command-palette"
|
||||
import "./dialog-command-palette-v2.css"
|
||||
|
||||
function groups(entries: CommandPaletteEntry[]) {
|
||||
const map = new Map<string, CommandPaletteEntry[]>()
|
||||
for (const entry of entries) map.set(entry.category, [...(map.get(entry.category) ?? []), entry])
|
||||
return Array.from(map.entries()).map(([category, entries]) => ({ category, entries }))
|
||||
}
|
||||
|
||||
function matchesEntry(entry: CommandPaletteEntry, query: string) {
|
||||
const value = query.toLowerCase()
|
||||
return [entry.title, entry.description, entry.category].some((text) => text?.toLowerCase().includes(value))
|
||||
}
|
||||
|
||||
export function DialogCommandPaletteV2(props: { onOpenFile?: (path: string) => void }) {
|
||||
const palette = createCommandPaletteModel(props)
|
||||
const [query, setQuery] = createSignal("")
|
||||
const [active, setActive] = createSignal(0)
|
||||
|
||||
const loadItems = async (text: string) => {
|
||||
const q = text.trim()
|
||||
if (!q) return [...palette.preferredCommandEntries(), ...palette.recentFileEntries()]
|
||||
|
||||
const [files, nextSessions] = await Promise.all([palette.file.searchFiles(q), Promise.resolve(palette.sessions(q))])
|
||||
const category = palette.language.t("palette.group.files")
|
||||
return [
|
||||
...palette.commandEntries().filter((entry) => matchesEntry(entry, q)),
|
||||
...nextSessions.filter((entry) => matchesEntry(entry, q)),
|
||||
...files.map((path) => createCommandPaletteFileEntry(path, category)),
|
||||
]
|
||||
}
|
||||
|
||||
const [entries] = createResource(query, loadItems, { initialValue: [] as CommandPaletteEntry[] })
|
||||
// Render stale results while a new query loads to avoid flashing "Loading" per keystroke.
|
||||
const visibleEntries = createMemo(() => uniqueCommandPaletteEntries(entries.latest ?? []))
|
||||
const groupedEntries = createMemo(() => groups(visibleEntries()))
|
||||
const activeEntry = createMemo(() => visibleEntries()[active()])
|
||||
|
||||
createEffect(() => {
|
||||
query()
|
||||
visibleEntries()
|
||||
setActive(0)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
palette.highlight(activeEntry())
|
||||
})
|
||||
|
||||
let resultsRef: HTMLDivElement | undefined
|
||||
|
||||
const move = (delta: -1 | 1) => {
|
||||
const count = visibleEntries().length
|
||||
if (count === 0) return
|
||||
setActive((index) => (index + delta + count) % count)
|
||||
requestAnimationFrame(() => {
|
||||
resultsRef?.querySelector("[data-active]")?.scrollIntoView({ block: "nearest" })
|
||||
})
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault()
|
||||
move(1)
|
||||
return
|
||||
}
|
||||
if (event.key === "ArrowUp") {
|
||||
event.preventDefault()
|
||||
move(-1)
|
||||
return
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
palette.select(activeEntry())
|
||||
return
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault()
|
||||
palette.close()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog class="command-palette-v2" size="large">
|
||||
<DialogBody class="command-palette-v2-body">
|
||||
<div class="command-palette-v2-search">
|
||||
<TextInputV2
|
||||
value={query()}
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
spellcheck={false}
|
||||
appearance="large"
|
||||
placeholder={palette.language.t("palette.search.placeholder")}
|
||||
leadingIcon={<Icon name="magnifying-glass" />}
|
||||
onInput={(event) => setQuery(event.currentTarget.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
<ScrollView class="command-palette-v2-scroll" viewportRef={(el) => (resultsRef = el)}>
|
||||
<div class="command-palette-v2-results" role="listbox">
|
||||
<Show
|
||||
when={visibleEntries().length > 0}
|
||||
fallback={
|
||||
<div class="command-palette-v2-state">
|
||||
{entries.loading ? palette.language.t("common.loading") : palette.language.t("palette.empty")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<For each={groupedEntries()}>
|
||||
{(group) => (
|
||||
<div class="command-palette-v2-group">
|
||||
<Show when={group.category}>
|
||||
<div class="command-palette-v2-group-title">{group.category}</div>
|
||||
</Show>
|
||||
<For each={group.entries}>
|
||||
{(item) => (
|
||||
<PaletteRow
|
||||
item={item}
|
||||
active={activeEntry()?.id === item.id}
|
||||
language={palette.language}
|
||||
onActive={() => setActive(visibleEntries().findIndex((entry) => entry.id === item.id))}
|
||||
onSelect={() => palette.select(item)}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
</ScrollView>
|
||||
</DialogBody>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function PaletteRow(props: {
|
||||
item: CommandPaletteEntry
|
||||
active: boolean
|
||||
language: ReturnType<typeof useLanguage>
|
||||
onActive: () => void
|
||||
onSelect: () => void
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class="command-palette-v2-row"
|
||||
role="option"
|
||||
aria-selected={props.active}
|
||||
data-active={props.active ? "" : undefined}
|
||||
onMouseMove={(event) => {
|
||||
// Ignore hover from a static cursor when keyboard scrolling moves rows underneath it.
|
||||
if (event.movementX === 0 && event.movementY === 0) return
|
||||
props.onActive()
|
||||
}}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={props.onSelect}
|
||||
>
|
||||
<Switch
|
||||
fallback={
|
||||
<div class="command-palette-v2-row-main">
|
||||
<FileIcon node={{ path: props.item.path ?? "", type: "file" }} class="command-palette-v2-row-icon size-4" />
|
||||
<div class="command-palette-v2-file-path">
|
||||
<span class="command-palette-v2-file-dir">{getDirectory(props.item.path ?? "")}</span>
|
||||
<span class="command-palette-v2-file-name">{getFilename(props.item.path ?? "")}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Match when={props.item.type === "command"}>
|
||||
<div class="command-palette-v2-row-main">
|
||||
<div class="command-palette-v2-row-text">
|
||||
<span class="command-palette-v2-title">{props.item.title}</span>
|
||||
<Show when={props.item.description}>
|
||||
<span class="command-palette-v2-description">{props.item.description}</span>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.item.keybind}>
|
||||
<KeybindV2 keys={formatKeybindParts(props.item.keybind ?? "", props.language.t)} variant="neutral" />
|
||||
</Show>
|
||||
</Match>
|
||||
<Match when={props.item.type === "session"}>
|
||||
<div class="command-palette-v2-row-main">
|
||||
<Icon name="status" class="command-palette-v2-row-icon" />
|
||||
<div class="command-palette-v2-row-text">
|
||||
<span class="command-palette-v2-title" classList={{ "opacity-70": !!props.item.archived }}>
|
||||
{props.item.title}
|
||||
</span>
|
||||
<Show when={props.item.description}>
|
||||
<span class="command-palette-v2-description" classList={{ "opacity-70": !!props.item.archived }}>
|
||||
{props.item.description}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={props.item.updated}>
|
||||
<span class="command-palette-v2-meta">
|
||||
{getRelativeTime(new Date(props.item.updated!).toISOString(), props.language.t)}
|
||||
</span>
|
||||
</Show>
|
||||
</Match>
|
||||
</Switch>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,329 +1,84 @@
|
|||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||
import { List } from "@opencode-ai/ui/list"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { createMemo, createSignal, lazy, Match, onCleanup, Show, Switch } from "solid-js"
|
||||
import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
|
||||
import { useServerSDK, type ServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLayout } from "@/context/layout"
|
||||
import { useFile } from "@/context/file"
|
||||
import { createMemo, createSignal, lazy, Match, Show, Switch } from "solid-js"
|
||||
import { formatKeybind } from "@/context/command"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||
import { createSessionTabs } from "@/pages/session/helpers"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { getRelativeTime } from "@/utils/time"
|
||||
import {
|
||||
createCommandPaletteFileEntry,
|
||||
createCommandPaletteFileOpener,
|
||||
createCommandPaletteModel,
|
||||
uniqueCommandPaletteEntries,
|
||||
type CommandPaletteEntry,
|
||||
} from "./command-palette"
|
||||
|
||||
const DialogSelectFileV2 = lazy(() =>
|
||||
import("./dialog-select-directory-v2").then((module) => ({ default: module.DialogSelectDirectoryV2 })),
|
||||
)
|
||||
|
||||
type EntryType = "command" | "file" | "session"
|
||||
|
||||
type Entry = {
|
||||
id: string
|
||||
type: EntryType
|
||||
title: string
|
||||
description?: string
|
||||
keybind?: string
|
||||
category: string
|
||||
option?: CommandOption
|
||||
path?: string
|
||||
directory?: string
|
||||
sessionID?: string
|
||||
archived?: number
|
||||
updated?: number
|
||||
}
|
||||
const DialogCommandPaletteV2 = lazy(() =>
|
||||
import("./dialog-command-palette-v2").then((module) => ({ default: module.DialogCommandPaletteV2 })),
|
||||
)
|
||||
|
||||
type DialogSelectFileMode = "all" | "files"
|
||||
|
||||
const ENTRY_LIMIT = 5
|
||||
const COMMON_COMMAND_IDS = [
|
||||
"session.new",
|
||||
"workspace.new",
|
||||
"session.previous",
|
||||
"session.next",
|
||||
"terminal.toggle",
|
||||
"review.toggle",
|
||||
] as const
|
||||
|
||||
const uniqueEntries = (items: Entry[]) => {
|
||||
const seen = new Set<string>()
|
||||
const out: Entry[] = []
|
||||
for (const item of items) {
|
||||
if (seen.has(item.id)) continue
|
||||
seen.add(item.id)
|
||||
out.push(item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
const createCommandEntry = (option: CommandOption, category: string): Entry => ({
|
||||
id: "command:" + option.id,
|
||||
type: "command",
|
||||
title: option.title,
|
||||
description: option.description,
|
||||
keybind: option.keybind,
|
||||
category,
|
||||
option,
|
||||
})
|
||||
|
||||
const createFileEntry = (path: string, category: string): Entry => ({
|
||||
id: "file:" + path,
|
||||
type: "file",
|
||||
title: path,
|
||||
category,
|
||||
path,
|
||||
})
|
||||
|
||||
const createSessionEntry = (
|
||||
input: {
|
||||
directory: string
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
archived?: number
|
||||
updated?: number
|
||||
},
|
||||
category: string,
|
||||
): Entry => ({
|
||||
id: `session:${input.directory}:${input.id}`,
|
||||
type: "session",
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
category,
|
||||
directory: input.directory,
|
||||
sessionID: input.id,
|
||||
archived: input.archived,
|
||||
updated: input.updated,
|
||||
})
|
||||
|
||||
function createCommandEntries(props: {
|
||||
filesOnly: () => boolean
|
||||
command: ReturnType<typeof useCommand>
|
||||
language: ReturnType<typeof useLanguage>
|
||||
}) {
|
||||
const allowed = createMemo(() => {
|
||||
if (props.filesOnly()) return []
|
||||
return props.command.options.filter(
|
||||
(option) =>
|
||||
!option.disabled && !option.hidden && !option.id.startsWith("suggested.") && option.id !== "file.open",
|
||||
)
|
||||
})
|
||||
|
||||
const list = createMemo(() => {
|
||||
const category = props.language.t("palette.group.commands")
|
||||
return allowed().map((option) => createCommandEntry(option, category))
|
||||
})
|
||||
|
||||
const picks = createMemo(() => {
|
||||
const all = allowed()
|
||||
const order = new Map<string, number>(COMMON_COMMAND_IDS.map((id, index) => [id, index]))
|
||||
const picked = all.filter((option) => order.has(option.id))
|
||||
const base = picked.length ? picked : all.slice(0, ENTRY_LIMIT)
|
||||
const sorted = picked.length ? [...base].sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)) : base
|
||||
const category = props.language.t("palette.group.commands")
|
||||
return sorted.map((option) => createCommandEntry(option, category))
|
||||
})
|
||||
|
||||
return { allowed, list, picks }
|
||||
}
|
||||
|
||||
function createFileEntries(props: {
|
||||
file: ReturnType<typeof useFile>
|
||||
tabs: () => ReturnType<ReturnType<typeof useLayout>["tabs"]>
|
||||
language: ReturnType<typeof useLanguage>
|
||||
}) {
|
||||
const tabState = createSessionTabs({
|
||||
tabs: props.tabs,
|
||||
pathFromTab: props.file.pathFromTab,
|
||||
normalizeTab: (tab) => (tab.startsWith("file://") ? props.file.tab(tab) : tab),
|
||||
})
|
||||
const recent = createMemo(() => {
|
||||
const all = tabState.openedTabs()
|
||||
const active = tabState.activeFileTab()
|
||||
const order = active ? [active, ...all.filter((item) => item !== active)] : all
|
||||
const seen = new Set<string>()
|
||||
const category = props.language.t("palette.group.files")
|
||||
const items: Entry[] = []
|
||||
|
||||
for (const item of order) {
|
||||
const path = props.file.pathFromTab(item)
|
||||
if (!path) continue
|
||||
if (seen.has(path)) continue
|
||||
seen.add(path)
|
||||
items.push(createFileEntry(path, category))
|
||||
}
|
||||
|
||||
return items.slice(0, ENTRY_LIMIT)
|
||||
})
|
||||
|
||||
const root = createMemo(() => {
|
||||
const category = props.language.t("palette.group.files")
|
||||
const nodes = props.file.tree.children("")
|
||||
const paths = nodes
|
||||
.filter((node) => node.type === "file")
|
||||
.map((node) => node.path)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
return paths.slice(0, ENTRY_LIMIT).map((path) => createFileEntry(path, category))
|
||||
})
|
||||
|
||||
return { recent, root }
|
||||
}
|
||||
|
||||
function createSessionEntries(props: {
|
||||
workspaces: () => string[]
|
||||
label: (directory: string) => string
|
||||
serverSDK: ServerSDK
|
||||
language: ReturnType<typeof useLanguage>
|
||||
}) {
|
||||
const state: {
|
||||
token: number
|
||||
inflight: Promise<Entry[]> | undefined
|
||||
cached: Entry[] | undefined
|
||||
} = {
|
||||
token: 0,
|
||||
inflight: undefined,
|
||||
cached: undefined,
|
||||
}
|
||||
|
||||
const sessions = (text: string) => {
|
||||
const query = text.trim()
|
||||
if (!query) {
|
||||
state.token += 1
|
||||
state.inflight = undefined
|
||||
state.cached = undefined
|
||||
return [] as Entry[]
|
||||
}
|
||||
|
||||
if (state.cached) return state.cached
|
||||
if (state.inflight) return state.inflight
|
||||
|
||||
const current = state.token
|
||||
const dirs = props.workspaces()
|
||||
if (dirs.length === 0) return [] as Entry[]
|
||||
|
||||
state.inflight = Promise.all(
|
||||
dirs.map((directory) => {
|
||||
const description = props.label(directory)
|
||||
return props.serverSDK.client.session
|
||||
.list({ directory, roots: true })
|
||||
.then((x) =>
|
||||
(x.data ?? [])
|
||||
.filter((s) => !!s?.id)
|
||||
.map((s) => ({
|
||||
id: s.id,
|
||||
title: s.title ?? props.language.t("command.session.new"),
|
||||
description,
|
||||
directory,
|
||||
archived: s.time?.archived,
|
||||
updated: s.time?.updated,
|
||||
})),
|
||||
)
|
||||
.catch(
|
||||
() =>
|
||||
[] as {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
directory: string
|
||||
archived?: number
|
||||
updated?: number
|
||||
}[],
|
||||
)
|
||||
}),
|
||||
)
|
||||
.then((results) => {
|
||||
if (state.token !== current) return [] as Entry[]
|
||||
const seen = new Set<string>()
|
||||
const category = props.language.t("command.category.session")
|
||||
const next = results
|
||||
.flat()
|
||||
.filter((item) => {
|
||||
const key = `${item.directory}:${item.id}`
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
.map((item) => createSessionEntry(item, category))
|
||||
state.cached = next
|
||||
return next
|
||||
})
|
||||
.catch(() => [] as Entry[])
|
||||
.finally(() => {
|
||||
state.inflight = undefined
|
||||
})
|
||||
|
||||
return state.inflight
|
||||
}
|
||||
|
||||
return { sessions }
|
||||
}
|
||||
|
||||
export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFile?: (path: string) => void }) {
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const settings = useSettings()
|
||||
const layout = useLayout()
|
||||
const file = useFile()
|
||||
const dialog = useDialog()
|
||||
const navigate = useNavigate()
|
||||
const serverSDK = useServerSDK()
|
||||
const serverSync = useServerSync()
|
||||
const { params, tabs, view } = useSessionLayout()
|
||||
const filesOnly = () => props.mode === "files"
|
||||
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||
const [grouped, setGrouped] = createSignal(false)
|
||||
const commandEntries = createCommandEntries({ filesOnly, command, language })
|
||||
const fileEntries = createFileEntries({ file, tabs, language })
|
||||
|
||||
const projectDirectory = createMemo(() => decode64(params.dir) ?? "")
|
||||
const project = createMemo(() => {
|
||||
const directory = projectDirectory()
|
||||
if (!directory) return
|
||||
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
|
||||
})
|
||||
const workspaces = createMemo(() => {
|
||||
const directory = projectDirectory()
|
||||
const current = project()
|
||||
if (!current) return directory ? [directory] : []
|
||||
|
||||
const dirs = [current.worktree, ...(current.sandboxes ?? [])]
|
||||
if (directory && !dirs.includes(directory)) return [...dirs, directory]
|
||||
return dirs
|
||||
})
|
||||
const homedir = createMemo(() => serverSync().data.path.home)
|
||||
const label = (directory: string) => {
|
||||
const current = project()
|
||||
const kind =
|
||||
current && directory === current.worktree
|
||||
? language.t("workspace.type.local")
|
||||
: language.t("workspace.type.sandbox")
|
||||
const [store] = serverSync().child(directory, { bootstrap: false })
|
||||
const home = homedir()
|
||||
const path = home ? directory.replace(home, "~") : directory
|
||||
const name = store.vcs?.branch ?? getFilename(directory)
|
||||
return `${kind} : ${name || path}`
|
||||
if (!filesOnly() && settings.general.newLayoutDesigns()) {
|
||||
return <DialogCommandPaletteV2 onOpenFile={props.onOpenFile} />
|
||||
}
|
||||
|
||||
const { sessions } = createSessionEntries({ workspaces, label, serverSDK: serverSDK(), language })
|
||||
if (filesOnly() && platform.platform === "desktop" && settings.general.newLayoutDesigns()) {
|
||||
return <DialogSelectFileDesktopV2 onOpenFile={props.onOpenFile} />
|
||||
}
|
||||
|
||||
return <DialogSelectFileLegacy filesOnly={filesOnly} onOpenFile={props.onOpenFile} />
|
||||
}
|
||||
|
||||
function DialogSelectFileDesktopV2(props: { onOpenFile?: (path: string) => void }) {
|
||||
const language = useLanguage()
|
||||
const serverSDK = useServerSDK()
|
||||
const { params } = useSessionLayout()
|
||||
const projectDirectory = createMemo(() => decode64(params.dir) ?? "")
|
||||
const openFile = createCommandPaletteFileOpener(props.onOpenFile)
|
||||
|
||||
return (
|
||||
<DialogSelectFileV2
|
||||
server={serverSDK().server}
|
||||
mode="file"
|
||||
start={projectDirectory()}
|
||||
title={language.t("session.header.searchFiles")}
|
||||
onSelect={(result) => {
|
||||
if (typeof result !== "string") return
|
||||
openFile(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogSelectFileLegacy(props: { filesOnly: () => boolean; onOpenFile?: (path: string) => void }) {
|
||||
const palette = createCommandPaletteModel(props)
|
||||
const [grouped, setGrouped] = createSignal(false)
|
||||
|
||||
const items = async (text: string) => {
|
||||
const query = text.trim()
|
||||
setGrouped(query.length > 0)
|
||||
|
||||
if (!query && filesOnly()) {
|
||||
const loaded = file.tree.state("")?.loaded
|
||||
const pending = loaded ? Promise.resolve() : file.tree.list("")
|
||||
const next = uniqueEntries([...fileEntries.recent(), ...fileEntries.root()])
|
||||
if (!query && props.filesOnly()) {
|
||||
const loaded = palette.file.tree.state("")?.loaded
|
||||
const pending = loaded ? Promise.resolve() : palette.file.tree.list("")
|
||||
const next = uniqueCommandPaletteEntries([...palette.recentFileEntries(), ...palette.rootFileEntries()])
|
||||
|
||||
if (loaded || next.length > 0) {
|
||||
void pending
|
||||
|
|
@ -331,79 +86,24 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||
}
|
||||
|
||||
await pending
|
||||
return uniqueEntries([...fileEntries.recent(), ...fileEntries.root()])
|
||||
return uniqueCommandPaletteEntries([...palette.recentFileEntries(), ...palette.rootFileEntries()])
|
||||
}
|
||||
|
||||
if (!query) return [...commandEntries.picks(), ...fileEntries.recent()]
|
||||
if (!query) return [...palette.preferredCommandEntries(), ...palette.recentFileEntries()]
|
||||
|
||||
if (filesOnly()) {
|
||||
const files = await file.searchFiles(query)
|
||||
const category = language.t("palette.group.files")
|
||||
return files.map((path) => createFileEntry(path, category))
|
||||
if (props.filesOnly()) {
|
||||
const files = await palette.file.searchFiles(query)
|
||||
const category = palette.language.t("palette.group.files")
|
||||
return files.map((path) => createCommandPaletteFileEntry(path, category))
|
||||
}
|
||||
|
||||
const [files, nextSessions] = await Promise.all([file.searchFiles(query), Promise.resolve(sessions(query))])
|
||||
const category = language.t("palette.group.files")
|
||||
const entries = files.map((path) => createFileEntry(path, category))
|
||||
return [...commandEntries.list(), ...nextSessions, ...entries]
|
||||
}
|
||||
|
||||
const handleMove = (item: Entry | undefined) => {
|
||||
state.cleanup?.()
|
||||
if (!item) return
|
||||
if (item.type !== "command") return
|
||||
state.cleanup = item.option?.onHighlight?.()
|
||||
}
|
||||
|
||||
const open = (path: string) => {
|
||||
const value = file.tab(path)
|
||||
void tabs().open(value)
|
||||
void file.load(path)
|
||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
||||
layout.fileTree.setTab("all")
|
||||
props.onOpenFile?.(path)
|
||||
tabs().setActive(value)
|
||||
}
|
||||
|
||||
const handleSelect = (item: Entry | undefined) => {
|
||||
if (!item) return
|
||||
state.committed = true
|
||||
state.cleanup = undefined
|
||||
dialog.close()
|
||||
|
||||
if (item.type === "command") {
|
||||
item.option?.onSelect?.("palette")
|
||||
return
|
||||
}
|
||||
|
||||
if (item.type === "session") {
|
||||
if (!item.directory || !item.sessionID) return
|
||||
navigate(`/${base64Encode(item.directory)}/session/${item.sessionID}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!item.path) return
|
||||
open(item.path)
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
if (state.committed) return
|
||||
state.cleanup?.()
|
||||
})
|
||||
|
||||
if (filesOnly() && platform.platform === "desktop" && settings.general.newLayoutDesigns()) {
|
||||
return (
|
||||
<DialogSelectFileV2
|
||||
server={serverSDK().server}
|
||||
mode="file"
|
||||
start={projectDirectory()}
|
||||
title={language.t("session.header.searchFiles")}
|
||||
onSelect={(result) => {
|
||||
if (typeof result !== "string") return
|
||||
open(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
const [files, nextSessions] = await Promise.all([
|
||||
palette.file.searchFiles(query),
|
||||
Promise.resolve(palette.sessions(query)),
|
||||
])
|
||||
const category = palette.language.t("palette.group.files")
|
||||
const entries = files.map((path) => createCommandPaletteFileEntry(path, category))
|
||||
return [...palette.commandEntries(), ...nextSessions, ...entries]
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -411,21 +111,21 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||
<List
|
||||
class="px-3"
|
||||
search={{
|
||||
placeholder: filesOnly()
|
||||
? language.t("session.header.searchFiles")
|
||||
: language.t("palette.search.placeholder"),
|
||||
placeholder: props.filesOnly()
|
||||
? palette.language.t("session.header.searchFiles")
|
||||
: palette.language.t("palette.search.placeholder"),
|
||||
autofocus: true,
|
||||
hideIcon: true,
|
||||
}}
|
||||
emptyMessage={language.t("palette.empty")}
|
||||
loadingMessage={language.t("common.loading")}
|
||||
emptyMessage={palette.language.t("palette.empty")}
|
||||
loadingMessage={palette.language.t("common.loading")}
|
||||
items={items}
|
||||
key={(item) => item.id}
|
||||
filterKeys={["title", "description", "category"]}
|
||||
skipFilter={(item) => item.type === "file"}
|
||||
groupBy={grouped() ? (item) => item.category : () => ""}
|
||||
onMove={handleMove}
|
||||
onSelect={handleSelect}
|
||||
onMove={(item: CommandPaletteEntry | undefined) => palette.highlight(item)}
|
||||
onSelect={(item: CommandPaletteEntry | undefined) => palette.select(item)}
|
||||
>
|
||||
{(item) => (
|
||||
<Switch
|
||||
|
|
@ -452,7 +152,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||
</Show>
|
||||
</div>
|
||||
<Show when={item.keybind}>
|
||||
<Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "", language.t)}</Keybind>
|
||||
<Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "", palette.language.t)}</Keybind>
|
||||
</Show>
|
||||
</div>
|
||||
</Match>
|
||||
|
|
@ -479,7 +179,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||
</div>
|
||||
<Show when={item.updated}>
|
||||
<span class="text-12-regular text-text-weak whitespace-nowrap ml-2">
|
||||
{getRelativeTime(new Date(item.updated!).toISOString(), language.t)}
|
||||
{getRelativeTime(new Date(item.updated!).toISOString(), palette.language.t)}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { Tooltip } from "@opencode-ai/ui/tooltip"
|
|||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { Tag as TagV2 } from "@opencode-ai/ui/v2/badge-v2"
|
||||
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { ModelTooltip } from "./model-tooltip"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
|
|
@ -92,6 +93,7 @@ const ModelList: Component<{
|
|||
class="w-full"
|
||||
placement="right-start"
|
||||
gutter={12}
|
||||
openDelay={0}
|
||||
value={<ModelTooltip model={item} latest={item.latest} free={isFree(item.provider.id, item.cost)} />}
|
||||
>
|
||||
{node}
|
||||
|
|
@ -446,26 +448,41 @@ export function ModelSelectorPopoverV2(props: {
|
|||
<MenuV2.RadioGroup value={current()}>
|
||||
<For each={group.items}>
|
||||
{(item) => (
|
||||
<MenuV2.RadioItem
|
||||
value={modelKey(item)}
|
||||
data-option-key={modelKey(item)}
|
||||
data-selected-model={current() === modelKey(item) ? true : undefined}
|
||||
class="scroll-my-6"
|
||||
classList={{ "!bg-v2-overlay-simple-overlay-hover": store.active === modelKey(item) }}
|
||||
onMouseEnter={() => {
|
||||
setStore("active", modelKey(item))
|
||||
setTimeout(() => searchRef?.focus())
|
||||
}}
|
||||
onSelect={() => selectModel(item)}
|
||||
<TooltipV2
|
||||
class="w-full"
|
||||
placement="right-start"
|
||||
gutter={6}
|
||||
openDelay={0}
|
||||
value={
|
||||
<ModelTooltip
|
||||
model={item}
|
||||
latest={item.latest}
|
||||
free={isFree(item.provider.id, item.cost)}
|
||||
v2
|
||||
/>
|
||||
}
|
||||
>
|
||||
<span class="min-w-0 truncate">{item.name}</span>
|
||||
<Show when={isFree(item.provider.id, item.cost)}>
|
||||
<TagV2 class="shrink-0">{language.t("model.tag.free")}</TagV2>
|
||||
</Show>
|
||||
<Show when={item.latest}>
|
||||
<TagV2 class="shrink-0">{language.t("model.tag.latest")}</TagV2>
|
||||
</Show>
|
||||
</MenuV2.RadioItem>
|
||||
<MenuV2.RadioItem
|
||||
value={modelKey(item)}
|
||||
data-option-key={modelKey(item)}
|
||||
data-selected-model={current() === modelKey(item) ? true : undefined}
|
||||
class="scroll-my-6 w-full"
|
||||
classList={{ "!bg-v2-overlay-simple-overlay-hover": store.active === modelKey(item) }}
|
||||
onMouseEnter={() => {
|
||||
setStore("active", modelKey(item))
|
||||
setTimeout(() => searchRef?.focus())
|
||||
}}
|
||||
onSelect={() => selectModel(item)}
|
||||
>
|
||||
<span class="min-w-0 truncate">{item.name}</span>
|
||||
<Show when={isFree(item.provider.id, item.cost)}>
|
||||
<TagV2 class="shrink-0">{language.t("model.tag.free")}</TagV2>
|
||||
</Show>
|
||||
<Show when={item.latest}>
|
||||
<TagV2 class="shrink-0">{language.t("model.tag.latest")}</TagV2>
|
||||
</Show>
|
||||
</MenuV2.RadioItem>
|
||||
</TooltipV2>
|
||||
)}
|
||||
</For>
|
||||
</MenuV2.RadioGroup>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Show, type Component } from "solid-js"
|
||||
import { Show, type Component, type JSX } from "solid-js"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
type InputKey = "text" | "image" | "audio" | "video" | "pdf"
|
||||
|
|
@ -23,7 +23,18 @@ type ModelInfo = {
|
|||
}
|
||||
}
|
||||
|
||||
export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free?: boolean }> = (props) => {
|
||||
function ModelTooltipRow(props: { name: JSX.Element; value: JSX.Element }) {
|
||||
return (
|
||||
<div class="flex min-w-0 items-center gap-4">
|
||||
<span class="shrink-0 text-v2-text-text-muted">{props.name}</span>
|
||||
<span class="ml-auto min-w-0 truncate text-right text-v2-text-text-base">{props.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free?: boolean; v2?: boolean }> = (
|
||||
props,
|
||||
) => {
|
||||
const language = useLanguage()
|
||||
const sourceName = (model: ModelInfo) => {
|
||||
const value = `${model.id} ${model.name}`.toLowerCase()
|
||||
|
|
@ -51,6 +62,13 @@ export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free?
|
|||
const suffix = tags.length ? ` (${tags.join(", ")})` : ""
|
||||
return `${sourceName(props.model)} ${props.model.name}${suffix}`
|
||||
}
|
||||
const name = () => {
|
||||
const tags: Array<string> = []
|
||||
if (props.latest) tags.push(language.t("model.tag.latest"))
|
||||
if (props.free) tags.push(language.t("model.tag.free"))
|
||||
const suffix = tags.length ? ` (${tags.join(", ")})` : ""
|
||||
return `${props.model.name}${suffix}`
|
||||
}
|
||||
const inputs = () => {
|
||||
if (props.model.capabilities) {
|
||||
const input = props.model.capabilities.input
|
||||
|
|
@ -73,6 +91,21 @@ export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free?
|
|||
: language.t("model.tooltip.reasoning.none")
|
||||
}
|
||||
const context = () => language.t("model.tooltip.context", { limit: props.model.limit.context.toLocaleString() })
|
||||
const contextLimit = () => props.model.limit.context.toLocaleString(language.intl())
|
||||
|
||||
if (props.v2) {
|
||||
return (
|
||||
<div class="flex w-[180px] flex-col gap-2">
|
||||
<ModelTooltipRow name={language.t("model.tooltip.model")} value={name()} />
|
||||
<ModelTooltipRow name={language.t("model.tooltip.provider")} value={props.model.provider.name} />
|
||||
<Show when={inputs()}>
|
||||
{(value) => <ModelTooltipRow name={language.t("model.tooltip.inputs")} value={value()} />}
|
||||
</Show>
|
||||
<ModelTooltipRow name={language.t("model.tooltip.reasoning")} value={reasoning()} />
|
||||
<ModelTooltipRow name={language.t("model.tooltip.context.label")} value={contextLimit()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-1 py-1">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// @ts-nocheck
|
||||
import { createStore } from "solid-js/store"
|
||||
import type { Todo } from "@opencode-ai/sdk/v2"
|
||||
import { createPromptState } from "@/context/prompt"
|
||||
import { SessionComposerRegion, createSessionComposerRegionController } from "@/pages/session/composer"
|
||||
import { createPromptInputHistory, PromptInput } from "./prompt-input"
|
||||
|
||||
function createPromptInputStoryRuntime() {
|
||||
|
|
@ -104,94 +102,6 @@ function PromptInputExample() {
|
|||
)
|
||||
}
|
||||
|
||||
const todos: Todo[] = [
|
||||
{ id: "todo-1", content: "Inspect the session composer animation", status: "completed" },
|
||||
{ id: "todo-2", content: "Keep the dock settled on initial render", status: "in_progress" },
|
||||
{ id: "todo-3", content: "Verify session navigation behavior", status: "pending" },
|
||||
]
|
||||
|
||||
function PromptInputWithOpenDock() {
|
||||
const input = createPromptInputStoryRuntime()
|
||||
const [controls, setControls] = createStore({
|
||||
agent: "build",
|
||||
activeTab: undefined as string | undefined,
|
||||
todoCollapsed: false,
|
||||
})
|
||||
const inputControls = {
|
||||
agents: {
|
||||
available: [],
|
||||
options: ["build"],
|
||||
get current() {
|
||||
return controls.agent
|
||||
},
|
||||
loading: false,
|
||||
visible: true,
|
||||
select: (agent?: string) => setControls("agent", agent ?? "build"),
|
||||
},
|
||||
model: {
|
||||
selection: {
|
||||
current: () => ({ id: "claude-3-7-sonnet", name: "Claude 3.7 Sonnet", provider: { id: "anthropic" } }),
|
||||
variant: { list: () => [], current: () => undefined, set: () => {} },
|
||||
},
|
||||
paid: true,
|
||||
loading: false,
|
||||
},
|
||||
session: {
|
||||
id: "story-session",
|
||||
tabs: {
|
||||
active: () => controls.activeTab,
|
||||
all: () => [],
|
||||
open: () => {},
|
||||
setActive: (tab: string) => setControls("activeTab", tab),
|
||||
},
|
||||
reviewPanel: { opened: () => false, open: () => {} },
|
||||
},
|
||||
newLayoutDesigns: true,
|
||||
}
|
||||
const state = {
|
||||
blocked: () => false,
|
||||
questionRequest: () => undefined,
|
||||
permissionRequest: () => undefined,
|
||||
permissionResponding: () => false,
|
||||
decide: () => {},
|
||||
todos: () => todos,
|
||||
dock: () => true,
|
||||
closing: () => false,
|
||||
opening: () => false,
|
||||
}
|
||||
return (
|
||||
<SessionComposerRegion
|
||||
controller={createSessionComposerRegionController({
|
||||
state,
|
||||
sessionKey: () => "story-session",
|
||||
sessionID: () => "story-session",
|
||||
prompt: input.state,
|
||||
ready: () => true,
|
||||
centered: () => false,
|
||||
todo: {
|
||||
collapsed: () => controls.todoCollapsed,
|
||||
onToggle: () => setControls("todoCollapsed", (collapsed) => !collapsed),
|
||||
},
|
||||
followup: () => undefined,
|
||||
revert: () => undefined,
|
||||
onResponseSubmit: () => {},
|
||||
openParent: () => {},
|
||||
setPromptRef: () => {},
|
||||
setDockRef: () => {},
|
||||
})}
|
||||
promptInput={
|
||||
<PromptInput
|
||||
controls={inputControls}
|
||||
{...input}
|
||||
ref={() => {}}
|
||||
newSessionWorktree=""
|
||||
onNewSessionWorktreeReset={() => {}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "App/PromptInput",
|
||||
id: "app-prompt-input",
|
||||
|
|
@ -206,12 +116,3 @@ export const Basic = {
|
|||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const DockAlreadyOpen = {
|
||||
render: () => (
|
||||
<div class="pt-10">
|
||||
<h1 class="mb-4">Prompt Input with open Todo dock</h1>
|
||||
<PromptInputWithOpenDock />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,8 +221,6 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
|||
const sessionID = params.id
|
||||
if (!sessionID) return Promise.resolve()
|
||||
|
||||
serverSync().session.set("todo", sessionID, [])
|
||||
|
||||
input.onAbort?.()
|
||||
|
||||
const key = pendingKey(sessionID)
|
||||
|
|
|
|||
|
|
@ -440,11 +440,6 @@ export const SettingsGeneral: Component = () => {
|
|||
value={(o) => o.value}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => option && theme.setColorScheme(option.value)}
|
||||
onHighlight={(option) => {
|
||||
if (!option) return
|
||||
theme.previewColorScheme(option.value)
|
||||
return () => theme.cancelPreview()
|
||||
}}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
|
|
@ -471,11 +466,6 @@ export const SettingsGeneral: Component = () => {
|
|||
if (!option) return
|
||||
theme.setTheme(option.id)
|
||||
}}
|
||||
onHighlight={(option) => {
|
||||
if (!option) return
|
||||
theme.previewTheme(option.id)
|
||||
return () => theme.cancelPreview()
|
||||
}}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
|
|
|
|||
|
|
@ -422,11 +422,6 @@ export const SettingsGeneralV2: Component<{
|
|||
value={(o) => o.value}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => option && theme.setColorScheme(option.value)}
|
||||
onHighlight={(option) => {
|
||||
if (!option) return
|
||||
theme.previewColorScheme(option.value)
|
||||
return () => theme.cancelPreview()
|
||||
}}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
|
|
@ -454,11 +449,6 @@ export const SettingsGeneralV2: Component<{
|
|||
if (!option) return
|
||||
theme.setTheme(option.id)
|
||||
}}
|
||||
onHighlight={(option) => {
|
||||
if (!option) return
|
||||
theme.previewTheme(option.id)
|
||||
return () => theme.cancelPreview()
|
||||
}}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
|
|
|
|||
|
|
@ -246,18 +246,23 @@ export function TabNavItem(props: {
|
|||
}}
|
||||
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base group-data-[editing='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
||||
>
|
||||
<Show when={props.session()}>
|
||||
{(session) => (
|
||||
<span data-slot="project-avatar-slot">
|
||||
<span data-slot="project-avatar-slot" class="flex size-4 shrink-0 items-center justify-center">
|
||||
<Show
|
||||
when={props.session()}
|
||||
fallback={
|
||||
<span class="block size-4 rounded-[3px] border border-v2-border-border-muted" aria-hidden="true" />
|
||||
}
|
||||
>
|
||||
{(session) => (
|
||||
<SessionTabAvatar
|
||||
project={project()}
|
||||
directory={session().directory}
|
||||
sessionId={session().id}
|
||||
server={props.server}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</Show>
|
||||
</span>
|
||||
<span
|
||||
ref={(el) => {
|
||||
titleEl = el
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ function SessionTabSlot(props: {
|
|||
let ref!: HTMLDivElement
|
||||
const sdk = createMemo(() => props.serverCtx()?.sdk ?? null)
|
||||
const cachedSession = createMemo(() => props.serverCtx()?.sync.session.peek(props.tab.sessionId))
|
||||
const persisted = createMemo(() => tabs.info[props.id])
|
||||
const [loadedSession] = createResource(
|
||||
() => {
|
||||
const ctx = props.serverCtx()
|
||||
|
|
@ -71,8 +72,10 @@ function SessionTabSlot(props: {
|
|||
|
||||
createEffect(() => {
|
||||
const value = session()
|
||||
if (!value) return
|
||||
tabs.rememberSessionInfo(props.tab, value)
|
||||
const current = sdk()
|
||||
if (!value || !current) return
|
||||
if (!current) return
|
||||
createTabPromptState(tabs, props.tab, current.scope, {
|
||||
dir: base64Encode(value.directory),
|
||||
id: value.id,
|
||||
|
|
@ -86,7 +89,7 @@ function SessionTabSlot(props: {
|
|||
data-tab-key={props.id}
|
||||
data-active={props.active()}
|
||||
class="relative flex w-56 min-w-7 max-w-56 flex-shrink"
|
||||
classList={{ hidden: !session() && !missingSession() }}
|
||||
classList={{ hidden: !session() && !missingSession() && !persisted()?.title }}
|
||||
>
|
||||
<TabNavItem
|
||||
ref={(el) => {
|
||||
|
|
@ -95,7 +98,7 @@ function SessionTabSlot(props: {
|
|||
href={tabHref(props.tab)}
|
||||
server={props.tab.server}
|
||||
session={session}
|
||||
fallbackTitle={missingSession() ? language.t("session.tab.unknown") : undefined}
|
||||
fallbackTitle={persisted()?.title ?? (missingSession() ? language.t("session.tab.unknown") : undefined)}
|
||||
onTitleChange={(title) => {
|
||||
const value = session()
|
||||
const ctx = props.serverCtx()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { upsertCommandRegistration } from "./command"
|
||||
import { resolveKeybindOption, upsertCommandRegistration } from "./command"
|
||||
|
||||
describe("upsertCommandRegistration", () => {
|
||||
test("replaces keyed registrations", () => {
|
||||
|
|
@ -23,3 +23,19 @@ describe("upsertCommandRegistration", () => {
|
|||
expect(next[1]?.options).toBe(one)
|
||||
})
|
||||
})
|
||||
|
||||
describe("resolveKeybindOption", () => {
|
||||
test("prefers a matching contextual command over the global fallback", () => {
|
||||
const fallback = { id: "tab.close", title: "Close tab" }
|
||||
const contextual = { id: "terminal.close", title: "Close terminal", when: () => true }
|
||||
|
||||
expect(resolveKeybindOption([fallback, contextual], new KeyboardEvent("keydown"))).toBe(contextual)
|
||||
})
|
||||
|
||||
test("uses the global fallback outside the command context", () => {
|
||||
const fallback = { id: "tab.close", title: "Close tab" }
|
||||
const contextual = { id: "terminal.close", title: "Close terminal", when: () => false }
|
||||
|
||||
expect(resolveKeybindOption([fallback, contextual], new KeyboardEvent("keydown"))).toBe(fallback)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -82,10 +82,15 @@ export interface CommandOption {
|
|||
suggested?: boolean
|
||||
disabled?: boolean
|
||||
hidden?: boolean
|
||||
when?: (event: KeyboardEvent) => boolean
|
||||
onSelect?: (source?: "palette" | "keybind" | "slash") => void
|
||||
onHighlight?: () => (() => void) | void
|
||||
}
|
||||
|
||||
export function resolveKeybindOption(candidates: CommandOption[] | undefined, event: KeyboardEvent) {
|
||||
return candidates?.find((option) => option.when?.(event)) ?? candidates?.find((option) => !option.when)
|
||||
}
|
||||
|
||||
type CommandSource = "palette" | "keybind" | "slash"
|
||||
|
||||
export type CommandCatalogItem = {
|
||||
|
|
@ -334,7 +339,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||
})
|
||||
|
||||
const keymap = createMemo(() => {
|
||||
const map = new Map<string, CommandOption>()
|
||||
const map = new Map<string, CommandOption[]>()
|
||||
for (const option of options()) {
|
||||
if (option.id.startsWith(SUGGESTED_PREFIX)) continue
|
||||
if (option.disabled) continue
|
||||
|
|
@ -344,8 +349,12 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||
for (const kb of keybinds) {
|
||||
if (!kb.key) continue
|
||||
const sig = signature(kb.key, kb.ctrl, kb.meta, kb.shift, kb.alt)
|
||||
if (map.has(sig)) continue
|
||||
map.set(sig, option)
|
||||
const existing = map.get(sig)
|
||||
if (existing) {
|
||||
existing.push(option)
|
||||
continue
|
||||
}
|
||||
map.set(sig, [option])
|
||||
}
|
||||
}
|
||||
return map
|
||||
|
|
@ -374,7 +383,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||
|
||||
const sig = signatureFromEvent(event)
|
||||
const isPalette = palette().has(sig)
|
||||
const option = keymap().get(sig)
|
||||
const option = resolveKeybindOption(keymap().get(sig), event)
|
||||
const modified = event.ctrlKey || event.metaKey || event.altKey
|
||||
const isTab = event.key === "Tab"
|
||||
|
||||
|
|
@ -383,17 +392,19 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||
|
||||
if (isPalette) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
showPalette()
|
||||
return
|
||||
}
|
||||
|
||||
if (!option) return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
option.onSelect?.("keybind")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
makeEventListener(document, "keydown", handleKeyDown)
|
||||
makeEventListener(document, "keydown", handleKeyDown, { capture: true })
|
||||
})
|
||||
|
||||
function register(cb: () => CommandOption[]): void
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ const sessionFields = new Set([
|
|||
"session_status",
|
||||
"session_working",
|
||||
"session_diff",
|
||||
"todo",
|
||||
"permission",
|
||||
"question",
|
||||
"message",
|
||||
|
|
@ -115,7 +114,6 @@ export const createDirSyncContext = (
|
|||
index(sessionID)
|
||||
},
|
||||
diff: serverSync.session.diff,
|
||||
todo: serverSync.session.todo,
|
||||
history: serverSync.session.history,
|
||||
evict(sessionID: string) {
|
||||
serverSync.session.evict(sessionID)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ function directoryState() {
|
|||
return this.session_status[id]?.type !== "idle"
|
||||
},
|
||||
session_diff: {},
|
||||
todo: {},
|
||||
permission: {},
|
||||
question: {},
|
||||
mcp_ready: true,
|
||||
|
|
|
|||
|
|
@ -223,7 +223,6 @@ export function createChildStoreManager(input: {
|
|||
return (type ?? "idle") !== "idle"
|
||||
},
|
||||
session_diff: {},
|
||||
todo: {},
|
||||
permission: {},
|
||||
question: {},
|
||||
get mcp_ready() {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ const baseState = (input: Partial<State> = {}) =>
|
|||
sessionTotal: 0,
|
||||
session_status: {},
|
||||
session_diff: {},
|
||||
todo: {},
|
||||
permission: {},
|
||||
question: {},
|
||||
mcp: {},
|
||||
|
|
@ -216,7 +215,6 @@ describe("applyDirectoryEvent", () => {
|
|||
message: { ses_1: [message] },
|
||||
part: { [message.id]: [textPart("prt_1", "ses_1", message.id)] },
|
||||
session_diff: { ses_1: [] },
|
||||
todo: { ses_1: [] },
|
||||
permission: { ses_1: [] },
|
||||
question: { ses_1: [] },
|
||||
session_status: { ses_1: { type: "busy" } },
|
||||
|
|
@ -237,7 +235,6 @@ describe("applyDirectoryEvent", () => {
|
|||
expect(store.message.ses_1).toBeUndefined()
|
||||
expect(store.part[message.id]).toBeUndefined()
|
||||
expect(store.session_diff.ses_1).toBeUndefined()
|
||||
expect(store.todo.ses_1).toBeUndefined()
|
||||
expect(store.permission.ses_1).toBeUndefined()
|
||||
expect(store.question.ses_1).toBeUndefined()
|
||||
expect(store.session_status.ses_1).toBeUndefined()
|
||||
|
|
@ -262,7 +259,6 @@ describe("applyDirectoryEvent", () => {
|
|||
message: { [item.info.id]: [message] },
|
||||
part: { [message.id]: [textPart("prt_1", item.info.id, message.id)] },
|
||||
session_diff: { [item.info.id]: [] },
|
||||
todo: { [item.info.id]: [] },
|
||||
permission: { [item.info.id]: [] },
|
||||
question: { [item.info.id]: [] },
|
||||
session_status: { [item.info.id]: { type: "busy" } },
|
||||
|
|
@ -283,7 +279,6 @@ describe("applyDirectoryEvent", () => {
|
|||
expect(store.message[item.info.id]).toBeUndefined()
|
||||
expect(store.part[message.id]).toBeUndefined()
|
||||
expect(store.session_diff[item.info.id]).toBeUndefined()
|
||||
expect(store.todo[item.info.id]).toBeUndefined()
|
||||
expect(store.permission[item.info.id]).toBeUndefined()
|
||||
expect(store.question[item.info.id]).toBeUndefined()
|
||||
expect(store.session_status[item.info.id]).toBeUndefined()
|
||||
|
|
@ -294,7 +289,6 @@ describe("applyDirectoryEvent", () => {
|
|||
const dropped = rootSession({ id: "ses_b" })
|
||||
const kept = rootSession({ id: "ses_a" })
|
||||
const message = userMessage("msg_1", dropped.id)
|
||||
const todos: string[] = []
|
||||
const [store, setStore] = createStore(
|
||||
baseState({
|
||||
limit: 1,
|
||||
|
|
@ -302,7 +296,6 @@ describe("applyDirectoryEvent", () => {
|
|||
message: { [dropped.id]: [message] },
|
||||
part: { [message.id]: [textPart("prt_1", dropped.id, message.id)] },
|
||||
session_diff: { [dropped.id]: [] },
|
||||
todo: { [dropped.id]: [] },
|
||||
permission: { [dropped.id]: [] },
|
||||
question: { [dropped.id]: [] },
|
||||
session_status: { [dropped.id]: { type: "busy" } },
|
||||
|
|
@ -316,21 +309,15 @@ describe("applyDirectoryEvent", () => {
|
|||
push() {},
|
||||
directory: "/tmp",
|
||||
loadLsp() {},
|
||||
setSessionTodo(sessionID, value) {
|
||||
if (value !== undefined) return
|
||||
todos.push(sessionID)
|
||||
},
|
||||
})
|
||||
|
||||
expect(store.session.map((x) => x.id)).toEqual([kept.id])
|
||||
expect(store.message[dropped.id]).toBeUndefined()
|
||||
expect(store.part[message.id]).toBeUndefined()
|
||||
expect(store.session_diff[dropped.id]).toBeUndefined()
|
||||
expect(store.todo[dropped.id]).toBeUndefined()
|
||||
expect(store.permission[dropped.id]).toBeUndefined()
|
||||
expect(store.question[dropped.id]).toBeUndefined()
|
||||
expect(store.session_status[dropped.id]).toBeUndefined()
|
||||
expect(todos).toEqual([dropped.id])
|
||||
})
|
||||
|
||||
test("cleanupDroppedSessionCaches clears part-only orphan state", () => {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import type {
|
|||
QuestionRequest,
|
||||
Session,
|
||||
SessionStatus,
|
||||
SnapshotFileDiff,
|
||||
Todo,
|
||||
FileDiffInfo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import type { State, VcsCache } from "./types"
|
||||
import { trimSessions } from "./session-trim"
|
||||
|
|
@ -19,7 +18,6 @@ import { diffs as list, message as clean } from "@/utils/diffs"
|
|||
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
const SESSION_CONTENT_EVENTS = new Set([
|
||||
"session.diff",
|
||||
"todo.updated",
|
||||
"session.status",
|
||||
"message.updated",
|
||||
"message.removed",
|
||||
|
|
@ -62,13 +60,8 @@ export function applyGlobalEvent(input: {
|
|||
)
|
||||
}
|
||||
|
||||
function cleanupSessionCaches(
|
||||
setStore: SetStoreFunction<State>,
|
||||
sessionID: string,
|
||||
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void,
|
||||
) {
|
||||
function cleanupSessionCaches(setStore: SetStoreFunction<State>, sessionID: string) {
|
||||
if (!sessionID) return
|
||||
setSessionTodo?.(sessionID, undefined)
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
dropSessionCaches(draft, [sessionID])
|
||||
|
|
@ -76,17 +69,11 @@ function cleanupSessionCaches(
|
|||
)
|
||||
}
|
||||
|
||||
export function cleanupDroppedSessionCaches(
|
||||
store: Store<State>,
|
||||
setStore: SetStoreFunction<State>,
|
||||
next: Session[],
|
||||
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void,
|
||||
) {
|
||||
export function cleanupDroppedSessionCaches(store: Store<State>, setStore: SetStoreFunction<State>, next: Session[]) {
|
||||
const keep = new Set(next.map((item) => item.id))
|
||||
const stale = [
|
||||
...Object.keys(store.message),
|
||||
...Object.keys(store.session_diff),
|
||||
...Object.keys(store.todo),
|
||||
...Object.keys(store.permission),
|
||||
...Object.keys(store.question),
|
||||
...Object.keys(store.session_status),
|
||||
|
|
@ -95,9 +82,6 @@ export function cleanupDroppedSessionCaches(
|
|||
.filter((sessionID): sessionID is string => !!sessionID),
|
||||
].filter((sessionID, index, list) => !keep.has(sessionID) && list.indexOf(sessionID) === index)
|
||||
if (stale.length === 0) return
|
||||
for (const sessionID of stale) {
|
||||
setSessionTodo?.(sessionID, undefined)
|
||||
}
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
dropSessionCaches(draft, stale)
|
||||
|
|
@ -114,7 +98,6 @@ export function applyDirectoryEvent(input: {
|
|||
loadLsp: () => void
|
||||
loadReferences?: () => void
|
||||
vcsCache?: VcsCache
|
||||
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void
|
||||
retainedLimit?: number
|
||||
sessionContent?: boolean
|
||||
permission?: State["permission"]
|
||||
|
|
@ -138,7 +121,7 @@ export function applyDirectoryEvent(input: {
|
|||
next.splice(result.index, 0, info)
|
||||
const trimmed = trimSessions(next, { limit, permission: input.permission ?? input.store.permission })
|
||||
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed)
|
||||
if (!info.parentID) input.setStore("sessionTotal", (value) => value + 1)
|
||||
break
|
||||
}
|
||||
|
|
@ -155,7 +138,7 @@ export function applyDirectoryEvent(input: {
|
|||
}),
|
||||
)
|
||||
}
|
||||
cleanupSessionCaches(input.setStore, info.id, input.setSessionTodo)
|
||||
cleanupSessionCaches(input.setStore, info.id)
|
||||
if (info.parentID) break
|
||||
input.setStore("sessionTotal", (value) => Math.max(0, value - 1))
|
||||
break
|
||||
|
|
@ -168,7 +151,7 @@ export function applyDirectoryEvent(input: {
|
|||
next.splice(result.index, 0, info)
|
||||
const trimmed = trimSessions(next, { limit, permission: input.permission ?? input.store.permission })
|
||||
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed)
|
||||
break
|
||||
}
|
||||
case "session.deleted": {
|
||||
|
|
@ -182,22 +165,16 @@ export function applyDirectoryEvent(input: {
|
|||
}),
|
||||
)
|
||||
}
|
||||
cleanupSessionCaches(input.setStore, info.id, input.setSessionTodo)
|
||||
cleanupSessionCaches(input.setStore, info.id)
|
||||
if (info.parentID) break
|
||||
input.setStore("sessionTotal", (value) => Math.max(0, value - 1))
|
||||
break
|
||||
}
|
||||
case "session.diff": {
|
||||
const props = event.properties as { sessionID: string; diff: SnapshotFileDiff[] }
|
||||
const props = event.properties as { sessionID: string; diff: FileDiffInfo[] }
|
||||
input.setStore("session_diff", props.sessionID, reconcile(list(props.diff), { key: "file" }))
|
||||
break
|
||||
}
|
||||
case "todo.updated": {
|
||||
const props = event.properties as { sessionID: string; todos: Todo[] }
|
||||
input.setStore("todo", props.sessionID, reconcile(props.todos, { key: "id" }))
|
||||
input.setSessionTodo?.(props.sessionID, props.todos)
|
||||
break
|
||||
}
|
||||
case "session.status": {
|
||||
const props = event.properties as { sessionID: string; status: SessionStatus }
|
||||
input.setStore("session_status", props.sessionID, reconcile(props.status))
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import type {
|
|||
PermissionRequest,
|
||||
QuestionRequest,
|
||||
SessionStatus,
|
||||
SnapshotFileDiff,
|
||||
Todo,
|
||||
FileDiffInfo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache"
|
||||
|
||||
|
|
@ -33,8 +32,7 @@ describe("app session cache", () => {
|
|||
test("dropSessionCaches clears orphaned parts without message rows", () => {
|
||||
const store: {
|
||||
session_status: Record<string, SessionStatus | undefined>
|
||||
session_diff: Record<string, SnapshotFileDiff[] | undefined>
|
||||
todo: Record<string, Todo[] | undefined>
|
||||
session_diff: Record<string, FileDiffInfo[] | undefined>
|
||||
message: Record<string, Message[] | undefined>
|
||||
part: Record<string, Part[] | undefined>
|
||||
permission: Record<string, PermissionRequest[] | undefined>
|
||||
|
|
@ -43,7 +41,6 @@ describe("app session cache", () => {
|
|||
} = {
|
||||
session_status: { ses_1: { type: "busy" } as SessionStatus },
|
||||
session_diff: { ses_1: [] },
|
||||
todo: { ses_1: [] as Todo[] },
|
||||
message: {},
|
||||
part: { msg_1: [part("prt_1", "ses_1", "msg_1")] },
|
||||
permission: { ses_1: [] as PermissionRequest[] },
|
||||
|
|
@ -56,7 +53,6 @@ describe("app session cache", () => {
|
|||
expect(store.message.ses_1).toBeUndefined()
|
||||
expect(store.part.msg_1).toBeUndefined()
|
||||
expect(store.part_text_accum_delta.prt_1).toBeUndefined()
|
||||
expect(store.todo.ses_1).toBeUndefined()
|
||||
expect(store.session_diff.ses_1).toBeUndefined()
|
||||
expect(store.session_status.ses_1).toBeUndefined()
|
||||
expect(store.permission.ses_1).toBeUndefined()
|
||||
|
|
@ -67,8 +63,7 @@ describe("app session cache", () => {
|
|||
const m = msg("msg_1", "ses_1")
|
||||
const store: {
|
||||
session_status: Record<string, SessionStatus | undefined>
|
||||
session_diff: Record<string, SnapshotFileDiff[] | undefined>
|
||||
todo: Record<string, Todo[] | undefined>
|
||||
session_diff: Record<string, FileDiffInfo[] | undefined>
|
||||
message: Record<string, Message[] | undefined>
|
||||
part: Record<string, Part[] | undefined>
|
||||
permission: Record<string, PermissionRequest[] | undefined>
|
||||
|
|
@ -77,7 +72,6 @@ describe("app session cache", () => {
|
|||
} = {
|
||||
session_status: {},
|
||||
session_diff: {},
|
||||
todo: {},
|
||||
message: { ses_1: [m] },
|
||||
part: { [m.id]: [part("prt_1", "ses_1", m.id)] },
|
||||
permission: {},
|
||||
|
|
|
|||
|
|
@ -4,16 +4,14 @@ import type {
|
|||
PermissionRequest,
|
||||
QuestionRequest,
|
||||
SessionStatus,
|
||||
SnapshotFileDiff,
|
||||
Todo,
|
||||
FileDiffInfo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
|
||||
export const SESSION_CACHE_LIMIT = 40
|
||||
|
||||
type SessionCache = {
|
||||
session_status: Record<string, SessionStatus | undefined>
|
||||
session_diff: Record<string, SnapshotFileDiff[] | undefined>
|
||||
todo: Record<string, Todo[] | undefined>
|
||||
session_diff: Record<string, FileDiffInfo[] | undefined>
|
||||
message: Record<string, Message[] | undefined>
|
||||
part: Record<string, Part[] | undefined>
|
||||
permission: Record<string, PermissionRequest[] | undefined>
|
||||
|
|
@ -36,7 +34,6 @@ export function dropSessionCaches(store: SessionCache, sessionIDs: Iterable<stri
|
|||
|
||||
for (const sessionID of stale) {
|
||||
delete store.message[sessionID]
|
||||
delete store.todo[sessionID]
|
||||
delete store.session_diff[sessionID]
|
||||
delete store.session_status[sessionID]
|
||||
delete store.permission[sessionID]
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ import type {
|
|||
ReferenceInfo,
|
||||
Session,
|
||||
SessionStatus,
|
||||
SnapshotFileDiff,
|
||||
Todo,
|
||||
FileDiffInfo,
|
||||
VcsInfo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context"
|
||||
|
|
@ -51,10 +50,7 @@ export type State = {
|
|||
}
|
||||
session_working(id: string): boolean
|
||||
session_diff: {
|
||||
[sessionID: string]: SnapshotFileDiff[]
|
||||
}
|
||||
todo: {
|
||||
[sessionID: string]: Todo[]
|
||||
[sessionID: string]: FileDiffInfo[]
|
||||
}
|
||||
permission: {
|
||||
[sessionID: string]: PermissionRequest[]
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ type SessionView = {
|
|||
reviewOpen?: string[]
|
||||
pendingMessage?: string
|
||||
pendingMessageAt?: number
|
||||
todoCollapsed?: boolean
|
||||
}
|
||||
|
||||
type TabHandoff = {
|
||||
|
|
@ -836,18 +835,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||
setScroll(tab: string, pos: SessionScroll) {
|
||||
scroll.setScroll(key(), tab, pos)
|
||||
},
|
||||
todoCollapsed: {
|
||||
get: () => s().todoCollapsed ?? false,
|
||||
set(collapsed: boolean) {
|
||||
const session = key()
|
||||
const current = store.sessionView[session]
|
||||
if (!current) {
|
||||
setStore("sessionView", session, { scroll: {}, todoCollapsed: collapsed })
|
||||
} else {
|
||||
setStore("sessionView", session, "todoCollapsed", collapsed)
|
||||
}
|
||||
},
|
||||
},
|
||||
terminal: {
|
||||
opened: terminalOpened,
|
||||
open() {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ function setup(sessions: Record<string, Session>) {
|
|||
return response()
|
||||
},
|
||||
diff: async () => ({ data: [] }),
|
||||
todo: async () => ({ data: [] }),
|
||||
},
|
||||
} as unknown as OpencodeClient
|
||||
return { get, messages, store: createServerSession(client) }
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import type {
|
|||
QuestionRequest,
|
||||
Session,
|
||||
SessionStatus,
|
||||
SnapshotFileDiff,
|
||||
Todo,
|
||||
FileDiffInfo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { batch } from "solid-js"
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
|
|
@ -139,8 +138,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
const [data, setData] = createStore({
|
||||
info: {} as Record<string, Session | undefined>,
|
||||
session_status: {} as Record<string, SessionStatus>,
|
||||
session_diff: {} as Record<string, SnapshotFileDiff[]>,
|
||||
todo: {} as Record<string, Todo[]>,
|
||||
session_diff: {} as Record<string, FileDiffInfo[]>,
|
||||
permission: {} as Record<string, PermissionRequest[]>,
|
||||
question: {} as Record<string, QuestionRequest[]>,
|
||||
message: {} as Record<string, Message[]>,
|
||||
|
|
@ -153,7 +151,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
const requests = new Map<string, Promise<Session>>()
|
||||
const inflight = new Map<string, Promise<void>>()
|
||||
const inflightDiff = new Map<string, Promise<void>>()
|
||||
const inflightTodo = new Map<string, Promise<void>>()
|
||||
const optimistic = new Map<string, Map<string, OptimisticItem>>()
|
||||
const messageLoads = new Map<string, MessageLoadState>()
|
||||
const pendingParts = new Map<string, Map<string, Set<string>>>()
|
||||
|
|
@ -199,7 +196,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
...requests.keys(),
|
||||
...inflight.keys(),
|
||||
...inflightDiff.keys(),
|
||||
...inflightTodo.keys(),
|
||||
...messageLoads.keys(),
|
||||
...optimistic.keys(),
|
||||
...Object.entries(data.permission)
|
||||
|
|
@ -254,8 +250,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
!requests.has(sessionID) &&
|
||||
!messageLoads.has(sessionID) &&
|
||||
!inflight.has(sessionID) &&
|
||||
!inflightDiff.has(sessionID) &&
|
||||
!inflightTodo.has(sessionID)
|
||||
!inflightDiff.has(sessionID)
|
||||
)
|
||||
generations.delete(sessionID)
|
||||
}
|
||||
|
|
@ -418,7 +413,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
requests.delete(sessionID)
|
||||
inflight.delete(sessionID)
|
||||
inflightDiff.delete(sessionID)
|
||||
inflightTodo.delete(sessionID)
|
||||
messageLoads.delete(sessionID)
|
||||
pendingParts.delete(sessionID)
|
||||
orphanParts.delete(sessionID)
|
||||
|
|
@ -448,7 +442,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
...requests.keys(),
|
||||
...inflight.keys(),
|
||||
...inflightDiff.keys(),
|
||||
...inflightTodo.keys(),
|
||||
...messageLoads.keys(),
|
||||
...optimistic.keys(),
|
||||
...Object.entries(data.permission)
|
||||
|
|
@ -769,15 +762,10 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
return
|
||||
}
|
||||
case "session.diff": {
|
||||
const props = event.properties as { sessionID: string; diff: SnapshotFileDiff[] }
|
||||
const props = event.properties as { sessionID: string; diff: FileDiffInfo[] }
|
||||
setData("session_diff", props.sessionID, reconcile(cleanDiffs(props.diff), { key: "file" }))
|
||||
return
|
||||
}
|
||||
case "todo.updated": {
|
||||
const props = event.properties as { sessionID: string; todos: Todo[] }
|
||||
setData("todo", props.sessionID, reconcile(props.todos, { key: "id" }))
|
||||
return
|
||||
}
|
||||
case "session.status": {
|
||||
const props = event.properties as { sessionID: string; status: SessionStatus }
|
||||
setData("session_status", props.sessionID, reconcile(props.status))
|
||||
|
|
@ -1140,17 +1128,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
|||
})
|
||||
})
|
||||
},
|
||||
todo(sessionID: string, options?: { force?: boolean }) {
|
||||
touch(sessionID)
|
||||
if (data.todo[sessionID] !== undefined && !options?.force) return Promise.resolve()
|
||||
return runInflight(inflightTodo, sessionID, () => {
|
||||
const active = generation(sessionID)
|
||||
return retry(() => client.session.todo({ sessionID })).then((result) => {
|
||||
if (generations.get(sessionID) !== active) return
|
||||
setData("todo", sessionID, reconcile(result.data ?? [], { key: "id" }))
|
||||
})
|
||||
})
|
||||
},
|
||||
history: {
|
||||
more: (sessionID: string) =>
|
||||
data.message[sessionID] !== undefined &&
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ export type DraftTab = {
|
|||
|
||||
export type Tab = SessionTab | DraftTab
|
||||
|
||||
export type TabInfo = {
|
||||
title?: string
|
||||
directory?: string
|
||||
}
|
||||
|
||||
type RecentTab = {
|
||||
key?: string
|
||||
}
|
||||
|
|
@ -64,6 +69,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
createStore<Tab[]>([]),
|
||||
)
|
||||
const [recent, setRecent, , recentReady] = persisted(Persist.window("tabs.recent"), createStore<RecentTab>({}))
|
||||
const [info, setInfo] = persisted(Persist.window("tabs.info"), createStore<Record<string, TabInfo>>({}))
|
||||
const [closed, setClosed, , closedReady] = persisted(Persist.window("tabs.closed"), createStore<ClosedTab[]>([]))
|
||||
|
||||
const params = useParams()
|
||||
|
|
@ -102,6 +108,15 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
for (const key of draftPersistedKeys()) removePersisted(Persist.draft(draftID, key), platform)
|
||||
}
|
||||
|
||||
const removeInfo = (key: string) => {
|
||||
if (!info[key]) return
|
||||
setInfo(
|
||||
produce((draft) => {
|
||||
delete draft[key]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
onCleanup(memory.dispose)
|
||||
|
||||
createEffect(() => {
|
||||
|
|
@ -110,11 +125,19 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
const next = store.filter((tab) => servers.has(tab.server))
|
||||
if (next.length !== store.length) {
|
||||
for (const tab of store) {
|
||||
if (!servers.has(tab.server)) memory.remove(tabKey(tab))
|
||||
if (!servers.has(tab.server)) {
|
||||
const key = tabKey(tab)
|
||||
memory.remove(key)
|
||||
removeInfo(key)
|
||||
}
|
||||
}
|
||||
setStore(() => next)
|
||||
}
|
||||
if (recent.key && !next.some((tab) => tabKey(tab) === recent.key)) setRecentKey(undefined)
|
||||
const keys = new Set(next.map(tabKey))
|
||||
for (const key of Object.keys(info)) {
|
||||
if (!keys.has(key)) removeInfo(key)
|
||||
}
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
|
|
@ -150,6 +173,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
if (nextTab) navigateTab(nextTab)
|
||||
}).finally(() => closing.delete(key))
|
||||
memory.remove(key)
|
||||
removeInfo(key)
|
||||
if (draftID) removeDraftPersisted(draftID)
|
||||
}
|
||||
|
||||
|
|
@ -264,6 +288,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
const removed = store.filter((tab) => tab.server === key).map(tabKey)
|
||||
setStore((tabs) => tabs.filter((tab) => tab.server !== key))
|
||||
for (const key of removed) memory.remove(key)
|
||||
for (const key of removed) removeInfo(key)
|
||||
if (recent.key && removed.includes(recent.key)) setRecentKey(undefined)
|
||||
for (const draftID of drafts) removeDraftPersisted(draftID)
|
||||
if (server.key === key) navigate("/")
|
||||
|
|
@ -318,6 +343,14 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
if (recent.key && removed.includes(recent.key)) setRecentKey(undefined)
|
||||
})
|
||||
for (const key of removed) memory.remove(key)
|
||||
for (const key of removed) removeInfo(key)
|
||||
},
|
||||
rememberSessionInfo(tab: SessionTab, session: Session) {
|
||||
const key = tabKey(tab)
|
||||
const next = { title: session.title, directory: session.directory }
|
||||
const current = info[key]
|
||||
if (current?.title === next.title && current.directory === next.directory) return
|
||||
setInfo(key, next)
|
||||
},
|
||||
select: navigateTab,
|
||||
remember(tab: Tab) {
|
||||
|
|
@ -342,6 +375,6 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
|||
},
|
||||
}
|
||||
|
||||
return { ...actions, store, ready, recentReady }
|
||||
return { ...actions, store, info, ready, recentReady }
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "داكن",
|
||||
"command.sidebar.toggle": "تبديل الشريط الجانبي",
|
||||
"command.project.open": "فتح مشروع",
|
||||
"command.project.previous": "المشروع السابق",
|
||||
"command.project.next": "المشروع التالي",
|
||||
"command.project.index": "التبديل إلى المشروع {{index}}",
|
||||
"command.provider.connect": "اتصال بموفر",
|
||||
"command.server.switch": "تبديل الخادم",
|
||||
"command.settings.open": "فتح الإعدادات",
|
||||
|
|
@ -39,6 +42,7 @@ export const dict = {
|
|||
"command.session.new": "جلسة جديدة",
|
||||
"command.file.open": "فتح ملف",
|
||||
"command.tab.close": "إغلاق علامة التبويب",
|
||||
"command.tab.reopenClosed": "إعادة فتح علامة التبويب المغلقة",
|
||||
"command.context.addSelection": "إضافة التحديد إلى السياق",
|
||||
"command.context.addSelection.description": "إضافة الأسطر المحددة من الملف الحالي",
|
||||
"command.input.focus": "التركيز على حقل الإدخال",
|
||||
|
|
@ -193,6 +197,11 @@ export const dict = {
|
|||
"model.input.video": "فيديو",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "يسمح: {{inputs}}",
|
||||
"model.tooltip.context.label": "السياق",
|
||||
"model.tooltip.inputs": "المدخلات",
|
||||
"model.tooltip.model": "النموذج",
|
||||
"model.tooltip.provider": "الموفر",
|
||||
"model.tooltip.reasoning": "الاستنتاج",
|
||||
"model.tooltip.reasoning.allowed": "يسمح بالاستنتاج",
|
||||
"model.tooltip.reasoning.none": "بدون استنتاج",
|
||||
"model.tooltip.context": "حد السياق {{limit}}",
|
||||
|
|
@ -202,6 +211,7 @@ export const dict = {
|
|||
"common.loading": "جارٍ التحميل",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "إلغاء",
|
||||
"common.clear": "مسح",
|
||||
"common.connect": "اتصال",
|
||||
"common.disconnect": "قطع الاتصال",
|
||||
"common.continue": "إرسال",
|
||||
|
|
@ -218,6 +228,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc للخروج",
|
||||
"session.child.promptDisabled": "لا يمكن إرسال موجّهات إلى جلسات الوكلاء الفرعيين.",
|
||||
"session.child.backToParent": "العودة إلى الجلسة الرئيسية.",
|
||||
"prompt.example.1": "إصلاح TODO في قاعدة التعليمات البرمجية",
|
||||
"prompt.example.2": "ما هو المكدس التقني لهذا المشروع؟",
|
||||
"prompt.example.3": "إصلاح الاختبارات المعطلة",
|
||||
|
|
@ -314,6 +326,74 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "حذف",
|
||||
"dialog.server.current": "الخادم الحالي",
|
||||
"dialog.server.status.default": "افتراضي",
|
||||
"wsl.server.add": "إضافة خادم WSL",
|
||||
"wsl.server.addShort": "إضافة WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "خادم WSL",
|
||||
"wsl.server.retryStart": "إعادة محاولة التشغيل",
|
||||
"wsl.server.updating": "جارٍ التحديث...",
|
||||
"wsl.onboarding.step.distro": "اختر توزيعة",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "جارٍ التحقق من WSL...",
|
||||
"wsl.onboarding.restartRequired": "يحتاج Windows إلى إعادة التشغيل لإكمال تثبيت WSL.",
|
||||
"wsl.onboarding.ready": "WSL جاهز.",
|
||||
"wsl.onboarding.required": "يلزم WSL للمتابعة.",
|
||||
"wsl.onboarding.checkingDistros": "جارٍ التحقق من التوزيعات...",
|
||||
"wsl.onboarding.installingDistro": "جارٍ تثبيت {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "جارٍ التحقق من {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "جارٍ سرد التوزيعات...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} جاهزة.",
|
||||
"wsl.onboarding.distroNotInstalled": "لم يتم تثبيت {{distro}} بعد.",
|
||||
"wsl.onboarding.openDistroOnce": "افتح {{distro}} مرة واحدة لإكمال الإعداد.",
|
||||
"wsl.onboarding.finishingDistro": "جارٍ إكمال إعداد {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "اختر توزيعة أو ثبّت واحدة أدناه.",
|
||||
"wsl.onboarding.checkingOpencode": "جارٍ التحقق من OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "جارٍ التحقق من OpenCode في {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "جارٍ تحديث OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "جارٍ تحديث OpenCode في {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "حدّث OpenCode في {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "تحديث OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode جاهز في {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode جاهز.",
|
||||
"wsl.onboarding.installOpencodeIn": "ثبّت OpenCode في {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "تثبيت OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "اختر توزيعة أولاً.",
|
||||
"wsl.onboarding.loadFailed": "فشل تحميل حالة WSL.",
|
||||
"wsl.onboarding.loading": "جارٍ التحميل...",
|
||||
"wsl.onboarding.installedDistros": "التوزيعات المثبتة",
|
||||
"wsl.onboarding.checkAgain": "تحقق مجددًا",
|
||||
"wsl.onboarding.distroStatus.ready": "جاهزة",
|
||||
"wsl.onboarding.distroStatus.checking": "جارٍ التحقق...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode غير مثبت",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash و curl مفقودان",
|
||||
"wsl.onboarding.distroStatus.unsupported": "غير مدعومة · استخدم WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "هل تحتاج إلى توزيعة أخرى؟",
|
||||
"wsl.onboarding.needAnotherDistroHint": "ثبّت توزيعة Linux من كتالوج WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL غير مثبت",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"يلزم WSL (نظام Windows الفرعي لـ Linux) قبل أن يتمكن OpenCode من إضافة خادم WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL غير متاح",
|
||||
"wsl.onboarding.wslUnavailable.description": "تعذر على OpenCode التحقق من WSL على هذا الجهاز.",
|
||||
"wsl.onboarding.installWsl": "تثبيت WSL",
|
||||
"wsl.onboarding.windowsRestartRequired": "أعد تشغيل Windows لإكمال تثبيت WSL، ثم أعد فتح OpenCode.",
|
||||
"wsl.onboarding.next": "التالي",
|
||||
"wsl.onboarding.refresh": "تحديث",
|
||||
"wsl.onboarding.allDistrosAdded": "تمت إضافة جميع التوزيعات المثبتة بالفعل.",
|
||||
"wsl.onboarding.noDistros": "لم يتم اكتشاف أي توزيعات بعد.",
|
||||
"wsl.onboarding.install": "تثبيت",
|
||||
"wsl.onboarding.installing": "جارٍ التثبيت...",
|
||||
"wsl.onboarding.installDistro": "تثبيت توزيعة",
|
||||
"wsl.onboarding.searchDistros": "البحث عن توزيعات",
|
||||
"wsl.onboarding.wsl2Required": "يلزم WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "تحتاج هذه التوزيعة إلى bash و curl.",
|
||||
"wsl.onboarding.openTerminal": "فتح المحطة الطرفية",
|
||||
"wsl.onboarding.path": "المسار: {{path}}",
|
||||
"wsl.onboarding.notFound": "غير موجود",
|
||||
"wsl.onboarding.version": "الإصدار: {{version}}",
|
||||
"wsl.onboarding.unknown": "غير معروف",
|
||||
"wsl.onboarding.desktopVersion": "سطح المكتب {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "لا يتطابق الإصدار المثبت مع إصدار تطبيق سطح المكتب.",
|
||||
"wsl.onboarding.adding": "جارٍ الإضافة...",
|
||||
"dialog.project.edit.title": "تحرير المشروع",
|
||||
"dialog.project.edit.name": "الاسم",
|
||||
"dialog.project.edit.icon": "أيقونة",
|
||||
|
|
@ -370,6 +450,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "لغة",
|
||||
|
|
@ -406,10 +487,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "ليس الآن",
|
||||
"error.page.title": "حدث خطأ ما",
|
||||
"error.page.description": "حدث خطأ أثناء تحميل التطبيق.",
|
||||
"error.page.description.localServerStartup": "حدث خطأ أثناء بدء تشغيل الخادم المحلي.",
|
||||
"error.page.details.label": "تفاصيل الخطأ",
|
||||
"error.page.action.restart": "إعادة تشغيل",
|
||||
"error.page.action.report": "الإبلاغ عن الخطأ",
|
||||
"error.page.action.reported": "تم الإبلاغ عن الخطأ",
|
||||
"error.page.action.exportLogs": "تصدير السجلات",
|
||||
"error.page.action.checking": "جارٍ التحقق...",
|
||||
"error.page.action.checkUpdates": "التحقق من وجود تحديثات",
|
||||
"error.page.action.updateTo": "تحديث إلى {{version}}",
|
||||
|
|
@ -450,16 +533,38 @@ export const dict = {
|
|||
"home.recentProjects": "المشاريع الحديثة",
|
||||
"home.empty.title": "لا توجد مشاريع حديثة",
|
||||
"home.empty.description": "ابدأ بفتح مشروع محلي",
|
||||
"home.title": "الرئيسية",
|
||||
"home.projects": "المشاريع",
|
||||
"home.project.add": "إضافة مشروع",
|
||||
"home.recentlyClosed": "المغلقة مؤخرًا",
|
||||
"home.server.collapse": "طي مشاريع الخادم",
|
||||
"home.server.expand": "توسيع مشاريع الخادم",
|
||||
"home.sessions.search.placeholder": "البحث في الجلسات",
|
||||
"home.sessions.search.placeholder.scoped": "البحث في جلسات {{scope}}",
|
||||
"home.sessions.search.sessions": "الجلسات",
|
||||
"home.sessions.search.noResults": "لم يتم العثور على جلسات لـ {{query}}",
|
||||
"home.sessions.empty": "لا يوجد شيء هنا بعد",
|
||||
"home.sessions.empty.description": "أنشئ جلسة للبدء",
|
||||
"home.sessions.group.today": "اليوم",
|
||||
"home.sessions.group.yesterday": "أمس",
|
||||
"home.sessions.group.older": "الأقدم",
|
||||
"session.tab.session": "جلسة",
|
||||
"session.tab.review": "مراجعة",
|
||||
"session.tab.context": "سياق",
|
||||
"session.tab.unknown": "جلسة غير معروفة",
|
||||
"session.panel.reviewAndFiles": "المراجعة والملفات",
|
||||
"session.error.notFound": "لا يمكن العثور على هذه الجلسة",
|
||||
"session.error.notFound.description": "يشير هذا التبويب إلى جلسة لم تعد موجودة على هذا الخادم.",
|
||||
"session.error.notFound.closeTab": "إغلاق علامة التبويب",
|
||||
"session.error.serverConnection": "تعذر الاتصال بهذا الخادم",
|
||||
"session.review.filesChanged": "تم تغيير {{count}} ملفات",
|
||||
"session.review.change.one": "تغيير",
|
||||
"session.review.change.other": "تغييرات",
|
||||
"session.review.loadingChanges": "جارٍ تحميل التغييرات...",
|
||||
"session.review.empty": "لا توجد تغييرات في هذه الجلسة بعد",
|
||||
"session.review.noChanges": "لا توجد تغييرات",
|
||||
"session.review.noUncommittedChanges": "لا توجد تغييرات غير مُودَعة بعد",
|
||||
"session.review.noBranchChanges": "لا توجد تغييرات في الفرع بعد",
|
||||
"session.review.noVcs": "لم يتم اكتشاف نظام التحكم في الإصدار Git، لن يتم عرض التغييرات",
|
||||
"session.review.noSnapshot": "تم تعطيل تتبع اللقطات في التكوين، لذا فإن تغييرات الجلسة غير متوفرة",
|
||||
"session.files.selectToOpen": "اختر ملفًا لفتحه",
|
||||
|
|
@ -472,9 +577,10 @@ export const dict = {
|
|||
"session.messages.loading": "جارٍ تحميل الرسائل...",
|
||||
"session.messages.jumpToLatest": "الانتقال إلى الأحدث",
|
||||
"session.context.addToContext": "إضافة {{selection}} إلى السياق",
|
||||
"session.todo.title": "المهام",
|
||||
"session.todo.collapse": "طي",
|
||||
"session.todo.expand": "توسيع",
|
||||
"session.question.minimize": "تصغير السؤال",
|
||||
"session.question.restore": "استعادة السؤال",
|
||||
"session.question.pending.one": "{{count}} سؤال معلق",
|
||||
"session.question.pending.other": "الأسئلة المعلقة: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} رسالة في الانتظار",
|
||||
"session.followupDock.summary.other": "{{count}} رسائل في الانتظار",
|
||||
"session.followupDock.sendNow": "إرسال الآن",
|
||||
|
|
@ -487,9 +593,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "توسيع الرسائل التي تم التراجع عنها",
|
||||
"session.revertDock.restore": "استعادة الرسالة",
|
||||
"session.new.title": "ابنِ أي شيء",
|
||||
"session.new.project.new": "مشروع جديد",
|
||||
"session.new.project.search": "البحث عن المشاريع",
|
||||
"session.new.project.add": "إضافة مشروع",
|
||||
"session.new.worktree.main": "الفرع الرئيسي",
|
||||
"session.new.worktree.mainWithBranch": "الفرع الرئيسي ({{branch}})",
|
||||
"session.new.worktree.create": "إنشاء شجرة عمل جديدة",
|
||||
"session.new.workspace.runIn": "تشغيل الجلسة في",
|
||||
"session.new.workspace.triggerLocal": "محلي",
|
||||
"session.new.workspace.local": "المستودع المحلي",
|
||||
"session.new.workspace.existing": "مساحة عمل…",
|
||||
"session.new.lastModified": "آخر تعديل",
|
||||
"session.header.search.placeholder": "بحث {{project}}",
|
||||
"session.header.searchFiles": "بحث عن الملفات",
|
||||
|
|
@ -550,6 +663,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "الجلسات الحديثة",
|
||||
"sidebar.project.viewAllSessions": "عرض جميع الجلسات",
|
||||
"sidebar.project.clearNotifications": "مسح الإشعارات",
|
||||
"sidebar.empty.title": "لا توجد مشاريع مفتوحة",
|
||||
"sidebar.empty.description": "افتح مشروعًا للبدء",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "سطح المكتب",
|
||||
"settings.section.server": "الخادم",
|
||||
|
|
@ -559,6 +674,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.title": "تكامل WSL",
|
||||
"settings.desktop.wsl.description": "تشغيل خادم OpenCode داخل WSL على Windows.",
|
||||
"settings.general.section.appearance": "المظهر",
|
||||
"settings.general.section.advanced": "متقدم",
|
||||
"settings.general.section.notifications": "إشعارات النظام",
|
||||
"settings.general.section.updates": "التحديثات",
|
||||
"settings.general.section.sounds": "المؤثرات الصوتية",
|
||||
|
|
@ -566,6 +682,11 @@ export const dict = {
|
|||
"settings.general.section.display": "شاشة العرض",
|
||||
"settings.general.row.language.title": "اللغة",
|
||||
"settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode",
|
||||
"settings.general.row.shell.title": "Shell المحطة الطرفية",
|
||||
"settings.general.row.shell.description":
|
||||
"اختر shell المستخدم في المحطة الطرفية. تُستخدم واجهات shell المتوافقة أيضًا في استدعاءات أدوات الوكيل.",
|
||||
"settings.general.row.shell.autoDefault": "تلقائي (افتراضي)",
|
||||
"settings.general.row.shell.terminalOnly": "المحطة الطرفية فقط",
|
||||
"settings.general.row.appearance.title": "المظهر",
|
||||
"settings.general.row.appearance.description": "تخصيص كيفية ظهور OpenCode على جهازك",
|
||||
"settings.general.row.colorScheme.title": "مخطط الألوان",
|
||||
|
|
@ -574,14 +695,29 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "تخصيص سمة OpenCode.",
|
||||
"settings.general.row.font.title": "خط الكود",
|
||||
"settings.general.row.font.description": "خصّص الخط المستخدم في كتل التعليمات البرمجية",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "خط الطرفية",
|
||||
"settings.general.row.terminalFont.description": "خصّص الخط المستخدم في الطرفية",
|
||||
"settings.general.row.uiFont.title": "خط الواجهة",
|
||||
"settings.general.row.uiFont.description": "خصّص الخط المستخدم في الواجهة بأكملها",
|
||||
"settings.general.row.followup.title": "سلوك المتابعة",
|
||||
"settings.general.row.followup.description": "اختر ما إذا كانت طلبات المتابعة توجه فورًا أو تنتظر في قائمة انتظار",
|
||||
"settings.general.row.followup.option.queue": "قائمة انتظار",
|
||||
"settings.general.row.followup.option.steer": "توجيه",
|
||||
"settings.general.row.showFileTree.title": "شجرة الملفات",
|
||||
"settings.general.row.showFileTree.description": "إظهار لوحة شجرة الملفات في الجلسات",
|
||||
"settings.general.row.showNavigation.title": "عناصر التحكم في التنقل",
|
||||
"settings.general.row.showNavigation.description": "إظهار زري الرجوع والتقدم في شريط عنوان سطح المكتب",
|
||||
"settings.general.row.showSearch.title": "لوحة الأوامر",
|
||||
"settings.general.row.showSearch.description": "إظهار زر البحث ولوحة الأوامر في شريط العنوان",
|
||||
"settings.general.row.showTerminal.title": "المحطة الطرفية",
|
||||
"settings.general.row.showTerminal.description": "إظهار زر المحطة الطرفية في شريط عنوان سطح المكتب",
|
||||
"settings.general.row.showStatus.title": "حالة الخادم",
|
||||
"settings.general.row.showStatus.description": "إظهار زر حالة الخادم في شريط العنوان",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "التنقل السفلي",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"وضع شريط العنوان وعلامات تبويب الجلسات أسفل الشاشة على الأجهزة المحمولة",
|
||||
"settings.general.row.showCustomAgents.title": "الوكلاء المخصصون",
|
||||
"settings.general.row.showCustomAgents.description": "إظهار محدد الوكلاء في محرر الرسائل",
|
||||
"settings.general.row.reasoningSummaries.title": "إظهار ملخصات الاستنتاج",
|
||||
"settings.general.row.reasoningSummaries.description": "عرض ملخصات استنتاج النموذج في الشريط الزمني",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "توسيع أجزاء أداة shell",
|
||||
|
|
@ -590,6 +726,11 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "توسيع أجزاء أداة edit",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"إظهار أجزاء أدوات edit و write و patch موسعة بشكل افتراضي في الشريط الزمني",
|
||||
"settings.general.row.newLayoutDesigns.title": "التخطيط والتصاميم الجديدة",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"تمكين التخطيط والصفحة الرئيسية ومحرر الرسائل وواجهة الجلسة المعاد تصميمها",
|
||||
"settings.general.row.pinchZoom.title": "التكبير بإيماءة القرص",
|
||||
"settings.general.row.pinchZoom.description": "السماح بإيماءة القرص على لوحة اللمس وإيماءة Ctrl-scroll للتكبير",
|
||||
"settings.general.row.wayland.title": "استخدام Wayland الأصلي",
|
||||
"settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -602,6 +743,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "التحقق يدويًا من التحديثات وتثبيتها إذا كانت متاحة",
|
||||
"settings.updates.action.checkNow": "تحقق الآن",
|
||||
"settings.updates.action.checking": "جارٍ التحقق...",
|
||||
"settings.updates.action.downloading": "جارٍ التنزيل...",
|
||||
"settings.updates.action.installing": "جارٍ التثبيت...",
|
||||
"settings.updates.toast.latest.title": "أنت على آخر إصدار",
|
||||
"settings.updates.toast.latest.description": "أنت تستخدم أحدث إصدار من OpenCode.",
|
||||
"sound.option.none": "بلا",
|
||||
|
|
@ -721,8 +864,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "تحميل مهارة بالاسم",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "تشغيل استعلامات خادم اللغة",
|
||||
"settings.permissions.tool.todowrite.title": "كتابة المهام",
|
||||
"settings.permissions.tool.todowrite.description": "تحديث قائمة المهام",
|
||||
"settings.permissions.tool.webfetch.title": "جلب الويب",
|
||||
"settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL",
|
||||
"settings.permissions.tool.websearch.title": "بحث الويب",
|
||||
|
|
@ -785,7 +926,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "تتبع ومراجعة والتراجع عن التغييرات في هذا المشروع",
|
||||
"session.review.noVcs.createGit.actionLoading": "جاري إنشاء مستودع Git...",
|
||||
"session.review.noVcs.createGit.action": "إنشاء مستودع Git",
|
||||
"session.todo.progress": "تم إكمال {{done}} من {{total}} مهام",
|
||||
"session.question.progress": "{{current}} من {{total}} أسئلة",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "مستكشف الملفات",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "Escuro",
|
||||
"command.sidebar.toggle": "Alternar barra lateral",
|
||||
"command.project.open": "Abrir projeto",
|
||||
"command.project.previous": "Projeto anterior",
|
||||
"command.project.next": "Próximo projeto",
|
||||
"command.project.index": "Alternar para o projeto {{index}}",
|
||||
"command.provider.connect": "Conectar provedor",
|
||||
"command.server.switch": "Trocar servidor",
|
||||
"command.settings.open": "Abrir configurações",
|
||||
|
|
@ -39,6 +42,7 @@ export const dict = {
|
|||
"command.session.new": "Nova sessão",
|
||||
"command.file.open": "Abrir arquivo",
|
||||
"command.tab.close": "Fechar aba",
|
||||
"command.tab.reopenClosed": "Reabrir aba fechada",
|
||||
"command.context.addSelection": "Adicionar seleção ao contexto",
|
||||
"command.context.addSelection.description": "Adicionar as linhas selecionadas do arquivo atual",
|
||||
"command.input.focus": "Focar entrada",
|
||||
|
|
@ -193,6 +197,11 @@ export const dict = {
|
|||
"model.input.video": "vídeo",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Permite: {{inputs}}",
|
||||
"model.tooltip.context.label": "Contexto",
|
||||
"model.tooltip.inputs": "Entradas",
|
||||
"model.tooltip.model": "Modelo",
|
||||
"model.tooltip.provider": "Provedor",
|
||||
"model.tooltip.reasoning": "Raciocínio",
|
||||
"model.tooltip.reasoning.allowed": "Permite raciocínio",
|
||||
"model.tooltip.reasoning.none": "Sem raciocínio",
|
||||
"model.tooltip.context": "Limite de contexto {{limit}}",
|
||||
|
|
@ -202,6 +211,7 @@ export const dict = {
|
|||
"common.loading": "Carregando",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Cancelar",
|
||||
"common.clear": "Limpar",
|
||||
"common.connect": "Conectar",
|
||||
"common.disconnect": "Desconectar",
|
||||
"common.continue": "Enviar",
|
||||
|
|
@ -218,6 +228,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc para sair",
|
||||
"session.child.promptDisabled": "Não é possível enviar prompts em sessões de subagentes.",
|
||||
"session.child.backToParent": "Voltar à sessão principal.",
|
||||
"prompt.example.1": "Corrigir um TODO no código",
|
||||
"prompt.example.2": "Qual é a stack tecnológica deste projeto?",
|
||||
"prompt.example.3": "Corrigir testes quebrados",
|
||||
|
|
@ -314,6 +326,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Excluir",
|
||||
"dialog.server.current": "Servidor atual",
|
||||
"dialog.server.status.default": "Padrão",
|
||||
"wsl.server.add": "Adicionar servidor WSL",
|
||||
"wsl.server.addShort": "Adicionar WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Servidor WSL",
|
||||
"wsl.server.retryStart": "Tentar iniciar novamente",
|
||||
"wsl.server.updating": "Atualizando...",
|
||||
"wsl.onboarding.step.distro": "Escolher distribuição",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Verificando o WSL...",
|
||||
"wsl.onboarding.restartRequired": "O Windows precisa ser reiniciado para concluir a instalação do WSL.",
|
||||
"wsl.onboarding.ready": "O WSL está pronto.",
|
||||
"wsl.onboarding.required": "O WSL é necessário para continuar.",
|
||||
"wsl.onboarding.checkingDistros": "Verificando distribuições...",
|
||||
"wsl.onboarding.installingDistro": "Instalando {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Verificando {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Listando distribuições...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} está pronta.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ainda não está instalada.",
|
||||
"wsl.onboarding.openDistroOnce": "Abra {{distro}} uma vez para concluir a configuração.",
|
||||
"wsl.onboarding.finishingDistro": "Concluindo a configuração de {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Escolha uma distribuição ou instale uma abaixo.",
|
||||
"wsl.onboarding.checkingOpencode": "Verificando o OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Verificando o OpenCode em {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Atualizando o OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Atualizando o OpenCode em {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Atualize o OpenCode em {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Atualizar o OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "O OpenCode está pronto em {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "O OpenCode está pronto.",
|
||||
"wsl.onboarding.installOpencodeIn": "Instale o OpenCode em {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Instalar o OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Escolha primeiro uma distribuição.",
|
||||
"wsl.onboarding.loadFailed": "Falha ao carregar o estado do WSL.",
|
||||
"wsl.onboarding.loading": "Carregando...",
|
||||
"wsl.onboarding.installedDistros": "Distribuições instaladas",
|
||||
"wsl.onboarding.checkAgain": "Verificar novamente",
|
||||
"wsl.onboarding.distroStatus.ready": "Pronta",
|
||||
"wsl.onboarding.distroStatus.checking": "Verificando...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode não instalado",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash e curl ausentes",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Sem suporte · Use o WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Precisa de outra distribuição?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Instale uma distribuição Linux pelo catálogo do WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL não instalado",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"O WSL (Windows Subsystem for Linux) é necessário para que o OpenCode possa adicionar um servidor WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL indisponível",
|
||||
"wsl.onboarding.wslUnavailable.description": "O OpenCode não conseguiu verificar o WSL neste computador.",
|
||||
"wsl.onboarding.installWsl": "Instalar o WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Reinicie o Windows para concluir a instalação do WSL e abra o OpenCode novamente.",
|
||||
"wsl.onboarding.next": "Próximo",
|
||||
"wsl.onboarding.refresh": "Atualizar",
|
||||
"wsl.onboarding.allDistrosAdded": "Todas as distribuições instaladas já foram adicionadas.",
|
||||
"wsl.onboarding.noDistros": "Nenhuma distribuição detectada ainda.",
|
||||
"wsl.onboarding.install": "Instalar",
|
||||
"wsl.onboarding.installing": "Instalando...",
|
||||
"wsl.onboarding.installDistro": "Instalar distribuição",
|
||||
"wsl.onboarding.searchDistros": "Buscar distribuições",
|
||||
"wsl.onboarding.wsl2Required": "O WSL 2 é necessário.",
|
||||
"wsl.onboarding.toolsRequired": "Esta distribuição precisa do bash e do curl.",
|
||||
"wsl.onboarding.openTerminal": "Abrir terminal",
|
||||
"wsl.onboarding.path": "Caminho: {{path}}",
|
||||
"wsl.onboarding.notFound": "não encontrado",
|
||||
"wsl.onboarding.version": "Versão: {{version}}",
|
||||
"wsl.onboarding.unknown": "desconhecido",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "A versão instalada não corresponde à versão do aplicativo para desktop.",
|
||||
"wsl.onboarding.adding": "Adicionando...",
|
||||
"dialog.project.edit.title": "Editar projeto",
|
||||
"dialog.project.edit.name": "Nome",
|
||||
"dialog.project.edit.icon": "Ícone",
|
||||
|
|
@ -371,6 +452,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Idioma",
|
||||
|
|
@ -407,10 +489,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "Agora não",
|
||||
"error.page.title": "Algo deu errado",
|
||||
"error.page.description": "Ocorreu um erro ao carregar a aplicação.",
|
||||
"error.page.description.localServerStartup": "Ocorreu um erro ao iniciar o servidor local.",
|
||||
"error.page.details.label": "Detalhes do Erro",
|
||||
"error.page.action.restart": "Reiniciar",
|
||||
"error.page.action.report": "Reportar erro",
|
||||
"error.page.action.reported": "Erro reportado",
|
||||
"error.page.action.exportLogs": "Exportar logs",
|
||||
"error.page.action.checking": "Verificando...",
|
||||
"error.page.action.checkUpdates": "Verificar atualizações",
|
||||
"error.page.action.updateTo": "Atualizar para {{version}}",
|
||||
|
|
@ -453,10 +537,30 @@ export const dict = {
|
|||
"home.recentProjects": "Projetos recentes",
|
||||
"home.empty.title": "Nenhum projeto recente",
|
||||
"home.empty.description": "Comece abrindo um projeto local",
|
||||
"home.title": "Início",
|
||||
"home.projects": "Projetos",
|
||||
"home.project.add": "Adicionar projeto",
|
||||
"home.recentlyClosed": "Projetos fechados recentemente",
|
||||
"home.server.collapse": "Recolher projetos do servidor",
|
||||
"home.server.expand": "Expandir projetos do servidor",
|
||||
"home.sessions.search.placeholder": "Buscar sessões",
|
||||
"home.sessions.search.placeholder.scoped": "Buscar sessões em {{scope}}",
|
||||
"home.sessions.search.sessions": "Sessões",
|
||||
"home.sessions.search.noResults": "Nenhuma sessão encontrada para {{query}}",
|
||||
"home.sessions.empty": "Nada por aqui ainda",
|
||||
"home.sessions.empty.description": "Crie uma sessão para começar",
|
||||
"home.sessions.group.today": "Hoje",
|
||||
"home.sessions.group.yesterday": "Ontem",
|
||||
"home.sessions.group.older": "Mais antigas",
|
||||
"session.tab.session": "Sessão",
|
||||
"session.tab.review": "Revisão",
|
||||
"session.tab.context": "Contexto",
|
||||
"session.tab.unknown": "Sessão desconhecida",
|
||||
"session.panel.reviewAndFiles": "Revisão e arquivos",
|
||||
"session.error.notFound": "Esta sessão não foi encontrada",
|
||||
"session.error.notFound.description": "Esta aba aponta para uma sessão que não existe mais neste servidor.",
|
||||
"session.error.notFound.closeTab": "Fechar aba",
|
||||
"session.error.serverConnection": "Não é possível conectar a este servidor",
|
||||
"session.review.filesChanged": "{{count}} Arquivos Alterados",
|
||||
"session.review.change.one": "Alteração",
|
||||
"session.review.change.other": "Alterações",
|
||||
|
|
@ -466,6 +570,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"O rastreamento de snapshot está desabilitado na configuração, então as alterações da sessão estão indisponíveis",
|
||||
"session.review.noChanges": "Sem alterações",
|
||||
"session.review.noUncommittedChanges": "Ainda não há alterações sem commit",
|
||||
"session.review.noBranchChanges": "Ainda não há alterações na branch",
|
||||
"session.files.selectToOpen": "Selecione um arquivo para abrir",
|
||||
"session.files.all": "Todos os arquivos",
|
||||
"session.files.empty": "Nenhum arquivo",
|
||||
|
|
@ -476,9 +582,10 @@ export const dict = {
|
|||
"session.messages.loading": "Carregando mensagens...",
|
||||
"session.messages.jumpToLatest": "Ir para a mais recente",
|
||||
"session.context.addToContext": "Adicionar {{selection}} ao contexto",
|
||||
"session.todo.title": "Tarefas",
|
||||
"session.todo.collapse": "Recolher",
|
||||
"session.todo.expand": "Expandir",
|
||||
"session.question.minimize": "Minimizar pergunta",
|
||||
"session.question.restore": "Restaurar pergunta",
|
||||
"session.question.pending.one": "{{count}} pergunta pendente",
|
||||
"session.question.pending.other": "{{count}} perguntas pendentes",
|
||||
"session.followupDock.summary.one": "{{count}} mensagem na fila",
|
||||
"session.followupDock.summary.other": "{{count}} mensagens na fila",
|
||||
"session.followupDock.sendNow": "Enviar agora",
|
||||
|
|
@ -491,9 +598,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "Expandir mensagens revertidas",
|
||||
"session.revertDock.restore": "Restaurar mensagem",
|
||||
"session.new.title": "Crie qualquer coisa",
|
||||
"session.new.project.new": "Novo projeto",
|
||||
"session.new.project.search": "Buscar projetos",
|
||||
"session.new.project.add": "Adicionar projeto",
|
||||
"session.new.worktree.main": "Branch principal",
|
||||
"session.new.worktree.mainWithBranch": "Branch principal ({{branch}})",
|
||||
"session.new.worktree.create": "Criar novo worktree",
|
||||
"session.new.workspace.runIn": "Executar sessão em",
|
||||
"session.new.workspace.triggerLocal": "Local",
|
||||
"session.new.workspace.local": "Repositório local",
|
||||
"session.new.workspace.existing": "Espaço de trabalho…",
|
||||
"session.new.lastModified": "Última modificação",
|
||||
"session.header.search.placeholder": "Buscar {{project}}",
|
||||
"session.header.searchFiles": "Buscar arquivos",
|
||||
|
|
@ -557,15 +671,18 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Sessões recentes",
|
||||
"sidebar.project.viewAllSessions": "Ver todas as sessões",
|
||||
"sidebar.project.clearNotifications": "Limpar notificações",
|
||||
"sidebar.empty.title": "Nenhum projeto aberto",
|
||||
"sidebar.empty.description": "Abra um projeto para começar",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Desktop",
|
||||
"settings.section.server": "Servidor",
|
||||
"settings.tab.general": "Geral",
|
||||
"settings.tab.shortcuts": "Atalhos",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integration",
|
||||
"settings.desktop.wsl.title": "Integração com WSL",
|
||||
"settings.desktop.wsl.description": "Executar o servidor OpenCode dentro do WSL no Windows.",
|
||||
"settings.general.section.appearance": "Aparência",
|
||||
"settings.general.section.advanced": "Avançado",
|
||||
"settings.general.section.notifications": "Notificações do sistema",
|
||||
"settings.general.section.updates": "Atualizações",
|
||||
"settings.general.section.sounds": "Efeitos sonoros",
|
||||
|
|
@ -573,6 +690,11 @@ export const dict = {
|
|||
"settings.general.section.display": "Tela",
|
||||
"settings.general.row.language.title": "Idioma",
|
||||
"settings.general.row.language.description": "Alterar o idioma de exibição do OpenCode",
|
||||
"settings.general.row.shell.title": "Shell do terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Escolha o shell usado no terminal. Os shells compatíveis também são usados nas chamadas de ferramentas do agente.",
|
||||
"settings.general.row.shell.autoDefault": "Automático (Padrão)",
|
||||
"settings.general.row.shell.terminalOnly": "somente no terminal",
|
||||
"settings.general.row.appearance.title": "Aparência",
|
||||
"settings.general.row.appearance.description": "Personalize como o OpenCode aparece no seu dispositivo",
|
||||
"settings.general.row.colorScheme.title": "Esquema de cores",
|
||||
|
|
@ -581,8 +703,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.",
|
||||
"settings.general.row.font.title": "Fonte de código",
|
||||
"settings.general.row.font.description": "Personalize a fonte usada em blocos de código",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Fonte do terminal",
|
||||
"settings.general.row.terminalFont.description": "Personalize a fonte usada no terminal",
|
||||
"settings.general.row.uiFont.title": "Fonte da interface",
|
||||
"settings.general.row.uiFont.description": "Personalize a fonte usada em toda a interface",
|
||||
"settings.general.row.followup.title": "Comportamento de acompanhamento",
|
||||
|
|
@ -590,6 +712,22 @@ export const dict = {
|
|||
"Escolha se os prompts de acompanhamento orientam imediatamente ou esperam na fila",
|
||||
"settings.general.row.followup.option.queue": "Fila",
|
||||
"settings.general.row.followup.option.steer": "Orientar",
|
||||
"settings.general.row.showFileTree.title": "Árvore de arquivos",
|
||||
"settings.general.row.showFileTree.description": "Mostrar o painel da árvore de arquivos nas sessões",
|
||||
"settings.general.row.showNavigation.title": "Controles de navegação",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Mostrar os botões de voltar e avançar na barra de título do desktop",
|
||||
"settings.general.row.showSearch.title": "Paleta de comandos",
|
||||
"settings.general.row.showSearch.description": "Mostrar o botão de busca e paleta de comandos na barra de título",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Mostrar o botão do terminal na barra de título do desktop",
|
||||
"settings.general.row.showStatus.title": "Status do servidor",
|
||||
"settings.general.row.showStatus.description": "Mostrar o botão de status do servidor na barra de título",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navegação inferior",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Posicionar a barra de título e as abas da sessão na parte inferior da tela em dispositivos móveis",
|
||||
"settings.general.row.showCustomAgents.title": "Agentes personalizados",
|
||||
"settings.general.row.showCustomAgents.description": "Mostrar o seletor de agentes na área de composição",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resumos de raciocínio",
|
||||
"settings.general.row.reasoningSummaries.description": "Exibir resumos de raciocínio do modelo na linha do tempo",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Expandir partes da ferramenta shell",
|
||||
|
|
@ -598,6 +736,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Expandir partes da ferramenta de edição",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Mostrar partes das ferramentas de edição, escrita e patch expandidas por padrão na linha do tempo",
|
||||
"settings.general.row.newLayoutDesigns.title": "Novo layout e design",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Ativar o layout, a página inicial, a área de composição e a interface de sessão reformulados",
|
||||
"settings.general.row.pinchZoom.title": "Zoom com gesto de pinça",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Permitir gestos de pinça no trackpad e de Ctrl+rolagem para aplicar zoom",
|
||||
"settings.general.row.wayland.title": "Usar Wayland nativo",
|
||||
"settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -610,6 +754,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Verificar atualizações manualmente e instalar se houver",
|
||||
"settings.updates.action.checkNow": "Verificar agora",
|
||||
"settings.updates.action.checking": "Verificando...",
|
||||
"settings.updates.action.downloading": "Baixando...",
|
||||
"settings.updates.action.installing": "Instalando...",
|
||||
"settings.updates.toast.latest.title": "Você está atualizado",
|
||||
"settings.updates.toast.latest.description": "Você está usando a versão mais recente do OpenCode.",
|
||||
"sound.option.none": "Nenhum",
|
||||
|
|
@ -731,8 +877,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Carregar uma habilidade por nome",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Executar consultas de servidor de linguagem",
|
||||
"settings.permissions.tool.todowrite.title": "Escrever Tarefas",
|
||||
"settings.permissions.tool.todowrite.description": "Atualizar a lista de tarefas",
|
||||
"settings.permissions.tool.webfetch.title": "Buscar Web",
|
||||
"settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL",
|
||||
"settings.permissions.tool.websearch.title": "Pesquisa Web",
|
||||
|
|
@ -795,7 +939,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Rastreie, revise e desfaça alterações neste projeto",
|
||||
"session.review.noVcs.createGit.actionLoading": "Criando repositório Git...",
|
||||
"session.review.noVcs.createGit.action": "Criar repositório Git",
|
||||
"session.todo.progress": "{{done}} de {{total}} tarefas concluídas",
|
||||
"session.question.progress": "{{current}} de {{total}} perguntas",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorador de Arquivos",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "Prikaži/sakrij bočnu traku",
|
||||
"command.project.open": "Otvori projekat",
|
||||
"command.project.previous": "Prethodni projekat",
|
||||
"command.project.next": "Sljedeći projekat",
|
||||
"command.project.index": "Prebaci na projekat {{index}}",
|
||||
"command.provider.connect": "Poveži provajdera",
|
||||
"command.server.switch": "Promijeni server",
|
||||
"command.settings.open": "Otvori postavke",
|
||||
|
|
@ -45,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "Nova sesija",
|
||||
"command.file.open": "Otvori datoteku",
|
||||
"command.tab.close": "Zatvori karticu",
|
||||
"command.tab.reopenClosed": "Ponovo otvori zatvorenu karticu",
|
||||
"command.context.addSelection": "Dodaj odabir u kontekst",
|
||||
"command.context.addSelection.description": "Dodaj odabrane linije iz trenutne datoteke",
|
||||
"command.input.focus": "Fokusiraj polje za unos",
|
||||
|
|
@ -209,6 +213,11 @@ export const dict = {
|
|||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Podržava: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontekst",
|
||||
"model.tooltip.inputs": "Ulazi",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Provajder",
|
||||
"model.tooltip.reasoning": "Rasuđivanje",
|
||||
"model.tooltip.reasoning.allowed": "Podržava rasuđivanje",
|
||||
"model.tooltip.reasoning.none": "Bez rasuđivanja",
|
||||
"model.tooltip.context": "Limit konteksta {{limit}}",
|
||||
|
|
@ -219,6 +228,7 @@ export const dict = {
|
|||
"common.loading": "Učitavanje",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Otkaži",
|
||||
"common.clear": "Očisti",
|
||||
"common.connect": "Poveži",
|
||||
"common.disconnect": "Prekini vezu",
|
||||
"common.continue": "Pošalji",
|
||||
|
|
@ -236,6 +246,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc za izlaz",
|
||||
"session.child.promptDisabled": "Sesijama podagenta nije moguće slati upite.",
|
||||
"session.child.backToParent": "Nazad na glavnu sesiju.",
|
||||
|
||||
"prompt.example.1": "Popravi TODO u bazi koda",
|
||||
"prompt.example.2": "Koji je tehnološki stack ovog projekta?",
|
||||
|
|
@ -342,6 +354,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Izbriši",
|
||||
"dialog.server.current": "Trenutni server",
|
||||
"dialog.server.status.default": "Podrazumijevano",
|
||||
"wsl.server.add": "Dodaj WSL server",
|
||||
"wsl.server.addShort": "Dodaj WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL server",
|
||||
"wsl.server.retryStart": "Ponovi pokretanje",
|
||||
"wsl.server.updating": "Ažuriranje...",
|
||||
"wsl.onboarding.step.distro": "Odaberi distribuciju",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Provjera WSL-a...",
|
||||
"wsl.onboarding.restartRequired": "Potrebno je ponovo pokrenuti Windows da bi se završila instalacija WSL-a.",
|
||||
"wsl.onboarding.ready": "WSL je spreman.",
|
||||
"wsl.onboarding.required": "WSL je potreban za nastavak.",
|
||||
"wsl.onboarding.checkingDistros": "Provjera distribucija...",
|
||||
"wsl.onboarding.installingDistro": "Instaliranje {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Provjera {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Učitavanje liste distribucija...",
|
||||
"wsl.onboarding.distroReady": "Distribucija {{distro}} je spremna.",
|
||||
"wsl.onboarding.distroNotInstalled": "Distribucija {{distro}} još nije instalirana.",
|
||||
"wsl.onboarding.openDistroOnce": "Otvori {{distro}} jednom da završiš postavljanje.",
|
||||
"wsl.onboarding.finishingDistro": "Završavanje postavljanja za {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Odaberi distribuciju ili instaliraj jednu ispod.",
|
||||
"wsl.onboarding.checkingOpencode": "Provjera OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Provjera OpenCode u distribuciji {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Ažuriranje OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Ažuriranje OpenCode u distribuciji {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Ažuriraj OpenCode u distribuciji {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Ažuriraj OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode je spreman u distribuciji {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode je spreman.",
|
||||
"wsl.onboarding.installOpencodeIn": "Instaliraj OpenCode u distribuciji {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Instaliraj OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Prvo odaberi distribuciju.",
|
||||
"wsl.onboarding.loadFailed": "Nije uspjelo učitavanje WSL stanja.",
|
||||
"wsl.onboarding.loading": "Učitavanje...",
|
||||
"wsl.onboarding.installedDistros": "Instalirane distribucije",
|
||||
"wsl.onboarding.checkAgain": "Provjeri ponovo",
|
||||
"wsl.onboarding.distroStatus.ready": "Spremno",
|
||||
"wsl.onboarding.distroStatus.checking": "Provjera...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode nije instaliran",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Nedostaju bash i curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nije podržano · Koristi WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Treba ti druga distribucija?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Instaliraj Linux distribuciju iz WSL kataloga",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL nije instaliran",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) je potreban da bi OpenCode mogao dodati WSL server",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL nije dostupan",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode nije mogao provjeriti WSL na ovom uređaju.",
|
||||
"wsl.onboarding.installWsl": "Instaliraj WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Ponovo pokreni Windows da završiš instalaciju WSL-a, zatim ponovo otvori OpenCode.",
|
||||
"wsl.onboarding.next": "Dalje",
|
||||
"wsl.onboarding.refresh": "Osvježi",
|
||||
"wsl.onboarding.allDistrosAdded": "Sve instalirane distribucije su već dodane.",
|
||||
"wsl.onboarding.noDistros": "Još nema otkrivenih distribucija.",
|
||||
"wsl.onboarding.install": "Instaliraj",
|
||||
"wsl.onboarding.installing": "Instaliranje...",
|
||||
"wsl.onboarding.installDistro": "Instaliraj distribuciju",
|
||||
"wsl.onboarding.searchDistros": "Pretraži distribucije",
|
||||
"wsl.onboarding.wsl2Required": "Potreban je WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Ovoj distribuciji su potrebni bash i curl.",
|
||||
"wsl.onboarding.openTerminal": "Otvori terminal",
|
||||
"wsl.onboarding.path": "Putanja: {{path}}",
|
||||
"wsl.onboarding.notFound": "nije pronađeno",
|
||||
"wsl.onboarding.version": "Verzija: {{version}}",
|
||||
"wsl.onboarding.unknown": "nepoznato",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Instalirana verzija ne odgovara verziji desktop aplikacije.",
|
||||
"wsl.onboarding.adding": "Dodavanje...",
|
||||
|
||||
"dialog.project.edit.title": "Uredi projekat",
|
||||
"dialog.project.edit.name": "Naziv",
|
||||
|
|
@ -405,6 +486,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -453,10 +535,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Nešto je pošlo po zlu",
|
||||
"error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.",
|
||||
"error.page.description.localServerStartup": "Došlo je do greške pri pokretanju lokalnog servera.",
|
||||
"error.page.details.label": "Detalji greške",
|
||||
"error.page.action.restart": "Restartuj",
|
||||
"error.page.action.report": "Prijavi grešku",
|
||||
"error.page.action.reported": "Greška prijavljena",
|
||||
"error.page.action.exportLogs": "Izvezi logove",
|
||||
"error.page.action.checking": "Provjera...",
|
||||
"error.page.action.checkUpdates": "Provjeri ažuriranja",
|
||||
"error.page.action.updateTo": "Ažuriraj na {{version}}",
|
||||
|
|
@ -504,11 +588,31 @@ export const dict = {
|
|||
"home.recentProjects": "Nedavni projekti",
|
||||
"home.empty.title": "Nema nedavnih projekata",
|
||||
"home.empty.description": "Kreni tako što ćeš otvoriti lokalni projekat",
|
||||
"home.title": "Početna",
|
||||
"home.projects": "Projekti",
|
||||
"home.project.add": "Dodaj projekat",
|
||||
"home.recentlyClosed": "Nedavno zatvoreni projekti",
|
||||
"home.server.collapse": "Sažmi projekte servera",
|
||||
"home.server.expand": "Proširi projekte servera",
|
||||
"home.sessions.search.placeholder": "Pretraži sesije",
|
||||
"home.sessions.search.placeholder.scoped": "Pretraži sesije u {{scope}}",
|
||||
"home.sessions.search.sessions": "Sesije",
|
||||
"home.sessions.search.noResults": "Nema pronađenih sesija za {{query}}",
|
||||
"home.sessions.empty": "Ovdje još nema ničega",
|
||||
"home.sessions.empty.description": "Kreiraj sesiju za početak",
|
||||
"home.sessions.group.today": "Danas",
|
||||
"home.sessions.group.yesterday": "Jučer",
|
||||
"home.sessions.group.older": "Starije",
|
||||
|
||||
"session.tab.session": "Sesija",
|
||||
"session.tab.review": "Pregled",
|
||||
"session.tab.context": "Kontekst",
|
||||
"session.tab.unknown": "Nepoznata sesija",
|
||||
"session.panel.reviewAndFiles": "Pregled i datoteke",
|
||||
"session.error.notFound": "Ova sesija nije pronađena",
|
||||
"session.error.notFound.description": "Ova kartica upućuje na sesiju koja više ne postoji na ovom serveru.",
|
||||
"session.error.notFound.closeTab": "Zatvori karticu",
|
||||
"session.error.serverConnection": "Nije moguće povezati se s ovim serverom",
|
||||
"session.review.filesChanged": "Izmijenjeno {{count}} datoteka",
|
||||
"session.review.change.one": "Izmjena",
|
||||
"session.review.change.other": "Izmjene",
|
||||
|
|
@ -518,6 +622,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"Praćenje snimaka (snapshot) je onemogućeno u konfiguraciji, pa promjene sesije nisu dostupne",
|
||||
"session.review.noChanges": "Nema izmjena",
|
||||
"session.review.noUncommittedChanges": "Još nema nepotvrđenih promjena",
|
||||
"session.review.noBranchChanges": "Još nema promjena na grani",
|
||||
|
||||
"session.files.selectToOpen": "Odaberi datoteku za otvaranje",
|
||||
"session.files.all": "Sve datoteke",
|
||||
|
|
@ -531,9 +637,10 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Idi na najnovije",
|
||||
|
||||
"session.context.addToContext": "Dodaj {{selection}} u kontekst",
|
||||
"session.todo.title": "Zadaci",
|
||||
"session.todo.collapse": "Sažmi",
|
||||
"session.todo.expand": "Proširi",
|
||||
"session.question.minimize": "Minimiziraj pitanje",
|
||||
"session.question.restore": "Vrati pitanje",
|
||||
"session.question.pending.one": "{{count}} pitanje na čekanju",
|
||||
"session.question.pending.other": "{{count}} pitanja na čekanju",
|
||||
"session.followupDock.summary.one": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.summary.other": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.sendNow": "Pošalji sada",
|
||||
|
|
@ -547,9 +654,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Vrati poruku",
|
||||
|
||||
"session.new.title": "Napravi bilo šta",
|
||||
"session.new.project.new": "Novi projekat",
|
||||
"session.new.project.search": "Pretraži projekte",
|
||||
"session.new.project.add": "Dodaj projekat",
|
||||
"session.new.worktree.main": "Glavna grana",
|
||||
"session.new.worktree.mainWithBranch": "Glavna grana ({{branch}})",
|
||||
"session.new.worktree.create": "Kreiraj novi worktree",
|
||||
"session.new.workspace.runIn": "Pokreni sesiju u",
|
||||
"session.new.workspace.triggerLocal": "Lokalno",
|
||||
"session.new.workspace.local": "Lokalni repozitorij",
|
||||
"session.new.workspace.existing": "Radni prostor…",
|
||||
"session.new.lastModified": "Posljednja izmjena",
|
||||
|
||||
"session.header.search.placeholder": "Pretraži {{project}}",
|
||||
|
|
@ -618,6 +732,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Nedavne sesije",
|
||||
"sidebar.project.viewAllSessions": "Prikaži sve sesije",
|
||||
"sidebar.project.clearNotifications": "Očisti obavijesti",
|
||||
"sidebar.empty.title": "Nema otvorenih projekata",
|
||||
"sidebar.empty.description": "Otvori projekat za početak",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
|
||||
|
|
@ -630,6 +746,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "Pokreni OpenCode server unutar WSL-a na Windowsu.",
|
||||
|
||||
"settings.general.section.appearance": "Izgled",
|
||||
"settings.general.section.advanced": "Napredno",
|
||||
"settings.general.section.notifications": "Sistemske obavijesti",
|
||||
"settings.general.section.updates": "Ažuriranja",
|
||||
"settings.general.section.sounds": "Zvučni efekti",
|
||||
|
|
@ -638,6 +755,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "Jezik",
|
||||
"settings.general.row.language.description": "Promijeni jezik prikaza u OpenCode-u",
|
||||
"settings.general.row.shell.title": "Shell terminala",
|
||||
"settings.general.row.shell.description":
|
||||
"Odaberi shell koji se koristi za terminal. Kompatibilni shellovi koriste se i za pozive alata agenta.",
|
||||
"settings.general.row.shell.autoDefault": "Automatski (podrazumijevano)",
|
||||
"settings.general.row.shell.terminalOnly": "samo terminal",
|
||||
"settings.general.row.appearance.title": "Izgled",
|
||||
"settings.general.row.appearance.description": "Prilagodi kako OpenCode izgleda na tvom uređaju",
|
||||
"settings.general.row.colorScheme.title": "Šema boja",
|
||||
|
|
@ -646,14 +768,30 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Prilagodi temu OpenCode-a.",
|
||||
"settings.general.row.font.title": "Font za kod",
|
||||
"settings.general.row.font.description": "Prilagodi font koji se koristi u blokovima koda",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Font terminala",
|
||||
"settings.general.row.terminalFont.description": "Prilagodite font koji se koristi u terminalu",
|
||||
"settings.general.row.uiFont.title": "UI font",
|
||||
"settings.general.row.uiFont.description": "Prilagodi font koji se koristi u cijelom interfejsu",
|
||||
"settings.general.row.followup.title": "Ponašanje nadovezivanja",
|
||||
"settings.general.row.followup.description": "Odaberi da li upiti nadovezivanja usmjeravaju odmah ili čekaju u redu",
|
||||
"settings.general.row.followup.option.queue": "Red čekanja",
|
||||
"settings.general.row.followup.option.steer": "Usmjeri",
|
||||
"settings.general.row.showFileTree.title": "Stablo datoteka",
|
||||
"settings.general.row.showFileTree.description": "Prikaži stablo datoteka u sesijama",
|
||||
"settings.general.row.showNavigation.title": "Kontrole navigacije",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Prikaži dugmad za nazad i naprijed u naslovnoj traci desktop aplikacije",
|
||||
"settings.general.row.showSearch.title": "Paleta komandi",
|
||||
"settings.general.row.showSearch.description": "Prikaži dugme za pretragu i paletu komandi u naslovnoj traci",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Prikaži dugme terminala u naslovnoj traci desktop aplikacije",
|
||||
"settings.general.row.showStatus.title": "Status servera",
|
||||
"settings.general.row.showStatus.description": "Prikaži dugme statusa servera u naslovnoj traci",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Donja navigacija",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Postavi naslovnu traku i kartice sesije na dno ekrana na mobilnim uređajima",
|
||||
"settings.general.row.showCustomAgents.title": "Prilagođeni agenti",
|
||||
"settings.general.row.showCustomAgents.description": "Prikaži izbor agenta u uređivaču poruke",
|
||||
"settings.general.row.reasoningSummaries.title": "Prikaži sažetke rasuđivanja",
|
||||
"settings.general.row.reasoningSummaries.description": "Prikaži sažetke rasuđivanja modela na vremenskoj traci",
|
||||
|
||||
|
|
@ -663,6 +801,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Proširi dijelove alata za uređivanje",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Prikaži dijelove alata za uređivanje, pisanje i patch podrazumijevano proširene na vremenskoj traci",
|
||||
"settings.general.row.newLayoutDesigns.title": "Novi raspored i dizajn",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Omogući redizajnirani raspored, početnu stranicu, uređivač poruke i interfejs sesije",
|
||||
"settings.general.row.pinchZoom.title": "Zumiranje štipanjem",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Dozvoli zumiranje gestom štipanja na dodirnoj ploči i pomoću Ctrl-pomjeranja",
|
||||
"settings.general.row.wayland.title": "Koristi nativni Wayland",
|
||||
"settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -677,6 +821,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Ručno provjeri ažuriranja i instaliraj ako su dostupna",
|
||||
"settings.updates.action.checkNow": "Provjeri sada",
|
||||
"settings.updates.action.checking": "Provjera...",
|
||||
"settings.updates.action.downloading": "Preuzimanje...",
|
||||
"settings.updates.action.installing": "Instaliranje...",
|
||||
"settings.updates.toast.latest.title": "Sve je ažurno",
|
||||
"settings.updates.toast.latest.description": "Koristiš najnoviju verziju OpenCode-a.",
|
||||
"sound.option.none": "Nijedan",
|
||||
|
|
@ -805,8 +951,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Učitaj vještinu po nazivu",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Pokreni upite jezičnog servera",
|
||||
"settings.permissions.tool.todowrite.title": "Ažuriranje liste zadataka",
|
||||
"settings.permissions.tool.todowrite.description": "Ažuriraj listu zadataka",
|
||||
"settings.permissions.tool.webfetch.title": "Web preuzimanje",
|
||||
"settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a",
|
||||
"settings.permissions.tool.websearch.title": "Web pretraga",
|
||||
|
|
@ -871,7 +1015,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Pratite, pregledajte i poništite promjene u ovom projektu",
|
||||
"session.review.noVcs.createGit.actionLoading": "Kreiranje Git repozitorija...",
|
||||
"session.review.noVcs.createGit.action": "Kreiraj Git repozitorij",
|
||||
"session.todo.progress": "{{done}} od {{total}} zadataka završeno",
|
||||
"session.question.progress": "{{current}} od {{total}} pitanja",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "Skift sidebjælke",
|
||||
"command.project.open": "Åbn projekt",
|
||||
"command.project.previous": "Forrige projekt",
|
||||
"command.project.next": "Næste projekt",
|
||||
"command.project.index": "Skift til projekt {{index}}",
|
||||
"command.provider.connect": "Tilslut udbyder",
|
||||
"command.server.switch": "Skift server",
|
||||
"command.settings.open": "Åbn indstillinger",
|
||||
|
|
@ -45,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "Ny session",
|
||||
"command.file.open": "Åbn fil",
|
||||
"command.tab.close": "Luk fane",
|
||||
"command.tab.reopenClosed": "Åbn lukket fane igen",
|
||||
"command.context.addSelection": "Tilføj markering til kontekst",
|
||||
"command.context.addSelection.description": "Tilføj markerede linjer fra den aktuelle fil",
|
||||
"command.input.focus": "Fokuser inputfelt",
|
||||
|
|
@ -208,6 +212,11 @@ export const dict = {
|
|||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Tillader: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontekst",
|
||||
"model.tooltip.inputs": "Input",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Udbyder",
|
||||
"model.tooltip.reasoning": "Tænkning",
|
||||
"model.tooltip.reasoning.allowed": "Tillader tænkning",
|
||||
"model.tooltip.reasoning.none": "Ingen tænkning",
|
||||
"model.tooltip.context": "Kontekstgrænse {{limit}}",
|
||||
|
|
@ -217,6 +226,7 @@ export const dict = {
|
|||
"common.loading": "Indlæser",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Annuller",
|
||||
"common.clear": "Ryd",
|
||||
"common.connect": "Forbind",
|
||||
"common.disconnect": "Frakobl",
|
||||
"common.continue": "Indsend",
|
||||
|
|
@ -234,6 +244,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc for at afslutte",
|
||||
"session.child.promptDisabled": "Der kan ikke sendes prompts til underagentsessioner.",
|
||||
"session.child.backToParent": "Tilbage til hovedsessionen.",
|
||||
|
||||
"prompt.example.1": "Ret en TODO i koden",
|
||||
"prompt.example.2": "Hvad er teknologistakken for dette projekt?",
|
||||
|
|
@ -340,6 +352,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Slet",
|
||||
"dialog.server.current": "Nuværende server",
|
||||
"dialog.server.status.default": "Standard",
|
||||
"wsl.server.add": "Tilføj WSL-server",
|
||||
"wsl.server.addShort": "Tilføj WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL-server",
|
||||
"wsl.server.retryStart": "Prøv at starte igen",
|
||||
"wsl.server.updating": "Opdaterer...",
|
||||
"wsl.onboarding.step.distro": "Vælg distribution",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Tjekker WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows skal genstartes for at fuldføre installationen af WSL.",
|
||||
"wsl.onboarding.ready": "WSL er klar.",
|
||||
"wsl.onboarding.required": "WSL er påkrævet for at fortsætte.",
|
||||
"wsl.onboarding.checkingDistros": "Tjekker distributioner...",
|
||||
"wsl.onboarding.installingDistro": "Installerer {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Tjekker {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Henter liste over distributioner...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} er klar.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} er ikke installeret endnu.",
|
||||
"wsl.onboarding.openDistroOnce": "Åbn {{distro}} én gang for at fuldføre opsætningen.",
|
||||
"wsl.onboarding.finishingDistro": "Fuldfører opsætningen af {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Vælg en distribution, eller installer en nedenfor.",
|
||||
"wsl.onboarding.checkingOpencode": "Tjekker OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Tjekker OpenCode i {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Opdaterer OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Opdaterer OpenCode i {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Opdater OpenCode i {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Opdater OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode er klar i {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode er klar.",
|
||||
"wsl.onboarding.installOpencodeIn": "Installer OpenCode i {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Installer OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Vælg først en distribution.",
|
||||
"wsl.onboarding.loadFailed": "Kunne ikke indlæse WSL-status.",
|
||||
"wsl.onboarding.loading": "Indlæser...",
|
||||
"wsl.onboarding.installedDistros": "Installerede distributioner",
|
||||
"wsl.onboarding.checkAgain": "Tjek igen",
|
||||
"wsl.onboarding.distroStatus.ready": "Klar",
|
||||
"wsl.onboarding.distroStatus.checking": "Tjekker...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode er ikke installeret",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Mangler bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Ikke understøttet · Brug WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Har du brug for en anden distribution?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Installer en Linux-distribution fra WSL-kataloget",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL er ikke installeret",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) er påkrævet, før OpenCode kan tilføje en WSL-server",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL er ikke tilgængelig",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode kunne ikke bekræfte WSL på denne maskine.",
|
||||
"wsl.onboarding.installWsl": "Installer WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Genstart Windows for at fuldføre installationen af WSL, og åbn derefter OpenCode igen.",
|
||||
"wsl.onboarding.next": "Næste",
|
||||
"wsl.onboarding.refresh": "Opdater",
|
||||
"wsl.onboarding.allDistrosAdded": "Alle installerede distributioner er allerede tilføjet.",
|
||||
"wsl.onboarding.noDistros": "Ingen distributioner fundet endnu.",
|
||||
"wsl.onboarding.install": "Installer",
|
||||
"wsl.onboarding.installing": "Installerer...",
|
||||
"wsl.onboarding.installDistro": "Installer distribution",
|
||||
"wsl.onboarding.searchDistros": "Søg efter distributioner",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 er påkrævet.",
|
||||
"wsl.onboarding.toolsRequired": "Denne distribution kræver bash og curl.",
|
||||
"wsl.onboarding.openTerminal": "Åbn terminal",
|
||||
"wsl.onboarding.path": "Sti: {{path}}",
|
||||
"wsl.onboarding.notFound": "ikke fundet",
|
||||
"wsl.onboarding.version": "Version: {{version}}",
|
||||
"wsl.onboarding.unknown": "ukendt",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Den installerede version matcher ikke desktopappens version.",
|
||||
"wsl.onboarding.adding": "Tilføjer...",
|
||||
|
||||
"dialog.project.edit.title": "Rediger projekt",
|
||||
"dialog.project.edit.name": "Navn",
|
||||
|
|
@ -403,6 +484,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -450,10 +532,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Noget gik galt",
|
||||
"error.page.description": "Der opstod en fejl under indlæsning af applikationen.",
|
||||
"error.page.description.localServerStartup": "Der opstod en fejl under start af den lokale server.",
|
||||
"error.page.details.label": "Fejldetaljer",
|
||||
"error.page.action.restart": "Genstart",
|
||||
"error.page.action.report": "Rapportér fejl",
|
||||
"error.page.action.reported": "Fejl rapporteret",
|
||||
"error.page.action.exportLogs": "Eksportér logfiler",
|
||||
"error.page.action.checking": "Tjekker...",
|
||||
"error.page.action.checkUpdates": "Tjek for opdateringer",
|
||||
"error.page.action.updateTo": "Opdater til {{version}}",
|
||||
|
|
@ -501,11 +585,31 @@ export const dict = {
|
|||
"home.recentProjects": "Seneste projekter",
|
||||
"home.empty.title": "Ingen seneste projekter",
|
||||
"home.empty.description": "Kom i gang ved at åbne et lokalt projekt",
|
||||
"home.title": "Hjem",
|
||||
"home.projects": "Projekter",
|
||||
"home.project.add": "Tilføj projekt",
|
||||
"home.recentlyClosed": "Lukket for nylig",
|
||||
"home.server.collapse": "Skjul serverprojekter",
|
||||
"home.server.expand": "Udvid serverprojekter",
|
||||
"home.sessions.search.placeholder": "Søg i sessioner",
|
||||
"home.sessions.search.placeholder.scoped": "Søg i sessioner i {{scope}}",
|
||||
"home.sessions.search.sessions": "Sessioner",
|
||||
"home.sessions.search.noResults": "Ingen sessioner fundet for {{query}}",
|
||||
"home.sessions.empty": "Intet her endnu",
|
||||
"home.sessions.empty.description": "Opret en session for at komme i gang",
|
||||
"home.sessions.group.today": "I dag",
|
||||
"home.sessions.group.yesterday": "I går",
|
||||
"home.sessions.group.older": "Ældre",
|
||||
|
||||
"session.tab.session": "Session",
|
||||
"session.tab.review": "Gennemgang",
|
||||
"session.tab.context": "Kontekst",
|
||||
"session.tab.unknown": "Ukendt session",
|
||||
"session.panel.reviewAndFiles": "Gennemgang og filer",
|
||||
"session.error.notFound": "Denne session kan ikke findes",
|
||||
"session.error.notFound.description": "Denne fane henviser til en session, der ikke længere findes på denne server.",
|
||||
"session.error.notFound.closeTab": "Luk fane",
|
||||
"session.error.serverConnection": "Kan ikke oprette forbindelse til denne server",
|
||||
"session.review.filesChanged": "{{count}} Filer ændret",
|
||||
"session.review.change.one": "Ændring",
|
||||
"session.review.change.other": "Ændringer",
|
||||
|
|
@ -515,6 +619,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"Snapshot-sporing er deaktiveret i konfigurationen, så sessionsændringer er ikke tilgængelige",
|
||||
"session.review.noChanges": "Ingen ændringer",
|
||||
"session.review.noUncommittedChanges": "Ingen ændringer uden commit endnu",
|
||||
"session.review.noBranchChanges": "Ingen grenændringer endnu",
|
||||
"session.files.selectToOpen": "Vælg en fil at åbne",
|
||||
"session.files.all": "Alle filer",
|
||||
"session.files.empty": "Ingen filer",
|
||||
|
|
@ -526,9 +632,10 @@ export const dict = {
|
|||
|
||||
"session.messages.jumpToLatest": "Gå til seneste",
|
||||
"session.context.addToContext": "Tilføj {{selection}} til kontekst",
|
||||
"session.todo.title": "Opgaver",
|
||||
"session.todo.collapse": "Skjul",
|
||||
"session.todo.expand": "Udvid",
|
||||
"session.question.minimize": "Minimer spørgsmål",
|
||||
"session.question.restore": "Gendan spørgsmål",
|
||||
"session.question.pending.one": "{{count}} afventende spørgsmål",
|
||||
"session.question.pending.other": "{{count}} afventende spørgsmål",
|
||||
"session.followupDock.summary.one": "{{count}} besked i kø",
|
||||
"session.followupDock.summary.other": "{{count}} beskeder i kø",
|
||||
"session.followupDock.sendNow": "Send nu",
|
||||
|
|
@ -542,9 +649,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Gendan besked",
|
||||
|
||||
"session.new.title": "Byg hvad som helst",
|
||||
"session.new.project.new": "Nyt projekt",
|
||||
"session.new.project.search": "Søg efter projekter",
|
||||
"session.new.project.add": "Tilføj projekt",
|
||||
"session.new.worktree.main": "Hovedgren",
|
||||
"session.new.worktree.mainWithBranch": "Hovedgren ({{branch}})",
|
||||
"session.new.worktree.create": "Opret nyt worktree",
|
||||
"session.new.workspace.runIn": "Kør session i",
|
||||
"session.new.workspace.triggerLocal": "Lokal",
|
||||
"session.new.workspace.local": "Lokalt repository",
|
||||
"session.new.workspace.existing": "Arbejdsområde…",
|
||||
"session.new.lastModified": "Sidst ændret",
|
||||
|
||||
"session.header.search.placeholder": "Søg {{project}}",
|
||||
|
|
@ -614,6 +728,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Seneste sessioner",
|
||||
"sidebar.project.viewAllSessions": "Vis alle sessioner",
|
||||
"sidebar.project.clearNotifications": "Ryd notifikationer",
|
||||
"sidebar.empty.title": "Ingen åbne projekter",
|
||||
"sidebar.empty.description": "Åbn et projekt for at komme i gang",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Desktop",
|
||||
|
|
@ -625,6 +741,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "Kør OpenCode-serveren inde i WSL på Windows.",
|
||||
|
||||
"settings.general.section.appearance": "Udseende",
|
||||
"settings.general.section.advanced": "Avanceret",
|
||||
"settings.general.section.notifications": "Systemmeddelelser",
|
||||
"settings.general.section.updates": "Opdateringer",
|
||||
"settings.general.section.sounds": "Lydeffekter",
|
||||
|
|
@ -633,6 +750,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "Sprog",
|
||||
"settings.general.row.language.description": "Ændr visningssproget for OpenCode",
|
||||
"settings.general.row.shell.title": "Terminalshell",
|
||||
"settings.general.row.shell.description":
|
||||
"Vælg den shell, der bruges i din terminal. Kompatible shells bruges også til agentens værktøjskald.",
|
||||
"settings.general.row.shell.autoDefault": "Automatisk (standard)",
|
||||
"settings.general.row.shell.terminalOnly": "kun terminal",
|
||||
"settings.general.row.appearance.title": "Udseende",
|
||||
"settings.general.row.appearance.description": "Tilpas hvordan OpenCode ser ud på din enhed",
|
||||
"settings.general.row.colorScheme.title": "Farveskema",
|
||||
|
|
@ -641,14 +763,29 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.",
|
||||
"settings.general.row.font.title": "Kode-skrifttype",
|
||||
"settings.general.row.font.description": "Tilpas skrifttypen, der bruges i kodeblokke",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Terminalskrifttype",
|
||||
"settings.general.row.terminalFont.description": "Tilpas den skrifttype, der bruges i terminalen",
|
||||
"settings.general.row.uiFont.title": "UI-skrifttype",
|
||||
"settings.general.row.uiFont.description": "Tilpas skrifttypen, der bruges i hele brugerfladen",
|
||||
"settings.general.row.followup.title": "Opfølgningsadfærd",
|
||||
"settings.general.row.followup.description": "Vælg om opfølgende forespørgsler skal styre straks eller vente i kø",
|
||||
"settings.general.row.followup.option.queue": "Kø",
|
||||
"settings.general.row.followup.option.steer": "Styr",
|
||||
"settings.general.row.showFileTree.title": "Filtræ",
|
||||
"settings.general.row.showFileTree.description": "Vis filtræspanelet i sessioner",
|
||||
"settings.general.row.showNavigation.title": "Navigationsknapper",
|
||||
"settings.general.row.showNavigation.description": "Vis tilbage- og frem-knapperne i desktopappens titellinje",
|
||||
"settings.general.row.showSearch.title": "Kommandopalette",
|
||||
"settings.general.row.showSearch.description": "Vis knappen til søgning og kommandopaletten i titellinjen",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Vis terminalknappen i desktopappens titellinje",
|
||||
"settings.general.row.showStatus.title": "Serverstatus",
|
||||
"settings.general.row.showStatus.description": "Vis serverstatusknappen i titellinjen",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation nederst",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Placer titellinjen og sessionsfanerne nederst på skærmen på mobilenheder",
|
||||
"settings.general.row.showCustomAgents.title": "Brugerdefinerede agenter",
|
||||
"settings.general.row.showCustomAgents.description": "Vis agentvælgeren i promptfeltet",
|
||||
"settings.general.row.reasoningSummaries.title": "Vis tænkeoversigter",
|
||||
"settings.general.row.reasoningSummaries.description": "Vis model tænkeoversigter i tidslinjen",
|
||||
|
||||
|
|
@ -657,6 +794,11 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Udvid edit-værktøjsdele",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Vis edit-, write- og patch-værktøjsdele udvidet som standard i tidslinjen",
|
||||
"settings.general.row.newLayoutDesigns.title": "Nyt layout og design",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Aktivér det nydesignede layout, startsiden, promptfeltet og sessionsbrugerfladen",
|
||||
"settings.general.row.pinchZoom.title": "Knib for at zoome",
|
||||
"settings.general.row.pinchZoom.description": "Tillad knibebevægelser på pegefeltet og Ctrl-rulning for at zoome",
|
||||
"settings.general.row.wayland.title": "Brug native Wayland",
|
||||
"settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -671,6 +813,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Tjek manuelt for opdateringer og installer, hvis tilgængelig",
|
||||
"settings.updates.action.checkNow": "Tjek nu",
|
||||
"settings.updates.action.checking": "Tjekker...",
|
||||
"settings.updates.action.downloading": "Downloader...",
|
||||
"settings.updates.action.installing": "Installerer...",
|
||||
"settings.updates.toast.latest.title": "Du er opdateret",
|
||||
"settings.updates.toast.latest.description": "Du kører den nyeste version af OpenCode.",
|
||||
|
||||
|
|
@ -799,8 +943,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Indlæs en færdighed efter navn",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Kør sprogserverforespørgsler",
|
||||
"settings.permissions.tool.todowrite.title": "Skriv To-do",
|
||||
"settings.permissions.tool.todowrite.description": "Opdater to-do listen",
|
||||
"settings.permissions.tool.webfetch.title": "Webhentning",
|
||||
"settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
|
||||
"settings.permissions.tool.websearch.title": "Websøgning",
|
||||
|
|
@ -865,7 +1007,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Spor, gennemgå og fortryd ændringer i dette projekt",
|
||||
"session.review.noVcs.createGit.actionLoading": "Opretter Git-repository...",
|
||||
"session.review.noVcs.createGit.action": "Opret Git-repository",
|
||||
"session.todo.progress": "{{done}} af {{total}} opgaver fuldført",
|
||||
"session.question.progress": "{{current}} af {{total}} spørgsmål",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Stifinder",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "Dunkel",
|
||||
"command.sidebar.toggle": "Seitenleiste umschalten",
|
||||
"command.project.open": "Projekt öffnen",
|
||||
"command.project.previous": "Vorheriges Projekt",
|
||||
"command.project.next": "Nächstes Projekt",
|
||||
"command.project.index": "Zu Projekt {{index}} wechseln",
|
||||
"command.provider.connect": "Anbieter verbinden",
|
||||
"command.server.switch": "Server wechseln",
|
||||
"command.settings.open": "Einstellungen öffnen",
|
||||
|
|
@ -43,6 +46,7 @@ export const dict = {
|
|||
"command.session.new": "Neue Sitzung",
|
||||
"command.file.open": "Datei öffnen",
|
||||
"command.tab.close": "Tab schließen",
|
||||
"command.tab.reopenClosed": "Geschlossenen Tab wieder öffnen",
|
||||
"command.context.addSelection": "Auswahl zum Kontext hinzufügen",
|
||||
"command.context.addSelection.description": "Ausgewählte Zeilen aus der aktuellen Datei hinzufügen",
|
||||
"command.input.focus": "Eingabefeld fokussieren",
|
||||
|
|
@ -198,6 +202,11 @@ export const dict = {
|
|||
"model.input.video": "Video",
|
||||
"model.input.pdf": "PDF",
|
||||
"model.tooltip.allows": "Erlaubt: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontext",
|
||||
"model.tooltip.inputs": "Eingaben",
|
||||
"model.tooltip.model": "Modell",
|
||||
"model.tooltip.provider": "Anbieter",
|
||||
"model.tooltip.reasoning": "Reasoning",
|
||||
"model.tooltip.reasoning.allowed": "Erlaubt Reasoning",
|
||||
"model.tooltip.reasoning.none": "Kein Reasoning",
|
||||
"model.tooltip.context": "Kontextlimit {{limit}}",
|
||||
|
|
@ -207,6 +216,7 @@ export const dict = {
|
|||
"common.loading": "Laden",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Abbrechen",
|
||||
"common.clear": "Leeren",
|
||||
"common.connect": "Verbinden",
|
||||
"common.disconnect": "Trennen",
|
||||
"common.continue": "Absenden",
|
||||
|
|
@ -223,6 +233,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc zum Verlassen",
|
||||
"session.child.promptDisabled": "Unteragenten-Sitzungen können keine Prompts erhalten.",
|
||||
"session.child.backToParent": "Zurück zur Hauptsitzung.",
|
||||
"prompt.example.1": "Ein TODO in der Codebasis beheben",
|
||||
"prompt.example.2": "Was ist der Tech-Stack dieses Projekts?",
|
||||
"prompt.example.3": "Fehlerhafte Tests beheben",
|
||||
|
|
@ -320,6 +332,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Löschen",
|
||||
"dialog.server.current": "Aktueller Server",
|
||||
"dialog.server.status.default": "Standard",
|
||||
"wsl.server.add": "WSL-Server hinzufügen",
|
||||
"wsl.server.addShort": "WSL hinzufügen",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL-Server",
|
||||
"wsl.server.retryStart": "Start erneut versuchen",
|
||||
"wsl.server.updating": "Wird aktualisiert...",
|
||||
"wsl.onboarding.step.distro": "Distribution auswählen",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL wird geprüft...",
|
||||
"wsl.onboarding.restartRequired": "Windows muss neu gestartet werden, um die WSL-Installation abzuschließen.",
|
||||
"wsl.onboarding.ready": "WSL ist bereit.",
|
||||
"wsl.onboarding.required": "WSL ist erforderlich, um fortzufahren.",
|
||||
"wsl.onboarding.checkingDistros": "Distributionen werden geprüft...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} wird installiert...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} wird geprüft...",
|
||||
"wsl.onboarding.listingDistros": "Distributionen werden aufgelistet...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} ist bereit.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ist noch nicht installiert.",
|
||||
"wsl.onboarding.openDistroOnce": "Öffnen Sie {{distro}} einmal, um die Einrichtung abzuschließen.",
|
||||
"wsl.onboarding.finishingDistro": "Einrichtung von {{distro}} wird abgeschlossen.",
|
||||
"wsl.onboarding.pickDistro": "Wählen Sie eine Distribution aus oder installieren Sie unten eine.",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode wird geprüft...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "OpenCode in {{distro}} wird geprüft...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode wird aktualisiert...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "OpenCode in {{distro}} wird aktualisiert...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Aktualisieren Sie OpenCode in {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode aktualisieren",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode ist in {{distro}} bereit.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode ist bereit.",
|
||||
"wsl.onboarding.installOpencodeIn": "Installieren Sie OpenCode in {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "OpenCode installieren",
|
||||
"wsl.onboarding.chooseDistroFirst": "Wählen Sie zuerst eine Distribution aus.",
|
||||
"wsl.onboarding.loadFailed": "WSL-Status konnte nicht geladen werden.",
|
||||
"wsl.onboarding.loading": "Wird geladen...",
|
||||
"wsl.onboarding.installedDistros": "Installierte Distributionen",
|
||||
"wsl.onboarding.checkAgain": "Erneut prüfen",
|
||||
"wsl.onboarding.distroStatus.ready": "Bereit",
|
||||
"wsl.onboarding.distroStatus.checking": "Wird geprüft...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode nicht installiert",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Fehlend: bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nicht unterstützt · WSL 2 verwenden",
|
||||
"wsl.onboarding.needAnotherDistro": "Andere Distribution benötigt?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Installieren Sie eine Linux-Distribution aus dem WSL-Katalog",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL nicht installiert",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) ist erforderlich, bevor OpenCode einen WSL-Server hinzufügen kann",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL nicht verfügbar",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode konnte WSL auf diesem Computer nicht überprüfen.",
|
||||
"wsl.onboarding.installWsl": "WSL installieren",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Starten Sie Windows neu, um die WSL-Installation abzuschließen, und öffnen Sie dann OpenCode erneut.",
|
||||
"wsl.onboarding.next": "Weiter",
|
||||
"wsl.onboarding.refresh": "Aktualisieren",
|
||||
"wsl.onboarding.allDistrosAdded": "Alle installierten Distributionen wurden bereits hinzugefügt.",
|
||||
"wsl.onboarding.noDistros": "Noch keine Distributionen erkannt.",
|
||||
"wsl.onboarding.install": "Installieren",
|
||||
"wsl.onboarding.installing": "Wird installiert...",
|
||||
"wsl.onboarding.installDistro": "Distribution installieren",
|
||||
"wsl.onboarding.searchDistros": "Distributionen durchsuchen",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 ist erforderlich.",
|
||||
"wsl.onboarding.toolsRequired": "Diese Distribution benötigt bash und curl.",
|
||||
"wsl.onboarding.openTerminal": "Terminal öffnen",
|
||||
"wsl.onboarding.path": "Pfad: {{path}}",
|
||||
"wsl.onboarding.notFound": "nicht gefunden",
|
||||
"wsl.onboarding.version": "Version: {{version}}",
|
||||
"wsl.onboarding.unknown": "unbekannt",
|
||||
"wsl.onboarding.desktopVersion": "Desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Die installierte Version entspricht nicht der Version der Desktop-App.",
|
||||
"wsl.onboarding.adding": "Wird hinzugefügt...",
|
||||
"dialog.project.edit.title": "Projekt bearbeiten",
|
||||
"dialog.project.edit.name": "Name",
|
||||
"dialog.project.edit.icon": "Icon",
|
||||
|
|
@ -378,6 +459,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Sprache",
|
||||
|
|
@ -414,10 +496,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "Noch nicht",
|
||||
"error.page.title": "Etwas ist schiefgelaufen",
|
||||
"error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.",
|
||||
"error.page.description.localServerStartup": "Beim Starten des lokalen Servers ist ein Fehler aufgetreten.",
|
||||
"error.page.details.label": "Fehlerdetails",
|
||||
"error.page.action.restart": "Neustart",
|
||||
"error.page.action.report": "Fehler melden",
|
||||
"error.page.action.reported": "Fehler gemeldet",
|
||||
"error.page.action.exportLogs": "Protokolle exportieren",
|
||||
"error.page.action.checking": "Prüfen...",
|
||||
"error.page.action.checkUpdates": "Nach Updates suchen",
|
||||
"error.page.action.updateTo": "Auf {{version}} aktualisieren",
|
||||
|
|
@ -461,10 +545,31 @@ export const dict = {
|
|||
"home.recentProjects": "Letzte Projekte",
|
||||
"home.empty.title": "Keine letzten Projekte",
|
||||
"home.empty.description": "Starten Sie, indem Sie ein lokales Projekt öffnen",
|
||||
"home.title": "Startseite",
|
||||
"home.projects": "Projekte",
|
||||
"home.project.add": "Projekt hinzufügen",
|
||||
"home.recentlyClosed": "Kürzlich geschlossen",
|
||||
"home.server.collapse": "Serverprojekte einklappen",
|
||||
"home.server.expand": "Serverprojekte ausklappen",
|
||||
"home.sessions.search.placeholder": "Sitzungen durchsuchen",
|
||||
"home.sessions.search.placeholder.scoped": "Sitzungen in {{scope}} durchsuchen",
|
||||
"home.sessions.search.sessions": "Sitzungen",
|
||||
"home.sessions.search.noResults": "Keine Sitzungen für {{query}} gefunden",
|
||||
"home.sessions.empty": "Hier ist noch nichts",
|
||||
"home.sessions.empty.description": "Erstellen Sie eine Sitzung, um loszulegen",
|
||||
"home.sessions.group.today": "Heute",
|
||||
"home.sessions.group.yesterday": "Gestern",
|
||||
"home.sessions.group.older": "Älter",
|
||||
"session.tab.session": "Sitzung",
|
||||
"session.tab.review": "Überprüfung",
|
||||
"session.tab.context": "Kontext",
|
||||
"session.tab.unknown": "Unbekannte Sitzung",
|
||||
"session.panel.reviewAndFiles": "Überprüfung und Dateien",
|
||||
"session.error.notFound": "Diese Sitzung wurde nicht gefunden",
|
||||
"session.error.notFound.description":
|
||||
"Dieser Tab verweist auf eine Sitzung, die auf diesem Server nicht mehr existiert.",
|
||||
"session.error.notFound.closeTab": "Tab schließen",
|
||||
"session.error.serverConnection": "Verbindung zu diesem Server nicht möglich",
|
||||
"session.review.filesChanged": "{{count}} Dateien geändert",
|
||||
"session.review.change.one": "Änderung",
|
||||
"session.review.change.other": "Änderungen",
|
||||
|
|
@ -474,6 +579,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"Snapshot-Tracking ist in der Konfiguration deaktiviert, daher sind Sitzungsänderungen nicht verfügbar",
|
||||
"session.review.noChanges": "Keine Änderungen",
|
||||
"session.review.noUncommittedChanges": "Noch keine nicht committeten Änderungen",
|
||||
"session.review.noBranchChanges": "Noch keine Branch-Änderungen",
|
||||
"session.files.selectToOpen": "Datei zum Öffnen auswählen",
|
||||
"session.files.all": "Alle Dateien",
|
||||
"session.files.empty": "Keine Dateien",
|
||||
|
|
@ -484,9 +591,10 @@ export const dict = {
|
|||
"session.messages.loading": "Lade Nachrichten...",
|
||||
"session.messages.jumpToLatest": "Zum neuesten springen",
|
||||
"session.context.addToContext": "{{selection}} zum Kontext hinzufügen",
|
||||
"session.todo.title": "Aufgaben",
|
||||
"session.todo.collapse": "Einklappen",
|
||||
"session.todo.expand": "Ausklappen",
|
||||
"session.question.minimize": "Frage minimieren",
|
||||
"session.question.restore": "Frage wiederherstellen",
|
||||
"session.question.pending.one": "{{count}} ausstehende Frage",
|
||||
"session.question.pending.other": "{{count}} ausstehende Fragen",
|
||||
"session.followupDock.summary.one": "{{count}} Nachricht in der Warteschlange",
|
||||
"session.followupDock.summary.other": "{{count}} Nachrichten in der Warteschlange",
|
||||
"session.followupDock.sendNow": "Jetzt senden",
|
||||
|
|
@ -499,9 +607,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "Zurückgesetzte Nachrichten ausklappen",
|
||||
"session.revertDock.restore": "Nachricht wiederherstellen",
|
||||
"session.new.title": "Baue, was du willst",
|
||||
"session.new.project.new": "Neues Projekt",
|
||||
"session.new.project.search": "Projekte durchsuchen",
|
||||
"session.new.project.add": "Projekt hinzufügen",
|
||||
"session.new.worktree.main": "Haupt-Branch",
|
||||
"session.new.worktree.mainWithBranch": "Haupt-Branch ({{branch}})",
|
||||
"session.new.worktree.create": "Neuen Worktree erstellen",
|
||||
"session.new.workspace.runIn": "Sitzung ausführen in",
|
||||
"session.new.workspace.triggerLocal": "Lokal",
|
||||
"session.new.workspace.local": "Lokales Repository",
|
||||
"session.new.workspace.existing": "Arbeitsbereich…",
|
||||
"session.new.lastModified": "Zuletzt geändert",
|
||||
"session.header.search.placeholder": "{{project}} durchsuchen",
|
||||
"session.header.searchFiles": "Dateien suchen",
|
||||
|
|
@ -566,6 +681,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Letzte Sitzungen",
|
||||
"sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen",
|
||||
"sidebar.project.clearNotifications": "Benachrichtigungen löschen",
|
||||
"sidebar.empty.title": "Keine Projekte geöffnet",
|
||||
"sidebar.empty.description": "Öffnen Sie ein Projekt, um loszulegen",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Desktop",
|
||||
"settings.section.server": "Server",
|
||||
|
|
@ -575,6 +692,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.title": "WSL-Integration",
|
||||
"settings.desktop.wsl.description": "OpenCode-Server innerhalb von WSL unter Windows ausführen.",
|
||||
"settings.general.section.appearance": "Erscheinungsbild",
|
||||
"settings.general.section.advanced": "Erweitert",
|
||||
"settings.general.section.notifications": "Systembenachrichtigungen",
|
||||
"settings.general.section.updates": "Updates",
|
||||
"settings.general.section.sounds": "Soundeffekte",
|
||||
|
|
@ -582,6 +700,11 @@ export const dict = {
|
|||
"settings.general.section.display": "Anzeige",
|
||||
"settings.general.row.language.title": "Sprache",
|
||||
"settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern",
|
||||
"settings.general.row.shell.title": "Terminal-Shell",
|
||||
"settings.general.row.shell.description":
|
||||
"Wählen Sie die Shell für Ihr Terminal aus. Kompatible Shells werden auch für Tool-Aufrufe von Agenten verwendet.",
|
||||
"settings.general.row.shell.autoDefault": "Automatisch (Standard)",
|
||||
"settings.general.row.shell.terminalOnly": "nur Terminal",
|
||||
"settings.general.row.appearance.title": "Erscheinungsbild",
|
||||
"settings.general.row.appearance.description": "Anpassen, wie OpenCode auf Ihrem Gerät aussieht",
|
||||
"settings.general.row.colorScheme.title": "Farbschema",
|
||||
|
|
@ -591,8 +714,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
|
||||
"settings.general.row.font.title": "Code-Schriftart",
|
||||
"settings.general.row.font.description": "Die in Codeblöcken verwendete Schriftart anpassen",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Terminalschriftart",
|
||||
"settings.general.row.terminalFont.description": "Passe die im Terminal verwendete Schriftart an",
|
||||
"settings.general.row.uiFont.title": "UI-Schriftart",
|
||||
"settings.general.row.uiFont.description": "Die im gesamten Interface verwendete Schriftart anpassen",
|
||||
"settings.general.row.followup.title": "Verhalten bei Folgefragen",
|
||||
|
|
@ -600,6 +723,23 @@ export const dict = {
|
|||
"Wählen Sie, ob Folgefragen sofort steuern oder in einer Warteschlange warten",
|
||||
"settings.general.row.followup.option.queue": "Warteschlange",
|
||||
"settings.general.row.followup.option.steer": "Steuern",
|
||||
"settings.general.row.showFileTree.title": "Dateibaum",
|
||||
"settings.general.row.showFileTree.description": "Dateibaum in Sitzungen anzeigen",
|
||||
"settings.general.row.showNavigation.title": "Navigationssteuerung",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Schaltflächen zum Vor- und Zurücknavigieren in der Desktop-Titelleiste anzeigen",
|
||||
"settings.general.row.showSearch.title": "Befehlspalette",
|
||||
"settings.general.row.showSearch.description":
|
||||
"Schaltfläche für Suche und Befehlspalette in der Titelleiste anzeigen",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Terminal-Schaltfläche in der Desktop-Titelleiste anzeigen",
|
||||
"settings.general.row.showStatus.title": "Serverstatus",
|
||||
"settings.general.row.showStatus.description": "Serverstatus-Schaltfläche in der Titelleiste anzeigen",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation unten",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Titelleiste und Sitzungs-Tabs auf Mobilgeräten am unteren Bildschirmrand platzieren",
|
||||
"settings.general.row.showCustomAgents.title": "Benutzerdefinierte Agenten",
|
||||
"settings.general.row.showCustomAgents.description": "Agentenauswahl im Eingabebereich anzeigen",
|
||||
"settings.general.row.reasoningSummaries.title": "Reasoning-Zusammenfassungen anzeigen",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Zusammenfassungen des Modell-Reasonings in der Timeline anzeigen",
|
||||
|
|
@ -609,6 +749,11 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Edit-Tool-Abschnitte ausklappen",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Edit-, Write- und Patch-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen",
|
||||
"settings.general.row.newLayoutDesigns.title": "Neues Layout und Design",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Neu gestaltetes Layout, Startseite, Eingabebereich und Sitzungsoberfläche aktivieren",
|
||||
"settings.general.row.pinchZoom.title": "Zoom per Fingergeste",
|
||||
"settings.general.row.pinchZoom.description": "Zoomen per Zwei-Finger- und Ctrl-Scroll-Geste erlauben",
|
||||
"settings.general.row.wayland.title": "Natives Wayland verwenden",
|
||||
"settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -621,6 +766,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Manuell nach Updates suchen und installieren, wenn verfügbar",
|
||||
"settings.updates.action.checkNow": "Jetzt prüfen",
|
||||
"settings.updates.action.checking": "Wird geprüft...",
|
||||
"settings.updates.action.downloading": "Wird heruntergeladen...",
|
||||
"settings.updates.action.installing": "Wird installiert...",
|
||||
"settings.updates.toast.latest.title": "Du bist auf dem neuesten Stand",
|
||||
"settings.updates.toast.latest.description": "Du verwendest die aktuelle Version von OpenCode.",
|
||||
"sound.option.none": "Keine",
|
||||
|
|
@ -742,8 +889,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Eine Fähigkeit nach Namen laden",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Language-Server-Abfragen ausführen",
|
||||
"settings.permissions.tool.todowrite.title": "Todo schreiben",
|
||||
"settings.permissions.tool.todowrite.description": "Die Todo-Liste aktualisieren",
|
||||
"settings.permissions.tool.webfetch.title": "Web-Abruf",
|
||||
"settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
|
||||
"settings.permissions.tool.websearch.title": "Web-Suche",
|
||||
|
|
@ -807,7 +952,6 @@ export const dict = {
|
|||
"Änderungen in diesem Projekt verfolgen, überprüfen und rückgängig machen",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git-Repository wird erstellt...",
|
||||
"session.review.noVcs.createGit.action": "Git-Repository erstellen",
|
||||
"session.todo.progress": "{{done}} von {{total}} Aufgaben erledigt",
|
||||
"session.question.progress": "{{current}} von {{total}} Fragen",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Datei-Explorer",
|
||||
|
|
|
|||
|
|
@ -212,6 +212,11 @@ export const dict = {
|
|||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Allows: {{inputs}}",
|
||||
"model.tooltip.context.label": "Context",
|
||||
"model.tooltip.inputs": "Inputs",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Provider",
|
||||
"model.tooltip.reasoning": "Reasoning",
|
||||
"model.tooltip.reasoning.allowed": "Allows reasoning",
|
||||
"model.tooltip.reasoning.none": "No reasoning",
|
||||
"model.tooltip.context": "Context limit {{limit}}",
|
||||
|
|
@ -654,10 +659,6 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Jump to latest",
|
||||
|
||||
"session.context.addToContext": "Add {{selection}} to context",
|
||||
"session.todo.title": "Todos",
|
||||
"session.todo.collapse": "Collapse",
|
||||
"session.todo.expand": "Expand",
|
||||
"session.todo.progress": "{{done}} of {{total}} todos completed",
|
||||
"session.question.progress": "{{current}} of {{total}} questions",
|
||||
"session.question.minimize": "Minimize question",
|
||||
"session.question.restore": "Restore question",
|
||||
|
|
@ -1035,8 +1036,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Load a skill by name",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Run language server queries",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "Update the todo list",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Fetch content from a URL",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "Alternar barra lateral",
|
||||
"command.project.open": "Abrir proyecto",
|
||||
"command.project.previous": "Proyecto anterior",
|
||||
"command.project.next": "Siguiente proyecto",
|
||||
"command.project.index": "Cambiar al proyecto {{index}}",
|
||||
"command.provider.connect": "Conectar proveedor",
|
||||
"command.server.switch": "Cambiar servidor",
|
||||
"command.settings.open": "Abrir ajustes",
|
||||
|
|
@ -45,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "Nueva sesión",
|
||||
"command.file.open": "Abrir archivo",
|
||||
"command.tab.close": "Cerrar pestaña",
|
||||
"command.tab.reopenClosed": "Reabrir pestaña cerrada",
|
||||
"command.context.addSelection": "Añadir selección al contexto",
|
||||
"command.context.addSelection.description": "Añadir las líneas seleccionadas del archivo actual",
|
||||
"command.input.focus": "Enfocar entrada",
|
||||
|
|
@ -208,6 +212,11 @@ export const dict = {
|
|||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Permite: {{inputs}}",
|
||||
"model.tooltip.context.label": "Contexto",
|
||||
"model.tooltip.inputs": "Entradas",
|
||||
"model.tooltip.model": "Modelo",
|
||||
"model.tooltip.provider": "Proveedor",
|
||||
"model.tooltip.reasoning": "Razonamiento",
|
||||
"model.tooltip.reasoning.allowed": "Permite razonamiento",
|
||||
"model.tooltip.reasoning.none": "Sin razonamiento",
|
||||
"model.tooltip.context": "Límite de contexto {{limit}}",
|
||||
|
|
@ -218,6 +227,7 @@ export const dict = {
|
|||
"common.loading": "Cargando",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Cancelar",
|
||||
"common.clear": "Limpiar",
|
||||
"common.connect": "Conectar",
|
||||
"common.disconnect": "Desconectar",
|
||||
"common.continue": "Enviar",
|
||||
|
|
@ -235,6 +245,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc para salir",
|
||||
"session.child.promptDisabled": "No se pueden enviar prompts a las sesiones de subagentes.",
|
||||
"session.child.backToParent": "Volver a la sesión principal.",
|
||||
|
||||
"prompt.example.1": "Arreglar un TODO en el código",
|
||||
"prompt.example.2": "¿Cuál es el stack tecnológico de este proyecto?",
|
||||
|
|
@ -341,6 +353,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Eliminar",
|
||||
"dialog.server.current": "Servidor actual",
|
||||
"dialog.server.status.default": "Predeterminado",
|
||||
"wsl.server.add": "Añadir servidor WSL",
|
||||
"wsl.server.addShort": "Añadir WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Servidor WSL",
|
||||
"wsl.server.retryStart": "Reintentar el inicio",
|
||||
"wsl.server.updating": "Actualizando...",
|
||||
"wsl.onboarding.step.distro": "Elegir distribución",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Comprobando WSL...",
|
||||
"wsl.onboarding.restartRequired": "Es necesario reiniciar Windows para terminar de instalar WSL.",
|
||||
"wsl.onboarding.ready": "WSL está listo.",
|
||||
"wsl.onboarding.required": "Se necesita WSL para continuar.",
|
||||
"wsl.onboarding.checkingDistros": "Comprobando distribuciones...",
|
||||
"wsl.onboarding.installingDistro": "Instalando {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Comprobando {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Listando distribuciones...",
|
||||
"wsl.onboarding.distroReady": "La distribución {{distro}} está lista.",
|
||||
"wsl.onboarding.distroNotInstalled": "La distribución {{distro}} aún no está instalada.",
|
||||
"wsl.onboarding.openDistroOnce": "Abre {{distro}} una vez para completar la configuración.",
|
||||
"wsl.onboarding.finishingDistro": "Completando la configuración de {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Elige una distribución o instala una abajo.",
|
||||
"wsl.onboarding.checkingOpencode": "Comprobando OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Comprobando OpenCode en {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Actualizando OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Actualizando OpenCode en {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Actualiza OpenCode en {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Actualizar OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode está listo en {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode está listo.",
|
||||
"wsl.onboarding.installOpencodeIn": "Instala OpenCode en {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Instalar OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Elige primero una distribución.",
|
||||
"wsl.onboarding.loadFailed": "No se pudo cargar el estado de WSL.",
|
||||
"wsl.onboarding.loading": "Cargando...",
|
||||
"wsl.onboarding.installedDistros": "Distribuciones instaladas",
|
||||
"wsl.onboarding.checkAgain": "Comprobar de nuevo",
|
||||
"wsl.onboarding.distroStatus.ready": "Lista",
|
||||
"wsl.onboarding.distroStatus.checking": "Comprobando...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode no está instalado",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Faltan bash y curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "No compatible · Usa WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "¿Necesitas otra distribución?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Instala una distribución de Linux desde el catálogo de WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL no está instalado",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"Se necesita WSL (Subsistema de Windows para Linux) para que OpenCode pueda añadir un servidor WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL no disponible",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode no pudo comprobar WSL en este equipo.",
|
||||
"wsl.onboarding.installWsl": "Instalar WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Reinicia Windows para terminar de instalar WSL y, después, vuelve a abrir OpenCode.",
|
||||
"wsl.onboarding.next": "Siguiente",
|
||||
"wsl.onboarding.refresh": "Actualizar",
|
||||
"wsl.onboarding.allDistrosAdded": "Ya se han añadido todas las distribuciones instaladas.",
|
||||
"wsl.onboarding.noDistros": "Aún no se han detectado distribuciones.",
|
||||
"wsl.onboarding.install": "Instalar",
|
||||
"wsl.onboarding.installing": "Instalando...",
|
||||
"wsl.onboarding.installDistro": "Instalar distribución",
|
||||
"wsl.onboarding.searchDistros": "Buscar distribuciones",
|
||||
"wsl.onboarding.wsl2Required": "Se necesita WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Esta distribución necesita bash y curl.",
|
||||
"wsl.onboarding.openTerminal": "Abrir terminal",
|
||||
"wsl.onboarding.path": "Ruta: {{path}}",
|
||||
"wsl.onboarding.notFound": "no encontrado",
|
||||
"wsl.onboarding.version": "Versión: {{version}}",
|
||||
"wsl.onboarding.unknown": "desconocida",
|
||||
"wsl.onboarding.desktopVersion": "escritorio {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "La versión instalada no coincide con la versión de la aplicación de escritorio.",
|
||||
"wsl.onboarding.adding": "Añadiendo...",
|
||||
|
||||
"dialog.project.edit.title": "Editar proyecto",
|
||||
"dialog.project.edit.name": "Nombre",
|
||||
|
|
@ -405,6 +486,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -453,10 +535,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Algo salió mal",
|
||||
"error.page.description": "Ocurrió un error al cargar la aplicación.",
|
||||
"error.page.description.localServerStartup": "Ocurrió un error al iniciar el servidor local.",
|
||||
"error.page.details.label": "Detalles del error",
|
||||
"error.page.action.restart": "Reiniciar",
|
||||
"error.page.action.report": "Informar error",
|
||||
"error.page.action.reported": "Error informado",
|
||||
"error.page.action.exportLogs": "Exportar registros",
|
||||
"error.page.action.checking": "Comprobando...",
|
||||
"error.page.action.checkUpdates": "Buscar actualizaciones",
|
||||
"error.page.action.updateTo": "Actualizar a {{version}}",
|
||||
|
|
@ -505,11 +589,31 @@ export const dict = {
|
|||
"home.recentProjects": "Proyectos recientes",
|
||||
"home.empty.title": "Sin proyectos recientes",
|
||||
"home.empty.description": "Empieza abriendo un proyecto local",
|
||||
"home.title": "Inicio",
|
||||
"home.projects": "Proyectos",
|
||||
"home.project.add": "Añadir proyecto",
|
||||
"home.recentlyClosed": "Proyectos cerrados recientemente",
|
||||
"home.server.collapse": "Contraer proyectos del servidor",
|
||||
"home.server.expand": "Expandir proyectos del servidor",
|
||||
"home.sessions.search.placeholder": "Buscar sesiones",
|
||||
"home.sessions.search.placeholder.scoped": "Buscar sesiones en {{scope}}",
|
||||
"home.sessions.search.sessions": "Sesiones",
|
||||
"home.sessions.search.noResults": "No se encontraron sesiones para {{query}}",
|
||||
"home.sessions.empty": "Aún no hay nada",
|
||||
"home.sessions.empty.description": "Crea una sesión para empezar",
|
||||
"home.sessions.group.today": "Hoy",
|
||||
"home.sessions.group.yesterday": "Ayer",
|
||||
"home.sessions.group.older": "Anteriores",
|
||||
|
||||
"session.tab.session": "Sesión",
|
||||
"session.tab.review": "Revisión",
|
||||
"session.tab.context": "Contexto",
|
||||
"session.tab.unknown": "Sesión desconocida",
|
||||
"session.panel.reviewAndFiles": "Revisión y archivos",
|
||||
"session.error.notFound": "No se encuentra esta sesión",
|
||||
"session.error.notFound.description": "Esta pestaña apunta a una sesión que ya no existe en este servidor.",
|
||||
"session.error.notFound.closeTab": "Cerrar pestaña",
|
||||
"session.error.serverConnection": "No se puede conectar a este servidor",
|
||||
"session.review.filesChanged": "{{count}} Archivos Cambiados",
|
||||
"session.review.change.one": "Cambio",
|
||||
"session.review.change.other": "Cambios",
|
||||
|
|
@ -519,6 +623,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"El seguimiento de instantáneas está deshabilitado en la configuración, por lo que los cambios de sesión no están disponibles",
|
||||
"session.review.noChanges": "Sin cambios",
|
||||
"session.review.noUncommittedChanges": "Aún no hay cambios sin confirmar",
|
||||
"session.review.noBranchChanges": "Aún no hay cambios en la rama",
|
||||
|
||||
"session.files.selectToOpen": "Selecciona un archivo para abrir",
|
||||
"session.files.all": "Todos los archivos",
|
||||
|
|
@ -532,9 +638,10 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Ir al último",
|
||||
|
||||
"session.context.addToContext": "Añadir {{selection}} al contexto",
|
||||
"session.todo.title": "Tareas",
|
||||
"session.todo.collapse": "Contraer",
|
||||
"session.todo.expand": "Expandir",
|
||||
"session.question.minimize": "Minimizar pregunta",
|
||||
"session.question.restore": "Restaurar pregunta",
|
||||
"session.question.pending.one": "{{count}} pregunta pendiente",
|
||||
"session.question.pending.other": "{{count}} preguntas pendientes",
|
||||
"session.followupDock.summary.one": "{{count}} mensaje en cola",
|
||||
"session.followupDock.summary.other": "{{count}} mensajes en cola",
|
||||
"session.followupDock.sendNow": "Enviar ahora",
|
||||
|
|
@ -548,9 +655,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Restaurar mensaje",
|
||||
|
||||
"session.new.title": "Construye lo que quieras",
|
||||
"session.new.project.new": "Nuevo proyecto",
|
||||
"session.new.project.search": "Buscar proyectos",
|
||||
"session.new.project.add": "Añadir proyecto",
|
||||
"session.new.worktree.main": "Rama principal",
|
||||
"session.new.worktree.mainWithBranch": "Rama principal ({{branch}})",
|
||||
"session.new.worktree.create": "Crear nuevo árbol de trabajo",
|
||||
"session.new.workspace.runIn": "Ejecutar sesión en",
|
||||
"session.new.workspace.triggerLocal": "Local",
|
||||
"session.new.workspace.local": "Repositorio local",
|
||||
"session.new.workspace.existing": "Espacio de trabajo…",
|
||||
"session.new.lastModified": "Última modificación",
|
||||
|
||||
"session.header.search.placeholder": "Buscar {{project}}",
|
||||
|
|
@ -621,6 +735,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Sesiones recientes",
|
||||
"sidebar.project.viewAllSessions": "Ver todas las sesiones",
|
||||
"sidebar.project.clearNotifications": "Borrar notificaciones",
|
||||
"sidebar.empty.title": "No hay proyectos abiertos",
|
||||
"sidebar.empty.description": "Abre un proyecto para empezar",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
|
||||
|
|
@ -633,6 +749,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "Ejecutar el servidor OpenCode dentro de WSL en Windows.",
|
||||
|
||||
"settings.general.section.appearance": "Apariencia",
|
||||
"settings.general.section.advanced": "Avanzado",
|
||||
"settings.general.section.notifications": "Notificaciones del sistema",
|
||||
"settings.general.section.updates": "Actualizaciones",
|
||||
"settings.general.section.sounds": "Efectos de sonido",
|
||||
|
|
@ -641,6 +758,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "Idioma",
|
||||
"settings.general.row.language.description": "Cambiar el idioma de visualización para OpenCode",
|
||||
"settings.general.row.shell.title": "Shell del terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Elige el shell que se usará en el terminal. Los shells compatibles también se usarán para las llamadas a herramientas del agente.",
|
||||
"settings.general.row.shell.autoDefault": "Automático (Predeterminado)",
|
||||
"settings.general.row.shell.terminalOnly": "solo en el terminal",
|
||||
"settings.general.row.appearance.title": "Apariencia",
|
||||
"settings.general.row.appearance.description": "Personaliza cómo se ve OpenCode en tu dispositivo",
|
||||
"settings.general.row.colorScheme.title": "Esquema de color",
|
||||
|
|
@ -649,8 +771,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Personaliza el tema de OpenCode.",
|
||||
"settings.general.row.font.title": "Fuente de código",
|
||||
"settings.general.row.font.description": "Personaliza la fuente usada en bloques de código",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Fuente del terminal",
|
||||
"settings.general.row.terminalFont.description": "Personaliza la fuente utilizada en el terminal",
|
||||
"settings.general.row.uiFont.title": "Fuente de la interfaz",
|
||||
"settings.general.row.uiFont.description": "Personaliza la fuente usada en toda la interfaz",
|
||||
"settings.general.row.followup.title": "Comportamiento de seguimiento",
|
||||
|
|
@ -658,6 +780,23 @@ export const dict = {
|
|||
"Elige si los prompts de seguimiento se dirigen inmediatamente o esperan en una cola",
|
||||
"settings.general.row.followup.option.queue": "Cola",
|
||||
"settings.general.row.followup.option.steer": "Dirigir",
|
||||
"settings.general.row.showFileTree.title": "Árbol de archivos",
|
||||
"settings.general.row.showFileTree.description": "Mostrar el panel del árbol de archivos en las sesiones",
|
||||
"settings.general.row.showNavigation.title": "Controles de navegación",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Mostrar los botones para retroceder y avanzar en la barra de título de la aplicación de escritorio",
|
||||
"settings.general.row.showSearch.title": "Paleta de comandos",
|
||||
"settings.general.row.showSearch.description":
|
||||
"Mostrar el botón de búsqueda y paleta de comandos en la barra de título",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Mostrar el botón del terminal en la barra de título",
|
||||
"settings.general.row.showStatus.title": "Estado del servidor",
|
||||
"settings.general.row.showStatus.description": "Mostrar el botón de estado del servidor en la barra de título",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navegación inferior",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Colocar la barra de título y las pestañas de sesión en la parte inferior de la pantalla en dispositivos móviles",
|
||||
"settings.general.row.showCustomAgents.title": "Agentes personalizados",
|
||||
"settings.general.row.showCustomAgents.description": "Mostrar el selector de agentes en el editor",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resúmenes de razonamiento",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Mostrar resúmenes del razonamiento del modelo en la línea de tiempo",
|
||||
|
|
@ -667,6 +806,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Expandir partes de la herramienta de edición",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Mostrar las partes de las herramientas de edición, escritura y parcheado expandidas por defecto en la línea de tiempo",
|
||||
"settings.general.row.newLayoutDesigns.title": "Nuevo diseño y estilos",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Activar el nuevo diseño de la interfaz, la página de inicio, el editor y las sesiones",
|
||||
"settings.general.row.pinchZoom.title": "Pellizcar para ampliar",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Permitir ampliar con el gesto de pellizco del panel táctil y con Ctrl-scroll",
|
||||
"settings.general.row.wayland.title": "Usar Wayland nativo",
|
||||
"settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -682,6 +827,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Buscar actualizaciones manualmente e instalarlas si hay alguna",
|
||||
"settings.updates.action.checkNow": "Buscar ahora",
|
||||
"settings.updates.action.checking": "Buscando...",
|
||||
"settings.updates.action.downloading": "Descargando...",
|
||||
"settings.updates.action.installing": "Instalando...",
|
||||
"settings.updates.toast.latest.title": "Estás al día",
|
||||
"settings.updates.toast.latest.description": "Estás usando la última versión de OpenCode.",
|
||||
"sound.option.none": "Ninguno",
|
||||
|
|
@ -812,8 +959,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Cargar una habilidad por nombre",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Ejecutar consultas de servidor de lenguaje",
|
||||
"settings.permissions.tool.todowrite.title": "Escribir Todo",
|
||||
"settings.permissions.tool.todowrite.description": "Actualizar la lista de tareas",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
|
||||
"settings.permissions.tool.websearch.title": "Búsqueda Web",
|
||||
|
|
@ -878,7 +1023,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Rastrea, revisa y deshaz cambios en este proyecto",
|
||||
"session.review.noVcs.createGit.actionLoading": "Creando repositorio Git...",
|
||||
"session.review.noVcs.createGit.action": "Crear repositorio Git",
|
||||
"session.todo.progress": "{{done}} de {{total}} tareas completadas",
|
||||
"session.question.progress": "{{current}} de {{total}} preguntas",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorador de archivos",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "Sombre",
|
||||
"command.sidebar.toggle": "Basculer la barre latérale",
|
||||
"command.project.open": "Ouvrir un projet",
|
||||
"command.project.previous": "Projet précédent",
|
||||
"command.project.next": "Projet suivant",
|
||||
"command.project.index": "Passer au projet {{index}}",
|
||||
"command.provider.connect": "Connecter un fournisseur",
|
||||
"command.server.switch": "Changer de serveur",
|
||||
"command.settings.open": "Ouvrir les paramètres",
|
||||
|
|
@ -39,6 +42,7 @@ export const dict = {
|
|||
"command.session.new": "Nouvelle session",
|
||||
"command.file.open": "Ouvrir un fichier",
|
||||
"command.tab.close": "Fermer l'onglet",
|
||||
"command.tab.reopenClosed": "Rouvrir l'onglet fermé",
|
||||
"command.context.addSelection": "Ajouter la sélection au contexte",
|
||||
"command.context.addSelection.description": "Ajouter les lignes sélectionnées du fichier actuel",
|
||||
"command.input.focus": "Focus sur l'entrée",
|
||||
|
|
@ -193,6 +197,11 @@ export const dict = {
|
|||
"model.input.video": "vidéo",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Autorise : {{inputs}}",
|
||||
"model.tooltip.context.label": "Contexte",
|
||||
"model.tooltip.inputs": "Entrées",
|
||||
"model.tooltip.model": "Modèle",
|
||||
"model.tooltip.provider": "Fournisseur",
|
||||
"model.tooltip.reasoning": "Raisonnement",
|
||||
"model.tooltip.reasoning.allowed": "Autorise le raisonnement",
|
||||
"model.tooltip.reasoning.none": "Sans raisonnement",
|
||||
"model.tooltip.context": "Limite de contexte {{limit}}",
|
||||
|
|
@ -202,6 +211,7 @@ export const dict = {
|
|||
"common.loading": "Chargement",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Annuler",
|
||||
"common.clear": "Effacer",
|
||||
"common.connect": "Connecter",
|
||||
"common.disconnect": "Déconnecter",
|
||||
"common.continue": "Soumettre",
|
||||
|
|
@ -218,6 +228,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc pour quitter",
|
||||
"session.child.promptDisabled": "Les sessions de sous-agents ne peuvent pas recevoir de messages.",
|
||||
"session.child.backToParent": "Revenir à la session principale.",
|
||||
"prompt.example.1": "Corriger un TODO dans la base de code",
|
||||
"prompt.example.2": "Quelle est la pile technique de ce projet ?",
|
||||
"prompt.example.3": "Réparer les tests échoués",
|
||||
|
|
@ -315,6 +327,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Supprimer",
|
||||
"dialog.server.current": "Serveur actuel",
|
||||
"dialog.server.status.default": "Défaut",
|
||||
"wsl.server.add": "Ajouter un serveur WSL",
|
||||
"wsl.server.addShort": "Ajouter WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Serveur WSL",
|
||||
"wsl.server.retryStart": "Réessayer de démarrer",
|
||||
"wsl.server.updating": "Mise à jour...",
|
||||
"wsl.onboarding.step.distro": "Choisir une distribution",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Vérification de WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows doit redémarrer pour terminer l'installation de WSL.",
|
||||
"wsl.onboarding.ready": "WSL est prêt.",
|
||||
"wsl.onboarding.required": "WSL est requis pour continuer.",
|
||||
"wsl.onboarding.checkingDistros": "Vérification des distributions...",
|
||||
"wsl.onboarding.installingDistro": "Installation de {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Vérification de {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Récupération de la liste des distributions...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} est prête.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} n'est pas encore installée.",
|
||||
"wsl.onboarding.openDistroOnce": "Ouvrez {{distro}} une fois pour terminer la configuration.",
|
||||
"wsl.onboarding.finishingDistro": "Finalisation de la configuration de {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Choisissez une distribution ou installez-en une ci-dessous.",
|
||||
"wsl.onboarding.checkingOpencode": "Vérification d'OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Vérification d'OpenCode dans {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Mise à jour d'OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Mise à jour d'OpenCode dans {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Mettez à jour OpenCode dans {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Mettre à jour OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode est prêt dans {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode est prêt.",
|
||||
"wsl.onboarding.installOpencodeIn": "Installez OpenCode dans {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Installer OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Choisissez d'abord une distribution.",
|
||||
"wsl.onboarding.loadFailed": "Impossible de charger l'état de WSL.",
|
||||
"wsl.onboarding.loading": "Chargement...",
|
||||
"wsl.onboarding.installedDistros": "Distributions installées",
|
||||
"wsl.onboarding.checkAgain": "Vérifier à nouveau",
|
||||
"wsl.onboarding.distroStatus.ready": "Prête",
|
||||
"wsl.onboarding.distroStatus.checking": "Vérification...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode non installé",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash et curl manquants",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Non prise en charge · Utilisez WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Besoin d'une autre distribution ?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Installez une distribution Linux depuis le catalogue WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL n'est pas installé",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) est requis avant qu'OpenCode puisse ajouter un serveur WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL indisponible",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode n'a pas pu vérifier WSL sur cet appareil.",
|
||||
"wsl.onboarding.installWsl": "Installer WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Redémarrez Windows pour terminer l'installation de WSL, puis rouvrez OpenCode.",
|
||||
"wsl.onboarding.next": "Suivant",
|
||||
"wsl.onboarding.refresh": "Actualiser",
|
||||
"wsl.onboarding.allDistrosAdded": "Toutes les distributions installées ont déjà été ajoutées.",
|
||||
"wsl.onboarding.noDistros": "Aucune distribution détectée pour l'instant.",
|
||||
"wsl.onboarding.install": "Installer",
|
||||
"wsl.onboarding.installing": "Installation...",
|
||||
"wsl.onboarding.installDistro": "Installer une distribution",
|
||||
"wsl.onboarding.searchDistros": "Rechercher des distributions",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 est requis.",
|
||||
"wsl.onboarding.toolsRequired": "Cette distribution nécessite bash et curl.",
|
||||
"wsl.onboarding.openTerminal": "Ouvrir le terminal",
|
||||
"wsl.onboarding.path": "Chemin : {{path}}",
|
||||
"wsl.onboarding.notFound": "introuvable",
|
||||
"wsl.onboarding.version": "Version : {{version}}",
|
||||
"wsl.onboarding.unknown": "inconnue",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "La version installée ne correspond pas à celle de l'application desktop.",
|
||||
"wsl.onboarding.adding": "Ajout...",
|
||||
"dialog.project.edit.title": "Modifier le projet",
|
||||
"dialog.project.edit.name": "Nom",
|
||||
"dialog.project.edit.icon": "Icône",
|
||||
|
|
@ -373,6 +454,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Langue",
|
||||
|
|
@ -410,10 +492,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "Pas encore",
|
||||
"error.page.title": "Quelque chose s'est mal passé",
|
||||
"error.page.description": "Une erreur s'est produite lors du chargement de l'application.",
|
||||
"error.page.description.localServerStartup": "Une erreur s'est produite lors du démarrage du serveur local.",
|
||||
"error.page.details.label": "Détails de l'erreur",
|
||||
"error.page.action.restart": "Redémarrer",
|
||||
"error.page.action.report": "Signaler l'erreur",
|
||||
"error.page.action.reported": "Erreur signalée",
|
||||
"error.page.action.exportLogs": "Exporter les journaux",
|
||||
"error.page.action.checking": "Vérification...",
|
||||
"error.page.action.checkUpdates": "Vérifier les mises à jour",
|
||||
"error.page.action.updateTo": "Mettre à jour vers {{version}}",
|
||||
|
|
@ -458,16 +542,38 @@ export const dict = {
|
|||
"home.recentProjects": "Projets récents",
|
||||
"home.empty.title": "Aucun projet récent",
|
||||
"home.empty.description": "Commencez par ouvrir un projet local",
|
||||
"home.title": "Accueil",
|
||||
"home.projects": "Projets",
|
||||
"home.project.add": "Ajouter un projet",
|
||||
"home.recentlyClosed": "Récemment fermés",
|
||||
"home.server.collapse": "Réduire les projets du serveur",
|
||||
"home.server.expand": "Développer les projets du serveur",
|
||||
"home.sessions.search.placeholder": "Rechercher des sessions",
|
||||
"home.sessions.search.placeholder.scoped": "Rechercher les sessions dans {{scope}}",
|
||||
"home.sessions.search.sessions": "Sessions",
|
||||
"home.sessions.search.noResults": "Aucune session trouvée pour {{query}}",
|
||||
"home.sessions.empty": "Rien ici pour l'instant",
|
||||
"home.sessions.empty.description": "Créez une session pour commencer",
|
||||
"home.sessions.group.today": "Aujourd'hui",
|
||||
"home.sessions.group.yesterday": "Hier",
|
||||
"home.sessions.group.older": "Plus anciennes",
|
||||
"session.tab.session": "Session",
|
||||
"session.tab.review": "Revue",
|
||||
"session.tab.context": "Contexte",
|
||||
"session.tab.unknown": "Session inconnue",
|
||||
"session.panel.reviewAndFiles": "Revue et fichiers",
|
||||
"session.error.notFound": "Cette session est introuvable",
|
||||
"session.error.notFound.description": "Cet onglet pointe vers une session qui n'existe plus sur ce serveur.",
|
||||
"session.error.notFound.closeTab": "Fermer l'onglet",
|
||||
"session.error.serverConnection": "Impossible de se connecter à ce serveur",
|
||||
"session.review.filesChanged": "{{count}} fichiers modifiés",
|
||||
"session.review.change.one": "Modification",
|
||||
"session.review.change.other": "Modifications",
|
||||
"session.review.loadingChanges": "Chargement des modifications...",
|
||||
"session.review.empty": "Aucune modification dans cette session pour l'instant",
|
||||
"session.review.noChanges": "Aucune modification",
|
||||
"session.review.noUncommittedChanges": "Aucune modification non validée pour l'instant",
|
||||
"session.review.noBranchChanges": "Aucune modification de branche pour l'instant",
|
||||
"session.review.noVcs": "Aucun système de contrôle de version Git détecté, modifications non affichées",
|
||||
"session.review.noSnapshot":
|
||||
"Le suivi des instantanés est désactivé dans la configuration, les modifications de session sont donc indisponibles",
|
||||
|
|
@ -481,9 +587,10 @@ export const dict = {
|
|||
"session.messages.loading": "Chargement des messages...",
|
||||
"session.messages.jumpToLatest": "Aller au dernier",
|
||||
"session.context.addToContext": "Ajouter {{selection}} au contexte",
|
||||
"session.todo.title": "Tâches",
|
||||
"session.todo.collapse": "Réduire",
|
||||
"session.todo.expand": "Développer",
|
||||
"session.question.minimize": "Réduire la question",
|
||||
"session.question.restore": "Restaurer la question",
|
||||
"session.question.pending.one": "{{count}} question en attente",
|
||||
"session.question.pending.other": "{{count}} questions en attente",
|
||||
"session.followupDock.summary.one": "{{count}} message en file d'attente",
|
||||
"session.followupDock.summary.other": "{{count}} messages en file d'attente",
|
||||
"session.followupDock.sendNow": "Envoyer maintenant",
|
||||
|
|
@ -496,9 +603,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "Développer les messages annulés",
|
||||
"session.revertDock.restore": "Restaurer le message",
|
||||
"session.new.title": "Créez ce que vous voulez",
|
||||
"session.new.project.new": "Nouveau projet",
|
||||
"session.new.project.search": "Rechercher des projets",
|
||||
"session.new.project.add": "Ajouter un projet",
|
||||
"session.new.worktree.main": "Branche principale",
|
||||
"session.new.worktree.mainWithBranch": "Branche principale ({{branch}})",
|
||||
"session.new.worktree.create": "Créer un nouvel arbre de travail",
|
||||
"session.new.workspace.runIn": "Exécuter la session dans",
|
||||
"session.new.workspace.triggerLocal": "Local",
|
||||
"session.new.workspace.local": "Dépôt local",
|
||||
"session.new.workspace.existing": "Espace de travail…",
|
||||
"session.new.lastModified": "Dernière modification",
|
||||
"session.header.search.placeholder": "Rechercher {{project}}",
|
||||
"session.header.searchFiles": "Rechercher des fichiers",
|
||||
|
|
@ -564,6 +678,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Sessions récentes",
|
||||
"sidebar.project.viewAllSessions": "Voir toutes les sessions",
|
||||
"sidebar.project.clearNotifications": "Effacer les notifications",
|
||||
"sidebar.empty.title": "Aucun projet ouvert",
|
||||
"sidebar.empty.description": "Ouvrez un projet pour commencer",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Bureau",
|
||||
"settings.section.server": "Serveur",
|
||||
|
|
@ -573,6 +689,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.title": "Intégration WSL",
|
||||
"settings.desktop.wsl.description": "Exécuter le serveur OpenCode dans WSL sur Windows.",
|
||||
"settings.general.section.appearance": "Apparence",
|
||||
"settings.general.section.advanced": "Avancé",
|
||||
"settings.general.section.notifications": "Notifications système",
|
||||
"settings.general.section.updates": "Mises à jour",
|
||||
"settings.general.section.sounds": "Effets sonores",
|
||||
|
|
@ -580,6 +697,11 @@ export const dict = {
|
|||
"settings.general.section.display": "Affichage",
|
||||
"settings.general.row.language.title": "Langue",
|
||||
"settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode",
|
||||
"settings.general.row.shell.title": "Shell du terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Choisissez le shell utilisé par votre terminal. Les shells compatibles sont également utilisés pour les appels d'outils de l'agent.",
|
||||
"settings.general.row.shell.autoDefault": "Auto (par défaut)",
|
||||
"settings.general.row.shell.terminalOnly": "terminal uniquement",
|
||||
"settings.general.row.appearance.title": "Apparence",
|
||||
"settings.general.row.appearance.description": "Personnaliser l'apparence d'OpenCode sur votre appareil",
|
||||
"settings.general.row.colorScheme.title": "Schéma de couleurs",
|
||||
|
|
@ -588,8 +710,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.",
|
||||
"settings.general.row.font.title": "Police de code",
|
||||
"settings.general.row.font.description": "Personnaliser la police utilisée dans les blocs de code",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Police du terminal",
|
||||
"settings.general.row.terminalFont.description": "Personnalisez la police utilisée dans le terminal",
|
||||
"settings.general.row.uiFont.title": "Police de l'interface",
|
||||
"settings.general.row.uiFont.description": "Personnaliser la police utilisée dans toute l'interface",
|
||||
"settings.general.row.followup.title": "Comportement de suivi",
|
||||
|
|
@ -597,6 +719,24 @@ export const dict = {
|
|||
"Choisissez si les messages de suivi dirigent immédiatement ou attendent dans une file d'attente",
|
||||
"settings.general.row.followup.option.queue": "File d'attente",
|
||||
"settings.general.row.followup.option.steer": "Diriger",
|
||||
"settings.general.row.showFileTree.title": "Arborescence des fichiers",
|
||||
"settings.general.row.showFileTree.description": "Afficher le panneau d'arborescence des fichiers dans les sessions",
|
||||
"settings.general.row.showNavigation.title": "Commandes de navigation",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Afficher les boutons précédent et suivant dans la barre de titre de l'application desktop",
|
||||
"settings.general.row.showSearch.title": "Palette de commandes",
|
||||
"settings.general.row.showSearch.description":
|
||||
"Afficher le bouton de recherche et de palette de commandes dans la barre de titre",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description":
|
||||
"Afficher le bouton du terminal dans la barre de titre de l'application desktop",
|
||||
"settings.general.row.showStatus.title": "État du serveur",
|
||||
"settings.general.row.showStatus.description": "Afficher le bouton d'état du serveur dans la barre de titre",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation en bas",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Placer la barre de titre et les onglets de session en bas de l'écran sur mobile",
|
||||
"settings.general.row.showCustomAgents.title": "Agents personnalisés",
|
||||
"settings.general.row.showCustomAgents.description": "Afficher le sélecteur d'agent dans la zone de saisie",
|
||||
"settings.general.row.reasoningSummaries.title": "Afficher les résumés de raisonnement",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Afficher les résumés de raisonnement du modèle dans la chronologie",
|
||||
|
|
@ -606,6 +746,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Développer les parties de l'outil edit",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Afficher les parties des outils edit, write et patch développées par défaut dans la chronologie",
|
||||
"settings.general.row.newLayoutDesigns.title": "Nouvelle mise en page et nouveaux designs",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Activer la nouvelle mise en page et les nouvelles interfaces d'accueil, de saisie et de session",
|
||||
"settings.general.row.pinchZoom.title": "Pincer pour zoomer",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Autoriser les gestes de pincement du pavé tactile et Ctrl-défilement pour zoomer",
|
||||
"settings.general.row.wayland.title": "Utiliser Wayland natif",
|
||||
"settings.general.row.wayland.description": "Désactiver le repli X11 sur Wayland. Nécessite un redémarrage.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -618,6 +764,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Vérifier manuellement les mises à jour et installer si disponible",
|
||||
"settings.updates.action.checkNow": "Vérifier maintenant",
|
||||
"settings.updates.action.checking": "Vérification...",
|
||||
"settings.updates.action.downloading": "Téléchargement...",
|
||||
"settings.updates.action.installing": "Installation...",
|
||||
"settings.updates.toast.latest.title": "Vous êtes à jour",
|
||||
"settings.updates.toast.latest.description": "Vous utilisez la dernière version d'OpenCode.",
|
||||
"sound.option.none": "Aucun",
|
||||
|
|
@ -740,8 +888,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Charger une compétence par son nom",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Exécuter des requêtes de serveur de langage",
|
||||
"settings.permissions.tool.todowrite.title": "Écrire Todo",
|
||||
"settings.permissions.tool.todowrite.description": "Mettre à jour la liste de tâches",
|
||||
"settings.permissions.tool.webfetch.title": "Récupération Web",
|
||||
"settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
|
||||
"settings.permissions.tool.websearch.title": "Recherche Web",
|
||||
|
|
@ -804,7 +950,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Suivre, examiner et annuler les modifications dans ce projet",
|
||||
"session.review.noVcs.createGit.actionLoading": "Création du dépôt Git...",
|
||||
"session.review.noVcs.createGit.action": "Créer un dépôt Git",
|
||||
"session.todo.progress": "{{done}} tâches sur {{total}} terminées",
|
||||
"session.question.progress": "{{current}} questions sur {{total}}",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorateur de fichiers",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "ダーク",
|
||||
"command.sidebar.toggle": "サイドバーの切り替え",
|
||||
"command.project.open": "プロジェクトを開く",
|
||||
"command.project.previous": "前のプロジェクト",
|
||||
"command.project.next": "次のプロジェクト",
|
||||
"command.project.index": "プロジェクト{{index}}に切り替え",
|
||||
"command.provider.connect": "プロバイダーに接続",
|
||||
"command.server.switch": "サーバーの切り替え",
|
||||
"command.settings.open": "設定を開く",
|
||||
|
|
@ -39,6 +42,7 @@ export const dict = {
|
|||
"command.session.new": "新しいセッション",
|
||||
"command.file.open": "ファイルを開く",
|
||||
"command.tab.close": "タブを閉じる",
|
||||
"command.tab.reopenClosed": "閉じたタブを再度開く",
|
||||
"command.context.addSelection": "選択範囲をコンテキストに追加",
|
||||
"command.context.addSelection.description": "現在のファイルから選択した行を追加",
|
||||
"command.input.focus": "入力欄にフォーカス",
|
||||
|
|
@ -149,7 +153,7 @@ export const dict = {
|
|||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "小文字、数字、ハイフン、アンダースコア",
|
||||
"provider.custom.field.name.label": "表示名",
|
||||
"provider.custom.field.name.placeholder": "My AI Provider",
|
||||
"provider.custom.field.name.placeholder": "マイAIプロバイダー",
|
||||
"provider.custom.field.baseURL.label": "ベースURL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "APIキー",
|
||||
|
|
@ -192,6 +196,11 @@ export const dict = {
|
|||
"model.input.video": "動画",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "対応: {{inputs}}",
|
||||
"model.tooltip.context.label": "コンテキスト",
|
||||
"model.tooltip.inputs": "入力",
|
||||
"model.tooltip.model": "モデル",
|
||||
"model.tooltip.provider": "プロバイダー",
|
||||
"model.tooltip.reasoning": "推論",
|
||||
"model.tooltip.reasoning.allowed": "推論を許可",
|
||||
"model.tooltip.reasoning.none": "推論なし",
|
||||
"model.tooltip.context": "コンテキスト上限 {{limit}}",
|
||||
|
|
@ -201,6 +210,7 @@ export const dict = {
|
|||
"common.loading": "読み込み中",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "キャンセル",
|
||||
"common.clear": "クリア",
|
||||
"common.connect": "接続",
|
||||
"common.disconnect": "切断",
|
||||
"common.continue": "送信",
|
||||
|
|
@ -217,6 +227,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "シェル",
|
||||
"prompt.mode.normal": "プロンプト",
|
||||
"prompt.mode.shell.exit": "escで終了",
|
||||
"session.child.promptDisabled": "サブエージェントのセッションにはプロンプトを送信できません。",
|
||||
"session.child.backToParent": "メインセッションに戻る。",
|
||||
"prompt.example.1": "コードベースのTODOを修正",
|
||||
"prompt.example.2": "このプロジェクトの技術スタックは何ですか?",
|
||||
"prompt.example.3": "壊れたテストを修正",
|
||||
|
|
@ -313,6 +325,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "削除",
|
||||
"dialog.server.current": "現在のサーバー",
|
||||
"dialog.server.status.default": "デフォルト",
|
||||
"wsl.server.add": "WSLサーバーを追加",
|
||||
"wsl.server.addShort": "WSLを追加",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSLサーバー",
|
||||
"wsl.server.retryStart": "起動を再試行",
|
||||
"wsl.server.updating": "更新中...",
|
||||
"wsl.onboarding.step.distro": "ディストリビューションを選択",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSLを確認中...",
|
||||
"wsl.onboarding.restartRequired": "WSLのインストールを完了するにはWindowsの再起動が必要です。",
|
||||
"wsl.onboarding.ready": "WSLの準備ができました。",
|
||||
"wsl.onboarding.required": "続行するにはWSLが必要です。",
|
||||
"wsl.onboarding.checkingDistros": "ディストリビューションを確認中...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}}をインストール中...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}}を確認中...",
|
||||
"wsl.onboarding.listingDistros": "ディストリビューションの一覧を取得中...",
|
||||
"wsl.onboarding.distroReady": "{{distro}}の準備ができました。",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}}はまだインストールされていません。",
|
||||
"wsl.onboarding.openDistroOnce": "セットアップを完了するには、{{distro}}を一度開いてください。",
|
||||
"wsl.onboarding.finishingDistro": "{{distro}}のセットアップを完了しています。",
|
||||
"wsl.onboarding.pickDistro": "ディストリビューションを選択するか、以下からインストールしてください。",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCodeを確認中...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "{{distro}}内のOpenCodeを確認中...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCodeを更新中...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "{{distro}}内のOpenCodeを更新中...",
|
||||
"wsl.onboarding.updateOpencodeIn": "{{distro}}内のOpenCodeを更新してください。",
|
||||
"wsl.onboarding.updateOpencode": "OpenCodeを更新",
|
||||
"wsl.onboarding.opencodeReadyIn": "{{distro}}内のOpenCodeの準備ができました。",
|
||||
"wsl.onboarding.opencodeReady": "OpenCodeの準備ができました。",
|
||||
"wsl.onboarding.installOpencodeIn": "{{distro}}にOpenCodeをインストールしてください。",
|
||||
"wsl.onboarding.installOpencode": "OpenCodeをインストール",
|
||||
"wsl.onboarding.chooseDistroFirst": "先にディストリビューションを選択してください。",
|
||||
"wsl.onboarding.loadFailed": "WSLの状態を読み込めませんでした。",
|
||||
"wsl.onboarding.loading": "読み込み中...",
|
||||
"wsl.onboarding.installedDistros": "インストール済みのディストリビューション",
|
||||
"wsl.onboarding.checkAgain": "再確認",
|
||||
"wsl.onboarding.distroStatus.ready": "準備完了",
|
||||
"wsl.onboarding.distroStatus.checking": "確認中...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCodeがインストールされていません",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash、curlがありません",
|
||||
"wsl.onboarding.distroStatus.unsupported": "非対応 · WSL 2を使用してください",
|
||||
"wsl.onboarding.needAnotherDistro": "別のディストリビューションが必要ですか?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "WSLカタログからLinuxディストリビューションをインストール",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSLがインストールされていません",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"OpenCodeでWSLサーバーを追加するには、WSL (Windows Subsystem for Linux) が必要です",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSLを利用できません",
|
||||
"wsl.onboarding.wslUnavailable.description": "このマシンのWSLを確認できませんでした。",
|
||||
"wsl.onboarding.installWsl": "WSLをインストール",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"WSLのインストールを完了するにはWindowsを再起動し、OpenCodeをもう一度開いてください。",
|
||||
"wsl.onboarding.next": "次へ",
|
||||
"wsl.onboarding.refresh": "更新",
|
||||
"wsl.onboarding.allDistrosAdded": "インストール済みのディストリビューションはすべて追加済みです。",
|
||||
"wsl.onboarding.noDistros": "ディストリビューションはまだ検出されていません。",
|
||||
"wsl.onboarding.install": "インストール",
|
||||
"wsl.onboarding.installing": "インストール中...",
|
||||
"wsl.onboarding.installDistro": "ディストリビューションをインストール",
|
||||
"wsl.onboarding.searchDistros": "ディストリビューションを検索",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2が必要です。",
|
||||
"wsl.onboarding.toolsRequired": "このディストリビューションにはbashとcurlが必要です。",
|
||||
"wsl.onboarding.openTerminal": "ターミナルを開く",
|
||||
"wsl.onboarding.path": "パス: {{path}}",
|
||||
"wsl.onboarding.notFound": "見つかりません",
|
||||
"wsl.onboarding.version": "バージョン: {{version}}",
|
||||
"wsl.onboarding.unknown": "不明",
|
||||
"wsl.onboarding.desktopVersion": "デスクトップ {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "インストールされているバージョンがデスクトップアプリのバージョンと一致しません。",
|
||||
"wsl.onboarding.adding": "追加中...",
|
||||
"dialog.project.edit.title": "プロジェクトを編集",
|
||||
"dialog.project.edit.name": "名前",
|
||||
"dialog.project.edit.icon": "アイコン",
|
||||
|
|
@ -370,6 +451,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "言語",
|
||||
|
|
@ -406,10 +488,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "今はしない",
|
||||
"error.page.title": "問題が発生しました",
|
||||
"error.page.description": "アプリケーションの読み込み中にエラーが発生しました。",
|
||||
"error.page.description.localServerStartup": "ローカルサーバーの起動中にエラーが発生しました。",
|
||||
"error.page.details.label": "エラー詳細",
|
||||
"error.page.action.restart": "再起動",
|
||||
"error.page.action.report": "エラーを報告",
|
||||
"error.page.action.reported": "エラーを報告しました",
|
||||
"error.page.action.exportLogs": "ログをエクスポート",
|
||||
"error.page.action.checking": "確認中...",
|
||||
"error.page.action.checkUpdates": "アップデートを確認",
|
||||
"error.page.action.updateTo": "{{version}}にアップデート",
|
||||
|
|
@ -451,10 +535,30 @@ export const dict = {
|
|||
"home.recentProjects": "最近のプロジェクト",
|
||||
"home.empty.title": "最近のプロジェクトはありません",
|
||||
"home.empty.description": "ローカルプロジェクトを開いて始めましょう",
|
||||
"home.title": "ホーム",
|
||||
"home.projects": "プロジェクト",
|
||||
"home.project.add": "プロジェクトを追加",
|
||||
"home.recentlyClosed": "最近閉じたプロジェクト",
|
||||
"home.server.collapse": "サーバーのプロジェクトを折りたたむ",
|
||||
"home.server.expand": "サーバーのプロジェクトを展開",
|
||||
"home.sessions.search.placeholder": "セッションを検索",
|
||||
"home.sessions.search.placeholder.scoped": "{{scope}}のセッションを検索",
|
||||
"home.sessions.search.sessions": "セッション",
|
||||
"home.sessions.search.noResults": "{{query}}に一致するセッションが見つかりません",
|
||||
"home.sessions.empty": "まだ何もありません",
|
||||
"home.sessions.empty.description": "セッションを作成して始めましょう",
|
||||
"home.sessions.group.today": "今日",
|
||||
"home.sessions.group.yesterday": "昨日",
|
||||
"home.sessions.group.older": "それ以前",
|
||||
"session.tab.session": "セッション",
|
||||
"session.tab.review": "レビュー",
|
||||
"session.tab.context": "コンテキスト",
|
||||
"session.tab.unknown": "不明なセッション",
|
||||
"session.panel.reviewAndFiles": "レビューとファイル",
|
||||
"session.error.notFound": "このセッションが見つかりません",
|
||||
"session.error.notFound.description": "このタブが参照するセッションは、このサーバーには存在しません。",
|
||||
"session.error.notFound.closeTab": "タブを閉じる",
|
||||
"session.error.serverConnection": "このサーバーに接続できません",
|
||||
"session.review.filesChanged": "{{count}} ファイル変更",
|
||||
"session.review.change.one": "変更",
|
||||
"session.review.change.other": "変更",
|
||||
|
|
@ -463,6 +567,8 @@ export const dict = {
|
|||
"session.review.noVcs": "Gitバージョン管理システムが検出されないため、変更は表示されません",
|
||||
"session.review.noSnapshot": "設定でスナップショット追跡が無効になっているため、セッションの変更は利用できません",
|
||||
"session.review.noChanges": "変更なし",
|
||||
"session.review.noUncommittedChanges": "コミットされていない変更はまだありません",
|
||||
"session.review.noBranchChanges": "ブランチの変更はまだありません",
|
||||
"session.files.selectToOpen": "開くファイルを選択",
|
||||
"session.files.all": "すべてのファイル",
|
||||
"session.files.empty": "ファイルなし",
|
||||
|
|
@ -473,9 +579,10 @@ export const dict = {
|
|||
"session.messages.loading": "メッセージを読み込み中...",
|
||||
"session.messages.jumpToLatest": "最新へジャンプ",
|
||||
"session.context.addToContext": "{{selection}}をコンテキストに追加",
|
||||
"session.todo.title": "ToDo",
|
||||
"session.todo.collapse": "折りたたむ",
|
||||
"session.todo.expand": "展開",
|
||||
"session.question.minimize": "質問を最小化",
|
||||
"session.question.restore": "質問を復元",
|
||||
"session.question.pending.one": "{{count}}件の保留中の質問",
|
||||
"session.question.pending.other": "{{count}}件の保留中の質問",
|
||||
"session.followupDock.summary.one": "{{count}} 件のメッセージが待機中",
|
||||
"session.followupDock.summary.other": "{{count}} 件のメッセージが待機中",
|
||||
"session.followupDock.sendNow": "今すぐ送信",
|
||||
|
|
@ -488,9 +595,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "ロールバックされたメッセージを展開",
|
||||
"session.revertDock.restore": "メッセージを復元",
|
||||
"session.new.title": "何でも作る",
|
||||
"session.new.project.new": "新しいプロジェクト",
|
||||
"session.new.project.search": "プロジェクトを検索",
|
||||
"session.new.project.add": "プロジェクトを追加",
|
||||
"session.new.worktree.main": "メインブランチ",
|
||||
"session.new.worktree.mainWithBranch": "メインブランチ ({{branch}})",
|
||||
"session.new.worktree.create": "新しいワークツリーを作成",
|
||||
"session.new.workspace.runIn": "セッションの実行先",
|
||||
"session.new.workspace.triggerLocal": "ローカル",
|
||||
"session.new.workspace.local": "ローカルリポジトリ",
|
||||
"session.new.workspace.existing": "ワークスペース…",
|
||||
"session.new.lastModified": "最終更新",
|
||||
"session.header.search.placeholder": "{{project}}を検索",
|
||||
"session.header.searchFiles": "ファイルを検索",
|
||||
|
|
@ -554,6 +668,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "最近のセッション",
|
||||
"sidebar.project.viewAllSessions": "すべてのセッションを表示",
|
||||
"sidebar.project.clearNotifications": "通知をクリア",
|
||||
"sidebar.empty.title": "開いているプロジェクトはありません",
|
||||
"sidebar.empty.description": "プロジェクトを開いて始めましょう",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "デスクトップ",
|
||||
"settings.section.server": "サーバー",
|
||||
|
|
@ -563,6 +679,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.title": "WSL連携",
|
||||
"settings.desktop.wsl.description": "WindowsのWSL環境でOpenCodeサーバーを実行します。",
|
||||
"settings.general.section.appearance": "外観",
|
||||
"settings.general.section.advanced": "詳細設定",
|
||||
"settings.general.section.notifications": "システム通知",
|
||||
"settings.general.section.updates": "アップデート",
|
||||
"settings.general.section.sounds": "効果音",
|
||||
|
|
@ -570,6 +687,11 @@ export const dict = {
|
|||
"settings.general.section.display": "ディスプレイ",
|
||||
"settings.general.row.language.title": "言語",
|
||||
"settings.general.row.language.description": "OpenCodeの表示言語を変更します",
|
||||
"settings.general.row.shell.title": "ターミナルシェル",
|
||||
"settings.general.row.shell.description":
|
||||
"ターミナルで使用するシェルを選択します。互換性のあるシェルはエージェントのツール呼び出しにも使用されます。",
|
||||
"settings.general.row.shell.autoDefault": "自動 (デフォルト)",
|
||||
"settings.general.row.shell.terminalOnly": "ターミナルのみ",
|
||||
"settings.general.row.appearance.title": "外観",
|
||||
"settings.general.row.appearance.description": "デバイスでのOpenCodeの表示をカスタマイズします",
|
||||
"settings.general.row.colorScheme.title": "配色",
|
||||
|
|
@ -578,8 +700,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。",
|
||||
"settings.general.row.font.title": "コードフォント",
|
||||
"settings.general.row.font.description": "コードブロックで使用するフォントをカスタマイズします",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "ターミナルのフォント",
|
||||
"settings.general.row.terminalFont.description": "ターミナルで使用するフォントをカスタマイズ",
|
||||
"settings.general.row.uiFont.title": "UIフォント",
|
||||
"settings.general.row.uiFont.description": "インターフェース全体で使用するフォントをカスタマイズします",
|
||||
"settings.general.row.followup.title": "フォローアップの動作",
|
||||
|
|
@ -587,6 +709,21 @@ export const dict = {
|
|||
"フォローアッププロンプトを即座に実行するか、キューで待機させるかを選択します",
|
||||
"settings.general.row.followup.option.queue": "キューに追加",
|
||||
"settings.general.row.followup.option.steer": "即座に実行 (Steer)",
|
||||
"settings.general.row.showFileTree.title": "ファイルツリー",
|
||||
"settings.general.row.showFileTree.description": "セッションにファイルツリーパネルを表示します",
|
||||
"settings.general.row.showNavigation.title": "ナビゲーションコントロール",
|
||||
"settings.general.row.showNavigation.description": "デスクトップのタイトルバーに戻るボタンと進むボタンを表示します",
|
||||
"settings.general.row.showSearch.title": "コマンドパレット",
|
||||
"settings.general.row.showSearch.description": "タイトルバーに検索とコマンドパレットのボタンを表示します",
|
||||
"settings.general.row.showTerminal.title": "ターミナル",
|
||||
"settings.general.row.showTerminal.description": "デスクトップのタイトルバーにターミナルボタンを表示します",
|
||||
"settings.general.row.showStatus.title": "サーバーステータス",
|
||||
"settings.general.row.showStatus.description": "タイトルバーにサーバーステータスボタンを表示します",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "下部ナビゲーション",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"モバイルではタイトルバーとセッションタブを画面下部に配置します",
|
||||
"settings.general.row.showCustomAgents.title": "カスタムエージェント",
|
||||
"settings.general.row.showCustomAgents.description": "コンポーザーにエージェント選択を表示します",
|
||||
"settings.general.row.reasoningSummaries.title": "推論の要約を表示",
|
||||
"settings.general.row.reasoningSummaries.description": "タイムラインにモデルの推論の要約を表示します",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell ツールパーツを展開",
|
||||
|
|
@ -595,6 +732,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "edit ツールパーツを展開",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"タイムラインで edit、write、patch ツールパーツをデフォルトで展開して表示します",
|
||||
"settings.general.row.newLayoutDesigns.title": "新しいレイアウトとデザイン",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"刷新されたレイアウト、ホーム、コンポーザー、セッションUIを有効にします",
|
||||
"settings.general.row.pinchZoom.title": "ピンチでズーム",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"トラックパッドのピンチ操作とCtrl+スクロール操作によるズームを許可します",
|
||||
"settings.general.row.wayland.title": "ネイティブWaylandを使用",
|
||||
"settings.general.row.wayland.description": "WaylandでのX11フォールバックを無効にします。再起動が必要です。",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -607,6 +750,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "手動でアップデートを確認し、利用可能ならインストールします",
|
||||
"settings.updates.action.checkNow": "今すぐ確認",
|
||||
"settings.updates.action.checking": "確認中...",
|
||||
"settings.updates.action.downloading": "ダウンロード中...",
|
||||
"settings.updates.action.installing": "インストール中...",
|
||||
"settings.updates.toast.latest.title": "最新です",
|
||||
"settings.updates.toast.latest.description": "OpenCode は最新バージョンです。",
|
||||
"sound.option.none": "なし",
|
||||
|
|
@ -726,8 +871,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "名前によるスキルの読み込み",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "言語サーバークエリの実行",
|
||||
"settings.permissions.tool.todowrite.title": "Todo書き込み",
|
||||
"settings.permissions.tool.todowrite.description": "Todoリストの更新",
|
||||
"settings.permissions.tool.webfetch.title": "Web取得",
|
||||
"settings.permissions.tool.webfetch.description": "URLからコンテンツを取得",
|
||||
"settings.permissions.tool.websearch.title": "Web検索",
|
||||
|
|
@ -790,7 +933,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "このプロジェクトの変更を追跡、レビュー、元に戻す",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git リポジトリを作成中...",
|
||||
"session.review.noVcs.createGit.action": "Git リポジトリを作成",
|
||||
"session.todo.progress": "{{done}} 個中 {{total}} 個の Todo が完了",
|
||||
"session.question.progress": "{{total}} 問中 {{current}} 問",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "エクスプローラー",
|
||||
|
|
|
|||
|
|
@ -471,9 +471,6 @@ export const dict = {
|
|||
"session.messages.loading": "메시지 로드 중...",
|
||||
"session.messages.jumpToLatest": "최신으로 이동",
|
||||
"session.context.addToContext": "컨텍스트에 {{selection}} 추가",
|
||||
"session.todo.title": "할 일",
|
||||
"session.todo.collapse": "접기",
|
||||
"session.todo.expand": "펼치기",
|
||||
"session.followupDock.summary.one": "{{count}}개의 대기 중인 메시지",
|
||||
"session.followupDock.summary.other": "{{count}}개의 대기 중인 메시지",
|
||||
"session.followupDock.sendNow": "지금 전송",
|
||||
|
|
@ -575,8 +572,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "OpenCode 테마 사용자 지정",
|
||||
"settings.general.row.font.title": "코드 글꼴",
|
||||
"settings.general.row.font.description": "코드 블록에 사용되는 글꼴을 사용자 지정",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "터미널 글꼴",
|
||||
"settings.general.row.terminalFont.description": "터미널에서 사용할 글꼴을 설정합니다",
|
||||
"settings.general.row.uiFont.title": "UI 글꼴",
|
||||
"settings.general.row.uiFont.description": "인터페이스 전반에 사용되는 글꼴을 사용자 지정",
|
||||
"settings.general.row.followup.title": "후속 조치 동작",
|
||||
|
|
@ -721,8 +718,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "이름으로 기술 로드",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "언어 서버 쿼리 실행",
|
||||
"settings.permissions.tool.todowrite.title": "할 일 쓰기",
|
||||
"settings.permissions.tool.todowrite.description": "할 일 목록 업데이트",
|
||||
"settings.permissions.tool.webfetch.title": "웹 가져오기",
|
||||
"settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기",
|
||||
"settings.permissions.tool.websearch.title": "웹 검색",
|
||||
|
|
@ -785,7 +780,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "이 프로젝트의 변경 사항을 추적, 검토 및 실행 취소",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git 저장소 생성 중...",
|
||||
"session.review.noVcs.createGit.action": "Git 저장소 생성",
|
||||
"session.todo.progress": "{{total}}개의 할 일 중 {{done}}개 완료",
|
||||
"session.question.progress": "{{total}}개의 질문 중 {{current}}개",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "파일 탐색기",
|
||||
|
|
@ -848,4 +842,155 @@ export const dict = {
|
|||
"error.childStore.persistedProjectIconCreateFailed": "영구 프로젝트 아이콘 생성 실패",
|
||||
"error.childStore.storeCreateFailed": "저장소 생성 실패",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket이 비정상적으로 닫힘: {{code}}",
|
||||
|
||||
"command.project.previous": "이전 프로젝트",
|
||||
"command.project.next": "다음 프로젝트",
|
||||
"command.project.index": "{{index}}번 프로젝트로 전환",
|
||||
"command.tab.reopenClosed": "닫은 탭 다시 열기",
|
||||
"model.tooltip.context.label": "컨텍스트",
|
||||
"model.tooltip.inputs": "입력",
|
||||
"model.tooltip.model": "모델",
|
||||
"model.tooltip.provider": "공급자",
|
||||
"model.tooltip.reasoning": "추론",
|
||||
"common.clear": "지우기",
|
||||
"session.child.promptDisabled": "하위 에이전트 세션에는 프롬프트를 입력할 수 없습니다.",
|
||||
"session.child.backToParent": "메인 세션으로 돌아가기.",
|
||||
|
||||
"wsl.server.add": "WSL 서버 추가",
|
||||
"wsl.server.addShort": "WSL 추가",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL 서버",
|
||||
"wsl.server.retryStart": "시작 재시도",
|
||||
"wsl.server.updating": "업데이트 중...",
|
||||
"wsl.onboarding.step.distro": "배포판 선택",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL 확인 중...",
|
||||
"wsl.onboarding.restartRequired": "WSL 설치를 완료하려면 Windows를 다시 시작해야 합니다.",
|
||||
"wsl.onboarding.ready": "WSL이 준비되었습니다.",
|
||||
"wsl.onboarding.required": "계속하려면 WSL이 필요합니다.",
|
||||
"wsl.onboarding.checkingDistros": "배포판 확인 중...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} 배포판 설치 중...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} 배포판 확인 중...",
|
||||
"wsl.onboarding.listingDistros": "배포판 목록 확인 중...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} 배포판이 준비되었습니다.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} 배포판이 아직 설치되지 않았습니다.",
|
||||
"wsl.onboarding.openDistroOnce": "설정을 완료하려면 {{distro}} 배포판을 한 번 여세요.",
|
||||
"wsl.onboarding.finishingDistro": "{{distro}} 배포판 설정을 마무리하는 중입니다.",
|
||||
"wsl.onboarding.pickDistro": "배포판을 선택하거나 아래에서 설치하세요.",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode 확인 중...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "{{distro}}에서 OpenCode 확인 중...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode 업데이트 중...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "{{distro}}에서 OpenCode 업데이트 중...",
|
||||
"wsl.onboarding.updateOpencodeIn": "{{distro}}에서 OpenCode를 업데이트하세요.",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode 업데이트",
|
||||
"wsl.onboarding.opencodeReadyIn": "{{distro}}에서 OpenCode를 사용할 준비가 되었습니다.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode가 준비되었습니다.",
|
||||
"wsl.onboarding.installOpencodeIn": "{{distro}}에 OpenCode를 설치하세요.",
|
||||
"wsl.onboarding.installOpencode": "OpenCode 설치",
|
||||
"wsl.onboarding.chooseDistroFirst": "먼저 배포판을 선택하세요.",
|
||||
"wsl.onboarding.loadFailed": "WSL 상태를 불러오지 못했습니다.",
|
||||
"wsl.onboarding.loading": "로딩 중...",
|
||||
"wsl.onboarding.installedDistros": "설치된 배포판",
|
||||
"wsl.onboarding.checkAgain": "다시 확인",
|
||||
"wsl.onboarding.distroStatus.ready": "준비됨",
|
||||
"wsl.onboarding.distroStatus.checking": "확인 중...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode가 설치되지 않음",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash, curl 없음",
|
||||
"wsl.onboarding.distroStatus.unsupported": "지원되지 않음 · WSL 2 사용",
|
||||
"wsl.onboarding.needAnotherDistro": "다른 배포판이 필요하신가요?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "WSL 카탈로그에서 Linux 배포판을 설치하세요",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL이 설치되지 않음",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"OpenCode에서 WSL 서버를 추가하려면 WSL (Windows Subsystem for Linux)이 필요합니다",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL을 사용할 수 없음",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode에서 이 컴퓨터의 WSL을 확인할 수 없습니다.",
|
||||
"wsl.onboarding.installWsl": "WSL 설치",
|
||||
"wsl.onboarding.windowsRestartRequired": "WSL 설치를 완료하려면 Windows를 다시 시작한 다음 OpenCode를 다시 여세요.",
|
||||
"wsl.onboarding.next": "다음",
|
||||
"wsl.onboarding.refresh": "새로 고침",
|
||||
"wsl.onboarding.allDistrosAdded": "설치된 모든 배포판이 이미 추가되었습니다.",
|
||||
"wsl.onboarding.noDistros": "아직 감지된 배포판이 없습니다.",
|
||||
"wsl.onboarding.install": "설치",
|
||||
"wsl.onboarding.installing": "설치 중...",
|
||||
"wsl.onboarding.installDistro": "배포판 설치",
|
||||
"wsl.onboarding.searchDistros": "배포판 검색",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2가 필요합니다.",
|
||||
"wsl.onboarding.toolsRequired": "이 배포판에는 bash와 curl이 필요합니다.",
|
||||
"wsl.onboarding.openTerminal": "터미널 열기",
|
||||
"wsl.onboarding.path": "경로: {{path}}",
|
||||
"wsl.onboarding.notFound": "찾을 수 없음",
|
||||
"wsl.onboarding.version": "버전: {{version}}",
|
||||
"wsl.onboarding.unknown": "알 수 없음",
|
||||
"wsl.onboarding.desktopVersion": "데스크톱 {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "설치된 버전이 데스크톱 앱 버전과 일치하지 않습니다.",
|
||||
"wsl.onboarding.adding": "추가 중...",
|
||||
|
||||
"language.uk": "Українська",
|
||||
"error.page.description.localServerStartup": "로컬 서버를 시작하는 동안 오류가 발생했습니다.",
|
||||
"error.page.action.exportLogs": "로그 내보내기",
|
||||
|
||||
"home.title": "홈",
|
||||
"home.projects": "프로젝트",
|
||||
"home.project.add": "프로젝트 추가",
|
||||
"home.recentlyClosed": "최근에 닫은 항목",
|
||||
"home.server.collapse": "서버 프로젝트 접기",
|
||||
"home.server.expand": "서버 프로젝트 펼치기",
|
||||
"home.sessions.search.placeholder": "세션 검색",
|
||||
"home.sessions.search.placeholder.scoped": "{{scope}}에서 세션 검색",
|
||||
"home.sessions.search.sessions": "세션",
|
||||
"home.sessions.search.noResults": "{{query}}에 해당하는 세션을 찾을 수 없습니다",
|
||||
"home.sessions.empty": "아직 아무것도 없습니다",
|
||||
"home.sessions.empty.description": "세션을 생성하여 시작하세요",
|
||||
"home.sessions.group.today": "오늘",
|
||||
"home.sessions.group.yesterday": "어제",
|
||||
"home.sessions.group.older": "이전",
|
||||
|
||||
"session.tab.unknown": "알 수 없는 세션",
|
||||
"session.error.notFound": "이 세션을 찾을 수 없습니다",
|
||||
"session.error.notFound.description": "이 탭은 이 서버에 더 이상 존재하지 않는 세션을 가리킵니다.",
|
||||
"session.error.notFound.closeTab": "탭 닫기",
|
||||
"session.error.serverConnection": "이 서버에 연결할 수 없습니다",
|
||||
"session.review.noUncommittedChanges": "아직 커밋되지 않은 변경 사항이 없습니다",
|
||||
"session.review.noBranchChanges": "아직 브랜치 변경 사항이 없습니다",
|
||||
"session.question.minimize": "질문 최소화",
|
||||
"session.question.restore": "질문 복원",
|
||||
"session.question.pending.one": "{{count}}개의 대기 중인 질문",
|
||||
"session.question.pending.other": "{{count}}개의 대기 중인 질문",
|
||||
"session.new.project.new": "새 프로젝트",
|
||||
"session.new.project.search": "프로젝트 검색",
|
||||
"session.new.project.add": "프로젝트 추가",
|
||||
"session.new.workspace.runIn": "세션 실행 위치",
|
||||
"session.new.workspace.triggerLocal": "로컬",
|
||||
"session.new.workspace.local": "로컬 저장소",
|
||||
"session.new.workspace.existing": "작업 공간…",
|
||||
|
||||
"sidebar.empty.title": "열린 프로젝트 없음",
|
||||
"sidebar.empty.description": "프로젝트를 열어 시작하세요",
|
||||
|
||||
"settings.general.section.advanced": "고급",
|
||||
"settings.general.row.shell.title": "터미널 셸",
|
||||
"settings.general.row.shell.description":
|
||||
"터미널에서 사용할 셸을 선택하세요. 호환되는 셸은 에이전트 도구 호출에도 사용됩니다.",
|
||||
"settings.general.row.shell.autoDefault": "자동 (기본값)",
|
||||
"settings.general.row.shell.terminalOnly": "터미널 전용",
|
||||
"settings.general.row.showFileTree.title": "파일 트리",
|
||||
"settings.general.row.showFileTree.description": "세션에 파일 트리 패널 표시",
|
||||
"settings.general.row.showNavigation.title": "탐색 컨트롤",
|
||||
"settings.general.row.showNavigation.description": "데스크톱 제목 표시줄에 뒤로 및 앞으로 버튼 표시",
|
||||
"settings.general.row.showSearch.title": "명령 팔레트",
|
||||
"settings.general.row.showSearch.description": "제목 표시줄에 검색 및 명령 팔레트 버튼 표시",
|
||||
"settings.general.row.showTerminal.title": "터미널",
|
||||
"settings.general.row.showTerminal.description": "데스크톱 제목 표시줄에 터미널 버튼 표시",
|
||||
"settings.general.row.showStatus.title": "서버 상태",
|
||||
"settings.general.row.showStatus.description": "제목 표시줄에 서버 상태 버튼 표시",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "하단 탐색",
|
||||
"settings.general.row.mobileTitlebarBottom.description": "모바일에서 제목 표시줄과 세션 탭을 화면 하단에 배치",
|
||||
"settings.general.row.showCustomAgents.title": "사용자 지정 에이전트",
|
||||
"settings.general.row.showCustomAgents.description": "입력창에 에이전트 선택기 표시",
|
||||
"settings.general.row.newLayoutDesigns.title": "새 레이아웃 및 디자인",
|
||||
"settings.general.row.newLayoutDesigns.description": "새롭게 디자인된 레이아웃, 홈, 입력창 및 세션 UI 활성화",
|
||||
"settings.general.row.pinchZoom.title": "핀치 줌",
|
||||
"settings.general.row.pinchZoom.description": "트랙패드 핀치 및 Ctrl-스크롤 제스처로 확대/축소 허용",
|
||||
"settings.updates.action.downloading": "다운로드 중...",
|
||||
"settings.updates.action.installing": "설치 중...",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const dict = {
|
|||
"command.permissions.autoaccept.enable": "Aksepter tillatelser automatisk",
|
||||
"command.permissions.autoaccept.disable": "Stopp automatisk akseptering av tillatelser",
|
||||
"command.workspace.toggle": "Veksle arbeidsområder",
|
||||
"command.workspace.toggle.description": "Enable or disable multiple workspaces in the sidebar",
|
||||
"command.workspace.toggle.description": "Aktiver eller deaktiver flere arbeidsområder i sidefeltet",
|
||||
"command.session.undo": "Angre",
|
||||
"command.session.undo.description": "Angre siste melding",
|
||||
"command.session.redo": "Gjør om",
|
||||
|
|
@ -469,7 +469,7 @@ export const dict = {
|
|||
"Rotelement ikke funnet. Glemte du å legge det til i index.html? Eller kanskje id-attributten er feilstavet?",
|
||||
|
||||
"error.serverSync.connectFailed": "Kunne ikke koble til server. Kjører det en server på `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Invalid directory in URL.",
|
||||
"directory.error.invalidUrl": "Ugyldig mappe i URL-en.",
|
||||
|
||||
"error.chain.unknown": "Ukjent feil",
|
||||
"error.chain.causedBy": "Forårsaket av:",
|
||||
|
|
@ -532,9 +532,6 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Hopp til nyeste",
|
||||
|
||||
"session.context.addToContext": "Legg til {{selection}} i kontekst",
|
||||
"session.todo.title": "Oppgaver",
|
||||
"session.todo.collapse": "Skjul",
|
||||
"session.todo.expand": "Utvid",
|
||||
"session.followupDock.summary.one": "{{count}} melding i kø",
|
||||
"session.followupDock.summary.other": "{{count}} meldinger i kø",
|
||||
"session.followupDock.sendNow": "Send nå",
|
||||
|
|
@ -649,8 +646,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.",
|
||||
"settings.general.row.font.title": "Kodefont",
|
||||
"settings.general.row.font.description": "Tilpass skrifttypen som brukes i kodeblokker",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Terminalskrift",
|
||||
"settings.general.row.terminalFont.description": "Tilpass skrifttypen som brukes i terminalen",
|
||||
"settings.general.row.uiFont.title": "UI-skrift",
|
||||
"settings.general.row.uiFont.description": "Tilpass skrifttypen som brukes i hele grensesnittet",
|
||||
"settings.general.row.followup.title": "Oppfølgingsadferd",
|
||||
|
|
@ -806,8 +803,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Last en ferdighet etter navn",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Kjør språkserverforespørsler",
|
||||
"settings.permissions.tool.todowrite.title": "Skriv gjøremål",
|
||||
"settings.permissions.tool.todowrite.description": "Oppdater gjøremålslisten",
|
||||
"settings.permissions.tool.webfetch.title": "Webhenting",
|
||||
"settings.permissions.tool.webfetch.description": "Hent innhold fra en URL",
|
||||
"settings.permissions.tool.websearch.title": "Websøk",
|
||||
|
|
@ -872,7 +867,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Spor, gjennomgå og angre endringer i dette prosjektet",
|
||||
"session.review.noVcs.createGit.actionLoading": "Oppretter Git-depot...",
|
||||
"session.review.noVcs.createGit.action": "Opprett Git-depot",
|
||||
"session.todo.progress": "{{done}} av {{total}} oppgaver fullført",
|
||||
"session.question.progress": "{{current}} av {{total}} spørsmål",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Filutforsker",
|
||||
|
|
@ -936,4 +930,163 @@ export const dict = {
|
|||
"error.childStore.persistedProjectIconCreateFailed": "Kunne ikke opprette vedvarende prosjektikon",
|
||||
"error.childStore.storeCreateFailed": "Kunne ikke opprette lager",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket lukket unormalt: {{code}}",
|
||||
|
||||
"command.project.previous": "Forrige prosjekt",
|
||||
"command.project.next": "Neste prosjekt",
|
||||
"command.project.index": "Bytt til prosjekt {{index}}",
|
||||
"command.tab.reopenClosed": "Åpne lukket fane på nytt",
|
||||
|
||||
"model.tooltip.context.label": "Kontekst",
|
||||
"model.tooltip.inputs": "Inndata",
|
||||
"model.tooltip.model": "Modell",
|
||||
"model.tooltip.provider": "Leverandør",
|
||||
"model.tooltip.reasoning": "Resonnering",
|
||||
|
||||
"common.clear": "Tøm",
|
||||
|
||||
"session.child.promptDisabled": "Underagentsesjoner kan ikke motta forespørsler.",
|
||||
"session.child.backToParent": "Tilbake til hovedsesjonen.",
|
||||
|
||||
"wsl.server.add": "Legg til WSL-server",
|
||||
"wsl.server.addShort": "Legg til WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL-server",
|
||||
"wsl.server.retryStart": "Prøv å starte på nytt",
|
||||
"wsl.server.updating": "Oppdaterer...",
|
||||
"wsl.onboarding.step.distro": "Velg distribusjon",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Sjekker WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows må startes på nytt for å fullføre installasjonen av WSL.",
|
||||
"wsl.onboarding.ready": "WSL er klart.",
|
||||
"wsl.onboarding.required": "WSL kreves for å fortsette.",
|
||||
"wsl.onboarding.checkingDistros": "Sjekker distribusjoner...",
|
||||
"wsl.onboarding.installingDistro": "Installerer {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Sjekker {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Lister distribusjoner...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} er klar.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} er ikke installert ennå.",
|
||||
"wsl.onboarding.openDistroOnce": "Åpne {{distro}} én gang for å fullføre oppsettet.",
|
||||
"wsl.onboarding.finishingDistro": "Fullfører oppsettet for {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Velg en distribusjon eller installer en nedenfor.",
|
||||
"wsl.onboarding.checkingOpencode": "Sjekker OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Sjekker OpenCode i {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Oppdaterer OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Oppdaterer OpenCode i {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Oppdater OpenCode i {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Oppdater OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode er klar i {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode er klar.",
|
||||
"wsl.onboarding.installOpencodeIn": "Installer OpenCode i {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Installer OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Velg en distribusjon først.",
|
||||
"wsl.onboarding.loadFailed": "Kunne ikke laste inn WSL-status.",
|
||||
"wsl.onboarding.loading": "Laster...",
|
||||
"wsl.onboarding.installedDistros": "Installerte distribusjoner",
|
||||
"wsl.onboarding.checkAgain": "Sjekk på nytt",
|
||||
"wsl.onboarding.distroStatus.ready": "Klar",
|
||||
"wsl.onboarding.distroStatus.checking": "Sjekker...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode er ikke installert",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Mangler bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Ikke støttet · Bruk WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Trenger du en annen distribusjon?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Installer en Linux-distribusjon fra WSL-katalogen",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL er ikke installert",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) kreves før OpenCode kan legge til en WSL-server",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL er utilgjengelig",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode kunne ikke bekrefte WSL på denne maskinen.",
|
||||
"wsl.onboarding.installWsl": "Installer WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Start Windows på nytt for å fullføre installasjonen av WSL, og åpne deretter OpenCode på nytt.",
|
||||
"wsl.onboarding.next": "Neste",
|
||||
"wsl.onboarding.refresh": "Oppdater",
|
||||
"wsl.onboarding.allDistrosAdded": "Alle installerte distribusjoner er allerede lagt til.",
|
||||
"wsl.onboarding.noDistros": "Ingen distribusjoner oppdaget ennå.",
|
||||
"wsl.onboarding.install": "Installer",
|
||||
"wsl.onboarding.installing": "Installerer...",
|
||||
"wsl.onboarding.installDistro": "Installer distribusjon",
|
||||
"wsl.onboarding.searchDistros": "Søk etter distribusjoner",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 er påkrevd.",
|
||||
"wsl.onboarding.toolsRequired": "Denne distribusjonen trenger bash og curl.",
|
||||
"wsl.onboarding.openTerminal": "Åpne terminal",
|
||||
"wsl.onboarding.path": "Bane: {{path}}",
|
||||
"wsl.onboarding.notFound": "ikke funnet",
|
||||
"wsl.onboarding.version": "Versjon: {{version}}",
|
||||
"wsl.onboarding.unknown": "ukjent",
|
||||
"wsl.onboarding.desktopVersion": "skrivebordsapp {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Den installerte versjonen samsvarer ikke med versjonen av skrivebordsappen.",
|
||||
"wsl.onboarding.adding": "Legger til...",
|
||||
|
||||
"language.uk": "Українська",
|
||||
|
||||
"error.page.description.localServerStartup": "Det oppstod en feil under oppstart av den lokale serveren.",
|
||||
"error.page.action.exportLogs": "Eksporter logger",
|
||||
|
||||
"home.title": "Hjem",
|
||||
"home.projects": "Prosjekter",
|
||||
"home.project.add": "Legg til prosjekt",
|
||||
"home.recentlyClosed": "Nylig lukket",
|
||||
"home.server.collapse": "Fold sammen serverprosjekter",
|
||||
"home.server.expand": "Utvid serverprosjekter",
|
||||
"home.sessions.search.placeholder": "Søk etter sesjoner",
|
||||
"home.sessions.search.placeholder.scoped": "Søk etter sesjoner i {{scope}}",
|
||||
"home.sessions.search.sessions": "Sesjoner",
|
||||
"home.sessions.search.noResults": "Ingen sesjoner funnet for {{query}}",
|
||||
"home.sessions.empty": "Ingenting her ennå",
|
||||
"home.sessions.empty.description": "Opprett en sesjon for å komme i gang",
|
||||
"home.sessions.group.today": "I dag",
|
||||
"home.sessions.group.yesterday": "I går",
|
||||
"home.sessions.group.older": "Eldre",
|
||||
|
||||
"session.tab.unknown": "Ukjent sesjon",
|
||||
"session.error.notFound": "Denne sesjonen finnes ikke",
|
||||
"session.error.notFound.description": "Denne fanen peker til en sesjon som ikke lenger finnes på denne serveren.",
|
||||
"session.error.notFound.closeTab": "Lukk fane",
|
||||
"session.error.serverConnection": "Kan ikke koble til denne serveren",
|
||||
"session.review.noUncommittedChanges": "Ingen ucommittede endringer ennå",
|
||||
"session.review.noBranchChanges": "Ingen grenendringer ennå",
|
||||
"session.question.minimize": "Minimer spørsmål",
|
||||
"session.question.restore": "Gjenopprett spørsmål",
|
||||
"session.question.pending.one": "{{count}} ventende spørsmål",
|
||||
"session.question.pending.other": "{{count}} ventende spørsmål",
|
||||
"session.new.project.new": "Nytt prosjekt",
|
||||
"session.new.project.search": "Søk etter prosjekter",
|
||||
"session.new.project.add": "Legg til prosjekt",
|
||||
"session.new.workspace.runIn": "Kjør sesjonen i",
|
||||
"session.new.workspace.triggerLocal": "Lokalt",
|
||||
"session.new.workspace.local": "Lokalt depot",
|
||||
"session.new.workspace.existing": "Arbeidsområde…",
|
||||
|
||||
"sidebar.empty.title": "Ingen åpne prosjekter",
|
||||
"sidebar.empty.description": "Åpne et prosjekt for å komme i gang",
|
||||
|
||||
"settings.general.section.advanced": "Avansert",
|
||||
"settings.general.row.shell.title": "Terminalskall",
|
||||
"settings.general.row.shell.description":
|
||||
"Velg skallet som skal brukes i terminalen. Kompatible skall brukes også til agentens verktøykall.",
|
||||
"settings.general.row.shell.autoDefault": "Automatisk (standard)",
|
||||
"settings.general.row.shell.terminalOnly": "kun terminal",
|
||||
"settings.general.row.showFileTree.title": "Filtre",
|
||||
"settings.general.row.showFileTree.description": "Vis filtrepanelet i sesjoner",
|
||||
"settings.general.row.showNavigation.title": "Navigasjonskontroller",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Vis tilbake- og fremoverknappene på tittellinjen i skrivebordsappen",
|
||||
"settings.general.row.showSearch.title": "Kommandopalett",
|
||||
"settings.general.row.showSearch.description": "Vis knappen for søk og kommandopalett på tittellinjen",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Vis terminalknappen på tittellinjen i skrivebordsappen",
|
||||
"settings.general.row.showStatus.title": "Serverstatus",
|
||||
"settings.general.row.showStatus.description": "Vis serverstatusknappen på tittellinjen",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigasjon nederst",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Plasser tittellinjen og sesjonsfanene nederst på mobilskjermen",
|
||||
"settings.general.row.showCustomAgents.title": "Egendefinerte agenter",
|
||||
"settings.general.row.showCustomAgents.description": "Vis agentvelgeren i skrivefeltet",
|
||||
"settings.general.row.newLayoutDesigns.title": "Ny layout og utforming",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Aktiver ny layout og utforming for startsiden, skrivefeltet og sesjonsgrensesnittet",
|
||||
"settings.general.row.pinchZoom.title": "Knip for å zoome",
|
||||
"settings.general.row.pinchZoom.description": "Tillat knipebevegelser på styreflaten og Ctrl-rulling for å zoome",
|
||||
"settings.updates.action.downloading": "Laster ned...",
|
||||
"settings.updates.action.installing": "Installerer...",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
|
|
|||
|
|
@ -1,33 +1,118 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { dict as en } from "./en"
|
||||
import { dict as ar } from "./ar"
|
||||
import { dict as br } from "./br"
|
||||
import { dict as bs } from "./bs"
|
||||
import { dict as da } from "./da"
|
||||
import { dict as de } from "./de"
|
||||
import { dict as es } from "./es"
|
||||
import { dict as fr } from "./fr"
|
||||
import { dict as ja } from "./ja"
|
||||
import { dict as ko } from "./ko"
|
||||
import { dict as no } from "./no"
|
||||
import { dict as pl } from "./pl"
|
||||
import { dict as ru } from "./ru"
|
||||
import { dict as uk } from "./uk"
|
||||
import { dict as th } from "./th"
|
||||
import { dict as zh } from "./zh"
|
||||
import { dict as zht } from "./zht"
|
||||
import { dict as tr } from "./tr"
|
||||
|
||||
const locales = [ar, br, bs, da, de, es, fr, ja, ko, no, pl, ru, uk, th, tr, zh, zht]
|
||||
const keys = ["command.session.previous.unseen", "command.session.next.unseen"] as const
|
||||
const appLocales = [
|
||||
"ar",
|
||||
"br",
|
||||
"bs",
|
||||
"da",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"ja",
|
||||
"ko",
|
||||
"no",
|
||||
"pl",
|
||||
"ru",
|
||||
"uk",
|
||||
"th",
|
||||
"tr",
|
||||
"zh",
|
||||
"zht",
|
||||
] as const
|
||||
const desktopLocales = appLocales.filter((locale) => locale !== "th" && locale !== "tr")
|
||||
|
||||
const domains = [
|
||||
{
|
||||
name: "app",
|
||||
source: "./en.ts",
|
||||
target: (locale: string) => `./${locale}.ts`,
|
||||
locales: appLocales,
|
||||
},
|
||||
{
|
||||
name: "ui",
|
||||
source: "../../../ui/src/i18n/en.ts",
|
||||
target: (locale: string) => `../../../ui/src/i18n/${locale}.ts`,
|
||||
locales: appLocales,
|
||||
},
|
||||
{
|
||||
name: "desktop",
|
||||
source: "../../../desktop/src/renderer/i18n/en.ts",
|
||||
target: (locale: string) => `../../../desktop/src/renderer/i18n/${locale}.ts`,
|
||||
locales: desktopLocales,
|
||||
},
|
||||
] as const
|
||||
|
||||
describe("i18n parity", () => {
|
||||
test("non-English locales translate targeted unseen session keys", () => {
|
||||
for (const locale of locales) {
|
||||
for (const key of keys) {
|
||||
expect(locale[key]).toBeDefined()
|
||||
expect(locale[key]).not.toBe(en[key])
|
||||
test("non-English locales have every English key", async () => {
|
||||
for (const domain of domains) {
|
||||
const source = await dictionary(domain.source)
|
||||
for (const locale of domain.locales) {
|
||||
const target = await dictionary(domain.target(locale))
|
||||
const missing = Object.keys(source).filter((key) => !Object.hasOwn(target, key))
|
||||
const extra = Object.keys(target).filter((key) => !Object.hasOwn(source, key))
|
||||
expect({ domain: domain.name, locale, missing, extra }).toEqual({
|
||||
domain: domain.name,
|
||||
locale,
|
||||
missing: [],
|
||||
extra: [],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("non-English locales preserve English placeholders", async () => {
|
||||
for (const domain of domains) {
|
||||
const source = await dictionary(domain.source)
|
||||
for (const locale of domain.locales) {
|
||||
const target = await dictionary(domain.target(locale))
|
||||
const mismatched = Object.keys(source).filter(
|
||||
(key) => Object.hasOwn(target, key) && placeholders(source[key]).join() !== placeholders(target[key]).join(),
|
||||
)
|
||||
expect({ domain: domain.name, locale, mismatched }).toEqual({ domain: domain.name, locale, mismatched: [] })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("non-English locales translate targeted unseen session keys", async () => {
|
||||
const source = await dictionary("./en.ts")
|
||||
for (const locale of appLocales) {
|
||||
const target = await dictionary(`./${locale}.ts`)
|
||||
for (const key of ["command.session.previous.unseen", "command.session.next.unseen"]) {
|
||||
expect(target[key]).toBeDefined()
|
||||
expect(target[key]).not.toBe(source[key])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("changed-file summary keys preserve rendered English copy and localize complete phrases", async () => {
|
||||
const source = await dictionary("../../../ui/src/i18n/en.ts")
|
||||
expect(source["ui.sessionTurn.diffs.changed.one"].replace("{{count}}", "1")).toBe("1 Changed file")
|
||||
expect(source["ui.sessionTurn.diffs.changed.other"].replace("{{count}}", "2")).toBe("2 Changed files")
|
||||
expect(source["ui.sessionTurn.diffs.changed"]).toBeUndefined()
|
||||
|
||||
for (const locale of appLocales) {
|
||||
const target = await dictionary(`../../../ui/src/i18n/${locale}.ts`)
|
||||
for (const key of ["ui.sessionTurn.diffs.changed.one", "ui.sessionTurn.diffs.changed.other"]) {
|
||||
expect(target[key].trim()).not.toBe("")
|
||||
expect(placeholders(target[key])).toEqual(["count"])
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function dictionary(file: string) {
|
||||
const module: unknown = await import(file)
|
||||
if (typeof module !== "object" || module === null || !("dict" in module) || !isDictionary(module.dict)) {
|
||||
throw new Error(`Invalid translation dictionary: ${file}`)
|
||||
}
|
||||
return module.dict
|
||||
}
|
||||
|
||||
function isDictionary(value: unknown): value is Record<string, string> {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) return false
|
||||
return Object.values(value).every((item) => typeof item === "string")
|
||||
}
|
||||
|
||||
function placeholders(value: string) {
|
||||
return Array.from(value.matchAll(/{{\s*([^}]+?)\s*}}/g), (match) => match[1]).sort()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const dict = {
|
|||
"theme.scheme.dark": "Ciemny",
|
||||
"command.sidebar.toggle": "Przełącz pasek boczny",
|
||||
"command.project.open": "Otwórz projekt",
|
||||
"command.project.previous": "Poprzedni projekt",
|
||||
"command.project.next": "Następny projekt",
|
||||
"command.project.index": "Przełącz na projekt {{index}}",
|
||||
"command.provider.connect": "Połącz dostawcę",
|
||||
"command.server.switch": "Przełącz serwer",
|
||||
"command.settings.open": "Otwórz ustawienia",
|
||||
|
|
@ -39,6 +42,7 @@ export const dict = {
|
|||
"command.session.new": "Nowa sesja",
|
||||
"command.file.open": "Otwórz plik",
|
||||
"command.tab.close": "Zamknij kartę",
|
||||
"command.tab.reopenClosed": "Otwórz ponownie zamkniętą kartę",
|
||||
"command.context.addSelection": "Dodaj zaznaczenie do kontekstu",
|
||||
"command.context.addSelection.description": "Dodaj zaznaczone linie z bieżącego pliku",
|
||||
"command.input.focus": "Fokus na pole wejściowe",
|
||||
|
|
@ -194,6 +198,11 @@ export const dict = {
|
|||
"model.input.video": "wideo",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Obsługuje: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontekst",
|
||||
"model.tooltip.inputs": "Dane wejściowe",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Dostawca",
|
||||
"model.tooltip.reasoning": "Wnioskowanie",
|
||||
"model.tooltip.reasoning.allowed": "Obsługuje wnioskowanie",
|
||||
"model.tooltip.reasoning.none": "Brak wnioskowania",
|
||||
"model.tooltip.context": "Limit kontekstu {{limit}}",
|
||||
|
|
@ -203,6 +212,7 @@ export const dict = {
|
|||
"common.loading": "Ładowanie",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Anuluj",
|
||||
"common.clear": "Wyczyść",
|
||||
"common.connect": "Połącz",
|
||||
"common.disconnect": "Rozłącz",
|
||||
"common.continue": "Prześlij",
|
||||
|
|
@ -219,6 +229,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Terminal",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc aby wyjść",
|
||||
"session.child.promptDisabled": "Nie można wysyłać promptów w sesjach podagentów.",
|
||||
"session.child.backToParent": "Wróć do głównej sesji.",
|
||||
"prompt.example.1": "Napraw TODO w bazie kodu",
|
||||
"prompt.example.2": "Jaki jest stos technologiczny tego projektu?",
|
||||
"prompt.example.3": "Napraw zepsute testy",
|
||||
|
|
@ -315,6 +327,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Usuń",
|
||||
"dialog.server.current": "Obecny serwer",
|
||||
"dialog.server.status.default": "Domyślny",
|
||||
"wsl.server.add": "Dodaj serwer WSL",
|
||||
"wsl.server.addShort": "Dodaj WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Serwer WSL",
|
||||
"wsl.server.retryStart": "Ponów uruchamianie",
|
||||
"wsl.server.updating": "Aktualizowanie...",
|
||||
"wsl.onboarding.step.distro": "Wybierz dystrybucję",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Sprawdzanie WSL...",
|
||||
"wsl.onboarding.restartRequired": "Aby dokończyć instalację WSL, uruchom ponownie system Windows.",
|
||||
"wsl.onboarding.ready": "WSL jest gotowy.",
|
||||
"wsl.onboarding.required": "Aby kontynuować, wymagany jest WSL.",
|
||||
"wsl.onboarding.checkingDistros": "Sprawdzanie dystrybucji...",
|
||||
"wsl.onboarding.installingDistro": "Instalowanie {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Sprawdzanie {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Pobieranie listy dystrybucji...",
|
||||
"wsl.onboarding.distroReady": "Dystrybucja {{distro}} jest gotowa.",
|
||||
"wsl.onboarding.distroNotInstalled": "Dystrybucja {{distro}} nie jest jeszcze zainstalowana.",
|
||||
"wsl.onboarding.openDistroOnce": "Uruchom raz {{distro}}, aby dokończyć konfigurację.",
|
||||
"wsl.onboarding.finishingDistro": "Kończenie konfiguracji {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Wybierz dystrybucję lub zainstaluj jedną z poniższych.",
|
||||
"wsl.onboarding.checkingOpencode": "Sprawdzanie OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Sprawdzanie OpenCode w dystrybucji {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Aktualizowanie OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Aktualizowanie OpenCode w dystrybucji {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Zaktualizuj OpenCode w dystrybucji {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Zaktualizuj OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode jest gotowy w dystrybucji {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode jest gotowy.",
|
||||
"wsl.onboarding.installOpencodeIn": "Zainstaluj OpenCode w dystrybucji {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Zainstaluj OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Najpierw wybierz dystrybucję.",
|
||||
"wsl.onboarding.loadFailed": "Nie udało się załadować stanu WSL.",
|
||||
"wsl.onboarding.loading": "Ładowanie...",
|
||||
"wsl.onboarding.installedDistros": "Zainstalowane dystrybucje",
|
||||
"wsl.onboarding.checkAgain": "Sprawdź ponownie",
|
||||
"wsl.onboarding.distroStatus.ready": "Gotowa",
|
||||
"wsl.onboarding.distroStatus.checking": "Sprawdzanie...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode nie jest zainstalowany",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Brak bash i curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nieobsługiwana · Użyj WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Potrzebujesz innej dystrybucji?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Zainstaluj dystrybucję systemu Linux z katalogu WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL nie jest zainstalowany",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Podsystem Windows dla systemu Linux) jest wymagany, zanim OpenCode będzie mógł dodać serwer WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL jest niedostępny",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode nie mógł zweryfikować WSL na tym komputerze.",
|
||||
"wsl.onboarding.installWsl": "Zainstaluj WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Uruchom ponownie system Windows, aby dokończyć instalację WSL, a następnie ponownie otwórz OpenCode.",
|
||||
"wsl.onboarding.next": "Dalej",
|
||||
"wsl.onboarding.refresh": "Odśwież",
|
||||
"wsl.onboarding.allDistrosAdded": "Wszystkie zainstalowane dystrybucje zostały już dodane.",
|
||||
"wsl.onboarding.noDistros": "Nie wykryto jeszcze żadnych dystrybucji.",
|
||||
"wsl.onboarding.install": "Zainstaluj",
|
||||
"wsl.onboarding.installing": "Instalowanie...",
|
||||
"wsl.onboarding.installDistro": "Zainstaluj dystrybucję",
|
||||
"wsl.onboarding.searchDistros": "Szukaj dystrybucji",
|
||||
"wsl.onboarding.wsl2Required": "Wymagany jest WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Ta dystrybucja wymaga bash i curl.",
|
||||
"wsl.onboarding.openTerminal": "Otwórz terminal",
|
||||
"wsl.onboarding.path": "Ścieżka: {{path}}",
|
||||
"wsl.onboarding.notFound": "nie znaleziono",
|
||||
"wsl.onboarding.version": "Wersja: {{version}}",
|
||||
"wsl.onboarding.unknown": "nieznana",
|
||||
"wsl.onboarding.desktopVersion": "aplikacja komputerowa {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Zainstalowana wersja nie jest zgodna z wersją aplikacji komputerowej.",
|
||||
"wsl.onboarding.adding": "Dodawanie...",
|
||||
"dialog.project.edit.title": "Edytuj projekt",
|
||||
"dialog.project.edit.name": "Nazwa",
|
||||
"dialog.project.edit.icon": "Ikona",
|
||||
|
|
@ -324,7 +405,8 @@ export const dict = {
|
|||
"dialog.project.edit.color": "Kolor",
|
||||
"dialog.project.edit.color.select": "Wybierz kolor {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "Skrypt uruchamiania przestrzeni roboczej",
|
||||
"dialog.project.edit.worktree.startup.description": "Runs after creating a new workspace (worktree).",
|
||||
"dialog.project.edit.worktree.startup.description":
|
||||
"Uruchamiany po utworzeniu nowej przestrzeni roboczej (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "np. bun install",
|
||||
"context.breakdown.title": "Podział kontekstu",
|
||||
"context.breakdown.note": 'Przybliżony podział tokenów wejściowych. "Inne" obejmuje definicje narzędzi i narzut.',
|
||||
|
|
@ -371,6 +453,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Język",
|
||||
|
|
@ -407,10 +490,12 @@ export const dict = {
|
|||
"toast.update.action.notYet": "Jeszcze nie",
|
||||
"error.page.title": "Coś poszło nie tak",
|
||||
"error.page.description": "Wystąpił błąd podczas ładowania aplikacji.",
|
||||
"error.page.description.localServerStartup": "Wystąpił błąd podczas uruchamiania serwera lokalnego.",
|
||||
"error.page.details.label": "Szczegóły błędu",
|
||||
"error.page.action.restart": "Restartuj",
|
||||
"error.page.action.report": "Zgłoś błąd",
|
||||
"error.page.action.reported": "Błąd zgłoszony",
|
||||
"error.page.action.exportLogs": "Eksportuj dzienniki",
|
||||
"error.page.action.checking": "Sprawdzanie...",
|
||||
"error.page.action.checkUpdates": "Sprawdź aktualizacje",
|
||||
"error.page.action.updateTo": "Zaktualizuj do {{version}}",
|
||||
|
|
@ -430,7 +515,8 @@ export const dict = {
|
|||
"error.chain.didYouMean": "Czy miałeś na myśli: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model nie znaleziony: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Sprawdź swoją konfigurację (opencode.json) nazwy dostawców/modeli",
|
||||
"error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, OpenCode does not support MCP authentication yet.',
|
||||
"error.chain.mcpFailed":
|
||||
"Serwer MCP „{{name}}” nie uruchomił się. Uwaga: OpenCode nie obsługuje jeszcze uwierzytelniania MCP.",
|
||||
"error.chain.providerAuthFailed": "Uwierzytelnianie dostawcy nie powiodło się ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'Nie udało się zainicjować dostawcy "{{provider}}". Sprawdź poświadczenia i konfigurację.',
|
||||
|
|
@ -452,10 +538,30 @@ export const dict = {
|
|||
"home.recentProjects": "Ostatnie projekty",
|
||||
"home.empty.title": "Brak ostatnich projektów",
|
||||
"home.empty.description": "Zacznij od otwarcia lokalnego projektu",
|
||||
"home.title": "Strona główna",
|
||||
"home.projects": "Projekty",
|
||||
"home.project.add": "Dodaj projekt",
|
||||
"home.recentlyClosed": "Ostatnio zamknięte",
|
||||
"home.server.collapse": "Zwiń projekty serwera",
|
||||
"home.server.expand": "Rozwiń projekty serwera",
|
||||
"home.sessions.search.placeholder": "Szukaj sesji",
|
||||
"home.sessions.search.placeholder.scoped": "Szukaj sesji w {{scope}}",
|
||||
"home.sessions.search.sessions": "Sesje",
|
||||
"home.sessions.search.noResults": "Nie znaleziono sesji dla {{query}}",
|
||||
"home.sessions.empty": "Jeszcze nic tu nie ma",
|
||||
"home.sessions.empty.description": "Utwórz sesję, aby rozpocząć",
|
||||
"home.sessions.group.today": "Dzisiaj",
|
||||
"home.sessions.group.yesterday": "Wczoraj",
|
||||
"home.sessions.group.older": "Starsze",
|
||||
"session.tab.session": "Sesja",
|
||||
"session.tab.review": "Przegląd",
|
||||
"session.tab.context": "Kontekst",
|
||||
"session.tab.unknown": "Nieznana sesja",
|
||||
"session.panel.reviewAndFiles": "Przegląd i pliki",
|
||||
"session.error.notFound": "Nie można znaleźć tej sesji",
|
||||
"session.error.notFound.description": "Ta karta wskazuje sesję, która nie istnieje już na tym serwerze.",
|
||||
"session.error.notFound.closeTab": "Zamknij kartę",
|
||||
"session.error.serverConnection": "Nie można połączyć się z tym serwerem",
|
||||
"session.review.filesChanged": "Zmieniono {{count}} plików",
|
||||
"session.review.change.one": "Zmiana",
|
||||
"session.review.change.other": "Zmiany",
|
||||
|
|
@ -464,6 +570,8 @@ export const dict = {
|
|||
"session.review.noVcs": "Nie wykryto systemu kontroli wersji Git, zmiany nie są wyświetlane",
|
||||
"session.review.noSnapshot": "Śledzenie migawek jest wyłączone w konfiguracji, więc zmiany w sesji są niedostępne",
|
||||
"session.review.noChanges": "Brak zmian",
|
||||
"session.review.noUncommittedChanges": "Brak jeszcze niezatwierdzonych zmian",
|
||||
"session.review.noBranchChanges": "Brak jeszcze zmian w gałęzi",
|
||||
"session.files.selectToOpen": "Wybierz plik do otwarcia",
|
||||
"session.files.all": "Wszystkie pliki",
|
||||
"session.files.empty": "Brak plików",
|
||||
|
|
@ -474,9 +582,10 @@ export const dict = {
|
|||
"session.messages.loading": "Ładowanie wiadomości...",
|
||||
"session.messages.jumpToLatest": "Przejdź do najnowszych",
|
||||
"session.context.addToContext": "Dodaj {{selection}} do kontekstu",
|
||||
"session.todo.title": "Zadania",
|
||||
"session.todo.collapse": "Zwiń",
|
||||
"session.todo.expand": "Rozwiń",
|
||||
"session.question.minimize": "Zminimalizuj pytanie",
|
||||
"session.question.restore": "Przywróć pytanie",
|
||||
"session.question.pending.one": "{{count}} oczekujące pytanie",
|
||||
"session.question.pending.other": "Pytania oczekujące: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} wiadomość w kolejce",
|
||||
"session.followupDock.summary.other": "{{count}} wiadomości w kolejce",
|
||||
"session.followupDock.sendNow": "Wyślij teraz",
|
||||
|
|
@ -489,9 +598,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "Rozwiń cofnięte wiadomości",
|
||||
"session.revertDock.restore": "Przywróć wiadomość",
|
||||
"session.new.title": "Zbuduj cokolwiek",
|
||||
"session.new.project.new": "Nowy projekt",
|
||||
"session.new.project.search": "Szukaj projektów",
|
||||
"session.new.project.add": "Dodaj projekt",
|
||||
"session.new.worktree.main": "Główna gałąź",
|
||||
"session.new.worktree.mainWithBranch": "Główna gałąź ({{branch}})",
|
||||
"session.new.worktree.create": "Utwórz nowe drzewo robocze",
|
||||
"session.new.workspace.runIn": "Uruchom sesję w",
|
||||
"session.new.workspace.triggerLocal": "Lokalnie",
|
||||
"session.new.workspace.local": "Lokalne repozytorium",
|
||||
"session.new.workspace.existing": "Przestrzeń robocza…",
|
||||
"session.new.lastModified": "Ostatnio zmodyfikowano",
|
||||
"session.header.search.placeholder": "Szukaj {{project}}",
|
||||
"session.header.searchFiles": "Szukaj plików",
|
||||
|
|
@ -555,15 +671,18 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Ostatnie sesje",
|
||||
"sidebar.project.viewAllSessions": "Zobacz wszystkie sesje",
|
||||
"sidebar.project.clearNotifications": "Wyczyść powiadomienia",
|
||||
"sidebar.empty.title": "Brak otwartych projektów",
|
||||
"sidebar.empty.description": "Otwórz projekt, aby rozpocząć",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Pulpit",
|
||||
"settings.section.server": "Serwer",
|
||||
"settings.tab.general": "Ogólne",
|
||||
"settings.tab.shortcuts": "Skróty",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integration",
|
||||
"settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.",
|
||||
"settings.desktop.wsl.title": "Integracja z WSL",
|
||||
"settings.desktop.wsl.description": "Uruchamiaj serwer OpenCode wewnątrz WSL w systemie Windows.",
|
||||
"settings.general.section.appearance": "Wygląd",
|
||||
"settings.general.section.advanced": "Zaawansowane",
|
||||
"settings.general.section.notifications": "Powiadomienia systemowe",
|
||||
"settings.general.section.updates": "Aktualizacje",
|
||||
"settings.general.section.sounds": "Efekty dźwiękowe",
|
||||
|
|
@ -571,6 +690,11 @@ export const dict = {
|
|||
"settings.general.section.display": "Ekran",
|
||||
"settings.general.row.language.title": "Język",
|
||||
"settings.general.row.language.description": "Zmień język wyświetlania dla OpenCode",
|
||||
"settings.general.row.shell.title": "Powłoka terminala",
|
||||
"settings.general.row.shell.description":
|
||||
"Wybierz powłokę używaną w terminalu. Zgodne powłoki są również używane przez wywołania narzędzi agenta.",
|
||||
"settings.general.row.shell.autoDefault": "Automatycznie (domyślna)",
|
||||
"settings.general.row.shell.terminalOnly": "tylko terminal",
|
||||
"settings.general.row.appearance.title": "Wygląd",
|
||||
"settings.general.row.appearance.description": "Dostosuj wygląd OpenCode na swoim urządzeniu",
|
||||
"settings.general.row.colorScheme.title": "Schemat kolorów",
|
||||
|
|
@ -580,14 +704,30 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Dostosuj motyw OpenCode.",
|
||||
"settings.general.row.font.title": "Czcionka kodu",
|
||||
"settings.general.row.font.description": "Dostosuj czcionkę używaną w blokach kodu",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Czcionka terminala",
|
||||
"settings.general.row.terminalFont.description": "Dostosuj czcionkę używaną w terminalu",
|
||||
"settings.general.row.uiFont.title": "Czcionka interfejsu",
|
||||
"settings.general.row.uiFont.description": "Dostosuj czcionkę używaną w całym interfejsie",
|
||||
"settings.general.row.followup.title": "Zachowanie kontynuacji",
|
||||
"settings.general.row.followup.description": "Wybierz, czy kontynuacja ma być natychmiastowa, czy czekać w kolejce",
|
||||
"settings.general.row.followup.option.queue": "Kolejka",
|
||||
"settings.general.row.followup.option.steer": "Sterowanie",
|
||||
"settings.general.row.showFileTree.title": "Drzewo plików",
|
||||
"settings.general.row.showFileTree.description": "Pokaż panel drzewa plików w sesjach",
|
||||
"settings.general.row.showNavigation.title": "Elementy nawigacji",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Pokaż przyciski Wstecz i Dalej na pasku tytułu aplikacji komputerowej",
|
||||
"settings.general.row.showSearch.title": "Paleta poleceń",
|
||||
"settings.general.row.showSearch.description": "Pokaż przycisk wyszukiwania i palety poleceń na pasku tytułu",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Pokaż przycisk terminala na pasku tytułu aplikacji komputerowej",
|
||||
"settings.general.row.showStatus.title": "Status serwera",
|
||||
"settings.general.row.showStatus.description": "Pokaż przycisk statusu serwera na pasku tytułu",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Dolna nawigacja",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Umieść pasek tytułu i karty sesji u dołu ekranu na urządzeniach mobilnych",
|
||||
"settings.general.row.showCustomAgents.title": "Agenci niestandardowi",
|
||||
"settings.general.row.showCustomAgents.description": "Pokaż wybór agenta w edytorze wiadomości",
|
||||
"settings.general.row.reasoningSummaries.title": "Pokaż podsumowania wnioskowania",
|
||||
"settings.general.row.reasoningSummaries.description": "Wyświetlaj podsumowania wnioskowania modelu na osi czasu",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Rozwijaj elementy narzędzia shell",
|
||||
|
|
@ -596,6 +736,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Rozwijaj elementy narzędzia edit",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Domyślnie pokazuj rozwinięte elementy narzędzi edit, write i patch na osi czasu",
|
||||
"settings.general.row.newLayoutDesigns.title": "Nowy układ i wygląd",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Włącz przeprojektowany układ, stronę główną, edytor wiadomości i interfejs sesji",
|
||||
"settings.general.row.pinchZoom.title": "Powiększanie gestem szczypania",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Zezwalaj na powiększanie gestem szczypania na gładziku i przewijaniem z klawiszem Ctrl",
|
||||
"settings.general.row.wayland.title": "Użyj natywnego Wayland",
|
||||
"settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -608,6 +754,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Ręcznie sprawdź aktualizacje i zainstaluj, jeśli są dostępne",
|
||||
"settings.updates.action.checkNow": "Sprawdź teraz",
|
||||
"settings.updates.action.checking": "Sprawdzanie...",
|
||||
"settings.updates.action.downloading": "Pobieranie...",
|
||||
"settings.updates.action.installing": "Instalowanie...",
|
||||
"settings.updates.toast.latest.title": "Masz najnowszą wersję",
|
||||
"settings.updates.toast.latest.description": "Korzystasz z najnowszej wersji OpenCode.",
|
||||
"sound.option.none": "Brak",
|
||||
|
|
@ -728,8 +876,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Ładowanie umiejętności według nazwy",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Uruchamianie zapytań serwera językowego",
|
||||
"settings.permissions.tool.todowrite.title": "Zapis Todo",
|
||||
"settings.permissions.tool.todowrite.description": "Aktualizacja listy zadań",
|
||||
"settings.permissions.tool.webfetch.title": "Pobieranie z sieci",
|
||||
"settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL",
|
||||
"settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci",
|
||||
|
|
@ -792,7 +938,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Śledź, przeglądaj i cofaj zmiany w tym projekcie",
|
||||
"session.review.noVcs.createGit.actionLoading": "Tworzenie repozytorium Git...",
|
||||
"session.review.noVcs.createGit.action": "Utwórz repozytorium Git",
|
||||
"session.todo.progress": "Ukończono {{done}} z {{total}} zadań",
|
||||
"session.question.progress": "{{current}} z {{total}} pytań",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Eksplorator plików",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "Переключить боковую панель",
|
||||
"command.project.open": "Открыть проект",
|
||||
"command.project.previous": "Предыдущий проект",
|
||||
"command.project.next": "Следующий проект",
|
||||
"command.project.index": "Переключиться на проект {{index}}",
|
||||
"command.provider.connect": "Подключить провайдера",
|
||||
"command.server.switch": "Переключить сервер",
|
||||
"command.settings.open": "Открыть настройки",
|
||||
|
|
@ -45,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "Новая сессия",
|
||||
"command.file.open": "Открыть файл",
|
||||
"command.tab.close": "Закрыть вкладку",
|
||||
"command.tab.reopenClosed": "Повторно открыть закрытую вкладку",
|
||||
"command.context.addSelection": "Добавить выделение в контекст",
|
||||
"command.context.addSelection.description": "Добавить выбранные строки из текущего файла",
|
||||
"command.input.focus": "Фокус на поле ввода",
|
||||
|
|
@ -208,6 +212,11 @@ export const dict = {
|
|||
"model.input.video": "видео",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Разрешено: {{inputs}}",
|
||||
"model.tooltip.context.label": "Контекст",
|
||||
"model.tooltip.inputs": "Входные данные",
|
||||
"model.tooltip.model": "Модель",
|
||||
"model.tooltip.provider": "Провайдер",
|
||||
"model.tooltip.reasoning": "Рассуждение",
|
||||
"model.tooltip.reasoning.allowed": "Разрешает рассуждение",
|
||||
"model.tooltip.reasoning.none": "Без рассуждения",
|
||||
"model.tooltip.context": "Лимит контекста {{limit}}",
|
||||
|
|
@ -218,6 +227,7 @@ export const dict = {
|
|||
"common.loading": "Загрузка",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Отмена",
|
||||
"common.clear": "Очистить",
|
||||
"common.connect": "Подключить",
|
||||
"common.disconnect": "Отключить",
|
||||
"common.continue": "Отправить",
|
||||
|
|
@ -235,6 +245,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Оболочка",
|
||||
"prompt.mode.normal": "Промпт",
|
||||
"prompt.mode.shell.exit": "esc для выхода",
|
||||
"session.child.promptDisabled": "В сессиях субагентов нельзя отправлять запросы.",
|
||||
"session.child.backToParent": "Вернуться к основной сессии.",
|
||||
|
||||
"prompt.example.1": "Исправить TODO в коде",
|
||||
"prompt.example.2": "Какой технологический стек этого проекта?",
|
||||
|
|
@ -341,6 +353,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Удалить",
|
||||
"dialog.server.current": "Текущий сервер",
|
||||
"dialog.server.status.default": "По умолч.",
|
||||
"wsl.server.add": "Добавить сервер WSL",
|
||||
"wsl.server.addShort": "Добавить WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Сервер WSL",
|
||||
"wsl.server.retryStart": "Повторить запуск",
|
||||
"wsl.server.updating": "Обновление...",
|
||||
"wsl.onboarding.step.distro": "Выбрать дистрибутив",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Проверка WSL...",
|
||||
"wsl.onboarding.restartRequired": "Чтобы завершить установку WSL, перезапустите Windows.",
|
||||
"wsl.onboarding.ready": "WSL готова к работе.",
|
||||
"wsl.onboarding.required": "Для продолжения требуется WSL.",
|
||||
"wsl.onboarding.checkingDistros": "Проверка дистрибутивов...",
|
||||
"wsl.onboarding.installingDistro": "Установка {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Проверка {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Получение списка дистрибутивов...",
|
||||
"wsl.onboarding.distroReady": "Дистрибутив {{distro}} готов к работе.",
|
||||
"wsl.onboarding.distroNotInstalled": "Дистрибутив {{distro}} пока не установлен.",
|
||||
"wsl.onboarding.openDistroOnce": "Откройте {{distro}} один раз, чтобы завершить настройку.",
|
||||
"wsl.onboarding.finishingDistro": "Завершение настройки {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Выберите дистрибутив или установите его ниже.",
|
||||
"wsl.onboarding.checkingOpencode": "Проверка OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Проверка OpenCode в {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Обновление OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Обновление OpenCode в {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Обновите OpenCode в {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Обновить OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode в {{distro}} готов к работе.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode готов к работе.",
|
||||
"wsl.onboarding.installOpencodeIn": "Установите OpenCode в {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Установить OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Сначала выберите дистрибутив.",
|
||||
"wsl.onboarding.loadFailed": "Не удалось загрузить состояние WSL.",
|
||||
"wsl.onboarding.loading": "Загрузка...",
|
||||
"wsl.onboarding.installedDistros": "Установленные дистрибутивы",
|
||||
"wsl.onboarding.checkAgain": "Проверить снова",
|
||||
"wsl.onboarding.distroStatus.ready": "Готов",
|
||||
"wsl.onboarding.distroStatus.checking": "Проверка...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode не установлен",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Отсутствуют bash и curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Не поддерживается · Используйте WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Нужен другой дистрибутив?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Установите дистрибутив Linux из каталога WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL не установлена",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"Для добавления сервера WSL в OpenCode требуется WSL (Windows Subsystem for Linux)",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL недоступна",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode не смог проверить WSL на этом компьютере.",
|
||||
"wsl.onboarding.installWsl": "Установить WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Перезапустите Windows, чтобы завершить установку WSL, затем снова откройте OpenCode.",
|
||||
"wsl.onboarding.next": "Далее",
|
||||
"wsl.onboarding.refresh": "Обновить",
|
||||
"wsl.onboarding.allDistrosAdded": "Все установленные дистрибутивы уже добавлены.",
|
||||
"wsl.onboarding.noDistros": "Дистрибутивы пока не обнаружены.",
|
||||
"wsl.onboarding.install": "Установить",
|
||||
"wsl.onboarding.installing": "Установка...",
|
||||
"wsl.onboarding.installDistro": "Установить дистрибутив",
|
||||
"wsl.onboarding.searchDistros": "Поиск дистрибутивов",
|
||||
"wsl.onboarding.wsl2Required": "Требуется WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Для этого дистрибутива нужны bash и curl.",
|
||||
"wsl.onboarding.openTerminal": "Открыть терминал",
|
||||
"wsl.onboarding.path": "Путь: {{path}}",
|
||||
"wsl.onboarding.notFound": "не найден",
|
||||
"wsl.onboarding.version": "Версия: {{version}}",
|
||||
"wsl.onboarding.unknown": "неизвестно",
|
||||
"wsl.onboarding.desktopVersion": "приложение {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Установленная версия не соответствует версии приложения.",
|
||||
"wsl.onboarding.adding": "Добавление...",
|
||||
|
||||
"dialog.project.edit.title": "Редактировать проект",
|
||||
"dialog.project.edit.name": "Название",
|
||||
|
|
@ -405,6 +486,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -452,10 +534,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Что-то пошло не так",
|
||||
"error.page.description": "Произошла ошибка при загрузке приложения.",
|
||||
"error.page.description.localServerStartup": "Произошла ошибка при запуске локального сервера.",
|
||||
"error.page.details.label": "Детали ошибки",
|
||||
"error.page.action.restart": "Перезапустить",
|
||||
"error.page.action.report": "Сообщить об ошибке",
|
||||
"error.page.action.reported": "Об ошибке сообщено",
|
||||
"error.page.action.exportLogs": "Экспортировать журналы",
|
||||
"error.page.action.checking": "Проверка...",
|
||||
"error.page.action.checkUpdates": "Проверить обновления",
|
||||
"error.page.action.updateTo": "Обновить до {{version}}",
|
||||
|
|
@ -505,11 +589,31 @@ export const dict = {
|
|||
"home.recentProjects": "Недавние проекты",
|
||||
"home.empty.title": "Нет недавних проектов",
|
||||
"home.empty.description": "Начните с открытия локального проекта",
|
||||
"home.title": "Главная",
|
||||
"home.projects": "Проекты",
|
||||
"home.project.add": "Добавить проект",
|
||||
"home.recentlyClosed": "Недавно закрытые",
|
||||
"home.server.collapse": "Свернуть проекты сервера",
|
||||
"home.server.expand": "Развернуть проекты сервера",
|
||||
"home.sessions.search.placeholder": "Поиск сессий",
|
||||
"home.sessions.search.placeholder.scoped": "Поиск сессий в {{scope}}",
|
||||
"home.sessions.search.sessions": "Сессии",
|
||||
"home.sessions.search.noResults": "Сессии по запросу {{query}} не найдены",
|
||||
"home.sessions.empty": "Здесь пока ничего нет",
|
||||
"home.sessions.empty.description": "Создайте сессию, чтобы начать",
|
||||
"home.sessions.group.today": "Сегодня",
|
||||
"home.sessions.group.yesterday": "Вчера",
|
||||
"home.sessions.group.older": "Ранее",
|
||||
|
||||
"session.tab.session": "Сессия",
|
||||
"session.tab.review": "Обзор",
|
||||
"session.tab.context": "Контекст",
|
||||
"session.tab.unknown": "Неизвестная сессия",
|
||||
"session.panel.reviewAndFiles": "Обзор и файлы",
|
||||
"session.error.notFound": "Эта сессия не найдена",
|
||||
"session.error.notFound.description": "Эта вкладка указывает на сессию, которой больше нет на этом сервере.",
|
||||
"session.error.notFound.closeTab": "Закрыть вкладку",
|
||||
"session.error.serverConnection": "Не удаётся подключиться к этому серверу",
|
||||
"session.review.filesChanged": "{{count}} файлов изменено",
|
||||
"session.review.change.one": "Изменение",
|
||||
"session.review.change.other": "Изменения",
|
||||
|
|
@ -518,6 +622,8 @@ export const dict = {
|
|||
"session.review.noVcs": "Система контроля версий Git не обнаружена, изменения не отображаются",
|
||||
"session.review.noSnapshot": "Отслеживание снимков отключено в настройках, поэтому изменения сессии недоступны",
|
||||
"session.review.noChanges": "Нет изменений",
|
||||
"session.review.noUncommittedChanges": "Пока нет незафиксированных изменений",
|
||||
"session.review.noBranchChanges": "Пока нет изменений в ветке",
|
||||
"session.files.selectToOpen": "Выберите файл, чтобы открыть",
|
||||
"session.files.all": "Все файлы",
|
||||
"session.files.empty": "Нет файлов",
|
||||
|
|
@ -529,9 +635,10 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Перейти к последнему",
|
||||
|
||||
"session.context.addToContext": "Добавить {{selection}} в контекст",
|
||||
"session.todo.title": "Задачи",
|
||||
"session.todo.collapse": "Свернуть",
|
||||
"session.todo.expand": "Развернуть",
|
||||
"session.question.minimize": "Свернуть вопрос",
|
||||
"session.question.restore": "Восстановить вопрос",
|
||||
"session.question.pending.one": "{{count}} вопрос без ответа",
|
||||
"session.question.pending.other": "Вопросы без ответа: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} сообщение в очереди",
|
||||
"session.followupDock.summary.other": "{{count}} сообщений в очереди",
|
||||
"session.followupDock.sendNow": "Отправить сейчас",
|
||||
|
|
@ -545,9 +652,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Восстановить сообщение",
|
||||
|
||||
"session.new.title": "Создавайте что угодно",
|
||||
"session.new.project.new": "Новый проект",
|
||||
"session.new.project.search": "Поиск проектов",
|
||||
"session.new.project.add": "Добавить проект",
|
||||
"session.new.worktree.main": "Основная ветка",
|
||||
"session.new.worktree.mainWithBranch": "Основная ветка ({{branch}})",
|
||||
"session.new.worktree.create": "Создать новый worktree",
|
||||
"session.new.workspace.runIn": "Запустить сессию в",
|
||||
"session.new.workspace.triggerLocal": "Локально",
|
||||
"session.new.workspace.local": "Локальный репозиторий",
|
||||
"session.new.workspace.existing": "Рабочее пространство…",
|
||||
"session.new.lastModified": "Последнее изменение",
|
||||
|
||||
"session.header.search.placeholder": "Поиск {{project}}",
|
||||
|
|
@ -619,6 +733,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Недавние сессии",
|
||||
"sidebar.project.viewAllSessions": "Посмотреть все сессии",
|
||||
"sidebar.project.clearNotifications": "Очистить уведомления",
|
||||
"sidebar.empty.title": "Нет открытых проектов",
|
||||
"sidebar.empty.description": "Откройте проект, чтобы начать",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Приложение",
|
||||
|
|
@ -630,6 +746,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "Запускать сервер OpenCode внутри WSL на Windows.",
|
||||
|
||||
"settings.general.section.appearance": "Внешний вид",
|
||||
"settings.general.section.advanced": "Дополнительно",
|
||||
"settings.general.section.notifications": "Системные уведомления",
|
||||
"settings.general.section.updates": "Обновления",
|
||||
"settings.general.section.sounds": "Звуковые эффекты",
|
||||
|
|
@ -638,6 +755,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "Язык",
|
||||
"settings.general.row.language.description": "Изменить язык отображения OpenCode",
|
||||
"settings.general.row.shell.title": "Оболочка терминала",
|
||||
"settings.general.row.shell.description":
|
||||
"Выберите оболочку для терминала. Совместимые оболочки также используются агентом при вызове инструментов.",
|
||||
"settings.general.row.shell.autoDefault": "Авто (по умолчанию)",
|
||||
"settings.general.row.shell.terminalOnly": "только терминал",
|
||||
"settings.general.row.appearance.title": "Внешний вид",
|
||||
"settings.general.row.appearance.description": "Настройте как OpenCode выглядит на вашем устройстве",
|
||||
"settings.general.row.colorScheme.title": "Цветовая схема",
|
||||
|
|
@ -646,8 +768,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "Настройте оформление OpenCode.",
|
||||
"settings.general.row.font.title": "Шрифт кода",
|
||||
"settings.general.row.font.description": "Настройте шрифт, используемый в блоках кода",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Шрифт терминала",
|
||||
"settings.general.row.terminalFont.description": "Настройте шрифт, используемый в терминале",
|
||||
"settings.general.row.uiFont.title": "Шрифт интерфейса",
|
||||
"settings.general.row.uiFont.description": "Настройте шрифт, используемый во всем интерфейсе",
|
||||
"settings.general.row.followup.title": "Поведение уточняющих вопросов",
|
||||
|
|
@ -655,6 +777,22 @@ export const dict = {
|
|||
"Выберите, отправлять ли уточняющие вопросы сразу или помещать их в очередь",
|
||||
"settings.general.row.followup.option.queue": "Очередь",
|
||||
"settings.general.row.followup.option.steer": "Направлять",
|
||||
"settings.general.row.showFileTree.title": "Дерево файлов",
|
||||
"settings.general.row.showFileTree.description": "Показывать панель дерева файлов в сессиях",
|
||||
"settings.general.row.showNavigation.title": "Элементы навигации",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Показывать кнопки «Назад» и «Вперёд» в строке заголовка приложения",
|
||||
"settings.general.row.showSearch.title": "Палитра команд",
|
||||
"settings.general.row.showSearch.description": "Показывать кнопку поиска и палитры команд в строке заголовка",
|
||||
"settings.general.row.showTerminal.title": "Терминал",
|
||||
"settings.general.row.showTerminal.description": "Показывать кнопку терминала в строке заголовка приложения",
|
||||
"settings.general.row.showStatus.title": "Состояние сервера",
|
||||
"settings.general.row.showStatus.description": "Показывать кнопку состояния сервера в строке заголовка",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Нижняя навигация",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"На мобильных устройствах размещать строку заголовка и вкладки сессии внизу экрана",
|
||||
"settings.general.row.showCustomAgents.title": "Пользовательские агенты",
|
||||
"settings.general.row.showCustomAgents.description": "Показывать выбор агента в редакторе запросов",
|
||||
"settings.general.row.reasoningSummaries.title": "Показывать сводки рассуждений",
|
||||
"settings.general.row.reasoningSummaries.description": "Отображать сводки рассуждений модели в ленте",
|
||||
|
||||
|
|
@ -664,6 +802,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Разворачивать элементы инструмента edit",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Показывать элементы инструментов edit, write и patch в ленте развернутыми по умолчанию",
|
||||
"settings.general.row.newLayoutDesigns.title": "Новая компоновка и оформление",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Включить обновлённую компоновку, главную страницу, редактор запросов и интерфейс сессии",
|
||||
"settings.general.row.pinchZoom.title": "Масштабирование щипком",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Разрешить масштабирование жестом щипка на трекпаде и прокруткой с Ctrl",
|
||||
"settings.general.row.wayland.title": "Использовать нативный Wayland",
|
||||
"settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
|
|
@ -678,6 +822,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Проверить обновления вручную и установить, если доступны",
|
||||
"settings.updates.action.checkNow": "Проверить сейчас",
|
||||
"settings.updates.action.checking": "Проверка...",
|
||||
"settings.updates.action.downloading": "Загрузка...",
|
||||
"settings.updates.action.installing": "Установка...",
|
||||
"settings.updates.toast.latest.title": "У вас последняя версия",
|
||||
"settings.updates.toast.latest.description": "Вы используете последнюю версию OpenCode.",
|
||||
"sound.option.none": "Нет",
|
||||
|
|
@ -807,8 +953,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Загрузка навыка по имени",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Запросы к языковому серверу",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "Обновление списка задач",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Получение контента по URL",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
|
|
@ -874,7 +1018,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Отслеживайте, просматривайте и отменяйте изменения в этом проекте",
|
||||
"session.review.noVcs.createGit.actionLoading": "Создание репозитория Git...",
|
||||
"session.review.noVcs.createGit.action": "Создать репозиторий Git",
|
||||
"session.todo.progress": "Выполнено {{done}} из {{total}} задач",
|
||||
"session.question.progress": "{{current}} из {{total}} вопросов",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Проводник",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "สลับแถบข้าง",
|
||||
"command.project.open": "เปิดโปรเจกต์",
|
||||
"command.project.previous": "โปรเจกต์ก่อนหน้า",
|
||||
"command.project.next": "โปรเจกต์ถัดไป",
|
||||
"command.project.index": "สลับไปยังโปรเจกต์ {{index}}",
|
||||
"command.provider.connect": "เชื่อมต่อผู้ให้บริการ",
|
||||
"command.server.switch": "สลับเซิร์ฟเวอร์",
|
||||
"command.settings.open": "เปิดการตั้งค่า",
|
||||
|
|
@ -32,7 +35,7 @@ export const dict = {
|
|||
"command.session.next.unseen": "เซสชันที่ยังไม่ได้อ่านถัดไป",
|
||||
"command.session.archive": "จัดเก็บเซสชัน",
|
||||
|
||||
"command.palette": "คำสั่งค้นหา",
|
||||
"command.palette": "ชุดคำสั่ง",
|
||||
|
||||
"command.theme.cycle": "เปลี่ยนธีม",
|
||||
"command.theme.set": "ใช้ธีม: {{theme}}",
|
||||
|
|
@ -45,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "เซสชันใหม่",
|
||||
"command.file.open": "เปิดไฟล์",
|
||||
"command.tab.close": "ปิดแท็บ",
|
||||
"command.tab.reopenClosed": "เปิดแท็บที่ปิดไปอีกครั้ง",
|
||||
"command.context.addSelection": "เพิ่มส่วนที่เลือกไปยังบริบท",
|
||||
"command.context.addSelection.description": "เพิ่มบรรทัดที่เลือกจากไฟล์ปัจจุบัน",
|
||||
"command.input.focus": "โฟกัสช่องป้อนข้อมูล",
|
||||
|
|
@ -163,7 +167,7 @@ export const dict = {
|
|||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "ตัวอักษรพิมพ์เล็ก ตัวเลข ยัติภังค์ หรือขีดล่าง",
|
||||
"provider.custom.field.name.label": "ชื่อที่แสดง",
|
||||
"provider.custom.field.name.placeholder": "My AI Provider",
|
||||
"provider.custom.field.name.placeholder": "ผู้ให้บริการ AI ของฉัน",
|
||||
"provider.custom.field.baseURL.label": "URL พื้นฐาน",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "คีย์ API",
|
||||
|
|
@ -208,6 +212,11 @@ export const dict = {
|
|||
"model.input.video": "วิดีโอ",
|
||||
"model.input.pdf": "PDF",
|
||||
"model.tooltip.allows": "อนุญาต: {{inputs}}",
|
||||
"model.tooltip.context.label": "บริบท",
|
||||
"model.tooltip.inputs": "ข้อมูลนำเข้า",
|
||||
"model.tooltip.model": "โมเดล",
|
||||
"model.tooltip.provider": "ผู้ให้บริการ",
|
||||
"model.tooltip.reasoning": "การใช้เหตุผล",
|
||||
"model.tooltip.reasoning.allowed": "อนุญาตการใช้เหตุผล",
|
||||
"model.tooltip.reasoning.none": "ไม่มีการใช้เหตุผล",
|
||||
"model.tooltip.context": "ขีดจำกัดบริบท {{limit}}",
|
||||
|
|
@ -218,6 +227,7 @@ export const dict = {
|
|||
"common.loading": "กำลังโหลด",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "ยกเลิก",
|
||||
"common.clear": "ล้าง",
|
||||
"common.connect": "เชื่อมต่อ",
|
||||
"common.disconnect": "ยกเลิกการเชื่อมต่อ",
|
||||
"common.continue": "ส่ง",
|
||||
|
|
@ -235,6 +245,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "เชลล์",
|
||||
"prompt.mode.normal": "พรอมต์",
|
||||
"prompt.mode.shell.exit": "กด esc เพื่อออก",
|
||||
"session.child.promptDisabled": "ไม่สามารถส่งพรอมต์ในเซสชันเอเจนต์ย่อยได้.",
|
||||
"session.child.backToParent": "กลับไปยังเซสชันหลัก.",
|
||||
|
||||
"prompt.example.1": "แก้ไข TODO ในโค้ดเบส",
|
||||
"prompt.example.2": "เทคโนโลยีของโปรเจกต์นี้คืออะไร?",
|
||||
|
|
@ -341,6 +353,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "ลบ",
|
||||
"dialog.server.current": "เซิร์ฟเวอร์ปัจจุบัน",
|
||||
"dialog.server.status.default": "ค่าเริ่มต้น",
|
||||
"wsl.server.add": "เพิ่มเซิร์ฟเวอร์ WSL",
|
||||
"wsl.server.addShort": "เพิ่ม WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "เซิร์ฟเวอร์ WSL",
|
||||
"wsl.server.retryStart": "ลองเริ่มอีกครั้ง",
|
||||
"wsl.server.updating": "กำลังอัปเดต...",
|
||||
"wsl.onboarding.step.distro": "เลือกดิสโทร",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "กำลังตรวจสอบ WSL...",
|
||||
"wsl.onboarding.restartRequired": "ต้องรีสตาร์ท Windows เพื่อให้การติดตั้ง WSL เสร็จสมบูรณ์.",
|
||||
"wsl.onboarding.ready": "WSL พร้อมใช้งานแล้ว.",
|
||||
"wsl.onboarding.required": "ต้องมี WSL เพื่อดำเนินการต่อ.",
|
||||
"wsl.onboarding.checkingDistros": "กำลังตรวจสอบดิสโทร...",
|
||||
"wsl.onboarding.installingDistro": "กำลังติดตั้ง {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "กำลังตรวจสอบ {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "กำลังแสดงรายการดิสโทร...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} พร้อมใช้งานแล้ว.",
|
||||
"wsl.onboarding.distroNotInstalled": "ยังไม่ได้ติดตั้ง {{distro}}.",
|
||||
"wsl.onboarding.openDistroOnce": "เปิด {{distro}} หนึ่งครั้งเพื่อให้การตั้งค่าเสร็จสมบูรณ์.",
|
||||
"wsl.onboarding.finishingDistro": "กำลังตั้งค่า {{distro}} ให้เสร็จสมบูรณ์.",
|
||||
"wsl.onboarding.pickDistro": "เลือกดิสโทรหรือติดตั้งจากรายการด้านล่าง.",
|
||||
"wsl.onboarding.checkingOpencode": "กำลังตรวจสอบ OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "กำลังตรวจสอบ OpenCode ใน {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "กำลังอัปเดต OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "กำลังอัปเดต OpenCode ใน {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "อัปเดต OpenCode ใน {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "อัปเดต OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode พร้อมใช้งานใน {{distro}} แล้ว.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode พร้อมใช้งานแล้ว.",
|
||||
"wsl.onboarding.installOpencodeIn": "ติดตั้ง OpenCode ใน {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "ติดตั้ง OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "เลือกดิสโทรก่อน.",
|
||||
"wsl.onboarding.loadFailed": "ไม่สามารถโหลดสถานะ WSL ได้.",
|
||||
"wsl.onboarding.loading": "กำลังโหลด...",
|
||||
"wsl.onboarding.installedDistros": "ดิสโทรที่ติดตั้งแล้ว",
|
||||
"wsl.onboarding.checkAgain": "ตรวจสอบอีกครั้ง",
|
||||
"wsl.onboarding.distroStatus.ready": "พร้อมใช้งาน",
|
||||
"wsl.onboarding.distroStatus.checking": "กำลังตรวจสอบ...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "ยังไม่ได้ติดตั้ง OpenCode",
|
||||
"wsl.onboarding.distroStatus.missingTools": "ไม่มี bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "ไม่รองรับ · ใช้ WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "ต้องการดิสโทรอื่น?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "ติดตั้งดิสทริบิวชัน Linux จากแค็ตตาล็อก WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "ยังไม่ได้ติดตั้ง WSL",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"ต้องมี WSL (Windows Subsystem for Linux) ก่อนที่ OpenCode จะเพิ่มเซิร์ฟเวอร์ WSL ได้",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL ไม่พร้อมใช้งาน",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode ไม่สามารถตรวจสอบ WSL บนเครื่องนี้ได้.",
|
||||
"wsl.onboarding.installWsl": "ติดตั้ง WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"รีสตาร์ท Windows เพื่อให้การติดตั้ง WSL เสร็จสมบูรณ์ แล้วเปิด OpenCode อีกครั้ง.",
|
||||
"wsl.onboarding.next": "ถัดไป",
|
||||
"wsl.onboarding.refresh": "รีเฟรช",
|
||||
"wsl.onboarding.allDistrosAdded": "เพิ่มดิสโทรที่ติดตั้งไว้ทั้งหมดแล้ว.",
|
||||
"wsl.onboarding.noDistros": "ยังไม่พบดิสโทร.",
|
||||
"wsl.onboarding.install": "ติดตั้ง",
|
||||
"wsl.onboarding.installing": "กำลังติดตั้ง...",
|
||||
"wsl.onboarding.installDistro": "ติดตั้งดิสโทร",
|
||||
"wsl.onboarding.searchDistros": "ค้นหาดิสโทร",
|
||||
"wsl.onboarding.wsl2Required": "ต้องใช้ WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "ดิสโทรนี้ต้องมี bash และ curl.",
|
||||
"wsl.onboarding.openTerminal": "เปิดเทอร์มินัล",
|
||||
"wsl.onboarding.path": "เส้นทาง: {{path}}",
|
||||
"wsl.onboarding.notFound": "ไม่พบ",
|
||||
"wsl.onboarding.version": "เวอร์ชัน: {{version}}",
|
||||
"wsl.onboarding.unknown": "ไม่ทราบ",
|
||||
"wsl.onboarding.desktopVersion": "เดสก์ท็อป {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "เวอร์ชันที่ติดตั้งไม่ตรงกับเวอร์ชันของแอปเดสก์ท็อป.",
|
||||
"wsl.onboarding.adding": "กำลังเพิ่ม...",
|
||||
|
||||
"dialog.project.edit.title": "แก้ไขโปรเจกต์",
|
||||
"dialog.project.edit.name": "ชื่อ",
|
||||
|
|
@ -403,6 +484,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -451,10 +533,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "เกิดข้อผิดพลาด",
|
||||
"error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน",
|
||||
"error.page.description.localServerStartup": "เกิดข้อผิดพลาดขณะเริ่มเซิร์ฟเวอร์ในเครื่อง.",
|
||||
"error.page.details.label": "รายละเอียดข้อผิดพลาด",
|
||||
"error.page.action.restart": "รีสตาร์ท",
|
||||
"error.page.action.report": "รายงานข้อผิดพลาด",
|
||||
"error.page.action.reported": "รายงานข้อผิดพลาดแล้ว",
|
||||
"error.page.action.exportLogs": "ส่งออกบันทึก",
|
||||
"error.page.action.checking": "กำลังตรวจสอบ...",
|
||||
"error.page.action.checkUpdates": "ตรวจสอบการอัปเดต",
|
||||
"error.page.action.updateTo": "อัปเดตเป็น {{version}}",
|
||||
|
|
@ -500,11 +584,31 @@ export const dict = {
|
|||
"home.recentProjects": "โปรเจกต์ล่าสุด",
|
||||
"home.empty.title": "ไม่มีโปรเจกต์ล่าสุด",
|
||||
"home.empty.description": "เริ่มต้นโดยเปิดโปรเจกต์ในเครื่อง",
|
||||
"home.title": "หน้าหลัก",
|
||||
"home.projects": "โปรเจกต์",
|
||||
"home.project.add": "เพิ่มโปรเจกต์",
|
||||
"home.recentlyClosed": "ที่ปิดไปล่าสุด",
|
||||
"home.server.collapse": "ย่อโปรเจกต์ของเซิร์ฟเวอร์",
|
||||
"home.server.expand": "ขยายโปรเจกต์ของเซิร์ฟเวอร์",
|
||||
"home.sessions.search.placeholder": "ค้นหาเซสชัน",
|
||||
"home.sessions.search.placeholder.scoped": "ค้นหาเซสชันใน {{scope}}",
|
||||
"home.sessions.search.sessions": "เซสชัน",
|
||||
"home.sessions.search.noResults": "ไม่พบเซสชันสำหรับ {{query}}",
|
||||
"home.sessions.empty": "ยังไม่มีอะไรที่นี่",
|
||||
"home.sessions.empty.description": "สร้างเซสชันเพื่อเริ่มต้น",
|
||||
"home.sessions.group.today": "วันนี้",
|
||||
"home.sessions.group.yesterday": "เมื่อวาน",
|
||||
"home.sessions.group.older": "ก่อนหน้านี้",
|
||||
|
||||
"session.tab.session": "เซสชัน",
|
||||
"session.tab.review": "ตรวจสอบ",
|
||||
"session.tab.context": "บริบท",
|
||||
"session.tab.unknown": "เซสชันที่ไม่รู้จัก",
|
||||
"session.panel.reviewAndFiles": "ตรวจสอบและไฟล์",
|
||||
"session.error.notFound": "ไม่พบเซสชันนี้",
|
||||
"session.error.notFound.description": "แท็บนี้อ้างถึงเซสชันที่ไม่มีอยู่บนเซิร์ฟเวอร์นี้แล้ว.",
|
||||
"session.error.notFound.closeTab": "ปิดแท็บ",
|
||||
"session.error.serverConnection": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์นี้",
|
||||
"session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง",
|
||||
"session.review.change.one": "การเปลี่ยนแปลง",
|
||||
"session.review.change.other": "การเปลี่ยนแปลง",
|
||||
|
|
@ -513,6 +617,8 @@ export const dict = {
|
|||
"session.review.noVcs": "ไม่ตรวจพบระบบควบคุมเวอร์ชัน Git การเปลี่ยนแปลงจะไม่แสดง",
|
||||
"session.review.noSnapshot": "การติดตามสแนปชอตถูกปิดใช้งานในการกำหนดค่า ดังนั้นการเปลี่ยนแปลงเซสชันจึงไม่พร้อมใช้งาน",
|
||||
"session.review.noChanges": "ไม่มีการเปลี่ยนแปลง",
|
||||
"session.review.noUncommittedChanges": "ยังไม่มีการเปลี่ยนแปลงที่รอคอมมิต",
|
||||
"session.review.noBranchChanges": "ยังไม่มีการเปลี่ยนแปลงในสาขา",
|
||||
|
||||
"session.files.selectToOpen": "เลือกไฟล์เพื่อเปิด",
|
||||
"session.files.empty": "ไม่มีไฟล์",
|
||||
|
|
@ -526,9 +632,10 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "ไปที่ล่าสุด",
|
||||
|
||||
"session.context.addToContext": "เพิ่ม {{selection}} ไปยังบริบท",
|
||||
"session.todo.title": "สิ่งที่ต้องทำ",
|
||||
"session.todo.collapse": "ย่อ",
|
||||
"session.todo.expand": "ขยาย",
|
||||
"session.question.minimize": "ย่อคำถาม",
|
||||
"session.question.restore": "คืนค่าคำถาม",
|
||||
"session.question.pending.one": "คำถามที่รอดำเนินการ {{count}} ข้อ",
|
||||
"session.question.pending.other": "คำถามที่รอดำเนินการ {{count}} ข้อ",
|
||||
"session.followupDock.summary.one": "{{count}} ข้อความในคิว",
|
||||
"session.followupDock.summary.other": "{{count}} ข้อความในคิว",
|
||||
"session.followupDock.sendNow": "ส่งทันที",
|
||||
|
|
@ -542,9 +649,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "กู้คืนข้อความ",
|
||||
|
||||
"session.new.title": "สร้างอะไรก็ได้",
|
||||
"session.new.project.new": "โปรเจกต์ใหม่",
|
||||
"session.new.project.search": "ค้นหาโปรเจกต์",
|
||||
"session.new.project.add": "เพิ่มโปรเจกต์",
|
||||
"session.new.worktree.main": "สาขาหลัก",
|
||||
"session.new.worktree.mainWithBranch": "สาขาหลัก ({{branch}})",
|
||||
"session.new.worktree.create": "สร้าง worktree ใหม่",
|
||||
"session.new.workspace.runIn": "เรียกใช้เซสชันใน",
|
||||
"session.new.workspace.triggerLocal": "ในเครื่อง",
|
||||
"session.new.workspace.local": "รีโพซิทอรีในเครื่อง",
|
||||
"session.new.workspace.existing": "พื้นที่ทำงาน…",
|
||||
"session.new.lastModified": "แก้ไขล่าสุด",
|
||||
|
||||
"session.header.search.placeholder": "ค้นหา {{project}}",
|
||||
|
|
@ -612,6 +726,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "เซสชันล่าสุด",
|
||||
"sidebar.project.viewAllSessions": "ดูเซสชันทั้งหมด",
|
||||
"sidebar.project.clearNotifications": "ล้างการแจ้งเตือน",
|
||||
"sidebar.empty.title": "ไม่มีโปรเจกต์ที่เปิดอยู่",
|
||||
"sidebar.empty.description": "เปิดโปรเจกต์เพื่อเริ่มต้น",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
|
||||
|
|
@ -624,6 +740,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "เรียกใช้เซิร์ฟเวอร์ OpenCode ภายใน WSL บน Windows",
|
||||
|
||||
"settings.general.section.appearance": "รูปลักษณ์",
|
||||
"settings.general.section.advanced": "ขั้นสูง",
|
||||
"settings.general.section.notifications": "การแจ้งเตือนระบบ",
|
||||
"settings.general.section.updates": "การอัปเดต",
|
||||
"settings.general.section.sounds": "เสียงเอฟเฟกต์",
|
||||
|
|
@ -632,6 +749,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "ภาษา",
|
||||
"settings.general.row.language.description": "เปลี่ยนภาษาที่แสดงสำหรับ OpenCode",
|
||||
"settings.general.row.shell.title": "เชลล์เทอร์มินัล",
|
||||
"settings.general.row.shell.description":
|
||||
"เลือกเชลล์ที่จะใช้กับเทอร์มินัล เชลล์ที่เข้ากันได้จะใช้สำหรับการเรียกเครื่องมือของเอเจนต์ด้วย",
|
||||
"settings.general.row.shell.autoDefault": "อัตโนมัติ (ค่าเริ่มต้น)",
|
||||
"settings.general.row.shell.terminalOnly": "เทอร์มินัลเท่านั้น",
|
||||
"settings.general.row.appearance.title": "รูปลักษณ์",
|
||||
"settings.general.row.appearance.description": "ปรับแต่งวิธีการที่ OpenCode มีลักษณะบนอุปกรณ์ของคุณ",
|
||||
"settings.general.row.colorScheme.title": "โทนสี",
|
||||
|
|
@ -640,14 +762,29 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "ปรับแต่งวิธีการที่ OpenCode มีธีม",
|
||||
"settings.general.row.font.title": "ฟอนต์โค้ด",
|
||||
"settings.general.row.font.description": "ปรับแต่งฟอนต์ที่ใช้ในบล็อกโค้ด",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "ฟอนต์เทอร์มินัล",
|
||||
"settings.general.row.terminalFont.description": "ปรับแต่งฟอนต์ที่ใช้ในเทอร์มินัล",
|
||||
"settings.general.row.uiFont.title": "ฟอนต์ UI",
|
||||
"settings.general.row.uiFont.description": "ปรับแต่งฟอนต์ที่ใช้ทั่วทั้งอินเทอร์เฟซ",
|
||||
"settings.general.row.followup.title": "พฤติกรรมการติดตามผล",
|
||||
"settings.general.row.followup.description": "เลือกว่าจะให้พร้อมท์ติดตามผลทำงานทันทีหรือรอในคิว",
|
||||
"settings.general.row.followup.option.queue": "คิว",
|
||||
"settings.general.row.followup.option.steer": "นำทาง",
|
||||
"settings.general.row.showFileTree.title": "ต้นไม้ไฟล์",
|
||||
"settings.general.row.showFileTree.description": "แสดงแผงต้นไม้ไฟล์ในเซสชัน",
|
||||
"settings.general.row.showNavigation.title": "ตัวควบคุมการนำทาง",
|
||||
"settings.general.row.showNavigation.description": "แสดงปุ่มย้อนกลับและไปข้างหน้าในแถบชื่อเรื่องเดสก์ท็อป",
|
||||
"settings.general.row.showSearch.title": "ชุดคำสั่ง",
|
||||
"settings.general.row.showSearch.description": "แสดงปุ่มค้นหาและชุดคำสั่งในแถบชื่อเรื่อง",
|
||||
"settings.general.row.showTerminal.title": "เทอร์มินัล",
|
||||
"settings.general.row.showTerminal.description": "แสดงปุ่มเทอร์มินัลในแถบชื่อเรื่องเดสก์ท็อป",
|
||||
"settings.general.row.showStatus.title": "สถานะเซิร์ฟเวอร์",
|
||||
"settings.general.row.showStatus.description": "แสดงปุ่มสถานะเซิร์ฟเวอร์ในแถบชื่อเรื่อง",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "การนำทางด้านล่าง",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"วางแถบชื่อเรื่องและแท็บเซสชันไว้ด้านล่างของหน้าจอบนอุปกรณ์เคลื่อนที่",
|
||||
"settings.general.row.showCustomAgents.title": "เอเจนต์ที่กำหนดเอง",
|
||||
"settings.general.row.showCustomAgents.description": "แสดงตัวเลือกเอเจนต์ในช่องเขียนข้อความ",
|
||||
"settings.general.row.reasoningSummaries.title": "แสดงสรุปการใช้เหตุผล",
|
||||
"settings.general.row.reasoningSummaries.description": "แสดงสรุปการใช้เหตุผลของโมเดลในไทม์ไลน์",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "ขยายส่วนเครื่องมือ shell",
|
||||
|
|
@ -655,6 +792,11 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "ขยายส่วนเครื่องมือ edit",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"แสดงส่วนเครื่องมือ edit, write และ patch แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
|
||||
"settings.general.row.newLayoutDesigns.title": "เลย์เอาต์และดีไซน์ใหม่",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"เปิดใช้เลย์เอาต์ หน้าแรก ช่องเขียนข้อความ และ UI เซสชันที่ออกแบบใหม่",
|
||||
"settings.general.row.pinchZoom.title": "บีบนิ้วเพื่อซูม",
|
||||
"settings.general.row.pinchZoom.description": "อนุญาตให้ใช้ท่าบีบนิ้วบนแทร็คแพดและ Ctrl-scroll เพื่อซูม",
|
||||
"settings.general.row.wayland.title": "ใช้ Wayland แบบเนทีฟ",
|
||||
"settings.general.row.wayland.description": "ปิดใช้งาน X11 fallback บน Wayland ต้องรีสตาร์ท",
|
||||
"settings.general.row.wayland.tooltip": "บน Linux ที่มีจอภาพรีเฟรชเรตแบบผสม Wayland แบบเนทีฟอาจเสถียรกว่า",
|
||||
|
|
@ -668,6 +810,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "ตรวจสอบการอัปเดตด้วยตนเองและติดตั้งหากมี",
|
||||
"settings.updates.action.checkNow": "ตรวจสอบทันที",
|
||||
"settings.updates.action.checking": "กำลังตรวจสอบ...",
|
||||
"settings.updates.action.downloading": "กำลังดาวน์โหลด...",
|
||||
"settings.updates.action.installing": "กำลังติดตั้ง...",
|
||||
"settings.updates.toast.latest.title": "คุณเป็นเวอร์ชันล่าสุดแล้ว",
|
||||
"settings.updates.toast.latest.description": "คุณกำลังใช้งาน OpenCode เวอร์ชันล่าสุด",
|
||||
|
||||
|
|
@ -795,8 +939,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "โหลดทักษะตามชื่อ",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "เรียกใช้การสืบค้นเซิร์ฟเวอร์ภาษา",
|
||||
"settings.permissions.tool.todowrite.title": "เขียนรายการงาน",
|
||||
"settings.permissions.tool.todowrite.description": "อัปเดตรายการงาน",
|
||||
"settings.permissions.tool.webfetch.title": "ดึงข้อมูลจากเว็บ",
|
||||
"settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL",
|
||||
"settings.permissions.tool.websearch.title": "ค้นหาเว็บ",
|
||||
|
|
@ -857,11 +999,10 @@ export const dict = {
|
|||
"dialog.server.add.usernamePlaceholder": "ชื่อผู้ใช้",
|
||||
"dialog.server.add.passwordPlaceholder": "รหัสผ่าน",
|
||||
"server.row.noUsername": "ไม่มีชื่อผู้ใช้",
|
||||
"session.review.noVcs.createGit.title": "สร้าง Git repository",
|
||||
"session.review.noVcs.createGit.title": "สร้าง Git รีโพซิทอรี",
|
||||
"session.review.noVcs.createGit.description": "ติดตาม ตรวจสอบ และเลิกทำสิ่งเปลี่ยนแปลงในโปรเจกต์นี้",
|
||||
"session.review.noVcs.createGit.actionLoading": "กำลังสร้าง Git repository...",
|
||||
"session.review.noVcs.createGit.action": "สร้าง Git repository",
|
||||
"session.todo.progress": "เสร็จสิ้น {{done}} จาก {{total}} รายการ",
|
||||
"session.review.noVcs.createGit.actionLoading": "กำลังสร้าง Git รีโพซิทอรี...",
|
||||
"session.review.noVcs.createGit.action": "สร้าง Git รีโพซิทอรี",
|
||||
"session.question.progress": "{{current}} จาก {{total}} คำถาม",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "Kenar çubuğunu aç/kapat",
|
||||
"command.project.open": "Proje aç",
|
||||
"command.project.previous": "Önceki proje",
|
||||
"command.project.next": "Sonraki proje",
|
||||
"command.project.index": "{{index}} numaralı projeye geç",
|
||||
"command.provider.connect": "Sağlayıcı bağla",
|
||||
"command.server.switch": "Sunucu değiştir",
|
||||
"command.settings.open": "Ayarları aç",
|
||||
|
|
@ -49,6 +52,7 @@ export const dict = {
|
|||
"command.session.new": "Yeni oturum",
|
||||
"command.file.open": "Dosya aç",
|
||||
"command.tab.close": "Sekmeyi kapat",
|
||||
"command.tab.reopenClosed": "Kapatılan sekmeyi yeniden aç",
|
||||
"command.context.addSelection": "Seçimi bağlama ekle",
|
||||
"command.context.addSelection.description": "Mevcut dosyadan seçili satırları ekle",
|
||||
"command.input.focus": "Girişi odakla",
|
||||
|
|
@ -132,7 +136,7 @@ export const dict = {
|
|||
"provider.connect.status.waiting": "Yetkilendirme bekleniyor...",
|
||||
"provider.connect.status.failed": "Yetkilendirme başarısız: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"{{provider}} hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için {{provider}} API anahtarınızı girin.",
|
||||
"{{provider}} hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için API anahtarınızı girin.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API anahtarı",
|
||||
"provider.connect.apiKey.placeholder": "API anahtarı",
|
||||
"provider.connect.apiKey.required": "API anahtarı gerekli",
|
||||
|
|
@ -143,10 +147,10 @@ export const dict = {
|
|||
"provider.connect.opencodeZen.visit.prefix": "",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " adresini ziyaret ederek API anahtarınızı alın.",
|
||||
"provider.connect.oauth.code.visit.prefix":
|
||||
"Hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için ",
|
||||
"provider.connect.oauth.code.visit.prefix": "Yetkilendirme kodunuzu almak için ",
|
||||
"provider.connect.oauth.code.visit.link": "bu bağlantıya",
|
||||
"provider.connect.oauth.code.visit.suffix": " tıklayarak yetkilendirme kodunuzu alın.",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" gidin; ardından hesabınızı bağlayarak OpenCode'da {{provider}} modellerini kullanın.",
|
||||
"provider.connect.oauth.code.label": "{{method}} yetkilendirme kodu",
|
||||
"provider.connect.oauth.code.placeholder": "Yetkilendirme kodu",
|
||||
"provider.connect.oauth.code.required": "Yetkilendirme kodu gerekli",
|
||||
|
|
@ -213,6 +217,11 @@ export const dict = {
|
|||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Kabul eder: {{inputs}}",
|
||||
"model.tooltip.context.label": "Bağlam",
|
||||
"model.tooltip.inputs": "Girdiler",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Sağlayıcı",
|
||||
"model.tooltip.reasoning": "Akıl yürütme",
|
||||
"model.tooltip.reasoning.allowed": "Akıl yürütme destekler",
|
||||
"model.tooltip.reasoning.none": "Akıl yürütme yok",
|
||||
"model.tooltip.context": "Bağlam limiti {{limit}}",
|
||||
|
|
@ -223,6 +232,7 @@ export const dict = {
|
|||
"common.loading": "Yükleniyor",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "İptal",
|
||||
"common.clear": "Temizle",
|
||||
"common.connect": "Bağlan",
|
||||
"common.disconnect": "Bağlantı Kes",
|
||||
"common.continue": "Gönder",
|
||||
|
|
@ -240,6 +250,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Kabuk",
|
||||
"prompt.mode.normal": "Komut",
|
||||
"prompt.mode.shell.exit": "çıkmak için esc",
|
||||
"session.child.promptDisabled": "Alt ajan oturumlarına istem gönderilemez.",
|
||||
"session.child.backToParent": "Ana oturuma dön.",
|
||||
|
||||
"prompt.example.1": "Kod tabanındaki bir TODO'yu düzelt",
|
||||
"prompt.example.2": "Bu projenin teknoloji yığını nedir?",
|
||||
|
|
@ -346,6 +358,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Sil",
|
||||
"dialog.server.current": "Mevcut Sunucu",
|
||||
"dialog.server.status.default": "Varsayılan",
|
||||
"wsl.server.add": "WSL sunucusu ekle",
|
||||
"wsl.server.addShort": "WSL ekle",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL sunucusu",
|
||||
"wsl.server.retryStart": "Başlatmayı yeniden dene",
|
||||
"wsl.server.updating": "Güncelleniyor...",
|
||||
"wsl.onboarding.step.distro": "Dağıtım seç",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL kontrol ediliyor...",
|
||||
"wsl.onboarding.restartRequired": "WSL kurulumunu tamamlamak için Windows'un yeniden başlatılması gerekiyor.",
|
||||
"wsl.onboarding.ready": "WSL hazır.",
|
||||
"wsl.onboarding.required": "Devam etmek için WSL gerekli.",
|
||||
"wsl.onboarding.checkingDistros": "Dağıtımlar kontrol ediliyor...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} kuruluyor...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} kontrol ediliyor...",
|
||||
"wsl.onboarding.listingDistros": "Dağıtımlar listeleniyor...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} hazır.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} henüz kurulu değil.",
|
||||
"wsl.onboarding.openDistroOnce": "Kurulumu tamamlamak için {{distro}} dağıtımını bir kez açın.",
|
||||
"wsl.onboarding.finishingDistro": "{{distro}} kurulumu tamamlanıyor.",
|
||||
"wsl.onboarding.pickDistro": "Bir dağıtım seçin veya aşağıdan bir dağıtım kurun.",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode kontrol ediliyor...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "{{distro}} içinde OpenCode kontrol ediliyor...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode güncelleniyor...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "{{distro}} içinde OpenCode güncelleniyor...",
|
||||
"wsl.onboarding.updateOpencodeIn": "{{distro}} içindeki OpenCode'u güncelleyin.",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode'u güncelle",
|
||||
"wsl.onboarding.opencodeReadyIn": "{{distro}} içinde OpenCode hazır.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode hazır.",
|
||||
"wsl.onboarding.installOpencodeIn": "OpenCode'u {{distro}} içine kurun.",
|
||||
"wsl.onboarding.installOpencode": "OpenCode'u kur",
|
||||
"wsl.onboarding.chooseDistroFirst": "Önce bir dağıtım seçin.",
|
||||
"wsl.onboarding.loadFailed": "WSL durumu yüklenemedi.",
|
||||
"wsl.onboarding.loading": "Yükleniyor...",
|
||||
"wsl.onboarding.installedDistros": "Kurulu dağıtımlar",
|
||||
"wsl.onboarding.checkAgain": "Tekrar kontrol et",
|
||||
"wsl.onboarding.distroStatus.ready": "Hazır",
|
||||
"wsl.onboarding.distroStatus.checking": "Kontrol ediliyor...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode kurulu değil",
|
||||
"wsl.onboarding.distroStatus.missingTools": "bash ve curl eksik",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Desteklenmiyor · WSL 2 kullanın",
|
||||
"wsl.onboarding.needAnotherDistro": "Başka bir dağıtım mı gerekiyor?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "WSL kataloğundan bir Linux dağıtımı kurun",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL kurulu değil",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"OpenCode'un WSL sunucusu ekleyebilmesi için önce WSL (Linux için Windows Alt Sistemi) kurulmalıdır",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL kullanılamıyor",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode bu makinede WSL'yi doğrulayamadı.",
|
||||
"wsl.onboarding.installWsl": "WSL'yi kur",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"WSL kurulumunu tamamlamak için Windows'u yeniden başlatın, ardından OpenCode'u tekrar açın.",
|
||||
"wsl.onboarding.next": "İleri",
|
||||
"wsl.onboarding.refresh": "Yenile",
|
||||
"wsl.onboarding.allDistrosAdded": "Kurulu tüm dağıtımlar zaten eklenmiş.",
|
||||
"wsl.onboarding.noDistros": "Henüz dağıtım algılanmadı.",
|
||||
"wsl.onboarding.install": "Kur",
|
||||
"wsl.onboarding.installing": "Kuruluyor...",
|
||||
"wsl.onboarding.installDistro": "Dağıtım kur",
|
||||
"wsl.onboarding.searchDistros": "Dağıtım ara",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 gerekli.",
|
||||
"wsl.onboarding.toolsRequired": "Bu dağıtım için bash ve curl gerekli.",
|
||||
"wsl.onboarding.openTerminal": "Terminali aç",
|
||||
"wsl.onboarding.path": "Yol: {{path}}",
|
||||
"wsl.onboarding.notFound": "bulunamadı",
|
||||
"wsl.onboarding.version": "Sürüm: {{version}}",
|
||||
"wsl.onboarding.unknown": "bilinmiyor",
|
||||
"wsl.onboarding.desktopVersion": "masaüstü {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Kurulu sürüm masaüstü uygulamasının sürümüyle eşleşmiyor.",
|
||||
"wsl.onboarding.adding": "Ekleniyor...",
|
||||
|
||||
"dialog.project.edit.title": "Projeyi düzenle",
|
||||
"dialog.project.edit.name": "Ad",
|
||||
|
|
@ -408,6 +489,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -456,10 +538,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Bir şeyler yanlış gitti",
|
||||
"error.page.description": "Uygulama yüklenirken bir hata oluştu.",
|
||||
"error.page.description.localServerStartup": "Yerel sunucu başlatılırken bir hata oluştu.",
|
||||
"error.page.details.label": "Hata Detayları",
|
||||
"error.page.action.restart": "Yeniden Başlat",
|
||||
"error.page.action.report": "Hatayı Bildir",
|
||||
"error.page.action.reported": "Hata Bildirildi",
|
||||
"error.page.action.exportLogs": "Günlükleri Dışa Aktar",
|
||||
"error.page.action.checking": "Kontrol ediliyor...",
|
||||
"error.page.action.checkUpdates": "Güncellemeleri kontrol et",
|
||||
"error.page.action.updateTo": "{{version}} sürümüne güncelle",
|
||||
|
|
@ -509,11 +593,31 @@ export const dict = {
|
|||
"home.recentProjects": "Son projeler",
|
||||
"home.empty.title": "Son proje yok",
|
||||
"home.empty.description": "Yerel bir proje açarak başlayın",
|
||||
"home.title": "Ana Sayfa",
|
||||
"home.projects": "Projeler",
|
||||
"home.project.add": "Proje ekle",
|
||||
"home.recentlyClosed": "Yakın zamanda kapatılanlar",
|
||||
"home.server.collapse": "Sunucu projelerini daralt",
|
||||
"home.server.expand": "Sunucu projelerini genişlet",
|
||||
"home.sessions.search.placeholder": "Oturum ara",
|
||||
"home.sessions.search.placeholder.scoped": "{{scope}} kapsamında oturum ara",
|
||||
"home.sessions.search.sessions": "Oturumlar",
|
||||
"home.sessions.search.noResults": "{{query}} için oturum bulunamadı",
|
||||
"home.sessions.empty": "Henüz burada bir şey yok",
|
||||
"home.sessions.empty.description": "Başlamak için bir oturum oluşturun",
|
||||
"home.sessions.group.today": "Bugün",
|
||||
"home.sessions.group.yesterday": "Dün",
|
||||
"home.sessions.group.older": "Daha eski",
|
||||
|
||||
"session.tab.session": "Oturum",
|
||||
"session.tab.review": "İnceleme",
|
||||
"session.tab.context": "Bağlam",
|
||||
"session.tab.unknown": "Bilinmeyen Oturum",
|
||||
"session.panel.reviewAndFiles": "İnceleme ve dosyalar",
|
||||
"session.error.notFound": "Bu oturum bulunamıyor",
|
||||
"session.error.notFound.description": "Bu sekme, artık bu sunucuda bulunmayan bir oturuma yönlendiriyor.",
|
||||
"session.error.notFound.closeTab": "Sekmeyi Kapat",
|
||||
"session.error.serverConnection": "Bu sunucuya bağlanılamıyor",
|
||||
"session.review.filesChanged": "{{count}} Dosya Değişti",
|
||||
"session.review.change.one": "Değişiklik",
|
||||
"session.review.change.other": "Değişiklik",
|
||||
|
|
@ -523,6 +627,8 @@ export const dict = {
|
|||
"session.review.noSnapshot":
|
||||
"Yapılandırmada anlık görüntü takibi devre dışı bırakıldı, bu nedenle oturum değişiklikleri kullanılamıyor",
|
||||
"session.review.noChanges": "Değişiklik yok",
|
||||
"session.review.noUncommittedChanges": "Henüz commit edilmemiş değişiklik yok",
|
||||
"session.review.noBranchChanges": "Henüz dal değişikliği yok",
|
||||
|
||||
"session.files.selectToOpen": "Açmak için bir dosya seçin",
|
||||
"session.files.all": "Tüm dosyalar",
|
||||
|
|
@ -536,9 +642,10 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "En sona atla",
|
||||
|
||||
"session.context.addToContext": "{{selection}} bağlama ekle",
|
||||
"session.todo.title": "Görevler",
|
||||
"session.todo.collapse": "Daralt",
|
||||
"session.todo.expand": "Genişlet",
|
||||
"session.question.minimize": "Soruyu küçült",
|
||||
"session.question.restore": "Soruyu geri yükle",
|
||||
"session.question.pending.one": "{{count}} bekleyen soru",
|
||||
"session.question.pending.other": "{{count}} bekleyen soru",
|
||||
"session.followupDock.summary.one": "{{count}} sıradaki mesaj",
|
||||
"session.followupDock.summary.other": "{{count}} sıradaki mesaj",
|
||||
"session.followupDock.sendNow": "Şimdi gönder",
|
||||
|
|
@ -552,9 +659,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Mesajı geri yükle",
|
||||
|
||||
"session.new.title": "İstediğini yap",
|
||||
"session.new.project.new": "Yeni proje",
|
||||
"session.new.project.search": "Proje ara",
|
||||
"session.new.project.add": "Proje ekle",
|
||||
"session.new.worktree.main": "Ana dal",
|
||||
"session.new.worktree.mainWithBranch": "Ana dal ({{branch}})",
|
||||
"session.new.worktree.create": "Yeni çalışma ağacı oluştur",
|
||||
"session.new.workspace.runIn": "Oturumu şurada çalıştır",
|
||||
"session.new.workspace.triggerLocal": "Yerel",
|
||||
"session.new.workspace.local": "Yerel depo",
|
||||
"session.new.workspace.existing": "Çalışma alanı…",
|
||||
"session.new.lastModified": "Son değişiklik",
|
||||
|
||||
"session.header.search.placeholder": "{{project}} ara",
|
||||
|
|
@ -624,6 +738,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "Son oturumlar",
|
||||
"sidebar.project.viewAllSessions": "Tüm oturumları görüntüle",
|
||||
"sidebar.project.clearNotifications": "Bildirimleri temizle",
|
||||
"sidebar.empty.title": "Açık proje yok",
|
||||
"sidebar.empty.description": "Başlamak için bir proje açın",
|
||||
|
||||
"app.name.desktop": "OpenCode Masaüstü",
|
||||
|
||||
|
|
@ -636,6 +752,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "OpenCode sunucusunu Windows'ta WSL içinde çalıştırın.",
|
||||
|
||||
"settings.general.section.appearance": "Görünüm",
|
||||
"settings.general.section.advanced": "Gelişmiş",
|
||||
"settings.general.section.notifications": "Sistem bildirimleri",
|
||||
"settings.general.section.updates": "Güncellemeler",
|
||||
"settings.general.section.sounds": "Ses efektleri",
|
||||
|
|
@ -644,6 +761,11 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "Dil",
|
||||
"settings.general.row.language.description": "OpenCode'un görünüm dilini değiştirin",
|
||||
"settings.general.row.shell.title": "Terminal Kabuğu",
|
||||
"settings.general.row.shell.description":
|
||||
"Terminalinizde kullanılacak kabuğu seçin. Uyumlu kabuklar, ajan araç çağrılarında da kullanılır.",
|
||||
"settings.general.row.shell.autoDefault": "Otomatik (Varsayılan)",
|
||||
"settings.general.row.shell.terminalOnly": "yalnızca terminal",
|
||||
"settings.general.row.appearance.title": "Görünüm",
|
||||
"settings.general.row.appearance.description": "OpenCode'un cihazınızdaki görünümünü özelleştirin",
|
||||
"settings.general.row.colorScheme.title": "Renk şeması",
|
||||
|
|
@ -653,8 +775,8 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.",
|
||||
"settings.general.row.font.title": "Kod Yazı Tipi",
|
||||
"settings.general.row.font.description": "Kod bloklarında kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "Terminal yazı tipi",
|
||||
"settings.general.row.terminalFont.description": "Terminalde kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.uiFont.title": "Arayüz Yazı Tipi",
|
||||
"settings.general.row.uiFont.description": "Arayüz genelinde kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.followup.title": "Takip davranışı",
|
||||
|
|
@ -662,6 +784,21 @@ export const dict = {
|
|||
"Takip komutlarının hemen yönlendirilmesini mi yoksa sırada beklemesini mi istediğinizi seçin",
|
||||
"settings.general.row.followup.option.queue": "Sıra",
|
||||
"settings.general.row.followup.option.steer": "Yönlendir",
|
||||
"settings.general.row.showFileTree.title": "Dosya ağacı",
|
||||
"settings.general.row.showFileTree.description": "Oturumlarda dosya ağacı panelini göster",
|
||||
"settings.general.row.showNavigation.title": "Gezinme denetimleri",
|
||||
"settings.general.row.showNavigation.description": "Masaüstü başlık çubuğunda geri ve ileri düğmelerini göster",
|
||||
"settings.general.row.showSearch.title": "Komut paleti",
|
||||
"settings.general.row.showSearch.description": "Başlık çubuğunda arama ve komut paleti düğmesini göster",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Masaüstü başlık çubuğunda terminal düğmesini göster",
|
||||
"settings.general.row.showStatus.title": "Sunucu durumu",
|
||||
"settings.general.row.showStatus.description": "Başlık çubuğunda sunucu durumu düğmesini göster",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Alt gezinme",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Mobil cihazlarda başlık çubuğunu ve oturum sekmelerini ekranın altına yerleştir",
|
||||
"settings.general.row.showCustomAgents.title": "Özel ajanlar",
|
||||
"settings.general.row.showCustomAgents.description": "Yazma alanında ajan seçiciyi göster",
|
||||
"settings.general.row.reasoningSummaries.title": "Akıl yürütme özetlerini göster",
|
||||
"settings.general.row.reasoningSummaries.description": "Zaman çizelgesinde model akıl yürütme özetlerini görüntüle",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Kabuk araç bileşenlerini genişlet",
|
||||
|
|
@ -670,6 +807,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Düzenleme araç bileşenlerini genişlet",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Zaman çizelgesinde düzenleme, yazma ve yama araç bileşenlerini varsayılan olarak genişletilmiş göster",
|
||||
"settings.general.row.newLayoutDesigns.title": "Yeni düzen ve tasarımlar",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Yeniden tasarlanan düzeni, ana sayfayı, yazma alanını ve oturum arayüzünü etkinleştir",
|
||||
"settings.general.row.pinchZoom.title": "Sıkıştırarak yakınlaştır",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"İzleme dörtgeninde sıkıştırma ve Ctrl-kaydırma hareketleriyle yakınlaştırmaya izin ver",
|
||||
|
||||
"settings.general.row.wayland.title": "Yerel Wayland kullan",
|
||||
"settings.general.row.wayland.description":
|
||||
|
|
@ -686,6 +829,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Elle güncelleme kontrolü yap ve varsa yükle",
|
||||
"settings.updates.action.checkNow": "Şimdi kontrol et",
|
||||
"settings.updates.action.checking": "Kontrol ediliyor...",
|
||||
"settings.updates.action.downloading": "İndiriliyor...",
|
||||
"settings.updates.action.installing": "Kuruluyor...",
|
||||
"settings.updates.toast.latest.title": "Güncelsiniz",
|
||||
"settings.updates.toast.latest.description": "OpenCode'un en son sürümünü kullanıyorsunuz.",
|
||||
|
||||
|
|
@ -814,8 +959,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Ada göre bir beceri yükle",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Dil sunucusu sorguları çalıştır",
|
||||
"settings.permissions.tool.todowrite.title": "Görev Yaz",
|
||||
"settings.permissions.tool.todowrite.description": "Görev listesini güncelle",
|
||||
"settings.permissions.tool.webfetch.title": "Web Getir",
|
||||
"settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
|
||||
"settings.permissions.tool.websearch.title": "Web Ara",
|
||||
|
|
@ -880,7 +1023,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "Bu projedeki değişiklikleri takip et, incele ve geri al",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git deposu oluşturuluyor...",
|
||||
"session.review.noVcs.createGit.action": "Git deposu oluştur",
|
||||
"session.todo.progress": "{{total}} görevin {{done}} tanesi tamamlandı",
|
||||
"session.question.progress": "{{total}} sorunun {{current}} tanesi",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Dosya Gezgini",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export const dict = {
|
|||
"command.session.new": "Нова сесія",
|
||||
"command.file.open": "Відкрити файл",
|
||||
"command.tab.close": "Закрити вкладку",
|
||||
"command.tab.reopenClosed": "Повторно відкрити закриту вкладку",
|
||||
"command.context.addSelection": "Додати виділення до контексту",
|
||||
"command.context.addSelection.description": "Додати вибрані рядки з поточного файлу",
|
||||
"command.input.focus": "Фокус на полі введення",
|
||||
|
|
@ -212,6 +213,11 @@ export const dict = {
|
|||
"model.input.video": "відео",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Дозволяє: {{inputs}}",
|
||||
"model.tooltip.context.label": "Контекст",
|
||||
"model.tooltip.inputs": "Вхідні дані",
|
||||
"model.tooltip.model": "Модель",
|
||||
"model.tooltip.provider": "Провайдер",
|
||||
"model.tooltip.reasoning": "Мислення",
|
||||
"model.tooltip.reasoning.allowed": "Підтримує мислення",
|
||||
"model.tooltip.reasoning.none": "Без мислення",
|
||||
"model.tooltip.context": "Ліміт контексту {{limit}}",
|
||||
|
|
@ -222,6 +228,7 @@ export const dict = {
|
|||
"common.loading": "Завантаження",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Скасувати",
|
||||
"common.clear": "Очистити",
|
||||
"common.open": "Відкрити",
|
||||
"common.connect": "Підключити",
|
||||
"common.disconnect": "Відключити",
|
||||
|
|
@ -354,6 +361,75 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "Видалити",
|
||||
"dialog.server.current": "Поточний сервер",
|
||||
"dialog.server.status.default": "За замовчуванням",
|
||||
"wsl.server.add": "Додати сервер WSL",
|
||||
"wsl.server.addShort": "Додати WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "Сервер WSL",
|
||||
"wsl.server.retryStart": "Повторити запуск",
|
||||
"wsl.server.updating": "Оновлення...",
|
||||
"wsl.onboarding.step.distro": "Вибрати дистрибутив",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Перевірка WSL...",
|
||||
"wsl.onboarding.restartRequired": "Щоб завершити встановлення WSL, потрібно перезапустити Windows.",
|
||||
"wsl.onboarding.ready": "WSL готова до роботи.",
|
||||
"wsl.onboarding.required": "Для продовження потрібна WSL.",
|
||||
"wsl.onboarding.checkingDistros": "Перевірка дистрибутивів...",
|
||||
"wsl.onboarding.installingDistro": "Встановлення {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Перевірка {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Отримання списку дистрибутивів...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} готовий до роботи.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ще не встановлено.",
|
||||
"wsl.onboarding.openDistroOnce": "Відкрийте {{distro}} один раз, щоб завершити налаштування.",
|
||||
"wsl.onboarding.finishingDistro": "Завершення налаштування {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Виберіть дистрибутив або встановіть один із наведених нижче.",
|
||||
"wsl.onboarding.checkingOpencode": "Перевірка OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Перевірка OpenCode у {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Оновлення OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Оновлення OpenCode у {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Оновіть OpenCode у {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Оновити OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode готовий до роботи у {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode готовий до роботи.",
|
||||
"wsl.onboarding.installOpencodeIn": "Встановіть OpenCode у {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Встановити OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Спочатку виберіть дистрибутив.",
|
||||
"wsl.onboarding.loadFailed": "Не вдалося завантажити стан WSL.",
|
||||
"wsl.onboarding.loading": "Завантаження...",
|
||||
"wsl.onboarding.installedDistros": "Встановлені дистрибутиви",
|
||||
"wsl.onboarding.checkAgain": "Перевірити ще раз",
|
||||
"wsl.onboarding.distroStatus.ready": "Готовий",
|
||||
"wsl.onboarding.distroStatus.checking": "Перевірка...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode не встановлено",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Немає bash і curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Не підтримується · Використовуйте WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Потрібен інший дистрибутив?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Встановіть дистрибутив Linux із каталогу WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL не встановлено",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"Для додавання сервера WSL в OpenCode потрібна WSL (Підсистема Windows для Linux)",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL недоступна",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode не вдалося перевірити WSL на цьому комп'ютері.",
|
||||
"wsl.onboarding.installWsl": "Встановити WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Перезапустіть Windows, щоб завершити встановлення WSL, а потім знову відкрийте OpenCode.",
|
||||
"wsl.onboarding.next": "Далі",
|
||||
"wsl.onboarding.refresh": "Оновити",
|
||||
"wsl.onboarding.allDistrosAdded": "Усі встановлені дистрибутиви вже додано.",
|
||||
"wsl.onboarding.noDistros": "Дистрибутивів поки не виявлено.",
|
||||
"wsl.onboarding.install": "Встановити",
|
||||
"wsl.onboarding.installing": "Встановлення...",
|
||||
"wsl.onboarding.installDistro": "Встановити дистрибутив",
|
||||
"wsl.onboarding.searchDistros": "Пошук дистрибутивів",
|
||||
"wsl.onboarding.wsl2Required": "Для роботи потрібна WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Для цього дистрибутива потрібні bash і curl.",
|
||||
"wsl.onboarding.openTerminal": "Відкрити термінал",
|
||||
"wsl.onboarding.path": "Шлях: {{path}}",
|
||||
"wsl.onboarding.notFound": "не знайдено",
|
||||
"wsl.onboarding.version": "Версія: {{version}}",
|
||||
"wsl.onboarding.unknown": "невідомо",
|
||||
"wsl.onboarding.desktopVersion": "десктопний застосунок {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Встановлена версія не відповідає версії десктопного застосунку.",
|
||||
"wsl.onboarding.adding": "Додавання...",
|
||||
"server.row.noUsername": "без імені користувача",
|
||||
|
||||
"dialog.project.edit.title": "Редагувати проєкт",
|
||||
|
|
@ -473,10 +549,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "Щось пішло не так",
|
||||
"error.page.description": "Під час завантаження програми сталася помилка.",
|
||||
"error.page.description.localServerStartup": "Під час запуску локального сервера сталася помилка.",
|
||||
"error.page.details.label": "Деталі помилки",
|
||||
"error.page.action.restart": "Перезапустити",
|
||||
"error.page.action.report": "Повідомити про помилку",
|
||||
"error.page.action.reported": "Помилку повідомлено",
|
||||
"error.page.action.exportLogs": "Експортувати журнали",
|
||||
"error.page.action.checking": "Перевірка...",
|
||||
"error.page.action.checkUpdates": "Перевірити оновлення",
|
||||
"error.page.action.updateTo": "Оновити до {{version}}",
|
||||
|
|
@ -533,11 +611,31 @@ export const dict = {
|
|||
"home.recentProjects": "Нещодавні проєкти",
|
||||
"home.empty.title": "Немає нещодавніх проєктів",
|
||||
"home.empty.description": "Почніть, відкривши локальний проєкт",
|
||||
"home.title": "Головна",
|
||||
"home.projects": "Проєкти",
|
||||
"home.project.add": "Додати проєкт",
|
||||
"home.recentlyClosed": "Нещодавно закриті",
|
||||
"home.server.collapse": "Згорнути проєкти сервера",
|
||||
"home.server.expand": "Розгорнути проєкти сервера",
|
||||
"home.sessions.search.placeholder": "Пошук сесій",
|
||||
"home.sessions.search.placeholder.scoped": "Пошук сесій у {{scope}}",
|
||||
"home.sessions.search.sessions": "Сесії",
|
||||
"home.sessions.search.noResults": "Сесій за запитом {{query}} не знайдено",
|
||||
"home.sessions.empty": "Тут поки нічого немає",
|
||||
"home.sessions.empty.description": "Створіть сесію, щоб почати",
|
||||
"home.sessions.group.today": "Сьогодні",
|
||||
"home.sessions.group.yesterday": "Учора",
|
||||
"home.sessions.group.older": "Раніше",
|
||||
|
||||
"session.tab.session": "Сесія",
|
||||
"session.tab.review": "Огляд",
|
||||
"session.tab.context": "Контекст",
|
||||
"session.tab.unknown": "Невідома сесія",
|
||||
"session.panel.reviewAndFiles": "Огляд і файли",
|
||||
"session.error.notFound": "Цю сесію не знайдено",
|
||||
"session.error.notFound.description": "Ця вкладка посилається на сесію, якої більше немає на цьому сервері.",
|
||||
"session.error.notFound.closeTab": "Закрити вкладку",
|
||||
"session.error.serverConnection": "Не вдається підключитися до цього сервера",
|
||||
"session.review.filesChanged": "Змінено файлів: {{count}}",
|
||||
"session.review.change.one": "Зміна",
|
||||
"session.review.change.other": "Зміни",
|
||||
|
|
@ -565,11 +663,11 @@ export const dict = {
|
|||
"session.messages.jumpToLatest": "Перейти до останніх",
|
||||
|
||||
"session.context.addToContext": "Додати {{selection}} до контексту",
|
||||
"session.todo.title": "Завдання",
|
||||
"session.todo.collapse": "Згорнути",
|
||||
"session.todo.expand": "Розгорнути",
|
||||
"session.todo.progress": "Виконано {{done}} з {{total}} завдань",
|
||||
"session.question.progress": "{{current}} з {{total}} запитань",
|
||||
"session.question.minimize": "Згорнути запитання",
|
||||
"session.question.restore": "Відновити запитання",
|
||||
"session.question.pending.one": "{{count}} запитання очікує відповіді",
|
||||
"session.question.pending.other": "Запитання, що очікують відповіді: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} повідомлення в черзі",
|
||||
"session.followupDock.summary.other": "{{count}} повідомлень у черзі",
|
||||
"session.followupDock.sendNow": "Надіслати зараз",
|
||||
|
|
@ -583,9 +681,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "Відновити повідомлення",
|
||||
|
||||
"session.new.title": "Створити що завгодно",
|
||||
"session.new.project.new": "Новий проєкт",
|
||||
"session.new.project.search": "Пошук проєктів",
|
||||
"session.new.project.add": "Додати проєкт",
|
||||
"session.new.worktree.main": "Основна гілка",
|
||||
"session.new.worktree.mainWithBranch": "Основна гілка ({{branch}})",
|
||||
"session.new.worktree.create": "Створити нове робоче дерево",
|
||||
"session.new.workspace.runIn": "Запустити сесію в",
|
||||
"session.new.workspace.triggerLocal": "Локально",
|
||||
"session.new.workspace.local": "Локальний репозиторій",
|
||||
"session.new.workspace.existing": "Робоча область…",
|
||||
"session.new.lastModified": "Востаннє змінено",
|
||||
|
||||
"session.header.search.placeholder": "Пошук {{project}}",
|
||||
|
|
@ -773,6 +878,11 @@ export const dict = {
|
|||
"settings.general.row.showTerminal.description": "Показувати кнопку термінала в заголовку робочого столу",
|
||||
"settings.general.row.showStatus.title": "Статус сервера",
|
||||
"settings.general.row.showStatus.description": "Показувати кнопку статусу сервера в заголовку",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Нижня навігація",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Розмістити заголовок і вкладки сесії внизу екрана на мобільних пристроях",
|
||||
"settings.general.row.showCustomAgents.title": "Користувацькі агенти",
|
||||
"settings.general.row.showCustomAgents.description": "Показувати вибір агента в редакторі запиту",
|
||||
"settings.general.row.reasoningSummaries.title": "Показувати підсумки мислення",
|
||||
"settings.general.row.reasoningSummaries.description": "Відображати підсумки мислення моделі на часовій шкалі",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Розгортати частини інструменту оболонки",
|
||||
|
|
@ -781,6 +891,12 @@ export const dict = {
|
|||
"settings.general.row.editToolPartsExpanded.title": "Розгортати частини інструменту редагування",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Показувати частини інструментів редагування, запису та патчів розгорнутими за замовчуванням на часовій шкалі",
|
||||
"settings.general.row.newLayoutDesigns.title": "Новий макет і дизайн",
|
||||
"settings.general.row.newLayoutDesigns.description":
|
||||
"Увімкнути оновлений макет та інтерфейси головної сторінки, редактора запитів і сесії",
|
||||
"settings.general.row.pinchZoom.title": "Масштабування щипком",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Дозволити масштабування жестом щипка на трекпаді та прокручуванням із Ctrl",
|
||||
|
||||
"settings.general.row.wayland.title": "Використовувати нативний Wayland",
|
||||
"settings.general.row.wayland.description": "Вимкнути резервний X11 на Wayland. Потребує перезапуску.",
|
||||
|
|
@ -796,6 +912,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "Вручну перевірити наявність оновлень і встановити, якщо доступні",
|
||||
"settings.updates.action.checkNow": "Перевірити зараз",
|
||||
"settings.updates.action.checking": "Перевірка...",
|
||||
"settings.updates.action.downloading": "Завантаження...",
|
||||
"settings.updates.action.installing": "Встановлення...",
|
||||
"settings.updates.toast.latest.title": "У вас актуальна версія",
|
||||
"settings.updates.toast.latest.description": "Ви використовуєте останню версію OpenCode.",
|
||||
"sound.option.none": "Немає",
|
||||
|
|
@ -925,8 +1043,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "Завантаження навички за назвою",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Виконання запитів мовного сервера",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "Оновлення списку завдань",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Отримання вмісту з URL",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ export const dict = {
|
|||
"command.sidebar.toggle": "切换侧边栏",
|
||||
|
||||
"command.project.open": "打开项目",
|
||||
"command.project.previous": "上一个项目",
|
||||
"command.project.next": "下一个项目",
|
||||
"command.project.index": "切换到项目 {{index}}",
|
||||
|
||||
"command.provider.connect": "连接提供商",
|
||||
|
||||
|
|
@ -56,6 +59,7 @@ export const dict = {
|
|||
"command.file.open": "打开文件",
|
||||
|
||||
"command.tab.close": "关闭标签页",
|
||||
"command.tab.reopenClosed": "重新打开已关闭的标签页",
|
||||
|
||||
"command.context.addSelection": "将所选内容添加到上下文",
|
||||
"command.context.addSelection.description": "添加当前文件中选中的行",
|
||||
|
|
@ -230,6 +234,11 @@ export const dict = {
|
|||
"model.input.video": "视频",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "支持:{{inputs}}",
|
||||
"model.tooltip.context.label": "上下文",
|
||||
"model.tooltip.inputs": "输入",
|
||||
"model.tooltip.model": "模型",
|
||||
"model.tooltip.provider": "提供商",
|
||||
"model.tooltip.reasoning": "推理",
|
||||
"model.tooltip.reasoning.allowed": "支持推理",
|
||||
"model.tooltip.reasoning.none": "不支持推理",
|
||||
"model.tooltip.context": "上下文上限 {{limit}}",
|
||||
|
|
@ -240,6 +249,7 @@ export const dict = {
|
|||
"common.loading": "加载中",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "取消",
|
||||
"common.clear": "清除",
|
||||
"common.connect": "连接",
|
||||
"common.disconnect": "断开连接",
|
||||
"common.continue": "提交",
|
||||
|
|
@ -257,6 +267,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "按 esc 退出",
|
||||
"session.child.promptDisabled": "无法向子智能体会话发送提示词。",
|
||||
"session.child.backToParent": "返回主会话。",
|
||||
"prompt.example.1": "修复代码库中的一个 TODO",
|
||||
"prompt.example.2": "这个项目的技术栈是什么?",
|
||||
"prompt.example.3": "修复失败的测试",
|
||||
|
|
@ -359,6 +371,73 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "删除",
|
||||
"dialog.server.current": "当前服务器",
|
||||
"dialog.server.status.default": "默认",
|
||||
"wsl.server.add": "添加 WSL 服务器",
|
||||
"wsl.server.addShort": "添加 WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL 服务器",
|
||||
"wsl.server.retryStart": "重试启动",
|
||||
"wsl.server.updating": "正在更新...",
|
||||
"wsl.onboarding.step.distro": "选择发行版",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "正在检查 WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows 需要重启才能完成 WSL 安装。",
|
||||
"wsl.onboarding.ready": "WSL 已就绪。",
|
||||
"wsl.onboarding.required": "需要 WSL 才能继续。",
|
||||
"wsl.onboarding.checkingDistros": "正在检查发行版...",
|
||||
"wsl.onboarding.installingDistro": "正在安装 {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "正在检查 {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "正在列出发行版...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} 已就绪。",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} 尚未安装。",
|
||||
"wsl.onboarding.openDistroOnce": "打开一次 {{distro}} 以完成设置。",
|
||||
"wsl.onboarding.finishingDistro": "正在完成 {{distro}} 的设置。",
|
||||
"wsl.onboarding.pickDistro": "选择一个发行版,或在下方安装。",
|
||||
"wsl.onboarding.checkingOpencode": "正在检查 OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "正在检查 {{distro}} 中的 OpenCode...",
|
||||
"wsl.onboarding.updatingOpencode": "正在更新 OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "正在更新 {{distro}} 中的 OpenCode...",
|
||||
"wsl.onboarding.updateOpencodeIn": "更新 {{distro}} 中的 OpenCode。",
|
||||
"wsl.onboarding.updateOpencode": "更新 OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "{{distro}} 中的 OpenCode 已就绪。",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode 已就绪。",
|
||||
"wsl.onboarding.installOpencodeIn": "在 {{distro}} 中安装 OpenCode。",
|
||||
"wsl.onboarding.installOpencode": "安装 OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "请先选择发行版。",
|
||||
"wsl.onboarding.loadFailed": "加载 WSL 状态失败。",
|
||||
"wsl.onboarding.loading": "加载中...",
|
||||
"wsl.onboarding.installedDistros": "已安装的发行版",
|
||||
"wsl.onboarding.checkAgain": "重新检查",
|
||||
"wsl.onboarding.distroStatus.ready": "已就绪",
|
||||
"wsl.onboarding.distroStatus.checking": "检查中...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "未安装 OpenCode",
|
||||
"wsl.onboarding.distroStatus.missingTools": "缺少 bash、curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "不支持 · 请使用 WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "需要其他发行版?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "从 WSL 目录安装 Linux 发行版",
|
||||
"wsl.onboarding.wslNotInstalled.title": "未安装 WSL",
|
||||
"wsl.onboarding.wslNotInstalled.description": "OpenCode 需要 WSL(Windows Subsystem for Linux)才能添加 WSL 服务器",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL 不可用",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode 无法验证此设备上的 WSL。",
|
||||
"wsl.onboarding.installWsl": "安装 WSL",
|
||||
"wsl.onboarding.windowsRestartRequired": "重启 Windows 以完成 WSL 安装,然后重新打开 OpenCode。",
|
||||
"wsl.onboarding.next": "下一步",
|
||||
"wsl.onboarding.refresh": "刷新",
|
||||
"wsl.onboarding.allDistrosAdded": "所有已安装的发行版均已添加。",
|
||||
"wsl.onboarding.noDistros": "尚未检测到发行版。",
|
||||
"wsl.onboarding.install": "安装",
|
||||
"wsl.onboarding.installing": "正在安装...",
|
||||
"wsl.onboarding.installDistro": "安装发行版",
|
||||
"wsl.onboarding.searchDistros": "搜索发行版",
|
||||
"wsl.onboarding.wsl2Required": "需要 WSL 2。",
|
||||
"wsl.onboarding.toolsRequired": "此发行版需要 bash 和 curl。",
|
||||
"wsl.onboarding.openTerminal": "打开终端",
|
||||
"wsl.onboarding.path": "路径:{{path}}",
|
||||
"wsl.onboarding.notFound": "未找到",
|
||||
"wsl.onboarding.version": "版本:{{version}}",
|
||||
"wsl.onboarding.unknown": "未知",
|
||||
"wsl.onboarding.desktopVersion": "桌面版 {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "已安装版本与桌面应用版本不一致。",
|
||||
"wsl.onboarding.adding": "正在添加...",
|
||||
|
||||
"dialog.project.edit.title": "编辑项目",
|
||||
"dialog.project.edit.name": "名称",
|
||||
|
|
@ -418,6 +497,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -456,10 +536,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "出了点问题",
|
||||
"error.page.description": "加载应用程序时发生错误。",
|
||||
"error.page.description.localServerStartup": "启动本地服务器时发生错误。",
|
||||
"error.page.details.label": "错误详情",
|
||||
"error.page.action.restart": "重启",
|
||||
"error.page.action.report": "上报错误",
|
||||
"error.page.action.reported": "错误已上报",
|
||||
"error.page.action.exportLogs": "导出日志",
|
||||
"error.page.action.checking": "检查中...",
|
||||
"error.page.action.checkUpdates": "检查更新",
|
||||
"error.page.action.updateTo": "更新到 {{version}}",
|
||||
|
|
@ -506,6 +588,9 @@ export const dict = {
|
|||
"home.title": "主页",
|
||||
"home.projects": "项目",
|
||||
"home.project.add": "添加项目",
|
||||
"home.recentlyClosed": "最近关闭",
|
||||
"home.server.collapse": "折叠服务器项目",
|
||||
"home.server.expand": "展开服务器项目",
|
||||
"home.sessions.search.placeholder": "搜索会话",
|
||||
"home.sessions.search.placeholder.scoped": "在 {{scope}} 中搜索会话",
|
||||
"home.sessions.search.sessions": "会话",
|
||||
|
|
@ -519,7 +604,12 @@ export const dict = {
|
|||
"session.tab.session": "会话",
|
||||
"session.tab.review": "审查",
|
||||
"session.tab.context": "上下文",
|
||||
"session.tab.unknown": "未知会话",
|
||||
"session.panel.reviewAndFiles": "审查和文件",
|
||||
"session.error.notFound": "找不到此会话",
|
||||
"session.error.notFound.description": "此标签页指向的会话已不存在于此服务器上。",
|
||||
"session.error.notFound.closeTab": "关闭标签页",
|
||||
"session.error.serverConnection": "无法连接到此服务器",
|
||||
"session.review.filesChanged": "{{count}} 个文件变更",
|
||||
"session.review.change.one": "更改",
|
||||
"session.review.change.other": "更改",
|
||||
|
|
@ -528,6 +618,8 @@ export const dict = {
|
|||
"session.review.noVcs": "未检测到 Git 版本控制系统,无法显示更改",
|
||||
"session.review.noSnapshot": "配置中已禁用快照跟踪,因此会话更改不可用",
|
||||
"session.review.noChanges": "无更改",
|
||||
"session.review.noUncommittedChanges": "尚无未提交的更改",
|
||||
"session.review.noBranchChanges": "尚无分支更改",
|
||||
"session.files.selectToOpen": "选择要打开的文件",
|
||||
"session.files.all": "所有文件",
|
||||
"session.files.empty": "无文件",
|
||||
|
|
@ -538,9 +630,10 @@ export const dict = {
|
|||
"session.messages.loading": "正在加载消息...",
|
||||
"session.messages.jumpToLatest": "跳转到最新",
|
||||
"session.context.addToContext": "将 {{selection}} 添加到上下文",
|
||||
"session.todo.title": "待办事项",
|
||||
"session.todo.collapse": "折叠",
|
||||
"session.todo.expand": "展开",
|
||||
"session.question.minimize": "最小化问题",
|
||||
"session.question.restore": "恢复问题",
|
||||
"session.question.pending.one": "{{count}} 个待处理问题",
|
||||
"session.question.pending.other": "{{count}} 个待处理问题",
|
||||
"session.followupDock.summary.one": "{{count}} 条排队消息",
|
||||
"session.followupDock.summary.other": "{{count}} 条排队消息",
|
||||
"session.followupDock.sendNow": "立即发送",
|
||||
|
|
@ -553,9 +646,16 @@ export const dict = {
|
|||
"session.revertDock.expand": "展开已回滚消息",
|
||||
"session.revertDock.restore": "恢复消息",
|
||||
"session.new.title": "构建任何东西",
|
||||
"session.new.project.new": "新建项目",
|
||||
"session.new.project.search": "搜索项目",
|
||||
"session.new.project.add": "添加项目",
|
||||
"session.new.worktree.main": "主分支",
|
||||
"session.new.worktree.mainWithBranch": "主分支({{branch}})",
|
||||
"session.new.worktree.create": "创建新的 worktree",
|
||||
"session.new.workspace.runIn": "运行会话的位置",
|
||||
"session.new.workspace.triggerLocal": "本地",
|
||||
"session.new.workspace.local": "本地仓库",
|
||||
"session.new.workspace.existing": "工作区…",
|
||||
"session.new.lastModified": "最后修改",
|
||||
"session.header.search.placeholder": "搜索 {{project}}",
|
||||
"session.header.searchFiles": "搜索文件",
|
||||
|
|
@ -623,6 +723,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "最近会话",
|
||||
"sidebar.project.viewAllSessions": "查看全部会话",
|
||||
"sidebar.project.clearNotifications": "清除通知",
|
||||
"sidebar.empty.title": "没有打开的项目",
|
||||
"sidebar.empty.description": "打开一个项目以开始使用",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
|
||||
|
|
@ -637,6 +739,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "在 Windows 的 WSL 环境中运行 OpenCode 服务器。",
|
||||
|
||||
"settings.general.section.appearance": "外观",
|
||||
"settings.general.section.advanced": "高级",
|
||||
"settings.general.section.notifications": "系统通知",
|
||||
"settings.general.section.updates": "更新",
|
||||
"settings.general.section.sounds": "音效",
|
||||
|
|
@ -644,6 +747,10 @@ export const dict = {
|
|||
"settings.general.section.display": "显示",
|
||||
"settings.general.row.language.title": "语言",
|
||||
"settings.general.row.language.description": "更改 OpenCode 的显示语言",
|
||||
"settings.general.row.shell.title": "终端 Shell",
|
||||
"settings.general.row.shell.description": "选择终端使用的 shell。兼容的 shell 也会用于智能体工具调用。",
|
||||
"settings.general.row.shell.autoDefault": "自动(默认)",
|
||||
"settings.general.row.shell.terminalOnly": "仅终端",
|
||||
"settings.general.row.appearance.title": "外观",
|
||||
"settings.general.row.appearance.description": "自定义 OpenCode 在你的设备上的外观",
|
||||
"settings.general.row.colorScheme.title": "配色方案",
|
||||
|
|
@ -652,20 +759,38 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "自定义 OpenCode 的主题。",
|
||||
"settings.general.row.font.title": "代码字体",
|
||||
"settings.general.row.font.description": "自定义代码块使用的字体",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "终端字体",
|
||||
"settings.general.row.terminalFont.description": "自定义终端使用的字体",
|
||||
"settings.general.row.uiFont.title": "界面字体",
|
||||
"settings.general.row.uiFont.description": "自定义整个界面使用的字体",
|
||||
"settings.general.row.followup.title": "跟进消息行为",
|
||||
"settings.general.row.followup.description": "选择跟进提示是立即引导还是在队列中等待",
|
||||
"settings.general.row.followup.option.queue": "排队",
|
||||
"settings.general.row.followup.option.steer": "引导",
|
||||
"settings.general.row.showFileTree.title": "文件树",
|
||||
"settings.general.row.showFileTree.description": "在会话中显示文件树面板",
|
||||
"settings.general.row.showNavigation.title": "导航控件",
|
||||
"settings.general.row.showNavigation.description": "在桌面版标题栏中显示前进和后退按钮",
|
||||
"settings.general.row.showSearch.title": "命令面板",
|
||||
"settings.general.row.showSearch.description": "在标题栏中显示搜索和命令面板按钮",
|
||||
"settings.general.row.showTerminal.title": "终端",
|
||||
"settings.general.row.showTerminal.description": "在桌面版标题栏中显示终端按钮",
|
||||
"settings.general.row.showStatus.title": "服务器状态",
|
||||
"settings.general.row.showStatus.description": "在标题栏中显示服务器状态按钮",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "底部导航",
|
||||
"settings.general.row.mobileTitlebarBottom.description": "在移动设备上将标题栏和会话标签页置于屏幕底部",
|
||||
"settings.general.row.showCustomAgents.title": "自定义智能体",
|
||||
"settings.general.row.showCustomAgents.description": "在输入框中显示智能体选择器",
|
||||
"settings.general.row.reasoningSummaries.title": "显示推理摘要",
|
||||
"settings.general.row.reasoningSummaries.description": "在时间线中显示模型推理摘要",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "展开 shell 工具部分",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "默认在时间线中展开 shell 工具部分",
|
||||
"settings.general.row.editToolPartsExpanded.title": "展开编辑工具部分",
|
||||
"settings.general.row.editToolPartsExpanded.description": "默认在时间线中展开 edit、write 和 patch 工具部分",
|
||||
"settings.general.row.newLayoutDesigns.title": "新版布局和设计",
|
||||
"settings.general.row.newLayoutDesigns.description": "启用重新设计的布局、主页、输入框和会话界面",
|
||||
"settings.general.row.pinchZoom.title": "双指缩放",
|
||||
"settings.general.row.pinchZoom.description": "允许使用触控板双指捏合和 Ctrl-scroll 手势缩放",
|
||||
"settings.general.row.wayland.title": "使用原生 Wayland",
|
||||
"settings.general.row.wayland.description": "在 Wayland 上禁用 X11 回退。需要重启。",
|
||||
"settings.general.row.wayland.tooltip": "在混合刷新率显示器的 Linux 系统上,原生 Wayland 可能更稳定。",
|
||||
|
|
@ -678,6 +803,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "手动检查更新并在有更新时安装",
|
||||
"settings.updates.action.checkNow": "立即检查",
|
||||
"settings.updates.action.checking": "正在检查...",
|
||||
"settings.updates.action.downloading": "正在下载...",
|
||||
"settings.updates.action.installing": "正在安装...",
|
||||
"settings.updates.toast.latest.title": "已是最新版本",
|
||||
"settings.updates.toast.latest.description": "你正在使用最新版本的 OpenCode。",
|
||||
|
||||
|
|
@ -805,8 +932,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "按名称加载技能",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "运行语言服务器查询",
|
||||
"settings.permissions.tool.todowrite.title": "更新待办",
|
||||
"settings.permissions.tool.todowrite.description": "更新待办列表",
|
||||
"settings.permissions.tool.webfetch.title": "网页获取",
|
||||
"settings.permissions.tool.webfetch.description": "从 URL 获取内容",
|
||||
"settings.permissions.tool.websearch.title": "网页搜索",
|
||||
|
|
@ -871,7 +996,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "在此项目中跟踪、审查和撤消更改",
|
||||
"session.review.noVcs.createGit.actionLoading": "正在创建 Git 仓库...",
|
||||
"session.review.noVcs.createGit.action": "创建 Git 仓库",
|
||||
"session.todo.progress": "已完成 {{done}} 个任务(共 {{total}} 个)",
|
||||
"session.question.progress": "{{current}}/{{total}} 个问题",
|
||||
"session.header.open.finder": "访达",
|
||||
"session.header.open.fileExplorer": "文件资源管理器",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ export const dict = {
|
|||
|
||||
"command.sidebar.toggle": "切換側邊欄",
|
||||
"command.project.open": "開啟專案",
|
||||
"command.project.previous": "上一個專案",
|
||||
"command.project.next": "下一個專案",
|
||||
"command.project.index": "切換至專案 {{index}}",
|
||||
"command.provider.connect": "連接提供者",
|
||||
"command.server.switch": "切換伺服器",
|
||||
"command.settings.open": "開啟設定",
|
||||
|
|
@ -49,6 +52,7 @@ export const dict = {
|
|||
"command.session.new": "新增工作階段",
|
||||
"command.file.open": "開啟檔案",
|
||||
"command.tab.close": "關閉分頁",
|
||||
"command.tab.reopenClosed": "重新開啟已關閉的分頁",
|
||||
"command.context.addSelection": "將選取內容加入上下文",
|
||||
"command.context.addSelection.description": "加入目前檔案中選取的行",
|
||||
"command.input.focus": "聚焦輸入框",
|
||||
|
|
@ -209,6 +213,11 @@ export const dict = {
|
|||
"model.input.video": "影片",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "支援: {{inputs}}",
|
||||
"model.tooltip.context.label": "上下文",
|
||||
"model.tooltip.inputs": "輸入",
|
||||
"model.tooltip.model": "模型",
|
||||
"model.tooltip.provider": "供應商",
|
||||
"model.tooltip.reasoning": "推理",
|
||||
"model.tooltip.reasoning.allowed": "支援推理",
|
||||
"model.tooltip.reasoning.none": "不支援推理",
|
||||
"model.tooltip.context": "上下文上限 {{limit}}",
|
||||
|
|
@ -218,6 +227,7 @@ export const dict = {
|
|||
"common.loading": "載入中",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "取消",
|
||||
"common.clear": "清除",
|
||||
"common.connect": "連線",
|
||||
"common.disconnect": "中斷連線",
|
||||
"common.continue": "提交",
|
||||
|
|
@ -235,6 +245,8 @@ export const dict = {
|
|||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "按 esc 退出",
|
||||
"session.child.promptDisabled": "無法在子代理程式工作階段中輸入提示詞。",
|
||||
"session.child.backToParent": "返回主要工作階段。",
|
||||
|
||||
"prompt.example.1": "修復程式碼庫中的一個 TODO",
|
||||
"prompt.example.2": "這個專案的技術堆疊是什麼?",
|
||||
|
|
@ -340,6 +352,73 @@ export const dict = {
|
|||
"dialog.server.menu.delete": "刪除",
|
||||
"dialog.server.current": "目前伺服器",
|
||||
"dialog.server.status.default": "預設",
|
||||
"wsl.server.add": "新增 WSL 伺服器",
|
||||
"wsl.server.addShort": "新增 WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL 伺服器",
|
||||
"wsl.server.retryStart": "重試啟動",
|
||||
"wsl.server.updating": "更新中...",
|
||||
"wsl.onboarding.step.distro": "選擇發行版",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "正在檢查 WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows 需要重新啟動,才能完成 WSL 安裝。",
|
||||
"wsl.onboarding.ready": "WSL 已就緒。",
|
||||
"wsl.onboarding.required": "需要 WSL 才能繼續。",
|
||||
"wsl.onboarding.checkingDistros": "正在檢查發行版...",
|
||||
"wsl.onboarding.installingDistro": "正在安裝 {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "正在檢查 {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "正在列出發行版...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} 已就緒。",
|
||||
"wsl.onboarding.distroNotInstalled": "尚未安裝 {{distro}}。",
|
||||
"wsl.onboarding.openDistroOnce": "開啟 {{distro}} 一次以完成設定。",
|
||||
"wsl.onboarding.finishingDistro": "正在完成 {{distro}} 的設定。",
|
||||
"wsl.onboarding.pickDistro": "選擇發行版,或在下方安裝。",
|
||||
"wsl.onboarding.checkingOpencode": "正在檢查 OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "正在檢查 {{distro}} 中的 OpenCode...",
|
||||
"wsl.onboarding.updatingOpencode": "正在更新 OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "正在更新 {{distro}} 中的 OpenCode...",
|
||||
"wsl.onboarding.updateOpencodeIn": "更新 {{distro}} 中的 OpenCode。",
|
||||
"wsl.onboarding.updateOpencode": "更新 OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "{{distro}} 中的 OpenCode 已就緒。",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode 已就緒。",
|
||||
"wsl.onboarding.installOpencodeIn": "在 {{distro}} 中安裝 OpenCode。",
|
||||
"wsl.onboarding.installOpencode": "安裝 OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "請先選擇發行版。",
|
||||
"wsl.onboarding.loadFailed": "無法載入 WSL 狀態。",
|
||||
"wsl.onboarding.loading": "載入中...",
|
||||
"wsl.onboarding.installedDistros": "已安裝的發行版",
|
||||
"wsl.onboarding.checkAgain": "再次檢查",
|
||||
"wsl.onboarding.distroStatus.ready": "就緒",
|
||||
"wsl.onboarding.distroStatus.checking": "檢查中...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "未安裝 OpenCode",
|
||||
"wsl.onboarding.distroStatus.missingTools": "缺少 bash、curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "不支援 · 請使用 WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "需要其他發行版?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "從 WSL 目錄安裝 Linux 發行版",
|
||||
"wsl.onboarding.wslNotInstalled.title": "未安裝 WSL",
|
||||
"wsl.onboarding.wslNotInstalled.description": "新增 WSL 伺服器前,必須先安裝 WSL(Windows Subsystem for Linux)",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL 無法使用",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode 無法確認此電腦上的 WSL。",
|
||||
"wsl.onboarding.installWsl": "安裝 WSL",
|
||||
"wsl.onboarding.windowsRestartRequired": "重新啟動 Windows 以完成 WSL 安裝,然後重新開啟 OpenCode。",
|
||||
"wsl.onboarding.next": "下一步",
|
||||
"wsl.onboarding.refresh": "重新整理",
|
||||
"wsl.onboarding.allDistrosAdded": "所有已安裝的發行版都已新增。",
|
||||
"wsl.onboarding.noDistros": "尚未偵測到發行版。",
|
||||
"wsl.onboarding.install": "安裝",
|
||||
"wsl.onboarding.installing": "安裝中...",
|
||||
"wsl.onboarding.installDistro": "安裝發行版",
|
||||
"wsl.onboarding.searchDistros": "搜尋發行版",
|
||||
"wsl.onboarding.wsl2Required": "需要 WSL 2。",
|
||||
"wsl.onboarding.toolsRequired": "此發行版需要 bash 和 curl。",
|
||||
"wsl.onboarding.openTerminal": "開啟終端機",
|
||||
"wsl.onboarding.path": "路徑: {{path}}",
|
||||
"wsl.onboarding.notFound": "找不到",
|
||||
"wsl.onboarding.version": "版本: {{version}}",
|
||||
"wsl.onboarding.unknown": "未知",
|
||||
"wsl.onboarding.desktopVersion": "桌面版 {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "已安裝版本與桌面應用程式版本不符。",
|
||||
"wsl.onboarding.adding": "新增中...",
|
||||
|
||||
"dialog.project.edit.title": "編輯專案",
|
||||
"dialog.project.edit.name": "名稱",
|
||||
|
|
@ -402,6 +481,7 @@ export const dict = {
|
|||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
|
|
@ -449,10 +529,12 @@ export const dict = {
|
|||
|
||||
"error.page.title": "出了點問題",
|
||||
"error.page.description": "載入應用程式時發生錯誤。",
|
||||
"error.page.description.localServerStartup": "啟動本機伺服器時發生錯誤。",
|
||||
"error.page.details.label": "錯誤詳情",
|
||||
"error.page.action.restart": "重新啟動",
|
||||
"error.page.action.report": "回報錯誤",
|
||||
"error.page.action.reported": "已回報錯誤",
|
||||
"error.page.action.exportLogs": "匯出日誌",
|
||||
"error.page.action.checking": "檢查中...",
|
||||
"error.page.action.checkUpdates": "檢查更新",
|
||||
"error.page.action.updateTo": "更新到 {{version}}",
|
||||
|
|
@ -498,17 +580,39 @@ export const dict = {
|
|||
"home.recentProjects": "最近專案",
|
||||
"home.empty.title": "沒有最近專案",
|
||||
"home.empty.description": "透過開啟本地專案開始使用",
|
||||
"home.title": "首頁",
|
||||
"home.projects": "專案",
|
||||
"home.project.add": "新增專案",
|
||||
"home.recentlyClosed": "最近關閉",
|
||||
"home.server.collapse": "收合伺服器專案",
|
||||
"home.server.expand": "展開伺服器專案",
|
||||
"home.sessions.search.placeholder": "搜尋工作階段",
|
||||
"home.sessions.search.placeholder.scoped": "在 {{scope}} 中搜尋工作階段",
|
||||
"home.sessions.search.sessions": "工作階段",
|
||||
"home.sessions.search.noResults": "找不到符合 {{query}} 的工作階段",
|
||||
"home.sessions.empty": "這裡還沒有內容",
|
||||
"home.sessions.empty.description": "建立工作階段以開始使用",
|
||||
"home.sessions.group.today": "今天",
|
||||
"home.sessions.group.yesterday": "昨天",
|
||||
"home.sessions.group.older": "更早",
|
||||
|
||||
"session.tab.session": "工作階段",
|
||||
"session.tab.review": "審查",
|
||||
"session.tab.context": "上下文",
|
||||
"session.tab.unknown": "未知的工作階段",
|
||||
"session.panel.reviewAndFiles": "審查與檔案",
|
||||
"session.error.notFound": "找不到此工作階段",
|
||||
"session.error.notFound.description": "此分頁指向的工作階段已不存在於此伺服器。",
|
||||
"session.error.notFound.closeTab": "關閉分頁",
|
||||
"session.error.serverConnection": "無法連線至此伺服器",
|
||||
"session.review.filesChanged": "{{count}} 個檔案變更",
|
||||
"session.review.change.one": "變更",
|
||||
"session.review.change.other": "變更",
|
||||
"session.review.loadingChanges": "正在載入變更...",
|
||||
"session.review.empty": "此工作階段暫無變更",
|
||||
"session.review.noChanges": "沒有變更",
|
||||
"session.review.noUncommittedChanges": "尚無未提交的變更",
|
||||
"session.review.noBranchChanges": "尚無分支變更",
|
||||
"session.review.noVcs": "未偵測到 Git 版本控制系統,無法顯示變更",
|
||||
"session.review.noSnapshot": "設定中已停用快照追蹤,因此無法使用工作階段變更",
|
||||
"session.files.selectToOpen": "選取要開啟的檔案",
|
||||
|
|
@ -522,9 +626,10 @@ export const dict = {
|
|||
|
||||
"session.messages.jumpToLatest": "跳到最新",
|
||||
"session.context.addToContext": "將 {{selection}} 新增到上下文",
|
||||
"session.todo.title": "待辦事項",
|
||||
"session.todo.collapse": "折疊",
|
||||
"session.todo.expand": "展開",
|
||||
"session.question.minimize": "最小化問題",
|
||||
"session.question.restore": "還原問題",
|
||||
"session.question.pending.one": "{{count}} 個待處理問題",
|
||||
"session.question.pending.other": "{{count}} 個待處理問題",
|
||||
"session.followupDock.summary.one": "{{count}} 則佇列訊息",
|
||||
"session.followupDock.summary.other": "{{count}} 則佇列訊息",
|
||||
"session.followupDock.sendNow": "立即傳送",
|
||||
|
|
@ -538,9 +643,16 @@ export const dict = {
|
|||
"session.revertDock.restore": "還原訊息",
|
||||
|
||||
"session.new.title": "建構任何東西",
|
||||
"session.new.project.new": "新專案",
|
||||
"session.new.project.search": "搜尋專案",
|
||||
"session.new.project.add": "新增專案",
|
||||
"session.new.worktree.main": "主分支",
|
||||
"session.new.worktree.mainWithBranch": "主分支 ({{branch}})",
|
||||
"session.new.worktree.create": "建立新的 worktree",
|
||||
"session.new.workspace.runIn": "工作階段執行位置",
|
||||
"session.new.workspace.triggerLocal": "本機",
|
||||
"session.new.workspace.local": "本機儲存庫",
|
||||
"session.new.workspace.existing": "工作區…",
|
||||
"session.new.lastModified": "最後修改",
|
||||
|
||||
"session.header.search.placeholder": "搜尋 {{project}}",
|
||||
|
|
@ -608,6 +720,8 @@ export const dict = {
|
|||
"sidebar.project.recentSessions": "最近工作階段",
|
||||
"sidebar.project.viewAllSessions": "查看全部工作階段",
|
||||
"sidebar.project.clearNotifications": "清除通知",
|
||||
"sidebar.empty.title": "未開啟任何專案",
|
||||
"sidebar.empty.description": "開啟專案以開始使用",
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "桌面",
|
||||
|
|
@ -619,6 +733,7 @@ export const dict = {
|
|||
"settings.desktop.wsl.description": "在 Windows 上的 WSL 中執行 OpenCode 伺服器。",
|
||||
|
||||
"settings.general.section.appearance": "外觀",
|
||||
"settings.general.section.advanced": "進階",
|
||||
"settings.general.section.notifications": "系統通知",
|
||||
"settings.general.section.updates": "更新",
|
||||
"settings.general.section.sounds": "音效",
|
||||
|
|
@ -627,6 +742,10 @@ export const dict = {
|
|||
|
||||
"settings.general.row.language.title": "語言",
|
||||
"settings.general.row.language.description": "變更 OpenCode 的顯示語言",
|
||||
"settings.general.row.shell.title": "終端機 Shell",
|
||||
"settings.general.row.shell.description": "選擇終端機使用的 Shell。相容的 Shell 也會用於代理程式工具呼叫。",
|
||||
"settings.general.row.shell.autoDefault": "自動(預設)",
|
||||
"settings.general.row.shell.terminalOnly": "僅限終端機",
|
||||
"settings.general.row.appearance.title": "外觀",
|
||||
"settings.general.row.appearance.description": "自訂 OpenCode 在你的裝置上的外觀",
|
||||
"settings.general.row.colorScheme.title": "配色方案",
|
||||
|
|
@ -635,14 +754,28 @@ export const dict = {
|
|||
"settings.general.row.theme.description": "自訂 OpenCode 的主題。",
|
||||
"settings.general.row.font.title": "程式碼字型",
|
||||
"settings.general.row.font.description": "自訂程式碼區塊使用的字型",
|
||||
"settings.general.row.terminalFont.title": "Terminal Font",
|
||||
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
|
||||
"settings.general.row.terminalFont.title": "終端機字型",
|
||||
"settings.general.row.terminalFont.description": "自訂終端機使用的字型",
|
||||
"settings.general.row.uiFont.title": "介面字型",
|
||||
"settings.general.row.uiFont.description": "自訂整個介面使用的字型",
|
||||
"settings.general.row.followup.title": "後續追問行為",
|
||||
"settings.general.row.followup.description": "選擇後續追問提示是立即引導還是進入佇列等待",
|
||||
"settings.general.row.followup.option.queue": "佇列",
|
||||
"settings.general.row.followup.option.steer": "引導",
|
||||
"settings.general.row.showFileTree.title": "檔案樹",
|
||||
"settings.general.row.showFileTree.description": "在工作階段中顯示檔案樹面板",
|
||||
"settings.general.row.showNavigation.title": "導覽控制項",
|
||||
"settings.general.row.showNavigation.description": "在桌面標題列中顯示上一頁和下一頁按鈕",
|
||||
"settings.general.row.showSearch.title": "命令面板",
|
||||
"settings.general.row.showSearch.description": "在標題列中顯示搜尋和命令面板按鈕",
|
||||
"settings.general.row.showTerminal.title": "終端機",
|
||||
"settings.general.row.showTerminal.description": "在桌面標題列中顯示終端機按鈕",
|
||||
"settings.general.row.showStatus.title": "伺服器狀態",
|
||||
"settings.general.row.showStatus.description": "在標題列中顯示伺服器狀態按鈕",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "底部導覽",
|
||||
"settings.general.row.mobileTitlebarBottom.description": "在行動裝置上將標題列和工作階段分頁置於畫面底部",
|
||||
"settings.general.row.showCustomAgents.title": "自訂代理程式",
|
||||
"settings.general.row.showCustomAgents.description": "在輸入區顯示代理程式選擇器",
|
||||
"settings.general.row.reasoningSummaries.title": "顯示推理摘要",
|
||||
"settings.general.row.reasoningSummaries.description": "在時間軸中顯示模型推理摘要",
|
||||
|
||||
|
|
@ -650,6 +783,10 @@ export const dict = {
|
|||
"settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊",
|
||||
"settings.general.row.editToolPartsExpanded.title": "展開 edit 工具區塊",
|
||||
"settings.general.row.editToolPartsExpanded.description": "在時間軸中預設展開 edit、write 和 patch 工具區塊",
|
||||
"settings.general.row.newLayoutDesigns.title": "新版面與設計",
|
||||
"settings.general.row.newLayoutDesigns.description": "啟用重新設計的版面、首頁、輸入區和工作階段介面",
|
||||
"settings.general.row.pinchZoom.title": "雙指縮放",
|
||||
"settings.general.row.pinchZoom.description": "允許使用觸控板雙指開合和 Ctrl-捲動手勢縮放",
|
||||
"settings.general.row.wayland.title": "使用原生 Wayland",
|
||||
"settings.general.row.wayland.description": "在 Wayland 上停用 X11 後備模式。需要重新啟動。",
|
||||
"settings.general.row.wayland.tooltip": "在混合更新率螢幕的 Linux 系統上,原生 Wayland 可能更穩定。",
|
||||
|
|
@ -663,6 +800,8 @@ export const dict = {
|
|||
"settings.updates.row.check.description": "手動檢查更新並在有更新時安裝",
|
||||
"settings.updates.action.checkNow": "立即檢查",
|
||||
"settings.updates.action.checking": "檢查中...",
|
||||
"settings.updates.action.downloading": "下載中...",
|
||||
"settings.updates.action.installing": "安裝中...",
|
||||
"settings.updates.toast.latest.title": "已是最新版本",
|
||||
"settings.updates.toast.latest.description": "你正在使用最新版本的 OpenCode。",
|
||||
|
||||
|
|
@ -789,8 +928,6 @@ export const dict = {
|
|||
"settings.permissions.tool.skill.description": "按名稱載入技能",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "執行語言伺服器查詢",
|
||||
"settings.permissions.tool.todowrite.title": "更新待辦",
|
||||
"settings.permissions.tool.todowrite.description": "更新待辦清單",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "從 URL 取得內容",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
|
|
@ -855,7 +992,6 @@ export const dict = {
|
|||
"session.review.noVcs.createGit.description": "追蹤、檢閱及復原此專案中的變更",
|
||||
"session.review.noVcs.createGit.actionLoading": "正在建立 Git 儲存庫...",
|
||||
"session.review.noVcs.createGit.action": "建立 Git 儲存庫",
|
||||
"session.todo.progress": "已完成 {{done}} 個待辦事項(共 {{total}} 個)",
|
||||
"session.question.progress": "{{current}}/{{total}} 個問題",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "檔案總管",
|
||||
|
|
|
|||
|
|
@ -224,7 +224,26 @@
|
|||
|
||||
[data-slot="model-selector-scroll"]::before {
|
||||
top: 0;
|
||||
background: linear-gradient(to bottom, var(--v2-background-bg-layer-01), transparent);
|
||||
height: 12px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 80%, transparent) 0%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 78.3336%, transparent) 4.7%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 76.4824%, transparent) 8.9%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 74.368%, transparent) 12.8%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 71.9123%, transparent) 16.56%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 69.037%, transparent) 20.37%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 65.664%, transparent) 24.4%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 61.715%, transparent) 28.83%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 57.1117%, transparent) 33.84%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 51.776%, transparent) 39.6%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 45.6296%, transparent) 46.3%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 38.5944%, transparent) 54.1%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 30.592%, transparent) 63.2%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 21.5443%, transparent) 73.76%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 11.373%, transparent) 85.97%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 0%, transparent) 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-slot="manage-models-scroll"]::before {
|
||||
|
|
@ -239,7 +258,26 @@
|
|||
|
||||
[data-slot="model-selector-scroll"]::after {
|
||||
bottom: 0;
|
||||
background: linear-gradient(to top, var(--v2-background-bg-layer-01), transparent);
|
||||
height: 12px;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 80%, transparent) 0%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 78.3336%, transparent) 4.7%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 76.4824%, transparent) 8.9%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 74.368%, transparent) 12.8%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 71.9123%, transparent) 16.56%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 69.037%, transparent) 20.37%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 65.664%, transparent) 24.4%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 61.715%, transparent) 28.83%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 57.1117%, transparent) 33.84%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 51.776%, transparent) 39.6%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 45.6296%, transparent) 46.3%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 38.5944%, transparent) 54.1%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 30.592%, transparent) 63.2%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 21.5443%, transparent) 73.76%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 11.373%, transparent) 85.97%,
|
||||
color-mix(in srgb, var(--v2-background-bg-layer-01) 0%, transparent) 100%
|
||||
);
|
||||
}
|
||||
|
||||
@supports (animation-timeline: --home-projects-scroll) and (timeline-scope: --home-projects-scroll) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { usePlatform } from "@/context/platform"
|
|||
import { DateTime } from "luxon"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { useDirectoryPicker } from "@/components/directory-picker"
|
||||
import { useSettingsCommand } from "@/components/settings-dialog"
|
||||
import { useSettingsDialog } from "@/components/settings-dialog"
|
||||
import { DialogSelectServer, useServerManagementController } from "@/components/dialog-select-server"
|
||||
import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
|
||||
import { ServerConnection, serverName, useServer } from "@/context/server"
|
||||
|
|
@ -267,7 +267,7 @@ export function NewHome() {
|
|||
const command = useCommand()
|
||||
const notification = useNotification()
|
||||
const marked = useMarked()
|
||||
const openSettings = useSettingsCommand()
|
||||
const openSettings = useSettingsDialog()
|
||||
let focusSessionSearch: (() => void) | undefined
|
||||
const [state, setState] = createStore({
|
||||
search: "",
|
||||
|
|
@ -1278,7 +1278,7 @@ function HomeSessionSearch(props: {
|
|||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<label class="relative z-20 flex h-9 w-full items-center gap-2 rounded-[6px] bg-v2-background-bg-layer-02/60 py-1 pl-3 pr-2 text-v2-icon-icon-muted transition-[background-color,box-shadow] duration-[120ms] ease-in-out hover:bg-v2-background-bg-layer-02">
|
||||
<label class="relative z-20 flex h-9 w-full items-center gap-2 rounded-[6px] bg-v2-background-bg-layer-02/60 py-1 pl-3 pr-2 text-v2-icon-icon-muted transition-[background-color,box-shadow] duration-[120ms] ease-in-out hover:bg-v2-background-bg-layer-02 focus-within:bg-v2-background-bg-layer-02">
|
||||
<IconV2 name="magnifying-glass" />
|
||||
<input
|
||||
ref={input}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
|
|||
import { usePlatform } from "@/context/platform"
|
||||
import { setNavigate } from "@/utils/notification-click"
|
||||
import { setV2Toast, ToastRegion } from "@/utils/toast"
|
||||
import { useSettingsCommand } from "@/components/settings-dialog"
|
||||
|
||||
export default function NewLayout(props: ParentProps) {
|
||||
const platform = usePlatform()
|
||||
const navigate = useNavigate()
|
||||
setNavigate(navigate)
|
||||
useSettingsCommand()
|
||||
|
||||
createEffect(() => setV2Toast(true))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { createMemo, type Accessor } from "solid-js"
|
|||
import { useGlobal } from "@/context/global"
|
||||
import { useNotification } from "@/context/notification"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { sessionPermissionRequest, sessionQuestionRequest } from "@/pages/session/composer/session-request-tree"
|
||||
import { ServerConnection } from "@/context/server"
|
||||
import { sessionPermissionRequest } from "@/pages/session/composer/session-request-tree"
|
||||
|
||||
export function useSessionTabAvatarState(
|
||||
server: Accessor<ServerConnection.Key>,
|
||||
|
|
@ -26,15 +26,20 @@ export function useSessionTabAvatarState(
|
|||
return !permission.autoResponds(item, directory())
|
||||
})
|
||||
})
|
||||
const unread = createMemo(() => {
|
||||
if (hasPermissions()) return true
|
||||
if (!connection()) return false
|
||||
return notification.ensureServerState(server()).session.unseenCount(sessionId()) > 0
|
||||
const hasQuestions = createMemo(() => {
|
||||
const serverSync = sync()
|
||||
if (!serverSync) return false
|
||||
const [store] = serverSync.child(directory(), { bootstrap: false })
|
||||
return !!sessionQuestionRequest(store.session, serverSync.session.data.question, sessionId())
|
||||
})
|
||||
const needsAttention = createMemo(() => hasPermissions() || hasQuestions())
|
||||
const unread = createMemo(
|
||||
() => needsAttention() || notification.ensureServerState(server()).session.unseenCount(sessionId()) > 0,
|
||||
)
|
||||
const loading = createMemo(() => {
|
||||
const serverSync = sync()
|
||||
if (!serverSync) return false
|
||||
if (hasPermissions()) return false
|
||||
if (needsAttention()) return false
|
||||
return serverSync.session.data.session_working(sessionId())
|
||||
})
|
||||
return { unread, loading }
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { Tooltip } from "@opencode-ai/ui/tooltip"
|
|||
import { NewSessionDesignView } from "@/components/session"
|
||||
import { PromptInput } from "@/components/prompt-input"
|
||||
import { StatusPopoverV2 } from "@/components/status-popover"
|
||||
import { useSettingsCommand } from "@/components/settings-dialog"
|
||||
import {
|
||||
PromptProjectAddButton,
|
||||
PromptProjectSelector,
|
||||
|
|
@ -45,7 +44,6 @@ export default function NewSessionPage() {
|
|||
const [searchParams, setSearchParams] = useSearchParams<{ draftId?: string; prompt?: string }>()
|
||||
|
||||
useComposerCommands()
|
||||
useSettingsCommand()
|
||||
|
||||
let inputRef: HTMLDivElement | undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { previewSelectedLines } from "@opencode-ai/session-ui/pierre/selection-b
|
|||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { base64Encode, checksum } from "@opencode-ai/core/util/encode"
|
||||
import { Navigate, useLocation, useNavigate, useParams, useSearchParams } from "@solidjs/router"
|
||||
import { useLocation, useNavigate, useParams, useSearchParams } from "@solidjs/router"
|
||||
import { NewSessionView, SessionHeader } from "@/components/session"
|
||||
import { ErrorPage } from "@/pages/error"
|
||||
import { CommentsProvider, useComments } from "@/context/comments"
|
||||
|
|
@ -56,7 +56,6 @@ import { useSync } from "@/context/sync"
|
|||
import { useTabs } from "@/context/tabs"
|
||||
import { TerminalProvider, useTerminal } from "@/context/terminal"
|
||||
import { PromptInput } from "@/components/prompt-input"
|
||||
import { useSettingsCommand } from "@/components/settings-dialog"
|
||||
import { type FollowupDraft, sendFollowupDraft } from "@/components/prompt-input/submit"
|
||||
import {
|
||||
createPromptInputController,
|
||||
|
|
@ -153,7 +152,7 @@ export function TargetSessionRouteContent() {
|
|||
)
|
||||
}
|
||||
|
||||
function SessionRouteErrorBoundary(
|
||||
export function SessionRouteErrorBoundary(
|
||||
props: ParentProps<{ sessionID?: string; serverKey?: ServerConnection.Key; padded?: boolean }>,
|
||||
) {
|
||||
const settings = useSettings()
|
||||
|
|
@ -221,7 +220,6 @@ function SessionErrorFallback(props: { error: unknown; sessionID?: string; serve
|
|||
|
||||
function ResolvedTargetSessionRoute() {
|
||||
const params = useParams<{ serverKey: string; id: string }>()
|
||||
const settings = useSettings()
|
||||
const tabs = useTabs()
|
||||
const sync = useServerSync()
|
||||
const serverKey = createMemo(() => requireServerKey(params.serverKey))
|
||||
|
|
@ -248,16 +246,11 @@ function ResolvedTargetSessionRoute() {
|
|||
the terminal. Same-workspace tab switches keep it open because warm
|
||||
targets resolve synchronously from the sync cache. */}
|
||||
<Show when={directory()}>
|
||||
<Show
|
||||
when={settings.general.newLayoutDesigns()}
|
||||
fallback={<Navigate href={legacySessionHref(directory()!, params.id)} />}
|
||||
>
|
||||
<SDKProvider directory={targetDirectory}>
|
||||
<DirectoryDataProvider directory={targetDirectory} server={serverKey}>
|
||||
<TargetSessionPage />
|
||||
</DirectoryDataProvider>
|
||||
</SDKProvider>
|
||||
</Show>
|
||||
<SDKProvider directory={targetDirectory}>
|
||||
<DirectoryDataProvider directory={targetDirectory} server={serverKey}>
|
||||
<TargetSessionPage />
|
||||
</DirectoryDataProvider>
|
||||
</SDKProvider>
|
||||
</Show>
|
||||
</TargetServerScopedProviders>
|
||||
)
|
||||
|
|
@ -572,8 +565,6 @@ export default function Page() {
|
|||
})
|
||||
|
||||
let reviewFrame: number | undefined
|
||||
let todoFrame: number | undefined
|
||||
let todoTimer: number | undefined
|
||||
let diffFrame: number | undefined
|
||||
let diffTimer: number | undefined
|
||||
|
||||
|
|
@ -780,41 +771,6 @@ export default function Page() {
|
|||
|
||||
const hasScrollGesture = () => Date.now() - ui.scrollGesture < scrollGestureWindowMs
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => {
|
||||
const id = params.id
|
||||
return [
|
||||
sdk().directory,
|
||||
id,
|
||||
id ? (sync().data.session_status[id]?.type ?? "idle") : "idle",
|
||||
id ? composer.blocked() : false,
|
||||
] as const
|
||||
},
|
||||
([dir, id, status, blocked]) => {
|
||||
if (todoFrame !== undefined) cancelAnimationFrame(todoFrame)
|
||||
if (todoTimer !== undefined) window.clearTimeout(todoTimer)
|
||||
todoFrame = undefined
|
||||
todoTimer = undefined
|
||||
if (!id) return
|
||||
if (status === "idle" && !blocked) return
|
||||
const cached = untrack(() => sync().data.todo[id] !== undefined)
|
||||
|
||||
todoFrame = requestAnimationFrame(() => {
|
||||
todoFrame = undefined
|
||||
todoTimer = window.setTimeout(() => {
|
||||
todoTimer = undefined
|
||||
if (sdk().directory !== dir || params.id !== id) return
|
||||
untrack(() => {
|
||||
void sync().session.todo(id, cached ? { force: true } : undefined)
|
||||
})
|
||||
}, 0)
|
||||
})
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => visibleUserMessages().at(-1)?.id,
|
||||
|
|
@ -1021,7 +977,6 @@ export default function Page() {
|
|||
}
|
||||
|
||||
useComposerCommands()
|
||||
useSettingsCommand()
|
||||
useSessionCommands({
|
||||
navigateMessageByOffset,
|
||||
setActiveMessage,
|
||||
|
|
@ -1890,8 +1845,6 @@ export default function Page() {
|
|||
|
||||
onCleanup(() => {
|
||||
if (reviewFrame !== undefined) cancelAnimationFrame(reviewFrame)
|
||||
if (todoFrame !== undefined) cancelAnimationFrame(todoFrame)
|
||||
if (todoTimer !== undefined) window.clearTimeout(todoTimer)
|
||||
if (diffFrame !== undefined) cancelAnimationFrame(diffFrame)
|
||||
if (diffTimer !== undefined) window.clearTimeout(diffTimer)
|
||||
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
|
||||
|
|
@ -1906,12 +1859,7 @@ export default function Page() {
|
|||
sessionKey,
|
||||
sessionID: () => params.id,
|
||||
prompt,
|
||||
ready: () => !store.deferRender && messagesReady(),
|
||||
centered,
|
||||
todo: {
|
||||
collapsed: () => view().todoCollapsed.get(),
|
||||
onToggle: () => view().todoCollapsed.set(!view().todoCollapsed.get()),
|
||||
},
|
||||
followup: () =>
|
||||
params.id && !isChildSession()
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
||||
import { type Accessor, createEffect, createMemo, createResource, onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { type Accessor, createEffect, createMemo, createResource } from "solid-js"
|
||||
import type { PromptInputState } from "@/components/prompt-input"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff"
|
||||
|
|
@ -26,12 +23,7 @@ export function createSessionComposerRegionController(input: {
|
|||
sessionKey: Accessor<string>
|
||||
sessionID: Accessor<string | undefined>
|
||||
prompt: PromptInputState
|
||||
ready: Accessor<boolean>
|
||||
centered: Accessor<boolean>
|
||||
todo: {
|
||||
collapsed: Accessor<boolean>
|
||||
onToggle: () => void
|
||||
}
|
||||
followup: Accessor<SessionComposerFollowupDock | undefined>
|
||||
revert: Accessor<SessionComposerRevertDock | undefined>
|
||||
onResponseSubmit: () => void
|
||||
|
|
@ -40,41 +32,6 @@ export function createSessionComposerRegionController(input: {
|
|||
setDockRef: (el: HTMLDivElement) => void
|
||||
}) {
|
||||
const sync = useSync()
|
||||
const [store, setStore] = createStore({
|
||||
ready: input.ready() || input.state.dock(),
|
||||
height: 320,
|
||||
body: undefined as HTMLDivElement | undefined,
|
||||
})
|
||||
let timer: number | undefined
|
||||
let frame: number | undefined
|
||||
|
||||
const clear = () => {
|
||||
if (timer !== undefined) window.clearTimeout(timer)
|
||||
if (frame !== undefined) cancelAnimationFrame(frame)
|
||||
timer = undefined
|
||||
frame = undefined
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
input.sessionKey()
|
||||
const ready = input.ready()
|
||||
const dock = input.state.dock()
|
||||
|
||||
clear()
|
||||
if (store.ready || (!ready && !dock)) return
|
||||
if (dock) {
|
||||
setStore("ready", true)
|
||||
return
|
||||
}
|
||||
|
||||
frame = requestAnimationFrame(() => {
|
||||
frame = undefined
|
||||
timer = window.setTimeout(() => {
|
||||
setStore("ready", true)
|
||||
timer = undefined
|
||||
}, 140)
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!input.prompt.ready()) return
|
||||
|
|
@ -92,27 +49,10 @@ export function createSessionComposerRegionController(input: {
|
|||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const el = store.body
|
||||
if (!el) return
|
||||
const update = () => setStore("height", el.getBoundingClientRect().height)
|
||||
createResizeObserver(el, update)
|
||||
update()
|
||||
})
|
||||
|
||||
onCleanup(clear)
|
||||
|
||||
const parentID = createMemo(() => {
|
||||
const id = input.sessionID()
|
||||
return id ? sync().session.get(id)?.parentID : undefined
|
||||
})
|
||||
const open = createMemo(() => store.ready && input.state.dock() && !input.state.closing())
|
||||
const progress = useSpring(
|
||||
() => (open() ? 1 : 0),
|
||||
{ visualDuration: 0.3, bounce: 0 },
|
||||
() => `${input.sessionKey()}\0${store.ready}`,
|
||||
)
|
||||
const value = createMemo(() => Math.max(0, Math.min(1, progress())))
|
||||
const ready = Promise.resolve()
|
||||
const [promptReady] = createResource(
|
||||
() => input.prompt.ready.promise ?? ready,
|
||||
|
|
@ -122,7 +62,6 @@ export function createSessionComposerRegionController(input: {
|
|||
return {
|
||||
state: input.state,
|
||||
centered: input.centered,
|
||||
todo: input.todo,
|
||||
followup: input.followup,
|
||||
revert: input.revert,
|
||||
onResponseSubmit: input.onResponseSubmit,
|
||||
|
|
@ -134,11 +73,7 @@ export function createSessionComposerRegionController(input: {
|
|||
showComposer: () => !input.state.blocked() || !!parentID(),
|
||||
handoffPrompt: () => getSessionHandoff(input.sessionKey())?.prompt,
|
||||
promptReady: () => input.prompt.ready() || promptReady(),
|
||||
dock: () => (store.ready && input.state.dock()) || value() > 0.001,
|
||||
dockProgress: value,
|
||||
dockHeight: () => Math.max(78, store.height),
|
||||
lift: () => (input.revert()?.items.length ? 18 : 36 * value()),
|
||||
setDockBodyRef: (el: HTMLDivElement) => setStore("body", el),
|
||||
lift: () => (input.revert()?.items.length ? 18 : 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { SessionPermissionDock } from "@/pages/session/composer/session-permissi
|
|||
import { SessionQuestionDock } from "@/pages/session/composer/session-question-dock"
|
||||
import { SessionFollowupDock } from "@/pages/session/composer/session-followup-dock"
|
||||
import { SessionRevertDock } from "@/pages/session/composer/session-revert-dock"
|
||||
import { SessionTodoDock } from "@/pages/session/composer/session-todo-dock"
|
||||
import type { SessionComposerRegionController } from "./session-composer-region-controller"
|
||||
|
||||
export function SessionComposerRegion(props: {
|
||||
|
|
@ -60,28 +59,6 @@ export function SessionComposerRegion(props: {
|
|||
</Show>
|
||||
|
||||
<Show when={controller.showComposer()}>
|
||||
<Show when={controller.dock()}>
|
||||
<div
|
||||
classList={{
|
||||
"overflow-hidden": true,
|
||||
"pointer-events-none": controller.dockProgress() < 0.98,
|
||||
}}
|
||||
style={{
|
||||
"max-height": `${controller.dockHeight() * controller.dockProgress()}px`,
|
||||
}}
|
||||
>
|
||||
<div ref={controller.setDockBodyRef}>
|
||||
<SessionTodoDock
|
||||
todos={controller.state.todos()}
|
||||
collapsed={controller.todo.collapsed()}
|
||||
onToggle={controller.todo.onToggle}
|
||||
collapseLabel={language.t("session.todo.collapse")}
|
||||
expandLabel={language.t("session.todo.expand")}
|
||||
dockProgress={controller.dockProgress()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<Show
|
||||
when={controller.promptReady()}
|
||||
fallback={
|
||||
|
|
@ -98,10 +75,7 @@ export function SessionComposerRegion(props: {
|
|||
</div>
|
||||
)}
|
||||
</Show>
|
||||
<div
|
||||
class="w-full min-h-32 md:min-h-40 rounded-md border border-border-weak-base bg-background-base/50 px-4 py-3 text-text-weak whitespace-pre-wrap pointer-events-none"
|
||||
style={{ "margin-top": `${-36 * controller.dockProgress()}px` }}
|
||||
>
|
||||
<div class="w-full min-h-32 md:min-h-40 rounded-md border border-border-weak-base bg-background-base/50 px-4 py-3 text-text-weak whitespace-pre-wrap pointer-events-none">
|
||||
{controller.handoffPrompt() || language.t("prompt.loading")}
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -109,11 +83,7 @@ export function SessionComposerRegion(props: {
|
|||
>
|
||||
<Show when={rolled()} keyed>
|
||||
{(revert) => (
|
||||
<div
|
||||
style={{
|
||||
"margin-top": `${-36 * controller.dockProgress()}px`,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<SessionRevertDock
|
||||
items={revert.items}
|
||||
restoring={revert.restoring}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { PermissionRequest, QuestionRequest, Session } from "@opencode-ai/sdk/v2/client"
|
||||
import { todoDockAtBoundary, todoState } from "./session-composer-state"
|
||||
import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree"
|
||||
|
||||
const session = (input: { id: string; parentID?: string }) =>
|
||||
|
|
@ -104,35 +103,3 @@ describe("sessionQuestionRequest", () => {
|
|||
expect(sessionQuestionRequest(sessions, questions, "root")?.id).toBe("q-grand")
|
||||
})
|
||||
})
|
||||
|
||||
describe("todoState", () => {
|
||||
test("hides when there are no todos", () => {
|
||||
expect(todoState({ count: 0, done: false, live: true })).toBe("hide")
|
||||
})
|
||||
|
||||
test("opens while the session is still working", () => {
|
||||
expect(todoState({ count: 2, done: false, live: true })).toBe("open")
|
||||
})
|
||||
|
||||
test("closes completed todos after a running turn", () => {
|
||||
expect(todoState({ count: 2, done: true, live: true })).toBe("close")
|
||||
})
|
||||
|
||||
test("clears stale todos when the turn ends", () => {
|
||||
expect(todoState({ count: 2, done: false, live: false })).toBe("clear")
|
||||
})
|
||||
|
||||
test("clears completed todos when the session is no longer live", () => {
|
||||
expect(todoState({ count: 2, done: true, live: false })).toBe("clear")
|
||||
})
|
||||
})
|
||||
|
||||
describe("todoDockAtBoundary", () => {
|
||||
test("shows active todos when entering a session", () => {
|
||||
expect(todoDockAtBoundary("open")).toBe(true)
|
||||
})
|
||||
|
||||
test("hides completed todos when entering a session", () => {
|
||||
expect(todoDockAtBoundary("close")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,35 +1,20 @@
|
|||
import { createEffect, createMemo, on, onCleanup } from "solid-js"
|
||||
import { createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import type { PermissionRequest, QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
|
||||
import type { PermissionRequest, QuestionRequest } from "@opencode-ai/sdk/v2"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree"
|
||||
|
||||
export const todoState = (input: {
|
||||
count: number
|
||||
done: boolean
|
||||
live: boolean
|
||||
}): "hide" | "clear" | "open" | "close" => {
|
||||
if (input.count === 0) return "hide"
|
||||
if (!input.live) return "clear"
|
||||
if (!input.done) return "open"
|
||||
return "close"
|
||||
}
|
||||
|
||||
export const todoDockAtBoundary = (state: ReturnType<typeof todoState>) => state === "open"
|
||||
|
||||
const idle = { type: "idle" as const }
|
||||
|
||||
export function createSessionComposerController(options?: { closeMs?: number | (() => number) }) {
|
||||
export function createSessionComposerController() {
|
||||
const params = useParams()
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
const serverSync = useServerSync()
|
||||
const language = useLanguage()
|
||||
const permission = usePermission()
|
||||
|
||||
|
|
@ -49,24 +34,8 @@ export function createSessionComposerController(options?: { closeMs?: number | (
|
|||
return !!permissionRequest() || !!questionRequest()
|
||||
})
|
||||
|
||||
const todos = createMemo((): Todo[] => {
|
||||
const id = params.id
|
||||
if (!id) return []
|
||||
return serverSync().session.data.todo[id] ?? []
|
||||
})
|
||||
|
||||
const done = createMemo(
|
||||
() => todos().length > 0 && todos().every((todo) => todo.status === "completed" || todo.status === "cancelled"),
|
||||
)
|
||||
|
||||
const live = createMemo(() => sync().data.session_working(params.id ?? "") || blocked())
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
sessionID: params.id,
|
||||
responding: undefined as string | undefined,
|
||||
dock: todos().length > 0 && !done() && live(),
|
||||
closing: false,
|
||||
opening: false,
|
||||
})
|
||||
|
||||
const permissionResponding = createMemo(() => {
|
||||
|
|
@ -92,112 +61,12 @@ export function createSessionComposerController(options?: { closeMs?: number | (
|
|||
})
|
||||
}
|
||||
|
||||
let timer: number | undefined
|
||||
let raf: number | undefined
|
||||
|
||||
const closeMs = () => {
|
||||
const value = options?.closeMs
|
||||
if (typeof value === "function") return Math.max(0, value())
|
||||
if (typeof value === "number") return Math.max(0, value)
|
||||
return 400
|
||||
}
|
||||
|
||||
const scheduleClose = () => {
|
||||
if (timer) window.clearTimeout(timer)
|
||||
timer = window.setTimeout(() => {
|
||||
setStore({ dock: false, closing: false })
|
||||
timer = undefined
|
||||
}, closeMs())
|
||||
}
|
||||
|
||||
// Keep stale turn todos from reopening if the model never clears them.
|
||||
const clear = () => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
sync().set("todo", id, [])
|
||||
}
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => [params.id, todos().length, done(), live()] as const,
|
||||
([id, count, complete, active], previous) => {
|
||||
if (raf) cancelAnimationFrame(raf)
|
||||
raf = undefined
|
||||
|
||||
const next = todoState({
|
||||
count,
|
||||
done: complete,
|
||||
live: active,
|
||||
})
|
||||
|
||||
if (!previous || previous[0] !== id) {
|
||||
if (timer) window.clearTimeout(timer)
|
||||
timer = undefined
|
||||
setStore({ sessionID: id, dock: todoDockAtBoundary(next), closing: false, opening: false })
|
||||
if (next === "clear") clear()
|
||||
return
|
||||
}
|
||||
|
||||
if (next === "hide") {
|
||||
if (timer) window.clearTimeout(timer)
|
||||
timer = undefined
|
||||
setStore({ dock: false, closing: false, opening: false })
|
||||
return
|
||||
}
|
||||
|
||||
if (next === "clear") {
|
||||
if (timer) window.clearTimeout(timer)
|
||||
timer = undefined
|
||||
clear()
|
||||
return
|
||||
}
|
||||
|
||||
if (next === "open") {
|
||||
if (timer) window.clearTimeout(timer)
|
||||
timer = undefined
|
||||
const hidden = !store.dock || store.closing
|
||||
setStore({ dock: true, closing: false })
|
||||
if (hidden) {
|
||||
setStore("opening", true)
|
||||
raf = requestAnimationFrame(() => {
|
||||
setStore("opening", false)
|
||||
raf = undefined
|
||||
})
|
||||
return
|
||||
}
|
||||
setStore("opening", false)
|
||||
return
|
||||
}
|
||||
|
||||
setStore({ dock: true, opening: false, closing: true })
|
||||
if (!timer) scheduleClose()
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
onCleanup(() => {
|
||||
if (!timer) return
|
||||
window.clearTimeout(timer)
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
if (!raf) return
|
||||
cancelAnimationFrame(raf)
|
||||
})
|
||||
|
||||
return {
|
||||
blocked,
|
||||
questionRequest,
|
||||
permissionRequest,
|
||||
permissionResponding,
|
||||
decide,
|
||||
todos,
|
||||
dock: () =>
|
||||
store.sessionID === params.id
|
||||
? store.dock
|
||||
: todoDockAtBoundary(todoState({ count: todos().length, done: done(), live: live() })),
|
||||
closing: () => store.sessionID === params.id && store.closing,
|
||||
opening: () => store.sessionID === params.id && store.opening,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,256 +0,0 @@
|
|||
import type { Todo } from "@opencode-ai/sdk/v2"
|
||||
import { AnimatedNumber } from "@opencode-ai/ui/animated-number"
|
||||
import { Checkbox } from "@opencode-ai/ui/checkbox"
|
||||
import { DockTray } from "@opencode-ai/ui/dock-surface"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
||||
import { TextReveal } from "@opencode-ai/ui/text-reveal"
|
||||
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
import { Index, createEffect, createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
const doneToken = "\u0000done\u0000"
|
||||
const totalToken = "\u0000total\u0000"
|
||||
|
||||
function dot(status: Todo["status"]) {
|
||||
if (status !== "in_progress") return undefined
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 12 12"
|
||||
width="12"
|
||||
height="12"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="block"
|
||||
>
|
||||
<circle
|
||||
cx="6"
|
||||
cy="6"
|
||||
r="3"
|
||||
style={{
|
||||
animation: "var(--animate-pulse-scale)",
|
||||
"transform-origin": "center",
|
||||
"transform-box": "fill-box",
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SessionTodoDock(props: {
|
||||
todos: Todo[]
|
||||
collapsed: boolean
|
||||
onToggle: () => void
|
||||
collapseLabel: string
|
||||
expandLabel: string
|
||||
dockProgress: number
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({
|
||||
height: 78,
|
||||
})
|
||||
|
||||
const total = createMemo(() => props.todos.length)
|
||||
const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length)
|
||||
const label = createMemo(() => language.t("session.todo.progress", { done: done(), total: total() }))
|
||||
const progress = createMemo(() =>
|
||||
language
|
||||
.t("session.todo.progress", { done: doneToken, total: totalToken })
|
||||
.split(/(\u0000done\u0000|\u0000total\u0000)/),
|
||||
)
|
||||
|
||||
const active = createMemo(
|
||||
() =>
|
||||
props.todos.find((todo) => todo.status === "in_progress") ??
|
||||
props.todos.find((todo) => todo.status === "pending") ??
|
||||
props.todos.filter((todo) => todo.status === "completed").at(-1) ??
|
||||
props.todos[0],
|
||||
)
|
||||
|
||||
const preview = createMemo(() => active()?.content ?? "")
|
||||
const collapse = useSpring(() => (props.collapsed ? 1 : 0), { visualDuration: 0.3, bounce: 0 })
|
||||
const dock = createMemo(() => Math.max(0, Math.min(1, props.dockProgress)))
|
||||
const shut = createMemo(() => 1 - dock())
|
||||
const value = createMemo(() => Math.max(0, Math.min(1, collapse())))
|
||||
const hide = createMemo(() => Math.max(value(), shut()))
|
||||
const off = createMemo(() => hide() > 0.98)
|
||||
const turn = createMemo(() => Math.max(0, Math.min(1, value())))
|
||||
const full = createMemo(() => Math.max(78, store.height))
|
||||
let contentRef: HTMLDivElement | undefined
|
||||
|
||||
createEffect(() => {
|
||||
const el = contentRef
|
||||
if (!el) return
|
||||
const update = () => {
|
||||
setStore("height", (height) => Math.max(height, el.scrollHeight))
|
||||
}
|
||||
update()
|
||||
createResizeObserver(el, update)
|
||||
})
|
||||
|
||||
return (
|
||||
<DockTray
|
||||
data-component="session-todo-dock"
|
||||
style={{
|
||||
"overflow-x": "visible",
|
||||
"overflow-y": "hidden",
|
||||
"max-height": `${Math.max(78, full() - value() * (full() - 78))}px`,
|
||||
}}
|
||||
>
|
||||
<div ref={contentRef}>
|
||||
<div
|
||||
data-action="session-todo-toggle"
|
||||
class="pl-3 pr-2 py-2 flex items-center gap-2 overflow-visible"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={props.onToggle}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return
|
||||
event.preventDefault()
|
||||
props.onToggle()
|
||||
}}
|
||||
>
|
||||
<span
|
||||
class="text-14-regular text-text-strong cursor-default inline-flex items-baseline shrink-0 overflow-visible"
|
||||
aria-label={label()}
|
||||
style={{
|
||||
"--tool-motion-odometer-ms": "600ms",
|
||||
"--tool-motion-mask": "18%",
|
||||
"--tool-motion-mask-height": "0px",
|
||||
"--tool-motion-spring-ms": "560ms",
|
||||
"white-space": "pre",
|
||||
opacity: `${Math.max(0, Math.min(1, 1 - shut()))}`,
|
||||
}}
|
||||
>
|
||||
<Index each={progress()}>
|
||||
{(item) =>
|
||||
item() === doneToken ? (
|
||||
<AnimatedNumber value={done()} />
|
||||
) : item() === totalToken ? (
|
||||
<AnimatedNumber value={total()} />
|
||||
) : (
|
||||
<span>{item()}</span>
|
||||
)
|
||||
}
|
||||
</Index>
|
||||
</span>
|
||||
<div
|
||||
data-slot="session-todo-preview"
|
||||
class="ml-1 min-w-0 overflow-hidden"
|
||||
style={{
|
||||
flex: "1 1 auto",
|
||||
"max-width": "100%",
|
||||
}}
|
||||
>
|
||||
<TextReveal
|
||||
class="text-14-regular text-text-base cursor-default"
|
||||
text={props.collapsed ? preview() : undefined}
|
||||
duration={600}
|
||||
travel={25}
|
||||
edge={17}
|
||||
spring="cubic-bezier(0.34, 1, 0.64, 1)"
|
||||
springSoft="cubic-bezier(0.34, 1, 0.64, 1)"
|
||||
growOnly
|
||||
truncate
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<IconButton
|
||||
data-action="session-todo-toggle-button"
|
||||
data-collapsed={props.collapsed ? "true" : "false"}
|
||||
icon="chevron-down"
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
style={{ transform: `rotate(${turn() * 180}deg)` }}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
props.onToggle()
|
||||
}}
|
||||
aria-label={props.collapsed ? props.expandLabel : props.collapseLabel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-slot="session-todo-list"
|
||||
aria-hidden={props.collapsed || off()}
|
||||
classList={{
|
||||
"pointer-events-none": hide() > 0.1,
|
||||
}}
|
||||
style={{
|
||||
visibility: off() ? "hidden" : "visible",
|
||||
opacity: `${Math.max(0, Math.min(1, 1 - hide()))}`,
|
||||
}}
|
||||
>
|
||||
<TodoList todos={props.todos} />
|
||||
</div>
|
||||
</div>
|
||||
</DockTray>
|
||||
)
|
||||
}
|
||||
|
||||
function TodoList(props: { todos: Todo[] }) {
|
||||
const [store, setStore] = createStore({
|
||||
stuck: false,
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="relative">
|
||||
<div
|
||||
class="px-3 pb-11 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar"
|
||||
style={{ "overflow-anchor": "none" }}
|
||||
onScroll={(e) => {
|
||||
setStore("stuck", e.currentTarget.scrollTop > 0)
|
||||
}}
|
||||
>
|
||||
<Index each={props.todos}>
|
||||
{(todo) => (
|
||||
<Checkbox
|
||||
readOnly
|
||||
checked={todo().status === "completed"}
|
||||
indeterminate={todo().status === "in_progress"}
|
||||
data-in-progress={todo().status === "in_progress" ? "" : undefined}
|
||||
data-state={todo().status}
|
||||
icon={dot(todo().status)}
|
||||
style={{
|
||||
"--checkbox-align": "flex-start",
|
||||
"--checkbox-offset": "1px",
|
||||
transition: "opacity 220ms var(--tool-motion-ease, cubic-bezier(0.22, 1, 0.36, 1))",
|
||||
opacity: todo().status === "pending" ? "0.94" : "1",
|
||||
}}
|
||||
>
|
||||
<TextStrikethrough
|
||||
active={todo().status === "completed" || todo().status === "cancelled"}
|
||||
text={todo().content}
|
||||
class="text-14-regular min-w-0 break-words"
|
||||
style={{
|
||||
"line-height": "var(--line-height-normal)",
|
||||
transition:
|
||||
"color 220ms var(--tool-motion-ease, cubic-bezier(0.22, 1, 0.36, 1)), opacity 220ms var(--tool-motion-ease, cubic-bezier(0.22, 1, 0.36, 1))",
|
||||
color:
|
||||
todo().status === "completed" || todo().status === "cancelled"
|
||||
? "var(--text-weak)"
|
||||
: "var(--text-strong)",
|
||||
opacity: todo().status === "pending" ? "0.92" : "1",
|
||||
}}
|
||||
/>
|
||||
</Checkbox>
|
||||
)}
|
||||
</Index>
|
||||
</div>
|
||||
<div
|
||||
class="pointer-events-none absolute top-0 left-0 right-0 h-4 transition-opacity duration-150"
|
||||
style={{
|
||||
background: "linear-gradient(to bottom, var(--background-base), transparent)",
|
||||
opacity: store.stuck ? 1 : 0,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,623 +0,0 @@
|
|||
// @ts-nocheck
|
||||
import { createEffect, createMemo, onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import type { Todo } from "@opencode-ai/sdk/v2"
|
||||
import { useServerSync } from "@/context/global-sync"
|
||||
import { PromptInput } from "@/components/prompt-input"
|
||||
import { usePrompt } from "@/context/prompt"
|
||||
import {
|
||||
SessionComposerRegion,
|
||||
createSessionComposerController,
|
||||
createSessionComposerRegionController,
|
||||
} from "@/pages/session/composer"
|
||||
|
||||
export default {
|
||||
title: "UI/Todo Panel Motion",
|
||||
id: "components-todo-panel-motion",
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: `### Overview
|
||||
This playground renders the real session composer region from app code.
|
||||
|
||||
### Source path
|
||||
- \`packages/app/src/pages/session/composer/session-composer-region.tsx\`
|
||||
|
||||
### Includes
|
||||
- \`SessionTodoDock\` (real)
|
||||
- \`PromptInput\` (real)
|
||||
|
||||
No visual reimplementation layer is used for the dock/input stack.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const pool = [
|
||||
"Refactor ToolStatusTitle DOM measurement to offscreen global measurer (unconstrained by timeline layout)",
|
||||
"Remove inline measure nodes/CSS hooks and keep width morph behavior intact",
|
||||
"Run typechecks/tests and report what changed",
|
||||
"Verify reduced-motion behavior in timeline",
|
||||
"Review diff for animation edge cases",
|
||||
"Document rollout notes in PR description",
|
||||
"Check keyboard and screen reader semantics",
|
||||
"Add storybook controls for iteration speed",
|
||||
]
|
||||
|
||||
const btn = (accent?: boolean) =>
|
||||
({
|
||||
padding: "6px 14px",
|
||||
"border-radius": "6px",
|
||||
border: "1px solid var(--color-divider, #333)",
|
||||
background: accent ? "var(--color-accent, #58f)" : "var(--color-fill-element, #222)",
|
||||
color: "var(--color-text, #eee)",
|
||||
cursor: "pointer",
|
||||
"font-size": "13px",
|
||||
}) as const
|
||||
|
||||
const controls = {
|
||||
agents: { available: [], options: ["build"], current: "build", loading: false, visible: true, select: () => {} },
|
||||
model: {
|
||||
selection: {
|
||||
current: () => ({ id: "claude-3-7-sonnet", name: "Claude 3.7 Sonnet", provider: { id: "anthropic" } }),
|
||||
variant: { list: () => [], current: () => undefined, set: () => {} },
|
||||
},
|
||||
paid: true,
|
||||
loading: false,
|
||||
},
|
||||
session: {
|
||||
id: "story-session",
|
||||
tabs: { active: () => undefined, all: () => [], open: () => {}, setActive: () => {} },
|
||||
reviewPanel: { opened: () => false, open: () => {} },
|
||||
},
|
||||
newLayoutDesigns: true,
|
||||
}
|
||||
|
||||
const css = `
|
||||
[data-component="todo-stage"] {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
[data-component="todo-preview"] {
|
||||
height: 560px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
[data-component="todo-session-root"] {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background-base);
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
[data-component="todo-session-frame"] {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
[data-component="todo-session-panel"] {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background-stronger);
|
||||
}
|
||||
|
||||
[data-slot="todo-preview-content"] {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-slot="todo-preview-scroll"] {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
[data-slot="todo-preview-spacer"] {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
[data-slot="todo-preview-msg"] {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
background: var(--surface-base);
|
||||
color: var(--text-weak);
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
[data-slot="todo-preview-msg"][data-strong="true"] {
|
||||
color: var(--text-strong);
|
||||
}
|
||||
`
|
||||
|
||||
export const Playground = {
|
||||
render: () => {
|
||||
const global = useServerSync()
|
||||
const prompt = usePrompt()
|
||||
const [cfg, setCfg] = createStore({
|
||||
open: true,
|
||||
collapsed: false,
|
||||
step: 1,
|
||||
dockOpenDuration: 0.3,
|
||||
dockOpenBounce: 0,
|
||||
dockCloseDuration: 0.3,
|
||||
dockCloseBounce: 0,
|
||||
drawerExpandDuration: 0.3,
|
||||
drawerExpandBounce: 0,
|
||||
drawerCollapseDuration: 0.3,
|
||||
drawerCollapseBounce: 0,
|
||||
subtitleDuration: 600,
|
||||
subtitleAuto: true,
|
||||
subtitleTravel: 25,
|
||||
subtitleEdge: 17,
|
||||
countDuration: 600,
|
||||
countMask: 18,
|
||||
countMaskHeight: 0,
|
||||
countWidthDuration: 560,
|
||||
})
|
||||
const open = () => cfg.open
|
||||
const step = () => cfg.step
|
||||
const dockOpenDuration = () => cfg.dockOpenDuration
|
||||
const dockOpenBounce = () => cfg.dockOpenBounce
|
||||
const dockCloseDuration = () => cfg.dockCloseDuration
|
||||
const dockCloseBounce = () => cfg.dockCloseBounce
|
||||
const drawerExpandDuration = () => cfg.drawerExpandDuration
|
||||
const drawerExpandBounce = () => cfg.drawerExpandBounce
|
||||
const drawerCollapseDuration = () => cfg.drawerCollapseDuration
|
||||
const drawerCollapseBounce = () => cfg.drawerCollapseBounce
|
||||
const subtitleDuration = () => cfg.subtitleDuration
|
||||
const subtitleAuto = () => cfg.subtitleAuto
|
||||
const subtitleTravel = () => cfg.subtitleTravel
|
||||
const subtitleEdge = () => cfg.subtitleEdge
|
||||
const countDuration = () => cfg.countDuration
|
||||
const countMask = () => cfg.countMask
|
||||
const countMaskHeight = () => cfg.countMaskHeight
|
||||
const countWidthDuration = () => cfg.countWidthDuration
|
||||
const state = createSessionComposerController({ closeMs: () => Math.round(dockCloseDuration() * 1000) })
|
||||
let frame
|
||||
let scrollRef
|
||||
|
||||
const todos = createMemo<Todo[]>(() => {
|
||||
const done = Math.max(0, Math.min(3, step()))
|
||||
return pool.slice(0, 3).map((content, i) => ({
|
||||
id: `todo-${i + 1}`,
|
||||
content,
|
||||
status: i < done ? "completed" : i === done && done < 3 ? "in_progress" : "pending",
|
||||
}))
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
global.todo.set("story-session", todos())
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
if (frame) cancelAnimationFrame(frame)
|
||||
frame = undefined
|
||||
}
|
||||
|
||||
const pin = () => {
|
||||
if (!scrollRef) return
|
||||
scrollRef.scrollTop = scrollRef.scrollHeight
|
||||
}
|
||||
|
||||
const collapsed = () => cfg.collapsed
|
||||
const setCollapsed = (value: boolean) => setCfg("collapsed", value)
|
||||
const openDock = () => {
|
||||
clear()
|
||||
setCfg("open", true)
|
||||
frame = requestAnimationFrame(() => {
|
||||
pin()
|
||||
frame = undefined
|
||||
})
|
||||
}
|
||||
|
||||
const closeDock = () => {
|
||||
clear()
|
||||
setCfg("open", false)
|
||||
}
|
||||
|
||||
const dockOpen = () => open()
|
||||
|
||||
const toggleDock = () => {
|
||||
if (dockOpen()) {
|
||||
closeDock()
|
||||
return
|
||||
}
|
||||
openDock()
|
||||
}
|
||||
|
||||
const toggleDrawer = () => {
|
||||
if (!dockOpen()) {
|
||||
openDock()
|
||||
frame = requestAnimationFrame(() => {
|
||||
pin()
|
||||
setCollapsed(true)
|
||||
frame = undefined
|
||||
})
|
||||
return
|
||||
}
|
||||
setCollapsed(!collapsed())
|
||||
}
|
||||
|
||||
const cycle = () => {
|
||||
setCfg("step", (value) => (value + 1) % 4)
|
||||
}
|
||||
|
||||
onCleanup(clear)
|
||||
|
||||
return (
|
||||
<div data-component="todo-stage">
|
||||
<style>{css}</style>
|
||||
|
||||
<div data-component="todo-preview">
|
||||
<div data-component="todo-session-root">
|
||||
<div data-component="todo-session-frame">
|
||||
<div data-component="todo-session-panel">
|
||||
<div data-slot="todo-preview-content">
|
||||
<div data-slot="todo-preview-scroll" class="scroll-view__viewport" ref={scrollRef}>
|
||||
<div data-slot="todo-preview-spacer" />
|
||||
<div data-slot="todo-preview-msg" data-strong="true">
|
||||
Thinking Checking type safety
|
||||
</div>
|
||||
<div data-slot="todo-preview-msg">Shell Prints five topic blocks between timed commands</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SessionComposerRegion
|
||||
controller={createSessionComposerRegionController({
|
||||
state,
|
||||
sessionKey: () => "story-session",
|
||||
sessionID: () => "story-session",
|
||||
prompt,
|
||||
ready: () => true,
|
||||
centered: () => false,
|
||||
todo: { collapsed, onToggle: () => setCollapsed(!collapsed()) },
|
||||
followup: () => undefined,
|
||||
revert: () => undefined,
|
||||
onResponseSubmit: pin,
|
||||
openParent: () => {},
|
||||
setPromptRef: () => {},
|
||||
setDockRef: () => {},
|
||||
})}
|
||||
promptInput={
|
||||
<PromptInput
|
||||
controls={controls}
|
||||
submission={{ abort: () => {}, handleSubmit: (event) => event.preventDefault() }}
|
||||
ref={() => {}}
|
||||
newSessionWorktree=""
|
||||
onNewSessionWorktreeReset={() => {}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", gap: "8px", "flex-wrap": "wrap" }}>
|
||||
<button onClick={toggleDock} style={btn(dockOpen())}>
|
||||
{dockOpen() ? "Animate close" : "Animate open"}
|
||||
</button>
|
||||
<button onClick={toggleDrawer} style={btn(dockOpen() && collapsed())}>
|
||||
{dockOpen() && collapsed() ? "Expand todo dock" : "Collapse todo dock"}
|
||||
</button>
|
||||
<button onClick={cycle} style={btn(step() > 0)}>
|
||||
Cycle progress ({step()}/3 done)
|
||||
</button>
|
||||
{[0, 1, 2, 3].map((value) => (
|
||||
<button onClick={() => setCfg("step", value)} style={btn(step() === value)}>
|
||||
{value} done
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gap: "10px", "max-width": "560px" }}>
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)" }}>Dock open</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0.1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={dockOpenDuration()}
|
||||
onInput={(event) => setCfg("dockOpenDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(dockOpenDuration() * 1000)}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
bounce
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={dockOpenBounce()}
|
||||
onInput={(event) => setCfg("dockOpenBounce", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{dockOpenBounce().toFixed(2)}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)", "margin-top": "4px" }}>
|
||||
Dock close
|
||||
</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0.1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={dockCloseDuration()}
|
||||
onInput={(event) => setCfg("dockCloseDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(dockCloseDuration() * 1000)}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
bounce
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={dockCloseBounce()}
|
||||
onInput={(event) => setCfg("dockCloseBounce", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{dockCloseBounce().toFixed(2)}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)", "margin-top": "4px" }}>
|
||||
Drawer expand
|
||||
</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0.1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={drawerExpandDuration()}
|
||||
onInput={(event) => setCfg("drawerExpandDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(drawerExpandDuration() * 1000)}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
bounce
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={drawerExpandBounce()}
|
||||
onInput={(event) => setCfg("drawerExpandBounce", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{drawerExpandBounce().toFixed(2)}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)", "margin-top": "4px" }}>
|
||||
Drawer collapse
|
||||
</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0.1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={drawerCollapseDuration()}
|
||||
onInput={(event) => setCfg("drawerCollapseDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(drawerCollapseDuration() * 1000)}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
bounce
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={drawerCollapseBounce()}
|
||||
onInput={(event) => setCfg("drawerCollapseBounce", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{drawerCollapseBounce().toFixed(2)}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)", "margin-top": "4px" }}>
|
||||
Subtitle odometer
|
||||
</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="120"
|
||||
max="1400"
|
||||
step="10"
|
||||
value={subtitleDuration()}
|
||||
onInput={(event) => setCfg("subtitleDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(subtitleDuration())}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
auto fit
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={subtitleAuto()}
|
||||
onInput={(event) => setCfg("subtitleAuto", event.currentTarget.checked)}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{subtitleAuto() ? "on" : "off"}
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
travel
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="40"
|
||||
step="1"
|
||||
value={subtitleTravel()}
|
||||
onInput={(event) => setCfg("subtitleTravel", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>{subtitleTravel()}px</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
edge
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="40"
|
||||
step="1"
|
||||
value={subtitleEdge()}
|
||||
onInput={(event) => setCfg("subtitleEdge", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>{subtitleEdge()}%</span>
|
||||
</label>
|
||||
|
||||
<div style={{ "font-size": "12px", color: "var(--color-text-secondary, #a3a3a3)", "margin-top": "4px" }}>
|
||||
Count odometer
|
||||
</div>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
duration
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="120"
|
||||
max="1400"
|
||||
step="10"
|
||||
value={countDuration()}
|
||||
onInput={(event) => setCfg("countDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(countDuration())}ms
|
||||
</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
mask
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="4"
|
||||
max="40"
|
||||
step="1"
|
||||
value={countMask()}
|
||||
onInput={(event) => setCfg("countMask", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>{countMask()}%</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
mask height
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="14"
|
||||
step="1"
|
||||
value={countMaskHeight()}
|
||||
onInput={(event) => setCfg("countMaskHeight", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>{countMaskHeight()}px</span>
|
||||
</label>
|
||||
<label style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
||||
<span style={{ width: "110px", "font-size": "13px", color: "var(--color-text-secondary, #a3a3a3)" }}>
|
||||
width spring
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1200"
|
||||
step="10"
|
||||
value={countWidthDuration()}
|
||||
onInput={(event) => setCfg("countWidthDuration", event.currentTarget.valueAsNumber)}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<span style={{ width: "64px", "text-align": "right", "font-size": "13px" }}>
|
||||
{Math.round(countWidthDuration())}ms
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createEffect, onCleanup, type JSX } from "solid-js"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import { SessionReview } from "@opencode-ai/session-ui/session-review"
|
||||
import type {
|
||||
SessionReviewCommentActions,
|
||||
|
|
@ -14,7 +14,7 @@ import type { LineComment } from "@/context/comments"
|
|||
|
||||
export type DiffStyle = "unified" | "split"
|
||||
|
||||
type ReviewDiff = SnapshotFileDiff | VcsFileDiff
|
||||
type ReviewDiff = FileDiffInfo | VcsFileDiff
|
||||
|
||||
export interface SessionReviewTabProps {
|
||||
title?: JSX.Element
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
|
|||
import { Mark } from "@opencode-ai/ui/logo"
|
||||
import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter } from "@thisbeyond/solid-dnd"
|
||||
import type { DragEvent } from "@thisbeyond/solid-dnd"
|
||||
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
|
||||
|
|
@ -23,7 +23,6 @@ import { useFile, type SelectedLineRange } from "@/context/file"
|
|||
import { useLanguage } from "@/context/language"
|
||||
import { useLayout } from "@/context/layout"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { createFileTabListSync } from "@/pages/session/file-tab-scroll"
|
||||
import { FileTabContent } from "@/pages/session/file-tabs"
|
||||
import {
|
||||
|
|
@ -36,15 +35,9 @@ import {
|
|||
import { setSessionHandoff } from "@/pages/session/handoff"
|
||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||
|
||||
type RenderDiff = (SnapshotFileDiff & { file: string }) | VcsFileDiff
|
||||
|
||||
function renderDiff(value: SnapshotFileDiff | VcsFileDiff): value is RenderDiff {
|
||||
return typeof value.file === "string"
|
||||
}
|
||||
|
||||
export function SessionSidePanel(props: {
|
||||
canReview: () => boolean
|
||||
diffs: () => (SnapshotFileDiff | VcsFileDiff)[]
|
||||
diffs: () => (FileDiffInfo | VcsFileDiff)[]
|
||||
diffsReady: () => boolean
|
||||
empty: () => string
|
||||
hasReview: () => boolean
|
||||
|
|
@ -59,7 +52,6 @@ export function SessionSidePanel(props: {
|
|||
}) {
|
||||
const layout = useLayout()
|
||||
const settings = useSettings()
|
||||
const sync = useSync()
|
||||
const file = useFile()
|
||||
const language = useLanguage()
|
||||
const command = useCommand()
|
||||
|
|
@ -88,7 +80,7 @@ export function SessionSidePanel(props: {
|
|||
})
|
||||
const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px"))
|
||||
|
||||
const diffs = createMemo(() => props.diffs().filter(renderDiff))
|
||||
const diffs = createMemo(() => props.diffs())
|
||||
const diffFiles = createMemo(() => diffs().map((d) => d.file))
|
||||
const kinds = createMemo(() => {
|
||||
const merge = (a: "add" | "del" | "mix" | undefined, b: "add" | "del" | "mix") => {
|
||||
|
|
|
|||
|
|
@ -160,8 +160,10 @@ function TimelineDiffSummaryRow(props: { diffs: SummaryDiff[] }) {
|
|||
>
|
||||
<div data-slot="session-turn-diffs-header">
|
||||
<span data-slot="session-turn-diffs-label">
|
||||
{props.diffs.length} {language.t("ui.sessionTurn.diffs.changed")}{" "}
|
||||
{language.t(props.diffs.length === 1 ? "ui.common.file.one" : "ui.common.file.other")}
|
||||
{language.t(
|
||||
props.diffs.length === 1 ? "ui.sessionTurn.diffs.changed.one" : "ui.sessionTurn.diffs.changed.other",
|
||||
{ count: String(props.diffs.length) },
|
||||
)}
|
||||
</span>
|
||||
<DiffChanges changes={props.diffs} />
|
||||
<Show when={overflow() > 0}>
|
||||
|
|
@ -1245,7 +1247,7 @@ export function MessageTimeline(props: {
|
|||
const value = row()
|
||||
if (value._tag !== "AssistantPart" || value.group.type !== "part") return false
|
||||
const part = getMsgPart(value.group.ref.messageID, value.group.ref.partID)
|
||||
return part?.type === "tool" && ["edit", "write", "apply_patch"].includes(part.tool)
|
||||
return part?.type === "tool" && ["edit", "write", "patch", "apply_patch"].includes(part.tool)
|
||||
}
|
||||
const [ready, setReady] = createSignal(initialItem.size <= timelineFallbackItemSize || !asyncFile())
|
||||
let contentMeasureFrame: number | undefined
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ import { createSessionTabs } from "@/pages/session/helpers"
|
|||
import { extractPromptFromParts } from "@/utils/prompt"
|
||||
import { UserMessage } from "@opencode-ai/sdk/v2"
|
||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { requireServerKey } from "@/utils/session-route"
|
||||
import { createSessionOwnership } from "./session-ownership"
|
||||
|
||||
export type SessionCommandContext = {
|
||||
|
|
@ -48,7 +46,6 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
const settings = useSettings()
|
||||
const sync = useSync()
|
||||
const terminal = useTerminal()
|
||||
const sessionTabs = useTabs()
|
||||
const layout = useLayout()
|
||||
const navigate = useNavigate()
|
||||
const { params, sessionKey, tabs, view } = useSessionLayout()
|
||||
|
|
@ -269,6 +266,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
view().terminal.open()
|
||||
}
|
||||
|
||||
const closeTerminal = () => {
|
||||
const id = terminal.active()
|
||||
if (!id) return
|
||||
const last = terminal.all().length === 1
|
||||
void terminal.close(id)
|
||||
if (last) view().terminal.close()
|
||||
}
|
||||
|
||||
const chooseMcp = () => {
|
||||
void openDialog(
|
||||
() => import("@/components/dialog-select-mcp"),
|
||||
|
|
@ -411,9 +416,9 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
title: language.t("command.session.new"),
|
||||
keybind: "mod+shift+s",
|
||||
slash: "new",
|
||||
onSelect: () => {
|
||||
if (params.serverKey) {
|
||||
sessionTabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory })
|
||||
onSelect: (source) => {
|
||||
if (settings.general.newLayoutDesigns()) {
|
||||
command.trigger("tab.new", source)
|
||||
return
|
||||
}
|
||||
navigate(`/${params.dir}/session`)
|
||||
|
|
@ -518,6 +523,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|||
]
|
||||
|
||||
const terminalCmds = () => [
|
||||
terminalCommand({
|
||||
id: "terminal.close",
|
||||
title: language.t("terminal.close"),
|
||||
keybind: "mod+w",
|
||||
hidden: true,
|
||||
when: (event) => event.target instanceof Element && !!event.target.closest('[data-component="terminal"]'),
|
||||
onSelect: closeTerminal,
|
||||
}),
|
||||
terminalCommand({
|
||||
id: "terminal.new",
|
||||
title: language.t("command.terminal.new"),
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { Kind } from "@/components/file-tree-v2"
|
||||
import { normalizeFileTreeV2Path } from "@/components/file-tree-v2-model"
|
||||
|
||||
export type RenderDiff = (SnapshotFileDiff & { file: string }) | VcsFileDiff
|
||||
export type RenderDiff = FileDiffInfo | VcsFileDiff
|
||||
|
||||
export function normalizePath(p: string) {
|
||||
return normalizeFileTreeV2Path(p)
|
||||
}
|
||||
|
||||
export function filterRenderableDiff(value: SnapshotFileDiff | VcsFileDiff): value is RenderDiff {
|
||||
return typeof value.file === "string"
|
||||
}
|
||||
|
||||
export function reviewDiffKinds(diffs: RenderDiff[]) {
|
||||
const merge = (a: Kind | undefined, b: Kind) => {
|
||||
if (!a) return b
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createMemo, createSignal, Show, type JSX } from "solid-js"
|
||||
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import {
|
||||
SESSION_REVIEW_V2_SIDEBAR_WIDTH_MAX,
|
||||
SESSION_REVIEW_V2_SIDEBAR_WIDTH_MIN,
|
||||
|
|
@ -21,16 +21,11 @@ import type {
|
|||
import FileTreeV2 from "@/components/file-tree-v2"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import {
|
||||
filterRenderableDiff,
|
||||
filterReviewFiles,
|
||||
reviewDiffKinds,
|
||||
type RenderDiff,
|
||||
} from "@/pages/session/v2/review-diff-kinds"
|
||||
import { filterReviewFiles, reviewDiffKinds, type RenderDiff } from "@/pages/session/v2/review-diff-kinds"
|
||||
import type { ReviewPanelV2State } from "@/pages/session/v2/review-panel-v2-state"
|
||||
import { applyFileListKeyDown, SessionFileListV2 } from "@/pages/session/v2/session-file-list-v2"
|
||||
|
||||
type ReviewDiff = SnapshotFileDiff | VcsFileDiff
|
||||
type ReviewDiff = FileDiffInfo | VcsFileDiff
|
||||
|
||||
export type ReviewPanelV2Props = {
|
||||
title?: JSX.Element
|
||||
|
|
@ -54,7 +49,7 @@ export type ReviewPanelV2Props = {
|
|||
export function ReviewPanelV2(props: ReviewPanelV2Props) {
|
||||
const sdk = useSDK()
|
||||
|
||||
const diffs = createMemo(() => props.diffs().filter(filterRenderableDiff))
|
||||
const diffs = createMemo(() => props.diffs())
|
||||
const filteredFiles = createMemo(() =>
|
||||
filterReviewFiles(
|
||||
diffs().map((diff) => diff.file),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo } from "@opencode-ai/sdk/v2"
|
||||
import type { Message } from "@opencode-ai/sdk/v2/client"
|
||||
import { diffs, message } from "./diffs"
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ const item = {
|
|||
additions: 1,
|
||||
deletions: 1,
|
||||
status: "modified",
|
||||
} satisfies SnapshotFileDiff
|
||||
} satisfies FileDiffInfo
|
||||
|
||||
describe("diffs", () => {
|
||||
test("keeps valid arrays", () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
|
||||
import type { FileDiffInfo } from "@opencode-ai/sdk/v2"
|
||||
import type { Message } from "@opencode-ai/sdk/v2/client"
|
||||
|
||||
type Diff = SnapshotFileDiff | VcsFileDiff
|
||||
type Diff = FileDiffInfo
|
||||
|
||||
function diff(value: unknown): value is Diff {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return false
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
|
@ -46,10 +46,12 @@
|
|||
"effect": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
"jsonc-parser": "3.3.1",
|
||||
"open": "10.1.2",
|
||||
"opentui-spinner": "catalog:",
|
||||
"semver": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"strip-ansi": "7.1.2"
|
||||
"strip-ansi": "7.1.2",
|
||||
"uqr": "0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -3,27 +3,13 @@ import { Spec } from "../framework/spec"
|
|||
|
||||
declare const OPENCODE_CLI_NAME: string | undefined
|
||||
|
||||
const MiniParams = {
|
||||
continue: Flag.boolean("continue").pipe(
|
||||
Flag.withAlias("c"),
|
||||
Flag.withDescription("Continue the last session"),
|
||||
const ServerParams = {
|
||||
standalone: Flag.boolean("standalone").pipe(
|
||||
Flag.withDescription("Run with a private server instead of the background service"),
|
||||
Flag.withDefault(false),
|
||||
),
|
||||
session: Flag.string("session").pipe(
|
||||
Flag.withAlias("s"),
|
||||
Flag.withDescription("Session ID to continue"),
|
||||
Flag.optional,
|
||||
),
|
||||
fork: Flag.boolean("fork").pipe(
|
||||
Flag.withDescription("Fork the session when continuing"),
|
||||
Flag.withDefault(false),
|
||||
),
|
||||
replay: Flag.boolean("replay").pipe(
|
||||
Flag.withDescription("Replay session history on resume and after resize"),
|
||||
Flag.withDefault(true),
|
||||
),
|
||||
replayLimit: Flag.integer("replay-limit").pipe(
|
||||
Flag.withDescription("Cap visible replay to the newest N messages"),
|
||||
server: Flag.string("server").pipe(
|
||||
Flag.withDescription("Connect to a server URL instead of the background service"),
|
||||
Flag.optional,
|
||||
),
|
||||
}
|
||||
|
|
@ -31,18 +17,11 @@ const MiniParams = {
|
|||
export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
|
||||
description: "OpenCode 2.0 preview command line interface",
|
||||
params: {
|
||||
...ServerParams,
|
||||
directory: Argument.string("directory").pipe(
|
||||
Argument.withDescription("Directory to start OpenCode in"),
|
||||
Argument.optional,
|
||||
),
|
||||
standalone: Flag.boolean("standalone").pipe(
|
||||
Flag.withDescription("Run with a private server instead of the background service"),
|
||||
Flag.withDefault(false),
|
||||
),
|
||||
server: Flag.string("server").pipe(
|
||||
Flag.withDescription("Connect to a server URL instead of the background service"),
|
||||
Flag.optional,
|
||||
),
|
||||
continue: Flag.boolean("continue").pipe(
|
||||
Flag.withAlias("c"),
|
||||
Flag.withDescription("Continue the last session"),
|
||||
|
|
@ -75,6 +54,17 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
description: "Debugging and troubleshooting tools",
|
||||
commands: [Spec.make("agents", { description: "List all agents" })],
|
||||
}),
|
||||
Spec.make("console", {
|
||||
description: "Manage OpenCode Console access",
|
||||
commands: [
|
||||
Spec.make("login", {
|
||||
description: "Log in to OpenCode Console",
|
||||
params: {
|
||||
url: Argument.string("url").pipe(Argument.withDescription("Console server URL"), Argument.optional),
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Spec.make("mcp", {
|
||||
description: "Manage MCP (Model Context Protocol) servers",
|
||||
commands: [
|
||||
|
|
@ -116,10 +106,28 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
Spec.make("mini", {
|
||||
description: "Start the minimal interactive interface",
|
||||
params: {
|
||||
...MiniParams,
|
||||
project: Argument.string("project").pipe(
|
||||
Argument.withDescription("Path to start OpenCode in"),
|
||||
Argument.optional,
|
||||
...ServerParams,
|
||||
continue: Flag.boolean("continue").pipe(
|
||||
Flag.withAlias("c"),
|
||||
Flag.withDescription("Continue the last session"),
|
||||
Flag.withDefault(false),
|
||||
),
|
||||
session: Flag.string("session").pipe(
|
||||
Flag.withAlias("s"),
|
||||
Flag.withDescription("Session ID to continue"),
|
||||
Flag.optional,
|
||||
),
|
||||
fork: Flag.boolean("fork").pipe(
|
||||
Flag.withDescription("Fork the session when continuing"),
|
||||
Flag.withDefault(false),
|
||||
),
|
||||
replay: Flag.boolean("replay").pipe(
|
||||
Flag.withDescription("Replay session history on resume and after resize"),
|
||||
Flag.withDefault(true),
|
||||
),
|
||||
replayLimit: Flag.integer("replay-limit").pipe(
|
||||
Flag.withDescription("Cap visible replay to the newest N messages"),
|
||||
Flag.optional,
|
||||
),
|
||||
model: Flag.string("model").pipe(
|
||||
Flag.withAlias("m"),
|
||||
|
|
@ -128,16 +136,13 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
),
|
||||
agent: Flag.string("agent").pipe(Flag.withDescription("Agent to use"), Flag.optional),
|
||||
prompt: Flag.string("prompt").pipe(Flag.withDescription("Prompt to use"), Flag.optional),
|
||||
server: Flag.string("server").pipe(
|
||||
Flag.withDescription("Connect to a server URL instead of the background service"),
|
||||
Flag.optional,
|
||||
),
|
||||
demo: Flag.boolean("demo").pipe(Flag.withDefault(false), Flag.withHidden),
|
||||
},
|
||||
}),
|
||||
Spec.make("run", {
|
||||
description: "Run OpenCode with a message",
|
||||
params: {
|
||||
...ServerParams,
|
||||
message: Argument.string("message").pipe(
|
||||
Argument.withDescription("Message to send"),
|
||||
Argument.variadic({ min: 0 }),
|
||||
|
|
@ -158,7 +163,7 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
),
|
||||
model: Flag.string("model").pipe(
|
||||
Flag.withAlias("m"),
|
||||
Flag.withDescription("Model to use in the format provider/model"),
|
||||
Flag.withDescription("Model to use in the format provider/model#variant"),
|
||||
Flag.optional,
|
||||
),
|
||||
agent: Flag.string("agent").pipe(Flag.withDescription("Agent to use"), Flag.optional),
|
||||
|
|
@ -172,12 +177,6 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
Flag.atMost(100),
|
||||
),
|
||||
title: Flag.string("title").pipe(Flag.withDescription("Session title"), Flag.optional),
|
||||
server: Flag.string("server").pipe(
|
||||
Flag.withDescription("Connect to a server URL instead of the background service"),
|
||||
Flag.optional,
|
||||
),
|
||||
dir: Flag.string("dir").pipe(Flag.withDescription("Directory to run in"), Flag.optional),
|
||||
variant: Flag.string("variant").pipe(Flag.withDescription("Model variant"), Flag.optional),
|
||||
thinking: Flag.boolean("thinking").pipe(
|
||||
Flag.withDescription("Show thinking blocks"),
|
||||
Flag.withDefault(false),
|
||||
|
|
@ -187,10 +186,6 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
Flag.withDefault(false),
|
||||
),
|
||||
yolo: Flag.boolean("yolo").pipe(Flag.withDefault(false), Flag.withHidden),
|
||||
dangerouslySkipPermissions: Flag.boolean("dangerously-skip-permissions").pipe(
|
||||
Flag.withDefault(false),
|
||||
Flag.withHidden,
|
||||
),
|
||||
},
|
||||
}),
|
||||
Spec.make("service", {
|
||||
|
|
@ -214,6 +209,7 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO
|
|||
}),
|
||||
],
|
||||
}),
|
||||
Spec.make("link", { description: "Show server connection information" }),
|
||||
Spec.make("serve", {
|
||||
description: "Start the v2 API server",
|
||||
params: {
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ export default Runtime.handler(
|
|||
Effect.fn("cli.api")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const transport = found ?? (yield* Service.start(options))
|
||||
const endpoint = found ?? (yield* Service.start(options))
|
||||
const params = Option.getOrElse(input.param, () => ({}))
|
||||
const request = yield* resolveRequest(transport, input.request, params)
|
||||
const headers = new Headers(transport.headers)
|
||||
const request = yield* resolveRequest(endpoint, input.request, params)
|
||||
const headers = new Headers(Service.headers(endpoint))
|
||||
for (const header of input.header) {
|
||||
const index = header.indexOf(":")
|
||||
if (index < 1) return yield* Effect.fail(new Error(`Invalid header, expected name:value: ${header}`))
|
||||
|
|
@ -33,7 +33,7 @@ export default Runtime.handler(
|
|||
if (body !== undefined && !headers.has("content-type")) headers.set("content-type", "application/json")
|
||||
|
||||
const response = yield* Effect.tryPromise(() =>
|
||||
fetch(new URL(request.path, transport.url), {
|
||||
fetch(new URL(request.path, endpoint.url), {
|
||||
method: request.method,
|
||||
headers,
|
||||
body,
|
||||
|
|
@ -60,7 +60,7 @@ export function rawRequest(input: readonly string[]) {
|
|||
}
|
||||
|
||||
function resolveRequest(
|
||||
transport: Service.Transport,
|
||||
endpoint: Service.Endpoint,
|
||||
input: readonly string[],
|
||||
params: Record<string, string>,
|
||||
) {
|
||||
|
|
@ -68,7 +68,7 @@ function resolveRequest(
|
|||
if (raw) return Effect.succeed(raw)
|
||||
if (input.length !== 1) return Effect.fail(new Error("Expected an operation name or an HTTP method and path"))
|
||||
return Effect.tryPromise(async () => {
|
||||
const response = await fetch(new URL("/openapi.json", transport.url), { headers: transport.headers })
|
||||
const response = await fetch(new URL("/openapi.json", endpoint.url), { headers: Service.headers(endpoint) })
|
||||
if (!response.ok) throw new Error(`Failed to load OpenAPI document: HTTP ${response.status}`)
|
||||
return resolveOperation((await response.json()) as OpenApi, input[0], params)
|
||||
})
|
||||
|
|
|
|||
117
packages/cli/src/commands/handlers/console/login.ts
Normal file
117
packages/cli/src/commands/handlers/console/login.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import { Cause, Effect, Exit, Option } from "effect"
|
||||
import { Service } from "@opencode-ai/client/effect"
|
||||
import { OpenCode, type OpenCodeClient } from "@opencode-ai/client/promise"
|
||||
import { AppProcess } from "@opencode-ai/core/process"
|
||||
import { Commands } from "../../commands"
|
||||
import { Runtime } from "../../../framework/runtime"
|
||||
import { ServiceConfig } from "../../../services/service-config"
|
||||
import { createTimelineHost, type TimelineHost } from "../../../ui/timeline"
|
||||
|
||||
const integrationID = "opencode"
|
||||
const location = { directory: process.cwd() }
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.console.commands.login,
|
||||
Effect.fn("cli.console.login")(function* (input) {
|
||||
const timeline = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => createTimelineHost()),
|
||||
(value) => request(() => value.close()).pipe(Effect.ignore),
|
||||
)
|
||||
const exit = yield* login(timeline, Option.getOrUndefined(input.url)).pipe(
|
||||
Effect.raceFirst(AppProcess.waitForAbort(timeline.signal)),
|
||||
Effect.exit,
|
||||
)
|
||||
if (Exit.isSuccess(exit)) return
|
||||
|
||||
const cancelled = timeline.signal.aborted
|
||||
yield* request(() => timeline.failure(cancelled ? "Authorization cancelled" : errorMessage(exit.cause))).pipe(
|
||||
Effect.ignore,
|
||||
)
|
||||
process.exitCode = cancelled ? 130 : 1
|
||||
}),
|
||||
)
|
||||
|
||||
const login = Effect.fn("cli.console.login.run")(function* (timeline: TimelineHost, server?: string) {
|
||||
yield* request(() => timeline.intro("Log in"))
|
||||
yield* request(() => timeline.pending("Connecting to OpenCode..."))
|
||||
|
||||
const endpoint = yield* Service.start(yield* ServiceConfig.options())
|
||||
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const found = yield* request((signal) => client.integration.get({ integrationID, location }, { signal }))
|
||||
const integration = yield* required(found.data, "OpenCode Console integration is unavailable")
|
||||
const method = yield* required(
|
||||
integration.methods.find((candidate) => candidate.type === "oauth"),
|
||||
"OpenCode Console login is unavailable",
|
||||
)
|
||||
|
||||
yield* request(() => timeline.pending("Starting authorization..."))
|
||||
const started = yield* request((signal) =>
|
||||
client.integration.connect.oauth(
|
||||
{
|
||||
integrationID,
|
||||
methodID: method.id,
|
||||
inputs: server ? { server } : {},
|
||||
location,
|
||||
},
|
||||
{ signal },
|
||||
),
|
||||
)
|
||||
const attempt = started.data
|
||||
yield* Effect.addFinalizer(() =>
|
||||
request(() =>
|
||||
client.integration.attempt.cancel(
|
||||
{ attemptID: attempt.attemptID, location },
|
||||
{ signal: AbortSignal.timeout(5_000) },
|
||||
),
|
||||
).pipe(Effect.ignore),
|
||||
)
|
||||
if (attempt.mode !== "auto") yield* Effect.fail(new Error("OpenCode Console requires a device login"))
|
||||
|
||||
yield* request(() => timeline.item(`Go to: ${attempt.url}`))
|
||||
yield* request(() => timeline.item(attempt.instructions))
|
||||
yield* request(async () => {
|
||||
const { default: open } = await import("open")
|
||||
await open(attempt.url)
|
||||
}).pipe(Effect.ignore)
|
||||
yield* request(() => timeline.pending("Waiting for authorization..."))
|
||||
|
||||
const status = yield* waitForConsoleLogin(client, attempt.attemptID)
|
||||
if (status.status === "failed") yield* Effect.fail(new Error(status.message))
|
||||
if (status.status === "expired") yield* Effect.fail(new Error("Device code expired"))
|
||||
|
||||
yield* request(() => timeline.success("Connected to OpenCode Console"))
|
||||
yield* request(() => timeline.outro("Done"))
|
||||
})
|
||||
|
||||
const waitForConsoleLogin = Effect.fn("cli.console.login.wait")(function* (
|
||||
client: OpenCodeClient,
|
||||
attemptID: string,
|
||||
) {
|
||||
while (true) {
|
||||
const response = yield* request((signal) =>
|
||||
client.integration.attempt.status({ attemptID, location }, { signal }),
|
||||
)
|
||||
if (response.data.status !== "pending") return response.data
|
||||
yield* Effect.sleep(500)
|
||||
}
|
||||
})
|
||||
|
||||
function request<A>(task: (signal: AbortSignal) => Promise<A>) {
|
||||
return Effect.tryPromise({
|
||||
try: task,
|
||||
catch: (cause) => cause,
|
||||
})
|
||||
}
|
||||
|
||||
function required<A>(value: A | null | undefined, message: string) {
|
||||
return value === null || value === undefined ? Effect.fail(new Error(message)) : Effect.succeed(value)
|
||||
}
|
||||
|
||||
function errorMessage(cause: Cause.Cause<unknown>) {
|
||||
const error = Cause.squash(cause)
|
||||
if (error instanceof Error) return error.message
|
||||
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
||||
return error.message
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ export default Runtime.handler(
|
|||
Effect.fn("cli.debug.agents")(function* () {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const transport = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: transport.url, headers: transport.headers })
|
||||
const endpoint = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const response = yield* Effect.promise(() => client.v2.agent.list({ location: { directory: process.cwd() } }))
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
|
|
|
|||
|
|
@ -1,89 +1,50 @@
|
|||
import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { run } from "@opencode-ai/tui"
|
||||
import { loadBuiltinPlugins } from "@opencode-ai/tui/builtins"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { Commands } from "../commands"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { Effect, Option, Redacted } from "effect"
|
||||
import { Service } from "@opencode-ai/client/effect"
|
||||
import { Env } from "../../env"
|
||||
import { ServiceConfig } from "../../services/service-config"
|
||||
import { Standalone } from "../../services/standalone"
|
||||
import { Effect, Option } from "effect"
|
||||
import { Server } from "../../services/server"
|
||||
import { Updater } from "../../services/updater"
|
||||
|
||||
export default Runtime.handler(Commands, (input) =>
|
||||
Effect.gen(function* () {
|
||||
const directory = Option.getOrUndefined(input.directory)
|
||||
if (directory !== undefined) process.chdir(directory)
|
||||
const requestedDirectory = Option.getOrUndefined(input.directory)
|
||||
if (requestedDirectory !== undefined) process.chdir(requestedDirectory)
|
||||
const updater = yield* Updater.Service
|
||||
yield* updater.check().pipe(Effect.forkScoped)
|
||||
const server = Option.getOrUndefined(input.server)
|
||||
if (server !== undefined && input.standalone)
|
||||
return yield* Effect.fail(new Error("--server and --standalone cannot be combined"))
|
||||
const transport = yield* Effect.gen(function* () {
|
||||
if (server !== undefined) {
|
||||
const password = yield* Env.password
|
||||
const explicit = {
|
||||
url: server,
|
||||
headers: password
|
||||
? { authorization: "Basic " + btoa("opencode:" + Redacted.value(password)) }
|
||||
: undefined,
|
||||
} satisfies Service.Transport
|
||||
// Fail loudly before entering the TUI: an explicit server that is
|
||||
// unreachable or rejects auth should not present as reconnect churn.
|
||||
const response = yield* Effect.tryPromise(() =>
|
||||
fetch(new URL("/api/health", server), { headers: explicit.headers, signal: AbortSignal.timeout(5_000) }),
|
||||
).pipe(Effect.mapError((cause) => new Error(`Could not reach server at ${server}`, { cause })))
|
||||
if (response.status === 401)
|
||||
return yield* Effect.fail(
|
||||
new Error(
|
||||
password
|
||||
? `Server at ${server} rejected the password`
|
||||
: `Server at ${server} requires a password; set OPENCODE_PASSWORD`,
|
||||
),
|
||||
)
|
||||
if (!response.ok)
|
||||
return yield* Effect.fail(new Error(`Server at ${server} responded with status ${response.status}`))
|
||||
return explicit
|
||||
}
|
||||
if (input.standalone) return yield* Standalone.transport()
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
return found ?? (yield* Service.start(options))
|
||||
const server = yield* Server.resolve({
|
||||
server: Option.getOrUndefined(input.server),
|
||||
standalone: input.standalone,
|
||||
})
|
||||
const { runTui } = yield* Effect.promise(() => import("../../tui"))
|
||||
// The TUI re-runs discover whenever its event stream drops. For an explicit
|
||||
// --server or a standalone child the transport is fixed, so reconnects
|
||||
// retry the same address; for the managed service discovery re-reads the
|
||||
// registration and may start a replacement.
|
||||
const serviceOptions = server === undefined && !input.standalone ? yield* ServiceConfig.options() : undefined
|
||||
// Only startup enforces the CLI version. A reconnect must accept a server
|
||||
// replaced by another client or the two clients will restart it forever.
|
||||
const reconnectOptions = serviceOptions ? { ...serviceOptions, version: undefined } : undefined
|
||||
const discover = reconnectOptions
|
||||
? () =>
|
||||
Effect.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const found = yield* Service.discover(reconnectOptions)
|
||||
return found ?? (yield* Service.start(reconnectOptions))
|
||||
}).pipe(Effect.provide(NodeFileSystem.layer)),
|
||||
)
|
||||
: () => Promise.resolve(transport)
|
||||
// Restart the managed service in place; start() resolves once the
|
||||
// replacement is healthy and the reconnect loop reattaches on its own.
|
||||
// Only meaningful in service mode: --server is not ours to restart and a
|
||||
// standalone child cannot be respawned.
|
||||
const reload = serviceOptions
|
||||
? () =>
|
||||
Effect.runPromise(
|
||||
Effect.gen(function* () {
|
||||
yield* Service.stop(serviceOptions)
|
||||
yield* Service.start(serviceOptions)
|
||||
}).pipe(Effect.provide(NodeFileSystem.layer)),
|
||||
)
|
||||
: undefined
|
||||
yield* runTui(
|
||||
transport,
|
||||
{ continue: input.continue, sessionID: Option.getOrUndefined(input.session) },
|
||||
discover,
|
||||
reload,
|
||||
)
|
||||
const config = TuiConfig.resolve({}, { terminalSuspend: false })
|
||||
let disposeSlots: (() => void) | undefined
|
||||
const runFork = Effect.runForkWith(yield* Effect.context())
|
||||
yield* run({
|
||||
server,
|
||||
args: { continue: input.continue, sessionID: Option.getOrUndefined(input.session) },
|
||||
config,
|
||||
log: (level, message, tags) => {
|
||||
const effect =
|
||||
level === "debug"
|
||||
? Effect.logDebug(message, tags)
|
||||
: level === "warn"
|
||||
? Effect.logWarning(message, tags)
|
||||
: level === "error"
|
||||
? Effect.logError(message, tags)
|
||||
: Effect.logInfo(message, tags)
|
||||
runFork(effect)
|
||||
},
|
||||
pluginHost: {
|
||||
async start(pluginInput) {
|
||||
disposeSlots = await loadBuiltinPlugins(pluginInput.api, pluginInput.runtime)
|
||||
},
|
||||
async dispose() {
|
||||
disposeSlots?.()
|
||||
},
|
||||
},
|
||||
}).pipe(Effect.provide(AppNodeBuilder.build(Global.node)))
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
43
packages/cli/src/commands/handlers/link.ts
Normal file
43
packages/cli/src/commands/handlers/link.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { EOL } from "os"
|
||||
import { Effect } from "effect"
|
||||
import { Service } from "@opencode-ai/client/effect"
|
||||
import { OpenCode } from "@opencode-ai/client/promise"
|
||||
import { renderUnicodeCompact } from "uqr"
|
||||
import { Commands } from "../commands"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { ServiceConfig } from "../../services/service-config"
|
||||
|
||||
export default Runtime.handler(
|
||||
Commands.commands.link,
|
||||
Effect.fn("cli.link")(function* () {
|
||||
const endpoint = yield* Service.start(yield* ServiceConfig.options())
|
||||
const password = yield* ServiceConfig.password()
|
||||
const server = yield* Effect.tryPromise(() =>
|
||||
OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) }).server.get(),
|
||||
)
|
||||
const info = { urls: server.urls, username: "opencode", password }
|
||||
process.stdout.write(
|
||||
[
|
||||
"",
|
||||
` URLs ${info.urls[0] ?? "(none)"}`,
|
||||
...info.urls.slice(1).map((url) => ` ${url}`),
|
||||
` Username ${info.username}`,
|
||||
` Password ${info.password}`,
|
||||
"",
|
||||
" Scan to connect",
|
||||
"",
|
||||
renderUnicodeCompact(JSON.stringify(info), { border: 2 })
|
||||
.split(EOL)
|
||||
.map((line) => " " + line)
|
||||
.join(EOL),
|
||||
"",
|
||||
].join(EOL) + EOL,
|
||||
)
|
||||
|
||||
const hostname = new URL(endpoint.url).hostname
|
||||
if (!["localhost", "127.0.0.1", "[::1]"].includes(hostname)) return
|
||||
process.stderr.write(
|
||||
` Run \`opencode service set hostname 0.0.0.0\` to access the service remotely.${EOL}${EOL}`,
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
|
@ -19,8 +19,8 @@ export default Runtime.handler(
|
|||
Effect.fn("cli.mcp.auth")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const transport = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: transport.url, headers: transport.headers })
|
||||
const endpoint = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
|
||||
const integration = yield* resolveIntegration(client, input.name, location)
|
||||
if (!integration)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default Runtime.handler(
|
|||
Effect.fn("cli.mcp.list")(function* () {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const transport = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: transport.url, headers: transport.headers })
|
||||
const endpoint = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
const response = yield* Effect.promise(() => client.v2.mcp.list({ location: { directory: process.cwd() } }))
|
||||
const servers = (response.data?.data ?? []).toSorted((a, b) => a.name.localeCompare(b.name))
|
||||
if (servers.length === 0) {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export default Runtime.handler(
|
|||
Effect.fn("cli.mcp.logout")(function* (input) {
|
||||
const options = yield* ServiceConfig.options()
|
||||
const found = yield* Service.discover(options)
|
||||
const transport = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: transport.url, headers: transport.headers })
|
||||
const endpoint = found ?? (yield* Service.start(options))
|
||||
const client = createOpencodeClient({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
|
||||
|
||||
const integration = yield* resolveIntegration(client, input.name, location)
|
||||
if (!integration) {
|
||||
|
|
|
|||
|
|
@ -1,25 +1,17 @@
|
|||
import { Effect, Option, Redacted } from "effect"
|
||||
import path from "node:path"
|
||||
import { Effect, Option } from "effect"
|
||||
import { Commands } from "../commands"
|
||||
import { Env } from "../../env"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { Server } from "../../services/server"
|
||||
|
||||
export default Runtime.handler(Commands.commands.mini, (input) =>
|
||||
Effect.gen(function* () {
|
||||
const { runMini } = yield* Effect.promise(() => import("../../mini"))
|
||||
const project = Option.getOrUndefined(input.project)
|
||||
const server = Option.getOrUndefined(input.server)
|
||||
const password = yield* Env.password
|
||||
const { runMini, validateMiniTerminal } = yield* Effect.promise(() => import("../../mini"))
|
||||
yield* Effect.promise(async () => validateMiniTerminal())
|
||||
const serverURL = Option.getOrUndefined(input.server)
|
||||
const server = yield* Server.resolve({ server: serverURL, standalone: input.standalone })
|
||||
yield* Effect.promise(() =>
|
||||
runMini({
|
||||
attach: server,
|
||||
password: password ? Redacted.value(password) : undefined,
|
||||
directory:
|
||||
server !== undefined
|
||||
? project
|
||||
: project === undefined
|
||||
? process.cwd()
|
||||
: path.resolve(process.env.PWD ?? process.cwd(), project),
|
||||
server,
|
||||
continue: input.continue,
|
||||
session: Option.getOrUndefined(input.session),
|
||||
fork: input.fork,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
import { Effect, Option, Redacted } from "effect"
|
||||
import { Effect, Option } from "effect"
|
||||
import { Commands } from "../commands"
|
||||
import { Env } from "../../env"
|
||||
import { Runtime } from "../../framework/runtime"
|
||||
import { Server } from "../../services/server"
|
||||
|
||||
export default Runtime.handler(Commands.commands.run, (input) =>
|
||||
Effect.gen(function* () {
|
||||
const { runNonInteractive } = yield* Effect.promise(() => import("../../mini"))
|
||||
const password = yield* Env.password
|
||||
const separator = process.argv.indexOf("--", 2)
|
||||
const server = yield* Server.resolve({
|
||||
server: Option.getOrUndefined(input.server),
|
||||
standalone: input.standalone,
|
||||
})
|
||||
yield* Effect.promise(() =>
|
||||
runNonInteractive({
|
||||
server,
|
||||
message: [...input.message, ...(separator === -1 ? [] : process.argv.slice(separator + 1))],
|
||||
continue: input.continue,
|
||||
session: Option.getOrUndefined(input.session),
|
||||
|
|
@ -19,12 +23,8 @@ export default Runtime.handler(Commands.commands.run, (input) =>
|
|||
format: input.format,
|
||||
file: [...input.file],
|
||||
title: Option.getOrUndefined(input.title),
|
||||
server: Option.getOrUndefined(input.server),
|
||||
password: password ? Redacted.value(password) : undefined,
|
||||
directory: Option.getOrUndefined(input.dir),
|
||||
variant: Option.getOrUndefined(input.variant),
|
||||
thinking: input.thinking,
|
||||
dangerouslySkipPermissions: input.auto || input.yolo || input.dangerouslySkipPermissions,
|
||||
auto: input.auto || input.yolo,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
import { Service } from "@opencode-ai/client/effect"
|
||||
import { ClientError, isUnauthorizedError, OpenCode } from "@opencode-ai/client/promise"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { ServerAuth } from "@opencode-ai/server/auth"
|
||||
import { Effect } from "effect"
|
||||
import { ServiceConfig } from "./services/service-config"
|
||||
|
||||
export type SharedOptions = {
|
||||
readonly mode: "shared"
|
||||
readonly command?: ReadonlyArray<string>
|
||||
}
|
||||
export type AttachOptions = {
|
||||
readonly mode: "attach"
|
||||
readonly url: string
|
||||
readonly username?: string
|
||||
readonly password?: string
|
||||
}
|
||||
export type Options = SharedOptions | AttachOptions
|
||||
|
||||
const attach = Effect.fn("cli.daemon.attach")(function* (options: AttachOptions) {
|
||||
const transport = {
|
||||
url: options.url,
|
||||
headers:
|
||||
options.password === undefined
|
||||
? undefined
|
||||
: ServerAuth.headers({ password: options.password, username: options.username }),
|
||||
} satisfies Service.Transport
|
||||
const client = OpenCode.make({ baseUrl: transport.url, headers: transport.headers })
|
||||
const health = yield* Effect.tryPromise({
|
||||
try: () => client.health.get({ signal: AbortSignal.timeout(5_000) }),
|
||||
catch: (cause) => attachError(options, cause),
|
||||
})
|
||||
if (health.version !== InstallationVersion)
|
||||
process.stderr.write(
|
||||
`Warning: Server at ${options.url} has version ${health.version}; this client is ${InstallationVersion}. Continuing anyway.\n`,
|
||||
)
|
||||
return transport
|
||||
})
|
||||
|
||||
const shared = Effect.fn("cli.daemon.shared")(function* (options: SharedOptions) {
|
||||
const config = yield* ServiceConfig.options()
|
||||
const service = options.command === undefined ? config : { ...config, command: options.command }
|
||||
const found = yield* Service.discover(service)
|
||||
if (found) return found
|
||||
return yield* Service.start(service)
|
||||
})
|
||||
|
||||
export function transport(options: AttachOptions): ReturnType<typeof attach>
|
||||
export function transport(options: SharedOptions): ReturnType<typeof shared>
|
||||
export function transport(options: Options): ReturnType<typeof attach> | ReturnType<typeof shared>
|
||||
export function transport(options: Options) {
|
||||
if (options.mode === "attach") return attach(options)
|
||||
return shared(options)
|
||||
}
|
||||
|
||||
function attachError(options: AttachOptions, cause: unknown) {
|
||||
if (isUnauthorizedError(cause)) {
|
||||
return new Error(
|
||||
options.password === undefined
|
||||
? `Server at ${options.url} requires authentication; provide a password`
|
||||
: `Server at ${options.url} rejected the supplied credentials`,
|
||||
{ cause },
|
||||
)
|
||||
}
|
||||
if (cause instanceof ClientError && cause.reason === "Transport")
|
||||
return new Error(`Could not reach server at ${options.url}`, { cause })
|
||||
return new Error(`Server at ${options.url} did not provide a compatible V2 health response`, { cause })
|
||||
}
|
||||
|
||||
export * as Daemon from "./daemon"
|
||||
|
|
@ -24,6 +24,9 @@ const Handlers = Runtime.handlers(Commands, {
|
|||
debug: {
|
||||
agents: () => import("./commands/handlers/debug/agents"),
|
||||
},
|
||||
console: {
|
||||
login: () => import("./commands/handlers/console/login"),
|
||||
},
|
||||
mcp: {
|
||||
list: () => import("./commands/handlers/mcp/list"),
|
||||
add: () => import("./commands/handlers/mcp/add"),
|
||||
|
|
@ -33,6 +36,7 @@ const Handlers = Runtime.handlers(Commands, {
|
|||
migrate: () => import("./commands/handlers/migrate"),
|
||||
mini: () => import("./commands/handlers/mini"),
|
||||
run: () => import("./commands/handlers/run"),
|
||||
link: () => import("./commands/handlers/link"),
|
||||
service: {
|
||||
start: () => import("./commands/handlers/service/start"),
|
||||
restart: () => import("./commands/handlers/service/restart"),
|
||||
|
|
|
|||
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