Compare commits

...
Author SHA1 Message Date
LukeParkerDev 21197e4e6f perf(desktop): enable the V8 code cache for the renderer scheme
Chromium only persists compiled bytecode for schemes that opt in, so
every launch re-parsed and re-compiled the whole renderer bundle. With
the codeCache privilege on oc:// the third launch onwards loads from the
Code Cache directory.

Packaged build, warm service, renderer main-thread task time until idle
481 -> 447 ms (script 110 -> 83 ms); shell visible 450 -> 405 ms after
the renderer process starts.
2026-09-18 23:00:28 +10:00
Shoubhit Dash db80806651 refactor(core): move native compaction mechanisms into a plugin (#49575) 2026-09-18 17:15:44 +05:30
Luke Parker 47f66de8dd perf(app): load draft image bytes on demand (#49703) 2026-09-18 19:32:15 +10:00
opencode c076066c33 sync release versions for v2.0.8 2026-09-18 09:12:33 +00:00
opencode-agent[bot] 5c09d051e5 chore: update nix node_modules hashes 2026-09-18 08:45:27 +00:00
Luke Parker 0a6111291e refactor(desktop): send IPC messages by structured clone (#49701) 2026-09-18 18:28:20 +10:00
Luke Parker 609044ef0a feat(app): scan pairing QR code from add server dialog (#49699) 2026-09-18 17:36:30 +10:00
Luke Parker 4f32bfca57 fix(desktop): copy IPC payloads out of the shared msgpack buffer (#49696) 2026-09-18 17:14:29 +10:00
Luke Parker 90112f52db fix(app): deliver text and oversized attachments by path (#49682) 2026-09-18 06:30:57 +00:00
Luke Parker d4303a9ca5 feat(app): show provider metrics in debug bar (#49674) 2026-09-18 15:51:08 +10:00
Kit Langton 735556eab8 fix(tui): open completed subagent sessions (#49675) 2026-09-18 05:42:40 +00:00
Kit Langton 10cac9ab5d fix(tui): show update installation progress (#49676) 2026-09-17 22:33:43 -07:00
Kit Langton 2d0ce64111 fix(client): reconcile stale tools at settlement (#49672) 2026-09-17 22:23:45 -07:00
Luke Parker 3355c93efd fix(app): create local sessions in project root (#49668) 2026-09-18 14:12:18 +10:00
Luke Parker 469e1c035e feat(app): stream large attachments to the server with progress (#49647) 2026-09-18 14:05:30 +10:00
opencode-agent[bot]andrekram1-node b2e3569add test(codemode): isolate OpenAPI fixtures (#49667)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-09-17 23:01:36 -05:00
James Long 0ac458b3b3 refactor(theme): rename surface tokens to raised (#49655) 2026-09-17 22:05:42 -04:00
Brendan DukeandAiden Cline b278ef6b82 fix(tui): include reasoning tokens in throughput (#48689)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-09-17 20:12:06 -05:00
Dax 3e3a4ae46b feat(tui): add /btw side question command (#49646) 2026-09-17 21:03:09 -04:00
Dax Raad fe0d9579a7 fix(tui): preserve block tool spacing 2026-09-17 20:18:52 -04:00
Aiden Cline a2594ddefb fix(tui): sync model preferences across clients (#49611) 2026-09-17 14:54:34 -05:00
Aiden Cline 3555cef1c8 chore(client): regenerate policy types (#49613) 2026-09-17 14:38:09 -05:00
opencode 0fa881efd0 sync release versions for v2.0.7 2026-09-17 19:29:59 +00:00
opencode-agent[bot]andrekram1-node 37ff501cc6 fix(cli): keep updates client-owned (#49577)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-09-17 12:47:49 -04:00
Dax fa126d68e8 feat(core): enforce permission policies 2026-09-17 12:39:20 -04:00
opencode-agent[bot]andJames Long a0571a8b14 fix(tui): keep upgrade as a search keyword (#49567)
Co-authored-by: James Long <17031+jlongster@users.noreply.github.com>
2026-09-17 11:32:16 -04:00
Dax Raad cae475df37 sync 2026-09-17 10:47:52 -04:00
Shoubhit Dash 19781a4447 fix(acp): propagate request cancellation and close sessions cleanly (#49563) 2026-09-17 20:16:07 +05:30
Brendan Allan e32de90d88 fix(app): prevent settings project cache reload loop (#49564) 2026-09-17 14:45:03 +00:00
Kit Langton 0ee5ef6c43 fix(tui): keep spinner glyph from shrinking beside long labels (#49395) 2026-09-17 07:43:07 -07:00
a72f988c90 fix(tui): switch vertical tabs sooner (#49440)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-09-17 09:43:02 -05:00
Dax Raad 2a132d6f50 fix(tui): include provider latency in TPS 2026-09-17 10:40:12 -04:00
Dax Raad 23418b3885 docs: reorder sidebar navigation 2026-09-17 10:20:57 -04:00
Dax Raad b4ac938c3d docs: improve installation guide 2026-09-17 10:10:20 -04:00
Dax Raad 4b25739e93 docs(cli): drop command-name leads in command descriptions 2026-09-17 09:44:31 -04:00
Dax Raad 2f97e1324f fix(www): restore OpenTUI Mono docs font 2026-09-17 09:31:40 -04:00
Dax Raad 0018f08832 feat(cli): add reload command 2026-09-17 09:21:41 -04:00
Dax Raad b52f241294 feat(location): reload configuration 2026-09-17 09:18:51 -04:00
Dax Raad 254ace8594 docs: add CLI settings reference 2026-09-17 09:17:14 -04:00
usrnk1 657f700f5d feat(desktop): disable security code autofill (#49531) 2026-09-17 20:35:14 +08:00
Victor Navarro 7e1d28b549 feat(cli): support custom Console logins (#49542) 2026-09-17 13:50:32 +02:00
opencode 040d2ffb48 sync release versions for v2.0.6 2026-09-17 11:17:33 +00:00
usrnk1 0675975f98 feat(desktop): remove pointer cursor from non-link elements (#49323) 2026-09-17 20:41:24 +10:00
opencode-agent[bot]andiamdavidhill 130232feb6 chore(desktop): update bundled themes (#48713)
Co-authored-by: iamdavidhill <iamdavidhill@users.noreply.github.com>
2026-09-17 20:40:07 +10:00
opencode-agent[bot]andiamdavidhill 54f2356166 fix(app): remove Anthropic Pro and Max copy (#49369)
Co-authored-by: iamdavidhill <1879069+iamdavidhill@users.noreply.github.com>
2026-09-17 20:39:39 +10:00
David Hill ac0dd75c6a fix(app): hide browser when the right panel closes (#49425) 2026-09-17 20:39:02 +10:00
David Hill 430886142b fix(app): center start screen beside summary panels (#49429) 2026-09-17 20:38:36 +10:00
David Hill 5a02ec776e fix(app): use muted worktree icon in session timeline (#49459) 2026-09-17 20:37:37 +10:00
David Hill c303a11725 fix(app): mute session tab hover and active backgrounds (#49465) 2026-09-17 20:37:13 +10:00
David Hill dfceca8de7 fix(app): refine start-screen logo (#49475) 2026-09-17 20:36:45 +10:00
Luke Parker f04c3fd82b feat(app): attach any file and deliver unsupported ones by path (#49467) 2026-09-17 20:35:00 +10:00
Luke Parker 1685e70c4b feat(server): add fs.write endpoint (#49466) 2026-09-17 20:17:14 +10:00
Chris Yang 73e7b7bc79 fix(app): derive directory names from listed paths (#49122) 2026-09-17 18:46:07 +10:00
David Hill a5b3802ca3 fix(app): keep compact tab icons visible (#49460) 2026-09-17 14:48:47 +08:00
Dax Raad f28d1b44e3 fix(core): serialize MCP endpoint startup 2026-09-17 00:52:34 -04:00
Dax Raad b81be469c8 fix(client): preserve state during location reload 2026-09-17 00:52:34 -04:00
opencode-agent[bot]andkitlangton fca4a8fa3b feat(tui): show explicit subagent model (#49388)
Co-authored-by: kitlangton <7587245+kitlangton@users.noreply.github.com>
2026-09-16 22:50:28 -05:00
Aiden Cline 8520617ca8 fix(core): retry provider failures for about 84s with 10s gap cap (#49441) 2026-09-16 20:59:16 -05:00
Dax Raad ab3566ab82 feat(server): expose server info endpoint 2026-09-16 21:43:58 -04:00
Dax Raad b642c1d9ea docs: add CLI settings reference 2026-09-16 21:18:35 -04:00
David HillandLukeParkerDev c8cc984aa0 feat(app): improve project settings and actions (#49423)
Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
2026-09-17 00:39:21 +00:00
Luke Parker 5a9448acbb fix(app): retry transport errors wrapped by the client (#49426) 2026-09-17 10:34:52 +10:00
David Hill f6604cd367 feat(desktop): add animated first-launch loading screen (#49408) 2026-09-17 10:02:09 +10:00
Luke Parker acfacede2d fix(app): preserve native clipboard paste (#49424) 2026-09-17 09:47:00 +10:00
Aiden Cline 4a27842fe6 fix(ai): classify gateway account limits as quota and keep 4xx non-retryable (#49195) 2026-09-16 16:14:09 -05:00
Aiden Cline d797722187 feat(codemode): carry cause and own data across the error boundary (#49390) 2026-09-16 15:58:05 -05:00
Dax 7390832f13 feat(tui): reload all locations
Add a global location reload endpoint, shared shutdown lifecycle, client resynchronization, and the TUI /reload command.\n\nfrom OpenCode
2026-09-16 16:47:44 -04:00
Kit Langton 7689c3654e fix(tui): hide error hint when MCP Enter starts sign-in (#49403) 2026-09-16 19:33:08 +00:00
Dax 7df0935ada fix(tui): apply model selection on blank submit (#49374)
Apply the selected model on blank Enter and simplify shared prompt submission setup and recovery.\n\nfrom OpenCode
2026-09-16 19:32:45 +00:00
Dax bcd43760df feat(cli): support inline config content (#49399) 2026-09-16 19:18:55 +00:00
Aiden Cline 9073c522ef feat(plugin): add experimental WebSocket send and receive hooks (#49136) 2026-09-16 14:13:33 -05:00
opencode-agent[bot]andrekram1-node 04c296310e fix(core): skip session warming for subagents (#49387)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-09-16 13:58:40 -05:00
429 changed files with 7065 additions and 2526 deletions
+1
View File
@@ -184,6 +184,7 @@ const table = sqliteTable("session", {
- Keep `SessionRunner`, model resolution, tool registry, permissions, and filesystem Location-scoped. Omitted `Location.workspaceID` means implicit-local placement; explicit workspace identity remains reserved for future placement semantics.
- Preserve one explicit `llm.stream(request)` call per Physical Attempt and reload projected history before durable continuation. A logical Step may use generic pre-output retries, one full-context retry after continuation rejection, incomplete-stream continuation, or one overflow-compaction rebuild. Generic retries retain the logical step number and do not consume another agent-step allowance. Do not delegate orchestration to an in-memory tool loop.
- Keep local Session drains process-local until clustering is implemented. `SessionRunCoordinator` joins explicit same-Session resumes, coalesces prompt wakeups, and allows different Sessions to run concurrently. A write-ahead execution claim marks a process-local busy period for restart recovery: terminal completion, failure, or user interruption releases it, while shutdown interruption and process death preserve it. Startup recovery resumes claimed top-level Sessions with durable per-execution attempt accounting. The claim is a recovery marker, not clustered ownership, fencing, or an exactly-once guarantee.
- Keep native compaction mechanisms out of `SessionCompaction`. Plugins register `native` strategies through the `SessionCompaction` editor that turn a prepared request into a replacement window (the built-in `NativeCompactionPlugin` handles `@opencode/ai` compaction operations); later registrations win. Core owns the provider-mode decision, route provenance, the retry policy, overflow recovery, interruption, usage accounting, and checkpoint persistence.
- Keep delivery vocabulary explicit. Prompts steer by default. At safe step boundaries, steered compaction takes priority up to the first steered move control; other steers retain enqueue order. At an idle boundary, steers take priority; otherwise exactly one queued item delivers before the runner reevaluates continuation. Inbox items may be cancelled or changed between queue and steer before delivery. Promoting new user input resets the selected agent's step allowance; a batch of steers resets it once.
- One step is one logical LLM call; its durable record covers only the model-visible span. Do not write "provider turn", and do not use bare "turn" for a single call: "turn" is reserved for the future assistant-turn unit containing all steps from prompt promotion until the session would go idle.
- Keep event replay ownership separate from clustered Session execution ownership.
+2 -2
View File
@@ -54,7 +54,7 @@ Review endpoints in document order. For each endpoint, select one disposition an
### [x] `GET /api/health` and `GET /api/server`
- **Decision:** Merge and rename
- **Replacement:** `GET /api/status` with operation ID `server.status`.
- **Replacement:** `GET /api/info` with operation ID `server.info`.
- **Notes:** Returns `version`, `pid`, and connection `urls`; readiness is conveyed by HTTP status.
### [x] `GET /api/project/current`
@@ -74,7 +74,7 @@ Review endpoints in document order. For each endpoint, select one disposition an
| Done | Method | Path | Operation ID | Decision | Notes |
|---|---|---|---|---|---|
| [x] 001–002 | `GET` | `/api/status` | `server.status` | Keep | Replaces the former health and server endpoints. |
| [x] 001–002 | `GET` | `/api/info` | `server.info` | Keep | Replaces the former health and server endpoints. |
| [x] 003 | `GET` | `/api/location` | `location.get` | Keep | Workspace selectors and response fields removed until workspace support ships. |
| [x] 004 | `GET` | `/api/project` | `project.list` | Keep | Removed unused `time.initialized`; the database column remains for migration data. |
| [x] 005 | `PATCH` | `/api/project/{projectID}` | `project.update` | Keep | Request and response accepted as-is. |
+35 -36
View File
@@ -32,7 +32,7 @@
},
"packages/ai": {
"name": "@opencode/ai",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@aws-sdk/credential-providers": "3.1057.0",
"@opencode/schema": "workspace:*",
@@ -54,7 +54,7 @@
},
"packages/app": {
"name": "@opencode/app",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@corvu/drawer": "catalog:",
"@dnd-kit/abstract": "0.5.0",
@@ -113,7 +113,7 @@
},
"packages/cli": {
"name": "@opencode/cli",
"version": "2.0.5",
"version": "2.0.8",
"bin": {
"opencode2": "./bin/opencode2.cjs",
},
@@ -177,7 +177,7 @@
},
"packages/client": {
"name": "@opencode/client",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/protocol": "workspace:*",
"@opencode/schema": "workspace:*",
@@ -203,7 +203,7 @@
},
"packages/codemode": {
"name": "@opencode/codemode",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"acorn": "8.15.0",
"effect": "catalog:",
@@ -217,7 +217,7 @@
},
"packages/console/app": {
"name": "@opencode/console-app",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -253,7 +253,7 @@
},
"packages/console/core": {
"name": "@opencode/console-core",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -280,7 +280,7 @@
},
"packages/console/function": {
"name": "@opencode/console-function",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@openauthjs/openauth": "0.0.0-20250322224806",
"@opencode/console-core": "workspace:*",
@@ -297,7 +297,7 @@
},
"packages/console/mail": {
"name": "@opencode/console-mail",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -321,7 +321,7 @@
},
"packages/console/support": {
"name": "@opencode/console-support",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@opencode/console-core": "workspace:*",
@@ -341,7 +341,7 @@
},
"packages/core": {
"name": "@opencode/core",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@ai-sdk/cohere": "3.0.27",
"@ai-sdk/gateway": "3.0.104",
@@ -409,7 +409,7 @@
},
"packages/desktop": {
"name": "@opencode/desktop",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"electron-context-menu": "4.1.2",
@@ -456,12 +456,11 @@
"@lydell/node-pty-linux-x64": "1.2.0-beta.12",
"@lydell/node-pty-win32-arm64": "1.2.0-beta.12",
"@lydell/node-pty-win32-x64": "1.2.0-beta.12",
"msgpackr-extract": "3.0.4",
},
},
"packages/enterprise": {
"name": "@opencode/enterprise",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@hono/standard-validator": "catalog:",
"@opencode-ai/sdk": "1.18.21",
@@ -498,7 +497,7 @@
},
"packages/function": {
"name": "@opencode/function",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -514,7 +513,7 @@
},
"packages/http-recorder": {
"name": "@opencode/http-recorder",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@effect/platform-node-shared": "4.0.0-rc.112",
},
@@ -533,7 +532,7 @@
},
"packages/httpapi-codegen": {
"name": "@opencode/httpapi-codegen",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"effect": "catalog:",
"prettier": "3.6.2",
@@ -546,7 +545,7 @@
},
"packages/latex": {
"name": "@opencode/latex",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/plugin": "workspace:*",
"@opentui/core": "catalog:",
@@ -560,7 +559,7 @@
},
"packages/merman": {
"name": "@opencode/merman",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/plugin": "workspace:*",
"@opentui/core": "catalog:",
@@ -575,7 +574,7 @@
},
"packages/plugin": {
"name": "@opencode/plugin",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@ai-sdk/provider": "3.0.8",
"@opencode/ai": "workspace:*",
@@ -614,7 +613,7 @@
},
"packages/plugin-browser": {
"name": "@opencode/plugin-browser",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/plugin": "workspace:*",
"@opencode/schema": "workspace:*",
@@ -644,7 +643,7 @@
},
"packages/protocol": {
"name": "@opencode/protocol",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/schema": "workspace:*",
"effect": "catalog:",
@@ -659,7 +658,7 @@
},
"packages/schema": {
"name": "@opencode/schema",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@standard-schema/spec": "catalog:",
"effect": "catalog:",
@@ -683,7 +682,7 @@
},
"packages/sdk": {
"name": "@opencode/sdk",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/client": "workspace:*",
"@opencode/core": "workspace:*",
@@ -704,7 +703,7 @@
},
"packages/server": {
"name": "@opencode/server",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@effect/platform-node": "catalog:",
"@effect/platform-node-shared": "catalog:",
@@ -726,7 +725,7 @@
},
"packages/session-ui": {
"name": "@opencode/session-ui",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode/client": "workspace:*",
@@ -761,7 +760,7 @@
},
"packages/simulation": {
"name": "@opencode/simulation",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/ai": "workspace:*",
"@opencode/core": "workspace:*",
@@ -781,7 +780,7 @@
},
"packages/stats/app": {
"name": "@opencode/stats-app",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@ibm/plex": "6.4.1",
"@kobalte/core": "catalog:",
@@ -815,7 +814,7 @@
},
"packages/stats/core": {
"name": "@opencode/stats-core",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@aws-sdk/client-athena": "3.933.0",
"@planetscale/database": "1.19.0",
@@ -834,7 +833,7 @@
},
"packages/stats/server": {
"name": "@opencode/stats-server",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@aws-sdk/client-firehose": "3.933.0",
"@effect/platform-node": "catalog:",
@@ -880,7 +879,7 @@
},
"packages/theme": {
"name": "@opencode/theme",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opentui/core": "catalog:",
"effect": "catalog:",
@@ -894,7 +893,7 @@
},
"packages/tui": {
"name": "@opencode/tui",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@opencode/client": "workspace:*",
"@opencode/core": "workspace:*",
@@ -929,7 +928,7 @@
},
"packages/ui": {
"name": "@opencode/ui",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@kobalte/core": "catalog:",
"@pierre/diffs": "catalog:",
@@ -964,7 +963,7 @@
},
"packages/util": {
"name": "@opencode/util",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@effect/opentelemetry": "catalog:",
"@effect/platform-node": "catalog:",
@@ -997,7 +996,7 @@
},
"packages/web": {
"name": "@opencode/web",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -1038,7 +1037,7 @@
},
"services/update": {
"name": "@opencode/update",
"version": "2.0.5",
"version": "2.0.8",
"dependencies": {
"jose": "6.0.11",
"semver": "catalog:",
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-U9IuP/ev6w4urvogOwQyl3rdumY6W4YaY18NkFaOVHU=",
"aarch64-linux": "sha256-Wc8OT2DRZpVo56KaoGE0Hsj1NDknakbWXO9w2qy6j+0=",
"aarch64-darwin": "sha256-wAea8+jajnMDxZ6XJL+Hsrf0621hwtBtWyD1+dS45dE=",
"x86_64-darwin": "sha256-g8PCNBSV6rO+VQjKU9AtYqj+r18o+fhLDXEQq+X2EZ4="
"x86_64-linux": "sha256-TlCPtuIvTNF01fLI0yHCLzVkx53yF9xxvAObDMGeo8M=",
"aarch64-linux": "sha256-aEjyPIXHeA5w1iZXbPKJAOIJQBfXg+LAx/y7C5uFz4g=",
"aarch64-darwin": "sha256-lthMTio2qNy+H2tL9cAPmqC41O+wdSADI61cnW4dIb0=",
"x86_64-darwin": "sha256-ONFbRQnwzOYHo0Pyh327QfJG2uzqzNEn/SS2vbYYMdM="
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "opencode",
"description": "AI-powered development tool",
"version": "2.0.5",
"version": "2.0.8",
"private": true,
"type": "module",
"packageManager": "bun@1.4.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "2.0.5",
"version": "2.0.8",
"name": "@opencode/ai",
"type": "module",
"license": "MIT",
+21 -6
View File
@@ -59,7 +59,15 @@ export const isContextOverflowFailure = (failure: unknown) =>
: Schema.is(ProviderErrorEvent)(failure) && failure.classification === "context-overflow"
const decodeJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown))
const QUOTA_CODES = new Set(["insufficient_quota", "usage_not_included", "billing_error"])
// OpenCode Zen reports account caps as typed 429/402 errors that are not throttles.
const QUOTA_CODES = new Set([
"insufficient_quota",
"usage_not_included",
"billing_error",
"gousagelimiterror",
"freeusagelimiterror",
"creditlimitexceeded",
])
const AUTH_CODES = new Set(["authentication_error", "permission_error"])
const SERVER_CODES = new Set([
"api_error",
@@ -87,7 +95,8 @@ const CONTENT_POLICY_CODES = new Set([
// as a `[code]` label at the start of the rewritten message.
const GATEWAY_CODE_LABEL = /^[^:\n]+: \[([A-Za-z0-9_.-]+)\]/
const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i
const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded/i
// Only consulted on 429, where throttles and account caps share a status.
const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded|budget exceeded|usage limit/i
// Policy rejections without a dedicated code, matched against the provider's own
// explanation only. OpenAI reuses `invalid_prompt` for usage-policy rejections while
// Bedrock Mantle reuses it for schema validation; Anthropic reports blocked output
@@ -143,7 +152,11 @@ export function classifyProviderFailure(input: ProviderFailure): AIError["reason
return new InvalidRequestError({ ...details, classification: "payload-too-large" })
if (codes.some((code) => CONTENT_POLICY_CODES.has(code)) || (clientScoped && CONTENT_POLICY_TEXT.test(input.message)))
return new ContentPolicyError(details)
if (codes.some((code) => QUOTA_CODES.has(code)) || (input.status === 429 && QUOTA_TEXT.test(text)))
if (
input.status === 402 ||
codes.some((code) => QUOTA_CODES.has(code)) ||
(input.status === 429 && QUOTA_TEXT.test(text))
)
return new QuotaExceededError(details)
if (input.status === 401 || input.status === 403 || codes.some((code) => AUTH_CODES.has(code)))
return new AuthenticationError(details)
@@ -163,10 +176,12 @@ export function classifyProviderFailure(input: ProviderFailure): AIError["reason
input.status === 408 ||
input.status === 409 ||
(input.status !== undefined && input.status >= 500) ||
// Server codes and phrasing only decide when no HTTP status contradicts them:
// gateways such as OpenCode Zen substitute `server_error` for codes they do
// not forward, so a 4xx with a server code is still a rejected request.
((input.status === undefined || input.status < 400) &&
!codes.some((code) => INVALID_REQUEST_CODES.has(code)) &&
SERVER_ERROR_TEXT.test(text)) ||
codes.some((code) => SERVER_CODES.has(code) || code.includes("exhausted") || code.includes("unavailable"))
((!codes.some((code) => INVALID_REQUEST_CODES.has(code)) && SERVER_ERROR_TEXT.test(text)) ||
codes.some((code) => SERVER_CODES.has(code) || code.includes("exhausted") || code.includes("unavailable"))))
)
return new ProviderInternalError({
...details,
+3 -3
View File
@@ -309,7 +309,7 @@ describe("RequestExecutor", () => {
}),
)
it.effect("classifies provider overloads hidden behind HTTP 400", () =>
it.effect("does not let server codes override a 4xx rejection", () =>
Effect.gen(function* () {
const classify = (body: string) =>
Effect.gen(function* () {
@@ -317,11 +317,11 @@ describe("RequestExecutor", () => {
const error = yield* executor.execute(request).pipe(Effect.flip)
expectAIError(error)
expect(error.reason).toMatchObject({ _tag: "ProviderInternal" })
expect(error.reason).toMatchObject({ _tag: "InvalidRequest" })
}).pipe(Effect.provide(fixedResponse(body, { status: 400 })))
yield* classify('{"code":"resource_exhausted"}')
yield* classify('{"code":"service_unavailable"}')
yield* classify('{"error":{"type":"server_error","message":"Upstream request failed: Model is unavailable."}}')
}),
)
+47 -3
View File
@@ -249,10 +249,54 @@ describe("provider error classification", () => {
test("classifies any remaining 4xx status as an invalid request", () => {
expect(
[400, 402, 404, 418, 422, 451].map(
(status) => classifyProviderFailure({ message: `HTTP ${status}`, status })._tag,
[400, 404, 418, 422, 451].map((status) => classifyProviderFailure({ message: `HTTP ${status}`, status })._tag),
).toEqual(Array(5).fill("InvalidRequest"))
})
test("classifies 402 as exhausted quota", () => {
expect(classifyProviderFailure({ message: "Payment Required", status: 402 })._tag).toBe("QuotaExceeded")
})
test("classifies OpenCode Zen account limits as quota rather than throttling", () => {
const typed = (type: string, message: string) => ({ type: "error", error: { type, message } })
const substituted = (message: string) => ({
error: { type: "server_error", message: `Upstream request failed: ${message}` },
})
const cases: ReadonlyArray<[number, { error: { message: string } }]> = [
[429, typed("GoUsageLimitError", "Go usage limit exceeded")],
[429, typed("FreeUsageLimitError", "Rate limit exceeded. Please try again later.")],
[402, typed("CreditLimitExceeded", "Credit limit exceeded.")],
[402, substituted("Insufficient account funds")],
[402, substituted("Account invoice is overdue")],
[429, substituted("Account budget exceeded")],
]
expect(
cases.map(
([status, body]) =>
classifyProviderFailure({ message: body.error.message, status, rawBody: JSON.stringify(body) })._tag,
),
).toEqual(Array(6).fill("InvalidRequest"))
).toEqual(Array(6).fill("QuotaExceeded"))
})
test("does not let substituted server codes make a 4xx retryable", () => {
const openai = { error: { type: "server_error", message: "Upstream request failed: Model is unavailable." } }
const anthropic = {
type: "error",
error: { type: "api_error", message: "Upstream request failed: Model is unavailable." },
}
expect(
[openai, anthropic].map(
(body) =>
classifyProviderFailure({ message: body.error.message, status: 400, rawBody: JSON.stringify(body) })._tag,
),
).toEqual(["InvalidRequest", "InvalidRequest"])
// Without a contradicting status the same codes still mark provider trouble.
expect(classifyProviderFailure({ message: openai.error.message, rawBody: JSON.stringify(openai) })._tag).toBe(
"ProviderInternal",
)
expect(
classifyProviderFailure({ message: openai.error.message, status: 200, rawBody: JSON.stringify(openai) })._tag,
).toBe("ProviderInternal")
})
test("classifies nested provider codes when a top-level code is also present", () => {
@@ -42,3 +42,18 @@ story("hides and restores the same registration for Review tabs and unmount", as
await root.getByRole("button", { name: "Unmount pane", exact: true }).click()
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
})
story("hides the native view immediately while the pane stays mounted", async ({ page }) => {
const root = page.getByTestId("browser-pane-fixture")
const toggle = root.getByRole("button", { name: "Toggle Review tab", exact: true })
await expect(toggle).toBeEnabled()
// Read in the same task as the click so a deferred animation-frame hide cannot pass.
const visible = await toggle.evaluate((element) => {
element.dispatchEvent(new MouseEvent("click", { bubbles: true }))
return document.querySelector('[data-testid="native-Alpha"]')?.getAttribute("data-visible")
})
expect(visible).toBe("false")
await expect(root.locator("#browser-panel")).toHaveCount(1)
await toggle.click()
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
})
@@ -9,12 +9,17 @@ for (const theme of ["light", "dark"]) {
const reveal = logo.locator('[data-slot="wordmark-reveal"]')
const shimmer = logo.locator(".wordmark-shimmer")
const base = logo.locator("svg").first()
await expect(base).toHaveCSS("opacity", theme === "dark" ? "0.5" : "0.6")
await expect(base).toHaveCSS("opacity", "0.16")
await expect(shimmer).toHaveCSS("animation-iteration-count", "1")
await expect(logo.locator("g[mask]")).toHaveCount(0)
await expect(shimmer).toHaveCSS("color", "rgb(255, 255, 255)")
await expect(shimmer).toHaveCSS("mix-blend-mode", "screen")
await expect(base.locator("g[fill]")).toHaveAttribute("fill", "currentColor")
await expect(base.locator("path").nth(1)).toHaveAttribute("fill", "var(--icon-base)")
await expect(base.locator("path").nth(9)).toHaveCSS(
"fill",
await base.locator("path").nth(1).evaluate((element) => getComputedStyle(element).fill),
)
await expect(base.locator('path[fill="var(--icon-weak-base)"]')).toHaveCount(8)
await logo.evaluate((element) => {
element.getAnimations({ subtree: true }).forEach((animation) => {
@@ -77,7 +82,7 @@ for (const theme of ["light", "dark"]) {
await page.setViewportSize({ width: 360, height: 640 })
const component = await mount("app-new-session-wordmark--reveal", { globals: { theme } })
const logo = component.locator('[data-component="new-session-wordmark"]')
await expect(logo.locator("svg").first()).toHaveCSS("opacity", theme === "dark" ? "0.5" : "0.6")
await expect(logo.locator("svg").first()).toHaveCSS("opacity", "0.16")
await expect(logo.locator('[data-slot="wordmark-reveal"]')).toHaveCSS("opacity", "1")
await expect(logo.locator(".wordmark-shimmer")).toHaveCSS("opacity", "0")
expect(await logo.evaluate((element) => element.getAnimations({ subtree: true }).length)).toBe(0)
@@ -485,6 +485,7 @@ export function stepStarted(message: SessionMessageAssistant) {
assistantMessageID: message.id,
agent: message.agent,
model: message.model,
started: message.time.created,
})
}
@@ -62,7 +62,8 @@ async function mockServers(page: Page, requests: string[]) {
const current = url.origin === serverA ? sessionA : sessionB
const directory = url.searchParams.get("directory")
if (directory && directory !== current.directory) return json(route, { name: "InvalidDirectory" }, 500)
if (url.pathname === "/api/status") return json(route, { version: "test", pid: 1, urls: [url.origin] })
if (url.pathname === "/api/info")
return json(route, { version: "test", pid: 1, urls: [url.origin], paths: { tmp: "/tmp/opencode" } })
if (url.pathname === "/api/session") return json(route, { data: [currentSession(current)], cursor: {} })
if (url.pathname === "/api/session/active") return json(route, { data: {} })
if (url.pathname === `/api/session/${current.id}`) return json(route, { data: currentSession(current) })
@@ -108,6 +108,15 @@ test("non-Git folders show their status without offering worktree actions", asyn
).toBeEnabled()
})
test("submits locally after changing a new worktree draft to Local", async ({ page }) => {
const mock = await openDraft(page, "create", { currentDirectory: workspace })
await page.getByRole("button", { name: "New worktree", exact: true }).click()
await page.getByRole("menuitem", { name: "Local repository", exact: true }).click()
await page.locator('[data-component="composer-editor"]').fill("Run locally")
await page.locator('[data-action="composer-submit"]').click()
await expect.poll(() => mock.calls.find((call) => call.type === "session")?.directory).toBe(directory)
})
test("new worktree MCP choices persist per draft and apply before the first prompt", async ({ page }, testInfo) => {
const mock = await openDraft(page, "create")
await page.locator('[data-component="composer-editor"]').fill("Use my selected MCPs")
@@ -296,7 +305,12 @@ test("new worktree sign-in completes before the draft can send", async ({ page,
expect(attempts).toHaveLength(1)
})
async function openDraft(page: Page, worktree = "main", options: { git?: boolean; direction?: "ltr" | "rtl" } = {}) {
async function openDraft(
page: Page,
worktree = "main",
options: { git?: boolean; direction?: "ltr" | "rtl"; currentDirectory?: string } = {},
) {
const currentDirectory = options.currentDirectory ?? directory
const project = {
id: "proj_new_summary",
worktree: directory,
@@ -315,7 +329,7 @@ async function openDraft(page: Page, worktree = "main", options: { git?: boolean
const prompts: { sessionID: string; body: Record<string, unknown> }[] = []
const state: { fail: boolean; hold?: Promise<void>; holdDirectory?: string } = { fail: false }
await mockOpenCodeServer(page, {
directory,
directory: currentDirectory,
project,
sessions,
provider: {
@@ -442,7 +456,7 @@ async function openDraft(page: Page, worktree = "main", options: { git?: boolean
},
)
await page.addInitScript(
({ directory, server, draftID, secondDraftID, worktree }) => {
({ directory, currentDirectory, server, draftID, secondDraftID, worktree }) => {
if (!localStorage.getItem("opencode.global.dat:server"))
localStorage.setItem(
"opencode.global.dat:server",
@@ -455,12 +469,12 @@ async function openDraft(page: Page, worktree = "main", options: { git?: boolean
localStorage.setItem(
"opencode.window.browser.dat:tabs",
JSON.stringify([
{ type: "draft", draftID, server, directory, worktree },
{ type: "draft", draftID: secondDraftID, server, directory, worktree },
{ type: "draft", draftID, server, directory: currentDirectory, worktree },
{ type: "draft", draftID: secondDraftID, server, directory: currentDirectory, worktree },
]),
)
},
{ directory, server, draftID, secondDraftID, worktree },
{ directory, currentDirectory, server, draftID, secondDraftID, worktree },
)
if (options.direction) await openWithDirection(page, draftPath, options.direction)
if (!options.direction) await page.goto(draftPath)
@@ -16,8 +16,13 @@ test("server dialog keeps focus above fullscreen settings", async ({ page }) =>
body: 'data: {"id":"evt_connected","type":"server.connected","data":{}}\n\n',
})
}
if (url.pathname === "/api/status") {
return json(route, { version: "2.0.0", pid: 1, urls: [url.origin] })
if (url.pathname === "/api/info") {
return json(route, {
version: "2.0.0",
pid: 1,
urls: [url.origin],
paths: { tmp: "/tmp/opencode" },
})
}
return json(route, {})
})
@@ -284,7 +284,7 @@ for (const delivery of ["steer", "queue"] as const) {
await expect(thinking).toHaveCount(0)
// The next assistant step still belongs to U1: U2 has been admitted, not delivered.
mock.emit("session.step.started", { sessionID, assistantMessageID: assistantID, agent: "build", model })
mock.emit("session.step.started", { sessionID, assistantMessageID: assistantID, agent: "build", model, started: Date.now() })
for (const tool of [
{ id: "tool_queue_read", name: "read", input: { path: "src/queue.ts" } },
{ id: "tool_queue_grep", name: "grep", input: { pattern: "retry", path: "src" } },
@@ -341,7 +341,7 @@ for (const delivery of ["steer", "queue"] as const) {
)
const later = { sessionID, assistantMessageID: "msg_queue_follow_up_assistant" }
mock.emit("session.step.started", { ...later, agent: "build", model })
mock.emit("session.step.started", { ...later, agent: "build", model, started: Date.now() })
mock.emit("session.text.started", { ...later, ordinal: 0 })
mock.emit("session.text.ended", { ...later, ordinal: 0, text: "A3: Now checking the retry path for U2." })
const response = transcript
@@ -138,7 +138,14 @@ test("MCP authentication starts before a slow resource catalog finishes", async
test("multiple desktop connections show the session's server name", async ({ page }) => {
await mockStressTimeline(page)
await page.route("http://secondary.test/**", (route) =>
route.fulfill({ json: { version: "2.0.0", pid: 1, urls: ["http://secondary.test"] } }),
route.fulfill({
json: {
version: "2.0.0",
pid: 1,
urls: ["http://secondary.test"],
paths: { tmp: "/tmp/opencode" },
},
}),
)
await page.addInitScript(
({ directory, server }) => {
@@ -109,11 +109,11 @@ test("passes through non-event fetches", async ({ page }) => {
const timeline = await setupTimeline(page)
const health = await page.evaluate(async () => {
const response = await fetch("/api/status")
const response = await fetch("/api/info")
return response.json()
})
expect(health).toEqual({ version: "2.0.0", pid: 1, urls: [] })
expect(health).toEqual({ version: "2.0.0", pid: 1, urls: [], paths: { tmp: "/tmp/opencode" } })
await expect.poll(async () => (await timeline.transport.connections()).length).toBe(1)
})
@@ -172,64 +172,6 @@ test("Models and Shortcuts autofocus their filters on normal navigation", async
await expect(result).toBeFocused()
})
for (const count of [7, 8]) {
test(`Projects search uses the full list threshold with ${count} projects`, async ({ page }) => {
await page.route("**/api/project", (route) => route.fulfill({ json: projectList(count) }))
await page.reload()
const view = ui(page)
await view.search.fill("OpenCode")
await expect(view.results.getByRole("option")).toHaveCount(count)
await view.search.clear()
await view.settings.getByRole("tab", { name: "Projects", exact: true }).click()
const search = view.settings.getByRole("searchbox", { name: "Search projects", exact: true })
const projects = view.settings.getByRole("button", { name: /^OpenCode / })
await expect(projects).toHaveCount(count)
if (count === 7) {
await expect(search).toHaveCount(0)
return
}
await expect(search).toBeFocused()
await search.fill(" CODE 06 ")
await expect(projects).toHaveCount(1)
await expect(projects).toHaveAccessibleName("OpenCode 06")
await expect(search).toBeVisible()
await search.fill("missing-project")
await expect(projects).toHaveCount(0)
await expect(view.settings.getByText("No projects found", { exact: true })).toBeVisible()
await view.settings.getByRole("button", { name: "Clear", exact: true }).click()
await expect(search).toBeFocused()
await expect(projects).toHaveCount(count)
await view.settings.getByRole("tab", { name: "Models", exact: true }).click()
await view.settings.getByRole("tab", { name: "Projects", exact: true }).click()
await expect(search).toBeFocused()
await search.fill("OpenCode 06")
await projects.click()
await expect(view.settings.getByRole("heading", { name: "OpenCode 06", exact: true })).toBeVisible()
})
}
test("Projects search focuses when the qualifying inventory arrives after opening", async ({ page }) => {
const inventory = Promise.withResolvers<void>()
await page.route("**/api/project", async (route) => {
await inventory.promise
await route.fulfill({ json: projectList(8) })
})
const requested = page.waitForRequest((request) => new URL(request.url()).pathname === "/api/project")
await page.reload()
await requested
const view = ui(page)
const search = view.settings.getByRole("searchbox", { name: "Search projects", exact: true })
try {
await view.settings.getByRole("tab", { name: "Projects", exact: true }).click()
await expect(view.settings.getByRole("heading", { name: "Projects", exact: true })).toBeVisible()
await expect(search).toHaveCount(0)
} finally {
inventory.resolve()
}
await expect(search).toBeFocused()
await expect(view.settings.getByRole("button", { name: /^OpenCode / })).toHaveCount(8)
})
test("all indexed client controls resolve to visible production controls", async ({ page }) => {
const view = ui(page)
for (const entry of clientSettings.filter((entry) => entry.target && !entry.available)) {
@@ -81,10 +81,10 @@ const fixture = test.extend<{ site: Site }, { builds: Record<string, Record<stri
response.setHeader("cache-control", "no-store")
if (path === "/observer.html")
return void response.writeHead(200, { "content-type": "text/html" }).end("<title>Worker observer</title>")
if (path === "/api/status")
if (path === "/api/info")
return void response
.writeHead(200, { "content-type": "application/json" })
.end(`{"version":"test","pid":1,"urls":["${url.origin}"]}`)
.end(`{"version":"test","pid":1,"urls":["${url.origin}"],"paths":{"tmp":"/tmp/opencode"}}`)
if (path === "/sw.js" && state.legacy && state.version === "old") {
// Model the shipped worker's shared precache name and cache-first navigation behavior.
const urls = Object.keys(builds.old).filter(
@@ -334,8 +334,13 @@ fixture("upgrades the legacy shared precache only after old tabs close", async (
fixture("does not substitute cached HTML for API or missing asset navigations", async ({ page, site }) => {
await install(page, site.url)
const api = await page.goto(`${site.url}/api/status`)
expect(await api?.json()).toEqual({ version: "test", pid: 1, urls: ["http://localhost"] })
const api = await page.goto(`${site.url}/api/info`)
expect(await api?.json()).toEqual({
version: "test",
pid: 1,
urls: ["http://localhost"],
paths: { tmp: "/tmp/opencode" },
})
expect(api?.fromServiceWorker()).toBe(false)
const asset = await page.goto(`${site.url}/_assets/missing.js`)
expect(asset?.status()).toBe(404)
+1 -1
View File
@@ -33,7 +33,7 @@ export class MockBadRequest extends Schema.TaggedError<MockBadRequest>()("MockBa
}) {}
const Group = HttpApiGroup.make("mock")
.add(HttpApiEndpoint.get("status", "/api/status", { success: Json }))
.add(HttpApiEndpoint.get("info", "/api/info", { success: Json }))
.add(
HttpApiEndpoint.get("event", "/api/event", {
success: Schema.String.pipe(HttpApiSchema.asText({ contentType: "text/event-stream" })),
+7 -1
View File
@@ -219,7 +219,13 @@ function mockHandlers(config: MockServerConfig, state: { cursors: Map<string, st
}),
)
.handleAll({
status: () => Effect.succeed({ version: "2.0.0", pid: 1, urls: config.server ? [config.server] : [] }),
info: () =>
Effect.succeed({
version: "2.0.0",
pid: 1,
urls: config.server ? [config.server] : [],
paths: { tmp: "/tmp/opencode" },
}),
config: () => Effect.succeed(configEntries),
reference: () =>
Effect.succeed({
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode/app",
"version": "2.0.5",
"version": "2.0.8",
"description": "",
"type": "module",
"exports": {
@@ -16,6 +16,86 @@
}
}
[data-component="upload-row"] {
display: flex;
flex-direction: column;
gap: 4px;
& + & {
margin-top: 8px;
}
[data-slot="upload-row-label"] {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 8px;
font-size: 13px;
line-height: var(--line-height-base);
letter-spacing: -0.04px;
}
[data-slot="upload-row-name"] {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--v2-text-text-base);
font-weight: 530;
}
[data-slot="upload-row-percent"] {
flex-shrink: 0;
color: var(--v2-text-text-muted);
font-variant-numeric: tabular-nums;
}
[data-slot="upload-row-track"] {
display: flex;
align-items: center;
gap: 8px;
}
[data-component="upload-progress"] {
flex: 1;
height: 4px;
overflow: hidden;
border-radius: 999px;
background: var(--v2-background-bg-layer-02);
}
[data-slot="upload-progress-bar"] {
height: 100%;
border-radius: inherit;
background: var(--v2-icon-icon-base);
transition: width 160ms ease-out;
}
[data-slot="upload-row-cancel"] {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
padding: 0;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--v2-icon-icon-muted);
cursor: pointer;
&:hover {
background: var(--v2-overlay-simple-overlay-hover);
color: var(--v2-icon-icon-base);
}
svg {
width: 12px;
height: 12px;
}
}
}
[data-component="composer-attachments"] {
timeline-scope: --composer-attachments-scroll;
@@ -1,66 +1,12 @@
import { onCleanup, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { makeEventListener } from "@solid-primitives/event-listener"
import { createBlobReference } from "@/runtime/persistence/drafts"
import { uuid } from "@/runtime/persistence/uuid"
import type { ComposerAttachment, ComposerPrompt } from "../types"
const accepted = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"application/pdf",
"text/*",
"application/json",
"application/ld+json",
"application/toml",
"application/x-toml",
"application/x-yaml",
"application/xml",
"application/yaml",
".c",
".cc",
".cjs",
".conf",
".cpp",
".css",
".csv",
".cts",
".env",
".go",
".gql",
".graphql",
".h",
".hh",
".hpp",
".htm",
".html",
".ini",
".java",
".js",
".json",
".jsx",
".log",
".md",
".mdx",
".mjs",
".mts",
".py",
".rb",
".rs",
".sass",
".scss",
".sh",
".sql",
".toml",
".ts",
".tsx",
".txt",
".xml",
".yaml",
".yml",
".zsh",
]
import type { ComposerPrompt } from "../types"
import type { ImageAttachmentPart, PathAttachmentPart } from "../state"
import type { AttachmentDestination } from "./destination"
import { uploads } from "./uploads"
type PromptTarget = {
current: () => ComposerPrompt
@@ -74,10 +20,11 @@ export type ComposerAttachmentConfig = {
onFile: (file: File) => Promise<unknown>,
) => Promise<void>
directory: () => string
destination: () => AttachmentDestination
isDialogActive: () => boolean
warn: () => void
duplicate: () => void
onError: (error: unknown) => void
onUploadError: (error: unknown) => void
readClipboardImage?: () => Promise<File | null>
getPathForFile?: (file: File) => string
onDragCancel?: (callback: () => void) => () => void
@@ -102,13 +49,23 @@ export function createComposerAttachments(
if (!editor) return
return { prompt, cursor: prompt.cursor() ?? cursorPosition(editor) }
}
const add = async (file: File, toast = true, target = capture(), clipboard = false) => {
// Uploads this composer started; they finish (or fail) even if the composer unmounts.
const [pending, setPending] = createStore<{ ids: string[] }>({ ids: [] })
// Media the model reads natively travels inline with the prompt, so its bytes live in the draft
// store. Everything else, including text, reaches the model as a path on the server that its
// tools open; those bytes never enter the store, and never get base64-encoded into the request.
const add = async (file: File, target = capture(), clipboard = false) => {
if (!target) return false
const mime = await attachmentMime(file)
if (!mime) {
if (toast) input.warn()
return false
}
const destination = input.destination()
if (native(mime, destination.input) && file.size <= MAX_INLINE_BYTES) return addInline(file, mime, target, clipboard)
const sourcePath = input.getPathForFile?.(file) || undefined
if (destination.local && sourcePath) return addPath(target, { filename: file.name, mime, path: sourcePath })
void stage(file, mime, target, destination)
return true
}
const addInline = async (file: File, mime: string, target: NonNullable<ReturnType<typeof capture>>, clipboard: boolean) => {
const blob = input.store ? await input.store(file) : await createBlobReference(file)
const sourcePath = input.getPathForFile?.(file) || undefined
// Native clipboard images arrive with a fresh timestamped filename on every paste, so identical
@@ -127,24 +84,45 @@ export function createComposerAttachments(
input.duplicate()
return true
}
const attachment: ComposerAttachment = {
type: "image",
id: uuid(),
filename: file.name,
sourcePath,
mime,
blob,
}
const attachment: ImageAttachmentPart = { type: "image", id: uuid(), filename: file.name, sourcePath, mime, blob }
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
return true
}
const addAttachments = async (files: File[], toast = true, target = capture()) => {
const found = await files.reduce(async (result, file) => {
const addPath = (
target: NonNullable<ReturnType<typeof capture>>,
attachment: Pick<PathAttachmentPart, "filename" | "mime" | "path">,
) => {
if (target.prompt.current().some((part) => part.type === "path" && part.path === attachment.path)) {
input.duplicate()
return true
}
target.prompt.set([...target.prompt.current(), { type: "path", id: uuid(), ...attachment }], target.prompt.cursor())
return true
}
const stage = async (
file: File,
mime: string,
target: NonNullable<ReturnType<typeof capture>>,
destination: AttachmentDestination,
) => {
const id = uuid()
setPending("ids", (ids) => [...ids, id])
const path = await uploads
.track({ id, filename: file.name, mime, size: file.size }, (report, signal) =>
destination.upload(file, report, signal),
)
.catch((error: unknown) => {
input.onUploadError(error)
return undefined
})
.finally(() => setPending("ids", (ids) => ids.filter((item) => item !== id)))
if (path) addPath(target, { filename: file.name, mime, path })
}
const addAttachments = async (files: File[], target = capture()) => {
return files.reduce(async (result, file) => {
const previous = await result
return (await add(file, false, target)) || previous
return (await add(file, target)) || previous
}, Promise.resolve(false))
if (!found && files.length > 0 && toast) input.warn()
return found
}
const handlePaste = async (event: ClipboardEvent) => {
const clipboardData = event.clipboardData
@@ -159,13 +137,13 @@ export function createComposerAttachments(
return file ? [file] : []
})
if (files.length > 0) {
await addAttachments(files, true, target)
await addAttachments(files, target)
return
}
const plainText = clipboardData.getData("text/plain") ?? ""
if (input.readClipboardImage && !plainText) {
const file = await input.readClipboardImage()
if (file && (await add(file, true, target, true))) return
if (file && (await add(file, target, true))) return
}
if (!plainText) return
const text = plainText.includes("\r") ? plainText.replace(/\r\n?/g, "\n") : plainText
@@ -218,20 +196,33 @@ export function createComposerAttachments(
addAttachments,
handlePaste,
handleDrop,
/** Uploads still in flight for this composer; sending waits for them. */
pending: () => uploads.items().filter((item) => pending.ids.includes(item.id)),
cancel(id: string) {
uploads.items().find((item) => item.id === id)?.cancel()
},
pick(fallback: () => void) {
if (!input.picker) {
fallback()
return
}
void input
.picker({ defaultPath: input.directory(), multiple: true, accept: accepted }, (file) => add(file))
.catch(input.onError)
void input.picker({ defaultPath: input.directory(), multiple: true }, (file) => add(file)).catch(input.onError)
},
}
}
const imageMimes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"])
// The server rejects inline attachments above this size, so larger media takes the path route.
const MAX_INLINE_BYTES = 20 * 1024 * 1024
// Mirrors the media the server forwards to the model as message content.
function native(mime: string, input: AttachmentDestination["input"]) {
if (imageMimes.has(mime)) return input.image
if (mime === "application/pdf") return input.pdf
return false
}
const imageExtensions = new Map([
["gif", "image/gif"],
["jpeg", "image/jpeg"],
@@ -249,6 +240,8 @@ const textMimes = new Set([
"application/yaml",
])
// Text-like files normalize to text/plain so the chip labels them as text; every other file keeps
// a binary type. Delivery is decided separately: native media inline, everything else by path.
async function attachmentMime(file: File) {
const type = file.type.split(";", 1)[0]?.trim().toLowerCase() ?? ""
if (imageMimes.has(type) || type === "application/pdf") return type
@@ -259,10 +252,11 @@ async function attachmentMime(file: File) {
if (type.startsWith("text/") || textMimes.has(type) || type.endsWith("+json") || type.endsWith("+xml")) {
return "text/plain"
}
const binary = type || "application/octet-stream"
const bytes = new Uint8Array(await file.slice(0, 4096).arrayBuffer())
if (bytes.some((byte) => byte === 0)) return
if (bytes.some((byte) => byte === 0)) return binary
const control = bytes.filter((byte) => byte < 9 || (byte > 13 && byte < 32)).length
if (bytes.length > 0 && control / bytes.length > 0.3) return
if (bytes.length > 0 && control / bytes.length > 0.3) return binary
return "text/plain"
}
@@ -0,0 +1,57 @@
import type { Accessor } from "solid-js"
import { useServer } from "@/runtime/server/current"
import { useServerSDK } from "@/runtime/server/client"
import { authTokenFromCredentials } from "@/runtime/server/api"
import { useWorkspaceLocation } from "@/workspaces/location"
import type { ComposerControls } from "../adapter"
// Where a prompt is headed: the model that reads it and the server that runs its tools.
export type AttachmentDestination = {
/** Input modalities the selected model reads natively. */
input: { image: boolean; pdf: boolean }
/** The server shares the client's filesystem, so an attachment's source path resolves as-is. */
local: boolean
/** Streams a file into the server's temporary directory and returns its absolute path there. */
upload: (file: File, report: (loaded: number) => void, signal: AbortSignal) => Promise<string>
}
export function useAttachmentDestination(controls: Accessor<ComposerControls>) {
const server = useServer()
const sdk = useServerSDK()
const location = useWorkspaceLocation()
return (): AttachmentDestination => ({
input: controls().model.selection.current()?.capabilities.input ?? { image: false, pdf: false },
local: server.isLocal,
upload: async (file, report, signal) => {
const info = await sdk.api.server.info({ signal })
// One directory per upload keeps the original filename without collisions; the server
// normalizes the separators and returns the resolved path.
const url = new URL("/api/experimental/fs/write", server.conn.http.url)
url.searchParams.set("location[directory]", location().directory)
url.searchParams.set("path", `${info.paths.tmp}/uploads/${crypto.randomUUID()}/${file.name}`)
return write(url, file, server.conn.http.password, report, signal)
},
})
}
// fetch cannot report upload progress and Chromium only streams request bodies over HTTP/2, so
// the one request that needs both goes through XMLHttpRequest. The browser streams the File
// from disk; nothing is buffered in the renderer.
function write(url: URL, file: File, password: string | undefined, report: (loaded: number) => void, signal: AbortSignal) {
return new Promise<string>((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("POST", url)
xhr.responseType = "json"
xhr.setRequestHeader("content-type", "application/octet-stream")
if (password) xhr.setRequestHeader("authorization", `Basic ${authTokenFromCredentials({ password })}`)
xhr.upload.addEventListener("progress", (event) => report(event.loaded))
xhr.addEventListener("load", () => {
if (xhr.status !== 200) return reject(new Error(`Upload failed with status ${xhr.status}`))
resolve((xhr.response as { data: { path: string } }).data.path)
})
xhr.addEventListener("error", () => reject(new Error("Upload failed")))
xhr.addEventListener("abort", () => reject(new DOMException("Upload aborted", "AbortError")))
signal.addEventListener("abort", () => xhr.abort(), { once: true })
xhr.send(file)
})
}
@@ -0,0 +1,116 @@
import { createEffect, createRoot, For, on, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import { Icon } from "@opencode/ui/icon"
import { Toast, toaster } from "@opencode/ui/toast"
import { useLanguage } from "@/runtime/i18n/language"
export type Upload = {
id: string
filename: string
mime: string
size: number
loaded: number
cancel: () => void
}
// Uploads outlive the composer that started them, so one process-wide list feeds every chip
// and the single progress toast.
const [state, setState] = createStore<{ items: Upload[] }>({ items: [] })
export const uploads = {
items: () => state.items,
/** Runs `work` while the upload is listed. Resolves to undefined when the user cancels it. */
async track<T>(
input: Pick<Upload, "id" | "filename" | "mime" | "size">,
work: (report: (loaded: number) => void, signal: AbortSignal) => Promise<T>,
): Promise<T | undefined> {
const controller = new AbortController()
setState("items", (items) => [...items, { ...input, loaded: 0, cancel: () => controller.abort() }])
try {
return await work((loaded) => setState("items", (item) => item.id === input.id, "loaded", loaded), controller.signal)
} catch (error) {
if (controller.signal.aborted) return undefined
throw error
} finally {
setState("items", (items) => items.filter((item) => item.id !== input.id))
}
},
}
// Sonner builds toast content outside the app's Solid tree: no context and no owner. This host
// lives inside the providers, lends the toast its language instance, and gives the content a
// root of its own so progress stays reactive.
export function UploadToastHost() {
const language = useLanguage()
let active: { id: number; dispose: () => void } | undefined
const dismiss = () => {
if (!active) return
toaster.dismiss(active.id)
active.dispose()
active = undefined
}
createEffect(
on(
() => state.items.length > 0,
(uploading) => {
if (!uploading) return dismiss()
if (active) return
const id = toaster.show(
(props) =>
createRoot((dispose) => {
active = { id: props.toastId, dispose }
return <UploadToast toastId={props.toastId} language={language} />
}),
{ persistent: true, resize: () => state.items.length },
)
active ??= { id, dispose: () => {} }
},
),
)
onCleanup(dismiss)
return null
}
function UploadToast(props: { toastId: number; language: ReturnType<typeof useLanguage> }) {
const percent = (item: Upload) => (item.size === 0 ? 100 : Math.floor((item.loaded / item.size) * 100))
return (
<Toast toastId={props.toastId}>
<Toast.Content>
<For each={state.items}>
{(item) => (
<div data-component="upload-row">
<div data-slot="upload-row-label">
<span data-slot="upload-row-name" title={item.filename}>
{item.filename}
</span>
<span data-slot="upload-row-percent">
{props.language.t("prompt.toast.uploading.percent", { percent: percent(item) })}
</span>
</div>
<div data-slot="upload-row-track">
<div
data-component="upload-progress"
role="progressbar"
aria-label={item.filename}
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={percent(item)}
>
<div data-slot="upload-progress-bar" style={{ width: `${percent(item)}%` }} />
</div>
<button
type="button"
data-slot="upload-row-cancel"
aria-label={props.language.t("prompt.toast.uploading.cancel")}
onClick={() => item.cancel()}
>
<Icon name="outline-xmark" />
</button>
</div>
</div>
)}
</For>
</Toast.Content>
</Toast>
)
}
+20
View File
@@ -8,6 +8,13 @@ export type PromptComment = {
origin?: "review" | "file"
}
/** An attachment the model receives as a path on the server rather than inline bytes. */
export type PromptAttachmentReference = {
name: string
mime: string
path: string
}
function selection(selection: unknown) {
if (!selection || typeof selection !== "object") return undefined
const startLine = Number((selection as FileSelection).startLine)
@@ -58,8 +65,17 @@ export function readPromptPresentation(value: unknown) {
const displayText = (value as { displayText?: unknown }).displayText
const comments = (value as { comments?: unknown }).comments
if (typeof displayText !== "string" || !Array.isArray(comments)) return
const attachments = (value as { attachments?: unknown }).attachments
return {
displayText,
attachments: (Array.isArray(attachments) ? attachments : []).flatMap((item): PromptAttachmentReference[] => {
if (!item || typeof item !== "object") return []
const name = (item as { name?: unknown }).name
const mime = (item as { mime?: unknown }).mime
const path = (item as { path?: unknown }).path
if (typeof name !== "string" || typeof mime !== "string" || typeof path !== "string") return []
return [{ name, mime, path }]
}),
comments: comments.flatMap((item): PromptComment[] => {
if (!item || typeof item !== "object") return []
const path = (item as { path?: unknown }).path
@@ -80,6 +96,10 @@ export function readPromptPresentation(value: unknown) {
}
}
export function formatAttachmentReference(input: PromptAttachmentReference) {
return `Attached file: \`${input.path}\``
}
export function formatCommentNote(input: { path: string; selection?: FileSelection; comment: string }) {
const start = input.selection ? Math.min(input.selection.startLine, input.selection.endLine) : undefined
const end = input.selection ? Math.max(input.selection.startLine, input.selection.endLine) : undefined
+6 -6
View File
@@ -7,7 +7,7 @@ import type {
ComposerPersistedState,
ComposerPrompt,
} from "../types"
import { promptLength } from "../prompt-parts"
import { isAttachment, promptLength } from "../prompt-parts"
export type ComposerStateStore = [
Store<ComposerPersistedState> | Accessor<Store<ComposerPersistedState>>,
@@ -48,7 +48,7 @@ export function createComposerEditorActions(input: ComposerStateStoreInput) {
setStore()((state) => ({
prompt: [
{ type: "text", content, start: 0, end: content.length },
...state.prompt.filter((part) => part.type === "image"),
...state.prompt.filter(isAttachment),
],
cursor: content.length,
retry: undefined,
@@ -83,7 +83,7 @@ export function createComposerEditorActions(input: ComposerStateStoreInput) {
clearRetry()
},
removeAttachment(id: string) {
setStore()("prompt", (parts) => parts.filter((part) => part.type !== "image" || part.id !== id))
setStore()("prompt", (parts) => parts.filter((part) => !isAttachment(part) || part.id !== id))
clearRetry()
},
}
@@ -93,7 +93,7 @@ function insertText(prompt: ComposerPrompt, cursor: number, content: string): Co
let position = 0
let inserted = false
const parts = prompt.flatMap<ComposerPrompt[number]>((part) => {
if (part.type === "image") return [part]
if (isAttachment(part)) return [part]
const start = position
position += part.content.length
if (inserted) return [part]
@@ -121,7 +121,7 @@ function insertMention(
}
let position = 0
const parts = prompt.flatMap<ComposerPrompt[number]>((part) => {
if (part.type === "image") return [part]
if (isAttachment(part)) return [part]
const partStart = position
position += part.content.length
if (part.type !== "text" || start < partStart || end > position) return [part]
@@ -139,7 +139,7 @@ function insertMention(
function withOffsets(prompt: ComposerPrompt): ComposerPrompt {
let offset = 0
return prompt.map((part) => {
if (part.type === "image") return part
if (isAttachment(part)) return part
const next = { ...part, start: offset, end: offset + part.content.length }
offset = next.end
return next
+59 -15
View File
@@ -1,9 +1,10 @@
import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show, type JSX } from "solid-js"
import { createEffect, createMemo, createResource, createSignal, For, onCleanup, onMount, Show, type JSX } from "solid-js"
import { createStore } from "solid-js/store"
import { FileIcon } from "@opencode/ui/file-icon"
import { Icon } from "@opencode/ui/icon"
import { IconButton } from "@opencode/ui/icon-button"
import { createAnimatedPresence } from "@/runtime/animated-presence"
import { resolveBlobUrl } from "@/runtime/persistence/drafts"
import { ProviderIcon } from "@opencode/ui/provider-icon"
import { useI18n } from "@opencode/ui/context/i18n"
import { Button } from "@opencode/ui/button"
@@ -12,6 +13,8 @@ import { Menu } from "@opencode/ui/menu"
import { Tooltip } from "@opencode/ui/tooltip"
import { ScrollView } from "@opencode/ui/scroll-view"
import { AttachmentCard } from "@opencode/session-ui/attachment-card"
import { ProgressCircle } from "@opencode/ui/progress-circle"
import type { Upload } from "../attachments/uploads"
import { CommentCard } from "@opencode/session-ui/comment-card"
import { typeLabel } from "@opencode/session-ui/message-file"
import { Skill } from "@opencode/schema/skill"
@@ -24,6 +27,7 @@ import type {
ComposerSuggestion,
} from "../types"
import type { ComposerEditorModel, ComposerSelectControl } from "./interaction"
import { isAttachment } from "../prompt-parts"
import "../attachments/attachments.css"
import "./editor.css"
@@ -102,7 +106,6 @@ export function ComposerEditor(props: ComposerEditorProps) {
ref={props.controller.setFileInput}
type="file"
multiple
accept="image/png,image/jpeg,image/gif,image/webp,application/pdf,text/*,application/json,application/ld+json,application/toml,application/x-toml,application/x-yaml,application/xml,application/yaml,.c,.cc,.cjs,.conf,.cpp,.css,.csv,.cts,.env,.go,.gql,.graphql,.h,.hh,.hpp,.htm,.html,.ini,.java,.js,.json,.jsx,.log,.md,.mdx,.mjs,.mts,.py,.rb,.rs,.sass,.scss,.sh,.sql,.toml,.ts,.tsx,.txt,.xml,.yaml,.yml,.zsh"
class="hidden"
onChange={(event) => {
const list = event.currentTarget.files
@@ -149,11 +152,13 @@ export function ComposerEditor(props: ComposerEditorProps) {
<Show when={state.mode === "normal"}>
<ComposerAttachments
attachments={props.controller.attachments()}
uploads={props.controller.uploads()}
comments={props.controller.comments()}
activeCommentID={state.activeContextID}
removeLabel={i18n.t("ui.promptInput.removeAttachment")}
onAttachmentClick={props.controller.openAttachment}
onAttachmentRemove={(attachment) => props.controller.removeAttachment(attachment.id)}
onUploadCancel={(upload) => props.controller.cancelUpload(upload.id)}
onCommentClick={(comment) => props.controller.toggleContext(comment.key)}
onCommentRemove={(comment) => props.controller.removeContext(comment.key)}
/>
@@ -192,9 +197,9 @@ export function ComposerEditor(props: ComposerEditorProps) {
onInput={(event) => {
const cursor = composerCursor(event.currentTarget)
const prompt = parseComposerEditor(event.currentTarget)
const images = props.controller.parts().filter((part) => part.type === "image")
const attachments = props.controller.parts().filter(isAttachment)
localInput = true
props.controller.onInput(prompt.map((part) => part.content).join(""), [...prompt, ...images], cursor)
props.controller.onInput(prompt.map((part) => part.content).join(""), [...prompt, ...attachments], cursor)
}}
onKeyDown={(event) => {
if (!view.draftOnly && props.controller.onKeyDown(event)) return
@@ -349,7 +354,7 @@ function renderComposerEditor(editor: HTMLDivElement, prompt: ComposerPrompt) {
const active = document.activeElement === editor
editor.replaceChildren(
...prompt.flatMap<Node>((part) => {
if (part.type === "image") return []
if (isAttachment(part)) return []
if (part.type === "text") return [document.createTextNode(part.content)]
const mention = document.createElement("span")
mentionParts.set(mention, part)
@@ -476,17 +481,22 @@ function composerCursor(editor: HTMLDivElement) {
export function ComposerAttachments(props: {
attachments: ComposerAttachment[]
uploads?: Upload[]
comments?: ComposerComment[]
activeCommentID?: string
removeLabel: string
onAttachmentClick?: (attachment: ComposerAttachment) => void
onAttachmentRemove: (attachment: ComposerAttachment) => void
onUploadCancel?: (upload: Upload) => void
onCommentClick?: (comment: ComposerComment) => void
onCommentRemove?: (comment: ComposerComment) => void
}) {
const i18n = useI18n()
const percent = (upload: Upload) => (upload.size === 0 ? 100 : Math.floor((upload.loaded / upload.size) * 100))
return (
<Show when={props.attachments.length > 0 || (props.comments?.length ?? 0) > 0}>
<Show
when={props.attachments.length > 0 || (props.uploads?.length ?? 0) > 0 || (props.comments?.length ?? 0) > 0}
>
<div data-component="composer-attachments" data-slot="composer-attachments" class="relative">
<div
data-slot="composer-attachments-scroll"
@@ -523,22 +533,34 @@ export function ComposerAttachments(props: {
<For each={props.attachments}>
{(attachment) => (
<div class="relative group shrink-0">
<Tooltip value={attachment.filename} placement="top" contentClass="break-all">
<Tooltip
value={attachment.type === "path" ? attachment.path : attachment.filename}
placement="top"
contentClass="break-all"
>
<Show
when={attachment.mime.startsWith("image/")}
when={attachment.type === "image" && attachment.mime.startsWith("image/") ? attachment : undefined}
fallback={
<AttachmentCard title={attachment.filename}>
{typeLabel(attachment.filename, attachment.mime, i18n.t("ui.common.file"))}
</AttachmentCard>
}
>
<img
src={attachment.blob.url}
alt={attachment.filename}
class="w-[58px] h-[46px] rounded-[6px] object-cover"
onClick={() => props.onAttachmentClick?.(attachment)}
/>
<div class="absolute inset-0 rounded-[6px] shadow-[inset_0_0_0_0.5px_var(--v2-border-border-base)] pointer-events-none" />
{(image) => {
// Restored drafts and history carry image ids only; bytes load when shown.
const [url] = createResource(() => image().blob, resolveBlobUrl)
return (
<>
<img
src={url() ?? ""}
alt={attachment.filename}
class="w-[58px] h-[46px] rounded-[6px] object-cover"
onClick={() => props.onAttachmentClick?.(attachment)}
/>
<div class="absolute inset-0 rounded-[6px] shadow-[inset_0_0_0_0.5px_var(--v2-border-border-base)] pointer-events-none" />
</>
)
}}
</Show>
</Tooltip>
<button
@@ -552,6 +574,28 @@ export function ComposerAttachments(props: {
</div>
)}
</For>
<For each={props.uploads ?? []}>
{(upload) => (
<div class="relative group shrink-0" data-slot="composer-upload">
<Tooltip value={upload.filename} placement="top" contentClass="break-all">
<AttachmentCard title={upload.filename}>
<span class="inline-flex items-center gap-1">
<ProgressCircle percentage={percent(upload)} />
{i18n.t("ui.promptInput.uploading", { percent: percent(upload) })}
</span>
</AttachmentCard>
</Tooltip>
<button
type="button"
onClick={() => props.onUploadCancel?.(upload)}
class="absolute -top-1 -end-1 size-4 rounded-full bg-v2-icon-icon-muted outline-solid outline-1 outline-v2-icon-icon-contrast flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
aria-label={i18n.t("ui.promptInput.cancelUpload")}
>
<Icon name="outline-xmark" class="text-v2-icon-icon-contrast" />
</button>
</div>
)}
</For>
</div>
<div
data-slot="composer-attachments-fade-left"
@@ -0,0 +1,19 @@
import { expect, test } from "bun:test"
import { shouldHandlePasteAsAttachment } from "./interaction"
test("leaves paste to the browser when web clipboard data is unavailable", () => {
expect(shouldHandlePasteAsAttachment(clipboard(), false)).toBe(false)
})
test("uses native image reading only when the clipboard has no text", () => {
expect(shouldHandlePasteAsAttachment(clipboard(), true)).toBe(true)
expect(shouldHandlePasteAsAttachment(clipboard(["text/plain"]), true)).toBe(false)
})
test("handles clipboard files as attachments", () => {
expect(shouldHandlePasteAsAttachment(clipboard([], [{ kind: "file" }]), false)).toBe(true)
})
function clipboard(types: string[] = [], items: Array<{ kind: string }> = []) {
return { types, items } as unknown as DataTransfer
}
+29 -14
View File
@@ -2,6 +2,7 @@ import { createEffect, type Accessor } from "solid-js"
import { createStore, reconcile } from "solid-js/store"
import { useFilteredList } from "@opencode/ui/hooks"
import { createComposerAttachments, type ComposerAttachmentConfig } from "../attachments/attachments"
import type { Upload } from "../attachments/uploads"
import { createComposerEditorActions, type ComposerStateStoreInput } from "./actions"
import type {
ComposerAttachment,
@@ -18,7 +19,7 @@ import {
type ComposerInteractionCommand,
type ComposerInteractionEvent,
} from "../suggestions/machine"
import { clonePrompt, promptLength } from "../prompt-parts"
import { clonePrompt, isAttachment, promptLength } from "../prompt-parts"
import type { ComposerQueue } from "../adapter"
export type ComposerSelectControl = {
@@ -74,7 +75,7 @@ export function createComposerEditor(input: {
const draft = createComposerEditorActions(input.store)
const [state, setState] = input.state ?? createComposerEditorState(draft.state.mode)
function addPart(part: ComposerPersistedState["prompt"][number]) {
if (part.type === "image") return false
if (isAttachment(part)) return false
if (part.type === "file" || part.type === "agent") {
draft.addMention(part)
return true
@@ -169,7 +170,7 @@ export function createComposerEditor(input: {
if (!action || state.popover.type !== "command-menu") result.commands.forEach(execute)
if (action && event.item.kind === "command" && state.popover.type !== "command-menu") {
draft.setPrompt(
draft.state.prompt.filter((part): part is ComposerAttachment => part.type === "image"),
draft.state.prompt.filter(isAttachment),
0,
)
}
@@ -315,7 +316,13 @@ export function createComposerEditor(input: {
return draft.state.context.items.filter((item) => !!item.comment?.trim())
},
attachments(): ComposerAttachment[] {
return draft.state.prompt.filter((part): part is ComposerAttachment => part.type === "image")
return draft.state.prompt.filter(isAttachment)
},
uploads(): Upload[] {
return attachments?.pending() ?? []
},
cancelUpload(id: string) {
attachments?.cancel(id)
},
toggleContext(id: string) {
dispatch({ type: "context.active", id })
@@ -336,11 +343,12 @@ export function createComposerEditor(input: {
canSubmit() {
if (input.view.submit.available?.() === false) return false
if (input.view.draftOnly) return false
if (attachments?.pending().length) return false
const persisted = draft.state
if (state.mode === "shell") {
return persisted.prompt.some((part) => "content" in part && !!part.content.trim())
}
if (persisted.prompt.some((part) => part.type === "image")) return true
if (persisted.prompt.some(isAttachment)) return true
if (persisted.context.items.some((item) => !!item.comment?.trim())) return true
return persisted.prompt.some((part) => "content" in part && !!part.content.trim())
},
@@ -369,6 +377,7 @@ export function createComposerEditor(input: {
submit(options?: { alternate?: boolean }) {
if (input.view.submit.available?.() === false) return
if (input.view.draftOnly) return
if (attachments?.pending().length) return
input.view.submit.onSubmit(options)
dispatch({ type: "popover.close" })
},
@@ -384,20 +393,20 @@ export function createComposerEditor(input: {
},
onPaste(event: ClipboardEvent) {
const clipboard = event.clipboardData
if (
attachments &&
(Array.from(clipboard?.items ?? []).some((item) => item.kind === "file") || !clipboard?.getData("text/plain"))
) {
const text = clipboard?.getData("text/plain")
if (attachments && shouldHandlePasteAsAttachment(clipboard, !!input.attachments?.readClipboardImage)) {
void attachments.handlePaste(event)
return
}
const text = clipboard?.getData("text/plain").replace(/\r\n?/g, "\n")
if (!text) return
event.preventDefault()
// insertText emits input events per line, repeatedly parsing and saving the draft.
// Escaped HTML inserts multiline text once and preserves native selection and undo.
const multiline = text.includes("\n")
const value = multiline ? text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;") : text
const normalized = text.replace(/\r\n?/g, "\n")
const multiline = normalized.includes("\n")
const value = multiline
? normalized.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")
: normalized
if (
typeof document.execCommand === "function" &&
document.execCommand(multiline ? "insertHTML" : "insertText", false, value)
@@ -408,13 +417,13 @@ export function createComposerEditor(input: {
if (!(target instanceof HTMLElement) || !selection?.rangeCount || !target.contains(selection.anchorNode)) return
const range = selection.getRangeAt(0)
range.deleteContents()
const node = document.createTextNode(text)
const node = document.createTextNode(normalized)
range.insertNode(node)
range.setStartAfter(node)
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
target.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "insertFromPaste", data: text }))
target.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "insertFromPaste", data: normalized }))
},
onDragEnter(event: DragEvent) {
event.preventDefault()
@@ -450,6 +459,12 @@ export function createComposerEditor(input: {
export type ComposerEditorModel = ReturnType<typeof createComposerEditor>
export function shouldHandlePasteAsAttachment(clipboard: DataTransfer | null, readClipboardImage: boolean) {
if (Array.from(clipboard?.items ?? []).some((item) => item.kind === "file")) return true
if (Array.from(clipboard?.types ?? []).some((type) => type.startsWith("text/"))) return false
return readClipboardImage
}
function canNavigateHistory(direction: "up" | "down", text: string, cursor: number, inHistory: boolean) {
const position = Math.max(0, Math.min(cursor, text.length))
if (inHistory) return position === 0 || position === text.length
+4 -4
View File
@@ -1,6 +1,6 @@
import type { Prompt } from "@/composer/state"
import type { SelectedLineRange } from "@/workspaces/files/model"
import { clonePrompt } from "../prompt-parts"
import { clonePrompt, isAttachment } from "../prompt-parts"
import type { PromptHistoryComment, PromptHistoryEntry } from "../schema"
export type { PromptHistoryComment, PromptHistoryEntry } from "../schema"
@@ -35,9 +35,9 @@ export function prependHistoryEntry(
.map((part) => ("content" in part ? part.content : ""))
.join("")
.trim()
const hasImages = prompt.some((part) => part.type === "image")
const hasAttachments = prompt.some(isAttachment)
const hasComments = comments.some((comment) => !!comment.comment.trim())
if (!text && !hasImages && !hasComments) return entries
if (!text && !hasAttachments && !hasComments) return entries
const entry = {
prompt: clonePrompt(prompt),
@@ -86,7 +86,7 @@ function isPromptEqual(entryA: PromptHistoryStoredEntry, entryB: PromptHistorySt
if (partA.type === "skill") {
if (partB.type !== "skill" || partA.id !== partB.id || partA.name !== partB.name) return false
}
if (partA.type === "image" && partA.id !== (partB.type === "image" ? partB.id : "")) return false
if (isAttachment(partA) && partA.id !== (isAttachment(partB) ? partB.id : "")) return false
}
if (entryA.comments.length !== entryB.comments.length) return false
for (let i = 0; i < entryA.comments.length; i++) {
+17 -9
View File
@@ -11,17 +11,19 @@ import { useLanguage } from "@/runtime/i18n/language"
import { useLayout } from "@/shell/state/layout"
import { usePlatform } from "@/runtime/platform/platform"
import { useWorkspaceLocation } from "@/workspaces/location"
import { resolveBlobUrl } from "@/runtime/persistence/drafts"
import { useData, useServer } from "@/runtime/server/current"
import { createSessionTabs } from "@/session/helpers"
import { showToast } from "@/shell/notifications/toast"
import { formatServerError } from "@/runtime/server/errors"
import { Skill } from "@opencode/schema/skill"
import type { ComposerAdapter, ComposerControls, ComposerQueue } from "./adapter"
import type { ImageAttachmentPart } from "./state"
import { isAttachment } from "./prompt-parts"
import type { PromptHistoryComment } from "./history/entry"
import { createComposerHistory } from "./history/store"
import { composerPlaceholder } from "./placeholder"
import { createComposerSubmit } from "./submit"
import { useAttachmentDestination } from "./attachments/destination"
export type ComposerModel = ComposerEditorModel & {
readonly model: ComposerControls["model"]
@@ -72,7 +74,7 @@ export function createComposerModel(adapter: ComposerAdapter, options?: { queue?
}, [])
})
const attachments = createMemo(() =>
prompt.current().filter((part): part is ImageAttachmentPart => part.type === "image"),
prompt.current().filter(isAttachment),
)
const commentCount = createMemo(() => {
if (mode() === "shell") return 0
@@ -319,8 +321,12 @@ export function createComposerModel(adapter: ComposerAdapter, options?: { queue?
onContextRemove(item) {
if (item?.commentID) comments.remove(item.path, item.commentID)
},
openAttachment: (attachment) =>
dialog.show(() => createComponent(ImagePreview, { src: attachment.blob.url, alt: attachment.filename })),
openAttachment: (attachment) => {
if (attachment.type !== "image") return
void resolveBlobUrl(attachment.blob).then((src) => {
if (src) dialog.show(() => createComponent(ImagePreview, { src, alt: attachment.filename }))
})
},
openContext(key) {
const item = controller.contextItem(key)
if (item) openComment(item, adapter.controls(), layout, files, comments)
@@ -338,13 +344,15 @@ export function createComposerModel(adapter: ComposerAdapter, options?: { queue?
attachments: {
picker: platform.openAttachmentPickerDialog,
directory: () => sdk().directory,
destination: useAttachmentDestination(adapter.controls),
isDialogActive: () => !!dialog.active,
warn: () =>
showToast({
title: language.t("prompt.toast.pasteUnsupported.title"),
description: language.t("prompt.toast.pasteUnsupported.description"),
}),
duplicate: () => showToast({ title: language.t("prompt.toast.attachmentDuplicate.title") }),
onUploadError: (error) =>
showToast({
variant: "error",
title: language.t("prompt.toast.uploadFailed.title"),
description: composerErrorMessage(language, error),
}),
onError: (error) =>
showToast({
variant: "error",
+7 -2
View File
@@ -1,4 +1,9 @@
import type { Prompt } from "./state"
import type { ContentPart, ImageAttachmentPart, PathAttachmentPart, Prompt } from "./state"
/** Parts that sit beside the text rather than inside it. */
export function isAttachment(part: ContentPart): part is ImageAttachmentPart | PathAttachmentPart {
return part.type === "image" || part.type === "path"
}
export function clonePrompt(prompt: Prompt): Prompt {
return prompt.map((part) =>
@@ -17,7 +22,7 @@ export function appendPrompt(prompt: Prompt, following: Prompt): Prompt {
...clonePrompt(prompt),
{ type: "text", content: "\n\n", start, end: offset },
...clonePrompt(following).map((part) =>
part.type === "image" ? part : { ...part, start: part.start + offset, end: part.end + offset },
isAttachment(part) ? part : { ...part, start: part.start + offset, end: part.end + offset },
),
]
}
+9 -21
View File
@@ -1,8 +1,12 @@
import { describe, expect, test } from "bun:test"
import { Skill } from "@opencode/schema/skill"
import type { Prompt } from "@/composer/state"
import type { ImageAttachmentPart, Prompt } from "@/composer/state"
import { buildPromptRequest } from "./request"
function inline(filename: string, mime: string, extra?: Partial<ImageAttachmentPart>) {
return { type: "image" as const, id: `img_${filename}`, filename, mime, dataUrl: `data:${mime};base64,AAA`, ...extra }
}
describe("buildPromptRequest", () => {
test("builds text, files, and agents from the prompt", () => {
const prompt: Prompt = [
@@ -21,9 +25,7 @@ describe("buildPromptRequest", () => {
const result = buildPromptRequest({
prompt,
context: [{ key: "ctx:1", type: "file", path: "src/bar.ts", comment: "check this" }],
images: [
{ type: "image", id: "img_1", filename: "a.png", mime: "image/png", dataUrl: "data:image/png;base64,AAA" },
],
images: [inline("a.png", "image/png")],
text: "hello @src/foo.ts @planner",
sessionDirectory: "/repo",
})
@@ -45,16 +47,7 @@ describe("buildPromptRequest", () => {
const result = buildPromptRequest({
prompt: [{ type: "text", content: "check these", start: 0, end: 11 }],
context: [],
images: [
{ type: "image", id: "img_1", filename: "a.png", mime: "image/png", dataUrl: "data:image/png;base64,AAA" },
{
type: "image",
id: "img_2",
filename: "b.pdf",
mime: "application/pdf",
dataUrl: "data:application/pdf;base64,BBB",
},
],
images: [inline("a.png", "image/png"), inline("b.pdf", "application/pdf")],
text: "check these",
sessionDirectory: "/repo",
})
@@ -70,14 +63,9 @@ describe("buildPromptRequest", () => {
prompt: [],
context: [],
images: [
{
type: "image",
id: "img_external",
filename: "opencode.global.dat",
inline("opencode.global.dat", "text/plain", {
sourcePath: "C:\\Users\\Luke\\AppData\\Roaming\\ai.opencode.desktop.beta\\opencode.global.dat",
mime: "text/plain",
dataUrl: "data:text/plain;base64,AAA",
},
}),
],
text: "inspect this",
sessionDirectory: "C:\\Repos\\sst\\opencode",
+21 -5
View File
@@ -1,8 +1,13 @@
import { getFilename } from "@opencode/util/path"
import type { FileSelection } from "@/workspaces/files/model"
import { encodeFilePath } from "@/workspaces/files/path"
import type { AgentPart, FileAttachmentPart, ImageAttachmentPart, Prompt, SkillPart } from "@/composer/state"
import { formatCommentNote, type PromptComment } from "@/composer/comment-note"
import type { AgentPart, FileAttachmentPart, ImageAttachmentPart, PathAttachmentPart, Prompt, SkillPart } from "@/composer/state"
import {
formatAttachmentReference,
formatCommentNote,
type PromptAttachmentReference,
type PromptComment,
} from "@/composer/comment-note"
// Network fields feed both boundaries; display fields keep desktop-only rendering details in the local echo.
type PromptRequest = {
@@ -12,6 +17,7 @@ type PromptRequest = {
agents: { name: string; mention?: { start: number; end: number; text: string } }[]
skills: { id: string; name: string; mention?: { start: number; end: number; text: string } }[]
comments: PromptComment[]
attachments: PromptAttachmentReference[]
}
type ContextFile = {
@@ -56,6 +62,7 @@ const parseCommentMentions = (comment: string) => {
const isFileAttachment = (part: Prompt[number]): part is FileAttachmentPart => part.type === "file"
const isAgentAttachment = (part: Prompt[number]): part is AgentPart => part.type === "agent"
const isSkillAttachment = (part: Prompt[number]): part is SkillPart => part.type === "skill"
const isPathAttachment = (part: Prompt[number]): part is PathAttachmentPart => part.type === "path"
export function buildPromptRequest(input: BuildPromptRequestInput): PromptRequest {
const skills = input.prompt.filter(isSkillAttachment).map((attachment) => ({
@@ -106,18 +113,27 @@ export function buildPromptRequest(input: BuildPromptRequestInput): PromptReques
return [file, ...mentions]
})
const images = input.images.map((attachment) => ({
const inline = input.images.map((attachment) => ({
uri: attachment.dataUrl,
mime: attachment.mime,
name: attachment.sourcePath ?? attachment.filename,
}))
// Like comments, path references reach the model as text and the message UI through metadata.
const attachments = input.prompt
.filter(isPathAttachment)
.map((part) => ({ name: part.filename, mime: part.mime, path: part.path }))
return {
text: [...(input.text.trim() ? [input.text] : []), ...comments.map(formatCommentNote)].join("\n"),
text: [
...(input.text.trim() ? [input.text] : []),
...attachments.map(formatAttachmentReference),
...comments.map(formatCommentNote),
].join("\n"),
displayText: input.text,
files: [...files, ...context, ...images],
files: [...files, ...context, ...inline],
agents,
skills,
comments,
attachments,
}
}
+6 -2
View File
@@ -136,7 +136,7 @@ describe("composer persistence schemas", () => {
).toEqual(value)
})
test("migrates inline images but never encodes dataUrl or unresolved references", () => {
test("migrates inline images, keeps store references without a URL, and never encodes dataUrl", () => {
const value = Schema.decodeUnknownSync(
Persistence.withInitial(ComposerStore, { prompt: DEFAULT_PROMPT, context: { items: [] } }),
)({
@@ -149,12 +149,16 @@ describe("composer persistence schemas", () => {
{ ...image, blob: { id: "missing" }, dataUrl: "data:image/png;base64,YQ==" },
],
})
expect(value.prompt).toHaveLength(3)
expect(value.prompt).toHaveLength(6)
expect(value.prompt[0]).toEqual({
...image,
sourcePath: "/image.png",
blob: { id: "data:image/png;base64,YQ==", url: "data:image/png;base64,YQ==" },
})
// Bytes still in the draft store resolve on use; a non-blob URL is discarded in favour of the id.
expect(value.prompt[3]).toEqual({ ...image, blob: { id: "missing", url: "" } })
expect(value.prompt[4]).toEqual({ ...image, blob: { id: "bad", url: "" } })
expect(value.prompt[5]).toEqual({ ...image, blob: { id: "missing", url: "" } })
const encoded = Schema.encodeSync(ComposerStore)(value)
expect(JSON.stringify(encoded)).not.toContain("dataUrl")
expect(
+22 -4
View File
@@ -61,11 +61,12 @@ const ImageFields = {
}
const Image = Persistence.struct({
...ImageFields,
blob: Schema.Struct({ id: Schema.NonEmptyString, url: Schema.String.check(Schema.isPattern(/^(blob:|data:)/)) }),
// An empty URL is an image whose bytes are still in the draft store; see `resolveBlobUrl`.
blob: Schema.Struct({ id: Schema.NonEmptyString, url: Schema.String.check(Schema.isPattern(/^(blob:|data:|$)/)) }),
})
// Draft storage hydrates content-addressed blobs before this codec runs. Legacy
// inline data remains usable, but unresolved references are not renderable.
// Draft storage keeps content-addressed blobs in the store until an image is shown or sent; a
// reference without a URL resolves through `resolveBlobUrl`. Legacy inline data remains usable.
export const ImageAttachmentPart = Schema.Struct({
...ImageFields,
blob: Persistence.optional(
@@ -94,7 +95,24 @@ export const ImageAttachmentPart = Schema.Struct({
)
export type ImageAttachmentPart = typeof ImageAttachmentPart.Type
export const ContentPart = Schema.Union([TextPart, FileAttachmentPart, AgentPart, SkillPart, ImageAttachmentPart])
// A file the model receives as a path on the server: its bytes never enter the draft store.
export const PathAttachmentPart = Persistence.struct({
type: Schema.Literal("path"),
id: Schema.String,
filename: Schema.String,
mime: Schema.String,
path: Schema.String,
})
export type PathAttachmentPart = typeof PathAttachmentPart.Type
export const ContentPart = Schema.Union([
TextPart,
FileAttachmentPart,
AgentPart,
SkillPart,
ImageAttachmentPart,
PathAttachmentPart,
])
export type ContentPart = typeof ContentPart.Type
export const Prompt = Persistence.array(ContentPart)
export type Prompt = typeof Prompt.Type
+9
View File
@@ -88,6 +88,15 @@ describe("prompt state initialization", () => {
start: 5,
end: 12,
},
// A reference without a usable URL keeps its id; the bytes resolve from the draft store on use.
{
type: "image",
id: "missing-blob",
filename: "missing.png",
mime: "image/png",
blob: { id: "content-hash-without-a-url", url: "" },
},
{ type: "image", id: "invalid-url", filename: "invalid.png", mime: "image/png", blob: { id: "hash", url: "" } },
{
type: "image",
id: "legacy",
+1
View File
@@ -23,6 +23,7 @@ export type {
FileAttachmentPart,
FileContextItem,
ImageAttachmentPart,
PathAttachmentPart,
Prompt,
PromptModel,
SkillPart,
@@ -1,5 +1,5 @@
import type { ComposerState, ContextItem, Prompt } from "./state"
import { appendPrompt, clonePrompt } from "./prompt-parts"
import { appendPrompt, clonePrompt, isAttachment } from "./prompt-parts"
export type ComposerStateTarget = ReturnType<ComposerState["capture"]>
@@ -22,7 +22,7 @@ export function createComposerSubmission(input: {
if (initial !== target) {
initial.reset()
// A preparing session may already have an unsent follow-up in its promoted composer.
if (preserveDraft && target.current().some((part) => part.type === "image" || part.content.length > 0))
if (preserveDraft && target.current().some((part) => isAttachment(part) || part.content.length > 0))
following = clonePrompt(target.current())
}
if (!following) target.reset()
+21 -10
View File
@@ -8,7 +8,8 @@ import type { ComposerAdapter, ComposerDelivery, ComposerSelection, ComposerSess
import { createComposerSubmission } from "./submission-state"
import { buildPromptRequest } from "./request"
import { setCursorPosition } from "./editor/dom"
import { blobDataUrl } from "@/runtime/persistence/drafts"
import { blobDataUrl, resolveBlobUrl } from "@/runtime/persistence/drafts"
import { isAttachment } from "./prompt-parts"
import type { ModelSelection } from "@/providers/models/selection"
const submitting = new WeakSet<object>()
@@ -58,12 +59,22 @@ export function createComposerSubmit(input: ComposerSubmitInput) {
selection: item.selection ? { ...item.selection } : undefined,
})),
})
const value = readSubmission(input, submission.prompt, submission.context, options?.alternate ?? false)
if (!value) {
const read = readSubmission(input, submission.prompt, submission.context, options?.alternate ?? false)
if (!read) {
if (input.adapter.working() && input.adapter.kind === "active-session") void input.adapter.interrupt()
return
}
if (submitting.has(input.adapter.state)) return
// Images restored from a draft or history carry ids only; the optimistic message shows their URLs.
const value = {
...read,
images: await Promise.all(
read.images.map(async (image) => ({
...image,
blob: { ...image.blob, url: (await resolveBlobUrl(image.blob)) ?? image.blob.url },
})),
),
}
submitting.add(input.adapter.state)
const comments = input.comments.capture()
// Capture command intent before starting a session in a worktree whose catalog has not loaded.
@@ -151,6 +162,9 @@ function handoffMessage(value: ComposerSubmission): SessionMessageUser {
})),
metadata: {
displayText: value.text,
attachments: value.prompt.flatMap((part) =>
part.type === "path" ? [{ name: part.filename, mime: part.mime, path: part.path }] : [],
),
comments: value.context.flatMap((item) =>
item.comment?.trim()
? [
@@ -185,7 +199,7 @@ function readSubmission(
if (mode === "shell" && !text.trim()) return
const images = prompt.filter((part): part is ImageAttachmentPart => part.type === "image")
const comments = context.filter((item) => !!item.comment?.trim()).length
if (!text.trim() && images.length === 0 && comments === 0) return
if (!text.trim() && !prompt.some(isAttachment) && comments === 0) return
const controls = input.adapter.controls()
const model = controls.model.selection.current()
@@ -358,6 +372,7 @@ async function sendPrompt(
metadata: {
displayText: request.displayText,
comments: request.comments,
attachments: request.attachments,
agent: value.selection.agent,
model: {
...value.selection.model,
@@ -372,19 +387,15 @@ async function sendPrompt(
async function buildSubmissionRequest(session: ComposerSession, value: ComposerSubmission) {
const images = await Promise.all(
value.images.map(async (attachment) => ({
...attachment,
dataUrl: await blobDataUrl(attachment.blob, attachment.mime),
})),
value.images.map(async (attachment) => ({ ...attachment, dataUrl: await blobDataUrl(attachment.blob, attachment.mime) })),
)
const request = buildPromptRequest({
return buildPromptRequest({
prompt: value.prompt,
context: value.context,
images,
text: value.text,
sessionDirectory: session.directory,
})
return request
}
function failSubmission(
@@ -1,4 +1,5 @@
import type { ComposerHistoryEntry, ComposerPersistedState, ComposerSuggestion } from "../types"
import { isAttachment } from "../prompt-parts"
export type ComposerInteractionState = {
mode: "normal" | "shell"
@@ -236,7 +237,7 @@ function populated(persisted: ComposerPersistedState) {
return (
!!promptText(persisted).trim() ||
persisted.context.items.length > 0 ||
persisted.prompt.some((part) => part.type === "file" || part.type === "image")
persisted.prompt.some((part) => part.type === "file" || isAttachment(part))
)
}
+10 -2
View File
@@ -1,9 +1,17 @@
import type { AgentPart, ComposerStore, FileAttachmentPart, ImageAttachmentPart, Prompt, SkillPart } from "./state"
import type {
AgentPart,
ComposerStore,
FileAttachmentPart,
ImageAttachmentPart,
PathAttachmentPart,
Prompt,
SkillPart,
} from "./state"
export type ComposerFilePart = FileAttachmentPart
export type ComposerAgentPart = AgentPart
export type ComposerSkillPart = SkillPart
export type ComposerAttachment = ImageAttachmentPart
export type ComposerAttachment = ImageAttachmentPart | PathAttachmentPart
export type ComposerPrompt = Prompt
export type ComposerComment = ComposerStore["context"]["items"][number]
export type ComposerPersistedState = ComposerStore
+1 -1
View File
@@ -1,5 +1,4 @@
export { AppBaseProviders, AppInterface, preloadRoute } from "./app"
export { ACCEPTED_FILE_EXTENSIONS } from "./runtime/platform/file-picker"
export { useCommand } from "./shell/commands/command"
export { currentRoute, type LayoutRoute, useCurrentRoute } from "./shell/state/layout"
export { loadLocaleDict, normalizeLocale, type Locale, useLanguage } from "./runtime/i18n/language"
@@ -15,6 +14,7 @@ export type {
BrowserPaneTarget,
} from "./runtime/platform/browser-pane"
export { ServerConnection, useServers } from "./runtime/server/registry"
export { useGlobal } from "./runtime/server/runtime"
export { useTabs } from "./shell/tabs/tabs"
export { createDraftStore } from "./runtime/persistence/drafts"
export { createNamespaceStorage, type NamespaceStorage } from "./runtime/persistence/namespace"
+21 -2
View File
@@ -42,8 +42,14 @@
container-name: session-chat;
}
[data-component="new-session"] {
container-type: inline-size;
container-name: new-session;
}
[data-slot="session-chat-panel"]
:is([data-timeline-virtual-content], [data-component="session-composer-dock"] > div) {
:is([data-timeline-virtual-content], [data-component="session-composer-dock"] > div),
[data-slot="new-session-content"] {
translate: var(--session-summary-resize-translate, var(--session-summary-translate, none));
transition: translate 240ms cubic-bezier(0.4, 0, 0.2, 1);
@@ -53,7 +59,8 @@
}
[data-slot="session-chat-panel"][data-summary-resizing="true"]
:is([data-timeline-virtual-content], [data-component="session-composer-dock"] > div) {
:is([data-timeline-virtual-content], [data-component="session-composer-dock"] > div),
[data-component="new-session"][data-summary-resizing="true"] [data-slot="new-session-content"] {
transition: none;
}
@@ -70,6 +77,18 @@
}
}
/* Shift only while centered content overlaps the 280px summary cards and their 16px edge inset:
880px + 2 * (280px + 16px) = 1472px. Center it in the remaining space, reserving 320px. */
@container new-session (min-width: 1200px) and (width < 1472px) {
[data-component="new-session"][data-summary-open="true"] [data-slot="new-session-content"] {
--session-summary-translate: -160px;
&:dir(rtl) {
--session-summary-translate: 160px;
}
}
}
[data-slot="session-chat-panel"][data-scrollbar-hidden="true"]
[data-slot="session-timeline-scroll"]
> .scroll-view__thumb {
@@ -48,7 +48,8 @@ export function createNewSessionComposerAdapter(props: {
submitted: props.submitted,
async start(selection, submission, message) {
const draftID = props.draftID
const projectDirectory = location().directory
const currentDirectory = location().directory
const projectDirectory = data.location.info({ directory: currentDirectory })?.project.canonical ?? currentDirectory
const worktree = props.worktree()
const branch = props.branch()
const mcp = props.mcp.capture()
+26 -3
View File
@@ -1,8 +1,10 @@
import { useDialog } from "@opencode/ui/context/dialog"
import { Tooltip } from "@opencode/ui/tooltip"
import { Icon } from "@opencode/ui/icon"
import { Show, Suspense, createMemo, createSignal, lazy } from "solid-js"
import { Show, Suspense, createMemo, createSignal, lazy, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { makeEventListener } from "@solid-primitives/event-listener"
import { debounce } from "@solid-primitives/scheduled"
import { Schema } from "effect"
import createPresence from "solid-presence"
import { Composer } from "@/composer/composer"
@@ -48,7 +50,21 @@ export function NewSessionView(props: {
workspace: NewSessionWorkspaceController
mcp: DraftMcpControls
}) {
const [store, setStore] = createStore({ summary: false })
const [store, setStore] = createStore({
summary: false,
content: undefined as HTMLDivElement | undefined,
summaryResizeTranslate: undefined as string | undefined,
})
const finishWindowResize = debounce(() => setStore("summaryResizeTranslate", undefined), 150)
onMount(() => {
makeEventListener(window, "resize", () => {
if (store.summaryResizeTranslate === undefined) {
// Freeze the painted offset, including an in-flight slide, until resizing settles.
setStore("summaryResizeTranslate", store.content ? getComputedStyle(store.content).translate : "none")
}
finishWindowResize()
})
})
const [onboarding, setOnboarding, , onboardingReady] = persisted(
Persist.global("workspace-onboarding"),
WorkspaceOnboardingSchema,
@@ -63,6 +79,9 @@ export function NewSessionView(props: {
<div class="@container relative flex flex-col min-h-0 h-full flex-1">
<div
data-component="new-session"
data-summary-open={store.summary}
data-summary-resizing={store.summaryResizeTranslate !== undefined}
style={{ "--session-summary-resize-translate": store.summaryResizeTranslate }}
class="relative flex-1 min-h-0 overflow-hidden rounded-[10px] bg-v2-background-bg-base shadow-[var(--v2-elevation-raised)]"
>
<ComposerDropzone
@@ -89,7 +108,11 @@ export function NewSessionView(props: {
</SummaryPopover>
</div>
<div class="absolute inset-x-0 top-[25.375%] flex justify-center px-6">
<div class={NEW_SESSION_CONTENT_WIDTH}>
<div
ref={(element) => setStore("content", element)}
data-slot="new-session-content"
class={NEW_SESSION_CONTENT_WIDTH}
>
<NewSessionWordmark />
<div class="mt-8 flex flex-col gap-8">
<Composer model={props.composer} />
@@ -1,7 +1,12 @@
[data-component="new-session-wordmark"] {
--icon-strong-base: var(--icon-base);
--wordmark-shimmer-opacity: 0.45;
.wordmark-shimmer {
--icon-base: white;
--icon-strong-base: white;
--icon-weak-base: transparent;
/* Blend above the base logo's opacity so the sweep only adds light in either theme. */
color: white;
mix-blend-mode: screen;
@@ -13,6 +18,10 @@
}
}
[data-color-scheme="light"] [data-component="new-session-wordmark"] {
--icon-weak-base: color-mix(in srgb, var(--icon-base) 30%, transparent);
}
[data-color-scheme="dark"] [data-component="new-session-wordmark"] {
/* Preserve the previous dark highlight strength: 0.16 × 0.7 × 0.6. */
--wordmark-shimmer-opacity: 0.0672;
+4 -4
View File
@@ -1,4 +1,4 @@
import { Wordmark } from "@opencode/ui/wordmark"
import { Logo } from "@opencode/ui/logo"
import "./wordmark.css"
export function NewSessionWordmark() {
@@ -8,9 +8,9 @@ export function NewSessionWordmark() {
aria-hidden="true"
class="pointer-events-none mx-auto w-full max-w-[720px] text-v2-background-bg-inverse"
>
<div data-slot="wordmark-reveal" class="relative">
<Wordmark fade={false} class="block h-auto w-full opacity-60 [[data-color-scheme=dark]_&]:opacity-50" />
<Wordmark fade={false} muted={false} class="wordmark-shimmer absolute inset-0 h-auto w-full" />
<div data-slot="wordmark-reveal" class="relative mx-auto w-4/5">
<Logo class="block aspect-[720/129] w-full opacity-[0.16]" />
<Logo class="wordmark-shimmer absolute inset-0 aspect-[720/129] w-full" />
</div>
</div>
)
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "ClaudeáŖ GPTáŖ Gemini áŠĨና ሌሎá‰Ŋንም ጨምሮ የተáˆĩተáŠĢከሉ ሞዴሎá‰Ŋ",
"dialog.provider.opencode.tagline": "á‰ŗáˆ›áŠ የተመá‰ģቹ ሞዴሎá‰Ŋ",
"dialog.provider.opencodeGo.tagline": "ዝቅተኛ ወáŒĒ ለሁሉም ሰው",
"dialog.provider.anthropic.note": "የቀáŒĨá‰ŗ የClaude ሞዴሎá‰ŊáŖ ፕሮ áŠĨና ማክáˆĩን ጨምሮ",
"dialog.provider.copilot.note": "AI ሞዴሎá‰Ŋ በGitHub Copilot] በኩል ኮá‹ĩ ለማá‹ĩረግ áŠĨገዛ",
"dialog.provider.openai.note": "GPT ሞዴሎá‰Ŋ áˆˆáˆáŒŖáŠ• áŠĨና á‰ŊáˆŽá‰ŗ ላለው አጠቃላይ AI á‰°áŒá‰ŖáˆĢá‰ĩ",
"dialog.provider.google.note": "Gemini áˆˆáˆáŒŖáŠ•áŖ የተዋቀሩ ምላሾá‰Ŋ ሞዴሎá‰Ŋ",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ከ70+ በላይ አቅáˆĢá‰ĸዎá‰Ŋን ይመልከቱ",
"dialog.provider.viewAll": "ተጨማáˆĒ አቅáˆĢá‰ĸዎá‰Ŋን áŠ áˆŗá‹­",
"provider.connect.title": "{{provider}} አገናኝ",
"provider.connect.title.anthropicProMax": "በClaude Pro/Max ይግቡ",
"provider.connect.selectMethod": "የመግá‰ĸá‹Ģ ዘዴን ለ{{provider}} ምረáŒĨáĸ",
"provider.connect.method.apiKey": "API ቁልፍ",
"provider.connect.method.browser": "áŠ áˆŗáˆŊ",
@@ -371,8 +369,6 @@ export const dict = {
"prompt.attachment.remove": "á‹“á‰ŖáˆĒን አáˆĩወግá‹ĩ",
"prompt.action.send": "ላክ",
"prompt.action.stop": "አቁም",
"prompt.toast.pasteUnsupported.title": "የማይደገፍ á‹“á‰ŖáˆĒ",
"prompt.toast.pasteUnsupported.description": "ምáˆĩሎá‰ŊáŖ ፒዲኤፎá‰Ŋ ወይም የáŒŊሑፍ ፋይሎá‰Ŋ á‰Ĩá‰ģ áŠĨዚህ ጋር áˆŠáŒŖáˆ˜áˆŠ ይá‰Ŋላሉáĸ",
"prompt.toast.attachmentDuplicate.title": "ይህ ፋይል አáˆĩቀá‹ĩሞ ተሰቅሏል",
"prompt.toast.modelAgentRequired.title": "ወáŠĒል áŠĨና ሞዴል ይምረጡ",
"prompt.toast.modelAgentRequired.description": "ፕሮምፕá‰ĩ ከመላክዎ በፊá‰ĩ ወáŠĒል áŠĨና ሞዴል ይምረጡáĸ",
-4
View File
@@ -198,7 +198,6 @@ export const dict = {
"dialog.provider.opencode.note": "Ų†Ų…Ø§Ø°ØŦ Ų…ØŽØĒØ§ØąØŠ ØĒØĒØļŲ…Ų† Claude ؈ GPT ؈ Gemini ŲˆØ§Ų„Ų…Ø˛ŲŠØ¯",
"dialog.provider.opencode.tagline": "Ų†Ų…Ø§Ø°ØŦ Ų…ŲˆØĢŲˆŲ‚ØŠ ŲˆŲ…Ø­ØŗŲ†ØŠ",
"dialog.provider.opencodeGo.tagline": "اشØĒØąØ§Ųƒ Ų…Ų†ØŽŲØļ Ø§Ų„ØĒŲƒŲ„ŲØŠ Ų„Ų„ØŦŲ…ŲŠØš",
"dialog.provider.anthropic.note": "؈ØĩŲˆŲ„ Ų…Ø¨Ø§Ø´Øą ØĨŲ„Ų‰ Ų†Ų…Ø§Ø°ØŦ Claude، Ø¨Ų…Ø§ ŲŲŠŲ‡Ø§ Pro ؈ Max",
"dialog.provider.copilot.note": "Ų†Ų…Ø§Ø°ØŦ Ø°ŲƒØ§ØĄ اØĩØˇŲ†Ø§ØšŲŠ Ų„Ų„Ų…ØŗØ§ØšØ¯ØŠ ؁؊ Ø§Ų„Ø¨ØąŲ…ØŦØŠ ØšØ¨Øą GitHub Copilot",
"dialog.provider.openai.note": "Ų†Ų…Ø§Ø°ØŦ GPT Ų„Ų…Ų‡Ø§Ų… Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ø§Ų„ØšØ§Ų…ØŠ Ø§Ų„ØŗØąŲŠØšØŠ ŲˆØ§Ų„Ų…ØĒŲ‚Ø¯Ų…ØŠ",
"dialog.provider.google.note": "Ų†Ų…Ø§Ø°ØŦ Gemini Ų„Ø§ØŗØĒØŦاباØĒ ØŗØąŲŠØšØŠ ŲˆŲ…Ų†Ø¸Ų…ØŠ",
@@ -215,7 +214,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ØšØąØļ ØŖŲƒØĢØą Ų…Ų† 70 Ų…ŲˆŲØąŲ‹Ø§ ØĨØļØ§ŲŲŠŲ‹Ø§",
"dialog.provider.viewAll": "ØšØąØļ Ø§Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ŲˆŲØąŲŠŲ†",
"provider.connect.title": "Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ų€ {{provider}}",
"provider.connect.title.anthropicProMax": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Claude Pro/Max",
"provider.connect.selectMethod": "حدد ØˇØąŲŠŲ‚ØŠ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ų„Ų€ {{provider}}.",
"provider.connect.method.apiKey": "؅؁ØĒاح API",
"provider.connect.method.browser": "Ø§Ų„Ų…ØĒØĩŲØ­",
@@ -380,9 +378,7 @@ export const dict = {
"prompt.attachment.remove": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØąŲŲ‚",
"prompt.action.send": "ØĨØąØŗØ§Ų„",
"prompt.action.stop": "ØĨŲŠŲ‚Ø§Ų",
"prompt.toast.pasteUnsupported.title": "Ų…ØąŲŲ‚ ØēŲŠØą Ų…Ø¯ØšŲˆŲ…",
"prompt.toast.attachmentDuplicate.title": "ØĒŲ… ØĒØ­Ų…ŲŠŲ„ Ų‡Ø°Ø§ Ø§Ų„Ų…Ų„Ų Ø¨Ø§Ų„ŲØšŲ„",
"prompt.toast.pasteUnsupported.description": "ŲŠŲ…ŲƒŲ† ØĨØąŲØ§Ų‚ Ø§Ų„ØĩŲˆØą ØŖŲˆ Ų…Ų„ŲØ§ØĒ PDF ØŖŲˆ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ų†ØĩŲŠØŠ ŲŲ‚Øˇ Ų‡Ų†Ø§.",
"prompt.toast.modelAgentRequired.title": "حدد ŲˆŲƒŲŠŲ„Ø§Ų‹ ŲˆŲ†Ų…ŲˆØ°ØŦØ§Ų‹",
"prompt.toast.modelAgentRequired.description": "ا؎ØĒØą ŲˆŲƒŲŠŲ„Ø§Ų‹ ŲˆŲ†Ų…ŲˆØ°ØŦØ§Ų‹ Ų‚Ø¨Ų„ ØĨØąØŗØ§Ų„ Ø§Ų„Ų…ŲˆØŦŲ‡.",
"prompt.toast.worktreeCreateFailed.title": "ŲØ´Ų„ ØĨŲ†Ø´Ø§ØĄ Ø´ØŦØąØŠ Ø§Ų„ØšŲ…Ų„",
-4
View File
@@ -194,7 +194,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini və daha çoxu daxil olmaqla seçilmiş modellər",
"dialog.provider.opencode.tagline": "EtibarlÄą optimallaşdÄąrÄąlmÄąÅŸ modellər",
"dialog.provider.opencodeGo.tagline": "HamÄą ÃŧçÃŧn aşağı qiymətli abunəlik",
"dialog.provider.anthropic.note": "Pro və Max daxil olmaqla Claude modellərinə birbaşa giriş",
"dialog.provider.copilot.note": "GitHub Copilot vasitəsilə kodlaşdÄąrma yardÄąmÄą ÃŧçÃŧn AI modelləri",
"dialog.provider.openai.note": "SÃŧrətli və bacarÄąqlÄą Ãŧmumi AI tapÅŸÄąrÄąqlarÄą ÃŧçÃŧn GPT modelləri",
"dialog.provider.google.note": "SÃŧrətli, strukturlaşdÄąrÄąlmÄąÅŸ cavablar ÃŧçÃŧn Gemini modelləri",
@@ -211,7 +210,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Daha 70+ provayderə baxın",
"dialog.provider.viewAll": "Daha çox provayder gÃļstər",
"provider.connect.title": "{{provider}} provayderini qoş",
"provider.connect.title.anthropicProMax": "Claude Pro/Max ilə daxil ol",
"provider.connect.selectMethod": "{{provider}} ÃŧçÃŧn giriş Ãŧsulunu seçin.",
"provider.connect.method.apiKey": "API açarÄą",
"provider.connect.method.browser": "Brauzer",
@@ -378,8 +376,6 @@ export const dict = {
"prompt.attachment.remove": "Əlavəni sil",
"prompt.action.send": "GÃļndər",
"prompt.action.stop": "DayandÄąr",
"prompt.toast.pasteUnsupported.title": "Dəstəklənməyən əlavə",
"prompt.toast.pasteUnsupported.description": "Buraya yalnÄąz şəkillər, PDF-lər və ya mətn fayllarÄą əlavə edilə bilər.",
"prompt.toast.attachmentDuplicate.title": "Bu fayl artÄąq yÃŧklənib",
"prompt.toast.modelAgentRequired.title": "Agent və model seçin",
"prompt.toast.modelAgentRequired.description": "Prompt gÃļndərməzdən əvvəl agent və model seçin.",
-4
View File
@@ -194,7 +194,6 @@ export const dict = {
"dialog.provider.opencode.note": "ĐŸĐžĐ´ĐąŅ€Đ°ĐŊи ĐŧОдĐĩĐģи, вĐēĐģŅŽŅ‡Đ¸Ņ‚ĐĩĐģĐŊĐž Claude, GPT, Gemini и Đ´Ņ€ŅƒĐŗĐ¸",
"dialog.provider.opencode.tagline": "НадĐĩĐļĐ´ĐŊи ĐžĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊи ĐŧОдĐĩĐģи",
"dialog.provider.opencodeGo.tagline": "АйОĐŊаĐŧĐĩĐŊŅ‚ ĐŊа ĐŊĐ¸ŅĐēа ҆ĐĩĐŊа Са Đ˛ŅĐĩĐēи",
"dialog.provider.anthropic.note": "Đ”Đ¸Ņ€ĐĩĐēŅ‚ĐĩĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Claude ĐŧОдĐĩĐģи, вĐēĐģŅŽŅ‡Đ¸Ņ‚ĐĩĐģĐŊĐž Pro и Max",
"dialog.provider.copilot.note": "AI ĐŧОдĐĩĐģи Са ĐŋĐžĐŧĐžŅ‰ ĐŋŅ€Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ ҇ҀĐĩС GitHub Copilot",
"dialog.provider.openai.note": "GPT ĐŧОдĐĩĐģи Са ĐąŅŠŅ€ĐˇĐ¸, ҁĐŋĐžŅĐžĐąĐŊи ĐžĐąŅ‰Đ¸ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ ҁ иСĐēŅƒŅŅ‚Đ˛ĐĩĐŊ иĐŊŅ‚ĐĩĐģĐĩĐēŅ‚",
"dialog.provider.google.note": "Gemini ĐŧОдĐĩĐģи Са ĐąŅŠŅ€ĐˇĐ¸, ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ¸Ņ€Đ°ĐŊи ĐžŅ‚ĐŗĐžĐ˛ĐžŅ€Đ¸",
@@ -211,7 +210,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ВиĐļŅ‚Đĩ ĐžŅ‰Đĩ 70+ Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Ņ†Đ¸",
"dialog.provider.viewAll": "ПоĐēаСваĐŊĐĩ ĐŊа ĐžŅ‰Đĩ Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Ņ†Đ¸",
"provider.connect.title": "ĐĄĐ˛ŅŠŅ€ĐļĐĩŅ‚Đĩ {{provider}}",
"provider.connect.title.anthropicProMax": "ВĐģĐĩĐˇŅ‚Đĩ ҁ Claude Pro/Max",
"provider.connect.selectMethod": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ Са вĐģиСаĐŊĐĩ Са {{provider}}.",
"provider.connect.method.apiKey": "API ĐēĐģŅŽŅ‡",
"provider.connect.method.browser": "Đ‘Ņ€Đ°ŅƒĐˇŅŠŅ€",
@@ -378,8 +376,6 @@ export const dict = {
"prompt.attachment.remove": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģ",
"prompt.action.send": "ИСĐŋŅ€Đ°Ņ‚ĐĩŅ‚Đĩ",
"prompt.action.stop": "ĐĄĐŋŅ€ĐĩŅ‚Đĩ",
"prompt.toast.pasteUnsupported.title": "НĐĩĐŋĐžĐ´Đ´ŅŠŅ€ĐļаĐŊ ĐŋŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊ Ņ„Đ°ĐšĐģ",
"prompt.toast.pasteUnsupported.description": "Đĸ҃Đē ĐŧĐžĐŗĐ°Ņ‚ да ҁĐĩ ĐŋŅ€Đ¸ĐēĐ°Ņ‡Đ˛Đ°Ņ‚ ŅĐ°ĐŧĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ, PDF иĐģи Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛Đ¸ Ņ„Đ°ĐšĐģОвĐĩ.",
"prompt.toast.attachmentDuplicate.title": "ĐĸОСи Ņ„Đ°ĐšĐģ вĐĩ҇Đĩ Đĩ ĐēĐ°Ņ‡ĐĩĐŊ",
"prompt.toast.modelAgentRequired.title": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚ и ĐŧОдĐĩĐģ",
"prompt.toast.modelAgentRequired.description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚ и ĐŧОдĐĩĐģ, ĐŋŅ€Đĩди да иСĐŋŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŋОдĐēаĐŊа.",
-4
View File
@@ -192,7 +192,6 @@ export const dict: Record<string, string> = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini āĻāĻŦāĻ‚ āφāϰāĻ“ āĻ…āύ⧇āĻ• āĻ•āĻŋāϛ⧁ āϏāĻš āĻ•āĻŋāωāϰ⧇āϟ āĻ•āϰāĻž āĻŽāĻĄā§‡āϞ",
"dialog.provider.opencode.tagline": "āύāĻŋāĻ°ā§āĻ­āϰāϝ⧋āĻ—ā§āϝ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ āĻ•āϰāĻž āĻŽāĻĄā§‡āϞ",
"dialog.provider.opencodeGo.tagline": "āϏāĻŦāĻžāϰ āϜāĻ¨ā§āϝ āĻ•āĻŽ āĻ–āϰāĻšā§‡ āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻŋāĻĒāĻļāύ",
"dialog.provider.anthropic.note": "āĻĒā§āϰ⧋ āĻāĻŦāĻ‚ āĻŽā§āϝāĻžāĻ•ā§āϏ āϏāĻš Claude āĻŽāĻĄā§‡āϞāϗ⧁āϞāĻŋāϤ⧇ āϏāϰāĻžāϏāϰāĻŋ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ",
"dialog.provider.copilot.note": "GitHub Copilot āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϕ⧋āĻĄāĻŋāĻ‚ āϏāĻšāĻžāϝāĻŧāϤāĻžāϰ āϜāĻ¨ā§āϝ AI āĻŽāĻĄā§‡āϞ",
"dialog.provider.openai.note": "āĻĻā§āϰ⧁āϤ, āϏāĻ•ā§āώāĻŽ āϏāĻžāϧāĻžāϰāĻŖ AI āĻ•āĻžāĻ°ā§āϝāϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ GPT āĻŽāĻĄā§‡āϞ",
"dialog.provider.google.note": "āĻĻā§āϰ⧁āϤ, āĻ•āĻžāĻ āĻžāĻŽā§‹āĻ—āϤ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϰ āϜāĻ¨ā§āϝ Gemini āĻŽāĻĄā§‡āϞ",
@@ -209,7 +208,6 @@ export const dict: Record<string, string> = {
"dialog.model.unpaid.viewMoreProviders": "āφāϰāĻ“ 70+ āĻĒā§āϰāĻĻāĻžāύāĻ•āĻžāϰ⧀ āĻĻ⧇āϖ⧁āύ",
"dialog.provider.viewAll": "āφāϰ⧋ āĻĒā§āϰāĻĻāĻžāύāĻ•āĻžāϰ⧀ āĻĻ⧇āĻ–āĻžāύ",
"provider.connect.title": "{{provider}} āϏāĻ‚āϝ⧋āĻ— āĻ•āϰ⧁āύ",
"provider.connect.title.anthropicProMax": "Claude Pro/Max āĻĻāĻŋāϝāĻŧ⧇ āϞāĻ—āχāύ āĻ•āϰ⧁āύ",
"provider.connect.selectMethod": "{{provider}} āĻāϰ āϜāĻ¨ā§āϝ āϞāĻ—āχāύ āĻĒāĻĻā§āϧāϤāĻŋ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύāĨ¤",
"provider.connect.method.apiKey": "API āϕ⧀",
"provider.connect.method.browser": "āĻŦā§āϰāĻžāωāϜāĻžāϰ",
@@ -375,8 +373,6 @@ export const dict: Record<string, string> = {
"prompt.attachment.remove": "āϏāĻ‚āϝ⧁āĻ•ā§āϤāĻŋ āϏāϰāĻžāύ",
"prompt.action.send": "āĻĒāĻžāĻ āĻžāύ",
"prompt.action.stop": "āĻĨāĻžāĻŽā§‹",
"prompt.toast.pasteUnsupported.title": "āĻ…āϏāĻŽāĻ°ā§āĻĨāĻŋāϤ āϏāĻ‚āϝ⧁āĻ•ā§āϤāĻŋ",
"prompt.toast.pasteUnsupported.description": "āĻāĻ–āĻžāύ⧇ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻ›āĻŦāĻŋ, āĻĒāĻŋāĻĄāĻŋāĻāĻĢ āĻŦāĻž āĻŸā§‡āĻ•ā§āϏāϟ āĻĢāĻžāχāϞ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻ•āϰāĻž āϝāĻžāĻŦ⧇āĨ¤",
"prompt.toast.attachmentDuplicate.title": "āĻāχ āĻĢāĻžāχāϞ āχāϤāĻŋāĻŽāĻ§ā§āϝ⧇ āφāĻĒāϞ⧋āĻĄ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇",
"prompt.toast.modelAgentRequired.title": "āĻāĻ•āϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻŦāĻ‚ āĻŽāĻĄā§‡āϞ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύ",
"prompt.toast.modelAgentRequired.description": "āĻĒā§āϰāĻŽā§āĻĒāϟ āĻĒāĻžāĻ āĻžāύ⧋āϰ āφāϗ⧇ āĻāĻ•āϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻŦāĻ‚ āĻŽāĻĄā§‡āϞ āĻŦ⧇āϛ⧇ āύāĻŋāύāĨ¤",
-4
View File
@@ -200,7 +200,6 @@ export const dict = {
"dialog.provider.opencode.note": "Modelos selecionados incluindo Claude, GPT, Gemini e mais",
"dialog.provider.opencode.tagline": "Modelos otimizados e confiÃĄveis",
"dialog.provider.opencodeGo.tagline": "Assinatura de baixo custo para todos",
"dialog.provider.anthropic.note": "Acesso direto aos modelos Claude, incluindo Pro e Max",
"dialog.provider.copilot.note": "Modelos de IA para auxílio à programaÃ§ÃŖo pelo GitHub Copilot",
"dialog.provider.openai.note": "Modelos GPT rÃĄpidos e avançados para tarefas gerais de IA",
"dialog.provider.google.note": "Modelos Gemini para respostas rÃĄpidas e estruturadas",
@@ -217,7 +216,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Ver mais de 70 provedores",
"dialog.provider.viewAll": "Ver mais provedores",
"provider.connect.title": "Conectar {{provider}}",
"provider.connect.title.anthropicProMax": "Entrar com Claude Pro/Max",
"provider.connect.selectMethod": "Selecionar mÊtodo de login para {{provider}}.",
"provider.connect.method.apiKey": "Chave de API",
"provider.connect.method.browser": "Navegador",
@@ -382,9 +380,7 @@ export const dict = {
"prompt.attachment.remove": "Remover anexo",
"prompt.action.send": "Enviar",
"prompt.action.stop": "Parar",
"prompt.toast.pasteUnsupported.title": "Anexo nÃŖo suportado",
"prompt.toast.attachmentDuplicate.title": "Este arquivo jÃĄ foi enviado",
"prompt.toast.pasteUnsupported.description": "Apenas imagens, PDFs ou arquivos de texto podem ser anexados aqui.",
"prompt.toast.modelAgentRequired.title": "Selecione um agente e modelo",
"prompt.toast.modelAgentRequired.description": "Escolha um agente e modelo antes de enviar um prompt.",
"prompt.toast.worktreeCreateFailed.title": "Falha ao criar worktree",
-4
View File
@@ -208,7 +208,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kurirani modeli uključujući Claude, GPT, Gemini i druge",
"dialog.provider.opencode.tagline": "Pouzdani optimizovani modeli",
"dialog.provider.opencodeGo.tagline": "Povoljna pretplata za sve",
"dialog.provider.anthropic.note": "Direktan pristup Claude modelima, uključujući Pro i Max",
"dialog.provider.copilot.note": "AI modeli za pomoć pri kodiranju putem GitHub Copilot",
"dialog.provider.openai.note": "GPT modeli za brze, sposobne opÅĄte AI zadatke",
"dialog.provider.google.note": "Gemini modeli za brze, strukturirane odgovore",
@@ -229,7 +228,6 @@ export const dict = {
"dialog.provider.viewAll": "PrikaÅži viÅĄe provajdera",
"provider.connect.title": "PoveÅži {{provider}}",
"provider.connect.title.anthropicProMax": "Prijavi se putem Claude Pro/Max",
"provider.connect.selectMethod": "Odaberi način prijave za {{provider}}.",
"provider.connect.method.apiKey": "API ključ",
"provider.connect.method.browser": "Preglednik",
@@ -403,9 +401,7 @@ export const dict = {
"prompt.action.send": "PoÅĄalji",
"prompt.action.stop": "Zaustavi",
"prompt.toast.pasteUnsupported.title": "NepodrÅžan prilog",
"prompt.toast.attachmentDuplicate.title": "Ova datoteka je već učitana",
"prompt.toast.pasteUnsupported.description": "Ovdje se mogu priloÅžiti samo slike, PDF-ovi ili tekstualne datoteke.",
"prompt.toast.modelAgentRequired.title": "Odaberi agenta i model",
"prompt.toast.modelAgentRequired.description": "Odaberi agenta i model prije slanja upita.",
"prompt.toast.worktreeCreateFailed.title": "NeuspjeÅĄno kreiranje worktree-a",
-4
View File
@@ -193,7 +193,6 @@ export const dict = {
"dialog.provider.opencode.note": "Models seleccionats que inclouen Claude, GPT, Gemini i mÊs",
"dialog.provider.opencode.tagline": "Models optimitzats fiables",
"dialog.provider.opencodeGo.tagline": "SubscripciÃŗ de baix cost per a tothom",
"dialog.provider.anthropic.note": "AccÊs directe a Claude models, inclosos Pro i Max",
"dialog.provider.copilot.note": "Models d'IA per a l'assistència de codificaciÃŗ mitjançant GitHub Copilot",
"dialog.provider.openai.note": "GPT models per a tasques d'IA generals ràpides i capaces",
"dialog.provider.google.note": "Gemini models per a respostes ràpides i estructurades",
@@ -210,7 +209,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Consulta mÊs de 70 proveïdors mÊs",
"dialog.provider.viewAll": "Mostra mÊs proveïdors",
"provider.connect.title": "Connecta {{provider}}",
"provider.connect.title.anthropicProMax": "Inicieu sessiÃŗ amb Claude Pro/Max",
"provider.connect.selectMethod": "Seleccioneu el mètode d'inici de sessiÃŗ per a {{provider}}.",
"provider.connect.method.apiKey": "tecla API.",
"provider.connect.method.browser": "Navegador",
@@ -377,8 +375,6 @@ export const dict = {
"prompt.attachment.remove": "Elimina el fitxer adjunt",
"prompt.action.send": "Enviar",
"prompt.action.stop": "Atureu-vos",
"prompt.toast.pasteUnsupported.title": "Fitxer adjunt no compatible",
"prompt.toast.pasteUnsupported.description": "Aquí nomÊs es poden adjuntar imatges, PDFs o fitxers de text.",
"prompt.toast.attachmentDuplicate.title": "Aquest fitxer ja s'ha penjat",
"prompt.toast.modelAgentRequired.title": "Seleccioneu un agent i un model",
"prompt.toast.modelAgentRequired.description": "Trieu un agent i un model abans d'enviar una sol¡licitud.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "VybranÊ modely včetně Claude, GPT, Gemini a dalÅĄÃ­ch",
"dialog.provider.opencode.tagline": "SpolehlivÊ optimalizovanÊ modely",
"dialog.provider.opencodeGo.tagline": "NízkonÃĄkladovÊ předplatnÊ pro kaÅždÊho",
"dialog.provider.anthropic.note": "PřímÃŊ přístup k modelům Claude, včetně modelů Pro a Max",
"dialog.provider.copilot.note": "Modely umělÊ inteligence pro pomoc s kÃŗdovÃĄním prostřednictvím GitHub Copilot",
"dialog.provider.openai.note": "GPT modely pro rychlÊ a schopnÊ obecnÊ Ãēlohy umělÊ inteligence",
"dialog.provider.google.note": "Gemini modely pro rychlÊ, strukturovanÊ odpovědi",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Zobrazit více neÅž 70 dalÅĄÃ­ch poskytovatelů",
"dialog.provider.viewAll": "Zobrazit dalÅĄÃ­ poskytovatele",
"provider.connect.title": "Připojit {{provider}}",
"provider.connect.title.anthropicProMax": "Přihlaste se pomocí Claude Pro/Max",
"provider.connect.selectMethod": "Vyberte způsob přihlÃĄÅĄení pro {{provider}}.",
"provider.connect.method.apiKey": "API klíč",
"provider.connect.method.browser": "ProhlíŞeč",
@@ -375,8 +373,6 @@ export const dict = {
"prompt.attachment.remove": "Odstraňte přílohu",
"prompt.action.send": "Odeslat",
"prompt.action.stop": "Přestaň",
"prompt.toast.pasteUnsupported.title": "NepodporovanÃĄ příloha",
"prompt.toast.pasteUnsupported.description": "Zde lze připojit pouze obrÃĄzky, PDFs nebo textovÊ soubory.",
"prompt.toast.attachmentDuplicate.title": "Tento soubor jiÅž byl nahrÃĄn",
"prompt.toast.modelAgentRequired.title": "Vyberte agenta a model",
"prompt.toast.modelAgentRequired.description": "Před odeslÃĄním vÃŊzvy vyberte zÃĄstupce a model.",
-4
View File
@@ -107,7 +107,6 @@ export const dict = {
"dialog.provider.opencode.note": "Udvalgte modeller inklusive Claude, GPT, Gemini og flere",
"dialog.provider.opencode.tagline": "PÃĨlidelige optimerede modeller",
"dialog.provider.opencodeGo.tagline": "Billigt abonnement for alle",
"dialog.provider.anthropic.note": "Direkte adgang til Claude-modeller, inklusive Pro og Max",
"dialog.provider.copilot.note": "AI-modeller til kodningsassistance via GitHub Copilot",
"dialog.provider.openai.note": "GPT-modeller til hurtige, kompetente generelle AI-opgaver",
"dialog.provider.google.note": "Gemini-modeller til hurtige, strukturerede svar",
@@ -128,7 +127,6 @@ export const dict = {
"dialog.provider.viewAll": "Vis flere udbydere",
"provider.connect.title": "Forbind {{provider}}",
"provider.connect.title.anthropicProMax": "Log ind med Claude Pro/Max",
"provider.connect.selectMethod": "VÃĻlg loginmetode for {{provider}}.",
"provider.connect.method.apiKey": "API-nøgle",
"provider.connect.method.browser": "Browser",
@@ -300,9 +298,7 @@ export const dict = {
"prompt.action.send": "Send",
"prompt.action.stop": "Stop",
"prompt.toast.pasteUnsupported.title": "Ikke understøttet vedhÃĻftning",
"prompt.toast.attachmentDuplicate.title": "Denne fil er allerede uploadet",
"prompt.toast.pasteUnsupported.description": "Kun billeder, PDF'er eller tekstfiler kan vedhÃĻftes her.",
"prompt.toast.modelAgentRequired.title": "VÃĻlg en agent og model",
"prompt.toast.modelAgentRequired.description": "VÃĻlg en agent og model før du sender en forespørgsel.",
"prompt.toast.worktreeCreateFailed.title": "Kunne ikke oprette worktree",
-4
View File
@@ -103,7 +103,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kuratierte Modelle inklusive Claude, GPT, Gemini und mehr",
"dialog.provider.opencode.tagline": "Zuverlässige, optimierte Modelle",
"dialog.provider.opencodeGo.tagline": "KostengÃŧnstiges Abo fÃŧr alle",
"dialog.provider.anthropic.note": "Direkter Zugriff auf Claude-Modelle, einschließlich Pro und Max",
"dialog.provider.copilot.note": "KI-Modelle zur ProgrammierunterstÃŧtzung Ãŧber GitHub Copilot",
"dialog.provider.openai.note": "GPT-Modelle fÃŧr schnelle, leistungsfähige allgemeine KI-Aufgaben",
"dialog.provider.google.note": "Gemini-Modelle fÃŧr schnelle, strukturierte Antworten",
@@ -120,7 +119,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Über 70 weitere Anbieter anzeigen",
"dialog.provider.viewAll": "Mehr Anbieter anzeigen",
"provider.connect.title": "{{provider}} verbinden",
"provider.connect.title.anthropicProMax": "Mit Claude Pro/Max anmelden",
"provider.connect.selectMethod": "Anmeldemethode fÃŧr {{provider}} auswählen.",
"provider.connect.method.apiKey": "API-SchlÃŧssel",
"provider.connect.method.browser": "Browser",
@@ -286,9 +284,7 @@ export const dict = {
"prompt.attachment.remove": "Anhang entfernen",
"prompt.action.send": "Senden",
"prompt.action.stop": "Stoppen",
"prompt.toast.pasteUnsupported.title": "Nicht unterstÃŧtzter Anhang",
"prompt.toast.attachmentDuplicate.title": "Diese Datei wurde bereits hochgeladen",
"prompt.toast.pasteUnsupported.description": "Hier kÃļnnen nur Bilder, PDFs oder Textdateien angehängt werden.",
"prompt.toast.modelAgentRequired.title": "Wählen Sie einen Agenten und ein Modell",
"prompt.toast.modelAgentRequired.description":
"Wählen Sie einen Agenten und ein Modell, bevor Sie eine Eingabe senden.",
-5
View File
@@ -195,7 +195,6 @@ export const dict = {
"Ū†Ū¨Ū‡ŪĒۃŪŦŪ“Ū° Ū†ŪĒۃŪŦۈۍۊŪĻŪ‡Ū¨ŪˆŪ§ Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ†ŪĒŪŽŪŦ یŪŦŪƒŪ­ŪŽŪĻŪ‡Ū¨ Claude، GPT، Gemini Ū‡ŪĻŪ‹Ū¨ Ū‡ŪŦŪ€ŪŦŪ‚Ū¨Ū€ŪŦŪ‚Ū° Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ‡Ū° Ū€Ū¨Ū‰ŪŦŪ‚ŪŦŪ‡ŪŦۈŪŦ",
"dialog.provider.opencode.tagline": "Ū‡Ū¨ŪŒŪĒŪ„Ū§ŪƒŪĒ Ū€ŪĒۃۍ Ū‡ŪŽŪ•Ū°Ū“Ū¨Ū‰ŪĻŪ‡Ū¨Ū’Ū°Ū‘Ū° Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ†ŪŦۈŪŦ",
"dialog.provider.opencodeGo.tagline": "Ū‡ŪŦŪ‚Ū°Ū‰ŪŦŪ‚Ū°Ū‚ŪĻہ۰ Ū†ŪĒŪ‘ŪĻ Ū‡ŪĻŪŽŪŦŪ‡Ū°ŪŽŪĻŪ‡Ū¨ ېŪĻŪ„Ū°ŪŪ°Ū†Ū°ŪƒŪ¨Ū•Ū°ŪŪĻŪ‚Ū° Ū‹Ū¨Ū‚ŪĒŪ‰ŪŦۈŪŦ",
"dialog.provider.anthropic.note": "Ū•Ū°ŪƒŪ¯ Ū‡ŪĻŪ‹Ū¨ Ū‰ŪŦۆ۰ې۰ Ū€Ū¨Ū‰ŪŦŪ‚Ū­ ŪŽŪŽŪŒŪĻہ۰ Claude Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ†ŪĻہ۰ ŪŪŠŪ‹Ū§ Ū‡ŪŦۆ۰ېŪŦې۰ ŪŪ¨Ū„Ū­Ū‚ŪŦ Ū‡ŪŦۈŪŦ",
"dialog.provider.copilot.note": "GitHub Copilot Ū‰ŪŦŪ‹ŪĒۈŪŦŪƒŪ¨Ū†ŪŽŪŪ° Ū†Ū¯Ū‘Ū¨Ū‚Ū°ŪŽ Ū‡ŪŦŪ€ŪŠŪŒŪŦۃۍۈŪĒŪ‰ŪĻہ۰ AI Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ‡Ū°",
"dialog.provider.openai.note": "Ū‡ŪĻۈŪĻŪŪ°ØŒ ŪŽŪ§Ū„Ū¨ŪŪ° Ū‡Ū§Ū‚Ū°Ū‰ŪĒ AI Ū“Ū§ŪŪ°Ū†Ū°ŪŒŪĻŪ†ŪĻہ۰ GPT Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ†ŪŦۈŪŦ",
"dialog.provider.google.note": "Ū‡ŪĻۈŪĻŪŪ°ØŒ ŪŪ°Ū“Ū°ŪƒŪĻŪ†Ū°Ū—ŪĻŪƒŪ‘Ū° ۃŪŦŪŪ°Ū•ŪŽŪ‚Ū°ŪŪ°ŪŒŪĻŪ†ŪĻہ۰ Gemini Ū‰ŪŽŪ‘ŪŦŪŪ°ŪŒŪĻŪ†ŪŦۈŪŦ",
@@ -212,7 +211,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Ū‡Ū¨ŪŒŪĒۃŪĒ 70+ Ū•Ū°ŪƒŪŽŪˆŪĻŪ‡Ū¨Ū‘ŪĻۃŪĒŪ‚Ū° Ū„ŪĻŪ‡Ū°ŪŪĻۈۧہŪŦۈŪŦ",
"dialog.provider.viewAll": "Ū‡Ū¨ŪŒŪĒۃŪĒ Ū•Ū°ŪƒŪŽŪˆŪĻŪ‡Ū¨Ū‘ŪĻۃŪĒŪ‚Ū° Ū‹ŪĻۇ۰ۆۧہŪŦۈŪŦ",
"provider.connect.title": "{{provider}} ŪŽŪĒŪ…ŪĒۈۧہŪŦۈŪŦ",
"provider.connect.title.anthropicProMax": "Claude Ū•Ū°ŪƒŪ¯/Ū‰ŪŦۆ۰ې۰ Ū‡Ū¨Ū‚Ū° ŪŪŽŪŽŪ¨Ū‚Ū° ŪˆŪ§Ū‚ŪŦŪ‡ŪŦۈŪŦ",
"provider.connect.selectMethod": "{{provider}} Ū‡ŪĻہ۰ ŪŪŽŪŽŪ¨Ū‚Ū° Ū‰ŪŦŪŒŪ¯Ū‘Ū° Ū€ŪŽŪˆŪ§ŪŪŦۈŪŦ.",
"provider.connect.method.apiKey": "API Ū†ŪŠ Ū‡ŪŦۈŪŦ",
"provider.connect.method.browser": "Ū„Ū°ŪƒŪĻŪ‡ŪĒŪ’Ū§ Ū‡ŪŦۈŪŦ",
@@ -380,9 +378,6 @@ export const dict = {
"prompt.attachment.remove": "Ū‡ŪŦŪ“Ū­Ū—Ū°Ū‰ŪĻŪ‚Ū°Ū“Ū° Ū‚ŪĻŪŽŪ§ŪŪŦۈŪŦ",
"prompt.action.send": "ŪŠŪŽŪ‚ŪĒۈŪĒŪ‚Ū°",
"prompt.action.stop": "Ū€ŪĒŪ‡Ū°Ū“ŪĒŪ‚Ū°",
"prompt.toast.pasteUnsupported.title": "ېŪĻŪ•Ū¯Ū“Ū° Ū‚ŪĒŪ†ŪĒۃۧ Ū‡ŪŦŪ“Ū­Ū—Ū°Ū‰ŪĻŪ‚Ū°Ū“ŪŦۈŪŦ",
"prompt.toast.pasteUnsupported.description":
"Ū‰Ū¨ŪŒŪĻŪ‚ŪĒŪŽŪĻŪ‡Ū¨ Ū‡ŪŦŪ“Ū­Ū—Ū° Ū†ŪĒۃŪŦŪˆŪ­Ū‚ŪŠ Ū€ŪĻŪ‰ŪĻŪ‡ŪŦŪ†ŪĻŪ‚Ū¨ یŪĻŪŪ°ŪˆŪŠŪƒŪĒØŒ PDFs، Ū‚ŪĒۈŪĻیŪĻ Ū“ŪŦۆ۰ې۰ۓ۰ ۊŪĻŪ‡Ū¨ŪŪ°ŪŒŪĻŪ†ŪŦۈŪŦ.",
"prompt.toast.attachmentDuplicate.title": "Ū‰Ū¨ ۊŪĻŪ‡Ū¨ŪŪ° Ū‰Ū¨Ū€Ū§ŪƒŪĒ ŪˆŪĻŪ‚ŪŠ Ū‡ŪĻŪ•Ū°ŪŪ¯Ū‘Ū°Ū†ŪŽŪŪ°ŪŠŪĻŪ‡ŪŦۈŪŦ",
"prompt.toast.modelAgentRequired.title": "Ū‡Ū­Ū–ŪŦŪ‚Ū°Ū“ŪĻŪ†Ū§Ū‡Ū¨ Ū‰ŪŽŪ‘ŪŦŪŪ°Ū‡ŪŦŪ‡Ū° Ū€ŪŽŪˆŪĒŪ‚Ū°",
"prompt.toast.modelAgentRequired.description": "Ū•Ū°ŪƒŪŽŪ‰Ū°Ū•Ū°Ū“ŪŦŪ‡Ū° ŪŠŪŽŪ‚ŪĒۈŪĒŪ‰ŪĒŪŽŪŦ Ū†ŪĒŪƒŪ¨Ū‚Ū° Ū‡Ū­Ū–ŪŦŪ‚Ū°Ū“ŪĻŪ†Ū§Ū‡Ū¨ Ū‰ŪŽŪ‘ŪŦŪŪ°Ū‡ŪŦŪ‡Ū° Ū€ŪŽŪˆŪĒŪ‚Ū°.",
-5
View File
@@ -194,7 +194,6 @@ export const dict: Record<string, string> = {
"dialog.provider.opencode.note": "Claude āŊ‘āŊ„āŧ‹ GPT āŊ‘āŊēāŧ‹āŊŖāŊĻāŧ‹ Gemini āŊ‘āŊ„āŧ‹āŊ‘āŊēāŧ‹āŊŖāŊĻāŧ‹āŊŖāžˇāŊ‚āŧ‹āŊĻāžŸāŊēāŧ‹ āŊ–āŊ€āŊŧāŊ‘āŧ‹āŊĻāž’āž˛āŊ˛āŊ‚āŧ‹āŊ āŊ–āŊ‘āŧ‹āŊĄāŊŧāŊ‘āŧ‹āŊ”āŊ āŊ˛āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ",
"dialog.provider.opencode.tagline": "āŊ–āžŗāŊŧāŧ‹āŊ‚āŊāŊ‘āŧ‹āŊ…āŊ“āŧ‹āŊ‚āžąāŊ˛āŧ‹āŊĄāŊĸāŧ‹āŊĸāž’āžąāŊĻāŧ‹āŊ…āŊ“āŧ‹āŊ‚āžąāŊ˛āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ‹āŊšāŊ´āŧ",
"dialog.provider.opencodeGo.tagline": "āŊ˜āŊ˛āŧ‹āŊšāŊ„āŧ‹āŊ˜āŊ āŊ˛āŧ‹āŊ‘āŊŧāŊ“āŧ‹āŊŖāŊ´āŧ‹ āŊ‚āŊŧāŊ„āŧ‹āŊšāŊ‘āŧ‹āŊ‘āŊ˜āŊ āŧ‹āŊ–āŊ āŊ˛āŧ‹ āŊ˜āŊāŊŧāŧ‹āŊ˜āŊ„āŊ‚āŊĻāŧ‹āŊ āŊ–āŊ‘āŧ‹āŊ“āŊ˛āŧ",
"dialog.provider.anthropic.note": "āŊ”āž˛āŊŧāŧ‹āŊ‘āŊ„āŧ‹āŊ˜āŊēāŊ‚āŊĻāŊ˛āŧ‹āŊšāŊ´āŧ‹āŊĸāžŠāŊ˛āŊĻāŧ‹āŊāŊēāŧ‹ ClaudeāŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ‹āŊšāŊ´āŧ‹āŊŖāŊ´āŧ‹āŊāŊ‘āŧ‹āŊ€āŊĸāŧ‹āŊ‘āŊ´āŧ‹āŊ āŊ›āŊ´āŊŖāŧ‹āŊĻāž¤āžąāŊŧāŊ‘āŧ",
"dialog.provider.copilot.note": "GitHub Copilot āŊ–āŊĸāž’āžąāŊ´āŊ‘āŧ‹āŊ‘āŊēāŧ‹ āŊ€āŊŧāŧ‹āŊŒāŊ˛āŊ„āŧ‹āŊ‚āž˛āŊŧāŊ‚āŊĻāŧ‹āŊĸāŊ˜āŧ‹āŊ‚āžąāŊ˛āŧ‹āŊ‘āŊŧāŊ“āŧ‹āŊŖāŊ´āŧ‹ AI āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ",
"dialog.provider.openai.note": "GPT āŊ˜āŊ‚āžąāŊŧāŊ‚āŊĻāŧ‹āŊ‘āž˛āŊ‚āŊĻāŧ‹āŊ‘āŊ„āŧ‹āŊŖāž•āŊŧāŊ‚āŊĻāŧ‹āŊ‚āž˛āŊ´āŊ–āŧ‹āŊ…āŊ“āŧ‹āŊ‚āžąāŊ˛āŧ‹āŊĻāž¤āžąāŊ˛āŊĸāŧ‹āŊ–āŊāŊ„āŧ‹āŊ¨āŊēāŧ‹āŊ¨āŊ āŊ˛āŧ‹āŊŖāŊĻāŧ‹āŊ āŊ‚āŊ“āŧ‹āŊšāŊ´āŧ‹āŊ‚āŊ˛āŧ‹āŊ‘āŊŧāŊ“āŧ‹āŊŖāŊ´āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ",
"dialog.provider.google.note": "Gemini āŊ˜āŊ‚āžąāŊŧāŊ‚āŊĻāŧ‹āŊ‘āž˛āŊ‚āŊĻāŧ‹āŊ‘āŊ„āŧ‹āŊĻāž’āž˛āŊ˛āŊ‚āŧ‹āŊ–āŊ€āŊŧāŊ‘āŧ‹āŊ…āŊ“āŧ‹āŊ‚āžąāŊ˛āŧ‹āŊŖāŊ“āŧ‹āŊ āŊ‘āŊēāŊ–āŊĻāŧ‹āŊ€āžąāŊ˛āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ",
@@ -212,7 +211,6 @@ export const dict: Record<string, string> = {
"dialog.model.unpaid.viewMoreProviders": "70+ āŊ˜āŊāŊŧāŧ‹āŊĻāž¤āž˛āŊŧāŊ‘āŧ‹āŊ āŊ–āŊ‘āŧ‹āŊ˜āŊ˛āŧ‹āŊšāŊ´āŧ‹āŊ–āŊŖāžŸāŧ",
"dialog.provider.viewAll": "āŊ–āžąāŊ˛āŊ“āŧ‹āŊ˜āŊ˛āŧ‹āŊ˜āŊ„āŊ˜āŧ‹āŊĻāžŸāŊŧāŊ“āŧ",
"provider.connect.title": "{{provider}}āŊ˜āŊāŊ´āŊ‘āŧ",
"provider.connect.title.anthropicProMax": "Claude Pro/Max āŊ‘āŊ„āŧ‹āŊ…āŊ˛āŊ‚āŧ‹āŊāŊĸāŧ‹āŊ“āŊ„āŧ‹āŊ–āŊĻāžāžąāŊŧāŊ“āŧ",
"provider.connect.selectMethod": "{{provider}}āŊ‚āŊ˛āŧ‹āŊ‘āŊŧāŊ“āŧ‹āŊŖāŊ´āŧ‹ āŊ“āŊ„āŧ‹āŊ–āŊĻāžāžąāŊŧāŊ‘āŧ‹āŊāŊ–āŊĻāŧ‹āŊŖāŊ˜āŧ‹āŊĻāŊēāŊŖāŧ‹āŊ āŊāŊ´āŧ‹āŊ āŊ–āŊ‘āŧ",
"provider.connect.method.apiKey": "APIāŊŖāžĄāŊēāŧ‹āŊ˜āŊ˛āŊ‚āŧ",
"provider.connect.method.browser": "āŊ–āŊĸāŊ āŊ´āŧ‹āŊŸāŊĸāŧ",
@@ -379,9 +377,6 @@ export const dict: Record<string, string> = {
"prompt.attachment.remove": "āŊ˜āŊ‰āŊ˜āŧ‹āŊĻāžĻāž˛āŊ‚āŊĻāŧ‹āŊĸāžŠāŧ‹āŊ–āŊĻāžāž˛āŊ‘āŧ‹āŊ‚āŊāŊ„āŧ‹āŧ",
"prompt.action.send": "āŊ–āŊāŊ„āŧŒāŊ“āŊ˛",
"prompt.action.stop": "āŊ–āŊ€āŊ‚āŧ‹āŊ”",
"prompt.toast.pasteUnsupported.title": "āŊĸāž’āžąāŊ–āŧ‹āŊĻāžāžąāŊŧāŊĸāŧ‹āŊ˜āŊēāŊ‘āŧ‹āŊ”āŊ āŊ˛āŧ‹āŊ˜āŊ‰āŊ˜āŧ‹āŊĻāžĻāž˛āŊ‚āŊĻāŧ",
"prompt.toast.pasteUnsupported.description":
"āŊ”āŊĸāŧ‹āŊĸāŊ˛āŊĻāŧ‹āŊ‘āŊ„āŧ‹āŊ”āŊ˛āŧ‹āŊŒāŊ˛āŧ‹āŊ¨āŊēāŊ•āŧ‹ āŊĄāŊ„āŧ‹āŊ“āŧ‹ āŊšāŊ˛āŊ‚āŧ‹āŊĄāŊ˛āŊ‚āŧ‹āŊĄāŊ˛āŊ‚āŧ‹āŊĻāžŖāŊŧāŊ‘āŧ‹āŊšāŊ´āŧ‹āŊĸāžāžąāŊ„āŊ˜āŧ‹āŊ…āŊ˛āŊ‚āŧ‹ āŊ“āŊąāŧ‹āŊŖāŊ´āŧ‹āŊ˜āŊ‰āŊ˜āŧ‹āŊĻāžĻāž˛āŊ‚āŊĻāŧ‹āŊ āŊ–āŊ‘āŧ‹āŊ–āŊāŊ´āŊ–āŧ",
"prompt.toast.attachmentDuplicate.title": "āŊĄāŊ˛āŊ‚āŧ‹āŊĻāžŖāŊŧāŊ‘āŧ‹āŊ āŊ‘āŊ˛āŧ‹āŊ§āŊēāŧ‹āŊ˜āŧ‹āŊŖāŊĻāŧ‹āŊĻāžāžąāŊēāŊŖāŧ‹āŊ–āŊ™āŊ´āŊ‚āŊĻāŧ‹āŊ āŊ–āŊ‘āŧ‹āŊĄāŊ˛āŧ",
"prompt.toast.modelAgentRequired.title": "āŊŖāŊĻāŧ‹āŊšāŊ–āŧ‹āŊ‘āŊ„āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ‹āŊ…āŊ˛āŊ‚āŧ‹āŊĻāŊēāŊŖāŧ‹āŊ āŊāŊ´āŧ‹āŊ āŊ–āŊ‘āŧ",
"prompt.toast.modelAgentRequired.description": "āŊ–āŊĸāžĄāŧ‹āŊĻāžŸāŊŧāŊ“āŧ‹āŊ˜āŧ‹āŊ‚āŊāŊ„āŧ‹āŊ”āŊ āŊ˛āŧ‹āŊ§āŊēāŧ‹āŊ˜āŧ‹ āŊŖāŊĻāŧ‹āŊšāŊ–āŧ‹āŊ‘āŊ„āŧ‹āŊ‘āŊ”āŊēāŧ‹āŊšāŊ‘āŧ‹āŊ‚āŊ‘āŊ˜āŧ‹āŊāŧ‹āŊĸāžāžąāŊ–āŊĻāŧ",
-4
View File
@@ -192,7 +192,6 @@ export const dict = {
"dialog.provider.opencode.note": "Î•Ī€ÎšÎŧÎĩÎģΡÎŧέÎŊÎą ÎŧÎŋÎŊĪ„Î­ÎģÎą ĪƒĪ…ÎŧĪ€ÎĩĪÎšÎģÎąÎŧβιÎŊÎŋÎŧέÎŊΉÎŊ ΄ΉÎŊ Claude, GPT, Gemini ÎēιΚ ÎŦÎģÎģΉÎŊ",
"dialog.provider.opencode.tagline": "Î‘ÎžÎšĪŒĪ€ÎšĪƒĪ„Îą βÎĩÎģĪ„ÎšĪƒĪ„ÎŋĪ€ÎŋΚΡÎŧέÎŊÎą ÎŧÎŋÎŊĪ„Î­ÎģÎą",
"dialog.provider.opencodeGo.tagline": "ÎŖĪ…ÎŊÎ´ĪÎŋÎŧÎŽ Ī‡ÎąÎŧΡÎģÎŋĪ ÎēĪŒĪƒĪ„ÎŋĪ…Ī‚ ÎŗÎšÎą ΌÎģÎŋĪ…Ī‚",
"dialog.provider.anthropic.note": "ΆÎŧÎĩĪƒÎˇ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ΃Îĩ Claude ÎŧÎŋÎŊĪ„Î­ÎģÎą, ĪƒĪ…ÎŧĪ€ÎĩĪÎšÎģÎąÎŧβιÎŊÎŋÎŧέÎŊΉÎŊ ΄ΉÎŊ Pro ÎēιΚ Max",
"dialog.provider.copilot.note": "ΜÎŋÎŊĪ„Î­ÎģÎą AI ÎŗÎšÎą βÎŋΎθÎĩΚι ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚ ÎŧÎ­ĪƒĪ‰ GitHub Copilot",
"dialog.provider.openai.note": "GPT ÎŧÎŋÎŊĪ„Î­ÎģÎą ÎŗÎšÎą ÎŗĪÎŽÎŗÎŋ΁ÎĩĪ‚, ΚÎēÎąÎŊÎ­Ī‚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎŗÎĩÎŊΚÎēÎŽĪ‚ Ī„Îĩ·ÎŊÎˇĪ„ÎŽĪ‚ ÎŊÎŋΡÎŧÎŋĪƒĪÎŊÎˇĪ‚",
"dialog.provider.google.note": "Gemini ÎŧÎŋÎŊĪ„Î­ÎģÎą ÎŗÎšÎą ÎŗĪÎŽÎŗÎŋ΁ÎĩĪ‚, δÎŋÎŧΡÎŧέÎŊÎĩĪ‚ ÎąĪ€ÎąÎŊĪ„ÎŽĪƒÎĩÎšĪ‚",
@@ -209,7 +208,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ΔÎĩÎ¯Ī„Îĩ 70+ ÎąÎēΌÎŧΡ Ī€ÎąĪĪŒĪ‡ÎŋĪ…Ī‚",
"dialog.provider.viewAll": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ΉÎŊ Ī€ÎąĪĪŒĪ‡Ī‰ÎŊ",
"provider.connect.title": "ÎŖĪÎŊδÎĩĪƒÎˇ {{provider}}",
"provider.connect.title.anthropicProMax": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ Claude Pro/Max",
"provider.connect.selectMethod": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ÎŧÎĩÎ¸ĪŒÎ´ÎŋĪ… ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ÎŗÎšÎą {{provider}}.",
"provider.connect.method.apiKey": "API ÎēÎģÎĩΚδί",
"provider.connect.method.browser": "Î ĪĪŒÎŗĪÎąÎŧÎŧÎą Ī€ÎĩĪÎšÎŽÎŗÎˇĪƒÎˇĪ‚",
@@ -376,8 +374,6 @@ export const dict = {
"prompt.attachment.remove": "ÎšÎąĪ„ÎŦĪÎŗÎˇĪƒÎˇ ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎŋĪ…",
"prompt.action.send": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎģÎŽ",
"prompt.action.stop": "ΔιαÎēÎŋĪ€ÎŽ",
"prompt.toast.pasteUnsupported.title": "Μη Ī…Ī€ÎŋĪƒĪ„ÎˇĪÎšÎļΌÎŧÎĩÎŊÎŋ ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎŋ",
"prompt.toast.pasteUnsupported.description": "Î•Î´ĪŽ ÎĩĪ€ÎšĪƒĪ…ÎŊÎŦ΀΄ÎŋÎŊĪ„ÎąÎš ÎŧΌÎŊÎŋ ÎĩΚÎēΌÎŊÎĩĪ‚, ÎąĪĪ‡ÎĩÎ¯Îą PDF ÎŽ ÎąĪĪ‡ÎĩÎ¯Îą ÎēÎĩΚÎŧέÎŊÎŋĪ….",
"prompt.toast.attachmentDuplicate.title": "Î‘Ī…Ī„ĪŒ Ī„Îŋ ÎąĪĪ‡ÎĩίÎŋ Î­Ī‡ÎĩΚ ΎδΡ ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸Îĩί",
"prompt.toast.modelAgentRequired.title": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ έÎŊÎąÎŊ ΀΁ÎŦÎēĪ„ÎŋĪÎą ÎēιΚ ÎŧÎŋÎŊĪ„Î­ÎģÎŋ",
"prompt.toast.modelAgentRequired.description":
+15 -4
View File
@@ -74,6 +74,7 @@ export const dict = {
"command.category.workspace": "Worktree",
"command.category.settings": "Settings",
"command.logs.export": "Export logs",
"command.debugBar.toggle": "Toggle debug bar",
"theme.scheme.system": "System",
"theme.scheme.light": "Light",
@@ -170,7 +171,7 @@ export const dict = {
"dialog.provider.opencode.note": "Curated models including Claude, GPT, Gemini and more",
"dialog.provider.opencode.tagline": "Reliable optimized models",
"dialog.provider.opencodeGo.tagline": "Low cost subscription for everyone",
"dialog.provider.anthropic.note": "Direct access to Claude models, including Pro and Max",
"dialog.provider.anthropic.note": "Direct access to Claude models via API key",
"dialog.provider.copilot.note": "AI models for coding assistance via GitHub Copilot",
"dialog.provider.openai.note": "GPT models for fast, capable general AI tasks",
"dialog.provider.google.note": "Gemini models for fast, structured responses",
@@ -191,7 +192,7 @@ export const dict = {
"dialog.provider.viewAll": "Show more providers",
"provider.connect.title": "Connect {{provider}}",
"provider.connect.title.anthropicProMax": "Login with Claude Pro/Max",
"provider.connect.title.anthropicProMax": "Login with Anthropic",
"provider.connect.selectMethod": "Select login method for {{provider}}.",
"provider.connect.method.apiKey": "API key",
"provider.connect.method.browser": "Browser",
@@ -365,9 +366,10 @@ export const dict = {
"prompt.action.send": "Send",
"prompt.action.stop": "Stop",
"prompt.toast.pasteUnsupported.title": "Unsupported attachment",
"prompt.toast.pasteUnsupported.description": "Only images, PDFs, or text files can be attached here.",
"prompt.toast.attachmentDuplicate.title": "This file has already been uploaded",
"prompt.toast.uploading.percent": "{{percent}}%",
"prompt.toast.uploading.cancel": "Cancel upload",
"prompt.toast.uploadFailed.title": "Upload failed",
"prompt.toast.modelAgentRequired.title": "Select an agent and model",
"prompt.toast.modelAgentRequired.description": "Choose an agent and model before sending a prompt.",
"prompt.toast.worktreeCreateFailed.title": "Failed to create worktree",
@@ -965,7 +967,16 @@ export const dict = {
"sidebar.empty.description": "Open a project to get started",
"debugBar.ariaLabel": "Development performance diagnostics",
"debugBar.providerAriaLabel": "Provider performance diagnostics",
"debugBar.na": "n/a",
"debugBar.ttft.label": "TTFT",
"debugBar.ttft.tip": "Time from provider request dispatch to the first model output.",
"debugBar.ttfa.label": "TTFA",
"debugBar.ttfa.tip": "Time from provider request dispatch to the first answer text.",
"debugBar.tps.label": "TPS",
"debugBar.tps.tip": "Output tokens per second after the first model output.",
"debugBar.e2e.label": "E2E",
"debugBar.e2e.tip": "Time from provider request dispatch until its response stream ended.",
"debugBar.nav.label": "NAV",
"debugBar.nav.tip":
"Last completed route transition touching a session page, measured from router start until the first paint after it settles.",
-5
View File
@@ -208,7 +208,6 @@ export const dict = {
"dialog.provider.opencode.note": "SelecciÃŗn de modelos como Claude, GPT, Gemini y otros",
"dialog.provider.opencode.tagline": "Modelos optimizados y fiables",
"dialog.provider.opencodeGo.tagline": "SuscripciÃŗn econÃŗmica para todos",
"dialog.provider.anthropic.note": "Acceso directo a modelos Claude, incluidos Pro y Max",
"dialog.provider.copilot.note": "Modelos de IA para asistencia de codificaciÃŗn a travÊs de GitHub Copilot",
"dialog.provider.openai.note": "Modelos GPT para tareas de IA generales rÃĄpidas y capaces",
"dialog.provider.google.note": "Modelos Gemini para respuestas rÃĄpidas y estructuradas",
@@ -229,7 +228,6 @@ export const dict = {
"dialog.provider.viewAll": "Ver mÃĄs proveedores",
"provider.connect.title": "Conectar {{provider}}",
"provider.connect.title.anthropicProMax": "Iniciar sesiÃŗn con Claude Pro/Max",
"provider.connect.selectMethod": "Seleccionar mÊtodo de inicio de sesiÃŗn para {{provider}}.",
"provider.connect.method.apiKey": "Clave API",
"provider.connect.method.browser": "Navegador",
@@ -403,10 +401,7 @@ export const dict = {
"prompt.action.send": "Enviar",
"prompt.action.stop": "Detener",
"prompt.toast.pasteUnsupported.title": "Adjunto no compatible",
"prompt.toast.attachmentDuplicate.title": "Este archivo ya se ha subido",
"prompt.toast.pasteUnsupported.description":
"Aquí solo se pueden adjuntar imÃĄgenes, archivos PDF o archivos de texto.",
"prompt.toast.modelAgentRequired.title": "Selecciona un agente y modelo",
"prompt.toast.modelAgentRequired.description": "Elige un agente y modelo antes de enviar un prompt.",
"prompt.toast.worktreeCreateFailed.title": "Fallo al crear el ÃĄrbol de trabajo",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kureeritud mudelid, sealhulgas Claude, GPT, Gemini ja palju muud",
"dialog.provider.opencode.tagline": "Usaldusväärsed optimeeritud mudelid",
"dialog.provider.opencodeGo.tagline": "Madala hinnaga tellimus kÃĩigile",
"dialog.provider.anthropic.note": "Otsene juurdepääs Claude mudelile, sealhulgas Pro ja Max",
"dialog.provider.copilot.note": "AI mudelid kodeerimisabiks GitHub Copilot kaudu",
"dialog.provider.openai.note": "GPT mudelid kiirete ja vÃĩimekate Ãŧldiste AI-Ãŧlesannete jaoks",
"dialog.provider.google.note": "Gemini mudelid kiirete ja struktureeritud vastuste jaoks",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Vaadake veel 70+ pakkujat",
"dialog.provider.viewAll": "Kuva rohkem teenusepakkujaid",
"provider.connect.title": "Ühenda {{provider}}",
"provider.connect.title.anthropicProMax": "Logi sisse Claude Pro/Max",
"provider.connect.selectMethod": "Valige {{provider}} jaoks sisselogimisviis.",
"provider.connect.method.apiKey": "API klahv",
"provider.connect.method.browser": "Brauser",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "Eemalda manus",
"prompt.action.send": "Saada",
"prompt.action.stop": "Peatus",
"prompt.toast.pasteUnsupported.title": "Toetamata manus",
"prompt.toast.pasteUnsupported.description": "Siia saab lisada ainult pilte, PDFs vÃĩi tekstifaile.",
"prompt.toast.attachmentDuplicate.title": "See fail on juba Ãŧles laaditud",
"prompt.toast.modelAgentRequired.title": "Valige agent ja mudel",
"prompt.toast.modelAgentRequired.description": "Enne viipa saatmist valige agent ja mudel.",
-4
View File
@@ -192,7 +192,6 @@ export const dict = {
"dialog.provider.opencode.note": "Ų…Ø¯Ų„ Ų‡Ø§ÛŒ Ø§Ų†ØĒ؎اب Ø´Ø¯Ų‡ Ø§Ø˛ ØŦŲ…Ų„Ų‡ Claude، GPT، Gemini ؈ Ų…ŲˆØ§ØąØ¯ Ø¯ÛŒÚ¯Øą",
"dialog.provider.opencode.tagline": "Ų…Ø¯Ų„ Ų‡Ø§ÛŒ Ø¨Ų‡ÛŒŲ†Ų‡ Ø´Ø¯Ų‡ Ų‚Ø§Ø¨Ų„ اؚØĒŲ…Ø§Ø¯",
"dialog.provider.opencodeGo.tagline": "اشØĒØąØ§ÚŠ ÚŠŲ… Ų‡Ø˛ÛŒŲ†Ų‡ Ø¨ØąØ§ÛŒ Ų‡Ų…Ų‡",
"dialog.provider.anthropic.note": "Ø¯ØŗØĒØąØŗÛŒ Ų…ØŗØĒŲ‚ÛŒŲ… Ø¨Ų‡ Ų…Ø¯Ų„ Ų‡Ø§ÛŒ Claude، Ø§Ø˛ ØŦŲ…Ų„Ų‡ Pro ؈ Max",
"dialog.provider.copilot.note": "Ų…Ø¯Ų„ Ų‡Ø§ÛŒ AI Ø¨ØąØ§ÛŒ ÚŠŲ…ÚŠ Ø¨Ų‡ ÚŠØ¯Ų†ŲˆÛŒØŗÛŒ Ø§Ø˛ ØˇØąÛŒŲ‚ GitHub Copilot",
"dialog.provider.openai.note": "Ų…Ø¯Ų„ Ų‡Ø§ÛŒ GPT Ø¨ØąØ§ÛŒ ÚŠØ§ØąŲ‡Ø§ÛŒ ØšŲ…ŲˆŲ…ÛŒ ØŗØąÛŒØš ؈ با Ų‚Ø§Ø¨Ų„ÛŒØĒ AI",
"dialog.provider.google.note": "Ų…Ø¯Ų„ Ų‡Ø§ÛŒ Gemini Ø¨ØąØ§ÛŒ ŲžØ§ØŗØŽ Ų‡Ø§ÛŒ ØŗØąÛŒØš ؈ ØŗØ§ØŽØĒØ§ØąÛŒØ§ŲØĒŲ‡",
@@ -209,7 +208,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "70+ Ø§ØąØ§ØĻŲ‡â€ŒØ¯Ų‡Ų†Ø¯Ų‡ Ø¯ÛŒÚ¯Øą ØąØ§ Ø¨Ø¨ÛŒŲ†ÛŒØ¯",
"dialog.provider.viewAll": "Ų†Ų…Ø§ÛŒØ´ Ø§ØąØ§ØĻŲ‡ Ø¯Ų‡Ų†Ø¯Ú¯Ø§Ų† بیشØĒØą",
"provider.connect.title": "{{provider}} ØąØ§ ؈ØĩŲ„ ÚŠŲ†ÛŒØ¯",
"provider.connect.title.anthropicProMax": "با Claude Pro/Max ŲˆØ§ØąØ¯ Ø´ŲˆÛŒØ¯",
"provider.connect.selectMethod": "ØąŲˆØ´ ŲˆØąŲˆØ¯ Ø¨ØąØ§ÛŒ {{provider}} ØąØ§ Ø§Ų†ØĒ؎اب ÚŠŲ†ÛŒØ¯.",
"provider.connect.method.apiKey": "ÚŠŲ„ÛŒØ¯ API",
"provider.connect.method.browser": "Ų…ØąŲˆØąÚ¯Øą",
@@ -375,8 +373,6 @@ export const dict = {
"prompt.attachment.remove": "Ø­Ø°Ų ŲžÛŒŲˆØŗØĒ",
"prompt.action.send": "Ø§ØąØŗØ§Ų„ ÚŠŲ†ÛŒØ¯",
"prompt.action.stop": "ØĒŲˆŲ‚Ų ÚŠŲ†ÛŒØ¯",
"prompt.toast.pasteUnsupported.title": "ŲžÛŒŲˆØŗØĒ ŲžØ´ØĒÛŒØ¨Ø§Ų†ÛŒ Ų†Ø´Ø¯Ų‡ Ø§ØŗØĒ",
"prompt.toast.pasteUnsupported.description": "ŲŲ‚Øˇ ØĒØĩØ§ŲˆÛŒØąØŒ PDFs، یا ŲØ§ÛŒŲ„ Ų‡Ø§ÛŒ Ų…ØĒŲ†ÛŒ ØąØ§ Ų…ÛŒ ØĒŲˆØ§Ų† Ø¯Øą Ø§ÛŒŲ†ØŦا ŲžÛŒŲˆØŗØĒ ÚŠØąØ¯.",
"prompt.toast.attachmentDuplicate.title": "Ø§ÛŒŲ† ŲØ§ÛŒŲ„ Ų‚Ø¨Ų„Ø§ ØĸŲžŲ„ŲˆØ¯ Ø´Ø¯Ų‡ Ø§ØŗØĒ",
"prompt.toast.modelAgentRequired.title": "یڊ ØšØ§Ų…Ų„ ؈ Ų…Ø¯Ų„ ØąØ§ Ø§Ų†ØĒ؎اب ÚŠŲ†ÛŒØ¯",
"prompt.toast.modelAgentRequired.description": "Ų‚Ø¨Ų„ Ø§Ø˛ Ø§ØąØŗØ§Ų„ Ø¯ØąØŽŲˆØ§ØŗØĒ، یڊ ØšØ§Ų…Ų„ ؈ Ų…Ø¯Ų„ ØąØ§ Ø§Ų†ØĒ؎اب ÚŠŲ†ÛŒØ¯.",
-4
View File
@@ -99,7 +99,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kuratoituja malleja, kuten Claude, GPT, Gemini ja muita",
"dialog.provider.opencode.tagline": "Luotettavat optimoidut mallit",
"dialog.provider.opencodeGo.tagline": "Edullinen tilaus kaikille",
"dialog.provider.anthropic.note": "Suora pääsy Claude-malleihin, mukaan lukien Pro ja Max",
"dialog.provider.copilot.note": "AI-malleja koodausapuun GitHub Copilotin kautta",
"dialog.provider.openai.note": "GPT-mallit nopeisiin, suorituskykyisiin yleisiin tekoälytehtäviin",
"dialog.provider.google.note": "Gemini-mallit nopeisiin, jäsenneltyihin vastauksiin",
@@ -116,7 +115,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Katso yli 70 muuta palveluntarjoajaa",
"dialog.provider.viewAll": "Näytä lisää palveluntarjoajia",
"provider.connect.title": "Yhdistä {{provider}}",
"provider.connect.title.anthropicProMax": "Kirjaudu sisään Claude Pro/Maxilla",
"provider.connect.selectMethod": "Valitse kirjautumistapa palvelulle {{provider}}.",
"provider.connect.method.apiKey": "API-avain",
"provider.connect.method.browser": "Selain",
@@ -282,8 +280,6 @@ export const dict = {
"prompt.attachment.remove": "Poista liite",
"prompt.action.send": "Lähetä",
"prompt.action.stop": "Pysäytä",
"prompt.toast.pasteUnsupported.title": "Liitettä ei tueta",
"prompt.toast.pasteUnsupported.description": "Vain kuvia, PDF-tiedostoja tai tekstitiedostoja voi liittää tähän.",
"prompt.toast.attachmentDuplicate.title": "Tämä tiedosto on jo ladattu",
"prompt.toast.modelAgentRequired.title": "Valitse agentti ja malli",
"prompt.toast.modelAgentRequired.description": "Valitse agentti ja malli ennen kehotteen lähettämistä.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kuraterað modell íroknað Claude, GPT, Gemini og fleiri",
"dialog.provider.opencode.tagline": "Álítandi optimerað modell",
"dialog.provider.opencodeGo.tagline": "LÃĄgkostnaðarhald fyri øll",
"dialog.provider.anthropic.note": "Beinleiðis atgongd til Claude modellir, eisini Pro og Max .",
"dialog.provider.copilot.note": "AI modellir til koduhjÃĄlp umvegis GitHub Copilot",
"dialog.provider.openai.note": "GPT modellir til skjÃŗtar, førar almennar AI uppgÃĄvur",
"dialog.provider.google.note": "Gemini modellir til skjÃŗt, strukturerað svar",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Sí 70+ fleiri veitarar",
"dialog.provider.viewAll": "Vís fleiri veitarar",
"provider.connect.title": "Samband {{provider}}",
"provider.connect.title.anthropicProMax": "Rita inn við Claude Pro/Max",
"provider.connect.selectMethod": "Vel innritanarhÃĄtt til {{provider}}.",
"provider.connect.method.apiKey": "API lykil",
"provider.connect.method.browser": "Kagari",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "Strika viðheftið",
"prompt.action.send": "Send",
"prompt.action.stop": "Steðga",
"prompt.toast.pasteUnsupported.title": "Óstuðlað viðhefti",
"prompt.toast.pasteUnsupported.description": "Bert myndir, PDFs, ella tekstfílur kunnu viðheftast her.",
"prompt.toast.attachmentDuplicate.title": "Hendan fílan er longu løgd upp.",
"prompt.toast.modelAgentRequired.title": "Vel agent og modell",
"prompt.toast.modelAgentRequired.description": "Vel agent og modell, ÃĄÃ°renn tÃē sendir ein prompt.",
-5
View File
@@ -200,7 +200,6 @@ export const dict = {
"dialog.provider.opencode.note": "Modèles sÊlectionnÊs incluant Claude, GPT, Gemini et plus",
"dialog.provider.opencode.tagline": "Modèles optimisÊs et fiables",
"dialog.provider.opencodeGo.tagline": "Abonnement abordable pour tous",
"dialog.provider.anthropic.note": "Connectez-vous avec Claude Pro/Max ou une clÊ API",
"dialog.provider.copilot.note": "Connectez-vous avec Copilot ou une clÊ API",
"dialog.provider.openai.note": "Connectez-vous avec ChatGPT Pro/Plus ou une clÊ API",
"dialog.provider.google.note": "Modèles Gemini pour des rÊponses rapides et structurÊes",
@@ -217,7 +216,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Voir plus de 70 autres fournisseurs",
"dialog.provider.viewAll": "Voir plus de fournisseurs",
"provider.connect.title": "Connecter {{provider}}",
"provider.connect.title.anthropicProMax": "Connexion avec Claude Pro/Max",
"provider.connect.selectMethod": "SÊlectionnez la mÊthode de connexion pour {{provider}}.",
"provider.connect.method.apiKey": "ClÊ API",
"provider.connect.method.browser": "Navigateur",
@@ -385,10 +383,7 @@ export const dict = {
"prompt.attachment.remove": "Supprimer la pièce jointe",
"prompt.action.send": "Envoyer",
"prompt.action.stop": "ArrÃĒter",
"prompt.toast.pasteUnsupported.title": "Pièce jointe non prise en charge",
"prompt.toast.attachmentDuplicate.title": "Ce fichier a dÊjà ÊtÊ tÊlÊversÊ",
"prompt.toast.pasteUnsupported.description":
"Seules les images, les PDF ou les fichiers texte peuvent ÃĒtre joints ici.",
"prompt.toast.modelAgentRequired.title": "SÊlectionnez un agent et un modèle",
"prompt.toast.modelAgentRequired.description": "Choisissez un agent et un modèle avant d'envoyer une invite.",
"prompt.toast.worktreeCreateFailed.title": "Échec de la crÊation de l'arbre de travail",
-4
View File
@@ -192,7 +192,6 @@ export const dict = {
"dialog.provider.opencode.note": "מבחר מודלים, בהם Claude, GPT, Gemini ו×ĸוד",
"dialog.provider.opencode.tagline": "מודלים אמינים ׊×ĸברו אופטימיז×Ļיה",
"dialog.provider.opencodeGo.tagline": "מנוי ב×ĸלו×Ē × ×ž×•×›×” לכולם",
"dialog.provider.anthropic.note": "גישה ישירה למודלי Claude, כולל Pro ו-Max",
"dialog.provider.copilot.note": "מודלי AI לסיו×ĸ בכ×Ēיב×Ē ×§×•×“ באמ×Ļ×ĸו×Ē GitHub Copilot",
"dialog.provider.openai.note": "מודלי GPT למשימו×Ē AI כלליו×Ē, מהירו×Ē ×•×ž×Ēקדמו×Ē",
"dialog.provider.google.note": "מודלי Gemini ל×Ēגובו×Ē ×ž×”×™×¨×•×Ē ×•×ž×•×‘× ×•×Ē",
@@ -209,7 +208,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ה×Ļג×Ē ×™×•×Ēר מ-70 ספקים נוספים",
"dialog.provider.viewAll": "ה×Ļג ×ĸוד ספקים",
"provider.connect.title": "חבר א×Ē {{provider}}",
"provider.connect.title.anthropicProMax": "ה×Ēחבר ×ĸם Claude Pro/Max",
"provider.connect.selectMethod": "בחר שיט×Ē ×”×Ēחברו×Ē ×ĸבור {{provider}}.",
"provider.connect.method.apiKey": "מפ×Ēח API",
"provider.connect.method.browser": "דפדפן",
@@ -373,8 +371,6 @@ export const dict = {
"prompt.attachment.remove": "הסר א×Ē ×”×§×•×‘×Ĩ המ×Ļו׍ת",
"prompt.action.send": "שלח",
"prompt.action.stop": "×ĸ×Ļור",
"prompt.toast.pasteUnsupported.title": "קוב×Ĩ מ×Ļו׍ת לא × ×Ēמך",
"prompt.toast.pasteUnsupported.description": "ני×Ēן ל×Ļ׍ת כאן רק ×Ēמונו×Ē, קוב×Ļי PDF או קב×Ļי טקץט.",
"prompt.toast.attachmentDuplicate.title": "הקוב×Ĩ הזה כבר הו×ĸלה",
"prompt.toast.modelAgentRequired.title": "בחר סוכן ומודל",
"prompt.toast.modelAgentRequired.description": "יש לבחור סוכן ומודל לפני שליח×Ē ×¤×¨×•×ž×¤×˜.",
-4
View File
@@ -199,7 +199,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini ⤔⤰ ⤅⤧ā¤ŋ⤕ ā¤¸ā¤šā¤ŋ⤤ ⤕āĨā¤¯āĨ‚⤰āĨ‡ā¤ŸāĨ‡ā¤Ą ā¤ŽāĨ‰ā¤Ąā¤˛",
"dialog.provider.opencode.tagline": "ā¤ĩā¤ŋā¤ļāĨā¤ĩ⤏⤍āĨ€ā¤¯ ⤅⤍āĨā¤•āĨ‚⤞ā¤ŋ⤤ ā¤ŽāĨ‰ā¤Ąā¤˛",
"dialog.provider.opencodeGo.tagline": "⤏⤭āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤•ā¤Ž ā¤˛ā¤žā¤—ā¤¤ ā¤ĩā¤žā¤˛āĨ€ ⤏ā¤Ļ⤏āĨā¤¯ā¤¤ā¤ž",
"dialog.provider.anthropic.note": "ā¤ĒāĨā¤°āĨ‹ ⤔⤰ ā¤ŽāĨˆā¤•āĨā¤¸ ā¤¸ā¤šā¤ŋ⤤ Claude ā¤ŽāĨ‰ā¤Ąā¤˛ ⤤⤕ ⤏āĨ€ā¤§āĨ€ ā¤Ēā¤šāĨā¤ā¤š",
"dialog.provider.copilot.note": "GitHub Copilot ⤕āĨ‡ ā¤Žā¤žā¤§āĨā¤¯ā¤Ž ⤏āĨ‡ ⤕āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤¸ā¤šā¤žā¤¯ā¤¤ā¤ž ⤕āĨ‡ ⤞ā¤ŋā¤ AI ā¤ŽāĨ‰ā¤Ąā¤˛",
"dialog.provider.openai.note": "⤤āĨ‡ā¤œā¤ŧ, ⤏⤕āĨā¤ˇā¤Ž ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ AI ā¤•ā¤žā¤°āĨā¤¯āĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ GPT ā¤ŽāĨ‰ā¤Ąā¤˛",
"dialog.provider.google.note": "⤤āĨ‡ā¤œā¤ŧ, ā¤¸ā¤‚ā¤°ā¤šā¤ŋ⤤ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤“ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ Gemini ā¤ŽāĨ‰ā¤Ąā¤˛",
@@ -216,7 +215,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "70+ ⤅⤧ā¤ŋ⤕ ā¤ĒāĨā¤°āĨ‹ā¤ĩā¤žā¤‡ā¤Ąā¤° ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚",
"dialog.provider.viewAll": "⤔⤰ ā¤ĒāĨā¤°āĨ‹ā¤ĩā¤žā¤‡ā¤Ąā¤° ā¤Ļā¤ŋā¤–ā¤žā¤ā¤",
"provider.connect.title": "{{provider}} ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰āĨ‡ā¤‚",
"provider.connect.title.anthropicProMax": "Claude ā¤ĒāĨā¤°āĨ‹/ā¤ŽāĨˆā¤•āĨā¤¸ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚",
"provider.connect.selectMethod": "{{provider}} ⤕āĨ‡ ⤞ā¤ŋā¤ ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĩā¤ŋ⤧ā¤ŋ ⤚āĨā¤¨āĨ‡ā¤‚āĨ¤",
"provider.connect.method.apiKey": "API ⤕āĨā¤‚ā¤œāĨ€",
"provider.connect.method.browser": "ā¤ŦāĨā¤°ā¤žā¤‰ā¤œā¤ŧ⤰",
@@ -382,8 +380,6 @@ export const dict = {
"prompt.attachment.remove": "⤅⤍āĨā¤˛ā¤—āĨā¤¨ā¤• ā¤šā¤Ÿā¤žā¤ā¤",
"prompt.action.send": "⤭āĨ‡ā¤œāĨ‡ā¤‚",
"prompt.action.stop": "⤰āĨ‹ā¤•āĨ‡ā¤‚",
"prompt.toast.pasteUnsupported.title": "ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ⤅⤍āĨā¤˛ā¤—āĨā¤¨ā¤•",
"prompt.toast.pasteUnsupported.description": "ā¤¯ā¤šā¤žā¤‚ ⤕āĨ‡ā¤ĩ⤞ ⤛ā¤ĩā¤ŋā¤¯ā¤žā¤‚, PDFs, ā¤¯ā¤ž ⤟āĨ‡ā¤•āĨā¤¸āĨā¤Ÿ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ⤏⤂⤞⤗āĨā¤¨ ⤕āĨ€ ā¤œā¤ž ⤏⤕⤤āĨ€ ā¤šāĨˆā¤‚āĨ¤",
"prompt.toast.attachmentDuplicate.title": "ā¤¯ā¤š ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤Ēā¤šā¤˛āĨ‡ ā¤šāĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤œā¤ž ⤚āĨā¤•āĨ€ ā¤šāĨˆ",
"prompt.toast.modelAgentRequired.title": "ā¤ā¤• ā¤ā¤œāĨ‡ā¤‚ā¤Ÿ ⤔⤰ ā¤ŽāĨ‰ā¤Ąā¤˛ ⤚āĨā¤¨āĨ‡ā¤‚",
"prompt.toast.modelAgentRequired.description": "ā¤ĒāĨā¤°āĨ‰ā¤ŽāĨā¤ĒāĨā¤Ÿ ⤭āĨ‡ā¤œā¤¨āĨ‡ ⤏āĨ‡ ā¤Ēā¤šā¤˛āĨ‡ ā¤ā¤• ā¤ā¤œāĨ‡ā¤‚ā¤Ÿ ⤔⤰ ā¤ŽāĨ‰ā¤Ąā¤˛ ⤚āĨā¤¨āĨ‡ā¤‚āĨ¤",
-4
View File
@@ -195,7 +195,6 @@ export const dict = {
"dialog.provider.opencode.note": "Odabrani modeli uključujući Claude, GPT, Gemini i druge",
"dialog.provider.opencode.tagline": "Pouzdani optimizirani modeli",
"dialog.provider.opencodeGo.tagline": "Niska cijena pretplate za sve",
"dialog.provider.anthropic.note": "Izravan pristup Claude modelima, uključujući Pro i Max",
"dialog.provider.copilot.note": "AI modeli za pomoć kodiranja putem GitHub Copilot",
"dialog.provider.openai.note": "GPT modeli za brze, sposobne opće AI zadatke",
"dialog.provider.google.note": "Gemini modeli za brze, strukturirane odgovore",
@@ -212,7 +211,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Pogledajte viÅĄe od 70 pruÅžatelja usluga",
"dialog.provider.viewAll": "PrikaÅži viÅĄe pruÅžatelja usluga",
"provider.connect.title": "PoveÅžite {{provider}}",
"provider.connect.title.anthropicProMax": "Prijavite se sa Claude Pro/Max",
"provider.connect.selectMethod": "Odaberite način prijave za {{provider}}.",
"provider.connect.method.apiKey": "Ključ API",
"provider.connect.method.browser": "preglednik",
@@ -379,8 +377,6 @@ export const dict = {
"prompt.attachment.remove": "Ukloni privitak",
"prompt.action.send": "Poslati",
"prompt.action.stop": "Zaustavi",
"prompt.toast.pasteUnsupported.title": "NepodrÅžani privitak",
"prompt.toast.pasteUnsupported.description": "Ovdje se mogu priloÅžiti samo slike, PDF-ovi ili tekstualne datoteke.",
"prompt.toast.attachmentDuplicate.title": "Ova datoteka je već učitana",
"prompt.toast.modelAgentRequired.title": "Odaberite agenta i model",
"prompt.toast.modelAgentRequired.description": "Odaberite agenta i model prije slanja upita.",
-4
View File
@@ -195,7 +195,6 @@ export const dict = {
"dialog.provider.opencode.note": "ÖsszevÃĄlogatott modellek, kÃļztÃŧk Claude, GPT, Gemini Ês mÊg sok mÃĄs",
"dialog.provider.opencode.tagline": "MegbízhatÃŗ optimalizÃĄlt modellek",
"dialog.provider.opencodeGo.tagline": "OlcsÃŗ előfizetÊs mindenki szÃĄmÃĄra",
"dialog.provider.anthropic.note": "KÃļzvetlen hozzÃĄfÊrÊs a Claude modellekhez, beleÊrtve a Pro Ês Max modelleket",
"dialog.provider.copilot.note": "AI modellek a kÃŗdolÃĄshoz a GitHub Copilot segítsÊgÊvel",
"dialog.provider.openai.note": "GPT modellek a gyors, ÃĄltalÃĄnos AI-feladatokhoz",
"dialog.provider.google.note": "Gemini modellek a gyors, strukturÃĄlt vÃĄlaszokhoz",
@@ -212,7 +211,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "TovÃĄbbi 70+ szolgÃĄltatÃŗ megtekintÊse",
"dialog.provider.viewAll": "TovÃĄbbi szolgÃĄltatÃŗk megjelenítÊse",
"provider.connect.title": "Csatlakoztassa a {{provider}}-t",
"provider.connect.title.anthropicProMax": "Jelentkezzen be a Claude Pro/Max",
"provider.connect.selectMethod": "VÃĄlassza ki a {{provider}} bejelentkezÊsi mÃŗdjÃĄt.",
"provider.connect.method.apiKey": "API kulcs",
"provider.connect.method.browser": "BÃļngÊsző",
@@ -379,8 +377,6 @@ export const dict = {
"prompt.attachment.remove": "TÃĄvolítsa el a mellÊkletet",
"prompt.action.send": "ElkÃŧld",
"prompt.action.stop": "LeÃĄllítÃĄs",
"prompt.toast.pasteUnsupported.title": "Nem tÃĄmogatott mellÊklet",
"prompt.toast.pasteUnsupported.description": "Ide csak kÊpeket, PDF-eket vagy szÃļveges fÃĄjlokat lehet csatolni.",
"prompt.toast.attachmentDuplicate.title": "Ezt a fÃĄjlt mÃĄr feltÃļltÃļttÊk",
"prompt.toast.modelAgentRequired.title": "VÃĄlasszon egy ÃŧgynÃļkÃļt Ês modellt",
"prompt.toast.modelAgentRequired.description": "A felszÃŗlítÃĄs elkÃŧldÊse előtt vÃĄlasszon ÃŧgynÃļkÃļt Ês modellt.",
-4
View File
@@ -193,7 +193,6 @@ export const dict = {
"dialog.provider.opencode.note": "Ô¸ÕļÕŋÖ€ÕžÕĄÕŽ Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր, ÕļÕĨÖ€ÕĄÕŧÕĩÕĄÕŦ՝ Claude, GPT, Gemini և ÕĄÕžÕĨÕŦÕĢÕļ",
"dialog.provider.opencode.tagline": "ՎÕŊÕŋÕĄÕ°ÕĨÕŦÕĢ Ö…ÕēÕŋÕĢÕ´ÕĢÕĻÕĄÖÕžÕĄÕŽ Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր",
"dialog.provider.opencodeGo.tagline": "Õ‘ÕĄÕŽÖ€ ÕŖÕļÕ¸Õž ÕĸÕĄÕĒÕĄÕļÕ¸Ö€Õ¤ÕĄÕŖÖ€Õ¸Ö‚ÕŠÕĩուÕļ ÕĸÕ¸ÕŦորÕĢ Õ°ÕĄÕ´ÕĄÖ€",
"dialog.provider.anthropic.note": "ÕˆÖ‚Õ˛ÕĢÕ˛ մուÕŋք Õ¤ÕĨÕēÕĢ Claude Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր, ÕļÕĨÖ€ÕĄÕŧÕĩÕĄÕŦ Pro և Max",
"dialog.provider.copilot.note": "AI Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր Õ¯Õ¸Õ¤ÕĄÕžÕ¸Ö€Õ´ÕĄÕļ Ö…ÕŖÕļÕ¸Ö‚ÕŠÕĩÕĄÕļ Õ°ÕĄÕ´ÕĄÖ€Õ GitHub Copilot",
"dialog.provider.openai.note": "GPT Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր ÕĄÖ€ÕĄÕŖ, ուÕļÕĄÕ¯ Õ¨ÕļÕ¤Õ°ÕĄÕļուր AI ÕĄÕŧÕĄÕģÕĄÕ¤Ö€ÕĄÕļքÕļÕĨրÕĢ Õ°ÕĄÕ´ÕĄÖ€",
"dialog.provider.google.note": "Gemini Õ´Õ¸Õ¤ÕĨÕŦÕļÕĨր ÕĄÖ€ÕĄÕŖ, Õ¯ÕĄÕŧÕ¸Ö‚ÖÕžÕĄÕŽÖ„ÕĄÕĩÕĢÕļ ÕēÕĄÕŋÕĄÕŊÕ­ÕĄÕļÕļÕĨրÕĢ Õ°ÕĄÕ´ÕĄÖ€",
@@ -210,7 +209,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ՏÕĨÕŊ 70+ ÕĄÕĩÕŦ Õ´ÕĄÕŋÕĄÕ¯ÕĄÖ€ÕĄÖ€ÕļÕĨր",
"dialog.provider.viewAll": "ՑուÕĩց ÕŋÕĄÕŦ ÕĄÕžÕĨÕŦÕĢ ÕˇÕĄÕŋ Õ´ÕĄÕŋÕĄÕ¯ÕĄÖ€ÕĄÖ€ÕļÕĨր",
"provider.connect.title": "ՄÕĢÕĄÖÕĨք {{provider}}",
"provider.connect.title.anthropicProMax": "ՄուÕŋք ÕŖÕ¸Ö€ÕŽÕĨք Claude Pro/Max-Õ¸Õž",
"provider.connect.selectMethod": "Ô¸ÕļÕŋրÕĨք մուÕŋքÕĢ ÕĨÕ˛ÕĄÕļÕĄÕ¯Õ¨ {{provider}}-ÕĢ Õ°ÕĄÕ´ÕĄÖ€Ö‰",
"provider.connect.method.apiKey": "API ÕĸÕĄÕļÕĄÕŦÕĢ",
"provider.connect.method.browser": "Ô´ÕĢÕŋÕĄÖ€Õ¯ÕĢÕš",
@@ -377,8 +375,6 @@ export const dict = {
"prompt.attachment.remove": "ՀÕĨÕŧÕĄÖÕļÕĨÕŦ Õ°ÕĄÕžÕĨÕŦÕžÕĄÕŽÕ¨",
"prompt.action.send": "ÕˆÖ‚Õ˛ÕĄÖ€Õ¯ÕĨÕŦ",
"prompt.action.stop": "ÔŋÕĄÕļÕŖÕļÕĨցÕļÕĨÕŦ",
"prompt.toast.pasteUnsupported.title": "Õ‰ÕĄÕģÕĄÕ¯ÖÕžÕ¸Õ˛ Õ°ÕĄÕžÕĨÕŦÕžÕĄÕŽ",
"prompt.toast.pasteUnsupported.description": "ÔąÕĩÕŊÕŋÕĨÕ˛ Õ¯ÕĄÖ€Õ¸Õ˛ ÕĨÕļ կց՞ÕĨÕŦ Õ´ÕĢÕĄÕĩÕļ ÕēÕĄÕŋÕ¯ÕĨրÕļÕĨր, PDF Õ¯ÕĄÕ´ ÕŋÕĨքÕŊÕŋÕĄÕĩÕĢÕļ Ö†ÕĄÕĩÕŦÕĨր։",
"prompt.toast.attachmentDuplicate.title": "ÔąÕĩÕŊ Ö†ÕĄÕĩÕŦÕļ ÕĄÖ€Õ¤ÕĨÕļ ÕžÕĨրÕĸÕĨÕŧÕļÕžÕĨÕŦ Õ§",
"prompt.toast.modelAgentRequired.title": "Ô¸ÕļÕŋրÕĨք ÕŖÕ¸Ö€ÕŽÕĄÕ¯ÕĄÕŦ և Õ´Õ¸Õ¤ÕĨÕŦ",
"prompt.toast.modelAgentRequired.description": "Ô¸ÕļÕŋրÕĨք ÕŖÕ¸Ö€ÕŽÕĄÕ¯ÕĄÕŦ և Õ´Õ¸Õ¤ÕĨÕŦ՝ ÕļÕĄÕ­Ö„ÕĄÕļ Õ°Õ¸Ö‚ÕˇÕ¸Ö‚Õ´ Õ¸Ö‚Õ˛ÕĄÖ€Õ¯ÕĨÕŦը։",
-4
View File
@@ -208,7 +208,6 @@ export const dict = {
"dialog.provider.opencode.note": "Model pilihan, termasuk Claude, GPT, Gemini, dan lainnya",
"dialog.provider.opencode.tagline": "Model andal yang dioptimalkan",
"dialog.provider.opencodeGo.tagline": "Langganan terjangkau untuk semua orang",
"dialog.provider.anthropic.note": "Akses langsung ke model Claude, termasuk Pro dan Max",
"dialog.provider.copilot.note": "Model AI untuk bantuan pengodean melalui GitHub Copilot",
"dialog.provider.openai.note": "Model GPT untuk tugas AI umum yang cepat dan mumpuni",
"dialog.provider.google.note": "Model Gemini untuk respons cepat dan terstruktur",
@@ -229,7 +228,6 @@ export const dict = {
"dialog.provider.viewAll": "Tampilkan lebih banyak penyedia",
"provider.connect.title": "Hubungkan {{provider}}",
"provider.connect.title.anthropicProMax": "Masuk dengan Claude Pro/Max",
"provider.connect.selectMethod": "Pilih metode masuk untuk {{provider}}.",
"provider.connect.method.apiKey": "Kunci API",
"provider.connect.method.browser": "Peramban",
@@ -403,8 +401,6 @@ export const dict = {
"prompt.action.send": "Kirim",
"prompt.action.stop": "Hentikan",
"prompt.toast.pasteUnsupported.title": "Lampiran tidak didukung",
"prompt.toast.pasteUnsupported.description": "Hanya gambar, PDF, atau berkas teks yang dapat dilampirkan di sini.",
"prompt.toast.attachmentDuplicate.title": "Berkas ini sudah diunggah",
"prompt.toast.modelAgentRequired.title": "Pilih agen dan model",
"prompt.toast.modelAgentRequired.description": "Pilih agen dan model sebelum mengirim prompt.",
-4
View File
@@ -195,7 +195,6 @@ export const dict = {
"dialog.provider.opencode.note": "SÃŊndar gerðir Þar ÃĄ meðal Claude, GPT, Gemini og fleira",
"dialog.provider.opencode.tagline": "Áreiðanleg bjartsÃŊni mÃŗdel",
"dialog.provider.opencodeGo.tagline": "LÃĄggjaldaÃĄskrift fyrir alla",
"dialog.provider.anthropic.note": "Beinn aðgangur að Claude gerðum, Þar ÃĄ meðal Pro og Max",
"dialog.provider.copilot.note": "AI mÃŗdel fyrir kÃŗÃ°aaðstoð í gegnum GitHub Copilot",
"dialog.provider.openai.note": "GPT gerðir fyrir hrÃļð, fÃĻr almenn gervigreind verkefni",
"dialog.provider.google.note": "Gemini mÃŗdel fyrir hrÃļð, skipulÃļgð viðbrÃļgð",
@@ -212,7 +211,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "SjÃĄÃ°u 70+ ÞjÃŗnustuveitendur í viðbÃŗt",
"dialog.provider.viewAll": "SÃŊna fleiri veitendur",
"provider.connect.title": "Tengdu {{provider}}",
"provider.connect.title.anthropicProMax": "SkrÃĄÃ°u Þig inn með Claude Pro/Max",
"provider.connect.selectMethod": "Veldu innskrÃĄningaraðferð fyrir {{provider}}.",
"provider.connect.method.apiKey": "API lykill",
"provider.connect.method.browser": "Vafri",
@@ -379,8 +377,6 @@ export const dict = {
"prompt.attachment.remove": "FjarlÃĻgðu viðhengi",
"prompt.action.send": "Senda",
"prompt.action.stop": "StÃļðva",
"prompt.toast.pasteUnsupported.title": "Óstudd viðhengi",
"prompt.toast.pasteUnsupported.description": "Aðeins er hÃĻgt að hengja myndir, PDF-skjÃļl eða textaskrÃĄr hÊr við.",
"prompt.toast.attachmentDuplicate.title": "Þessari skrÃĄ hefur Þegar verið hlaðið upp",
"prompt.toast.modelAgentRequired.title": "Veldu fulltrÃēa og líkan",
"prompt.toast.modelAgentRequired.description": "Veldu fulltrÃēa og líkan ÃĄÃ°ur en ÞÃē sendir kvaðningu.",
-4
View File
@@ -100,7 +100,6 @@ export const dict = {
"dialog.provider.opencode.note": "Modelli selezionati tra cui Claude, GPT, Gemini e altri",
"dialog.provider.opencode.tagline": "Modelli affidabili e ottimizzati",
"dialog.provider.opencodeGo.tagline": "Abbonamento economico per tutti",
"dialog.provider.anthropic.note": "Accesso diretto ai modelli Claude, inclusi Pro e Max",
"dialog.provider.copilot.note": "Modelli di IA per assistere nella programmazione tramite GitHub Copilot",
"dialog.provider.openai.note": "Modelli GPT veloci e potenti per attività generiche di IA",
"dialog.provider.google.note": "Modelli Gemini per risposte rapide e strutturate",
@@ -117,7 +116,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Scopri oltre 70 provider",
"dialog.provider.viewAll": "Mostra altri provider",
"provider.connect.title": "Connetti {{provider}}",
"provider.connect.title.anthropicProMax": "Accedi con Claude Pro/Max",
"provider.connect.selectMethod": "Seleziona il metodo di accesso per {{provider}}.",
"provider.connect.method.apiKey": "Chiave API",
"provider.connect.method.browser": "Browser",
@@ -284,8 +282,6 @@ export const dict = {
"prompt.attachment.remove": "Rimuovi l'allegato",
"prompt.action.send": "Invia",
"prompt.action.stop": "Interrompi",
"prompt.toast.pasteUnsupported.title": "Allegato non supportato",
"prompt.toast.pasteUnsupported.description": "Qui è possibile allegare solo immagini, PDF o file di testo.",
"prompt.toast.attachmentDuplicate.title": "Questo file è già stato caricato",
"prompt.toast.modelAgentRequired.title": "Seleziona un agente e un modello",
"prompt.toast.modelAgentRequired.description": "Scegli un agente e un modello prima di inviare un prompt.",
-4
View File
@@ -198,7 +198,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude, GPT, GeminiãĒおをåĢã‚€åŽŗé¸ã•ã‚ŒãŸãƒĸデãƒĢ",
"dialog.provider.opencode.tagline": "äŋĄé ŧ性ぎéĢ˜ã„æœ€éŠåŒ–ãƒĸデãƒĢ",
"dialog.provider.opencodeGo.tagline": "すずãĻぎäēēãĢäŊŽäžĄæ ŧぎã‚ĩブ゚クãƒĒãƒ—ã‚ˇãƒ§ãƒŗ",
"dialog.provider.anthropic.note": "ProやMaxをåĢむClaudeãƒĸデãƒĢãĢį›´æŽĨã‚ĸクã‚ģ゚",
"dialog.provider.copilot.note": "GitHub Copilotを通じãĻã‚ŗãƒŧãƒ‡ã‚Ŗãƒŗã‚°ã‚’æ”¯æ´ã™ã‚‹AIãƒĸデãƒĢ",
"dialog.provider.openai.note": "éĢ˜é€Ÿã§éĢ˜æ€§čƒŊãĒæąŽį”¨AIã‚ŋ゚ク向けぎGPTãƒĸデãƒĢ",
"dialog.provider.google.note": "éĢ˜é€Ÿã§æ§‹é€ åŒ–ã•ã‚ŒãŸåŋœį­”ぎためぎGeminiãƒĸデãƒĢ",
@@ -215,7 +214,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "そぎäģ–70äģĨ上ぎプロバイダãƒŧをčĻ‹ã‚‹",
"dialog.provider.viewAll": "さらãĢプロバイダãƒŧã‚’čĄ¨į¤ē",
"provider.connect.title": "{{provider}}をæŽĨįļš",
"provider.connect.title.anthropicProMax": "Claude Pro/Maxã§ãƒ­ã‚°ã‚¤ãƒŗ",
"provider.connect.selectMethod": "{{provider}}ãŽãƒ­ã‚°ã‚¤ãƒŗæ–šæŗ•ã‚’é¸æŠžã—ãĻください。",
"provider.connect.method.apiKey": "APIキãƒŧ",
"provider.connect.method.browser": "ブナã‚Ļã‚ļ",
@@ -379,9 +377,7 @@ export const dict = {
"prompt.attachment.remove": "æˇģäģ˜ãƒ•ã‚Ąã‚¤ãƒĢを削除",
"prompt.action.send": "送äŋĄ",
"prompt.action.stop": "停æ­ĸ",
"prompt.toast.pasteUnsupported.title": "ã‚ĩポãƒŧトされãĻいãĒいæˇģäģ˜ãƒ•ã‚Ąã‚¤ãƒĢ",
"prompt.toast.attachmentDuplicate.title": "ã“ãŽãƒ•ã‚Ąã‚¤ãƒĢはすでãĢã‚ĸップロãƒŧドされãĻいぞす",
"prompt.toast.pasteUnsupported.description": "į”ģ像、PDFã€ãžãŸã¯ãƒ†ã‚­ã‚šãƒˆãƒ•ã‚Ąã‚¤ãƒĢぎãŋæˇģäģ˜ã§ããžã™ã€‚",
"prompt.toast.modelAgentRequired.title": "エãƒŧã‚¸ã‚§ãƒŗãƒˆã¨ãƒĸデãƒĢを選択",
"prompt.toast.modelAgentRequired.description": "ãƒ—ãƒ­ãƒŗãƒ—ãƒˆã‚’é€äŋĄã™ã‚‹å‰ãĢエãƒŧã‚¸ã‚§ãƒŗãƒˆã¨ãƒĸデãƒĢを選択しãĻください。",
"prompt.toast.worktreeCreateFailed.title": "ワãƒŧクツãƒĒãƒŧぎäŊœæˆãĢå¤ąæ•—ã—ãžã—ãŸ",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "áƒ™áƒŖáƒ áƒ˜áƒ áƒ”áƒ‘áƒŖáƒšáƒ˜ მოდელები, მათ შორის Claude, GPT, Gemini და სხვა",
"dialog.provider.opencode.tagline": "სანდო ოპáƒĸáƒ˜áƒ›áƒ˜áƒ–áƒ”áƒ‘áƒŖáƒšáƒ˜ მოდელები",
"dialog.provider.opencodeGo.tagline": "დაბალფასიანი გამოáƒŦერა ყველასთვის",
"dialog.provider.anthropic.note": "პირდაპირი áƒŦვდომა Claude მოდელზე, მათ შორის Pro და Max",
"dialog.provider.copilot.note": "AI მოდელები კოდირების დახმარებისთვის GitHub Copilot",
"dialog.provider.openai.note": "GPT მოდელები ქáƒŦრაფი, ზოგადი AI ამოáƒĒანების áƒ¨áƒ”áƒĄáƒáƒĄáƒ áƒŖáƒšáƒ”áƒ‘áƒšáƒáƒ“",
"dialog.provider.google.note": "Gemini მოდელები ქáƒŦრაფი, ქáƒĸáƒ áƒŖáƒĨáƒĸáƒŖáƒ áƒ˜áƒ áƒ”áƒ‘áƒŖáƒšáƒ˜ áƒžáƒáƒĄáƒŖáƒŽáƒ”áƒ‘áƒ˜áƒĄáƒ—áƒ•áƒ˜áƒĄ",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "იხილეთ 70+ სხვა პროვაიდერი",
"dialog.provider.viewAll": "აჩვენე მეáƒĸი პროვაიდერი",
"provider.connect.title": "დაკავშირება {{provider}}",
"provider.connect.title.anthropicProMax": "შესვლა Claude Pro/Max-ით",
"provider.connect.selectMethod": "აირჩიეთ შესვლის მეთოდი {{provider}}-ისთვის.",
"provider.connect.method.apiKey": "API გასაáƒĻები",
"provider.connect.method.browser": "áƒ‘áƒ áƒáƒŖáƒ–áƒ”áƒ áƒ˜",
@@ -375,8 +373,6 @@ export const dict = {
"prompt.attachment.remove": "დანართის áƒŦაშლა",
"prompt.action.send": "გაგზავნა",
"prompt.action.stop": "შეჩერება",
"prompt.toast.pasteUnsupported.title": "áƒ›áƒŽáƒáƒ áƒ“áƒáƒŖáƒ­áƒ”áƒ áƒ”áƒšáƒ˜ დანართი",
"prompt.toast.pasteUnsupported.description": "აáƒĨ შეიáƒĢლება დაერთოს მხოლოდ áƒĄáƒŖáƒ áƒáƒ—áƒ”áƒ‘áƒ˜, PDF ან áƒĸეáƒĨქáƒĸáƒŖáƒ áƒ˜ ფაილები.",
"prompt.toast.attachmentDuplicate.title": "ეს ფაილი áƒŖáƒ™áƒ•áƒ” აáƒĸáƒ•áƒ˜áƒ áƒ—áƒŖáƒšáƒ˜áƒ",
"prompt.toast.modelAgentRequired.title": "აირჩიეთ აგენáƒĸი და მოდელი",
"prompt.toast.modelAgentRequired.description": "აირჩიეთ აგენáƒĸი და მოდელი მოთხოვნის გაგზავნამდე.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "ម៉ážŧដែលដែលបážļនជ្រើសរើសរážŊមមážļន Claude, GPT, Gemini និងច្រើនទៀត",
"dialog.provider.opencode.tagline": "ម៉ážŧដែលដែលបážļនកែលម្ážĸដែលážĸážļចទážģកចិត្តបážļន។",
"dialog.provider.opencodeGo.tagline": "កážļរជážļវតម្លៃទážļបសម្រážļប់ážĸ្នករážļល់គ្នážļ",
"dialog.provider.anthropic.note": "កážļរចážŧលប្រើដោយផ្ទážļល់ទៅកážļន់ម៉ážŧដែល Claude រážŊមទážļំង Pro និង Max",
"dialog.provider.copilot.note": "ម៉ážŧដែល AI សម្រážļប់ជំនážŊយកážļរសរសេរកážŧដតážļមរយៈ GitHub Copilot",
"dialog.provider.openai.note": "ម៉ážŧដែល GPT សម្រážļប់កិច្ចកážļរ AI ទážŧទៅដែលមážļនល្បážŋនលážŋន និងមážļនសមត្ថភážļព",
"dialog.provider.google.note": "ម៉ážŧដែល Gemini សម្រážļប់កážļរឆ្លើយតបរហ័ស និងមážļនរចនážļសម្ព័ន្ធ",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "មើលážĸ្នកផ្តល់សេវážļ 70+ ទៀត។",
"dialog.provider.viewAll": "បង្ហážļញážĸ្នកផ្តល់សេវážļបន្ថែម",
"provider.connect.title": "ភ្ជážļប់ {{provider}}",
"provider.connect.title.anthropicProMax": "ចážŧលដោយប្រើ Claude Pro/Max",
"provider.connect.selectMethod": "ជ្រើសរើសវិធីសážļស្ត្រចážŧលសម្រážļប់ {{provider}} ។",
"provider.connect.method.apiKey": "សោ API",
"provider.connect.method.browser": "កម្មវិធីរážģករក",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "លážģបឯកសážļរភ្ជážļប់ចេញ",
"prompt.action.send": "ផ្ញើ",
"prompt.action.stop": "ឈប់",
"prompt.toast.pasteUnsupported.title": "ឯកសážļរភ្ជážļប់ដែលមិនគážļំទ្រ",
"prompt.toast.pasteUnsupported.description": "មážļនតែរážŧបភážļព PDF ážŦឯកសážļរážĸត្ថបទប៉ážģណ្ណោះដែលážĸážļចភ្ជážļប់មកទីនេះបážļន។",
"prompt.toast.attachmentDuplicate.title": "ឯកសážļរនេះត្រážŧវបážļនផ្ទážģកឡើងរážŊចហើយ",
"prompt.toast.modelAgentRequired.title": "ជ្រើសរើសភ្នážļក់ងážļរ និងម៉ážŧដែល",
"prompt.toast.modelAgentRequired.description": "ជ្រើសរើសភ្នážļក់ងážļរ និងម៉ážŧដែលមážģនពេលផ្ញើប្រážĸប់បញ្ចážŧល។",
-4
View File
@@ -95,7 +95,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini ë“ąė„ íŦ함한 ė—„ė„ ëœ ëĒ¨ë¸",
"dialog.provider.opencode.tagline": "ė‹ ëĸ°í•  눘 ėžˆëŠ” ėĩœė í™” ëĒ¨ë¸",
"dialog.provider.opencodeGo.tagline": "ëĒ¨ë‘ëĨŧ ėœ„í•œ ė €ë ´í•œ ęĩŦ독",
"dialog.provider.anthropic.note": "Pro 및 MaxëĨŧ íŦ함한 Claude ëĒ¨ë¸ė— 링렑 ė•Ąė„¸ėŠ¤",
"dialog.provider.copilot.note": "GitHub Copilotė„ í†ĩ한 ėŊ”딊 ė§€ė›ėšŠ AI ëĒ¨ë¸",
"dialog.provider.openai.note": "ëš ëĨ´ęŗ  강ë Ĩ한 ë˛”ėšŠ AI ėž‘ė—…ė„ ėœ„í•œ GPT ëĒ¨ë¸",
"dialog.provider.google.note": "ëš ëĨ´ęŗ  ęĩŦėĄ°í™”ëœ ė‘ë‹ĩė„ ėœ„í•œ Gemini ëĒ¨ë¸",
@@ -112,7 +111,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "70氜 ė´ėƒė˜ ęŗĩę¸‰ėž 더 ëŗ´ę¸°",
"dialog.provider.viewAll": "더 ë§Žė€ ęŗĩę¸‰ėž ëŗ´ę¸°",
"provider.connect.title": "{{provider}} 뗰枰",
"provider.connect.title.anthropicProMax": "Claude Pro/Max로 ëĄœęˇ¸ė¸",
"provider.connect.selectMethod": "{{provider}} ëĄœęˇ¸ė¸ 방법 ė„ íƒ",
"provider.connect.method.apiKey": "API 키",
"provider.connect.method.browser": "브ëŧėš°ė €",
@@ -268,9 +266,7 @@ export const dict = {
"prompt.attachment.remove": "랍ëļ€ íŒŒėŧ ė œęą°",
"prompt.action.send": "ė „ė†Ą",
"prompt.action.stop": "뤑맀",
"prompt.toast.pasteUnsupported.title": "ė§€ė›ë˜ė§€ ė•ŠëŠ” 랍ëļ€ íŒŒėŧ",
"prompt.toast.attachmentDuplicate.title": "ė´ 파ėŧė€ ė´ë¯¸ ė—…ëĄœë“œë˜ė—ˆėŠĩ니다",
"prompt.toast.pasteUnsupported.description": "ė´ë¯¸ė§€, PDF 또는 í…ėŠ¤íŠ¸ 파ėŧ만 랍ëļ€í•  눘 ėžˆėŠĩ니다.",
"prompt.toast.modelAgentRequired.title": "ė—ė´ė „íŠ¸ 및 ëĒ¨ë¸ ė„ íƒ",
"prompt.toast.modelAgentRequired.description": "프ëĄŦ프트ëĨŧ ëŗ´ë‚´ę¸° 렄뗐 ė—ė´ė „íŠ¸ė™€ ëĒ¨ë¸ė„ ė„ íƒí•˜ė„¸ėš”.",
"prompt.toast.worktreeCreateFailed.title": "ėž‘ė—… 트ëĻŦ ėƒė„ą ė‹¤íŒ¨",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "āģāēšāēšāē—āēĩāģˆāē„āēąāē”āēĒāēąāē™āēĄāē˛āēĨāē§āēĄāē—āēąāē‡ Claude, GPT, Gemini āģāēĨāē°āē­āēˇāģˆāē™āģ†",
"dialog.provider.opencode.tagline": "āēŽāēšāēšāģāēšāēšāēāē˛āē™āģ€āēžāēĩāģˆāēĄāē›āē°āēĒāē´āē”āē—āē´āēžāē˛āēšāē—āēĩāģˆāģ€āēŠāēˇāģˆāē­āē–āēˇāģ„āē”āģ‰",
"dialog.provider.opencodeGo.tagline": "āēāē˛āē™āēĒāē°āģāēąāēāģƒāēŠāģ‰āēĨāē˛āē„āē˛āē•āģāģˆāē˛āēĒāēŗāēĨāēąāēšāē—āē¸āēāē„āēģāē™",
"dialog.provider.anthropic.note": "āģ€āē‚āēģāģ‰āē˛āģ€āē–āē´āē‡āē•āēģāē§āģāēšāēš Claude āģ‚āē”āēāēāēģāē‡, āēĨāē§āēĄāē—āēąāē‡ Pro āģāēĨāē° Max",
"dialog.provider.copilot.note": "āēŽāēšāēšāģāēšāēš AI āēĒāģāē˛āēĨāēąāēšāēāē˛āē™āēŠāģˆāē§āēāģ€āēĢāēŧāēˇāē­āēāē˛āē™āģ€āē‚āēģāģ‰āē˛āēĨāē°āēĢāēąāē”āēœāģˆāē˛āē™ GitHub Copilot",
"dialog.provider.openai.note": "āģāēšāēš GPT āēĒāģāē˛āēĨāēąāēšāē§āēŊāēāē‡āē˛āē™ AI āē—āēģāģˆāē§āģ„āē›āē—āēĩāģˆāģ„āē§, āēĄāēĩāē„āē§āē˛āēĄāēĒāē˛āēĄāē˛āē”",
"dialog.provider.google.note": "āģāēšāēš Gemini āēĒāģāē˛āēĨāēąāēšāēāē˛āē™āē•āē­āēšāēĒāē°āēĢāē™āē­āē‡āģ„āē§, āēĄāēĩāģ‚āē„āē‡āēĒāģ‰āē˛āē‡",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "āģ€āēšāē´āģˆāē‡ 70+ āēœāēšāģ‰āģƒāēĢāģ‰āēšāģāēĨāē´āēāē˛āē™āģ€āēžāēĩāģˆāēĄāģ€āē•āēĩāēĄ",
"dialog.provider.viewAll": "āēĒāē°āģāē”āē‡āēœāēšāģ‰āģƒāēĢāģ‰āēšāģāēĨāē´āēāē˛āē™āģ€āēžāēĩāģˆāēĄāģ€āē•āēĩāēĄ",
"provider.connect.title": "āģ€āēŠāēˇāģˆāē­āēĄāē•āģāģˆ {{provider}}",
"provider.connect.title.anthropicProMax": "āģ€āē‚āēģāģ‰āē˛āēĒāēšāģˆāēĨāē°āēšāēģāēšāē”āģ‰āē§āē Claude Pro/Max",
"provider.connect.selectMethod": "āģ€āēĨāēˇāē­āēāē§āē´āē—āēĩāēāē˛āē™āģ€āē‚āēģāģ‰āē˛āēĒāēšāģˆāēĨāē°āēšāēģāēšāēĒāģāē˛āēĨāēąāēš {{provider}}.",
"provider.connect.method.apiKey": "āēĨāē°āēĢāēąāē” API",
"provider.connect.method.browser": "āē•āēģāē§āē—āģˆāē­āē‡āģ€āē§āēąāēš",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "āģ€āē­āēģāē˛āģ„āēŸāēĨāģŒāģāē™āēšāē­āē­āē",
"prompt.action.send": "āēĒāēģāģˆāē‡",
"prompt.action.stop": "āēĸāē¸āē”",
"prompt.toast.pasteUnsupported.title": "āēšāģāģˆāēŽāē­āē‡āēŽāēąāēšāģ„āēŸāēĨāģŒāģāē™āēš",
"prompt.toast.pasteUnsupported.description": "āēžāēŊāē‡āģāē•āģˆāēŽāēšāēšāēžāē˛āēš, PDFs, āēĢāēŧāēˇāģ„āēŸāēĨāģŒāē‚āģāģ‰āē„āē§āē˛āēĄāēĒāē˛āēĄāē˛āē”āē•āē´āē”āēĸāēšāģˆāē™āēĩāģ‰.",
"prompt.toast.attachmentDuplicate.title": "āģ„āēŸāēĨāģŒāē™āēĩāģ‰āģ„āē”āģ‰āē–āēˇāēāē­āēąāēšāģ‚āēĢāēĨāē”āģ„āē›āēāģˆāē­āē™āģāēĨāģ‰āē§",
"prompt.toast.modelAgentRequired.title": "āģ€āēĨāēˇāē­āēāē•āēģāē§āģāē—āē™ āģāēĨāē°āē•āēģāē§āģāēšāēš",
"prompt.toast.modelAgentRequired.description": "āģ€āēĨāēˇāē­āēāē•āēģāē§āģāē—āē™ āģāēĨāē°āē•āēģāē§āģāēšāēšāēāģˆāē­āē™āēĒāēģāģˆāē‡ prompt.",
-4
View File
@@ -195,7 +195,6 @@ export const dict = {
"dialog.provider.opencode.note": "Kuruojami modeliai, įskaitant Claude, GPT, Gemini ir kt.",
"dialog.provider.opencode.tagline": "Patikimi optimizuoti modeliai",
"dialog.provider.opencodeGo.tagline": "MaÅžos kainos abonementas visiems",
"dialog.provider.anthropic.note": "Tiesioginė prieiga prie Claude modeliÅŗ, įskaitant Pro ir Max",
"dialog.provider.copilot.note": "AI modeliai, skirti kodavimo pagalbai per GitHub Copilot",
"dialog.provider.openai.note": "GPT modeliai, skirti greitoms, bendroms AI uÅžduotims atlikti",
"dialog.provider.google.note": "Gemini modeliai greitiems, struktÅĢriniams atsakymams",
@@ -212,7 +211,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ÅŊr. daugiau nei 70 tiekėjÅŗ",
"dialog.provider.viewAll": "Rodyti daugiau teikėjÅŗ",
"provider.connect.title": "Prijunkite {{provider}}",
"provider.connect.title.anthropicProMax": "Prisijunkite naudodami Claude Pro/Max",
"provider.connect.selectMethod": "Pasirinkite {{provider}} prisijungimo bÅĢdą.",
"provider.connect.method.apiKey": "API raktas",
"provider.connect.method.browser": "NarÅĄyklė",
@@ -380,8 +378,6 @@ export const dict = {
"prompt.attachment.remove": "PaÅĄalinti priedą",
"prompt.action.send": "SiÅŗsti",
"prompt.action.stop": "Stabdyti",
"prompt.toast.pasteUnsupported.title": "Nepalaikomas priedas",
"prompt.toast.pasteUnsupported.description": "Čia galima pridėti tik vaizdus, ​​PDF arba tekstinius failus.",
"prompt.toast.attachmentDuplicate.title": "Šis failas jau buvo įkeltas",
"prompt.toast.modelAgentRequired.title": "Pasirinkite agentą ir modelį",
"prompt.toast.modelAgentRequired.description": "PrieÅĄ siÅŗsdami raginimą, pasirinkite agentą ir modelį.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "AtlasÄĢti modeÄŧi, tostarp Claude, GPT, Gemini un citi",
"dialog.provider.opencode.tagline": "Uzticami optimizēti modeÄŧi",
"dialog.provider.opencodeGo.tagline": "Zemas cenas abonements visiem",
"dialog.provider.anthropic.note": "TieÅĄa piekÄŧuve Claude modeÄŧiem, tostarp Pro un Max",
"dialog.provider.copilot.note": "AI modeÄŧi programmÄ“ÅĄanas atbalstam ar GitHub Copilot",
"dialog.provider.openai.note": "GPT modeÄŧi ātriem, spējÄĢgiem vispārējiem AI uzdevumiem",
"dialog.provider.google.note": "Gemini modeÄŧi ātrām, strukturētām atbildēm",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "SkatÄĢt vēl 70+ nodroÅĄinātājus",
"dialog.provider.viewAll": "RādÄĢt vairāk nodroÅĄinātāju",
"provider.connect.title": "Savienot {{provider}}",
"provider.connect.title.anthropicProMax": "Pieteikties ar Claude Pro/Max",
"provider.connect.selectMethod": "Izvēlieties pieteikÅĄanās metodi pakalpojumam {{provider}}.",
"provider.connect.method.apiKey": "API atslēga",
"provider.connect.method.browser": "PārlÅĢks",
@@ -375,8 +373,6 @@ export const dict = {
"prompt.attachment.remove": "Noņemt pielikumu",
"prompt.action.send": "SÅĢtÄĢt",
"prompt.action.stop": "Apturēt",
"prompt.toast.pasteUnsupported.title": "NeatbalstÄĢts pielikums",
"prompt.toast.pasteUnsupported.description": "Šeit var pievienot tikai attēlus, PDF vai teksta failus.",
"prompt.toast.attachmentDuplicate.title": "Å is fails jau ir augÅĄupielādēts",
"prompt.toast.modelAgentRequired.title": "Izvēlieties aÄŖentu un modeli",
"prompt.toast.modelAgentRequired.description": "Pirms nosÅĢtÄĢÅĄanas izvēlieties aÄŖentu un modeli.",
-4
View File
@@ -192,7 +192,6 @@ export const dict = {
"dialog.provider.opencode.note": "ĐšŅƒŅ€Đ¸Ņ€Đ°ĐŊи ĐŧОдĐĩĐģи вĐēĐģŅƒŅ‡ŅƒĐ˛Đ°Ņ˜ŅœĐ¸ Claude, GPT, Gemini и ĐŋОвĐĩҜĐĩ",
"dialog.provider.opencode.tagline": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи ĐžĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊи ĐŧОдĐĩĐģи",
"dialog.provider.opencodeGo.tagline": "ĐĐ¸ŅĐēа ҆ĐĩĐŊа ĐŋŅ€ĐĩŅ‚ĐŋĐģĐ°Ņ‚Đ° Са ҁĐĩĐēĐžĐŗĐž",
"dialog.provider.anthropic.note": "Đ”Đ¸Ņ€ĐĩĐēŅ‚ĐĩĐŊ ĐŋŅ€Đ¸ŅŅ‚Đ°Đŋ Đ´Đž Claude ĐŧОдĐĩĐģи, вĐēĐģŅƒŅ‡ŅƒĐ˛Đ°Ņ˜ŅœĐ¸ ĐŗĐ¸ Pro и Max",
"dialog.provider.copilot.note": "МодĐĩĐģи ŅĐž вĐĩŅˆŅ‚Đ°Ņ‡Đēа иĐŊŅ‚ĐĩĐģĐ¸ĐŗĐĩĐŊŅ†Đ¸Ņ˜Đ° Са ĐŋĐžĐŧĐžŅˆ Са ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ ĐŋŅ€ĐĩĐē҃ GitHub Copilot",
"dialog.provider.openai.note": "GPT ĐŧОдĐĩĐģи Са ĐąŅ€ĐˇĐ¸, ҁĐŋĐžŅĐžĐąĐŊи ĐžĐŋŅˆŅ‚Đ¸ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ ŅĐž вĐĩŅˆŅ‚Đ°Ņ‡Đēа иĐŊŅ‚ĐĩĐģĐ¸ĐŗĐĩĐŊŅ†Đ¸Ņ˜Đ°",
"dialog.provider.google.note": "Gemini ĐŧОдĐĩĐģи Са ĐąŅ€ĐˇĐ¸, ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ¸Ņ€Đ°ĐŊи ĐžĐ´ĐŗĐžĐ˛ĐžŅ€Đ¸",
@@ -209,7 +208,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ВидĐĩŅ‚Đĩ ĐŋОвĐĩҜĐĩ Од 70 ĐŋŅ€ĐžĐ˛Đ°Ņ˜Đ´ĐĩŅ€Đ¸",
"dialog.provider.viewAll": "ĐŸŅ€Đ¸ĐēаĐļи ĐŋОвĐĩҜĐĩ ĐŋŅ€ĐžĐ˛Đ°Ņ˜Đ´ĐĩŅ€Đ¸",
"provider.connect.title": "ĐŸĐžĐ˛Ņ€ĐˇĐĩŅ‚Đĩ ҁĐĩ {{provider}}",
"provider.connect.title.anthropicProMax": "ĐĐ°Ņ˜Đ°Đ˛ĐĩŅ‚Đĩ ҁĐĩ ŅĐž Claude Pro/Max",
"provider.connect.selectMethod": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ Са ĐŊĐ°Ņ˜Đ°Đ˛ŅƒĐ˛Đ°ŅšĐĩ Са {{provider}}.",
"provider.connect.method.apiKey": "ĐēĐģŅƒŅ‡ API",
"provider.connect.method.browser": "ĐŸŅ€ĐĩĐģĐ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡",
@@ -376,8 +374,6 @@ export const dict = {
"prompt.attachment.remove": "ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊĐĩŅ‚Đĩ ĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐŗĐžŅ‚",
"prompt.action.send": "Đ˜ŅĐŋŅ€Đ°Ņ‚Đ¸",
"prompt.action.stop": "ĐĄŅ‚ĐžĐŋ",
"prompt.toast.pasteUnsupported.title": "НĐĩĐŋĐžĐ´Đ´Ņ€ĐļаĐŊ ĐŋŅ€Đ¸ĐģĐžĐŗ",
"prompt.toast.pasteUnsupported.description": "ОвдĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋŅ€Đ¸ĐēĐ°Ņ‡Đ°Ņ‚ ŅĐ°ĐŧĐž ҁĐģиĐēи, PDFs иĐģи Ņ‚ĐĩĐēŅŅ‚ŅƒĐ°ĐģĐŊи Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēи.",
"prompt.toast.attachmentDuplicate.title": "Оваа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа Đĩ вĐĩҜĐĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐĩĐŊа",
"prompt.toast.modelAgentRequired.title": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚ и ĐŧОдĐĩĐģ",
"prompt.toast.modelAgentRequired.description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚ и ĐŧОдĐĩĐģ ĐŋŅ€ĐĩĐ´ да Đ¸ŅĐŋŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ иСвĐĩŅŅ‚ŅƒĐ˛Đ°ŅšĐĩ.",
-4
View File
@@ -194,7 +194,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini йОĐģĐžĐŊ ĐąŅƒŅĐ°Đ´ ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´Ņ‹Đŗ ŅĐžĐŊĐŗĐžĐŊ ŅˆĐ°ĐģĐŗĐ°Ņ€ŅƒŅƒĐģŅĐ°ĐŊ",
"dialog.provider.opencode.tagline": "ĐĐ°ĐšĐ´Đ˛Đ°Ņ€Ņ‚Đ°Đš ĐžĐŊĐžĐ˛Ņ‡Ņ‚ĐžĐš ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´",
"dialog.provider.opencodeGo.tagline": "ĐĨŌ¯ĐŊ ĐąŌ¯Ņ€Ņ‚ ĐˇĐžŅ€Đ¸ŅƒĐģŅĐ°ĐŊ Ņ…ŅĐŧĐ´ ĶŠŅ€Ņ‚ĶŠĐŗŅ‚ŅĐš ĐˇĐ°Ņ…Đ¸Đ°ĐģĐŗĐ°",
"dialog.provider.anthropic.note": "Pro йОĐģĐžĐŊ Max ĐˇŅŅ€ŅĐŗ Claude ĐˇĐ°ĐŗĐ˛Đ°Ņ€Ņ‚ ŅˆŅƒŅƒĐ´ Ņ…Đ°ĐŊĐ´Đ°Ņ…",
"dialog.provider.copilot.note": "GitHub Copilot-ŅŅŅ€ даĐŧĐļ҃҃ĐģаĐŊ ĐēОдĐģĐžŅ…ĐžĐ´ Ņ‚ŅƒŅĐģĐ°Ņ… Ņ…Đ¸ĐšĐŧŅĐģ ĐžŅŽŅƒĐŊ ŅƒŅ…Đ°Đ°ĐŊŅ‹ ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´",
"dialog.provider.openai.note": "GPT AI-иКĐŊ Ņ…ŅƒŅ€Đ´Đ°ĐŊ, Ņ‡Đ°Đ´Đ˛Đ°Ņ€Ņ‚Đ°Đš ĐĩŅ€ĶŠĐŊŅ…Đ¸Đš аĐļĐģŅƒŅƒĐ´Đ°Đ´ ĐˇĐžŅ€Đ¸ŅƒĐģŅĐ°ĐŊ ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´",
"dialog.provider.google.note": "Gemini Ņ…ŅƒŅ€Đ´Đ°ĐŊ, ĐąŌ¯Ņ‚ŅŅ†Ņ‚ŅĐš Ņ…Đ°Ņ€Đ¸ŅƒĐģŅ‚ ĶŠĐŗĶŠŅ… ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´",
@@ -211,7 +210,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Ķ¨ĶŠŅ€ 70 ĐŗĐ°Ņ€ŅƒĐš Ō¯ĐšĐģŅ‡Đ¸ĐģĐŗŅŅ Ō¯ĐˇŌ¯Ō¯ĐģŅĐŗŅ‡Đ¸ĐšĐŗ Ņ…Đ°Ņ€ĐŊа ҃҃",
"dialog.provider.viewAll": "ИĐģŌ¯Ō¯ ĐžĐģĐžĐŊ Ō¯ĐšĐģŅ‡Đ¸ĐģĐŗŅŅ Ō¯ĐˇŌ¯Ō¯ĐģŅĐŗŅ‡Đ¸ĐšĐŗ Ņ…Đ°Ņ€ŅƒŅƒĐģĐ°Ņ…",
"provider.connect.title": "ĐĨĐžĐģĐąĐžŅ… {{provider}}",
"provider.connect.title.anthropicProMax": "Claude Pro/Max-Đ°Đ°Ņ€ ĐŊŅĐ˛Ņ‚ŅŅ€ĐŊŅ Ō¯Ō¯",
"provider.connect.selectMethod": "{{provider}}-Đ´ ĐŊŅĐ˛Ņ‚Ņ€ŅŅ… Đ°Ņ€ĐŗŅ‹Đŗ ŅĐžĐŊĐŗĐžĐŊĐž ҃҃.",
"provider.connect.method.apiKey": "API ҂ԝĐģ҅ԝԝҀ",
"provider.connect.method.browser": "ĐĨĶŠŅ‚ĶŠŅ‡",
@@ -378,8 +376,6 @@ export const dict = {
"prompt.attachment.remove": "ĐĨĐ°Đ˛ŅŅ€Đ°ĐģŅ‚Ņ‹Đŗ ŅƒŅŅ‚ĐŗĐ°ĐŊа ҃҃",
"prompt.action.send": "ИĐģĐŗŅŅŅ…",
"prompt.action.stop": "Đ—ĐžĐŗŅ",
"prompt.toast.pasteUnsupported.title": "Đ”ŅĐŧĐļĐ¸ĐŗĐ´ŅŅĐŗŌ¯Đš Ņ…Đ°Đ˛ŅŅ€Đ°ĐģŅ‚",
"prompt.toast.pasteUnsupported.description": "Đ­ĐŊĐ´ ĐˇĶŠĐ˛Ņ…ĶŠĐŊ ĐˇŅƒŅ€Đ°Đŗ, PDFҁ, ŅŅĐ˛ŅĐģ Ņ‚ĐĩĐēҁ҂ Ņ„Đ°ĐšĐģŅ‹Đŗ Ņ…Đ°Đ˛ŅĐ°Ņ€ĐŗĐ°Ņ… йОĐģĐžĐŧĐļŅ‚ĐžĐš.",
"prompt.toast.attachmentDuplicate.title": "Đ­ĐŊŅ Ņ„Đ°ĐšĐģŅ‹Đŗ аĐģҌ Ņ…ŅĐ´Đ¸ĐšĐŊ ĐąĐ°ĐšŅ€ŅˆŅƒŅƒĐģŅĐ°ĐŊ йаКĐŊа",
"prompt.toast.modelAgentRequired.title": "ĐĐŗĐĩĐŊŅ‚ йОĐģĐžĐŊ ĐˇĐ°ĐŗĐ˛Đ°Ņ€Ņ‹Đŗ ŅĐžĐŊĐŗĐžĐŊĐž ҃҃",
"prompt.toast.modelAgentRequired.description": "ĐŸŅ€ĐžĐŧĐŋŅ‚ иĐģĐŗŅŅŅ…ŅŅŅŅŅ ĶŠĐŧĐŊĶŠ Đ°ĐŗĐĩĐŊŅ‚ йОĐģĐžĐŊ ĐˇĐ°ĐŗĐ˛Đ°Ņ€Ņ‹Đŗ ŅĐžĐŊĐŗĐžĐŊĐž ҃҃.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "Model terpilih termasuk Claude, GPT, Gemini dan banyak lagi",
"dialog.provider.opencode.tagline": "Model dioptimumkan yang boleh dipercayai",
"dialog.provider.opencodeGo.tagline": "Langganan kos rendah untuk semua",
"dialog.provider.anthropic.note": "Akses terus ke model Claude, termasuk Pro dan Max",
"dialog.provider.copilot.note": "Model AI untuk bantuan pengekodan melalui GitHub Copilot",
"dialog.provider.openai.note": "Model GPT untuk tugasan AI umum yang pantas dan berkeupayaan",
"dialog.provider.google.note": "Model Gemini untuk respons pantas dan berstruktur",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Lihat 70+ lagi penyedia",
"dialog.provider.viewAll": "Tunjukkan lebih banyak penyedia",
"provider.connect.title": "Sambungkan {{provider}}",
"provider.connect.title.anthropicProMax": "Log masuk dengan Claude Pro/Max",
"provider.connect.selectMethod": "Pilih kaedah log masuk untuk {{provider}}.",
"provider.connect.method.apiKey": "Kunci API",
"provider.connect.method.browser": "Pelayar",
@@ -375,8 +373,6 @@ export const dict = {
"prompt.attachment.remove": "Buang lampiran",
"prompt.action.send": "Hantar",
"prompt.action.stop": "Henti",
"prompt.toast.pasteUnsupported.title": "Lampiran tidak disokong",
"prompt.toast.pasteUnsupported.description": "Hanya imej, PDF, atau fail teks boleh dilampirkan di sini.",
"prompt.toast.attachmentDuplicate.title": "Fail ini telah dimuat naik",
"prompt.toast.modelAgentRequired.title": "Pilih ejen dan model",
"prompt.toast.modelAgentRequired.description": "Pilih ejen dan model sebelum menghantar prompt.",
-5
View File
@@ -193,7 +193,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude၊ GPT၊ Gemini နှင့á€ē အခá€ŧá€Ŧးအရá€Ŧမá€ģá€Ŧး အပá€Ģအဝငá€ē ရá€Ŋေးခá€ģယá€ēထá€Ŧးသေá€Ŧ မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧး",
"dialog.provider.opencode.tagline": "ယုá€ļကá€ŧညá€ēစိတá€ēခá€ģရသေá€Ŧ အကေá€Ŧငá€ēးဆုá€ļးပá€ŧငá€ēဆငá€ēထá€Ŧးသေá€Ŧ မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧး",
"dialog.provider.opencodeGo.tagline": "လူတိုငá€ēးအတá€Ŋကá€ē ကုနá€ēကá€ģစရိတá€ēသကá€ēသá€Ŧသေá€Ŧ စá€Ŧရငá€ēးသá€Ŋငá€ēးမသု",
"dialog.provider.anthropic.note": "Pro နှင့á€ē Max အပá€Ģအဝငá€ē Claude မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧá€¸á€žá€­á€¯á€ˇ တိုကá€ēရိုကá€ēဝငá€ēရေá€Ŧကá€ēခá€Ŋင့á€ē",
"dialog.provider.copilot.note": "GitHub Copilot မှတစá€ēဆင့á€ē ကုဒá€ēရေးနညá€ēးအကူအညီအတá€Ŋကá€ē AI မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧး",
"dialog.provider.openai.note": "လá€ģငá€ēမá€ŧနá€ēပá€ŧီး လုပá€ēဆေá€Ŧငá€ēနိုငá€ēသေá€Ŧ အထá€Ŋေထá€Ŋေ AI လုပá€ēဆေá€Ŧငá€ēခá€ģကá€ēမá€ģá€Ŧးအတá€Ŋကá€ē GPT မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧး",
"dialog.provider.google.note": "မá€ŧနá€ēဆနá€ēပá€ŧီး ဖá€Ŋဲ့စညá€ēးတညá€ēဆေá€Ŧကá€ēထá€Ŧးသေá€Ŧ တုá€ļ့ပá€ŧနá€ēမသုမá€ģá€Ŧးအတá€Ŋကá€ē Gemini မေá€Ŧá€ēဒယá€ēမá€ģá€Ŧး",
@@ -210,7 +209,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "နေá€Ŧကá€ēထပá€ēပá€ļá€ˇá€•á€­á€¯á€¸á€•á€ąá€¸á€žá€° 70+ ကိုကá€ŧည့á€ēပá€Ģ။",
"dialog.provider.viewAll": "နေá€Ŧကá€ēထပá€ēဝနá€ēဆေá€Ŧငá€ēá€™á€žá€¯á€•á€ąá€¸á€žá€°á€™á€ģá€Ŧးကို ပá€ŧပá€Ģ။",
"provider.connect.title": "{{provider}} ကို ခá€ģိတá€ēဆကá€ēပá€Ģ။",
"provider.connect.title.anthropicProMax": "Claude Pro/Max ဖá€ŧင့á€ē ဝငá€ēရေá€Ŧကá€ēပá€Ģ။",
"provider.connect.selectMethod": "{{provider}} အတá€Ŋကá€ē အကေá€Ŧင့á€ēဝငá€ēနညá€ēးလမá€ēးကို ရá€Ŋေးပá€Ģ။",
"provider.connect.method.apiKey": "API သေá€Ŧ့",
"provider.connect.method.browser": "ဘရေá€Ŧကá€ēဆá€Ŧ",
@@ -378,9 +376,6 @@ export const dict = {
"prompt.attachment.remove": "ပူးတá€Ŋဲပá€Ģဖိုငá€ēကို ဖယá€ēရှá€Ŧးပá€Ģ။",
"prompt.action.send": "á€•á€­á€¯á€ˇá€•á€Ģ။",
"prompt.action.stop": "ရပá€ēပá€Ģ။",
"prompt.toast.pasteUnsupported.title": "ပူးတá€Ŋဲပá€Ģဖိုငá€ēကို ပá€ļá€ˇá€•á€­á€¯á€¸á€™á€‘á€Ŧးပá€Ģ။",
"prompt.toast.pasteUnsupported.description":
"ရုပá€ēပုá€ļမá€ģá€Ŧး၊ PDF မá€ģá€Ŧး á€žá€­á€¯á€ˇá€™á€Ÿá€¯á€á€ē စá€Ŧသá€Ŧးဖိုငá€ēမá€ģá€Ŧးကိုသá€Ŧ ဤနေရá€Ŧတá€Ŋငá€ē ပူးတá€Ŋá€˛á€”á€­á€¯á€„á€ēပá€Ģသညá€ē။",
"prompt.toast.attachmentDuplicate.title": "ဤဖိုငá€ēကို အပá€ēလုဒá€ēလုပá€ēပá€ŧီးပá€Ģပá€ŧီ။",
"prompt.toast.modelAgentRequired.title": "အေးဂá€ģင့á€ēနှင့á€ē မေá€Ŧá€ēဒယá€ēကို ရá€Ŋေးပá€Ģ။",
"prompt.toast.modelAgentRequired.description": "Prompt á€™á€•á€­á€¯á€ˇá€™á€Ž အေးဂá€ģင့á€ēနှင့á€ē မေá€Ŧá€ēဒယá€ēကို ရá€Ŋေးပá€Ģ။",
-4
View File
@@ -192,7 +192,6 @@ export const dict: Record<string, string> = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini ⤰ ā¤Ĩā¤Ē ā¤¸ā¤šā¤ŋ⤤ ⤕āĨā¤¯āĨā¤°āĨ‡ā¤Ÿ ⤗⤰ā¤ŋā¤ā¤•ā¤ž ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚",
"dialog.provider.opencode.tagline": "⤭⤰ā¤Ē⤰āĨā¤ĻāĨ‹ ⤅⤍āĨā¤•āĨ‚⤞ā¤ŋ⤤ ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚",
"dialog.provider.opencodeGo.tagline": "⤏ā¤ŦāĨˆā¤•āĨ‹ ā¤˛ā¤žā¤—ā¤ŋ ā¤•ā¤Ž ā¤˛ā¤žā¤—ā¤¤ ⤏ā¤Ļ⤏āĨā¤¯ā¤¤ā¤ž",
"dialog.provider.anthropic.note": "ā¤ĒāĨā¤°āĨ‹ ⤰ ā¤ŽāĨā¤¯ā¤žā¤•āĨā¤¸ ā¤¸ā¤šā¤ŋ⤤ Claude ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚ā¤Žā¤ž ā¤ĒāĨā¤°ā¤¤āĨā¤¯ā¤•āĨā¤ˇ ā¤Ēā¤šāĨā¤ā¤š",
"dialog.provider.copilot.note": "GitHub Copilot ā¤Žā¤žā¤°āĨā¤Ģ⤤ ⤕āĨ‹ā¤Ąā¤ŋ⤙ ā¤¸ā¤šā¤žā¤¯ā¤¤ā¤žā¤•ā¤ž ā¤˛ā¤žā¤—ā¤ŋ AI ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚",
"dialog.provider.openai.note": "⤛ā¤ŋ⤟āĨ‹, ⤏⤕āĨā¤ˇā¤Ž ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ AI ā¤•ā¤žā¤°āĨā¤¯ā¤šā¤°āĨ‚⤕āĨ‹ ā¤˛ā¤žā¤—ā¤ŋ GPT ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚",
"dialog.provider.google.note": "⤛ā¤ŋ⤟āĨ‹, ā¤¸ā¤‚ā¤°ā¤šā¤ŋ⤤ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤šā¤°āĨ‚⤕āĨ‹ ā¤˛ā¤žā¤—ā¤ŋ Gemini ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ā¤šā¤°āĨ‚",
@@ -209,7 +208,6 @@ export const dict: Record<string, string> = {
"dialog.model.unpaid.viewMoreProviders": "70+ ā¤Ĩā¤Ē ā¤ĒāĨā¤°ā¤Ļā¤žā¤¯ā¤•ā¤šā¤°āĨ‚ ā¤šāĨ‡ā¤°āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"dialog.provider.viewAll": "ā¤Ĩā¤Ē ā¤ĒāĨā¤°ā¤Ļā¤žā¤¯ā¤•ā¤šā¤°āĨ‚ ā¤ĻāĨ‡ā¤–ā¤žā¤‰ā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"provider.connect.title": "ā¤œā¤Ąā¤žā¤¨ ⤗⤰āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨ {{provider}}",
"provider.connect.title.anthropicProMax": "Claude Pro/Max ⤏⤁⤗ ⤞⤗⤇⤍ ⤗⤰āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"provider.connect.selectMethod": "{{provider}} ā¤•ā¤ž ā¤˛ā¤žā¤—ā¤ŋ ⤞⤗⤇⤍ ā¤ĩā¤ŋ⤧ā¤ŋ ⤚⤝⤍ ⤗⤰āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨāĨ¤",
"provider.connect.method.apiKey": "API ⤕āĨā¤žāĨā¤œāĨ€",
"provider.connect.method.browser": "ā¤ŦāĨā¤°ā¤žā¤‰ā¤œā¤°",
@@ -376,8 +374,6 @@ export const dict: Record<string, string> = {
"prompt.attachment.remove": "⤏⤂⤞⤗āĨā¤¨ā¤• ā¤šā¤Ÿā¤žā¤‰ā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"prompt.action.send": "ā¤Ēā¤ ā¤žā¤‰ā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"prompt.action.stop": "⤰āĨ‹ā¤•āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"prompt.toast.pasteUnsupported.title": "ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ⤏⤂⤞⤗āĨā¤¨ā¤•",
"prompt.toast.pasteUnsupported.description": "⤕āĨ‡ā¤ĩ⤞ ⤛ā¤ĩā¤ŋā¤šā¤°āĨ‚, PDF ā¤šā¤°āĨ‚, ā¤ĩā¤ž ā¤Ēā¤žā¤  ā¤Ģā¤žā¤‡ā¤˛ā¤šā¤°āĨ‚ ā¤¯ā¤šā¤žā¤ ⤏⤂⤞⤗āĨā¤¨ ⤗⤰āĨā¤¨ ⤏⤕ā¤ŋ⤍āĨā¤›āĨ¤",
"prompt.toast.attachmentDuplicate.title": "⤝āĨ‹ ā¤Ģā¤žā¤‡ā¤˛ ā¤Ēā¤šā¤ŋ⤞āĨ‡ ⤍āĨˆ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤗⤰ā¤ŋā¤ā¤•āĨ‹ ⤛",
"prompt.toast.modelAgentRequired.title": "ā¤ā¤• ā¤ā¤œāĨ‡ā¤¨āĨā¤Ÿ ⤰ ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ ⤚⤝⤍ ⤗⤰āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨ",
"prompt.toast.modelAgentRequired.description": "ā¤ĒāĨā¤°ā¤ŽāĨā¤ĒāĨā¤Ÿ ā¤Ēā¤ ā¤žā¤‰ā¤¨āĨ ā¤…ā¤˜ā¤ŋ ā¤ā¤œāĨ‡ā¤¨āĨā¤Ÿ ⤰ ā¤ŽāĨ‹ā¤ĄāĨ‡ā¤˛ ā¤›ā¤žā¤¨āĨā¤¨āĨā¤šāĨ‹ā¤¸āĨāĨ¤",
-5
View File
@@ -192,7 +192,6 @@ export const dict = {
"dialog.provider.opencode.note": "Samengestelde modellen, waaronder Claude, GPT, Gemini en meer",
"dialog.provider.opencode.tagline": "Betrouwbare geoptimaliseerde modellen",
"dialog.provider.opencodeGo.tagline": "Goedkoop abonnement voor iedereen",
"dialog.provider.anthropic.note": "Directe toegang tot Claude-modellen, inclusief Pro en Max",
"dialog.provider.copilot.note": "AI-modellen voor codeerondersteuning via GitHub Copilot",
"dialog.provider.openai.note": "GPT-modellen voor snelle, capabele algemene AI-taken",
"dialog.provider.google.note": "Gemini-modellen voor snelle, gestructureerde reacties",
@@ -209,7 +208,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Bekijk nog 70+ aanbieders",
"dialog.provider.viewAll": "Toon meer aanbieders",
"provider.connect.title": "Verbinding maken met {{provider}}",
"provider.connect.title.anthropicProMax": "Inloggen met Claude Pro/Max",
"provider.connect.selectMethod": "Selecteer de aanmeldingsmethode voor {{provider}}.",
"provider.connect.method.apiKey": "API-sleutel",
"provider.connect.method.browser": "Browser",
@@ -375,9 +373,6 @@ export const dict = {
"prompt.attachment.remove": "Bijlage verwijderen",
"prompt.action.send": "Verzenden",
"prompt.action.stop": "Stop",
"prompt.toast.pasteUnsupported.title": "Niet-ondersteunde bijlage",
"prompt.toast.pasteUnsupported.description":
"Hier kunnen alleen afbeeldingen, pdf's of tekstbestanden worden bijgevoegd.",
"prompt.toast.attachmentDuplicate.title": "Dit bestand is al geÃŧpload",
"prompt.toast.modelAgentRequired.title": "Selecteer een agent en model",
"prompt.toast.modelAgentRequired.description": "Kies een agent en model voordat je een prompt verzendt.",
-4
View File
@@ -206,7 +206,6 @@ export const dict = {
"dialog.provider.opencode.note": "Utvalgte modeller inkludert Claude, GPT, Gemini og mer",
"dialog.provider.opencode.tagline": "PÃĨlitelige, optimaliserte modeller",
"dialog.provider.opencodeGo.tagline": "Rimelig abonnement for alle",
"dialog.provider.anthropic.note": "Direkte tilgang til Claude-modeller, inkludert Pro og Max",
"dialog.provider.copilot.note": "AI-modeller for kodeassistanse via GitHub Copilot",
"dialog.provider.openai.note": "GPT-modeller for raske, dyktige generelle AI-oppgaver",
"dialog.provider.google.note": "Gemini-modeller for raske, strukturerte svar",
@@ -227,7 +226,6 @@ export const dict = {
"dialog.provider.viewAll": "Vis flere leverandører",
"provider.connect.title": "Koble til {{provider}}",
"provider.connect.title.anthropicProMax": "Logg inn med Claude Pro/Max",
"provider.connect.selectMethod": "Velg innloggingsmetode for {{provider}}.",
"provider.connect.method.apiKey": "API-nøkkel",
"provider.connect.method.browser": "Nettleser",
@@ -393,9 +391,7 @@ export const dict = {
"prompt.action.send": "Send",
"prompt.action.stop": "Stopp",
"prompt.toast.pasteUnsupported.title": "Ikke støttet vedlegg",
"prompt.toast.attachmentDuplicate.title": "Denne filen er allerede lastet opp",
"prompt.toast.pasteUnsupported.description": "Kun bilder, PDF-er eller tekstfiler kan legges ved her.",
"prompt.toast.modelAgentRequired.title": "Velg en agent og modell",
"prompt.toast.modelAgentRequired.description": "Velg en agent og modell før du sender en forespørsel.",
"prompt.toast.worktreeCreateFailed.title": "Kunne ikke opprette worktree",
-5
View File
@@ -198,7 +198,6 @@ export const dict = {
"dialog.provider.opencode.note": "Claude، GPT، Gemini ØĒے ÛŲˆØą ØŗŲ…ÛŒØĒ ÚŠÛŒŲˆØąÛŒŲšÚˆ Ų…Ø§ÚˆŲ„",
"dialog.provider.opencode.tagline": "Ų‚Ø§Ø¨Ų„ اؚØĒŲ…Ø§Ø¯ اØĩŲ„Ø§Ø­ شدہ Ų…Ø§ÚˆŲ„",
"dialog.provider.opencodeGo.tagline": "ÛØą اڊ Ų„ØĻی Ú¯ÚžŲš Ų‚ÛŒŲ…ØĒ دی ØąÚŠŲ†ÛŒØĒ",
"dialog.provider.anthropic.note": "ŲžØąŲˆ ØĒے Ų…ÛŒÚŠØŗ ØŗŲ…ÛŒØĒ Claude Ų…Ø§ÚˆŲ„ ØĒÚŠ Ø¨ØąØ§Û ØąØ§ØŗØĒ ØąØŗØ§ØĻی",
"dialog.provider.copilot.note": "GitHub Copilot دے Ø°ØąÛŒØšÛ’ ÚŠŲˆÚˆŲ†Ú¯ دی Ų…Ø¯Ø¯ Ų„ØĻی اے ØĸØĻی Ų…Ø§ÚˆŲ„",
"dialog.provider.openai.note": "ØĒÛŒØ˛ ØĒے ØĩŲ„Ø§Ø­ÛŒØĒ ŲˆØ§Ų„Û’ ØšØ§Ų… AI ÚŠŲ…Ø§Úē Ų„ØĻی GPT Ų…Ø§ÚˆŲ„",
"dialog.provider.google.note": "ØĒÛŒØ˛ ØĒے Ų…Ų†Ø¸Ų… ØŦŲˆØ§Ø¨Ø§Úē Ų„ØĻی Gemini Ų…Ø§ÚˆŲ„",
@@ -215,7 +214,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "70+ ÛŲˆØą ŲžØąŲˆŲˆØ§ØĻÛŒÚˆØą ŲˆÛŒÚŠÚžŲˆ",
"dialog.provider.viewAll": "ÛŲˆØą ŲžØąŲˆŲˆØ§ØĻÛŒÚˆØą ŲˆÚŠÚžØ§Ø¤",
"provider.connect.title": "{{provider}} Ų†Ø§Ų„ ØŦÚ‘Ųˆ",
"provider.connect.title.anthropicProMax": "Claude ŲžØąŲˆ/Ų…ÛŒÚŠØŗ دے Ų†Ø§Ų„ Ų„Ø§Ú¯ Ø§Ų† ÚŠØąŲˆ",
"provider.connect.selectMethod": "{{provider}} Ų„ØĻی Ų„Ø§Ú¯ Ø§Ų† دا ØˇØąÛŒŲ‚Û Ų…Ų†ØĒ؎ب ÚŠØąŲˆÛ”",
"provider.connect.method.apiKey": "API ÚŠŲ„ÛŒØ¯",
"provider.connect.method.browser": "Ø¨ØąØ§Ø¤Ø˛Øą",
@@ -381,9 +379,6 @@ export const dict = {
"prompt.attachment.remove": "Ų…Ų†ØŗŲ„ÚŠÛ ÛŲšØ§ Ø¯ÛŒŲˆ",
"prompt.action.send": "Ú¯ÚžŲ„Ųˆ",
"prompt.action.stop": "ØąŲˆÚŠŲˆ",
"prompt.toast.pasteUnsupported.title": "ØēÛŒØą ØĒØšØ§ŲˆŲ† ÛŒØ§ŲØĒہ Ų…Ų†ØŗŲ„ÚŠÛ",
"prompt.toast.pasteUnsupported.description":
"ایØĒÚžÛ’ ØĩØąŲ ØĒØĩŲˆÛŒØąØ§Ú稌 ŲžÛŒ ڈی Ø§ÛŒŲØŒ یا ŲšÛŒÚŠØŗŲš ŲØ§ØĻŲ„Ø§Úē Ų…Ų†ØŗŲ„ÚŠ ÚŠÛŒØĒیاÚē ØŦا ØŗÚŠØ¯ÛŒØ§Úē Ų†ÛŒÚē۔",
"prompt.toast.attachmentDuplicate.title": "ایہہ ŲØ§ØĻŲ„ ŲžÛŲ„Û’ ای Ø§Ųž Ų„ŲˆÚˆ ÛŲˆ Ú†ÚŠÛŒ اے",
"prompt.toast.modelAgentRequired.title": "اڊ ایØŦŲ†Ųš ØĒے Ų…Ø§ÚˆŲ„ Ú†Ų†Ųˆ",
"prompt.toast.modelAgentRequired.description": "ŲžØąØ§Ų…ŲžŲš بڞیØŦŲ† ØĒ؈Úē ŲžÛŲ„Ø§Úē Ø§ÚŠ Ø§ÛŒØŦŲ†Ųš ØĒے Ų…Ø§ÚˆŲ„ دا Ø§Ų†ØĒ؎اب ÚŠØąŲˆÛ”",
-4
View File
@@ -199,7 +199,6 @@ export const dict = {
"dialog.provider.opencode.note": "Starannie dobrane modele, w tym Claude, GPT, Gemini i inne",
"dialog.provider.opencode.tagline": "Niezawodne, zoptymalizowane modele",
"dialog.provider.opencodeGo.tagline": "Niedroga subskrypcja dla kaÅŧdego",
"dialog.provider.anthropic.note": "Bezpośredni dostęp do modeli Claude, w tym Pro i Max",
"dialog.provider.copilot.note": "Modele AI do pomocy w kodowaniu przez GitHub Copilot",
"dialog.provider.openai.note": "Modele GPT do szybkich i wszechstronnych zadań AI",
"dialog.provider.google.note": "Modele Gemini do szybkich i ustrukturyzowanych odpowiedzi",
@@ -216,7 +215,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Zobacz ponad 70 innych dostawcÃŗw",
"dialog.provider.viewAll": "Zobacz więcej dostawcÃŗw",
"provider.connect.title": "Połącz {{provider}}",
"provider.connect.title.anthropicProMax": "Zaloguj się za pomocą konta Claude Pro/Max",
"provider.connect.selectMethod": "Wybierz metodę logowania dla {{provider}}.",
"provider.connect.method.apiKey": "Klucz API",
"provider.connect.method.browser": "Przeglądarka",
@@ -382,9 +380,7 @@ export const dict = {
"prompt.attachment.remove": "Usuń załącznik",
"prompt.action.send": "Wyślij",
"prompt.action.stop": "Zatrzymaj",
"prompt.toast.pasteUnsupported.title": "Nieobsługiwany załącznik",
"prompt.toast.attachmentDuplicate.title": "Ten plik został juÅŧ przesłany",
"prompt.toast.pasteUnsupported.description": "MoÅŧna tutaj załączać tylko obrazy, pliki PDF lub pliki tekstowe.",
"prompt.toast.modelAgentRequired.title": "Wybierz agenta i model",
"prompt.toast.modelAgentRequired.description": "Wybierz agenta i model przed wysłaniem zapytania.",
"prompt.toast.worktreeCreateFailed.title": "Nie udało się utworzyć drzewa roboczego",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "Modele selectate, inclusiv Claude, GPT, Gemini și altele",
"dialog.provider.opencode.tagline": "Modele optimizate, fiabile",
"dialog.provider.opencodeGo.tagline": "Abonament accesibil pentru toți",
"dialog.provider.anthropic.note": "Acces direct la modelele Claude, inclusiv Pro și Max",
"dialog.provider.copilot.note": "Modele AI pentru asistență la programare prin GitHub Copilot",
"dialog.provider.openai.note": "Modele GPT pentru sarcini AI generale rapide și performante",
"dialog.provider.google.note": "Modele Gemini pentru răspunsuri rapide și structurate",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "Vezi peste 70 de furnizori",
"dialog.provider.viewAll": "Afișează mai mulți furnizori",
"provider.connect.title": "Conectează {{provider}}",
"provider.connect.title.anthropicProMax": "Autentificare cu Claude Pro/Max",
"provider.connect.selectMethod": "Selectează metoda de autentificare pentru {{provider}}.",
"provider.connect.method.apiKey": "Cheie API",
"provider.connect.method.browser": "Navigator",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "Elimină atașamentul",
"prompt.action.send": "Trimite",
"prompt.action.stop": "Oprește",
"prompt.toast.pasteUnsupported.title": "Atașament neacceptat",
"prompt.toast.pasteUnsupported.description": "Poți atașa doar imagini, PDF-uri sau fișiere text aici.",
"prompt.toast.attachmentDuplicate.title": "Acest fișier a fost deja ÃŽncărcat",
"prompt.toast.modelAgentRequired.title": "Selectează un agent și un model",
"prompt.toast.modelAgentRequired.description": "Alege un agent și un model ÃŽnainte de a trimite un prompt.",
-4
View File
@@ -207,7 +207,6 @@ export const dict = {
"dialog.provider.opencode.note": "ĐžŅ‚ĐžĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐŧОдĐĩĐģи, вĐēĐģŅŽŅ‡Đ°Ņ Claude, GPT, Gemini и Đ´Ņ€ŅƒĐŗĐ¸Đĩ",
"dialog.provider.opencode.tagline": "ĐĐ°Đ´Ņ‘ĐļĐŊŅ‹Đĩ ĐžĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ ĐŧОдĐĩĐģи",
"dialog.provider.opencodeGo.tagline": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊĐ°Ņ ĐŋОдĐŋĐ¸ŅĐēа Đ´ĐģŅ Đ˛ŅĐĩŅ…",
"dialog.provider.anthropic.note": "ĐŸŅ€ŅĐŧОК Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐŧОдĐĩĐģŅĐŧ Claude, вĐēĐģŅŽŅ‡Đ°Ņ Pro и Max",
"dialog.provider.copilot.note": "ИИ-ĐŧОдĐĩĐģи Đ´ĐģŅ ĐŋĐžĐŧĐžŅ‰Đ¸ в ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊии ҇ĐĩŅ€ĐĩС GitHub Copilot",
"dialog.provider.openai.note": "МодĐĩĐģи GPT Đ´ĐģŅ ĐąŅ‹ŅŅ‚Ņ€Ņ‹Ņ… и ĐŧĐžŅ‰ĐŊҋ҅ ĐˇĐ°Đ´Đ°Ņ‡ ĐžĐąŅ‰ĐĩĐŗĐž ИИ",
"dialog.provider.google.note": "МодĐĩĐģи Gemini Đ´ĐģŅ ĐąŅ‹ŅŅ‚Ņ€Ņ‹Ņ… и ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊҋ҅ ĐžŅ‚Đ˛ĐĩŅ‚ĐžĐ˛",
@@ -228,7 +227,6 @@ export const dict = {
"dialog.provider.viewAll": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ йОĐģҌ҈Đĩ ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ĐžĐ˛",
"provider.connect.title": "ПодĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ {{provider}}",
"provider.connect.title.anthropicProMax": "Đ’ĐžĐšŅ‚Đ¸ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ Claude Pro/Max",
"provider.connect.selectMethod": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ҁĐŋĐžŅĐžĐą Đ˛Ņ…ĐžĐ´Đ° Đ´ĐģŅ {{provider}}.",
"provider.connect.method.apiKey": "КĐģŅŽŅ‡ API",
"provider.connect.method.browser": "Đ‘Ņ€Đ°ŅƒĐˇĐĩŅ€",
@@ -401,9 +399,7 @@ export const dict = {
"prompt.action.send": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ",
"prompt.action.stop": "ĐžŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ",
"prompt.toast.pasteUnsupported.title": "НĐĩĐŋОддĐĩŅ€ĐļиваĐĩĐŧĐžĐĩ вĐģĐžĐļĐĩĐŊиĐĩ",
"prompt.toast.attachmentDuplicate.title": "Đ­Ņ‚ĐžŅ‚ Ņ„Đ°ĐšĐģ ҃ĐļĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊ",
"prompt.toast.pasteUnsupported.description": "ЗдĐĩҁҌ ĐŧĐžĐļĐŊĐž ĐŋŅ€Đ¸ĐēŅ€ĐĩĐŋĐ¸Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ, PDF иĐģи Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛Ņ‹Đĩ Ņ„Đ°ĐšĐģŅ‹.",
"prompt.toast.modelAgentRequired.title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚Đ° и ĐŧОдĐĩĐģҌ",
"prompt.toast.modelAgentRequired.description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Đ°ĐŗĐĩĐŊŅ‚Đ° и ĐŧОдĐĩĐģҌ ĐŋĐĩŅ€ĐĩĐ´ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐēОК СаĐŋŅ€ĐžŅĐ°.",
"prompt.toast.worktreeCreateFailed.title": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ worktree",
-4
View File
@@ -191,7 +191,6 @@ export const dict: Record<string, string> = {
"dialog.provider.opencode.note": "Claude, GPT, Gemini āˇƒāˇ„ āļ­āˇ€āļ­āˇŠ āļ¯āˇš āļ‡āļ­āˇ”⎅⎔⎀ ⎃āļ‚āˇ€āˇ˜āļ­ āļ†āļšāˇ˜āļ­āˇ’",
"dialog.provider.opencode.tagline": "āˇ€āˇ’āˇāˇŠāˇ€āˇƒāļąāˇ“āļē āļ´āˇŠāļģ⎁⎃⎊āļ­ āļ¸āˇāļ¯āˇ’āļŊ⎒",
"dialog.provider.opencodeGo.tagline": "āˇƒāˇ‘āļ¸ āļšāˇ™āļąāˇ™āļšāˇ”āļ§āļ¸ āļ…āļŠāˇ” ⎀⎒āļēāļ¯āļ¸āˇŠ āļ¯āˇāļēāļšāļ­āˇŠāˇ€āļēāļšāˇŠ",
"dialog.provider.anthropic.note": "Pro āˇƒāˇ„ Max āļ‡āļ­āˇ”⎅⎔⎀ Claude āļ¸āˇāļ¯āˇ’āļŊ⎒ ⎀⎙āļ­ āˇƒāˇ˜āļĸ⎔ āļ´āˇŠâ€āļģāˇ€āˇšāˇāļē",
"dialog.provider.copilot.note": "GitHub Copilot ⎄āļģāˇ„āˇ āļšāˇšāļ­āˇ“āļšāļģāļĢ āˇƒāˇ„āˇāļē ⎃āļŗāˇ„āˇ AI āļ†āļšāˇ˜āļ­āˇ’",
"dialog.provider.openai.note": "āˇ€āˇšāļœāˇ€āļ­āˇŠ, ⎄⎐āļšāˇ’āļēāˇāˇ€ āļ‡āļ­āˇ’ āˇƒāˇāļ¸āˇāļąāˇŠâ€āļē AI āļšāˇāļģ⎊āļēāļēāļąāˇŠ ⎃āļŗāˇ„āˇ GPT āļ¸āˇāļ¯āˇ’āļŊ⎒",
"dialog.provider.google.note": "āˇ€āˇšāļœāˇ€āļ­āˇŠ, āˇ€āˇŠâ€āļē⎔⎄āļœāļ­ āļ´āˇŠâ€āļģāļ­āˇ’āļ āˇāļģ ⎃āļŗāˇ„āˇ Gemini āļ†āļšāˇ˜āļ­āˇ’",
@@ -208,7 +207,6 @@ export const dict: Record<string, string> = {
"dialog.model.unpaid.viewMoreProviders": "āļ­āˇ€āļ­āˇŠ ⎃āļ´āļēāļąāˇŠāļąāļąāˇŠ 70+ āļļāļŊāļąāˇŠāļą",
"dialog.provider.viewAll": "āļ­āˇ€āļ­āˇŠ ⎃āļ´āļēāļąāˇŠāļąāļąāˇŠ āļ´āˇ™āļąāˇŠāˇ€āļąāˇŠāļą",
"provider.connect.title": "{{provider}} ⎃āļ¸āˇŠāļļāļąāˇŠāļ° āļšāļģāļąāˇŠāļą",
"provider.connect.title.anthropicProMax": "Claude Pro/Max ⎃āļ¸āļŸāˇ’āļąāˇŠ āļ´āˇ”āļģāļąāļē ⎀āļąāˇŠāļą",
"provider.connect.selectMethod": "{{provider}} ⎃āļŗāˇ„āˇ āļ´āˇ’āˇ€āˇ’āˇƒāˇ”āļ¸āˇŠ āļšāˇŠâ€āļģāļ¸āļē āļ­āˇāļģāļąāˇŠāļą.",
"provider.connect.method.apiKey": "API āļēāļ­āˇ”āļģ",
"provider.connect.method.browser": "āļļ⎊āļģāˇ€āˇ”āˇƒāļģāļē",
@@ -374,8 +372,6 @@ export const dict: Record<string, string> = {
"prompt.attachment.remove": "āļ‡āļ¸āˇ”āļĢ⎔āļ¸ āļ‰āˇ€āļ­āˇŠ āļšāļģāļąāˇŠāļą",
"prompt.action.send": "āļē⎀āļąāˇŠāļą",
"prompt.action.stop": "āļąāˇ€āļ­āˇŠāˇ€āļąāˇŠāļą",
"prompt.toast.pasteUnsupported.title": "āˇƒāˇ„āˇāļē āļąāˇœāļ¯āļšāˇŠāˇ€āļą āļ‡āļ¸āˇ”āļĢ⎔āļ¸",
"prompt.toast.pasteUnsupported.description": "āļ´āˇ’āļąāˇŠāļ­āˇ–āļģ, PDF āˇ„āˇ āļ´āˇ™āˇ… āļœāˇœāļąāˇ” āļ´āļ¸āļĢāļšāˇŠ āļ¸āˇ™āˇ„⎒ āļ‡āļ¸āˇ’āļĢ⎒āļē ⎄⎐āļš.",
"prompt.toast.attachmentDuplicate.title": "āļ¸āˇ™āļ¸ āļœāˇœāļąāˇ”⎀ āļ¯āˇāļąāļ§āļ¸āļ­āˇŠ āļ‹āļŠāˇ”āļœāļ­ āļšāļģ āļ‡āļ­",
"prompt.toast.modelAgentRequired.title": "āļąāˇ’āļēāˇāļĸ⎒āļ­āļē⎙āļšāˇ” āˇƒāˇ„ āļ†āļšāˇ˜āļ­āˇ’āļēāļšāˇŠ āļ­āˇāļģāļąāˇŠāļą",
"prompt.toast.modelAgentRequired.description": "āļ´āˇŠâ€āļģ⎜āļ¸āˇŠāļ´āˇŠāļ§āˇŠ āļ‘āļšāļšāˇŠ āļē⎐⎀⎓āļ¸āļ§ āļ´āˇ™āļģ āļąāˇ’āļēāˇāļĸ⎒āļ­āļē⎙āļšāˇ” āˇƒāˇ„ āļ†āļšāˇ˜āļ­āˇ’āļēāļšāˇŠ āļ­āˇāļģāļąāˇŠāļą.",
-4
View File
@@ -191,7 +191,6 @@ export const dict = {
"dialog.provider.opencode.note": "VybranÊ modely vrÃĄtane Claude, GPT, Gemini a ďalÅĄÃ­ch",
"dialog.provider.opencode.tagline": "SpoĞahlivÊ optimalizovanÊ modely",
"dialog.provider.opencodeGo.tagline": "NízkonÃĄkladovÊ predplatnÊ pre kaÅždÊho",
"dialog.provider.anthropic.note": "Priamy prístup k modelom Claude vrÃĄtane Pro a Max",
"dialog.provider.copilot.note": "AI modely na asistenciu pri kÃŗdovaní cez GitHub Copilot",
"dialog.provider.openai.note": "GPT modely na rÃŊchle a vÃŊkonnÊ vÅĄeobecnÊ AI Ãēlohy",
"dialog.provider.google.note": "Gemini modely na rÃŊchle, ÅĄtruktÃērovanÊ odpovede",
@@ -208,7 +207,6 @@ export const dict = {
"dialog.model.unpaid.viewMoreProviders": "ZobraziÅĨ viac ako 70 ďalÅĄÃ­ch poskytovateÄžov",
"dialog.provider.viewAll": "ZobraziÅĨ viac poskytovateÄžov",
"provider.connect.title": "PripojiÅĨ {{provider}}",
"provider.connect.title.anthropicProMax": "PrihlÃĄsiÅĨ sa cez Claude Pro/Max",
"provider.connect.selectMethod": "Vyberte spôsob prihlÃĄsenia pre {{provider}}.",
"provider.connect.method.apiKey": "API kÄžÃēč",
"provider.connect.method.browser": "Prehliadač",
@@ -374,8 +372,6 @@ export const dict = {
"prompt.attachment.remove": "OdstrÃĄniÅĨ prílohu",
"prompt.action.send": "OdoslaÅĨ",
"prompt.action.stop": "ZastaviÅĨ",
"prompt.toast.pasteUnsupported.title": "NepodporovanÃĄ príloha",
"prompt.toast.pasteUnsupported.description": "PripojiÅĨ moÅžno len obrÃĄzky, PDF alebo textovÊ sÃēbory.",
"prompt.toast.attachmentDuplicate.title": "Tento sÃēbor uÅž bol nahranÃŊ",
"prompt.toast.modelAgentRequired.title": "Vyberte agenta a model",
"prompt.toast.modelAgentRequired.description": "Pred odoslaním vÃŊzvy vyberte agenta a model.",

Some files were not shown because too many files have changed in this diff Show More