Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 414bd3e668 | |||
| 5d8c48718b | |||
| 41c5853739 | |||
| 8d989f6829 | |||
| 753fa4cd60 | |||
| d99a268ea5 | |||
| 1113adfd5e | |||
| 6afff5a55f | |||
| 71f5e4189d | |||
| b49bad9a86 | |||
| 2580f880a8 | |||
| 9d34029cd9 | |||
| 33296e7959 | |||
| 283258e95b | |||
| d7a7256bb6 | |||
| eec55fa5f8 | |||
| 7ff27ca8cf | |||
| b62dc4a636 | |||
| ceec9f5b66 | |||
| bbb1b5e7d0 | |||
| f0b8ad1242 | |||
| be53e3bd81 | |||
| c22942c1f3 | |||
| 6895728add | |||
| 16aad9e6ad | |||
| 9ab3029947 | |||
| 3c9f857bf1 | |||
| 5e16a5dc66 | |||
| c7852ef0fd | |||
| 60cd82aec8 | |||
| c29968bf08 | |||
| b5b87deaa2 | |||
| 5053c1bfdd | |||
| d97aca139f | |||
| b2f0f45c9e | |||
| f64b024f46 | |||
| ec11d6ed03 | |||
| 0dcedc199c | |||
| e9c55a15d4 | |||
| 9bc4eea79a | |||
| 68b2b08853 | |||
| 7bd6d002bb | |||
| ab872ffb0e |
@@ -281,7 +281,6 @@ jobs:
|
||||
|
||||
build-electron:
|
||||
needs:
|
||||
- build-cli
|
||||
- version
|
||||
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2'
|
||||
continue-on-error: false
|
||||
@@ -378,16 +377,14 @@ jobs:
|
||||
env:
|
||||
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
OPENCODE_CLI_ARTIFACT: ${{ (runner.os == 'Windows' && 'opencode-cli-windows') || 'opencode-cli' }}
|
||||
RUST_TARGET: ${{ matrix.settings.target }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
working-directory: packages/desktop
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
|
||||
@@ -405,8 +402,7 @@ jobs:
|
||||
env:
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
CSC_KEYCHAIN: build.keychain
|
||||
APPLE_API_KEY: ${{ runner.temp }}/apple-api-key.p8
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: rtl-aware-development
|
||||
description: OpenCode Desktop should be RTL-aware. Use when implementing or reviewing RTL/LTR behavior in the web app, desktop app, CSS, menus, scrolling, resizing, icons, mixed-direction text, or Electron title bars.
|
||||
---
|
||||
|
||||
# RTL-Aware Development
|
||||
|
||||
Treat direction as independent from language. Test English in both directions as well as real RTL and mixed-script content.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Set `lang` and `dir` on the document, and propagate direction through component providers used by portaled menus and popovers. Do not change the selected locale merely to force RTL.
|
||||
- Keep DOM and focus order semantic. Flexbox and Grid already follow `dir`; do not add `row-reverse`, CSS `order`, or reversed markup just to mirror a layout.
|
||||
- Prefer logical CSS for semantic layout. Reserve physical coordinates for pointer positions, canvas geometry, native window controls, and other genuinely physical placement.
|
||||
|
||||
```css
|
||||
/* Avoid */
|
||||
padding-left: 12px;
|
||||
right: 0;
|
||||
border-right: 1px solid;
|
||||
text-align: left;
|
||||
|
||||
/* Prefer */
|
||||
padding-inline-start: 12px;
|
||||
inset-inline-end: 0;
|
||||
border-inline-end: 1px solid;
|
||||
text-align: start;
|
||||
```
|
||||
|
||||
- Isolate mixed-direction text. Use `dir="auto"` or `<bdi>` for unknown text; keep code, URLs, IDs, and filesystem paths LTR without forcing the surrounding component LTR.
|
||||
|
||||
```html
|
||||
<span class="file-row"><bdi dir="auto">README.md</bdi></span> <bdi dir="ltr"><code>C:\src\app.ts</code></bdi>
|
||||
```
|
||||
|
||||
- Mirror directional meaning, not every image. Back/forward, previous/next, disclosure, indentation, and directional progress may need mirroring. Do not mirror brands, clocks, media controls, charts, or text. Reverse physical gradients, `translateX`, SVG transforms, and animation deltas explicitly.
|
||||
- Map interactions through direction. `clientX` remains physical; resizing a logical edge needs an RTL-aware delta. Logical previous/next keyboard controls may swap ArrowLeft/ArrowRight. Follow the relevant WAI-ARIA widget pattern.
|
||||
- Do not assume LTR scrolling. RTL `scrollLeft` can start at `0` and become negative. Prefer `scrollIntoView({ inline: "nearest" })` or a tested direction-normalizing helper.
|
||||
- For Electron title bars, prefer native caption controls and use `titleBarOverlay` plus `env(titlebar-area-*)` for the safe content rectangle. Keep Windows/macOS native-control avoidance and `trafficLightPosition` physical; keep app navigation inside that rectangle logical. Mark interactive titlebar children `app-region: no-drag`.
|
||||
- Verify behavior, not screenshots alone. Check computed styles, pseudo-element geometry, hit zones, focus order, keyboard behavior, submenu direction, zoom/scaling, and both LTR and RTL scroll endpoints.
|
||||
|
||||
## Test Matrix
|
||||
|
||||
- English + LTR
|
||||
- English + forced RTL
|
||||
- A real RTL locale + RTL
|
||||
- Mixed RTL/LTR content, long labels, numbers, code, and paths
|
||||
- Keyboard, pointer resize, scrolling, menus/submenus, and Electron titlebar controls in both directions
|
||||
|
||||
## References
|
||||
|
||||
- [RTL Styling 101, Ahmad Shadeed](https://rtlstyling.com/posts/rtl-styling/)
|
||||
- [CSS-Tricks: RTL Styling 101](https://css-tricks.com/rtl-styling-101/)
|
||||
- [CSS-Tricks: CSS Logical Properties and Values](https://css-tricks.com/css-logical-properties-and-values/)
|
||||
- [W3C: Structural markup and right-to-left text](https://www.w3.org/International/questions/qa-html-dir)
|
||||
- [W3C: Inline bidirectional markup](https://www.w3.org/International/articles/inline-bidi-markup/)
|
||||
- [MDN: CSS logical properties and values](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Logical_properties_and_values)
|
||||
- [MDN: `dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir)
|
||||
- [MDN: `scrollLeft`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft)
|
||||
- [web.dev: Logical properties](https://web.dev/learn/css/logical-properties/)
|
||||
- [Electron: Custom title bar](https://www.electronjs.org/docs/latest/tutorial/custom-title-bar)
|
||||
- [WAI-ARIA: Window splitter pattern](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/)
|
||||
- [Kobalte: I18n Provider](https://kobalte.dev/docs/core/components/i18n-provider/)
|
||||
@@ -252,33 +252,6 @@ These are not strictly enforced, they are just general guidelines:
|
||||
|
||||
For net-new functionality, start with a design conversation. Open an issue describing the problem, your proposed approach (optional), and why it belongs in OpenCode. The core team will help decide whether it should move forward; please wait for that approval instead of opening a feature PR directly.
|
||||
|
||||
## Trust & Vouch System
|
||||
|
||||
This project uses [vouch](https://github.com/mitchellh/vouch) to manage contributor trust. The vouch list is maintained in [`.github/VOUCHED.td`](.github/VOUCHED.td).
|
||||
|
||||
### How it works
|
||||
|
||||
- **Vouched users** are explicitly trusted contributors.
|
||||
- **Denounced users** are explicitly blocked. Issues and pull requests from denounced users are automatically closed. If you have been denounced, you can request to be unvouched by reaching out to a maintainer on [Discord](https://opencode.ai/discord)
|
||||
- **Everyone else** can participate normally — you don't need to be vouched to open issues or PRs.
|
||||
|
||||
### For maintainers
|
||||
|
||||
Collaborators with write access can manage the vouch list by commenting on any issue:
|
||||
|
||||
- `vouch` — vouch for the issue author
|
||||
- `vouch @username` — vouch for a specific user
|
||||
- `denounce` — denounce the issue author
|
||||
- `denounce @username` — denounce a specific user
|
||||
- `denounce @username <reason>` — denounce with a reason
|
||||
- `unvouch` / `unvouch @username` — remove someone from the list
|
||||
|
||||
Changes are committed automatically to `.github/VOUCHED.td`.
|
||||
|
||||
### Denouncement policy
|
||||
|
||||
Denouncement is reserved for users who repeatedly submit low-quality AI-generated contributions, spam, or otherwise act in bad faith. It is not used for disagreements or honest mistakes.
|
||||
|
||||
## Issue Requirements
|
||||
|
||||
All issues **must** use one of our issue templates:
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -220,7 +220,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -256,7 +256,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -283,7 +283,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -300,7 +300,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -324,7 +324,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -388,7 +388,7 @@
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "3.1.0",
|
||||
"gitlab-ai-provider": "6.11.1",
|
||||
"gitlab-ai-provider": "6.12.1",
|
||||
"google-auth-library": "10.5.0",
|
||||
"gray-matter": "4.0.3",
|
||||
"htmlparser2": "8.0.2",
|
||||
@@ -424,16 +424,16 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"electron-context-menu": "4.1.2",
|
||||
"electron-log": "^5",
|
||||
"electron-store": "11.0.2",
|
||||
"electron-updater": "6.8.9",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"marked": "^15",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/artifact": "4.0.0",
|
||||
@@ -478,7 +478,7 @@
|
||||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -492,7 +492,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -525,7 +525,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -541,7 +541,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@effect/platform-node-shared": "4.0.0-beta.101",
|
||||
},
|
||||
@@ -577,6 +577,7 @@
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
"entities": "7.0.1",
|
||||
"string-width": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -587,7 +588,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/ai": "workspace:*",
|
||||
@@ -612,9 +613,9 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opentui/core": ">=0.4.5",
|
||||
"@opentui/keymap": ">=0.4.5",
|
||||
"@opentui/solid": ">=0.4.5",
|
||||
"@opentui/core": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/solid": "0.0.0-20260808-9ecf7c0a",
|
||||
"solid-js": ">=1.9.0",
|
||||
},
|
||||
"optionalPeers": [
|
||||
@@ -703,7 +704,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/client": "workspace:*",
|
||||
@@ -718,15 +719,11 @@
|
||||
"@solid-primitives/media": "2.3.3",
|
||||
"@solid-primitives/resize-observer": "2.1.3",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@solidjs/router": "catalog:",
|
||||
"diff": "catalog:",
|
||||
"dompurify": "3.3.1",
|
||||
"fuzzysort": "catalog:",
|
||||
"katex": "0.16.27",
|
||||
"luxon": "catalog:",
|
||||
"marked": "catalog:",
|
||||
"marked-katex-extension": "5.1.6",
|
||||
"marked-shiki": "catalog:",
|
||||
"morphdom": "2.7.8",
|
||||
"motion": "12.34.5",
|
||||
"remeda": "catalog:",
|
||||
@@ -739,7 +736,6 @@
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/katex": "0.16.7",
|
||||
"@types/luxon": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -770,7 +766,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "1.18.5",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -783,7 +779,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -817,7 +813,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -836,7 +832,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -907,7 +903,6 @@
|
||||
"@opentui/keymap": "catalog:",
|
||||
"@opentui/solid": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
"clipboardy": "4.0.0",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
"get-east-asian-width": "catalog:",
|
||||
@@ -927,7 +922,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -943,7 +938,6 @@
|
||||
"katex": "0.16.27",
|
||||
"luxon": "catalog:",
|
||||
"marked": "catalog:",
|
||||
"marked-katex-extension": "5.1.6",
|
||||
"marked-shiki": "catalog:",
|
||||
"morphdom": "2.7.8",
|
||||
"motion": "12.34.5",
|
||||
@@ -953,6 +947,7 @@
|
||||
"remend": "catalog:",
|
||||
"shiki": "catalog:",
|
||||
"solid-list": "catalog:",
|
||||
"solid-sonner": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1024,7 +1019,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1079,21 +1074,23 @@
|
||||
"tree-sitter-bash",
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
|
||||
"effect@4.0.0-beta.101": "patches/effect@4.0.0-beta.101.patch",
|
||||
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
|
||||
"@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch",
|
||||
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
"@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch",
|
||||
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
|
||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
|
||||
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
|
||||
"@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@opentui/core": "catalog:",
|
||||
"@opentui/keymap": "catalog:",
|
||||
"@opentui/solid": "catalog:",
|
||||
"@opentui/core": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/solid": "0.0.0-20260808-9ecf7c0a",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -1111,9 +1108,9 @@
|
||||
"@npmcli/arborist": "9.4.0",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opentui/core": "0.4.5",
|
||||
"@opentui/keymap": "0.4.5",
|
||||
"@opentui/solid": "0.4.5",
|
||||
"@opentui/core": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/solid": "0.0.0-20260808-9ecf7c0a",
|
||||
"@pierre/diffs": "1.2.10",
|
||||
"@playwright/test": "1.59.1",
|
||||
"@sentry/solid": "10.36.0",
|
||||
@@ -1146,7 +1143,7 @@
|
||||
"hono": "4.10.7",
|
||||
"hono-openapi": "1.1.2",
|
||||
"luxon": "3.6.1",
|
||||
"marked": "17.0.6",
|
||||
"marked": "18.0.7",
|
||||
"marked-shiki": "1.2.1",
|
||||
"opentui-spinner": "0.0.7",
|
||||
"remeda": "2.26.0",
|
||||
@@ -1156,6 +1153,7 @@
|
||||
"shiki": "4.2.0",
|
||||
"solid-js": "1.9.10",
|
||||
"solid-list": "0.3.0",
|
||||
"solid-sonner": "0.3.1",
|
||||
"sst": "4.13.1",
|
||||
"string-width": "7.2.0",
|
||||
"tailwindcss": "4.1.11",
|
||||
@@ -2170,27 +2168,27 @@
|
||||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.4.5", "", { "dependencies": { "bun-ffi-structs": "0.2.4", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.4.5", "@opentui/core-darwin-x64": "0.4.5", "@opentui/core-linux-arm64": "0.4.5", "@opentui/core-linux-arm64-musl": "0.4.5", "@opentui/core-linux-x64": "0.4.5", "@opentui/core-linux-x64-musl": "0.4.5", "@opentui/core-win32-arm64": "0.4.5", "@opentui/core-win32-x64": "0.4.5" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-JsgRTPkA6e+Vxmumxai6SElOSlRQkbzNKHlCfemlArRiLhfC1IZ9RXJo2QH4xSu+uBOWAM90uss73/pPlkdEig=="],
|
||||
"@opentui/core": ["@opentui/core@0.0.0-20260808-9ecf7c0a", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.0.0-20260808-9ecf7c0a", "@opentui/core-darwin-x64": "0.0.0-20260808-9ecf7c0a", "@opentui/core-linux-arm64": "0.0.0-20260808-9ecf7c0a", "@opentui/core-linux-arm64-musl": "0.0.0-20260808-9ecf7c0a", "@opentui/core-linux-x64": "0.0.0-20260808-9ecf7c0a", "@opentui/core-linux-x64-musl": "0.0.0-20260808-9ecf7c0a", "@opentui/core-win32-arm64": "0.0.0-20260808-9ecf7c0a", "@opentui/core-win32-x64": "0.0.0-20260808-9ecf7c0a" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-y9MKL8LMup4ebW3uZ57RSsPyOv37sIZxuWiHq4XJK9wrzpzTMnVomzxpWOI0S/QtLH2QzZ7znnbF+IuvIQ1hLQ=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.4.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8KUG0oRidnR+oW1RSZJ72/PhZLl+qRRMk5U/mieF4c0SJ5V3tYACpBZAKzQfHNd1f7QzD8FHZct1lPpQgtmkWg=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20260808-9ecf7c0a", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Bn6fUZrwbUojkJG4YHowg/h/u3SPANmEivrl8YVbxBPCYOhZzQWxAVf7Fwg6PD0BpWk5gt9inQrir2DzWh5UpA=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.4.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-R2bocsg55gwjOqCp/MWFgFYzRmsduKegB6nzgFAPCvAD/L5Jf30xpWJWFlSg3x8vxe1L9WJ84dfqa4M7mZZ3wA=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20260808-9ecf7c0a", "", { "os": "darwin", "cpu": "x64" }, "sha512-lrVskzbjcT9yJCUgjOpvPpEq2ipmuDyHTMJ+OebsJFdATdryJLzRAr5BZiFGXEo4CJ2CS/+6vT+ki7dmhpM+0g=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.4.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-R4MZ25a4CzOAGVjW9aj1hUfzQGVfCJwrwBDbNs2SXaIvzcZqkxCVtU4FoQ5LsaD0j/BdNQVg2CIfFkFsm1fDuQ=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20260808-9ecf7c0a", "", { "os": "linux", "cpu": "arm64" }, "sha512-czqIqdnBvyFRSJ0BEdNn7moRN0KQo5gvhSsOSfrzueHxsktgWcmk1cMeCvTkzC3JCBvCwlB1KnSdt4jlwNO1EQ=="],
|
||||
|
||||
"@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.4.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ieqdyKI6EIYPalYAETB2wsdP83hr5Ifi+dFnBFUmdEEFHsoKwBmn2S7bsTOYlX7Bg03F4/YPIg+IvRpeC+cUJw=="],
|
||||
"@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.0.0-20260808-9ecf7c0a", "", { "os": "linux", "cpu": "arm64" }, "sha512-6nixv6wlgjelUVJVHkJsCpuARb1rE/oxhIIthyLd062u5veoBt83cRGH9KmcZiwowKvso58hXn2KC21qAfylhQ=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.4.5", "", { "os": "linux", "cpu": "x64" }, "sha512-SNyuQoxMKI1vuJhgxSSW96adWM6LqFl2SoS3GM4tGeneGOanVVG2Y06PvlytXvF4cKik97t0rqkVMRetmOs93w=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20260808-9ecf7c0a", "", { "os": "linux", "cpu": "x64" }, "sha512-cxJMfrSsMsrZdU1Ke6awrxz0rdIJuI34l8Le3QlUbM5yLpp09ygJ7CElILMT50g3F+EnRf6BKvVmkbRrDzFQ6g=="],
|
||||
|
||||
"@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.4.5", "", { "os": "linux", "cpu": "x64" }, "sha512-mKVKcIcPiSVVZZsdPSBoWwoa2/TCeQAaMDeHF7PFw2kt5bTXZPP7xxWfRQLCNIcA1eaGl59UuwUWHDR2Ve548Q=="],
|
||||
"@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.0.0-20260808-9ecf7c0a", "", { "os": "linux", "cpu": "x64" }, "sha512-7zVbZ0XgCDi3yImmd9Cj+e0WhSuQzWo1ziSQiFgPCR8cs/dfrpYLgBlX9PP0gFOcJKeRccJJv7rnBetl3CB7tg=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.4.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-GHTTsqeR45q2Iek9Rb7ty+x/hAKn2jZ1ujlCgPR8LBKyF7h0E1dNFryoZ7ehMc3kJndP1sKn836IemKFqxuDdQ=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20260808-9ecf7c0a", "", { "os": "win32", "cpu": "arm64" }, "sha512-K3SNYJDvB7XD5nxi8jt4jWtgmqPG0Qbob0Px9afqMH3g/R5hIoi2P3GDbqn8JNv82c4dPIGJjXXuNhHa0tZQbw=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.4.5", "", { "os": "win32", "cpu": "x64" }, "sha512-Y8T/yXCDGagRGiQrtmuB6AhRcPucKFs/Dre3v8kJwNYqDccI4FzUPKclZ7djfmRZNjl7JUqPhZZP/PwDpQocMg=="],
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20260808-9ecf7c0a", "", { "os": "win32", "cpu": "x64" }, "sha512-3JCpPS8+Gz3PWlQHiFwqYuJ0AM0vKlfbe4+O+0m2EYej/QjN5KjL1nBfVLq5XDj4dVPEIktQKN6e//YzSzdaqA=="],
|
||||
|
||||
"@opentui/keymap": ["@opentui/keymap@0.4.5", "", { "dependencies": { "@opentui/core": "0.4.5" }, "peerDependencies": { "@opentui/react": "0.4.5", "@opentui/solid": "0.4.5", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-S1wzKHhF70zT6bH+VBFY+lSeTImLcIFW28JNQiME8MoPcy6KGPs7rKFSHrb/U7P8rsTJeRfW5A4d1Cy6PKodDg=="],
|
||||
"@opentui/keymap": ["@opentui/keymap@0.0.0-20260808-9ecf7c0a", "", { "dependencies": { "@opentui/core": "0.0.0-20260808-9ecf7c0a" }, "peerDependencies": { "@opentui/react": "0.0.0-20260808-9ecf7c0a", "@opentui/solid": "0.0.0-20260808-9ecf7c0a", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-aGxw6P0RJSuUU3y3QtAnKa1B6tQShxR0mjmnN3Cadr4LoduSuWn/l1kE+27KwH0ap9WTAeGVsN7u2oawB9eWKw=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.4.5", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.4.5", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-B0RSkXnrtPVfEJOX+Hj+axjLJ3lzbG1BZw5I7Pvb9OPp48Vzg2cW2a3cSa86/q48ndLt647i/XwFPIw/jqnI5g=="],
|
||||
"@opentui/solid": ["@opentui/solid@0.0.0-20260808-9ecf7c0a", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20260808-9ecf7c0a", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-Q7ouv2KxZKO0/Sh0RbhNd7kEw6E7tlrU+yw36xxlnyXOqlK3TkQ+u1RMSZulYAn8mP20pnYLM9OxY4pOC/wK8Q=="],
|
||||
|
||||
"@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="],
|
||||
|
||||
@@ -3540,7 +3538,7 @@
|
||||
|
||||
"builder-util-runtime": ["builder-util-runtime@9.7.0", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw=="],
|
||||
|
||||
"bun-ffi-structs": ["bun-ffi-structs@0.2.4", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-AJzsqoVFs1KBbJbWHIYrVZLDC3NhTqqh25awRXqzoLzmBAKr5oqk6+CwuYHAekKx+VBCYVohBoKuRq40dV+TYg=="],
|
||||
"bun-ffi-structs": ["bun-ffi-structs@0.3.1", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-3gM7PpVWLyrwxWjcilSiGuhWanhZivvo6l0u573NziPH6f/gwk6McbaYgn7oJWov6pKGRTDbrg94W5DcJsKTtQ=="],
|
||||
|
||||
"bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="],
|
||||
|
||||
@@ -3624,8 +3622,6 @@
|
||||
|
||||
"cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="],
|
||||
|
||||
"clipboardy": ["clipboardy@4.0.0", "", { "dependencies": { "execa": "^8.0.1", "is-wsl": "^3.1.0", "is64bit": "^2.0.0" } }, "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
|
||||
@@ -3654,7 +3650,7 @@
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="],
|
||||
|
||||
@@ -4042,7 +4038,7 @@
|
||||
|
||||
"eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="],
|
||||
|
||||
"execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
||||
"execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
|
||||
|
||||
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
||||
|
||||
@@ -4192,7 +4188,7 @@
|
||||
|
||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||
|
||||
"gitlab-ai-provider": ["gitlab-ai-provider@6.11.1", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-SJ6f5qa7P8md6lPrserryER3zerLkrezlnqqYQ2AbvDPpHLbwtbyk0FYJ5kNRcmbI80i/VMcsMBP0YIRdc3ucQ=="],
|
||||
"gitlab-ai-provider": ["gitlab-ai-provider@6.12.1", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-Qn5iHqvjG8yktI5MWaUgdRR94l7O4WtYW0CAbhsCh1Tj0Fei/DeprOYPVyf4Nht1Ix6U2PXSYM32QOHI6Z2TDw=="],
|
||||
|
||||
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
|
||||
|
||||
@@ -4324,7 +4320,7 @@
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
|
||||
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
|
||||
|
||||
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
|
||||
|
||||
@@ -4468,8 +4464,6 @@
|
||||
|
||||
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
|
||||
|
||||
"is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="],
|
||||
|
||||
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||
|
||||
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
|
||||
@@ -4666,9 +4660,7 @@
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="],
|
||||
|
||||
"marked-katex-extension": ["marked-katex-extension@5.1.6", "", { "peerDependencies": { "katex": ">=0.16 <0.17", "marked": ">=4 <18" } }, "sha512-vYpLXwmlIDKILIhJtiRTgdyZRn5sEYdFBuTmbpjD7lbCIzg0/DWyK3HXIntN3Tp8zV6hvOUgpZNLWRCgWVc24A=="],
|
||||
"marked": ["marked@18.0.7", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-iDVQ5ldaiKXn6b2JroX5kgRfmwgqolW7NpaEzTl1k/2Zh1njIEN9yniyLV/mOvWwtsE8OGgkjsCYvijuPk1dtA=="],
|
||||
|
||||
"marked-shiki": ["marked-shiki@1.2.1", "", { "peerDependencies": { "marked": ">=7.0.0", "shiki": ">=1.0.0" } }, "sha512-yHxYQhPY5oYaIRnROn98foKhuClark7M373/VpLxiy5TrDu9Jd/LsMwo8w+U91Up4oDb9IXFrP0N1MFRz8W/DQ=="],
|
||||
|
||||
@@ -4810,7 +4802,7 @@
|
||||
|
||||
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
|
||||
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||
|
||||
@@ -4936,7 +4928,7 @@
|
||||
|
||||
"npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" } }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="],
|
||||
|
||||
"npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
|
||||
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
@@ -4962,7 +4954,7 @@
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
|
||||
|
||||
@@ -5468,6 +5460,8 @@
|
||||
|
||||
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
|
||||
|
||||
"solid-sonner": ["solid-sonner@0.3.1", "", { "peerDependencies": { "solid-js": "^1.6.0" } }, "sha512-F/+zi9yKJTHh5hX1UGJfkDvyC+F34Vi3jgy44NJwOKCgic1QtAon0b1iT9OsDO77RTgR+PCil+3Y5B8T2Owy1Q=="],
|
||||
|
||||
"solid-stripe": ["solid-stripe@0.8.1", "", { "peerDependencies": { "@stripe/stripe-js": ">=1.44.1 <8.0.0", "solid-js": "^1.6.0" } }, "sha512-l2SkWoe51rsvk9u1ILBRWyCHODZebChSGMR6zHYJTivTRC0XWrRnNNKs5x1PYXsaIU71KYI6ov5CZB5cOtGLWw=="],
|
||||
|
||||
"solid-transition-size": ["solid-transition-size@0.1.4", "", { "dependencies": { "@corvu/utils": "~0.3.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-ocHVnbfy23CgfaH4cEUR/AFg0Y3CEL8Oh3n9Qv8OHFJgPh+zkmERKZQfi/xH5XvxDCizg8VjPrVUhiHB1Gza8g=="],
|
||||
@@ -5562,7 +5556,7 @@
|
||||
|
||||
"strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="],
|
||||
|
||||
"strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
||||
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
|
||||
|
||||
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
||||
|
||||
@@ -5594,8 +5588,6 @@
|
||||
|
||||
"svgo": ["svgo@4.0.2", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng=="],
|
||||
|
||||
"system-architecture": ["system-architecture@0.1.0", "", {}, "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA=="],
|
||||
|
||||
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
|
||||
@@ -6382,8 +6374,6 @@
|
||||
|
||||
"@opencode-ai/desktop/@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="],
|
||||
|
||||
"@opencode-ai/desktop/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"@opencode-ai/desktop/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||
|
||||
"@opencode-ai/session-ui/@opencode-ai/sdk": ["@opencode-ai/sdk@../app/vendor/opencode-ai-sdk-1.18.8-dev.tgz", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-C2nfk4x0sPINwE5V6DPkFSuH3PkUmKPWHPzxpXC1j+3Ui5hslLCWJbkk8WcOG1Lyt3C0+yp4ea64v/kmtYCO4w=="],
|
||||
@@ -6536,8 +6526,6 @@
|
||||
|
||||
"@vercel/cli-config/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||
|
||||
"@vercel/cli-exec/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
|
||||
|
||||
"@vercel/functions/@vercel/oidc": ["@vercel/oidc@3.8.1", "", { "dependencies": { "@vercel/cli-config": "0.2.1", "@vercel/cli-exec": "1.0.0", "jose": "^5.9.6" } }, "sha512-ufdalm2MWOYksyj8KVpWjoOFPJO6zoYpuyvIggIQ2bB0CFCjTCiTkGXHqAKwG77GVRjOaN3/8S5ITlZpXWmqOw=="],
|
||||
|
||||
"@vercel/nft/acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
|
||||
@@ -6624,8 +6612,6 @@
|
||||
|
||||
"blume/katex": ["katex@0.17.0", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw=="],
|
||||
|
||||
"blume/marked": ["marked@18.0.7", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-iDVQ5ldaiKXn6b2JroX5kgRfmwgqolW7NpaEzTl1k/2Zh1njIEN9yniyLV/mOvWwtsE8OGgkjsCYvijuPk1dtA=="],
|
||||
|
||||
"blume/node-html-parser": ["node-html-parser@9.0.0", "", { "dependencies": { "css-select": "^5.1.0", "entities": "^8.0.0" } }, "sha512-MhdaHPyxnyYu/sf0TpiRvDnTrkum0UKHC7FdbDGIUQNlx3I7xzwXoyV0eMUMv/XU+lkJT1glOUzpDPq7b2p1Ew=="],
|
||||
|
||||
"blume/react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="],
|
||||
@@ -6724,9 +6710,11 @@
|
||||
|
||||
"estree-util-to-js/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
|
||||
"execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
|
||||
|
||||
"execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
|
||||
"execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
@@ -6760,6 +6748,8 @@
|
||||
|
||||
"jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
|
||||
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"matcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
@@ -6800,8 +6790,6 @@
|
||||
|
||||
"node-gyp/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="],
|
||||
|
||||
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||
|
||||
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
||||
|
||||
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
@@ -6890,12 +6878,8 @@
|
||||
|
||||
"terser/acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
|
||||
|
||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
|
||||
|
||||
"topojson-client/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"tree-sitter-bash/node-addon-api": ["node-addon-api@8.9.0", "", {}, "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q=="],
|
||||
|
||||
"tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
@@ -7572,20 +7556,6 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
|
||||
|
||||
"@vercel/cli-exec/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
|
||||
|
||||
"@vercel/cli-exec/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
|
||||
|
||||
"@vercel/cli-exec/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"@vercel/cli-exec/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
|
||||
|
||||
"@vercel/cli-exec/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"@vercel/cli-exec/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"@vercel/cli-exec/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
|
||||
|
||||
"@vercel/functions/@vercel/oidc/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||
|
||||
"@vercel/routing-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
@@ -7718,6 +7688,8 @@
|
||||
|
||||
"blume/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"blume/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
|
||||
"blume/node-html-parser/entities": ["entities@8.0.0", "", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="],
|
||||
|
||||
"blume/react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
@@ -7790,6 +7762,8 @@
|
||||
|
||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"mermaid/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
|
||||
"motion/framer-motion/motion-dom": ["motion-dom@12.42.2", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA=="],
|
||||
|
||||
"motion/framer-motion/motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="],
|
||||
@@ -8642,8 +8616,6 @@
|
||||
|
||||
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
|
||||
|
||||
"@vercel/cli-exec/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
|
||||
|
||||
+6
-2
@@ -288,8 +288,12 @@ new sst.cloudflare.x.SolidStart("Console", {
|
||||
server: {
|
||||
placement: { region: "aws:us-east-2" },
|
||||
transform: {
|
||||
worker: {
|
||||
tailConsumers: [{ service: logProcessor.nodes.worker.scriptName }],
|
||||
worker: (args) => {
|
||||
args.compatibilityFlags = $resolve(args.compatibilityFlags).apply((flags) => [
|
||||
...(flags ?? []),
|
||||
"global_fetch_strictly_public",
|
||||
])
|
||||
args.tailConsumers = [{ service: logProcessor.nodes.worker.scriptName }]
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-RFek0QoEEjsgbqmTE/SxQAmPtYyzs0IPR2ugFn5Okrs=",
|
||||
"aarch64-linux": "sha256-BmAxapY1YrAFn7mVq3/6A9+6Au5UIvSqBboHMkyJH3I=",
|
||||
"aarch64-darwin": "sha256-Sx3bGWQqLlgoa/RudJxanjSzhFRNklckT2ffnO2I5F4=",
|
||||
"x86_64-darwin": "sha256-CMOhiisHNowg06qadvgg4K+60zrynglwiT0qKYQ4NiA="
|
||||
"x86_64-linux": "sha256-uduwrM143NDSc+tXsi4lVVfoMll2a3BDHRUjuO7GB68=",
|
||||
"aarch64-linux": "sha256-6DUda78XdXY6DP86lIUkweSjys3iG4Y4mo1PiaNuXbg=",
|
||||
"aarch64-darwin": "sha256-AkJwfLULLZVwwz+XU1QcFUZoIS7oVPCn+n/MXEaxrqE=",
|
||||
"x86_64-darwin": "sha256-hAxKGdiITTxQ2uujQt6prNjo3NxGAMMeo+9HlMWK6GU="
|
||||
}
|
||||
}
|
||||
|
||||
+10
-7
@@ -46,9 +46,9 @@
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@hono/standard-validator": "0.2.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"@opentui/core": "0.4.5",
|
||||
"@opentui/keymap": "0.4.5",
|
||||
"@opentui/solid": "0.4.5",
|
||||
"@opentui/core": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/solid": "0.0.0-20260808-9ecf7c0a",
|
||||
"@tanstack/solid-virtual": "3.13.32",
|
||||
"@shikijs/stream": "4.2.0",
|
||||
"@standard-schema/spec": "1.1.0",
|
||||
@@ -78,7 +78,7 @@
|
||||
"fuzzysort": "3.1.0",
|
||||
"get-east-asian-width": "1.6.0",
|
||||
"luxon": "3.6.1",
|
||||
"marked": "17.0.6",
|
||||
"marked": "18.0.7",
|
||||
"marked-shiki": "1.2.1",
|
||||
"remend": "1.3.0",
|
||||
"@playwright/test": "1.59.1",
|
||||
@@ -100,6 +100,7 @@
|
||||
"@sentry/solid": "10.36.0",
|
||||
"@sentry/vite-plugin": "4.6.0",
|
||||
"solid-js": "1.9.10",
|
||||
"solid-sonner": "0.3.1",
|
||||
"vite-plugin-solid": "2.11.10",
|
||||
"@lydell/node-pty": "1.2.0-beta.12"
|
||||
}
|
||||
@@ -149,14 +150,16 @@
|
||||
"electron"
|
||||
],
|
||||
"overrides": {
|
||||
"@opentui/core": "catalog:",
|
||||
"@opentui/keymap": "catalog:",
|
||||
"@opentui/solid": "catalog:",
|
||||
"@opentui/core": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a",
|
||||
"@opentui/solid": "0.0.0-20260808-9ecf7c0a",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"effect": "catalog:"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch",
|
||||
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
|
||||
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
|
||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises"
|
||||
import * as path from "node:path"
|
||||
|
||||
const RECORDINGS_DIR = path.resolve(import.meta.dir, "..", "test", "fixtures", "recordings")
|
||||
const MODELS_DEV_URL = "https://models.dev/api.json"
|
||||
const MODELS_DEV_URL = "https://models.opencode.ai/api.json"
|
||||
|
||||
type JsonRecord = Record<string, unknown>
|
||||
|
||||
|
||||
@@ -422,14 +422,12 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part: Me
|
||||
|
||||
// Tool results may carry structured text, images, and documents. Keep media as provider-native
|
||||
// content instead of JSON-stringifying base64 into a prompt string.
|
||||
const lowerToolResultContentItem = Effect.fn("AnthropicMessages.lowerToolResultContentItem")(function* (
|
||||
item: Tool.Content,
|
||||
) {
|
||||
const lowerToolResultContentItem = Effect.fnUntraced(function* (item: Tool.Content) {
|
||||
if (item.type === "text") return { type: "text" as const, text: item.text } satisfies AnthropicTextBlock
|
||||
return yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name })
|
||||
})
|
||||
|
||||
const lowerToolResultContent = Effect.fn("AnthropicMessages.lowerToolResultContent")(function* (part: ToolResultPart) {
|
||||
const lowerToolResultContent = Effect.fnUntraced(function* (part: ToolResultPart) {
|
||||
// Text / json / error results stay as a string for backward compatibility
|
||||
// with existing cassettes and provider expectations.
|
||||
if (part.result.type !== "content") return ProviderShared.toolResultText(part)
|
||||
|
||||
@@ -358,7 +358,7 @@ const lowerMedia = Effect.fn("OpenResponses.lowerMedia")(function* (
|
||||
return { type: "input_image" as const, image_url: media.dataUrl }
|
||||
})
|
||||
|
||||
const lowerUserContent = Effect.fn("OpenResponses.lowerUserContent")(function* (
|
||||
const lowerUserContent = Effect.fnUntraced(function* (
|
||||
part: LLMRequest["messages"][number]["content"][number],
|
||||
request: LLMRequest,
|
||||
extension: Extension,
|
||||
@@ -370,7 +370,7 @@ const lowerUserContent = Effect.fn("OpenResponses.lowerUserContent")(function* (
|
||||
|
||||
// Tool results may carry structured text, images, and files. Keep media as provider-native
|
||||
// content instead of JSON-stringifying base64 into a prompt string.
|
||||
const lowerToolResultContentItem = Effect.fn("OpenResponses.lowerToolResultContentItem")(function* (
|
||||
const lowerToolResultContentItem = Effect.fnUntraced(function* (
|
||||
item: Content,
|
||||
request: LLMRequest,
|
||||
extension: Extension,
|
||||
@@ -383,7 +383,7 @@ const lowerToolResultContentItem = Effect.fn("OpenResponses.lowerToolResultConte
|
||||
)
|
||||
})
|
||||
|
||||
const lowerToolResultOutput = Effect.fn("OpenResponses.lowerToolResultOutput")(function* (
|
||||
const lowerToolResultOutput = Effect.fnUntraced(function* (
|
||||
part: ToolResultPart,
|
||||
request: LLMRequest,
|
||||
extension: Extension,
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options"
|
||||
import type { RouteDefaultsInput } from "../route/client"
|
||||
import { ProviderID, type ModelID } from "../schema"
|
||||
import * as OpenAIChat from "../protocols/openai-chat"
|
||||
import * as OpenAIResponses from "../protocols/openai-responses"
|
||||
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options"
|
||||
|
||||
export const id = ProviderID.make("github-copilot")
|
||||
|
||||
// GitHub Copilot has no canonical public URL — callers (opencode, etc.) must
|
||||
// supply `baseURL` explicitly.
|
||||
export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
ProviderAuthOption<"optional"> & {
|
||||
readonly baseURL: string
|
||||
readonly endpoint?: "chat" | "responses"
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export const shouldUseResponsesApi = (modelID: string | ModelID, endpoint?: LanguageModelOptions["endpoint"]) => {
|
||||
if (endpoint) return endpoint === "responses"
|
||||
const model = String(modelID)
|
||||
const match = /^gpt-(\d+)/.exec(model)
|
||||
if (!match) return false
|
||||
return Number(match[1]) >= 5 && !model.startsWith("gpt-5-mini")
|
||||
}
|
||||
|
||||
export const routes = [OpenAIResponses.route, OpenAIChat.route]
|
||||
|
||||
const chatRoute = OpenAIChat.route.with({ provider: id })
|
||||
const responsesRoute = OpenAIResponses.route.with({ provider: id })
|
||||
|
||||
const defaults = (options: LanguageModelOptions) => {
|
||||
const { apiKey: _, auth: _auth, baseURL: _baseURL, endpoint: _endpoint, ...rest } = options
|
||||
return rest
|
||||
}
|
||||
|
||||
const configuredResponsesRoute = (options: LanguageModelOptions) =>
|
||||
responsesRoute.with({
|
||||
endpoint: { baseURL: options.baseURL },
|
||||
auth: AuthOptions.bearer(options, []),
|
||||
})
|
||||
|
||||
const configuredChatRoute = (options: LanguageModelOptions) =>
|
||||
chatRoute.with({
|
||||
endpoint: { baseURL: options.baseURL },
|
||||
auth: AuthOptions.bearer(options, []),
|
||||
})
|
||||
|
||||
export const configure = (options: LanguageModelOptions) => {
|
||||
const responsesRoute = configuredResponsesRoute(options)
|
||||
const chatRoute = configuredChatRoute(options)
|
||||
const responses = (modelID: string | ModelID) =>
|
||||
responsesRoute
|
||||
.with(withOpenAIOptions(modelID, defaults(options)))
|
||||
.model<OpenAIProviderOptionsInput>({ id: modelID })
|
||||
const chat = (modelID: string | ModelID) =>
|
||||
chatRoute.with(withOpenAIOptions(modelID, defaults(options))).model<OpenAIProviderOptionsInput>({ id: modelID })
|
||||
return {
|
||||
id,
|
||||
model: (modelID: string | ModelID) =>
|
||||
shouldUseResponsesApi(modelID, options.endpoint) ? responses(modelID) : chat(modelID),
|
||||
responses,
|
||||
chat,
|
||||
configure,
|
||||
}
|
||||
}
|
||||
|
||||
export const provider = {
|
||||
id,
|
||||
configure,
|
||||
}
|
||||
@@ -5,7 +5,6 @@ export * as AmazonBedrockMantle from "./amazon-bedrock-mantle"
|
||||
export * as Azure from "./azure"
|
||||
export * as Cloudflare from "./cloudflare"
|
||||
export { CloudflareAIGateway, CloudflareWorkersAI } from "./cloudflare"
|
||||
export * as GitHubCopilot from "./github-copilot"
|
||||
export * as Google from "./google"
|
||||
export * as GoogleVertex from "./google-vertex"
|
||||
export * as GoogleVertexChat from "./google-vertex-chat"
|
||||
|
||||
@@ -185,15 +185,15 @@ const secretValues = (request: HttpClientRequest.HttpClientRequest) => {
|
||||
// Two passes: structural (redact `"name": "value"` and `name=value` patterns
|
||||
// for any field name that looks sensitive) plus literal (replace any actual
|
||||
// secret values we sent in the request, in case the response echoes one back).
|
||||
const redactBody = (body: string, request: HttpClientRequest.HttpClientRequest) =>
|
||||
Array.from(secretValues(request)).reduce(
|
||||
const redactBody = (body: string, secrets: ReadonlySet<string>) =>
|
||||
Array.from(secrets).reduce(
|
||||
(text, secret) => text.split(secret).join(REDACTED),
|
||||
body.replace(REDACT_JSON_FIELD, `$1"${REDACTED}"`).replace(REDACT_QUERY_FIELD, `$1${REDACTED}`),
|
||||
)
|
||||
|
||||
const responseBody = (body: string | void, request: HttpClientRequest.HttpClientRequest) => {
|
||||
const responseBody = (body: string | void, secrets: ReadonlySet<string>) => {
|
||||
if (body === undefined) return {}
|
||||
const redacted = redactBody(body, request)
|
||||
const redacted = redactBody(body, secrets)
|
||||
if (redacted.length <= BODY_LIMIT) return { body: redacted }
|
||||
return { body: redacted.slice(0, BODY_LIMIT), bodyTruncated: true }
|
||||
}
|
||||
@@ -240,7 +240,7 @@ const statusError =
|
||||
const headers = normalizedHeaders(response.headers)
|
||||
const retryAfter = retryAfterMs(headers)
|
||||
const rateLimit = rateLimitDetails(headers, retryAfter)
|
||||
const details = responseBody(body, request)
|
||||
const details = responseBody(body, secretValues(request))
|
||||
return yield* new AIError({
|
||||
module: "RequestExecutor",
|
||||
method: "execute",
|
||||
@@ -261,6 +261,42 @@ const statusError =
|
||||
})
|
||||
})
|
||||
|
||||
// Classifies an HTTP failure captured outside the executor (for example by the
|
||||
// AI SDK's own fetch) onto the same reason types and redacted HttpContext that
|
||||
// executor-driven requests produce. The originating request is not available on
|
||||
// that path, so the method is assumed (language model calls are always POST),
|
||||
// request headers are empty, and only structural body redaction applies.
|
||||
export const classifyHttpFailure = (input: {
|
||||
readonly message: string
|
||||
readonly url: string
|
||||
readonly status?: number | undefined
|
||||
readonly code?: string | undefined
|
||||
readonly responseHeaders?: Record<string, string> | undefined
|
||||
readonly responseBody?: string | undefined
|
||||
}) => {
|
||||
const headers = normalizedHeaders(Headers.fromInput(input.responseHeaders))
|
||||
const retryAfter = retryAfterMs(headers)
|
||||
const rateLimit = rateLimitDetails(headers, retryAfter)
|
||||
const details = responseBody(input.responseBody ?? undefined, new Set<string>())
|
||||
return classifyProviderFailure({
|
||||
message: input.message,
|
||||
status: input.status,
|
||||
code: input.code,
|
||||
retryAfterMs: retryAfter,
|
||||
rateLimit,
|
||||
http: new HttpContext({
|
||||
request: new HttpRequestDetails({ method: "POST", url: redactUrl(input.url), headers: {} }),
|
||||
response:
|
||||
input.status === undefined
|
||||
? undefined
|
||||
: new HttpResponseDetails({ status: input.status, headers: redactHeaders(Headers.fromInput(headers), []) }),
|
||||
...details,
|
||||
requestId: requestId(headers),
|
||||
rateLimit,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
const toHttpError = (redactedNames: ReadonlyArray<string | RegExp>) => (error: unknown) => {
|
||||
const transportError = (input: {
|
||||
readonly message: string
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as Anthropic from "../src/providers/anthropic"
|
||||
import * as AnthropicCompatible from "../src/providers/anthropic-compatible"
|
||||
import * as Azure from "../src/providers/azure"
|
||||
import * as Cloudflare from "../src/providers/cloudflare"
|
||||
import * as GitHubCopilot from "../src/providers/github-copilot"
|
||||
import * as Google from "../src/providers/google"
|
||||
import * as GoogleVertex from "../src/providers/google-vertex"
|
||||
import * as GoogleVertexChat from "../src/providers/google-vertex-chat"
|
||||
@@ -270,7 +269,3 @@ OpenAICompatible.deepseek.configure({ apiKey: "deepseek-key" }).model("deepseek-
|
||||
Cloudflare.CloudflareWorkersAI.configure({ accountId: "account", apiKey: "cf-key" }).model("@cf/meta/llama")
|
||||
// @ts-expect-error Cloudflare Workers AI model selectors only accept model ids.
|
||||
Cloudflare.CloudflareWorkersAI.configure({ accountId: "account", apiKey: "cf-key" }).model("@cf/meta/llama", {})
|
||||
|
||||
GitHubCopilot.configure({ baseURL: "https://copilot.test", apiKey: "copilot-key" }).model("gpt-4.1")
|
||||
// @ts-expect-error GitHub Copilot model selectors only accept model ids.
|
||||
GitHubCopilot.configure({ baseURL: "https://copilot.test", apiKey: "copilot-key" }).model("gpt-4.1", {})
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
OpenRouter,
|
||||
XAI,
|
||||
} from "@opencode-ai/ai/providers"
|
||||
import * as GitHubCopilot from "@opencode-ai/ai/providers/github-copilot"
|
||||
import {
|
||||
OpenAIChat,
|
||||
OpenAICompatibleChat,
|
||||
@@ -60,23 +59,6 @@ describe("public exports", () => {
|
||||
expect(XAI.provider.chat).toBe(XAI.chat)
|
||||
expect(XAI.configure({ apiKey: "fixture" }).responses("grok-4.3").route.id).toBe("openai-responses")
|
||||
expect(XAI.configure({ apiKey: "fixture" }).chat("grok-4.3").route.id).toBe("openai-compatible-chat")
|
||||
expect(
|
||||
GitHubCopilot.configure({ baseURL: "https://api.githubcopilot.test", apiKey: "fixture" }).model,
|
||||
).toBeFunction()
|
||||
expect(
|
||||
GitHubCopilot.configure({
|
||||
baseURL: "https://api.githubcopilot.test",
|
||||
apiKey: "fixture",
|
||||
endpoint: "responses",
|
||||
}).model("mai-code-1-flash-picker").route.id,
|
||||
).toBe("openai-responses")
|
||||
expect(
|
||||
GitHubCopilot.configure({
|
||||
baseURL: "https://api.githubcopilot.test",
|
||||
apiKey: "fixture",
|
||||
endpoint: "chat",
|
||||
}).model("gpt-5").route.id,
|
||||
).toBe("openai-chat")
|
||||
})
|
||||
|
||||
test("protocol barrels expose supported low-level routes", () => {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { LLM } from "../../src"
|
||||
import { GitHubCopilot } from "../../src/providers"
|
||||
|
||||
const model = GitHubCopilot.configure({ baseURL: "https://example.com" }).model("gpt-5")
|
||||
|
||||
LLM.request({ model, prompt: "Hello", providerOptions: { openai: { reasoningSummary: "auto" } } })
|
||||
|
||||
LLM.request({
|
||||
model,
|
||||
prompt: "Hello",
|
||||
// @ts-expect-error Copilot reasoning summaries use the OpenAI union.
|
||||
providerOptions: { openai: { reasoningSummary: "full" } },
|
||||
})
|
||||
@@ -19,6 +19,21 @@
|
||||
|
||||
- Always prefer `createStore` over multiple `createSignal` calls
|
||||
|
||||
## Localization
|
||||
|
||||
- NEVER hardcode user-visible English strings in production code. ALWAYS use an i18n key for visible copy, placeholders, accessible labels, tooltips, menus, dialogs, toasts, empty states, and displayed errors.
|
||||
- When migrating existing copy to i18n, preserve the English text byte-for-byte unless the task explicitly requests a copy change.
|
||||
- NEVER change existing English text or English keys to facilitate translation. English is intentional, designer-written source copy; adapt locale-specific translations and i18n mechanics around it.
|
||||
- Keep locale complexity behind the shared typed i18n APIs. Feature and component code should use `language.t(...)` for ordinary copy and `language.plural(baseKey, count, params)` for count-sensitive copy. It must not inspect the locale, call `Intl.PluralRules`, construct or select plural-category keys such as `.one` or `.other`, or branch on locale-specific grammar.
|
||||
- Prefer complete translated phrases. Do not concatenate grammatical fragments or make call sites assemble sentences. Keep placeholders to irreducible dynamic values such as names, paths, and counts.
|
||||
- If a translation cannot be expressed by the current API, deepen the shared language/UI i18n module so one typed call owns locale selection, plural resolution, fallback, and interpolation. Do not leak that machinery into product code.
|
||||
- Do not translate from model knowledge alone. Verify terminology and grammar with Unicode CLDR locale/plural data, Microsoft Localization Style Guides and terminology, Apple localization/style guidance and localized platform UI, Mozilla localization style guides, Mozilla Pontoon, and the Firefox localization corpus at `github.com/mozilla-l10n/firefox-l10n`.
|
||||
- For developer-facing terminology, prefer the words already used by the target language's developer community over literal dictionary translations. Cross-check maintained localized developer products such as Firefox, KDE, and VS Code; use at least two independent corpora when they are available. If established practice keeps an English loanword or acronym, keep it rather than inventing a translation.
|
||||
- Translate complete UI phrases in context. A glossary hit is evidence, not permission to translate word-by-word. Check terse labels such as session, prompt, agent, model, fork, shell, terminal, workspace, and worktree in the same grammatical role before choosing a term.
|
||||
- Before a locale is ready, audit recurring concepts for one consistent translation and review every value that still equals English. Classify retained English as a product name, provider/tool name, URL, code token, keyboard legend, acronym, asset name, or established borrowing; translate unexplained leftovers.
|
||||
- In translation review notes, name the corpora used and call out uncertain or region-specific terminology so native speakers can focus review where it matters.
|
||||
- Also use the relevant language authority or official dictionary for the locale (for example RAE/Fundéu, FranceTerme, Duden, TDK, Kotus/Kielitoimiston sanakirja, Språkrådet/Bokmålsordboka, Rada Języka Polskiego/PWN, the Russian and Arabic language academies, the Ukrainian Orthography, Taiwan MOE dictionaries, or the Royal Society of Thailand). Treat the English dictionary as the semantic source of truth and preserve placeholders, code identifiers, product names, and keyboard labels.
|
||||
|
||||
## Tool Calling
|
||||
|
||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
## Required Reading
|
||||
|
||||
- Before writing, changing, or reviewing E2E tests, ALWAYS read and follow Playwright's official [Best Practices](https://playwright.dev/docs/best-practices), [Auto-waiting](https://playwright.dev/docs/actionability), and [Assertions](https://playwright.dev/docs/test-assertions) guides.
|
||||
- Use the official [Locators](https://playwright.dev/docs/locators), [Network](https://playwright.dev/docs/network), and [Test Isolation](https://playwright.dev/docs/browser-contexts) guides when those concerns apply.
|
||||
|
||||
## Test Hygiene
|
||||
|
||||
- Test user-visible behavior with isolated, deterministic data and scoped, unique locators.
|
||||
- Prefer role, label, text, and explicit test-contract locators. Do not use `.first()` or `.last()` merely to silence strictness errors.
|
||||
- Use locator actions, Playwright auto-waiting, and web-first assertions for observable readiness and outcomes.
|
||||
- NEVER use `waitForTimeout`, `setTimeout`, sleeps, animation-frame counts, or other wall-clock delays to synchronize a test. Wait for the specific UI state, request, response, event, or application outcome instead.
|
||||
- Do not treat navigation, a network response, DOM attachment, or visibility alone as proof that asynchronously rendered UI is ready. Assert the state the next action actually requires.
|
||||
- Register event and network waits before the action that triggers them.
|
||||
- Do not retry state-changing actions. Retry idempotent readiness checks, then perform the action once and assert its outcome.
|
||||
- Keep action and assertion timeouts adaptive. Do not use short timeouts as readiness probes or rely on retries to hide flakes.
|
||||
- Assert exact outcomes and identities so stale state, duplicate rendering, and interactions with the wrong element cannot pass.
|
||||
@@ -8,7 +8,7 @@ import { expect, type Page } from "@playwright/test"
|
||||
import { Schema } from "effect"
|
||||
import { mockOpenCodeServer } from "../../utils/mock-server"
|
||||
import { installSseTransport } from "../../utils/sse-transport"
|
||||
import { expectSessionTitle } from "../../utils/waits"
|
||||
import { expectSessionReady } from "../../utils/waits"
|
||||
|
||||
export const directory = "C:/OpenCode/TimelineStability"
|
||||
export const projectID = "proj_timeline_stability"
|
||||
@@ -111,8 +111,9 @@ export async function setupTimeline(
|
||||
active?.info.role === "assistant" && active.info.time.completed === undefined ? { type: "busy" } : { type: "idle" },
|
||||
decodeOptions,
|
||||
)
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
const transport = await installSseTransport<EventPayload>(page, {
|
||||
server: `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`,
|
||||
server,
|
||||
retry: input.eventRetry ?? 20,
|
||||
})
|
||||
await mockOpenCodeServer(page, {
|
||||
@@ -161,8 +162,8 @@ export async function setupTimeline(
|
||||
})
|
||||
}
|
||||
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
|
||||
await expectSessionReady(page, { server, sessionID, title })
|
||||
await transport.waitForConnection()
|
||||
await expectSessionTitle(page, title)
|
||||
if (input.cpuRate && input.cpuRate > 1) {
|
||||
const devtools = await page.context().newCDPSession(page)
|
||||
await devtools.send("Emulation.setCPUThrottlingRate", { rate: input.cpuRate })
|
||||
@@ -198,7 +199,9 @@ export async function setupTimeline(
|
||||
)
|
||||
},
|
||||
async waitForPart(partID: string) {
|
||||
await expect(page.locator(`[data-timeline-part-id="${partID}"]`).first()).toBeVisible()
|
||||
const part = page.locator(`[data-timeline-part-id="${partID}"]`)
|
||||
await expect(part).toHaveCount(1)
|
||||
await expect(part).toBeVisible()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
import type { Page } from "@playwright/test"
|
||||
import { fixture, pageMessages } from "../smoke/session-timeline.fixture"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { expectAppVisible } from "../utils/waits"
|
||||
|
||||
const NAMES = ["alpha-service", "bravo-web", "charlie-api", "delta-tools", "echo-infra", "foxtrot-docs"]
|
||||
const worktrees = NAMES.map((name) => `/opencode-demo/${name}`)
|
||||
|
||||
// The sixth project sits outside the five-item recent cap, so it is only reachable if the
|
||||
// dialog hands every recent project to the list filter instead of a pre-truncated slice.
|
||||
const OUTSIDE_CAP = "foxtrot-docs"
|
||||
|
||||
// Dialog rows carry data-directory-path; the sidebar project list does not, so this
|
||||
// scopes assertions to the picker instead of matching the sidebar entry of the same name.
|
||||
const rows = (page: Page) => page.locator("[data-directory-path]")
|
||||
const row = (page: Page, name: string) => page.locator(`[data-directory-path*="${name}"]`)
|
||||
|
||||
async function openProjectDialog(page: Page) {
|
||||
await mockOpenCodeServer(page, {
|
||||
sessions: fixture.sessions,
|
||||
provider: fixture.provider,
|
||||
directory: fixture.directory,
|
||||
project: fixture.project,
|
||||
pageMessages,
|
||||
fileList: () => [],
|
||||
findFiles: () => [],
|
||||
})
|
||||
await page.addInitScript((dirs) => {
|
||||
localStorage.setItem(
|
||||
"opencode.global.dat:server",
|
||||
JSON.stringify({
|
||||
projects: { local: dirs.map((worktree: string) => ({ worktree, expanded: false })) },
|
||||
lastProject: {},
|
||||
}),
|
||||
)
|
||||
}, worktrees)
|
||||
await page.goto("/")
|
||||
const add = page.getByRole("button", { name: "Add project" }).first()
|
||||
await expectAppVisible(add)
|
||||
await add.click()
|
||||
await expect(rows(page)).toHaveCount(5)
|
||||
return page.getByRole("textbox").last()
|
||||
}
|
||||
|
||||
test("searches every recent project, not just the five most recent", async ({ page }) => {
|
||||
const search = await openProjectDialog(page)
|
||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
|
||||
|
||||
await search.fill("foxtrot")
|
||||
|
||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(1)
|
||||
})
|
||||
|
||||
test("still caps the idle recent list at five projects", async ({ page }) => {
|
||||
await openProjectDialog(page)
|
||||
|
||||
await expect(row(page, NAMES[4])).toHaveCount(1)
|
||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
|
||||
})
|
||||
@@ -18,6 +18,7 @@ test("opens the comment editor when code is clicked", async ({ page }) => {
|
||||
await line.click()
|
||||
|
||||
await expect(review.getByRole("textbox")).toBeVisible()
|
||||
await expect(review.locator('[data-slot="line-comment-editor-label"]')).toHaveText("Commenting on line 2")
|
||||
})
|
||||
|
||||
test("opens the comment editor when a line number is clicked", async ({ page }) => {
|
||||
@@ -27,6 +28,7 @@ test("opens the comment editor when a line number is clicked", async ({ page })
|
||||
await lineNumber.click()
|
||||
|
||||
await expect(review.getByRole("textbox")).toBeVisible()
|
||||
await expect(review.locator('[data-slot="line-comment-editor-label"]')).toHaveText("Commenting on line 1")
|
||||
})
|
||||
|
||||
test("opens the comment editor for a line number range", async ({ page }) => {
|
||||
@@ -36,15 +38,10 @@ test("opens the comment editor for a line number range", async ({ page }) => {
|
||||
await expectAppVisible(start)
|
||||
await expectAppVisible(end)
|
||||
|
||||
const from = await start.boundingBox()
|
||||
const to = await end.boundingBox()
|
||||
if (!from || !to) throw new Error("Missing line number bounds")
|
||||
await page.mouse.move(from.x + from.width / 2, from.y + from.height / 2)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(to.x + to.width / 2, to.y + to.height / 2)
|
||||
await page.mouse.up()
|
||||
await start.dragTo(end)
|
||||
|
||||
await expect(review.getByRole("textbox")).toBeVisible()
|
||||
await expect(review.locator('[data-slot="line-comment-editor-label"]')).toHaveText("Commenting on lines 1-3")
|
||||
})
|
||||
|
||||
test("shows a comment button when a line number is hovered", async ({ page }) => {
|
||||
@@ -54,31 +51,38 @@ test("shows a comment button when a line number is hovered", async ({ page }) =>
|
||||
|
||||
const comment = review.getByRole("button", { name: "Comment", exact: true })
|
||||
await expect(async () => {
|
||||
await page.mouse.move(0, 0)
|
||||
await lineNumber.hover()
|
||||
await expect(comment).toBeVisible({ timeout: 500 })
|
||||
await comment.click({ timeout: 500 })
|
||||
}).toPass()
|
||||
await expect(lineNumber).toHaveAttribute("data-hovered", "")
|
||||
await expect(comment).toHaveCount(1)
|
||||
await expect(comment).toHaveCSS("pointer-events", "auto")
|
||||
await comment.focus()
|
||||
await expect(comment).toBeFocused()
|
||||
}).toPass({ timeout: 10_000 })
|
||||
await comment.press("Enter")
|
||||
await expect(review.getByRole("textbox")).toBeVisible()
|
||||
await expect(review.locator('[data-slot="line-comment-editor-label"]')).toHaveText("Commenting on line 1")
|
||||
})
|
||||
|
||||
test("stages a submitted line comment in the prompt context", async ({ page }) => {
|
||||
const requests: string[] = []
|
||||
page.on("request", (request) => {
|
||||
if (request.method() !== "GET") requests.push(`${request.method()} ${new URL(request.url()).pathname}`)
|
||||
expect.soft(request.method(), `unexpected ${request.method()} ${new URL(request.url()).pathname}`).toBe("GET")
|
||||
})
|
||||
|
||||
const review = page.locator('[data-component="session-review"]')
|
||||
await review.getByText("export const value = 'after'", { exact: true }).click()
|
||||
await review.getByRole("textbox").fill("Use the existing value instead")
|
||||
await review.locator('[data-slot="line-comment-action"][data-variant="primary"]').click()
|
||||
const textbox = review.getByRole("textbox")
|
||||
await expect(textbox).toBeVisible()
|
||||
await expect(review.locator('[data-slot="line-comment-editor-label"]')).toHaveText("Commenting on line 2")
|
||||
await textbox.fill("Use the existing value instead")
|
||||
const submit = review.locator('[data-slot="line-comment-action"][data-variant="primary"]')
|
||||
await expect(submit).toBeEnabled()
|
||||
await submit.click()
|
||||
|
||||
await expect(review.getByText("Use the existing value instead", { exact: true })).toBeVisible()
|
||||
await page.getByRole("tab", { name: "Session" }).click()
|
||||
const context = page.getByText("Use the existing value instead", { exact: true }).last()
|
||||
await expect(context).toBeVisible()
|
||||
await expect(context.locator("..")).toContainText("review.ts:2")
|
||||
expect(requests).toEqual([])
|
||||
})
|
||||
|
||||
async function openReview(page: Page) {
|
||||
@@ -144,15 +148,22 @@ async function openReview(page: Page) {
|
||||
|
||||
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
|
||||
await expectSessionTitle(page, title)
|
||||
const diffResponse = page.waitForResponse((response) => new URL(response.url()).pathname === "/api/vcs/diff")
|
||||
await page.getByRole("tab", { name: "Changes" }).click()
|
||||
const changes = page.getByRole("tab", { name: "Changes" })
|
||||
const diffResponse = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === "GET" && response.ok() && new URL(response.url()).pathname === "/api/vcs/diff",
|
||||
)
|
||||
await changes.click()
|
||||
expect((await (await diffResponse).json()).data).toHaveLength(1)
|
||||
await expect(page.getByRole("tab", { selected: true })).toHaveAccessibleName(/Files Changed/)
|
||||
|
||||
const review = page.locator('[data-component="session-review"]')
|
||||
await expectAppVisible(review)
|
||||
await review
|
||||
.getByRole("heading", { name: /review\.ts/ })
|
||||
.getByRole("button")
|
||||
.first()
|
||||
.click()
|
||||
const file = review.locator('[data-file="src/review.ts"]')
|
||||
await expectAppVisible(file)
|
||||
const trigger = file.getByRole("button", { expanded: false })
|
||||
await expect(trigger).toHaveCount(1)
|
||||
await trigger.click()
|
||||
await expect(file.getByRole("button", { expanded: true })).toBeVisible()
|
||||
await expect(file.getByText("export const value = 'after'", { exact: true })).toBeVisible()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test, type Page } from "@playwright/test"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { expectSessionTitle } from "../utils/waits"
|
||||
import { expectSessionReady } from "../utils/waits"
|
||||
|
||||
const directory = "C:/OpenCode/ReviewTerminalStacked"
|
||||
const projectID = "proj_review_terminal_stacked"
|
||||
@@ -19,10 +19,6 @@ const branchDiffs = [
|
||||
|
||||
test("keeps the review tree and terminal sized when both panels are open", async ({ page }) => {
|
||||
test.setTimeout(120_000)
|
||||
const events: Array<{ directory: string; payload: Record<string, unknown> }> = []
|
||||
const sessionStatus = { [sessionID]: { type: "idle" as "busy" | "idle" } }
|
||||
let detailVersion = 1
|
||||
let detailFailures = 1
|
||||
await page.setViewportSize({ width: 1400, height: 900 })
|
||||
await mockOpenCodeServer(page, {
|
||||
protocol: "v2",
|
||||
@@ -57,10 +53,8 @@ test("keeps the review tree and terminal sized when both panels are open", async
|
||||
time: { created: 1700000000000, updated: 1700000000000 },
|
||||
},
|
||||
],
|
||||
sessionStatus: () => sessionStatus,
|
||||
sessionStatus: { [sessionID]: { type: "idle" } },
|
||||
pageMessages: () => ({ items: [] }),
|
||||
events: () => events.splice(0, 1),
|
||||
eventRetry: 16,
|
||||
})
|
||||
await page.route(/\/api\/vcs(?:\?.*)?$/, (route) =>
|
||||
route.fulfill({
|
||||
@@ -73,20 +67,12 @@ test("keeps the review tree and terminal sized when both panels are open", async
|
||||
}),
|
||||
)
|
||||
await page.route("**/api/vcs/diff**", (route) => {
|
||||
const url = new URL(route.request().url())
|
||||
const scope = url.searchParams.get("location[directory]")?.replaceAll("\\", "/")
|
||||
const detail = scope?.endsWith("/src/branch/d00027")
|
||||
if (detail && detailFailures-- > 0) return route.fulfill({ status: 500, body: "retry detail" })
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
location: { directory, project: { id: projectID, directory, canonical: directory } },
|
||||
data: detail
|
||||
? branchDiffs
|
||||
.filter((diff) => diff.file.startsWith("src/branch/d00027/"))
|
||||
.map((diff) => fileDiff(diff.file, diff.additions, true, detailVersion))
|
||||
: branchDiffs,
|
||||
data: branchDiffs,
|
||||
}),
|
||||
})
|
||||
})
|
||||
@@ -146,7 +132,8 @@ test("keeps the review tree and terminal sized when both panels are open", async
|
||||
})
|
||||
|
||||
await page.goto(`/${base64Encode(directory)}/session/${sessionID}`)
|
||||
await expectSessionTitle(page, title)
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
await expectSessionReady(page, { server, sessionID, title })
|
||||
await expect(page.locator("#review-panel")).toBeVisible()
|
||||
await expectTree(page, 2_773, "action.yml")
|
||||
await expect(page.locator("#session-side-panel-review-tab")).toHaveText("Files Changed 2740")
|
||||
@@ -154,77 +141,6 @@ test("keeps the review tree and terminal sized when both panels are open", async
|
||||
await expect(page.locator("#terminal-panel")).toBeVisible()
|
||||
await expectTree(page, 2_773, "action.yml")
|
||||
await expectStackGeometry(page)
|
||||
|
||||
const treeViewport = page.locator('#review-panel [data-slot="session-review-v2-sidebar-tree"] .scroll-view__viewport')
|
||||
await treeViewport.hover()
|
||||
await page.mouse.wheel(0, 100_000)
|
||||
await expect
|
||||
.poll(() => treeViewport.evaluate((element) => element.scrollHeight - element.clientHeight - element.scrollTop))
|
||||
.toBeLessThanOrEqual(1)
|
||||
const lastFile = page.getByRole("button", { name: "generated-2738.ts" })
|
||||
await expect(lastFile).toBeVisible()
|
||||
const bottomGap = await lastFile.evaluate((element) => {
|
||||
const viewport = element.closest<HTMLElement>(".scroll-view__viewport")!.getBoundingClientRect()
|
||||
return viewport.bottom - element.getBoundingClientRect().bottom
|
||||
})
|
||||
expect(bottomGap).toBeGreaterThanOrEqual(0)
|
||||
expect(bottomGap).toBeLessThanOrEqual(16)
|
||||
const lazyDiff = page.waitForRequest((request) => {
|
||||
const url = new URL(request.url())
|
||||
return (
|
||||
url.pathname === "/api/vcs/diff" &&
|
||||
url.searchParams.get("location[directory]")?.replaceAll("\\", "/").endsWith("/src/branch/d00027") === true
|
||||
)
|
||||
})
|
||||
await lastFile.click()
|
||||
await lazyDiff
|
||||
const preview = page.locator('[data-slot="session-review-v2-diff-scroll"]')
|
||||
await expect(preview).toContainText("after-1")
|
||||
detailVersion = 2
|
||||
sessionStatus[sessionID] = { type: "busy" }
|
||||
events.push(statusEvent("busy"))
|
||||
await expect(page.getByRole("button", { name: "Stop" })).toBeVisible()
|
||||
const refreshedDiff = page.waitForRequest((request) => {
|
||||
const url = new URL(request.url())
|
||||
return (
|
||||
url.pathname === "/api/vcs/diff" &&
|
||||
url.searchParams.get("location[directory]")?.replaceAll("\\", "/").endsWith("/src/branch/d00027") === true
|
||||
)
|
||||
})
|
||||
sessionStatus[sessionID] = { type: "idle" }
|
||||
events.push(statusEvent("idle"))
|
||||
await refreshedDiff
|
||||
await expect(preview).toContainText("after-2")
|
||||
const filter = page.getByRole("searchbox", { name: "Filter files" })
|
||||
await filter.fill("generated-2738")
|
||||
await expectTree(page, 1, "generated-2738.ts")
|
||||
await filter.fill("")
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
|
||||
await page.getByRole("button", { name: "Toggle file tree" }).click()
|
||||
await expect(page.locator('[data-slot="session-review-v2-sidebar"]')).toHaveCount(0)
|
||||
await expect(page.locator('#review-panel [data-component="file-tree-v2"]')).toHaveCount(0)
|
||||
await page.getByRole("button", { name: "Toggle file tree" }).click()
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
|
||||
await page.keyboard.press("Control+Backquote")
|
||||
await expect(page.locator("#terminal-panel")).toHaveCount(0)
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
await page.keyboard.press("Control+Backquote")
|
||||
await expect(page.locator("#terminal-panel")).toBeVisible()
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
|
||||
await page.getByRole("button", { name: "Toggle review" }).click()
|
||||
await expect(page.locator("#review-panel")).toHaveCount(0)
|
||||
await page.getByRole("button", { name: "Toggle review" }).click()
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
await page.setViewportSize({ width: 1_000, height: 700 })
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
await expectStackGeometry(page)
|
||||
await page.setViewportSize({ width: 1_000, height: 120 })
|
||||
await page.setViewportSize({ width: 1_400, height: 900 })
|
||||
await expectTree(page, 2_773, "generated-2738.ts")
|
||||
await expectStackGeometry(page)
|
||||
})
|
||||
|
||||
async function expectTree(page: Page, total: number, file: string) {
|
||||
@@ -255,36 +171,32 @@ async function expectStackGeometry(page: Page) {
|
||||
const terminal = document.querySelector<HTMLElement>("#terminal-panel")!
|
||||
const reviewParent = review.parentElement!.getBoundingClientRect()
|
||||
const terminalParent = terminal.parentElement!.getBoundingClientRect()
|
||||
const sidebar = review.querySelector<HTMLElement>('[data-slot="session-review-v2-sidebar"]')!
|
||||
return {
|
||||
review: review.getBoundingClientRect().height,
|
||||
reviewParent: reviewParent.height,
|
||||
terminal: terminal.getBoundingClientRect().height,
|
||||
terminalParent: terminalParent.height,
|
||||
sidebar: sidebar.getBoundingClientRect().width,
|
||||
}
|
||||
})
|
||||
expect(Math.abs(geometry.review - geometry.reviewParent)).toBeLessThanOrEqual(1)
|
||||
expect(Math.abs(geometry.terminal - geometry.terminalParent)).toBeLessThanOrEqual(1)
|
||||
expect(geometry.sidebar).toBeGreaterThanOrEqual(240)
|
||||
}
|
||||
|
||||
function base64Encode(value: string) {
|
||||
return Buffer.from(value, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
|
||||
}
|
||||
|
||||
function statusEvent(type: "busy" | "idle") {
|
||||
return {
|
||||
directory,
|
||||
payload: { type: "session.status", properties: { sessionID, status: { type } } },
|
||||
}
|
||||
}
|
||||
|
||||
function fileDiff(file: string, additions: number, loaded = true, version = 1) {
|
||||
function fileDiff(file: string, additions: number, loaded = true) {
|
||||
return {
|
||||
file,
|
||||
additions,
|
||||
deletions: 0,
|
||||
status: "modified",
|
||||
patch: loaded
|
||||
? `diff --git a/${file} b/${file}\n--- a/${file}\n+++ b/${file}\n@@ -1 +1 @@\n-export const value = 'before'\n+export const value = 'after-${version}'\n`
|
||||
? `diff --git a/${file} b/${file}\n--- a/${file}\n+++ b/${file}\n@@ -1 +1 @@\n-export const value = 'before'\n+export const value = 'after'\n`
|
||||
: `diff --git a/${file} b/${file}\n--- a/${file}\n+++ b/${file}`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { expect, test } from "@playwright/test"
|
||||
import { assistantMessage, setupTimeline, toolPart, userMessage } from "../performance/timeline-stability/fixture"
|
||||
|
||||
for (const profile of [
|
||||
{ locale: "de", label: "Erkundet" },
|
||||
{ locale: "de", label: "Erkundung abgeschlossen" },
|
||||
{ locale: "ar", label: "تم الاستكشاف" },
|
||||
] as const) {
|
||||
test(`projects translated context status in ${profile.locale}`, async ({ page }) => {
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
import {
|
||||
assistantMessage,
|
||||
partUpdated,
|
||||
setupTimeline,
|
||||
status,
|
||||
textPart,
|
||||
userMessage,
|
||||
} from "../performance/timeline-stability/fixture"
|
||||
import { expect, test, type Page } from "@playwright/test"
|
||||
import { partUpdated, setupTimeline, textPart } from "../performance/timeline-stability/fixture"
|
||||
|
||||
test("keeps one connection open while delivering multiple events", async ({ page }) => {
|
||||
const timeline = await setupTimeline(page)
|
||||
@@ -17,7 +10,7 @@ test("keeps one connection open while delivering multiple events", async ({ page
|
||||
await timeline.waitForPart("prt_transport_first")
|
||||
await timeline.waitForPart("prt_transport_second")
|
||||
expect(first.connectionID).toBe(second.connectionID)
|
||||
expect(await timeline.transport.connections()).toHaveLength(1)
|
||||
await expect.poll(async () => (await timeline.transport.connections()).length).toBe(1)
|
||||
expect(await timeline.transport.acknowledgements()).toHaveLength(2)
|
||||
})
|
||||
|
||||
@@ -51,20 +44,24 @@ test("parses split JSON and a split multibyte code point", async ({ page }) => {
|
||||
})
|
||||
|
||||
test("delivers server heartbeat without mutating the timeline", async ({ page }) => {
|
||||
const timeline = await setupTimeline(page, {
|
||||
messages: [userMessage(), assistantMessage([textPart("prt_transport_steady", "steady")])],
|
||||
})
|
||||
const before = await page.locator("[data-timeline-row]").allTextContents()
|
||||
const timeline = await setupTimeline(page)
|
||||
const partID = "prt_transport_heartbeat_sentinel"
|
||||
const sentinel = await timeline.transport.send(partUpdated(textPart(partID, "heartbeat sentinel")))
|
||||
await timeline.waitForPart(partID)
|
||||
await expect(page.locator(`[data-timeline-part-id="${partID}"] [data-component="markdown"]`)).toHaveAttribute(
|
||||
"data-markdown-ready",
|
||||
"",
|
||||
)
|
||||
const before = await timelineRows(page)
|
||||
const heartbeat = await timeline.transport.heartbeat()
|
||||
|
||||
await timeline.transport.heartbeat()
|
||||
await timeline.settle()
|
||||
|
||||
expect(await page.locator("[data-timeline-row]").allTextContents()).toEqual(before)
|
||||
expect(await timeline.transport.connections()).toHaveLength(1)
|
||||
await expect.poll(() => timelineRows(page)).toEqual(before)
|
||||
expect(heartbeat.connectionID).toBe(sentinel.connectionID)
|
||||
await expect.poll(async () => (await timeline.transport.connections()).length).toBe(1)
|
||||
})
|
||||
|
||||
test("reconnects after a clean close", async ({ page }) => {
|
||||
const timeline = await setupTimeline(page, { eventRetry: 10 })
|
||||
const timeline = await setupTimeline(page)
|
||||
const first = await timeline.transport.waitForConnection()
|
||||
|
||||
await timeline.transport.close()
|
||||
@@ -77,13 +74,14 @@ test("reconnects after a clean close", async ({ page }) => {
|
||||
})
|
||||
|
||||
test("reconnects after a stream error", async ({ page }) => {
|
||||
const timeline = await setupTimeline(page, { eventRetry: 10 })
|
||||
const timeline = await setupTimeline(page)
|
||||
const first = await timeline.transport.waitForConnection()
|
||||
|
||||
await timeline.transport.error("contract failure")
|
||||
const second = await timeline.transport.waitForConnection({ after: first.id })
|
||||
await timeline.transport.send(status("busy"))
|
||||
await timeline.transport.send(partUpdated(textPart("prt_transport_error", "after error")))
|
||||
|
||||
await timeline.waitForPart("prt_transport_error")
|
||||
await expect.poll(async () => (await timeline.transport.connections()).length).toBe(2)
|
||||
expect(second.id).toBeGreaterThan(first.id)
|
||||
expect((await timeline.transport.connections())[0]?.endedBy).toBe("error")
|
||||
@@ -112,5 +110,18 @@ test("passes through non-event fetches", async ({ page }) => {
|
||||
})
|
||||
|
||||
expect(health).toEqual({ healthy: true, version: "2.0.0", pid: 1 })
|
||||
expect(await timeline.transport.connections()).toHaveLength(1)
|
||||
await expect.poll(async () => (await timeline.transport.connections()).length).toBe(1)
|
||||
})
|
||||
|
||||
function timelineRows(page: Page) {
|
||||
return page.locator("[data-timeline-row]").evaluateAll((rows) =>
|
||||
rows.map((row) => ({
|
||||
kind: row.getAttribute("data-timeline-row"),
|
||||
message: row.getAttribute("data-message-id"),
|
||||
parts: Array.from(row.querySelectorAll("[data-timeline-part-id]"), (part) =>
|
||||
part.getAttribute("data-timeline-part-id"),
|
||||
),
|
||||
text: row.textContent,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ test("keeps the terminal session alive when switching session tabs in a workspac
|
||||
const connection = new URL(connections[0]!)
|
||||
expect(connection.pathname).toBe(`/api/pty/${ptyID}/connect`)
|
||||
expect(connection.searchParams.get("location[directory]")).toBe(directory)
|
||||
expect(connection.searchParams.get("ticket")).toBeNull()
|
||||
expect(connection.searchParams.get("ticket")).toBe("e2e-ticket")
|
||||
await writeProbe(page)
|
||||
|
||||
await switchTab(page, titleB)
|
||||
|
||||
@@ -234,18 +234,23 @@ export async function installSseTransport<T>(
|
||||
return {
|
||||
server,
|
||||
async waitForConnection(input = {}) {
|
||||
await page.waitForFunction(
|
||||
const connection = await page.waitForFunction(
|
||||
(after) => {
|
||||
const transport = (window as BrowserTransport).__testSseTransport
|
||||
const connections = transport?.command({ type: "connections" }) as SseConnectionRecord[] | undefined
|
||||
return connections?.some((connection) => connection.id > after)
|
||||
return connections?.findLast((connection) => connection.id > after && connection.endedAt === undefined)
|
||||
},
|
||||
input.after ?? 0,
|
||||
{ timeout: input.timeout },
|
||||
)
|
||||
return (await command<SseConnectionRecord[]>({ type: "connections" })).findLast(
|
||||
(connection) => connection.id > (input.after ?? 0),
|
||||
)!
|
||||
let result: SseConnectionRecord | undefined
|
||||
try {
|
||||
result = await connection.jsonValue()
|
||||
} finally {
|
||||
await connection.dispose()
|
||||
}
|
||||
if (!result) throw new Error("SSE transport connection disappeared while waiting")
|
||||
return result
|
||||
},
|
||||
send(payload, eventOptions) {
|
||||
return command({ type: "send", deliveries: [{ payload, options: eventOptions }], burst: false })
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { expect, type Locator, type Page } from "@playwright/test"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
|
||||
export const APP_READY_TIMEOUT = 30_000
|
||||
|
||||
@@ -9,3 +10,8 @@ export async function expectAppVisible(locator: Locator) {
|
||||
export async function expectSessionTitle(page: Page, title: string) {
|
||||
await expectAppVisible(page.getByRole("heading", { name: title }))
|
||||
}
|
||||
|
||||
export async function expectSessionReady(page: Page, input: { server: string; sessionID: string; title: string }) {
|
||||
await expect(page).toHaveURL(`/server/${base64Encode(input.server)}/session/${input.sessionID}`)
|
||||
await expectSessionTitle(page, input.title)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.8",
|
||||
"version": "1.18.15",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./desktop-menu": "./src/desktop-menu.ts",
|
||||
"./i18n/desktop-native": "./src/i18n/desktop-native.ts",
|
||||
"./updater": "./src/updater.ts",
|
||||
"./wsl/types": "./src/wsl/types.ts",
|
||||
"./vite": "./vite.js",
|
||||
@@ -19,9 +20,9 @@
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"test": "bun run test:unit && bun run test:browser",
|
||||
"test:unit": "bun test --only-failures --preload ./happydom.ts ./src",
|
||||
"test:unit": "bun test --conditions=solid --only-failures --preload ./happydom.ts ./src",
|
||||
"test:browser": "bun test --conditions=browser --preload ./happydom.ts ./test-browser",
|
||||
"test:unit:watch": "bun test --watch --preload ./happydom.ts ./src",
|
||||
"test:unit:watch": "bun test --conditions=solid --watch --preload ./happydom.ts ./src",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:local": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui",
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as Sentry from "@sentry/solid"
|
||||
import { I18nProvider } from "@opencode-ai/ui/context"
|
||||
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
|
||||
import { FileComponentProvider } from "@opencode-ai/ui/context/file"
|
||||
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
|
||||
import { File } from "@opencode-ai/session-ui/file"
|
||||
import { Font } from "@opencode-ai/ui/font"
|
||||
import { Splash } from "@opencode-ai/ui/logo"
|
||||
@@ -155,7 +154,13 @@ function ResolvedDraftRoute(props: { draft: DraftTab }) {
|
||||
|
||||
function UiI18nBridge(props: ParentProps) {
|
||||
const language = useLanguage()
|
||||
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
|
||||
return (
|
||||
<I18nProvider
|
||||
value={{ locale: language.intl, layoutLocale: language.layoutLocale, t: language.t, plural: language.plural }}
|
||||
>
|
||||
{props.children}
|
||||
</I18nProvider>
|
||||
)
|
||||
}
|
||||
|
||||
declare global {
|
||||
@@ -218,7 +223,7 @@ function DesktopCommands() {
|
||||
if (platform.platform === "desktop" && platform.exportDebugLogs) {
|
||||
commands.push({
|
||||
id: "logs.export",
|
||||
title: "Export logs",
|
||||
title: language.t("command.logs.export"),
|
||||
category: language.t("command.category.settings"),
|
||||
onSelect: () => {
|
||||
void platform.exportDebugLogs?.()
|
||||
@@ -267,7 +272,12 @@ function DraftProviders(props: ParentProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
|
||||
export function AppBaseProviders(
|
||||
props: ParentProps<{
|
||||
locale?: Locale
|
||||
onNativeTranslations?: Parameters<typeof LanguageProvider>[0]["onNativeTranslations"]
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<MetaProvider>
|
||||
<Font />
|
||||
@@ -276,7 +286,7 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
|
||||
void window.api?.setTitlebar?.({ mode, scheme })
|
||||
}}
|
||||
>
|
||||
<LanguageProvider locale={props.locale}>
|
||||
<LanguageProvider locale={props.locale} onNativeTranslations={props.onNativeTranslations}>
|
||||
<UiI18nBridge>
|
||||
<ErrorBoundary
|
||||
fallback={(error) => {
|
||||
@@ -287,9 +297,7 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
|
||||
<QueryProvider>
|
||||
<WslServersProvider>
|
||||
<DialogProvider>
|
||||
<MarkedProvider>
|
||||
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
|
||||
</MarkedProvider>
|
||||
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
|
||||
</DialogProvider>
|
||||
</WslServersProvider>
|
||||
</QueryProvider>
|
||||
|
||||
@@ -60,35 +60,44 @@ function Cell(props: {
|
||||
label: string
|
||||
tip: string
|
||||
value: string
|
||||
wide?: boolean
|
||||
span?: 2 | 3
|
||||
}) {
|
||||
const content = () => (
|
||||
<div
|
||||
classList={{
|
||||
"flex min-w-0 items-center": true,
|
||||
"min-h-[20px] w-fit flex-row justify-start gap-1.5 px-1.5 py-0.5 text-left": !!props.inline,
|
||||
"min-h-[20px] w-fit justify-start px-1.5 py-0.5 text-left": !!props.inline,
|
||||
"justify-center text-center": !props.inline,
|
||||
"min-h-[42px] w-full flex-col rounded-[8px] px-0.5 py-1": !props.inline,
|
||||
"col-span-2": !!props.wide && !props.inline,
|
||||
"col-span-2": props.span === 2 && !props.inline,
|
||||
"col-span-3": props.span === 3 && !props.inline,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
"text-[10px] leading-none font-black uppercase tracking-[0.04em] opacity-70": true,
|
||||
"flex min-w-0": true,
|
||||
"-translate-y-px items-baseline gap-1.5": !!props.inline,
|
||||
"flex-col items-center": !props.inline,
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</div>
|
||||
<div
|
||||
classList={{
|
||||
"uppercase leading-none font-bold tabular-nums": true,
|
||||
"text-[11px]": !!props.inline,
|
||||
"text-[13px] sm:text-[14px]": !props.inline,
|
||||
"text-text-on-critical-base": !!props.bad,
|
||||
"opacity-70": !!props.dim,
|
||||
}}
|
||||
>
|
||||
{props.value}
|
||||
<div
|
||||
classList={{
|
||||
"text-[10px] leading-none font-black uppercase tracking-[0.04em] opacity-70": true,
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</div>
|
||||
<div
|
||||
classList={{
|
||||
"uppercase leading-none font-bold tabular-nums": true,
|
||||
"text-[11px]": !!props.inline,
|
||||
"text-[13px] sm:text-[14px]": !props.inline,
|
||||
"text-text-on-critical-base": !!props.bad,
|
||||
"opacity-70": !!props.dim,
|
||||
}}
|
||||
>
|
||||
{props.value}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -108,37 +117,50 @@ function Cell(props: {
|
||||
)
|
||||
}
|
||||
|
||||
function FocusCell(props: { active: boolean; inline?: boolean; onClick: () => void }) {
|
||||
function ToggleCell(props: {
|
||||
active: boolean
|
||||
inline?: boolean
|
||||
label: string
|
||||
onClick: () => void
|
||||
tip: string
|
||||
value: string
|
||||
}) {
|
||||
const content = () => (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Force focus styles on all interactive elements"
|
||||
aria-label={`${props.label}: ${props.value}`}
|
||||
aria-pressed={props.active}
|
||||
classList={{
|
||||
"flex min-w-0 items-center font-mono uppercase hover:bg-surface-raised-base focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-border-focus": true,
|
||||
"min-h-[20px] w-fit flex-row justify-start gap-1.5 rounded px-1.5 py-0.5 text-left": !!props.inline,
|
||||
"min-h-[20px] w-fit justify-start rounded px-1.5 py-0.5 text-left": !!props.inline,
|
||||
"min-h-[42px] w-full flex-col justify-center rounded-[8px] px-0.5 py-1 text-center": !props.inline,
|
||||
"bg-surface-raised-base text-text-strong": props.active,
|
||||
}}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<span class="text-[10px] leading-none font-black tracking-[0.04em] opacity-70">FOCUS</span>
|
||||
<span classList={{ "leading-none font-bold": true, "text-[11px]": !!props.inline, "text-[13px]": !props.inline }}>
|
||||
{props.active ? "ON" : "OFF"}
|
||||
<span
|
||||
classList={{
|
||||
flex: true,
|
||||
"-translate-y-px items-baseline gap-1.5": !!props.inline,
|
||||
"flex-col items-center": !props.inline,
|
||||
}}
|
||||
>
|
||||
<span class="text-[10px] leading-none font-black tracking-[0.04em] opacity-70">{props.label}</span>
|
||||
<span class="text-[11px] leading-none font-bold">{props.value}</span>
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
if (props.inline) {
|
||||
return (
|
||||
<TooltipV2 value="Force focus styles on all interactive elements" placement="top">
|
||||
<TooltipV2 value={props.tip} placement="top">
|
||||
{content()}
|
||||
</TooltipV2>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip value="Force focus styles on all interactive elements" placement="top">
|
||||
<Tooltip value={props.tip} placement="top">
|
||||
{content()}
|
||||
</Tooltip>
|
||||
)
|
||||
@@ -457,7 +479,7 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
"gap-[9px]": !!props.inline,
|
||||
"gap-px": !props.inline,
|
||||
"flex w-full flex-nowrap items-center justify-start": !!props.inline,
|
||||
"grid-cols-5": !props.inline,
|
||||
"grid-cols-4": !props.inline,
|
||||
grid: !props.inline,
|
||||
}}
|
||||
>
|
||||
@@ -539,10 +561,25 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
bad={bad(heap(), 0.8)}
|
||||
dim={state.heap.used === undefined}
|
||||
inline={props.inline}
|
||||
wide={!platform.setForceFocus}
|
||||
span={platform.setForceFocus ? 2 : 3}
|
||||
/>
|
||||
<ToggleCell
|
||||
active={language.direction() === "rtl"}
|
||||
inline={props.inline}
|
||||
label={language.t("debugBar.direction.label")}
|
||||
tip={language.t("debugBar.direction.tip")}
|
||||
value={language.t(`debugBar.direction.${language.direction()}`)}
|
||||
onClick={() => language.setDirection(language.direction() === "rtl" ? "ltr" : "rtl")}
|
||||
/>
|
||||
{platform.setForceFocus && (
|
||||
<FocusCell active={state.focus} inline={props.inline} onClick={() => void toggleFocus()} />
|
||||
<ToggleCell
|
||||
active={state.focus}
|
||||
inline={props.inline}
|
||||
label={language.t("debugBar.focus.label")}
|
||||
tip={language.t("debugBar.focus.tip")}
|
||||
value={language.t(state.focus ? "debugBar.focus.on" : "debugBar.focus.off")}
|
||||
onClick={() => void toggleFocus()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { IntegrationMethod, IntegrationOauthConnectOutput } from "@opencode-ai/client/promise"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||
@@ -13,33 +12,21 @@ import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { DialogBody, DialogHeader, DialogTitle, DialogV2 } from "@opencode-ai/ui/v2/dialog-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import {
|
||||
type Accessor,
|
||||
type Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createResource,
|
||||
createUniqueId,
|
||||
For,
|
||||
Match,
|
||||
onCleanup,
|
||||
onMount,
|
||||
Show,
|
||||
Switch,
|
||||
} from "solid-js"
|
||||
import { createStore, produce } from "solid-js/store"
|
||||
import { type Accessor, type Component, createMemo, createUniqueId, For, Match, onMount, Show, Switch } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { Link } from "@/components/link"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { ExternalLink } from "@/components/external-link"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { popularProviders, useProviders } from "@/hooks/use-providers"
|
||||
import { CustomProviderForm } from "./dialog-custom-provider"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { createProviderConnectionController, type ProviderConnectMethod } from "./provider-connection-controller"
|
||||
|
||||
const CUSTOM_ID = "_custom"
|
||||
type ConnectMethod = Extract<IntegrationMethod, { type: "key" | "oauth" }>
|
||||
type IntegrationForm = NonNullable<ProviderConnectMethod["form"]>[number]
|
||||
type StringForm = Extract<IntegrationForm, { type: "string" }>
|
||||
|
||||
export function useProviderConnectController(options: { onBack?: () => void } = {}) {
|
||||
const [store, setStore] = createStore({ selected: undefined as string | undefined })
|
||||
@@ -383,120 +370,29 @@ function ProviderConnection(props: {
|
||||
}) {
|
||||
const dialog = useDialog()
|
||||
const serverSync = useServerSync()
|
||||
const serverSDK = useServerSDK()
|
||||
const params = useParams()
|
||||
const language = useLanguage()
|
||||
const settings = useSettings()
|
||||
const newLayout = settings.general.newLayoutDesigns
|
||||
const providers = useProviders(() => props.directory?.())
|
||||
const directory = () => props.directory?.() ?? decode64(params.dir)
|
||||
const location = () => {
|
||||
const value = directory()
|
||||
return value ? { directory: value } : undefined
|
||||
}
|
||||
|
||||
const alive = { value: true }
|
||||
const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
||||
|
||||
onCleanup(() => {
|
||||
alive.value = false
|
||||
if (timer.current === undefined) return
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
})
|
||||
|
||||
const provider = createMemo(
|
||||
() => providers.all().get(props.provider) ?? serverSync().data.provider.all.get(props.provider)!,
|
||||
)
|
||||
const fallback = createMemo<ConnectMethod[]>(() => [
|
||||
{
|
||||
type: "key" as const,
|
||||
label: language.t("provider.connect.method.apiKey"),
|
||||
const controller = createProviderConnectionController({
|
||||
provider: () => props.provider,
|
||||
directory,
|
||||
onComplete: () => {
|
||||
dialog.close()
|
||||
showToast({
|
||||
variant: "success",
|
||||
icon: "circle-check",
|
||||
title: language.t("provider.connect.toast.connected.title", { provider: provider().name }),
|
||||
description: language.t("provider.connect.toast.connected.description", { provider: provider().name }),
|
||||
})
|
||||
},
|
||||
])
|
||||
const [integration] = createResource(
|
||||
() => ({ provider: props.provider, directory: directory() }),
|
||||
(input) =>
|
||||
serverSDK()
|
||||
.api.integration.get({
|
||||
integrationID: input.provider,
|
||||
location: input.directory ? { directory: input.directory } : undefined,
|
||||
})
|
||||
.then((result) => result.data),
|
||||
)
|
||||
const loading = createMemo(() => integration.loading)
|
||||
const methods = createMemo<ConnectMethod[]>(() => {
|
||||
const values = integration.latest?.methods.filter(
|
||||
(method): method is ConnectMethod => method.type === "key" || method.type === "oauth",
|
||||
)
|
||||
return values?.length ? values : fallback()
|
||||
})
|
||||
const [store, setStore] = createStore({
|
||||
methodIndex: undefined as undefined | number,
|
||||
authorization: undefined as undefined | IntegrationOauthConnectOutput["data"],
|
||||
promptInputs: undefined as undefined | Record<string, string>,
|
||||
state: "pending" as undefined | "pending" | "complete" | "error" | "prompt",
|
||||
error: undefined as string | undefined,
|
||||
})
|
||||
|
||||
type Action =
|
||||
| { type: "method.select"; index: number }
|
||||
| { type: "method.reset" }
|
||||
| { type: "auth.prompt" }
|
||||
| { type: "auth.inputs"; inputs: Record<string, string> }
|
||||
| { type: "auth.pending" }
|
||||
| { type: "auth.complete"; authorization: IntegrationOauthConnectOutput["data"] }
|
||||
| { type: "auth.error"; error: string }
|
||||
|
||||
function dispatch(action: Action) {
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
if (action.type === "method.select") {
|
||||
draft.methodIndex = action.index
|
||||
draft.authorization = undefined
|
||||
draft.promptInputs = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "method.reset") {
|
||||
draft.methodIndex = undefined
|
||||
draft.authorization = undefined
|
||||
draft.promptInputs = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.prompt") {
|
||||
draft.state = "prompt"
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.inputs") {
|
||||
draft.promptInputs = action.inputs
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.pending") {
|
||||
draft.state = "pending"
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.complete") {
|
||||
draft.state = "complete"
|
||||
draft.authorization = action.authorization
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
draft.state = "error"
|
||||
draft.error = action.error
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const method = createMemo(() => (store.methodIndex !== undefined ? methods().at(store.methodIndex!) : undefined))
|
||||
|
||||
const methodLabel = (value?: { type?: string; label?: string }) => {
|
||||
if (!value) return ""
|
||||
if (value.type === "key") return language.t("provider.connect.method.apiKey")
|
||||
@@ -509,160 +405,116 @@ function ProviderConnection(props: {
|
||||
const hint = suffix?.[1]
|
||||
return {
|
||||
label: suffix ? label.slice(0, -suffix[0].length) : label,
|
||||
hint: hint ? hint[0].toUpperCase() + hint.slice(1) : value?.type === "key" ? "Browser" : undefined,
|
||||
hint:
|
||||
hint?.toLowerCase() === "headless"
|
||||
? language.t("provider.connect.method.headless")
|
||||
: hint?.toLowerCase() === "browser" || (!hint && value?.type === "key")
|
||||
? language.t("provider.connect.method.browser")
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function formatError(value: unknown, fallback: string): string {
|
||||
if (value && typeof value === "object" && "data" in value) {
|
||||
const data = (value as { data?: { message?: unknown } }).data
|
||||
if (typeof data?.message === "string" && data.message) return data.message
|
||||
}
|
||||
if (value && typeof value === "object" && "error" in value) {
|
||||
const nested = formatError((value as { error?: unknown }).error, "")
|
||||
if (nested) return nested
|
||||
}
|
||||
if (value && typeof value === "object" && "message" in value) {
|
||||
const message = (value as { message?: unknown }).message
|
||||
if (typeof message === "string" && message) return message
|
||||
}
|
||||
if (value instanceof Error && value.message) return value.message
|
||||
if (typeof value === "string" && value) return value
|
||||
return fallback
|
||||
}
|
||||
|
||||
async function selectMethod(index: number, inputs?: Record<string, string>) {
|
||||
if (timer.current !== undefined) {
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
}
|
||||
|
||||
const method = methods()[index]
|
||||
dispatch({ type: "method.select", index })
|
||||
|
||||
if (method.type === "oauth") {
|
||||
if (method.prompts?.length && !inputs) {
|
||||
dispatch({ type: "auth.prompt" })
|
||||
return
|
||||
}
|
||||
dispatch({ type: "auth.pending" })
|
||||
await serverSDK()
|
||||
.api.integration.oauth.connect({
|
||||
integrationID: props.provider,
|
||||
methodID: method.id,
|
||||
inputs: inputs ?? {},
|
||||
location: location(),
|
||||
})
|
||||
.then((x) => {
|
||||
if (!alive.value) return
|
||||
dispatch({ type: "auth.complete", authorization: x.data })
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!alive.value) return
|
||||
dispatch({ type: "auth.error", error: formatError(e, language.t("common.requestFailed")) })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function AuthPromptsView() {
|
||||
function AuthFormView() {
|
||||
const [formStore, setFormStore] = createStore({
|
||||
value: {} as Record<string, string>,
|
||||
index: 0,
|
||||
})
|
||||
|
||||
const prompts = createMemo(() => {
|
||||
const value = method()
|
||||
return value?.type === "oauth" ? (value.prompts ?? []) : []
|
||||
const fields = createMemo<StringForm[]>(() => {
|
||||
const value = controller.currentMethod()
|
||||
return (value?.form ?? []).flatMap((field) => (field.type === "string" ? [field] : []))
|
||||
})
|
||||
const matches = (prompt: NonNullable<ReturnType<typeof prompts>[number]>, value: Record<string, string>) => {
|
||||
if (!prompt.when) return true
|
||||
const actual = value[prompt.when.key]
|
||||
if (actual === undefined) return false
|
||||
return prompt.when.op === "eq" ? actual === prompt.when.value : actual !== prompt.when.value
|
||||
const matches = (field: StringForm, value: Record<string, string>) => {
|
||||
return (field.when ?? []).every((condition) => {
|
||||
const actual = value[condition.key]
|
||||
if (actual === undefined) return false
|
||||
return condition.op === "eq" ? actual === condition.value : actual !== condition.value
|
||||
})
|
||||
}
|
||||
const current = createMemo(() => {
|
||||
const all = prompts()
|
||||
const index = all.findIndex((prompt, index) => index >= formStore.index && matches(prompt, formStore.value))
|
||||
if (index === -1) return
|
||||
const all = fields()
|
||||
const index = all.findIndex((field, index) => index >= formStore.index && matches(field, formStore.value))
|
||||
if (index === -1) return undefined
|
||||
return {
|
||||
index,
|
||||
prompt: all[index],
|
||||
field: all[index],
|
||||
}
|
||||
})
|
||||
const valid = createMemo(() => {
|
||||
const item = current()
|
||||
if (!item || item.prompt.type !== "text") return false
|
||||
const value = formStore.value[item.prompt.key] ?? ""
|
||||
return value.trim().length > 0
|
||||
if (!item || item.field.options) return false
|
||||
if (!item.field.required) return true
|
||||
return (formStore.value[item.field.key] ?? "").trim().length > 0
|
||||
})
|
||||
|
||||
async function next(index: number, value: Record<string, string>) {
|
||||
if (store.methodIndex === undefined) return
|
||||
const next = prompts().findIndex((prompt, i) => i > index && matches(prompt, value))
|
||||
const selected = controller.methodIndex()
|
||||
if (selected === undefined) return
|
||||
const next = fields().findIndex((field, i) => i > index && matches(field, value))
|
||||
if (next !== -1) {
|
||||
setFormStore("index", next)
|
||||
return
|
||||
}
|
||||
await selectMethod(store.methodIndex, value)
|
||||
await controller.auth.select(selected, value)
|
||||
}
|
||||
|
||||
async function handleSubmit(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
const item = current()
|
||||
if (!item || item.prompt.type !== "text") return
|
||||
if (!item || item.field.options) return
|
||||
if (!valid()) return
|
||||
await next(item.index, formStore.value)
|
||||
}
|
||||
|
||||
const item = () => current()
|
||||
const text = createMemo(() => {
|
||||
const prompt = item()?.prompt
|
||||
if (!prompt || prompt.type !== "text") return
|
||||
return prompt
|
||||
const field = item()?.field
|
||||
if (!field || field.options) return undefined
|
||||
return field
|
||||
})
|
||||
const select = createMemo(() => {
|
||||
const prompt = item()?.prompt
|
||||
if (!prompt || prompt.type !== "select") return
|
||||
return prompt
|
||||
const field = item()?.field
|
||||
if (!field?.options) return undefined
|
||||
return field
|
||||
})
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
|
||||
<Switch>
|
||||
<Match when={item()?.prompt.type === "text"}>
|
||||
<Match when={item()?.field.options === undefined}>
|
||||
<TextField
|
||||
type="text"
|
||||
label={text()?.message ?? ""}
|
||||
label={text()?.title ?? ""}
|
||||
placeholder={text()?.placeholder}
|
||||
value={text() ? (formStore.value[text()!.key] ?? "") : ""}
|
||||
onChange={(value) => {
|
||||
const prompt = text()
|
||||
if (!prompt) return
|
||||
setFormStore("value", prompt.key, value)
|
||||
const field = text()
|
||||
if (!field) return
|
||||
setFormStore("value", field.key, value)
|
||||
}}
|
||||
/>
|
||||
<Button class="w-auto" type="submit" size="large" variant="primary" disabled={!valid()}>
|
||||
{language.t("common.continue")}
|
||||
</Button>
|
||||
</Match>
|
||||
<Match when={item()?.prompt.type === "select"}>
|
||||
<Match when={item()?.field.options !== undefined}>
|
||||
<div class="w-full flex flex-col gap-1.5">
|
||||
<div class="text-14-regular text-text-base">{select()?.message}</div>
|
||||
<div class="text-14-regular text-text-base">{select()?.title}</div>
|
||||
<div>
|
||||
<List
|
||||
class="px-3"
|
||||
items={select()?.options ?? []}
|
||||
key={(x) => x.value}
|
||||
current={select()?.options.find((x) => x.value === formStore.value[select()!.key])}
|
||||
current={select()?.options?.find((x) => x.value === formStore.value[select()!.key])}
|
||||
onSelect={(value) => {
|
||||
if (!value) return
|
||||
const prompt = select()
|
||||
if (!prompt) return
|
||||
const field = select()
|
||||
if (!field) return
|
||||
const nextValue = {
|
||||
...formStore.value,
|
||||
[prompt.key]: value.value,
|
||||
[field.key]: value.value,
|
||||
}
|
||||
setFormStore("value", prompt.key, value.value)
|
||||
setFormStore("value", field.key, value.value)
|
||||
void next(item()!.index, nextValue)
|
||||
}}
|
||||
>
|
||||
@@ -672,7 +524,7 @@ function ProviderConnection(props: {
|
||||
<div class="w-2.5 h-0.5 ml-0 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
|
||||
</div>
|
||||
<span>{option.label}</span>
|
||||
<span class="text-14-regular text-text-weak">{option.hint}</span>
|
||||
<span class="text-14-regular text-text-weak">{option.description}</span>
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
@@ -693,32 +545,9 @@ function ProviderConnection(props: {
|
||||
listRef?.onKeyDown(e)
|
||||
}
|
||||
|
||||
let auto = false
|
||||
createEffect(() => {
|
||||
if (auto) return
|
||||
if (loading()) return
|
||||
if (methods().length === 1) {
|
||||
auto = true
|
||||
void selectMethod(0)
|
||||
}
|
||||
})
|
||||
|
||||
async function complete() {
|
||||
await serverSync()
|
||||
.refreshProviders()
|
||||
.catch(() => undefined)
|
||||
dialog.close()
|
||||
showToast({
|
||||
variant: "success",
|
||||
icon: "circle-check",
|
||||
title: language.t("provider.connect.toast.connected.title", { provider: provider().name }),
|
||||
description: language.t("provider.connect.toast.connected.description", { provider: provider().name }),
|
||||
})
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (methods().length > 1 && store.methodIndex !== undefined) {
|
||||
dispatch({ type: "method.reset" })
|
||||
if (controller.methods().length > 1 && controller.methodIndex() !== undefined) {
|
||||
controller.auth.reset()
|
||||
return
|
||||
}
|
||||
props.onBack()
|
||||
@@ -734,14 +563,14 @@ function ProviderConnection(props: {
|
||||
{language.t("provider.connect.selectMethod", { provider: provider().name })}
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<For each={methods()}>
|
||||
<For each={controller.methods()}>
|
||||
{(item, index) => {
|
||||
const details = () => methodDetails(item)
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class="group flex h-9 w-full items-center gap-2 rounded-md px-3 text-left text-[13px] leading-5 tracking-[-0.04px] hover:bg-v2-overlay-simple-overlay-hover focus-visible:bg-v2-overlay-simple-overlay-hover focus-visible:outline-none"
|
||||
onClick={() => void selectMethod(index())}
|
||||
onClick={() => void controller.auth.select(index())}
|
||||
>
|
||||
<span class="flex h-2 w-4 shrink-0 items-center justify-center rounded-[1px] bg-v2-background-bg-base shadow-[var(--v2-elevation-button-neutral)]">
|
||||
<span class="hidden h-0.5 w-2.5 bg-v2-icon-icon-base group-hover:block group-focus-visible:block" />
|
||||
@@ -769,11 +598,11 @@ function ProviderConnection(props: {
|
||||
ref={(ref) => {
|
||||
listRef = ref
|
||||
}}
|
||||
items={methods}
|
||||
items={controller.methods}
|
||||
key={(m) => m?.label ?? m?.type}
|
||||
onSelect={async (selected, index) => {
|
||||
if (!selected) return
|
||||
void selectMethod(index)
|
||||
void controller.auth.select(index)
|
||||
}}
|
||||
>
|
||||
{(i) => (
|
||||
@@ -806,9 +635,9 @@ function ProviderConnection(props: {
|
||||
async function handleSubmit(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
const form = e.currentTarget as HTMLFormElement
|
||||
const formData = new FormData(form)
|
||||
const apiKey = formData.get("apiKey") as string
|
||||
if (!(e.currentTarget instanceof HTMLFormElement)) return
|
||||
const value = new FormData(e.currentTarget).get("apiKey")
|
||||
const apiKey = typeof value === "string" ? value : ""
|
||||
|
||||
if (!apiKey?.trim()) {
|
||||
setFormStore("error", language.t("provider.connect.apiKey.required"))
|
||||
@@ -816,12 +645,7 @@ function ProviderConnection(props: {
|
||||
}
|
||||
|
||||
setFormStore("error", undefined)
|
||||
await serverSDK().api.integration.connect.key({
|
||||
integrationID: props.provider,
|
||||
location: location(),
|
||||
key: apiKey,
|
||||
})
|
||||
await complete()
|
||||
await controller.auth.connectKey(apiKey)
|
||||
}
|
||||
|
||||
if (newLayout())
|
||||
@@ -836,12 +660,12 @@ function ProviderConnection(props: {
|
||||
<div>{language.t("provider.connect.opencodeZen.line2")}</div>
|
||||
<div>
|
||||
{language.t("provider.connect.opencodeZen.visit.prefix")}
|
||||
<Link
|
||||
<ExternalLink
|
||||
href="https://opencode.ai/zen"
|
||||
class="text-v2-text-text-base focus-visible:rounded-xs focus-visible:outline-2 focus-visible:outline-v2-border-border-focus"
|
||||
>
|
||||
{language.t("provider.connect.opencodeZen.visit.link")}
|
||||
</Link>
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.opencodeZen.visit.suffix")}
|
||||
</div>
|
||||
</div>
|
||||
@@ -886,9 +710,9 @@ function ProviderConnection(props: {
|
||||
<div class="text-14-regular text-text-base">{language.t("provider.connect.opencodeZen.line2")}</div>
|
||||
<div class="text-14-regular text-text-base">
|
||||
{language.t("provider.connect.opencodeZen.visit.prefix")}
|
||||
<Link href="https://opencode.ai/zen" tabIndex={-1}>
|
||||
<ExternalLink href="https://opencode.ai/zen" tabIndex={-1}>
|
||||
{language.t("provider.connect.opencodeZen.visit.link")}
|
||||
</Link>
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.opencodeZen.visit.suffix")}
|
||||
</div>
|
||||
</div>
|
||||
@@ -936,9 +760,9 @@ function ProviderConnection(props: {
|
||||
async function handleSubmit(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
const form = e.currentTarget as HTMLFormElement
|
||||
const formData = new FormData(form)
|
||||
const code = formData.get("code") as string
|
||||
if (!(e.currentTarget instanceof HTMLFormElement)) return
|
||||
const value = new FormData(e.currentTarget).get("code")
|
||||
const code = typeof value === "string" ? value : ""
|
||||
|
||||
if (!code?.trim()) {
|
||||
setFormStore("error", language.t("provider.connect.oauth.code.required"))
|
||||
@@ -946,20 +770,7 @@ function ProviderConnection(props: {
|
||||
}
|
||||
|
||||
setFormStore("error", undefined)
|
||||
const result = await serverSDK()
|
||||
.api.integration.oauth.complete({
|
||||
integrationID: props.provider,
|
||||
attemptID: store.authorization!.attemptID,
|
||||
location: location(),
|
||||
code,
|
||||
})
|
||||
.then(() => ({ ok: true as const }))
|
||||
.catch((error) => ({ ok: false as const, error }))
|
||||
if (result.ok) {
|
||||
await complete()
|
||||
return
|
||||
}
|
||||
setFormStore("error", formatError(result.error, language.t("provider.connect.oauth.code.invalid")))
|
||||
setFormStore("error", await controller.auth.completeCode(code))
|
||||
}
|
||||
|
||||
if (newLayout())
|
||||
@@ -967,14 +778,14 @@ function ProviderConnection(props: {
|
||||
<div class="flex flex-col gap-5 px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-muted">
|
||||
<div>
|
||||
{language.t("provider.connect.oauth.code.visit.prefix")}
|
||||
<Link href={store.authorization!.url} class="text-v2-text-text-base">
|
||||
<ExternalLink href={controller.authorization()!.url} class="text-v2-text-text-base">
|
||||
{language.t("provider.connect.oauth.code.visit.link")}
|
||||
</Link>
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.oauth.code.visit.suffix", { provider: provider().name })}
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-5 self-stretch">
|
||||
<label class="flex w-full flex-col gap-1 font-[530] leading-4 text-v2-text-text-base">
|
||||
{language.t("provider.connect.oauth.code.label", { method: method()?.label ?? "" })}
|
||||
{language.t("provider.connect.oauth.code.label", { method: controller.currentMethod()?.label ?? "" })}
|
||||
<TextInputV2
|
||||
ref={codeInput}
|
||||
class="!w-full"
|
||||
@@ -1006,7 +817,9 @@ function ProviderConnection(props: {
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="text-14-regular text-text-base">
|
||||
{language.t("provider.connect.oauth.code.visit.prefix")}
|
||||
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.code.visit.link")}</Link>
|
||||
<ExternalLink href={controller.authorization()!.url}>
|
||||
{language.t("provider.connect.oauth.code.visit.link")}
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.oauth.code.visit.suffix", { provider: provider().name })}
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
|
||||
@@ -1014,7 +827,9 @@ function ProviderConnection(props: {
|
||||
autofocus={!newLayout()}
|
||||
ref={codeInput}
|
||||
type="text"
|
||||
label={language.t("provider.connect.oauth.code.label", { method: method()?.label ?? "" })}
|
||||
label={language.t("provider.connect.oauth.code.label", {
|
||||
method: controller.currentMethod()?.label ?? "",
|
||||
})}
|
||||
placeholder={language.t("provider.connect.oauth.code.placeholder")}
|
||||
name="code"
|
||||
value={formStore.value}
|
||||
@@ -1032,52 +847,20 @@ function ProviderConnection(props: {
|
||||
|
||||
function OAuthAutoView() {
|
||||
const code = createMemo(() => {
|
||||
const instructions = store.authorization?.instructions
|
||||
const instructions = controller.authorization()?.instructions
|
||||
if (instructions?.includes(":")) {
|
||||
return instructions.split(":").pop()?.trim()
|
||||
}
|
||||
return instructions
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
const poll = async () => {
|
||||
const authorization = store.authorization
|
||||
if (!authorization || !alive.value) return
|
||||
const result = await serverSDK()
|
||||
.api.integration.oauth.status({
|
||||
integrationID: props.provider,
|
||||
attemptID: authorization.attemptID,
|
||||
location: location(),
|
||||
})
|
||||
.then((value) => ({ ok: true as const, status: value.data }))
|
||||
.catch((error) => ({ ok: false as const, error }))
|
||||
if (!alive.value) return
|
||||
if (!result.ok) {
|
||||
dispatch({ type: "auth.error", error: formatError(result.error, language.t("common.requestFailed")) })
|
||||
return
|
||||
}
|
||||
if (result.status.status === "complete") {
|
||||
await complete()
|
||||
return
|
||||
}
|
||||
if (result.status.status === "failed") {
|
||||
dispatch({ type: "auth.error", error: result.status.message })
|
||||
return
|
||||
}
|
||||
if (result.status.status === "expired") {
|
||||
dispatch({ type: "auth.error", error: language.t("common.requestFailed") })
|
||||
return
|
||||
}
|
||||
timer.current = setTimeout(poll, 1_000)
|
||||
}
|
||||
void poll()
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="text-14-regular text-text-base">
|
||||
{language.t("provider.connect.oauth.auto.visit.prefix")}
|
||||
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.auto.visit.link")}</Link>
|
||||
<ExternalLink href={controller.authorization()!.url}>
|
||||
{language.t("provider.connect.oauth.auto.visit.link")}
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.oauth.auto.visit.suffix", { provider: provider().name })}
|
||||
</div>
|
||||
<TextField
|
||||
@@ -1110,7 +893,9 @@ function ProviderConnection(props: {
|
||||
}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={props.provider === "anthropic" && method()?.label?.toLowerCase().includes("max")}>
|
||||
<Match
|
||||
when={props.provider === "anthropic" && controller.currentMethod()?.label?.toLowerCase().includes("max")}
|
||||
>
|
||||
{language.t("provider.connect.title.anthropicProMax")}
|
||||
</Match>
|
||||
<Match when={true}>{language.t("provider.connect.title", { provider: provider().name })}</Match>
|
||||
@@ -1121,10 +906,10 @@ function ProviderConnection(props: {
|
||||
<div
|
||||
onKeyDown={handleKey}
|
||||
tabIndex={newLayout() ? undefined : 0}
|
||||
autofocus={!newLayout() && store.methodIndex === undefined ? true : undefined}
|
||||
autofocus={!newLayout() && controller.methodIndex() === undefined ? true : undefined}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={loading()}>
|
||||
<Match when={controller.loading()}>
|
||||
<div class="text-14-regular text-text-base">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<Spinner />
|
||||
@@ -1132,10 +917,10 @@ function ProviderConnection(props: {
|
||||
</div>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={store.methodIndex === undefined}>
|
||||
<Match when={controller.methodIndex() === undefined}>
|
||||
<MethodSelection />
|
||||
</Match>
|
||||
<Match when={store.state === "pending"}>
|
||||
<Match when={controller.auth.state() === "pending"}>
|
||||
<div class="text-14-regular text-text-base">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<Spinner />
|
||||
@@ -1143,26 +928,26 @@ function ProviderConnection(props: {
|
||||
</div>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={store.state === "prompt"}>
|
||||
<AuthPromptsView />
|
||||
<Match when={controller.auth.state() === "form"}>
|
||||
<AuthFormView />
|
||||
</Match>
|
||||
<Match when={store.state === "error"}>
|
||||
<Match when={controller.auth.state() === "error"}>
|
||||
<div class="text-14-regular text-text-base">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
|
||||
<span>{language.t("provider.connect.status.failed", { error: store.error ?? "" })}</span>
|
||||
<span>{language.t("provider.connect.status.failed", { error: controller.auth.error() ?? "" })}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={method()?.type === "key"}>
|
||||
<Match when={controller.currentMethod()?.type === "key"}>
|
||||
<ApiAuthView />
|
||||
</Match>
|
||||
<Match when={method()?.type === "oauth"}>
|
||||
<Match when={controller.currentMethod()?.type === "oauth"}>
|
||||
<Switch>
|
||||
<Match when={store.authorization?.mode === "code"}>
|
||||
<Match when={controller.authorization()?.mode === "code"}>
|
||||
<OAuthCodeView />
|
||||
</Match>
|
||||
<Match when={store.authorization?.mode === "auto"}>
|
||||
<Match when={controller.authorization()?.mode === "auto"}>
|
||||
<OAuthAutoView />
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
@@ -8,8 +8,7 @@ import { TextField } from "@opencode-ai/ui/text-field"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { batch, For } from "solid-js"
|
||||
import { createStore, produce } from "solid-js/store"
|
||||
import { Link } from "@/components/link"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { ExternalLink } from "@/components/external-link"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { type FormState, headerRow, modelRow, validateCustomProvider } from "./dialog-custom-provider-form"
|
||||
@@ -43,7 +42,6 @@ export function DialogCustomProvider(props: Props) {
|
||||
export function CustomProviderForm(props: { autofocus?: boolean } = {}) {
|
||||
const dialog = useDialog()
|
||||
const serverSync = useServerSync()
|
||||
const serverSDK = useServerSDK()
|
||||
const language = useLanguage()
|
||||
|
||||
const [form, setForm] = createStore<FormState>({
|
||||
@@ -132,7 +130,7 @@ export function CustomProviderForm(props: { autofocus?: boolean } = {}) {
|
||||
const saveMutation = useMutation(() => ({
|
||||
mutationFn: async (result: NonNullable<ReturnType<typeof validate>>): Promise<typeof result> => {
|
||||
// TODO: Restore custom providers when V2 exposes config and arbitrary credential APIs.
|
||||
throw new Error(`Custom provider ${result.providerID} is unavailable`)
|
||||
throw new Error(language.t("provider.custom.unavailable"))
|
||||
},
|
||||
onSuccess: (result) => {
|
||||
dialog.close()
|
||||
@@ -168,9 +166,9 @@ export function CustomProviderForm(props: { autofocus?: boolean } = {}) {
|
||||
<form onSubmit={save} class="px-2.5 pb-6 flex flex-col gap-6">
|
||||
<p class="text-14-regular text-text-base">
|
||||
{language.t("provider.custom.description.prefix")}
|
||||
<Link href="https://opencode.ai/docs/providers/#custom-provider" tabIndex={-1}>
|
||||
<ExternalLink href="https://opencode.ai/docs/providers/#custom-provider" tabIndex={-1}>
|
||||
{language.t("provider.custom.description.link")}
|
||||
</Link>
|
||||
</ExternalLink>
|
||||
{language.t("provider.custom.description.suffix")}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ interface DialogSelectDirectoryProps {
|
||||
server: ServerConnection.Any
|
||||
}
|
||||
|
||||
const RECENT_PROJECT_LIMIT = 5
|
||||
|
||||
type Row = {
|
||||
absolute: string
|
||||
search: string
|
||||
@@ -107,7 +109,6 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||
return projects
|
||||
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
|
||||
.sort((a, b) => b.at - a.at || a.index - b.index)
|
||||
.slice(0, 5)
|
||||
.map(({ project }) => {
|
||||
const row = toRow(project.worktree, home(), "recent")
|
||||
const name = project.name || getFilename(project.worktree)
|
||||
@@ -121,7 +122,10 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||
const items = async (value: string) => {
|
||||
const results = await directories(value)
|
||||
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
|
||||
return uniqueRows([...recentProjects(), ...directoryRows])
|
||||
// Cap the idle list only. Once a query narrows the results, every project stays searchable.
|
||||
const recent = recentProjects()
|
||||
const visible = value ? recent : recent.slice(0, RECENT_PROJECT_LIMIT)
|
||||
return uniqueRows([...visible, ...directoryRows])
|
||||
}
|
||||
|
||||
function resolve(absolute: string) {
|
||||
|
||||
@@ -6,21 +6,33 @@ import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { List } from "@opencode-ai/ui/list"
|
||||
import { TextField } from "@opencode-ai/ui/text-field"
|
||||
import { useMutation } from "@tanstack/solid-query"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { createEffect, createMemo, createResource, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Show } from "solid-js"
|
||||
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
|
||||
import { type ServerHealth, useCheckServerHealth } from "@/utils/server-health"
|
||||
import { ServerConnection } from "@/context/server"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { type ServerDomainController } from "@/components/server/server-management-controller"
|
||||
|
||||
const DEFAULT_USERNAME = "opencode"
|
||||
type ServerConnectionFormController = {
|
||||
state: {
|
||||
adding: () => boolean
|
||||
busy: () => boolean
|
||||
value: () => string
|
||||
name: () => string
|
||||
username: () => string
|
||||
password: () => string
|
||||
error: () => string
|
||||
status: () => boolean | undefined
|
||||
}
|
||||
change: {
|
||||
value: (value: string) => void
|
||||
name: (value: string) => void
|
||||
username: (value: string) => void
|
||||
password: (value: string) => void
|
||||
}
|
||||
reset: () => void
|
||||
submit: () => void
|
||||
}
|
||||
|
||||
interface ServerFormProps {
|
||||
value: string
|
||||
@@ -39,76 +51,6 @@ interface ServerFormProps {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
function showRequestError(language: ReturnType<typeof useLanguage>, err: unknown) {
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: language.t("common.requestFailed"),
|
||||
description: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
}
|
||||
|
||||
function useDefaultServer() {
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const [defaultKey, defaultUrlActions] = createResource(
|
||||
async () => {
|
||||
try {
|
||||
const key = await platform.getDefaultServer?.()
|
||||
if (!key) return null
|
||||
return key
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
return null
|
||||
}
|
||||
},
|
||||
{ initialValue: null },
|
||||
)
|
||||
|
||||
const canDefault = createMemo(() => !!platform.getDefaultServer && !!platform.setDefaultServer)
|
||||
const setDefault = async (key: ServerConnection.Key | null) => {
|
||||
try {
|
||||
await platform.setDefaultServer?.(key)
|
||||
defaultUrlActions.mutate(key)
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
return { defaultKey: () => defaultKey.latest, canDefault, setDefault }
|
||||
}
|
||||
|
||||
function useServerPreview() {
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
|
||||
const looksComplete = (value: string) => {
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return false
|
||||
const host = normalized.replace(/^https?:\/\//, "").split("/")[0]
|
||||
if (!host) return false
|
||||
if (host.includes("localhost") || host.startsWith("127.0.0.1")) return true
|
||||
return host.includes(".") || host.includes(":")
|
||||
}
|
||||
|
||||
const previewStatus = async (
|
||||
value: string,
|
||||
username: string,
|
||||
password: string,
|
||||
setStatus: (value: boolean | undefined) => void,
|
||||
) => {
|
||||
setStatus(undefined)
|
||||
if (!looksComplete(value)) return
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return
|
||||
const http: ServerConnection.HttpBase = { url: normalized }
|
||||
if (username) http.username = username
|
||||
if (password) http.password = password
|
||||
const result = await checkServerHealth(http)
|
||||
setStatus(result.healthy)
|
||||
}
|
||||
|
||||
return { previewStatus }
|
||||
}
|
||||
|
||||
function ServerForm(props: ServerFormProps) {
|
||||
const language = useLanguage()
|
||||
const keyDown = (event: KeyboardEvent) => {
|
||||
@@ -174,387 +116,11 @@ function ServerForm(props: ServerFormProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function DialogSelectServer() {
|
||||
const dialog = useDialog()
|
||||
const controller = useServerManagementController({ onSelect: dialog.close })
|
||||
|
||||
return (
|
||||
<Dialog title={controller.formTitle()}>
|
||||
<div class="flex flex-1 min-h-0 flex-col px-5">
|
||||
<Show when={controller.isFormMode()} fallback={<ServerConnectionList controller={controller} />}>
|
||||
<ServerConnectionForm controller={controller} />
|
||||
</Show>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export function useServerManagementController(options: { onSelect?: () => void; navigateOnAdd?: boolean } = {}) {
|
||||
const navigate = useNavigate()
|
||||
const server = useServer()
|
||||
const tabs = useTabs()
|
||||
const global = useGlobal()
|
||||
const platform = usePlatform()
|
||||
const language = useLanguage()
|
||||
const { defaultKey, canDefault, setDefault } = useDefaultServer()
|
||||
const { previewStatus } = useServerPreview()
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
const [store, setStore] = createStore({
|
||||
addServer: {
|
||||
url: "",
|
||||
name: "",
|
||||
username: DEFAULT_USERNAME,
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
editServer: {
|
||||
id: undefined as string | undefined,
|
||||
value: "",
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
})
|
||||
|
||||
const resetAdd = () => {
|
||||
setStore("addServer", {
|
||||
url: "",
|
||||
name: "",
|
||||
username: DEFAULT_USERNAME,
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
const resetEdit = () => {
|
||||
setStore("editServer", {
|
||||
id: undefined,
|
||||
value: "",
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const addMutation = useMutation(() => ({
|
||||
mutationFn: async (value: string) => {
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) {
|
||||
resetAdd()
|
||||
return
|
||||
}
|
||||
|
||||
const conn: ServerConnection.Http = {
|
||||
type: "http",
|
||||
http: { url: normalized },
|
||||
}
|
||||
if (store.addServer.name.trim()) conn.displayName = store.addServer.name.trim()
|
||||
if (store.addServer.password) conn.http.password = store.addServer.password
|
||||
if (store.addServer.password && store.addServer.username) conn.http.username = store.addServer.username
|
||||
const result = await checkServerHealth(conn.http)
|
||||
if (!result.healthy) {
|
||||
setStore("addServer", { error: language.t("dialog.server.add.error") })
|
||||
return
|
||||
}
|
||||
|
||||
resetAdd()
|
||||
if (options.navigateOnAdd === false) {
|
||||
server.add(conn)
|
||||
options.onSelect?.()
|
||||
return
|
||||
}
|
||||
await select(conn, true)
|
||||
},
|
||||
}))
|
||||
|
||||
const editMutation = useMutation(() => ({
|
||||
mutationFn: async (input: { original: ServerConnection.Any; value: string }) => {
|
||||
if (input.original.type !== "http") return
|
||||
const normalized = normalizeServerUrl(input.value)
|
||||
if (!normalized) {
|
||||
resetEdit()
|
||||
return
|
||||
}
|
||||
|
||||
const name = store.editServer.name.trim() || undefined
|
||||
const username = store.editServer.username || undefined
|
||||
const password = store.editServer.password || undefined
|
||||
const existingName = input.original.displayName
|
||||
if (
|
||||
normalized === input.original.http.url &&
|
||||
name === existingName &&
|
||||
username === input.original.http.username &&
|
||||
password === input.original.http.password
|
||||
) {
|
||||
resetEdit()
|
||||
return
|
||||
}
|
||||
|
||||
const conn: ServerConnection.Http = {
|
||||
type: "http",
|
||||
displayName: name,
|
||||
http: { url: normalized, username, password },
|
||||
}
|
||||
const result = await checkServerHealth(conn.http)
|
||||
if (!result.healthy) {
|
||||
setStore("editServer", { error: language.t("dialog.server.add.error") })
|
||||
return
|
||||
}
|
||||
if (normalized === input.original.http.url) {
|
||||
server.add(conn)
|
||||
} else {
|
||||
replaceServer(input.original, conn)
|
||||
}
|
||||
|
||||
resetEdit()
|
||||
},
|
||||
}))
|
||||
|
||||
const replaceServer = (original: ServerConnection.Http, next: ServerConnection.Http) => {
|
||||
const originalKey = ServerConnection.key(original)
|
||||
const active = server.key
|
||||
tabs.removeServer(originalKey)
|
||||
const newConn = server.add(next)
|
||||
if (!newConn) return
|
||||
const nextActive = active === originalKey ? ServerConnection.key(newConn) : active
|
||||
if (nextActive) server.setActive(nextActive)
|
||||
server.remove(originalKey)
|
||||
}
|
||||
|
||||
const items = createMemo(() => {
|
||||
const current = server.current
|
||||
const list = server.list
|
||||
if (!current) return list
|
||||
if (!list.includes(current)) return [current, ...list]
|
||||
return [current, ...list.filter((x) => x !== current)]
|
||||
})
|
||||
|
||||
const settings = useSettings()
|
||||
const current = createMemo<ServerConnection.Any | undefined>(() =>
|
||||
settings.general.newLayoutDesigns()
|
||||
? undefined
|
||||
: (items().find((x) => ServerConnection.key(x) === server.key) ?? items()[0]),
|
||||
)
|
||||
|
||||
const sortedItems = createMemo(() => {
|
||||
const raw = items()
|
||||
const list = raw
|
||||
if (!list.length) return list
|
||||
const active = current()
|
||||
const order = new Map(list.map((url, index) => [url, index] as const))
|
||||
const rank = (value?: ServerHealth) => {
|
||||
if (value?.healthy === true) return 0
|
||||
if (value?.healthy === false) return 2
|
||||
return 1
|
||||
}
|
||||
return list.slice().sort((a, b) => {
|
||||
if (a === active) return -1
|
||||
if (b === active) return 1
|
||||
const diff =
|
||||
rank(global.servers.health[ServerConnection.key(a)]) - rank(global.servers.health[ServerConnection.key(b)])
|
||||
if (diff !== 0) return diff
|
||||
return (order.get(a) ?? 0) - (order.get(b) ?? 0)
|
||||
})
|
||||
})
|
||||
|
||||
async function select(conn: ServerConnection.Any, persist?: boolean) {
|
||||
if (!persist && global.servers.health[ServerConnection.key(conn)]?.healthy === false) return
|
||||
options.onSelect?.()
|
||||
if (persist && conn.type === "http") {
|
||||
server.add(conn)
|
||||
navigate("/")
|
||||
return
|
||||
}
|
||||
navigate("/")
|
||||
queueMicrotask(() => server.setActive(ServerConnection.key(conn)))
|
||||
}
|
||||
|
||||
const handleAddChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { url: value, error: "" })
|
||||
void previewStatus(value, store.addServer.username, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddNameChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { name: value, error: "" })
|
||||
}
|
||||
|
||||
const handleAddUsernameChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { username: value, error: "" })
|
||||
void previewStatus(store.addServer.url, value, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddPasswordChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { password: value, error: "" })
|
||||
void previewStatus(store.addServer.url, store.addServer.username, value, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { value, error: "" })
|
||||
void previewStatus(value, store.editServer.username, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditNameChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { name: value, error: "" })
|
||||
}
|
||||
|
||||
const handleEditUsernameChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { username: value, error: "" })
|
||||
void previewStatus(store.editServer.value, value, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditPasswordChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { password: value, error: "" })
|
||||
void previewStatus(store.editServer.value, store.editServer.username, value, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const mode = createMemo<"list" | "add" | "edit">(() => {
|
||||
if (store.editServer.id) return "edit"
|
||||
if (store.addServer.showForm) return "add"
|
||||
return "list"
|
||||
})
|
||||
|
||||
const editing = createMemo(() => {
|
||||
if (!store.editServer.id) return
|
||||
return items().find((x) => x.type === "http" && x.http.url === store.editServer.id)
|
||||
})
|
||||
|
||||
const resetForm = () => {
|
||||
resetAdd()
|
||||
resetEdit()
|
||||
}
|
||||
|
||||
const startAdd = () => {
|
||||
resetEdit()
|
||||
setStore("addServer", {
|
||||
showForm: true,
|
||||
url: "",
|
||||
name: "",
|
||||
username: DEFAULT_USERNAME,
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const startEdit = (conn: ServerConnection.Http) => {
|
||||
resetAdd()
|
||||
setStore("editServer", {
|
||||
id: conn.http.url,
|
||||
value: conn.http.url,
|
||||
name: conn.displayName ?? "",
|
||||
username: conn.http.username ?? "",
|
||||
password: conn.http.password ?? "",
|
||||
error: "",
|
||||
status: global.servers.health[ServerConnection.key(conn)]?.healthy,
|
||||
})
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
if (mode() === "add") {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { error: "" })
|
||||
addMutation.mutate(store.addServer.url)
|
||||
return
|
||||
}
|
||||
const original = editing()
|
||||
if (!original) return
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { error: "" })
|
||||
editMutation.mutate({ original, value: store.editServer.value })
|
||||
}
|
||||
|
||||
const isFormMode = createMemo(() => mode() !== "list")
|
||||
const isAddMode = createMemo(() => mode() === "add")
|
||||
const formBusy = createMemo(() => (isAddMode() ? addMutation.isPending : editMutation.isPending))
|
||||
|
||||
const formTitle = createMemo(() => {
|
||||
if (!isFormMode()) return language.t("dialog.server.title")
|
||||
return (
|
||||
<div class="flex items-center gap-2 -ml-2">
|
||||
<IconButton icon="arrow-left" variant="ghost" onClick={resetForm} aria-label={language.t("common.goBack")} />
|
||||
<span>{isAddMode() ? language.t("dialog.server.add.title") : language.t("dialog.server.edit.title")}</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!store.editServer.id) return
|
||||
if (editing()) return
|
||||
resetEdit()
|
||||
})
|
||||
|
||||
async function handleRemove(key: ServerConnection.Key) {
|
||||
try {
|
||||
if (key.startsWith("wsl:")) await platform.wslServers?.removeServer(key)
|
||||
tabs.removeServer(key)
|
||||
server.remove(key)
|
||||
if ((await platform.getDefaultServer?.()) === key) {
|
||||
await setDefault(null)
|
||||
}
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
defaultKey,
|
||||
canDefault,
|
||||
current,
|
||||
sortedItems,
|
||||
status: () => global.servers.health,
|
||||
isFormMode,
|
||||
isAddMode,
|
||||
formTitle,
|
||||
formBusy,
|
||||
formValue: () => (isAddMode() ? store.addServer.url : store.editServer.value),
|
||||
formName: () => (isAddMode() ? store.addServer.name : store.editServer.name),
|
||||
formUsername: () => (isAddMode() ? store.addServer.username : store.editServer.username),
|
||||
formPassword: () => (isAddMode() ? store.addServer.password : store.editServer.password),
|
||||
formError: () => (isAddMode() ? store.addServer.error : store.editServer.error),
|
||||
formStatus: () => (isAddMode() ? store.addServer.status : store.editServer.status),
|
||||
select,
|
||||
setDefault,
|
||||
startAdd,
|
||||
startEdit,
|
||||
resetForm,
|
||||
submitForm,
|
||||
canRemove: server.canRemove,
|
||||
handleRemove,
|
||||
handleFormChange: () => (isAddMode() ? handleAddChange : handleEditChange),
|
||||
handleFormNameChange: () => (isAddMode() ? handleAddNameChange : handleEditNameChange),
|
||||
handleFormUsernameChange: () => (isAddMode() ? handleAddUsernameChange : handleEditUsernameChange),
|
||||
handleFormPasswordChange: () => (isAddMode() ? handleAddPasswordChange : handleEditPasswordChange),
|
||||
}
|
||||
}
|
||||
|
||||
export function ServerConnectionList(props: { controller: ReturnType<typeof useServerManagementController> }) {
|
||||
export function ServerConnectionList(props: {
|
||||
domain: ServerDomainController
|
||||
onAdd: () => void
|
||||
onEdit: (server: ServerConnection.Http) => void
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const settings = useSettings()
|
||||
|
||||
@@ -568,10 +134,10 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
}}
|
||||
noInitialSelection
|
||||
emptyMessage={language.t("dialog.server.empty")}
|
||||
items={props.controller.sortedItems}
|
||||
items={props.domain.collection.items}
|
||||
key={(x) => x.http.url}
|
||||
onSelect={(x) => {
|
||||
if (x && !settings.general.newLayoutDesigns()) void props.controller.select(x)
|
||||
if (x && !settings.general.newLayoutDesigns()) void props.domain.selection.select(x)
|
||||
}}
|
||||
divider={true}
|
||||
>
|
||||
@@ -580,15 +146,15 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
return (
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1 w-full group/item">
|
||||
<div class="flex flex-col h-full items-center w-5">
|
||||
<ServerHealthIndicator health={props.controller.status()[key]} />
|
||||
<ServerHealthIndicator health={props.domain.collection.health()[key]} />
|
||||
</div>
|
||||
<ServerRow
|
||||
conn={i}
|
||||
dimmed={props.controller.status()[key]?.healthy === false}
|
||||
status={props.controller.status()[key]}
|
||||
dimmed={props.domain.collection.health()[key]?.healthy === false}
|
||||
status={props.domain.collection.health()[key]}
|
||||
class="flex items-center gap-3 min-w-0 flex-1"
|
||||
badge={
|
||||
<Show when={props.controller.defaultKey() === ServerConnection.key(i)}>
|
||||
<Show when={props.domain.defaults.key() === ServerConnection.key(i)}>
|
||||
<span class="text-text-base bg-surface-base text-14-regular px-1.5 rounded-xs">
|
||||
{language.t("dialog.server.status.default")}
|
||||
</span>
|
||||
@@ -597,7 +163,12 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
showCredentials
|
||||
/>
|
||||
<div class="flex items-center justify-center gap-4 pl-4">
|
||||
<Show when={props.controller.current() && ServerConnection.key(props.controller.current()!) === key}>
|
||||
<Show
|
||||
when={
|
||||
props.domain.collection.current() &&
|
||||
ServerConnection.key(props.domain.collection.current()!) === key
|
||||
}
|
||||
>
|
||||
<Icon name="check" class="h-6" />
|
||||
</Show>
|
||||
|
||||
@@ -616,27 +187,27 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => {
|
||||
if (i.type !== "http") return
|
||||
props.controller.startEdit(i)
|
||||
props.onEdit(i)
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<Show when={props.controller.canDefault() && props.controller.defaultKey() !== key}>
|
||||
<DropdownMenu.Item onSelect={() => props.controller.setDefault(key)}>
|
||||
<Show when={props.domain.defaults.available() && props.domain.defaults.key() !== key}>
|
||||
<DropdownMenu.Item onSelect={() => props.domain.defaults.set(key)}>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.default")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={props.controller.canDefault() && props.controller.defaultKey() === key}>
|
||||
<DropdownMenu.Item onSelect={() => props.controller.setDefault(null)}>
|
||||
<Show when={props.domain.defaults.available() && props.domain.defaults.key() === key}>
|
||||
<DropdownMenu.Item onSelect={() => props.domain.defaults.set(null)}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{language.t("dialog.server.menu.defaultRemove")}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={props.controller.canRemove(key)}>
|
||||
<Show when={props.domain.connection.canRemove(key)}>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => props.controller.handleRemove(ServerConnection.key(i))}
|
||||
onSelect={() => props.domain.connection.remove(key)}
|
||||
class="text-text-on-critical-base hover:bg-surface-critical-weak"
|
||||
>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.delete")}</DropdownMenu.ItemLabel>
|
||||
@@ -657,7 +228,7 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
variant="secondary"
|
||||
icon="plus-small"
|
||||
size="large"
|
||||
onClick={props.controller.startAdd}
|
||||
onClick={props.onAdd}
|
||||
class="py-1.5 pl-1.5 pr-3 flex items-center gap-1.5"
|
||||
>
|
||||
{language.t("dialog.server.add.button")}
|
||||
@@ -667,38 +238,38 @@ export function ServerConnectionList(props: { controller: ReturnType<typeof useS
|
||||
)
|
||||
}
|
||||
|
||||
export function ServerConnectionForm(props: { controller: ReturnType<typeof useServerManagementController> }) {
|
||||
export function ServerConnectionForm(props: { form: ServerConnectionFormController }) {
|
||||
const language = useLanguage()
|
||||
|
||||
return (
|
||||
<div class="flex flex-1 min-h-0 flex-col gap-4">
|
||||
<ServerForm
|
||||
value={props.controller.formValue()}
|
||||
name={props.controller.formName()}
|
||||
username={props.controller.formUsername()}
|
||||
password={props.controller.formPassword()}
|
||||
value={props.form.state.value()}
|
||||
name={props.form.state.name()}
|
||||
username={props.form.state.username()}
|
||||
password={props.form.state.password()}
|
||||
placeholder={language.t("dialog.server.add.placeholder")}
|
||||
busy={props.controller.formBusy()}
|
||||
error={props.controller.formError()}
|
||||
status={props.controller.formStatus()}
|
||||
onChange={props.controller.handleFormChange()}
|
||||
onNameChange={props.controller.handleFormNameChange()}
|
||||
onUsernameChange={props.controller.handleFormUsernameChange()}
|
||||
onPasswordChange={props.controller.handleFormPasswordChange()}
|
||||
onSubmit={props.controller.submitForm}
|
||||
onBack={props.controller.resetForm}
|
||||
busy={props.form.state.busy()}
|
||||
error={props.form.state.error()}
|
||||
status={props.form.state.status()}
|
||||
onChange={props.form.change.value}
|
||||
onNameChange={props.form.change.name}
|
||||
onUsernameChange={props.form.change.username}
|
||||
onPasswordChange={props.form.change.password}
|
||||
onSubmit={props.form.submit}
|
||||
onBack={props.form.reset}
|
||||
/>
|
||||
<div class="shrink-0 pb-5">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="large"
|
||||
onClick={props.controller.submitForm}
|
||||
disabled={props.controller.formBusy()}
|
||||
onClick={props.form.submit}
|
||||
disabled={props.form.state.busy()}
|
||||
class="px-3 py-1.5"
|
||||
>
|
||||
{props.controller.formBusy()
|
||||
{props.form.state.busy()
|
||||
? language.t("dialog.server.add.checking")
|
||||
: props.controller.isAddMode()
|
||||
: props.form.state.adding()
|
||||
? language.t("dialog.server.add.button")
|
||||
: language.t("common.save")}
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||
@@ -14,10 +15,11 @@ export type DialogGoUpsellProps = {
|
||||
|
||||
export function DialogUsageExceeded(props: DialogGoUpsellProps) {
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
|
||||
const runAction = () => {
|
||||
if (props.link) platform.openLink(props.link)
|
||||
if (props.link) platform.openExternal(props.link)
|
||||
props.onClose?.()
|
||||
dialog.close()
|
||||
}
|
||||
@@ -32,7 +34,7 @@ export function DialogUsageExceeded(props: DialogGoUpsellProps) {
|
||||
<div class="flex flex-col gap-4 pl-6 pr-2.5 pb-3">
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button variant="ghost" size="large" onClick={dismiss}>
|
||||
Don't show again
|
||||
{language.t("dialog.usageExceeded.dontShowAgain")}
|
||||
</Button>
|
||||
<Button variant="primary" size="large" onClick={runAction}>
|
||||
{props.actionLabel}
|
||||
|
||||
@@ -139,6 +139,7 @@ test("resolves directory autocomplete from the current browser root", async () =
|
||||
directories.push(input.location?.directory ?? "")
|
||||
return Promise.resolve({ data: [] })
|
||||
},
|
||||
list: () => Promise.resolve({ data: [] }),
|
||||
},
|
||||
},
|
||||
} as unknown as Parameters<typeof createDirectorySearch>[0]["sdk"]
|
||||
@@ -152,6 +153,67 @@ test("resolves directory autocomplete from the current browser root", async () =
|
||||
expect(directories).toEqual(["/repo", "/repo/src"])
|
||||
})
|
||||
|
||||
test("keeps indexed directory results for servers that support empty search", async () => {
|
||||
const sdk = {
|
||||
api: {
|
||||
file: {
|
||||
find: () => Promise.resolve({ data: [{ path: "projects/", type: "directory" }] }),
|
||||
list: () => Promise.reject(new Error("listing should not run when search returns results")),
|
||||
},
|
||||
},
|
||||
} as unknown as Parameters<typeof createDirectorySearch>[0]["sdk"]
|
||||
const search = createDirectorySearch({ sdk, home: () => "/home/luke", base: () => "/home/luke" })
|
||||
|
||||
expect(await search("")).toEqual(["/home/luke/projects"])
|
||||
})
|
||||
|
||||
test("lists the default directory when empty search is unsupported", async () => {
|
||||
const calls: string[] = []
|
||||
const directories = Array.from({ length: 60 }, (_, index) => ({
|
||||
path: `project-${index}/`,
|
||||
type: "directory" as const,
|
||||
}))
|
||||
const sdk = {
|
||||
api: {
|
||||
file: {
|
||||
find: () => Promise.resolve({ data: [] }),
|
||||
list: (input: { location?: { directory?: string } }) => {
|
||||
calls.push(input.location?.directory ?? "")
|
||||
return Promise.resolve({
|
||||
data: [...directories, { path: "README.md", type: "file" }],
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Parameters<typeof createDirectorySearch>[0]["sdk"]
|
||||
const search = createDirectorySearch({ sdk, home: () => "/home/luke", base: () => "/home/luke" })
|
||||
|
||||
const results = await search("")
|
||||
expect(results).toHaveLength(60)
|
||||
expect(results.at(-1)).toBe("/home/luke/project-59")
|
||||
expect(calls).toEqual(["/home/luke"])
|
||||
})
|
||||
|
||||
test("matches the default directory listing when typed search is unsupported", async () => {
|
||||
const sdk = {
|
||||
api: {
|
||||
file: {
|
||||
find: () => Promise.resolve({ data: [] }),
|
||||
list: () =>
|
||||
Promise.resolve({
|
||||
data: [
|
||||
{ path: "Documents/", type: "directory" },
|
||||
{ path: "Downloads/", type: "directory" },
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
} as unknown as Parameters<typeof createDirectorySearch>[0]["sdk"]
|
||||
const search = createDirectorySearch({ sdk, home: () => "/home/luke", base: () => "/home/luke" })
|
||||
|
||||
expect(await search("documents")).toEqual(["/home/luke/Documents"])
|
||||
})
|
||||
|
||||
test("searches from an absolute root without a default base", async () => {
|
||||
const directories: string[] = []
|
||||
const sdk = {
|
||||
|
||||
@@ -379,7 +379,14 @@ export function createDirectorySearch(args: { sdk: ServerSDK; base: () => string
|
||||
.then((result) => result.data.map((entry) => entry.path))
|
||||
.catch(() => [])
|
||||
if (!active()) return []
|
||||
return results.map((path) => joinPickerPath(input.directory, path)).slice(0, 50)
|
||||
if (results.length) {
|
||||
return results.map((path) => joinPickerPath(input.directory, path)).slice(0, 50)
|
||||
}
|
||||
const fallback = query
|
||||
? await match(input.directory, query, 50)
|
||||
: (await directories(input.directory)).map((item) => item.absolute)
|
||||
if (!active()) return []
|
||||
return fallback
|
||||
}
|
||||
const segments = query.replace(/^\/+/, "").split("/")
|
||||
const head = segments.slice(0, -1).filter((part) => part && part !== ".")
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentProps, splitProps } from "solid-js"
|
||||
|
||||
export interface ExternalLinkProps extends Omit<ComponentProps<"a">, "href"> {
|
||||
href: string
|
||||
}
|
||||
|
||||
export function ExternalLink(props: ExternalLinkProps) {
|
||||
const [local, rest] = splitProps(props, ["href", "children", "class", "target", "rel"])
|
||||
|
||||
return (
|
||||
<a
|
||||
href={local.href}
|
||||
class={`text-text-strong underline ${local.class ?? ""}`}
|
||||
target={local.target ?? "_blank"}
|
||||
rel={local.rel ?? "noopener noreferrer"}
|
||||
{...rest}
|
||||
>
|
||||
{local.children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -29,14 +29,14 @@ export type { Kind } from "@/components/file-tree"
|
||||
|
||||
const INDENT_STEP = 16
|
||||
|
||||
function rowPaddingLeft(level: number, type: FileNode["type"]) {
|
||||
function rowPaddingStart(level: number, type: FileNode["type"]) {
|
||||
if (type === "directory") return 8 + level * INDENT_STEP
|
||||
if (level === 0) return 8
|
||||
return 8 + level * INDENT_STEP - INDENT_STEP
|
||||
}
|
||||
|
||||
function guideLineLeft(level: number) {
|
||||
return rowPaddingLeft(level, "directory") + 8
|
||||
function guideLineStart(level: number) {
|
||||
return rowPaddingStart(level, "directory") + 8
|
||||
}
|
||||
|
||||
export const kindLabel = (kind: Kind) => {
|
||||
@@ -87,7 +87,7 @@ const FileTreeNodeV2 = (
|
||||
...local.classList,
|
||||
[local.class ?? ""]: !!local.class,
|
||||
}}
|
||||
style={`padding-left: ${rowPaddingLeft(local.level, local.node.type)}px`}
|
||||
style={`padding-inline-start: ${rowPaddingStart(local.level, local.node.type)}px`}
|
||||
draggable={local.draggable}
|
||||
onDragStart={(event: DragEvent) => {
|
||||
if (!local.draggable) return
|
||||
@@ -99,7 +99,9 @@ const FileTreeNodeV2 = (
|
||||
{...rest}
|
||||
>
|
||||
{local.children}
|
||||
<span class="flex-1 min-w-0 text-12-medium whitespace-nowrap truncate">{local.node.name}</span>
|
||||
<span class="flex-1 min-w-0 text-start text-12-medium whitespace-nowrap truncate">
|
||||
<bdi dir="auto">{local.node.name}</bdi>
|
||||
</span>
|
||||
{(() => {
|
||||
const value = kind()
|
||||
if (!value || local.node.type !== "file") return null
|
||||
@@ -116,7 +118,7 @@ const FileTreeNodeV2 = (
|
||||
function GuideLines(props: { level: number }) {
|
||||
return (
|
||||
<For each={Array.from({ length: props.level })}>
|
||||
{(_, index) => <div data-slot="file-tree-v2-guide" style={`left: ${guideLineLeft(index())}px`} />}
|
||||
{(_, index) => <div data-slot="file-tree-v2-guide" style={`inset-inline-start: ${guideLineStart(index())}px`} />}
|
||||
</For>
|
||||
)
|
||||
}
|
||||
@@ -227,7 +229,7 @@ export default function FileTreeV2(props: {
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
left: "0",
|
||||
"inset-inline-start": "0",
|
||||
width: "100%",
|
||||
height: `${item().size}px`,
|
||||
transform: `translateY(${item().start}px)`,
|
||||
|
||||
@@ -146,13 +146,13 @@ const FileTreeNode = (
|
||||
<Dynamic
|
||||
component={local.as ?? "div"}
|
||||
classList={{
|
||||
"w-full min-w-0 h-6 flex items-center justify-start gap-x-1.5 rounded-md px-1.5 py-0 text-left hover:bg-surface-raised-base-hover active:bg-surface-base-active transition-colors cursor-pointer": true,
|
||||
"w-full min-w-0 h-6 flex items-center justify-start gap-x-1.5 rounded-md px-1.5 py-0 text-start hover:bg-surface-raised-base-hover active:bg-surface-base-active transition-colors cursor-pointer": true,
|
||||
"bg-surface-base-active": local.node.path === local.active,
|
||||
...local.classList,
|
||||
[local.class ?? ""]: !!local.class,
|
||||
[local.nodeClass ?? ""]: !!local.nodeClass,
|
||||
}}
|
||||
style={`padding-left: ${Math.max(0, 8 + local.level * 12 - (local.node.type === "file" ? 24 : 4))}px`}
|
||||
style={`padding-inline-start: ${Math.max(0, 8 + local.level * 12 - (local.node.type === "file" ? 24 : 4))}px`}
|
||||
draggable={local.draggable}
|
||||
onDragStart={(event: DragEvent) => {
|
||||
if (!local.draggable) return
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { ComponentProps, splitProps } from "solid-js"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
|
||||
export interface LinkProps extends Omit<ComponentProps<"a">, "href"> {
|
||||
href: string
|
||||
}
|
||||
|
||||
export function Link(props: LinkProps) {
|
||||
const platform = usePlatform()
|
||||
const [local, rest] = splitProps(props, ["href", "children", "class"])
|
||||
|
||||
return (
|
||||
<a
|
||||
href={local.href}
|
||||
class={`text-text-strong underline ${local.class ?? ""}`}
|
||||
onClick={(event) => {
|
||||
if (!local.href) return
|
||||
event.preventDefault()
|
||||
platform.openLink(local.href)
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{local.children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -55,6 +55,7 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
|
||||
controller={props.controller}
|
||||
borderUnderlay={props.borderUnderlay}
|
||||
class={props.class}
|
||||
variantControlVisible={!props.controller.model.loading}
|
||||
attachKeybind={command.keybindParts("file.attach")}
|
||||
attachShortcut={command.keybind("file.attach")}
|
||||
modelControl={
|
||||
@@ -136,7 +137,10 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
|
||||
}),
|
||||
)
|
||||
const designPlaceholder = () => promptDesignPlaceholder(mode(), placeholder())
|
||||
const designPlaceholder = () =>
|
||||
promptDesignPlaceholder(mode(), placeholder(), (key, params) =>
|
||||
language.t(key as Parameters<typeof language.t>[0], params as never),
|
||||
)
|
||||
|
||||
const historyComments = () => {
|
||||
const byID = new Map(comments.all().map((item) => [`${item.file}\n${item.id}`, item] as const))
|
||||
@@ -343,7 +347,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
if (item?.commentID) comments.remove(item.path, item.commentID)
|
||||
},
|
||||
openAttachment: (attachment) =>
|
||||
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />),
|
||||
dialog.show(() => <ImagePreview src={attachment.blob.url} alt={attachment.filename} />),
|
||||
openContext(key) {
|
||||
const item = controller.contextItem(key)
|
||||
if (item) openComment(item, props, sync, layout, files, comments)
|
||||
@@ -367,6 +371,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
title: language.t("prompt.toast.pasteUnsupported.title"),
|
||||
description: language.t("prompt.toast.pasteUnsupported.description"),
|
||||
}),
|
||||
duplicate: () => showToast({ title: language.t("prompt.toast.attachmentDuplicate.title") }),
|
||||
onError: (error) =>
|
||||
showToast({
|
||||
variant: "error",
|
||||
@@ -375,6 +380,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
}),
|
||||
readClipboardImage: platform.readClipboardImage,
|
||||
getPathForFile: platform.getPathForFile,
|
||||
store: platform.draftStore?.putBlob,
|
||||
},
|
||||
view: {
|
||||
placeholder: designPlaceholder,
|
||||
|
||||
@@ -77,7 +77,7 @@ import { PromptPopover, type AtOption, type SlashCommand } from "./prompt-input/
|
||||
import { PromptContextItems } from "./prompt-input/context-items"
|
||||
import { PromptImageAttachments } from "./prompt-input/image-attachments"
|
||||
import { PromptDragOverlay } from "./prompt-input/drag-overlay"
|
||||
import { promptDesignPlaceholder, promptPlaceholder } from "./prompt-input/placeholder"
|
||||
import { promptPlaceholder } from "./prompt-input/placeholder"
|
||||
import { createPromptInputTransientState } from "./prompt-input/transient-state"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
||||
@@ -1489,10 +1489,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
<PromptImageAttachments
|
||||
attachments={imageAttachments()}
|
||||
onOpen={(attachment) =>
|
||||
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
||||
dialog.show(() => <ImagePreview src={attachment.blob.url} alt={attachment.filename} />)
|
||||
}
|
||||
onRemove={removeAttachment}
|
||||
removeLabel={language.t("prompt.attachment.remove")}
|
||||
fileLabel={language.t("ui.common.file")}
|
||||
newLayoutDesigns={false}
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -3,28 +3,13 @@ import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { type ContentPart, type ImageAttachmentPart, type usePrompt } from "@/context/prompt"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { uuid } from "@/utils/uuid"
|
||||
import { getCursorPosition } from "./editor-dom"
|
||||
import { createBlobReference, type DraftStore } from "@/utils/draft-store"
|
||||
import { attachmentMime } from "./files"
|
||||
import { normalizePaste, pasteMode } from "./paste"
|
||||
|
||||
function dataUrl(file: File, mime: string) {
|
||||
return new Promise<string>((resolve) => {
|
||||
const reader = new FileReader()
|
||||
reader.addEventListener("error", () => resolve(""))
|
||||
reader.addEventListener("load", () => {
|
||||
const value = typeof reader.result === "string" ? reader.result : ""
|
||||
const idx = value.indexOf(",")
|
||||
if (idx === -1) {
|
||||
resolve(value)
|
||||
return
|
||||
}
|
||||
resolve(`data:${mime};base64,${value.slice(idx + 1)}`)
|
||||
})
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
}
|
||||
|
||||
type PromptTarget = Pick<ReturnType<ReturnType<typeof usePrompt>["capture"]>, "current" | "cursor" | "set">
|
||||
type AttachmentTarget = { prompt: PromptTarget; cursor: number | undefined }
|
||||
|
||||
@@ -36,6 +21,7 @@ type PromptAttachmentsCoreInput = {
|
||||
warn?: () => void
|
||||
readClipboardImage?: () => Promise<File | null>
|
||||
getPathForFile?: (file: File) => string
|
||||
draftStore?: DraftStore
|
||||
}
|
||||
|
||||
export type PromptAttachmentsInput = {
|
||||
@@ -65,16 +51,13 @@ export function createPromptAttachmentsCore(input: PromptAttachmentsCoreInput) {
|
||||
return false
|
||||
}
|
||||
|
||||
const url = await dataUrl(file, mime)
|
||||
if (!url) return false
|
||||
|
||||
const attachment: ImageAttachmentPart = {
|
||||
type: "image",
|
||||
id: uuid(),
|
||||
filename: file.name,
|
||||
sourcePath: input.getPathForFile?.(file) || undefined,
|
||||
mime,
|
||||
dataUrl: url,
|
||||
blob: input.draftStore ? await input.draftStore.putBlob(file) : await createBlobReference(file),
|
||||
}
|
||||
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
|
||||
return true
|
||||
@@ -166,8 +149,10 @@ export function createPromptAttachmentsCore(input: PromptAttachmentsCoreInput) {
|
||||
|
||||
export function createPromptAttachments(input: PromptAttachmentsInput) {
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const attachments = createPromptAttachmentsCore({
|
||||
...input,
|
||||
draftStore: platform.draftStore,
|
||||
capture: input.prompt.capture,
|
||||
warn: () => {
|
||||
showToast({
|
||||
|
||||
@@ -25,7 +25,7 @@ type ContextFile = {
|
||||
type BuildRequestPartsInput = {
|
||||
prompt: Prompt
|
||||
context: ContextFile[]
|
||||
images: ImageAttachmentPart[]
|
||||
images: (Omit<ImageAttachmentPart, "blob"> & { dataUrl: string })[]
|
||||
text: string
|
||||
messageID: string
|
||||
sessionID: string
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
|
||||
import type { Prompt } from "@/context/prompt"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { prependHistoryEntry, type PromptHistoryComment, type PromptHistoryStoredEntry } from "./history"
|
||||
import {
|
||||
clonePromptHistoryComments,
|
||||
clonePromptParts,
|
||||
prependHistoryEntry,
|
||||
type PromptHistoryComment,
|
||||
type PromptHistoryStoredEntry,
|
||||
} from "./history"
|
||||
|
||||
export type PromptInputHistory = {
|
||||
entries: (mode: "normal" | "shell") => PromptHistoryStoredEntry[]
|
||||
@@ -35,13 +41,23 @@ export function createPromptInputHistory(): PromptInputHistory {
|
||||
}
|
||||
|
||||
export function createPersistedPromptInputHistory() {
|
||||
const [normal, setNormal] = persisted(
|
||||
Persist.global("prompt-history", ["prompt-history.v1"]),
|
||||
const [normal, setNormal, normalInit] = persisted(
|
||||
Persist.prompt(Persist.global("prompt-history", ["prompt-history.v1"])),
|
||||
createStore<PromptHistoryState>({ entries: [] }),
|
||||
)
|
||||
const [shell, setShell] = persisted(
|
||||
Persist.global("prompt-history-shell", ["prompt-history-shell.v1"]),
|
||||
const [shell, setShell, shellInit] = persisted(
|
||||
Persist.prompt(Persist.global("prompt-history-shell", ["prompt-history-shell.v1"])),
|
||||
createStore<PromptHistoryState>({ entries: [] }),
|
||||
)
|
||||
return createPromptInputHistoryStore(normal, setNormal, shell, setShell)
|
||||
const history = createPromptInputHistoryStore(normal, setNormal, shell, setShell)
|
||||
return {
|
||||
...history,
|
||||
add(prompt: Prompt, mode: "normal" | "shell", comments: PromptHistoryComment[]) {
|
||||
const ready = mode === "shell" ? shellInit : normalInit
|
||||
if (!(ready instanceof Promise)) return history.add(prompt, mode, comments)
|
||||
const saved = clonePromptParts(prompt)
|
||||
const metadata = clonePromptHistoryComments(comments)
|
||||
void ready.then(() => history.add(saved, mode, metadata))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("prompt-input history", () => {
|
||||
end: 12,
|
||||
selection: { startLine: 1, startChar: 1, endLine: 2, endChar: 1 },
|
||||
},
|
||||
{ type: "image", id: "1", filename: "img.png", mime: "image/png", dataUrl: "data:image/png;base64,abc" },
|
||||
{ type: "image", id: "1", filename: "img.png", mime: "image/png", blob: { id: "blob", url: "blob:test" } },
|
||||
]
|
||||
const copy = clonePromptParts(original)
|
||||
expect(copy).not.toBe(original)
|
||||
|
||||
@@ -16,6 +16,7 @@ type PromptImageAttachmentsProps = {
|
||||
onOpen: (attachment: ImageAttachmentPart) => void
|
||||
onRemove: (id: string) => void
|
||||
removeLabel: string
|
||||
fileLabel: string
|
||||
newLayoutDesigns: boolean
|
||||
comments?: PromptCommentItem[]
|
||||
commentActive?: (item: PromptCommentItem) => boolean
|
||||
@@ -94,13 +95,13 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p
|
||||
}
|
||||
>
|
||||
<AttachmentCardV2 title={attachment.filename}>
|
||||
{typeLabel(attachment.filename, attachment.mime)}
|
||||
{typeLabel(attachment.filename, attachment.mime, props.fileLabel)}
|
||||
</AttachmentCardV2>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={attachment.dataUrl}
|
||||
src={attachment.blob.url}
|
||||
alt={attachment.filename}
|
||||
class={props.newLayoutDesigns ? imageClassV2 : imageClass}
|
||||
onClick={() => props.onOpen(attachment)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { promptPlaceholder } from "./placeholder"
|
||||
import { promptDesignPlaceholder, promptPlaceholder } from "./placeholder"
|
||||
|
||||
describe("promptPlaceholder", () => {
|
||||
const t = (key: string, params?: Record<string, string>) => `${key}${params?.example ? `:${params.example}` : ""}`
|
||||
@@ -46,3 +46,18 @@ describe("promptPlaceholder", () => {
|
||||
expect(value).toBe("prompt.placeholder.simple")
|
||||
})
|
||||
})
|
||||
|
||||
describe("promptDesignPlaceholder", () => {
|
||||
const t = (key: string, params?: Record<string, string>) => {
|
||||
if (key !== "ui.promptInput.placeholder.normal") return key
|
||||
return `Ask anything, ${params?.slash} for commands, ${params?.at} for context...`
|
||||
}
|
||||
|
||||
test("composes the design placeholder from localized fragments", () => {
|
||||
expect(promptDesignPlaceholder("normal", "fallback", t)).toBe("Ask anything, / for commands, @ for context...")
|
||||
})
|
||||
|
||||
test("preserves the shell placeholder", () => {
|
||||
expect(promptDesignPlaceholder("shell", "Enter shell command...", t)).toBe("Enter shell command...")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,7 +14,11 @@ export function promptPlaceholder(input: PromptPlaceholderInput) {
|
||||
return input.t("prompt.placeholder.normal", { example: input.example })
|
||||
}
|
||||
|
||||
export function promptDesignPlaceholder(mode: PromptPlaceholderInput["mode"], placeholder: string) {
|
||||
export function promptDesignPlaceholder(
|
||||
mode: PromptPlaceholderInput["mode"],
|
||||
placeholder: string,
|
||||
t: PromptPlaceholderInput["t"],
|
||||
) {
|
||||
if (mode === "shell") return placeholder
|
||||
return "Ask anything, / for commands, @ for context..."
|
||||
return t("ui.promptInput.placeholder.normal", { slash: "/", at: "@" })
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { formatServerError } from "@/utils/server-errors"
|
||||
import { ScopedKey } from "@/utils/server-scope"
|
||||
import { createPromptSubmissionState } from "./submission-state"
|
||||
import { Event } from "@opencode-ai/schema/event"
|
||||
import { blobDataUrl } from "@/utils/draft-store"
|
||||
|
||||
type PendingPrompt = {
|
||||
abort: AbortController
|
||||
@@ -97,10 +98,12 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
|
||||
providerID: input.draft.model.providerID,
|
||||
variant: input.draft.variant,
|
||||
},
|
||||
files: images.map((attachment) => ({
|
||||
uri: attachment.dataUrl,
|
||||
name: attachment.filename,
|
||||
})),
|
||||
files: await Promise.all(
|
||||
images.map(async (attachment) => ({
|
||||
uri: await blobDataUrl(attachment.blob, attachment.mime),
|
||||
name: attachment.filename,
|
||||
})),
|
||||
),
|
||||
})
|
||||
return true
|
||||
} catch (err) {
|
||||
@@ -110,10 +113,16 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
|
||||
}
|
||||
|
||||
const messageID = input.messageID ?? Identifier.ascending("message")
|
||||
const encodedImages = await Promise.all(
|
||||
images.map(async (attachment) => ({
|
||||
...attachment,
|
||||
dataUrl: await blobDataUrl(attachment.blob, attachment.mime),
|
||||
})),
|
||||
)
|
||||
const { requestParts, optimisticParts } = buildRequestParts({
|
||||
prompt: input.draft.prompt,
|
||||
context: input.draft.context,
|
||||
images,
|
||||
images: encodedImages,
|
||||
text,
|
||||
sessionID: input.draft.sessionID,
|
||||
messageID,
|
||||
@@ -523,10 +532,12 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
arguments: args.join(" "),
|
||||
agent,
|
||||
model: { id: model.modelID, providerID: model.providerID, variant },
|
||||
files: images.map((attachment) => ({
|
||||
uri: attachment.dataUrl,
|
||||
name: attachment.filename,
|
||||
})),
|
||||
files: await Promise.all(
|
||||
images.map(async (attachment) => ({
|
||||
uri: await blobDataUrl(attachment.blob, attachment.mime),
|
||||
name: attachment.filename,
|
||||
})),
|
||||
),
|
||||
})
|
||||
.catch((err) => {
|
||||
serverSync().session.set("session_status", session.id, { type: "idle" })
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
import type { FormAnswer, IntegrationMethod, IntegrationOauthConnectOutput } from "@opencode-ai/client/promise"
|
||||
import { useQueryClient } from "@tanstack/solid-query"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { pathKey } from "@/utils/path-key"
|
||||
import { createEffect, createMemo, createResource, onCleanup } from "solid-js"
|
||||
import { createStore, produce } from "solid-js/store"
|
||||
|
||||
export type ProviderConnectMethod = Extract<IntegrationMethod, { type: "key" | "oauth" }>
|
||||
type Authorization = IntegrationOauthConnectOutput["data"]
|
||||
|
||||
export function createProviderConnectionController(options: {
|
||||
provider: () => string
|
||||
directory: () => string | undefined
|
||||
onComplete: () => void
|
||||
pollInterval?: number
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const serverSDK = useServerSDK()
|
||||
const serverSync = useServerSync()
|
||||
const queryClient = useQueryClient()
|
||||
const location = () => {
|
||||
const directory = options.directory()
|
||||
return directory ? { directory } : undefined
|
||||
}
|
||||
const [integration] = createResource(
|
||||
() => ({ provider: options.provider(), directory: options.directory() }),
|
||||
(input) =>
|
||||
serverSDK()
|
||||
.api.integration.get({ integrationID: input.provider, location: location() })
|
||||
.then((result) => result.data),
|
||||
)
|
||||
const methods = createMemo<ProviderConnectMethod[]>(() => {
|
||||
const values = integration.latest?.methods.filter(
|
||||
(method): method is ProviderConnectMethod => method.type === "key" || method.type === "oauth",
|
||||
)
|
||||
if (values?.length) return [...values]
|
||||
return [{ type: "key", label: language.t("provider.connect.method.apiKey") }]
|
||||
})
|
||||
const [store, setStore] = createStore({
|
||||
methodIndex: undefined as number | undefined,
|
||||
authorization: undefined as Authorization | undefined,
|
||||
formAnswer: undefined as FormAnswer | undefined,
|
||||
state: "pending" as "pending" | "complete" | "error" | "form" | undefined,
|
||||
error: undefined as string | undefined,
|
||||
})
|
||||
const polling = {
|
||||
generation: 0,
|
||||
timer: undefined as ReturnType<typeof setTimeout> | undefined,
|
||||
disposed: false,
|
||||
}
|
||||
const currentMethod = createMemo(() =>
|
||||
store.methodIndex === undefined ? undefined : methods().at(store.methodIndex),
|
||||
)
|
||||
|
||||
type Action =
|
||||
| { type: "method.select"; index: number }
|
||||
| { type: "method.reset" }
|
||||
| { type: "auth.form" }
|
||||
| { type: "auth.answer"; answer: FormAnswer | undefined }
|
||||
| { type: "auth.pending" }
|
||||
| { type: "auth.complete"; authorization: Authorization }
|
||||
| { type: "auth.error"; error: string }
|
||||
|
||||
const dispatch = (action: Action) => {
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
if (action.type === "method.select") {
|
||||
draft.methodIndex = action.index
|
||||
draft.authorization = undefined
|
||||
draft.formAnswer = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "method.reset") {
|
||||
draft.methodIndex = undefined
|
||||
draft.authorization = undefined
|
||||
draft.formAnswer = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.form") {
|
||||
draft.state = "form"
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.answer") {
|
||||
draft.formAnswer = action.answer
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.pending") {
|
||||
draft.state = "pending"
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.complete") {
|
||||
draft.state = "complete"
|
||||
draft.authorization = action.authorization
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
draft.state = "error"
|
||||
draft.error = action.error
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const cancelPolling = () => {
|
||||
polling.generation++
|
||||
if (polling.timer === undefined) return
|
||||
clearTimeout(polling.timer)
|
||||
polling.timer = undefined
|
||||
}
|
||||
const finish = async () => {
|
||||
cancelPolling()
|
||||
const directory = options.directory()
|
||||
await queryClient
|
||||
.refetchQueries(serverSync().queryOptions.providers(directory ? pathKey(directory) : null))
|
||||
.catch(() => undefined)
|
||||
if (polling.disposed) return
|
||||
options.onComplete()
|
||||
}
|
||||
const poll = async (authorization: Authorization, generation: number) => {
|
||||
const result = await serverSDK()
|
||||
.api.integration.oauth.status({
|
||||
integrationID: options.provider(),
|
||||
attemptID: authorization.attemptID,
|
||||
location: location(),
|
||||
})
|
||||
.then((response) => ({ ok: true as const, status: response.data }))
|
||||
.catch((error) => ({ ok: false as const, error }))
|
||||
if (polling.disposed || generation !== polling.generation) return
|
||||
if (!result.ok) {
|
||||
dispatch({
|
||||
type: "auth.error",
|
||||
error: result.error instanceof Error ? result.error.message : String(result.error),
|
||||
})
|
||||
return
|
||||
}
|
||||
if (result.status.status === "complete") {
|
||||
await finish()
|
||||
return
|
||||
}
|
||||
if (result.status.status === "failed") {
|
||||
dispatch({ type: "auth.error", error: result.status.message })
|
||||
return
|
||||
}
|
||||
if (result.status.status === "expired") {
|
||||
dispatch({ type: "auth.error", error: language.t("common.requestFailed") })
|
||||
return
|
||||
}
|
||||
polling.timer = setTimeout(() => void poll(authorization, generation), options.pollInterval ?? 1_000)
|
||||
}
|
||||
const select = async (index: number, answer?: FormAnswer) => {
|
||||
cancelPolling()
|
||||
const generation = polling.generation
|
||||
const selected = methods()[index]
|
||||
dispatch({ type: "method.select", index })
|
||||
if (selected.form?.length && !answer) {
|
||||
dispatch({ type: "auth.form" })
|
||||
return
|
||||
}
|
||||
if (selected.type === "key") {
|
||||
dispatch({ type: "auth.answer", answer })
|
||||
return
|
||||
}
|
||||
if (selected.type !== "oauth") return
|
||||
if (selected.form?.some((field) => field.type !== "string")) {
|
||||
dispatch({ type: "auth.error", error: "This authentication form contains unsupported fields" })
|
||||
return
|
||||
}
|
||||
dispatch({ type: "auth.pending" })
|
||||
const result = await serverSDK()
|
||||
.api.integration.oauth.connect({
|
||||
integrationID: options.provider(),
|
||||
methodID: selected.id,
|
||||
...(answer ? { answer } : {}),
|
||||
location: location(),
|
||||
})
|
||||
.then((response) => ({ ok: true as const, authorization: response.data }))
|
||||
.catch((error) => ({ ok: false as const, error }))
|
||||
if (polling.disposed || generation !== polling.generation) return
|
||||
if (!result.ok) {
|
||||
dispatch({ type: "auth.error", error: String(result.error) })
|
||||
return
|
||||
}
|
||||
dispatch({ type: "auth.complete", authorization: result.authorization })
|
||||
if (result.authorization.mode === "auto") void poll(result.authorization, generation)
|
||||
}
|
||||
const reset = () => {
|
||||
cancelPolling()
|
||||
dispatch({ type: "method.reset" })
|
||||
}
|
||||
const connectKey = async (key: string) => {
|
||||
await serverSDK().api.integration.connect.key({
|
||||
integrationID: options.provider(),
|
||||
location: location(),
|
||||
key,
|
||||
...(store.formAnswer ? { answer: store.formAnswer } : {}),
|
||||
})
|
||||
await finish()
|
||||
}
|
||||
const completeCode = async (code: string) => {
|
||||
const authorization = store.authorization
|
||||
if (!authorization) return language.t("provider.connect.oauth.code.invalid")
|
||||
const result = await serverSDK()
|
||||
.api.integration.oauth.complete({
|
||||
integrationID: options.provider(),
|
||||
attemptID: authorization.attemptID,
|
||||
location: location(),
|
||||
code,
|
||||
})
|
||||
.then(() => ({ ok: true as const }))
|
||||
.catch((error) => ({ ok: false as const, error }))
|
||||
if (!result.ok) {
|
||||
const message = result.error instanceof Error ? result.error.message : String(result.error)
|
||||
return message || language.t("provider.connect.oauth.code.invalid")
|
||||
}
|
||||
await finish()
|
||||
return undefined
|
||||
}
|
||||
|
||||
let auto = false
|
||||
createEffect(() => {
|
||||
if (auto || integration.loading || methods().length !== 1) return
|
||||
auto = true
|
||||
void select(0)
|
||||
})
|
||||
onCleanup(() => {
|
||||
polling.disposed = true
|
||||
cancelPolling()
|
||||
})
|
||||
|
||||
return {
|
||||
loading: () => integration.loading,
|
||||
methods,
|
||||
currentMethod,
|
||||
methodIndex: () => store.methodIndex,
|
||||
authorization: () => store.authorization,
|
||||
auth: {
|
||||
state: () => store.state,
|
||||
error: () => store.error,
|
||||
select,
|
||||
reset,
|
||||
connectKey,
|
||||
completeCode,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderConnectionController = ReturnType<typeof createProviderConnectionController>
|
||||
@@ -0,0 +1,141 @@
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { createMemo, createResource } from "solid-js"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { ServerConnection, useServer } from "@/context/server"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { type ServerHealth } from "@/utils/server-health"
|
||||
import { showToast } from "@/utils/toast"
|
||||
|
||||
function showRequestError(language: ReturnType<typeof useLanguage>, err: unknown) {
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: language.t("common.requestFailed"),
|
||||
description: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
}
|
||||
|
||||
function useDefaultServer() {
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const [defaultKey, defaultKeyActions] = createResource(
|
||||
async () => {
|
||||
try {
|
||||
return (await platform.getDefaultServer?.()) ?? null
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
return null
|
||||
}
|
||||
},
|
||||
{ initialValue: null },
|
||||
)
|
||||
|
||||
const set = async (key: ServerConnection.Key | null) => {
|
||||
try {
|
||||
await platform.setDefaultServer?.(key)
|
||||
defaultKeyActions.mutate(key)
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: () => defaultKey.latest,
|
||||
available: createMemo(() => !!platform.getDefaultServer && !!platform.setDefaultServer),
|
||||
set,
|
||||
}
|
||||
}
|
||||
|
||||
export function useServerActionsController() {
|
||||
const server = useServer()
|
||||
const tabs = useTabs()
|
||||
const platform = usePlatform()
|
||||
const language = useLanguage()
|
||||
const defaults = useDefaultServer()
|
||||
|
||||
const remove = async (key: ServerConnection.Key) => {
|
||||
try {
|
||||
if (key.startsWith("wsl:")) await platform.wslServers?.removeServer(key)
|
||||
tabs.removeServer(key)
|
||||
server.remove(key)
|
||||
if ((await platform.getDefaultServer?.()) === key) await defaults.set(null)
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
return { defaults, connection: { canRemove: server.canRemove, remove } }
|
||||
}
|
||||
|
||||
export type ServerActionsController = ReturnType<typeof useServerActionsController>
|
||||
|
||||
export function useServerCollectionController() {
|
||||
const server = useServer()
|
||||
const global = useGlobal()
|
||||
const settings = useSettings()
|
||||
const actions = useServerActionsController()
|
||||
|
||||
const items = createMemo(() => {
|
||||
const current = server.current
|
||||
const list = server.list
|
||||
if (!current) return list
|
||||
if (!list.includes(current)) return [current, ...list]
|
||||
return [current, ...list.filter((item) => item !== current)]
|
||||
})
|
||||
const current = createMemo<ServerConnection.Any | undefined>(() =>
|
||||
settings.general.newLayoutDesigns()
|
||||
? undefined
|
||||
: (items().find((item) => ServerConnection.key(item) === server.key) ?? items()[0]),
|
||||
)
|
||||
const sorted = createMemo(() => {
|
||||
const raw = items()
|
||||
const list = raw
|
||||
if (!list.length) return list
|
||||
const active = current()
|
||||
const order = new Map(list.map((item, index) => [item, index] as const))
|
||||
const rank = (value?: ServerHealth) => {
|
||||
if (value?.healthy === true) return 0
|
||||
if (value?.healthy === false) return 2
|
||||
return 1
|
||||
}
|
||||
return list.slice().sort((a, b) => {
|
||||
if (a === active) return -1
|
||||
if (b === active) return 1
|
||||
const diff =
|
||||
rank(global.servers.health[ServerConnection.key(a)]) - rank(global.servers.health[ServerConnection.key(b)])
|
||||
if (diff !== 0) return diff
|
||||
return (order.get(a) ?? 0) - (order.get(b) ?? 0)
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
collection: {
|
||||
items: sorted,
|
||||
current,
|
||||
health: () => global.servers.health,
|
||||
},
|
||||
...actions,
|
||||
}
|
||||
}
|
||||
|
||||
export type ServerCollectionController = ReturnType<typeof useServerCollectionController>
|
||||
|
||||
export function useServerDomainController(options: { onSelect?: () => void } = {}) {
|
||||
const navigate = useNavigate()
|
||||
const server = useServer()
|
||||
const global = useGlobal()
|
||||
const collection = useServerCollectionController()
|
||||
|
||||
const select = async (connection: ServerConnection.Any) => {
|
||||
if (global.servers.health[ServerConnection.key(connection)]?.healthy === false) return
|
||||
options.onSelect?.()
|
||||
navigate("/")
|
||||
queueMicrotask(() => server.setActive(ServerConnection.key(connection)))
|
||||
}
|
||||
|
||||
return { ...collection, selection: { select } }
|
||||
}
|
||||
|
||||
export type ServerDomainController = ReturnType<typeof useServerDomainController>
|
||||
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { ServerConnection } from "@/context/server"
|
||||
import { createServerHealthPreview, replaceServerConnection, type ServerFormValues } from "./server-management"
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void
|
||||
const promise = new Promise<T>((done) => {
|
||||
resolve = done
|
||||
})
|
||||
return { promise, resolve }
|
||||
}
|
||||
|
||||
const values = (url: string): ServerFormValues => ({ url, name: "", username: "opencode", password: "" })
|
||||
|
||||
describe("createServerHealthPreview", () => {
|
||||
test("ignores an older response that resolves after the latest response", async () => {
|
||||
const first = deferred<{ healthy: boolean }>()
|
||||
const second = deferred<{ healthy: boolean }>()
|
||||
const requests = [first, second]
|
||||
const status: Array<boolean | undefined> = []
|
||||
const preview = createServerHealthPreview(() => requests.shift()!.promise)
|
||||
|
||||
const older = preview.preview(values("old.example.com"), (value) => status.push(value))
|
||||
const latest = preview.preview(values("new.example.com"), (value) => status.push(value))
|
||||
second.resolve({ healthy: true })
|
||||
await latest
|
||||
first.resolve({ healthy: false })
|
||||
await older
|
||||
|
||||
expect(status).toEqual([undefined, undefined, true])
|
||||
})
|
||||
|
||||
test("an incomplete value invalidates an in-flight response", async () => {
|
||||
const request = deferred<{ healthy: boolean }>()
|
||||
const status: Array<boolean | undefined> = []
|
||||
const preview = createServerHealthPreview(() => request.promise)
|
||||
|
||||
const pending = preview.preview(values("server.example.com"), (value) => status.push(value))
|
||||
await preview.preview(values("server"), (value) => status.push(value))
|
||||
request.resolve({ healthy: true })
|
||||
await pending
|
||||
|
||||
expect(status).toEqual([undefined, undefined])
|
||||
})
|
||||
|
||||
test("cancellation prevents an in-flight response from updating status", async () => {
|
||||
const request = deferred<{ healthy: boolean }>()
|
||||
const status: Array<boolean | undefined> = []
|
||||
const preview = createServerHealthPreview(() => request.promise)
|
||||
|
||||
const pending = preview.preview(values("server.example.com"), (value) => status.push(value))
|
||||
preview.cancel()
|
||||
request.resolve({ healthy: true })
|
||||
await pending
|
||||
|
||||
expect(status).toEqual([undefined])
|
||||
})
|
||||
})
|
||||
|
||||
describe("replaceServerConnection", () => {
|
||||
const original: ServerConnection.Http = { type: "http", http: { url: "https://old.example.com" } }
|
||||
const next: ServerConnection.Http = { type: "http", http: { url: "https://new.example.com" } }
|
||||
|
||||
test("moves active selection after adding the replacement and removes the original", () => {
|
||||
const calls: string[] = []
|
||||
|
||||
replaceServerConnection(ServerConnection.key(original), next, {
|
||||
active: () => ServerConnection.key(original),
|
||||
removeTabs: (key) => calls.push(`tabs:${key}`),
|
||||
add: (server) => {
|
||||
calls.push(`add:${ServerConnection.key(server)}`)
|
||||
return server
|
||||
},
|
||||
setActive: (key) => calls.push(`active:${key}`),
|
||||
remove: (key) => calls.push(`remove:${key}`),
|
||||
})
|
||||
|
||||
expect(calls).toEqual([
|
||||
"tabs:https://old.example.com",
|
||||
"add:https://new.example.com",
|
||||
"active:https://new.example.com",
|
||||
"remove:https://old.example.com",
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps the original when the replacement cannot be added", () => {
|
||||
const removed: ServerConnection.Key[] = []
|
||||
|
||||
replaceServerConnection(ServerConnection.key(original), next, {
|
||||
active: () => ServerConnection.key(original),
|
||||
removeTabs: () => {},
|
||||
add: () => undefined,
|
||||
setActive: () => {},
|
||||
remove: (key) => removed.push(key),
|
||||
})
|
||||
|
||||
expect(removed).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import { normalizeServerUrl, ServerConnection } from "@/context/server"
|
||||
import type { ServerHealth } from "@/utils/server-health"
|
||||
|
||||
export type ServerFormValues = {
|
||||
url: string
|
||||
name: string
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export function createServerHealthPreview(
|
||||
check: (server: ServerConnection.HttpBase) => Promise<Pick<ServerHealth, "healthy">>,
|
||||
) {
|
||||
let generation = 0
|
||||
|
||||
const cancel = () => {
|
||||
generation += 1
|
||||
}
|
||||
|
||||
const preview = async (values: ServerFormValues, setStatus: (value: boolean | undefined) => void) => {
|
||||
const current = ++generation
|
||||
setStatus(undefined)
|
||||
const normalized = normalizeServerUrl(values.url)
|
||||
if (!normalized) return
|
||||
const host = normalized.replace(/^https?:\/\//, "").split("/")[0]
|
||||
if (!host) return
|
||||
if (!host.includes("localhost") && !host.startsWith("127.0.0.1") && !host.includes(".") && !host.includes(":"))
|
||||
return
|
||||
|
||||
const http: ServerConnection.HttpBase = { url: normalized }
|
||||
if (values.username) http.username = values.username
|
||||
if (values.password) http.password = values.password
|
||||
const result = await check(http)
|
||||
if (current !== generation) return
|
||||
setStatus(result.healthy)
|
||||
}
|
||||
|
||||
return { cancel, preview }
|
||||
}
|
||||
|
||||
export function replaceServerConnection(
|
||||
originalKey: ServerConnection.Key,
|
||||
next: ServerConnection.Http,
|
||||
operations: {
|
||||
active: () => ServerConnection.Key | undefined
|
||||
removeTabs: (key: ServerConnection.Key) => void
|
||||
add: (server: ServerConnection.Http) => ServerConnection.Any | undefined
|
||||
setActive: (key: ServerConnection.Key) => void
|
||||
remove: (key: ServerConnection.Key) => void
|
||||
},
|
||||
) {
|
||||
const active = operations.active()
|
||||
operations.removeTabs(originalKey)
|
||||
const added = operations.add(next)
|
||||
if (!added) return
|
||||
const nextActive = active === originalKey ? ServerConnection.key(added) : active
|
||||
if (nextActive) operations.setActive(nextActive)
|
||||
operations.remove(originalKey)
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
|
||||
import { type Component, Show } from "solid-js"
|
||||
import { useServerManagementController } from "@/components/dialog-select-server"
|
||||
import type { ServerActionsController } from "@/components/server/server-management-controller"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { ServerConnection } from "@/context/server"
|
||||
|
||||
export const ServerRowMenu: Component<{
|
||||
server: ServerConnection.Any
|
||||
controller: ReturnType<typeof useServerManagementController>
|
||||
domain: ServerActionsController
|
||||
onEdit: (server: ServerConnection.Http) => void
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
@@ -19,13 +19,13 @@ export const ServerRowMenu: Component<{
|
||||
<ServerRowMenuView
|
||||
server={props.server}
|
||||
labels={serverMenuLabels(language)}
|
||||
canDefault={props.controller.canDefault()}
|
||||
isDefault={props.controller.defaultKey() === key}
|
||||
canRemove={props.controller.canRemove(key)}
|
||||
canDefault={props.domain.defaults.available()}
|
||||
isDefault={props.domain.defaults.key() === key}
|
||||
canRemove={props.domain.connection.canRemove(key)}
|
||||
onEdit={props.onEdit}
|
||||
onSetDefault={() => props.controller.setDefault(key)}
|
||||
onRemoveDefault={() => props.controller.setDefault(null)}
|
||||
onRemove={() => props.controller.handleRemove(key)}
|
||||
onSetDefault={() => props.domain.defaults.set(key)}
|
||||
onRemoveDefault={() => props.domain.defaults.set(null)}
|
||||
onRemove={() => props.domain.connection.remove(key)}
|
||||
open={props.open}
|
||||
onOpenChange={props.onOpenChange}
|
||||
/>
|
||||
|
||||
@@ -5,12 +5,15 @@ import { checksum } from "@opencode-ai/core/util/encode"
|
||||
import { findLast } from "@opencode-ai/core/util/array"
|
||||
import { same } from "@/utils/same"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { Accordion } from "@opencode-ai/ui/accordion"
|
||||
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
|
||||
import { File } from "@opencode-ai/session-ui/file"
|
||||
import { Markdown } from "@opencode-ai/session-ui/markdown"
|
||||
import { ScrollView } from "@opencode-ai/ui/scroll-view"
|
||||
import type { Message, Part, UserMessage } from "@/types"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { downloadSessionExport, fetchSessionExport, sessionExportFilename } from "@/utils/session-export"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useProviders } from "@/hooks/use-providers"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
@@ -120,7 +123,8 @@ export function SessionContextTab() {
|
||||
() => {
|
||||
const revert = info()?.revert?.messageID
|
||||
if (!revert) return userMessages()
|
||||
return userMessages().filter((m) => m.id < revert)
|
||||
const boundary = userMessages().findIndex((message) => message.id === revert)
|
||||
return boundary < 0 ? userMessages() : userMessages().slice(0, boundary)
|
||||
},
|
||||
emptyUserMessages,
|
||||
{ equals: same },
|
||||
@@ -220,6 +224,31 @@ export function SessionContextTab() {
|
||||
{ label: "context.stats.lastActivity", value: () => formatter().time(ctx()?.message.time.created) },
|
||||
] satisfies { label: string; value: () => JSX.Element }[]
|
||||
|
||||
const exportSession = async () => {
|
||||
const sessionID = params.id
|
||||
if (!sessionID) return
|
||||
try {
|
||||
const data = await fetchSessionExport({
|
||||
sessionID,
|
||||
api: sdk().api,
|
||||
})
|
||||
const filename = sessionExportFilename(data.info)
|
||||
downloadSessionExport(filename, data)
|
||||
showToast({
|
||||
variant: "success",
|
||||
icon: "circle-check",
|
||||
title: language.t("toast.session.export.success.title"),
|
||||
description: language.t("toast.session.export.success.description", { filename }),
|
||||
})
|
||||
} catch (err) {
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: language.t("toast.session.export.failed.title"),
|
||||
description: err instanceof Error ? err.message : language.t("toast.session.export.failed.description"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let scroll: HTMLDivElement | undefined
|
||||
let frame: number | undefined
|
||||
let pending: { x: number; y: number } | undefined
|
||||
@@ -328,7 +357,18 @@ export function SessionContextTab() {
|
||||
</Show>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="text-12-regular text-text-weak">{language.t("context.rawMessages.title")}</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-12-regular text-text-weak">{language.t("context.rawMessages.title")}</div>
|
||||
<Button
|
||||
size="small"
|
||||
variant="ghost"
|
||||
class="gap-1.5 px-2 text-text-weak hover:text-text-base"
|
||||
onClick={exportSession}
|
||||
>
|
||||
<Icon name="download" size="small" />
|
||||
<span>{language.t("context.export.session")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Accordion multiple>
|
||||
<For each={messages()}>
|
||||
{(message) => (
|
||||
|
||||
@@ -289,9 +289,9 @@ export function SessionHeader() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={search() && centerMount()}>
|
||||
<Show when={search() && centerMount()} keyed>
|
||||
{(mount) => (
|
||||
<Portal mount={mount()}>
|
||||
<Portal mount={mount}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
@@ -308,10 +308,10 @@ export function SessionHeader() {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Show when={hotkey()}>
|
||||
<Show when={hotkey()} keyed>
|
||||
{(keybind) => (
|
||||
<Keybind class="shrink-0 !border-0 !bg-transparent !shadow-none px-0 text-text-weaker">
|
||||
{keybind()}
|
||||
{keybind}
|
||||
</Keybind>
|
||||
)}
|
||||
</Show>
|
||||
@@ -319,9 +319,9 @@ export function SessionHeader() {
|
||||
</Portal>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={rightMount()}>
|
||||
<Show when={rightMount()} keyed>
|
||||
{(mount) => (
|
||||
<Portal mount={mount()}>
|
||||
<Portal mount={mount}>
|
||||
<Show
|
||||
when={isV2}
|
||||
fallback={
|
||||
|
||||
@@ -3,51 +3,63 @@ import { Dialog, DialogBody, DialogFooter, DialogHeader, DialogTitle } from "@op
|
||||
import { DividerV2 } from "@opencode-ai/ui/v2/divider-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { type Component, Show, createEffect, createSignal, onCleanup, onMount } from "solid-js"
|
||||
import { useMutation } from "@tanstack/solid-query"
|
||||
import { type Component, Show, createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import {
|
||||
createServerHealthPreview,
|
||||
replaceServerConnection,
|
||||
type ServerFormValues,
|
||||
} from "@/components/server/server-management"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { type ServerConnection } from "@/context/server"
|
||||
import { useServerManagementController } from "../dialog-select-server"
|
||||
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { useCheckServerHealth } from "@/utils/server-health"
|
||||
import "./settings-v2.css"
|
||||
|
||||
const DEFAULT_USERNAME = "opencode"
|
||||
|
||||
type FormMode = "list" | "add" | "edit"
|
||||
|
||||
export const DialogServerV2: Component<{
|
||||
mode: "add" | "edit"
|
||||
server?: ServerConnection.Http
|
||||
}> = (props) => {
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const controller = useServerManagementController({
|
||||
const form = createFormController({
|
||||
onSelect: () => dialog.close(),
|
||||
navigateOnAdd: false,
|
||||
})
|
||||
const [opened, setOpened] = createSignal(false)
|
||||
|
||||
onMount(() => {
|
||||
if (props.mode === "add") controller.startAdd()
|
||||
if (props.mode === "edit" && props.server) controller.startEdit(props.server)
|
||||
if (props.mode === "add") form.start.add()
|
||||
if (props.mode === "edit" && props.server) form.start.edit(props.server)
|
||||
setOpened(true)
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
controller.resetForm()
|
||||
form.reset()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!opened()) return
|
||||
if (controller.isFormMode()) return
|
||||
if (form.state.open()) return
|
||||
dialog.close()
|
||||
})
|
||||
|
||||
const keyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== "Enter" || event.isComposing) return
|
||||
event.preventDefault()
|
||||
controller.submitForm()
|
||||
form.submit()
|
||||
}
|
||||
|
||||
const title = () =>
|
||||
props.mode === "add" ? language.t("dialog.server.add.title") : language.t("dialog.server.edit.title")
|
||||
|
||||
const submitLabel = () => {
|
||||
if (controller.formBusy()) return language.t("dialog.server.add.checking")
|
||||
if (form.state.busy()) return language.t("dialog.server.add.checking")
|
||||
if (props.mode === "add") return language.t("dialog.server.add.button")
|
||||
return language.t("common.save")
|
||||
}
|
||||
@@ -66,16 +78,16 @@ export const DialogServerV2: Component<{
|
||||
type="text"
|
||||
appearance="large"
|
||||
class="!w-full self-stretch"
|
||||
value={controller.formValue()}
|
||||
value={form.state.value()}
|
||||
placeholder={language.t("dialog.server.add.placeholder")}
|
||||
invalid={!!controller.formError()}
|
||||
disabled={controller.formBusy()}
|
||||
invalid={!!form.state.error()}
|
||||
disabled={form.state.busy()}
|
||||
autofocus
|
||||
onInput={(event) => controller.handleFormChange()(event.currentTarget.value)}
|
||||
onInput={(event) => form.change.value(event.currentTarget.value)}
|
||||
onKeyDown={keyDown}
|
||||
/>
|
||||
<Show when={controller.formError()}>
|
||||
<span class="settings-v2-server-dialog-error">{controller.formError()}</span>
|
||||
<Show when={form.state.error()}>
|
||||
<span class="settings-v2-server-dialog-error">{form.state.error()}</span>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="flex w-full min-w-0 flex-col gap-2">
|
||||
@@ -84,10 +96,10 @@ export const DialogServerV2: Component<{
|
||||
type="text"
|
||||
appearance="large"
|
||||
class="!w-full self-stretch"
|
||||
value={controller.formName()}
|
||||
value={form.state.name()}
|
||||
placeholder={language.t("dialog.server.add.namePlaceholder")}
|
||||
disabled={controller.formBusy()}
|
||||
onInput={(event) => controller.handleFormNameChange()(event.currentTarget.value)}
|
||||
disabled={form.state.busy()}
|
||||
onInput={(event) => form.change.name(event.currentTarget.value)}
|
||||
onKeyDown={keyDown}
|
||||
/>
|
||||
</div>
|
||||
@@ -98,10 +110,10 @@ export const DialogServerV2: Component<{
|
||||
type="text"
|
||||
appearance="large"
|
||||
class="!w-full self-stretch"
|
||||
value={controller.formUsername()}
|
||||
value={form.state.username()}
|
||||
placeholder={language.t("dialog.server.add.usernamePlaceholder")}
|
||||
disabled={controller.formBusy()}
|
||||
onInput={(event) => controller.handleFormUsernameChange()(event.currentTarget.value)}
|
||||
disabled={form.state.busy()}
|
||||
onInput={(event) => form.change.username(event.currentTarget.value)}
|
||||
onKeyDown={keyDown}
|
||||
/>
|
||||
</div>
|
||||
@@ -111,10 +123,10 @@ export const DialogServerV2: Component<{
|
||||
type="password"
|
||||
appearance="large"
|
||||
class="!w-full self-stretch"
|
||||
value={controller.formPassword()}
|
||||
value={form.state.password()}
|
||||
placeholder={language.t("dialog.server.add.passwordPlaceholder")}
|
||||
disabled={controller.formBusy()}
|
||||
onInput={(event) => controller.handleFormPasswordChange()(event.currentTarget.value)}
|
||||
disabled={form.state.busy()}
|
||||
onInput={(event) => form.change.password(event.currentTarget.value)}
|
||||
onKeyDown={keyDown}
|
||||
/>
|
||||
</div>
|
||||
@@ -122,13 +134,171 @@ export const DialogServerV2: Component<{
|
||||
</div>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<ButtonV2 variant="neutral" disabled={controller.formBusy()} onClick={() => dialog.close()}>
|
||||
<ButtonV2 variant="neutral" disabled={form.state.busy()} onClick={() => dialog.close()}>
|
||||
{language.t("common.cancel")}
|
||||
</ButtonV2>
|
||||
<ButtonV2 variant="contrast" disabled={controller.formBusy()} onClick={controller.submitForm}>
|
||||
<ButtonV2 variant="contrast" disabled={form.state.busy()} onClick={form.submit}>
|
||||
{submitLabel()}
|
||||
</ButtonV2>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function createFormController(options: { onSelect?: () => void } = {}) {
|
||||
const server = useServer()
|
||||
const tabs = useTabs()
|
||||
const global = useGlobal()
|
||||
const language = useLanguage()
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
const healthPreview = createServerHealthPreview(checkServerHealth)
|
||||
const [store, setStore] = createStore({
|
||||
mode: "list" as FormMode,
|
||||
originalUrl: undefined as string | undefined,
|
||||
values: { url: "", name: "", username: DEFAULT_USERNAME, password: "" },
|
||||
error: "",
|
||||
status: undefined as boolean | undefined,
|
||||
})
|
||||
|
||||
onCleanup(healthPreview.cancel)
|
||||
|
||||
const reset = () => {
|
||||
healthPreview.cancel()
|
||||
setStore({
|
||||
mode: "list",
|
||||
originalUrl: undefined,
|
||||
values: { url: "", name: "", username: DEFAULT_USERNAME, password: "" },
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
const allServers = () => {
|
||||
if (!server.current || server.list.includes(server.current)) return server.list
|
||||
return [server.current, ...server.list]
|
||||
}
|
||||
const editing = createMemo(() =>
|
||||
allServers().find((item) => item.type === "http" && item.http.url === store.originalUrl),
|
||||
)
|
||||
const add = (connection: ServerConnection.Http) => server.add(connection)
|
||||
const replace = (originalKey: ServerConnection.Key, next: ServerConnection.Http) =>
|
||||
replaceServerConnection(originalKey, next, {
|
||||
active: () => server.key,
|
||||
removeTabs: (key) => tabs.removeServer(key),
|
||||
add,
|
||||
setActive: (key) => server.setActive(key),
|
||||
remove: (key) => server.remove(key),
|
||||
})
|
||||
|
||||
const request = useMutation(() => ({
|
||||
mutationFn: async () => {
|
||||
const normalized = normalizeServerUrl(store.values.url)
|
||||
if (!normalized) {
|
||||
reset()
|
||||
return
|
||||
}
|
||||
|
||||
const original = store.mode === "edit" ? editing() : undefined
|
||||
if (store.mode === "edit" && !original) return
|
||||
const name = store.values.name.trim() || undefined
|
||||
const username = store.values.username || undefined
|
||||
const password = store.values.password || undefined
|
||||
if (
|
||||
original?.type === "http" &&
|
||||
normalized === original.http.url &&
|
||||
name === original.displayName &&
|
||||
username === original.http.username &&
|
||||
password === original.http.password
|
||||
) {
|
||||
reset()
|
||||
return
|
||||
}
|
||||
|
||||
const connection: ServerConnection.Http = {
|
||||
type: "http",
|
||||
displayName: name,
|
||||
http: {
|
||||
url: normalized,
|
||||
username: store.mode === "add" && !password ? undefined : username,
|
||||
password,
|
||||
},
|
||||
}
|
||||
const result = await checkServerHealth(connection.http)
|
||||
if (!result.healthy) {
|
||||
setStore("error", language.t("dialog.server.add.error"))
|
||||
return
|
||||
}
|
||||
if (original?.type === "http") {
|
||||
if (normalized === original.http.url) add(connection)
|
||||
if (normalized !== original.http.url) replace(ServerConnection.key(original), connection)
|
||||
reset()
|
||||
return
|
||||
}
|
||||
|
||||
reset()
|
||||
add(connection)
|
||||
options.onSelect?.()
|
||||
},
|
||||
}))
|
||||
|
||||
const preview = () => void healthPreview.preview(store.values, (status) => setStore("status", status))
|
||||
const change = (field: keyof ServerFormValues, value: string) => {
|
||||
if (request.isPending) return
|
||||
setStore("values", field, value)
|
||||
setStore("error", "")
|
||||
if (field !== "name") preview()
|
||||
}
|
||||
const startAdd = () => {
|
||||
reset()
|
||||
setStore("mode", "add")
|
||||
}
|
||||
const startEdit = (connection: ServerConnection.Http) => {
|
||||
reset()
|
||||
setStore({
|
||||
mode: "edit",
|
||||
originalUrl: connection.http.url,
|
||||
values: {
|
||||
url: connection.http.url,
|
||||
name: connection.displayName ?? "",
|
||||
username: connection.http.username ?? "",
|
||||
password: connection.http.password ?? "",
|
||||
},
|
||||
error: "",
|
||||
status: global.servers.health[ServerConnection.key(connection)]?.healthy,
|
||||
})
|
||||
}
|
||||
const submit = () => {
|
||||
if (store.mode === "list" || request.isPending) return
|
||||
setStore("error", "")
|
||||
request.mutate()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (store.mode !== "edit") return
|
||||
if (editing()) return
|
||||
reset()
|
||||
})
|
||||
|
||||
return {
|
||||
state: {
|
||||
mode: () => store.mode,
|
||||
open: () => store.mode !== "list",
|
||||
adding: () => store.mode === "add",
|
||||
busy: () => request.isPending,
|
||||
value: () => store.values.url,
|
||||
name: () => store.values.name,
|
||||
username: () => store.values.username,
|
||||
password: () => store.values.password,
|
||||
error: () => store.error,
|
||||
status: () => store.status,
|
||||
},
|
||||
change: {
|
||||
value: (value: string) => change("url", value),
|
||||
name: (value: string) => change("name", value),
|
||||
username: (value: string) => change("username", value),
|
||||
password: (value: string) => change("password", value),
|
||||
},
|
||||
start: { add: startAdd, edit: startEdit },
|
||||
reset,
|
||||
submit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { onCleanup } from "solid-js"
|
||||
|
||||
export type ShellOption = {
|
||||
path: string
|
||||
name: string
|
||||
acceptable: boolean
|
||||
}
|
||||
|
||||
export type ShellSelectOption = {
|
||||
id: string
|
||||
value: string
|
||||
name: string
|
||||
terminalOnly: boolean
|
||||
}
|
||||
|
||||
export function createShellOptions(input: { shells: ShellOption[]; current: string | undefined }) {
|
||||
const counts = input.shells.reduce((result, shell) => {
|
||||
result.set(shell.name, (result.get(shell.name) ?? 0) + 1)
|
||||
return result
|
||||
}, new Map<string, number>())
|
||||
const options: ShellSelectOption[] = [
|
||||
{ id: "auto", value: "", name: "", terminalOnly: false },
|
||||
...input.shells.map((shell) => {
|
||||
const ambiguous = (counts.get(shell.name) ?? 0) > 1
|
||||
const name = ambiguous ? shell.path : shell.name
|
||||
return {
|
||||
id: shell.path,
|
||||
value: ambiguous ? shell.path : shell.name,
|
||||
name,
|
||||
terminalOnly: !shell.acceptable,
|
||||
}
|
||||
}),
|
||||
]
|
||||
if (input.current && !options.some((option) => option.value === input.current)) {
|
||||
options.push({ id: input.current, value: input.current, name: input.current, terminalOnly: false })
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
export function createSoundPreviewController(player: (id: string | undefined) => Promise<(() => void) | undefined>) {
|
||||
let cleanup: (() => void) | undefined
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined
|
||||
let run = 0
|
||||
|
||||
const stop = () => {
|
||||
run += 1
|
||||
cleanup?.()
|
||||
clearTimeout(timeout)
|
||||
cleanup = undefined
|
||||
timeout = undefined
|
||||
}
|
||||
const play = (id: string | undefined) => {
|
||||
stop()
|
||||
if (!id) return
|
||||
const current = ++run
|
||||
timeout = setTimeout(() => {
|
||||
timeout = undefined
|
||||
void player(id).then((next) => {
|
||||
if (run === current) {
|
||||
cleanup = next
|
||||
return
|
||||
}
|
||||
next?.()
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
|
||||
onCleanup(stop)
|
||||
return { play, stop }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, test, vi } from "bun:test"
|
||||
import { createRoot } from "solid-js"
|
||||
import { createShellOptions, createSoundPreviewController } from "./general-controller-behavior"
|
||||
|
||||
describe("settings v2 controllers", () => {
|
||||
test("normalizes shell names and preserves an unavailable configured shell", () => {
|
||||
expect(
|
||||
createShellOptions({
|
||||
shells: [
|
||||
{ path: "/bin/bash", name: "bash", acceptable: true },
|
||||
{ path: "/opt/bash", name: "bash", acceptable: false },
|
||||
{ path: "/bin/zsh", name: "zsh", acceptable: true },
|
||||
],
|
||||
current: "fish",
|
||||
}),
|
||||
).toEqual([
|
||||
{ id: "auto", value: "", name: "", terminalOnly: false },
|
||||
{ id: "/bin/bash", value: "/bin/bash", name: "/bin/bash", terminalOnly: false },
|
||||
{ id: "/opt/bash", value: "/opt/bash", name: "/opt/bash", terminalOnly: true },
|
||||
{ id: "/bin/zsh", value: "zsh", name: "zsh", terminalOnly: false },
|
||||
{ id: "fish", value: "fish", name: "fish", terminalOnly: false },
|
||||
])
|
||||
})
|
||||
|
||||
test("debounces previews and stops owned audio on disposal", async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const played: string[] = []
|
||||
const stopped: string[] = []
|
||||
const owned = createRoot((dispose) => ({
|
||||
dispose,
|
||||
preview: createSoundPreviewController(async (id) => {
|
||||
played.push(id ?? "")
|
||||
return () => stopped.push(id ?? "")
|
||||
}),
|
||||
}))
|
||||
|
||||
owned.preview.play("first")
|
||||
vi.advanceTimersByTime(99)
|
||||
expect(played).toEqual([])
|
||||
|
||||
owned.preview.play("second")
|
||||
vi.advanceTimersByTime(100)
|
||||
await Promise.resolve()
|
||||
expect(played).toEqual(["second"])
|
||||
|
||||
owned.dispose()
|
||||
expect(stopped).toEqual(["second"])
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,172 @@
|
||||
import { createMemo, createResource, onMount, type Accessor } from "solid-js"
|
||||
import type { ColorScheme } from "@opencode-ai/ui/theme/context"
|
||||
import { useTheme } from "@opencode-ai/ui/theme/context"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import {
|
||||
monoDefault,
|
||||
monoFontFamily,
|
||||
monoInput,
|
||||
sansDefault,
|
||||
sansFontFamily,
|
||||
sansInput,
|
||||
terminalDefault,
|
||||
terminalFontFamily,
|
||||
terminalInput,
|
||||
useSettings,
|
||||
} from "@/context/settings"
|
||||
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
||||
import { createSoundPreviewController, type ShellOption } from "./general-controller-behavior"
|
||||
|
||||
export { createShellOptions, createSoundPreviewController } from "./general-controller-behavior"
|
||||
export type { ShellOption, ShellSelectOption } from "./general-controller-behavior"
|
||||
|
||||
export function createPermissionScopeController(sessionID: Accessor<string | undefined>) {
|
||||
const permission = usePermission()
|
||||
const serverSync = useServerSync()
|
||||
const directory = createMemo(() => {
|
||||
const id = sessionID()
|
||||
if (!id) return undefined
|
||||
return serverSync().session.lineage.peek(id)?.session.location.directory
|
||||
})
|
||||
|
||||
return {
|
||||
accepting: createMemo(() => {
|
||||
const id = sessionID()
|
||||
const dir = directory()
|
||||
if (!id || !dir) return false
|
||||
return permission.isAutoAccepting(id, dir)
|
||||
}),
|
||||
enabled: createMemo(() => !!directory()),
|
||||
set: (checked: boolean) => {
|
||||
const id = sessionID()
|
||||
const dir = directory()
|
||||
if (!id || !dir) return
|
||||
if (checked) return permission.enableAutoAccept(id, dir)
|
||||
permission.disableAutoAccept(id, dir)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function createShellSettingsController() {
|
||||
const serverSync = useServerSync()
|
||||
const [shells] = createResource(
|
||||
async () => {
|
||||
// TODO: Dax is considering the V2 shell discovery and config update APIs.
|
||||
// return (await sdk.api.pty.shells()).data
|
||||
return [] as ShellOption[]
|
||||
},
|
||||
{ initialValue: [] as ShellOption[] },
|
||||
)
|
||||
const current = createMemo(() => serverSync().data.config.shell ?? "")
|
||||
|
||||
return {
|
||||
shells: () => shells.latest,
|
||||
current,
|
||||
select: (value: string) => {
|
||||
if (value === current()) return
|
||||
// TODO: Dax is considering the V2 shell discovery and config update APIs.
|
||||
// void serverSync().updateConfig({ shell: value })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function createAppearanceSettingsController() {
|
||||
const settings = useSettings()
|
||||
const theme = useTheme()
|
||||
const themes = createMemo(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
|
||||
|
||||
onMount(() => void theme.loadThemes())
|
||||
|
||||
return {
|
||||
scheme: {
|
||||
current: theme.colorScheme,
|
||||
select: (value: ColorScheme) => theme.setColorScheme(value),
|
||||
},
|
||||
theme: {
|
||||
options: themes,
|
||||
current: createMemo(() => themes().find((option) => option.id === theme.themeId())),
|
||||
select: (option: { id: string } | null) => option && theme.setTheme(option.id),
|
||||
},
|
||||
fonts: {
|
||||
ui: createMemo(() => ({
|
||||
value: sansInput(settings.appearance.uiFont()),
|
||||
family: sansFontFamily(settings.appearance.uiFont()),
|
||||
placeholder: sansDefault,
|
||||
})),
|
||||
code: createMemo(() => ({
|
||||
value: monoInput(settings.appearance.font()),
|
||||
family: monoFontFamily(settings.appearance.font()),
|
||||
placeholder: monoDefault,
|
||||
})),
|
||||
terminal: createMemo(() => ({
|
||||
value: terminalInput(settings.appearance.terminalFont()),
|
||||
family: terminalFontFamily(settings.appearance.terminalFont()),
|
||||
placeholder: terminalDefault,
|
||||
})),
|
||||
setUI: (value: string) => settings.appearance.setUIFont(value),
|
||||
setCode: (value: string) => settings.appearance.setFont(value),
|
||||
setTerminal: (value: string) => settings.appearance.setTerminalFont(value),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const noneSound = { id: "none", label: "sound.option.none" } as const
|
||||
export const soundOptions = [noneSound, ...SOUND_OPTIONS]
|
||||
export type SoundSelectOption = (typeof soundOptions)[number]
|
||||
|
||||
export function createSoundSettingsController() {
|
||||
const settings = useSettings()
|
||||
const preview = createSoundPreviewController(playSoundById)
|
||||
const channel = (
|
||||
enabled: Accessor<boolean>,
|
||||
current: Accessor<string>,
|
||||
setEnabled: (value: boolean) => void,
|
||||
set: (id: string) => void,
|
||||
) => ({
|
||||
current: createMemo(() =>
|
||||
enabled() ? (soundOptions.find((option) => option.id === current()) ?? noneSound) : noneSound,
|
||||
),
|
||||
highlight: (option: SoundSelectOption | undefined) => {
|
||||
if (!option) return
|
||||
preview.play(option.id === "none" ? undefined : option.id)
|
||||
},
|
||||
select: (option: SoundSelectOption | null) => {
|
||||
if (!option) return
|
||||
if (option.id === "none") {
|
||||
setEnabled(false)
|
||||
preview.stop()
|
||||
return
|
||||
}
|
||||
setEnabled(true)
|
||||
set(option.id)
|
||||
preview.play(option.id)
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
agent: channel(
|
||||
settings.sounds.agentEnabled,
|
||||
settings.sounds.agent,
|
||||
(value) => settings.sounds.setAgentEnabled(value),
|
||||
(id) => settings.sounds.setAgent(id),
|
||||
),
|
||||
permissions: channel(
|
||||
settings.sounds.permissionsEnabled,
|
||||
settings.sounds.permissions,
|
||||
(value) => settings.sounds.setPermissionsEnabled(value),
|
||||
(id) => settings.sounds.setPermissions(id),
|
||||
),
|
||||
errors: channel(
|
||||
settings.sounds.errorsEnabled,
|
||||
settings.sounds.errors,
|
||||
(value) => settings.sounds.setErrorsEnabled(value),
|
||||
(id) => settings.sounds.setErrors(id),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export type PermissionScopeController = ReturnType<typeof createPermissionScopeController>
|
||||
export type ShellSettingsController = ReturnType<typeof createShellSettingsController>
|
||||
export type AppearanceSettingsController = ReturnType<typeof createAppearanceSettingsController>
|
||||
export type SoundSettingsController = ReturnType<typeof createSoundSettingsController>
|
||||
@@ -1,174 +1,294 @@
|
||||
import { Component, Show, createMemo, createResource, onMount } from "solid-js"
|
||||
import { Component, Show, createMemo, createResource } from "solid-js"
|
||||
import { createMediaQuery } from "@solid-primitives/media"
|
||||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { SelectV2 } from "@opencode-ai/ui/v2/select-v2"
|
||||
import { Switch } from "@opencode-ai/ui/v2/switch-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useUpdaterAction } from "../updater-action"
|
||||
import {
|
||||
monoDefault,
|
||||
monoFontFamily,
|
||||
monoInput,
|
||||
sansDefault,
|
||||
sansFontFamily,
|
||||
sansInput,
|
||||
terminalDefault,
|
||||
terminalFontFamily,
|
||||
terminalInput,
|
||||
useSettings,
|
||||
} from "@/context/settings"
|
||||
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
||||
import { Link } from "../link"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { ExternalLink } from "../external-link"
|
||||
import { SettingsListV2 } from "./parts/list"
|
||||
import { SettingsRowV2 } from "./parts/row"
|
||||
import {
|
||||
createAppearanceSettingsController,
|
||||
createPermissionScopeController,
|
||||
createShellOptions,
|
||||
createShellSettingsController,
|
||||
createSoundSettingsController,
|
||||
soundOptions,
|
||||
type AppearanceSettingsController,
|
||||
type PermissionScopeController,
|
||||
type ShellSettingsController,
|
||||
type SoundSettingsController,
|
||||
} from "./general-controllers"
|
||||
import "./settings-v2.css"
|
||||
|
||||
let demoSoundState = {
|
||||
cleanup: undefined as (() => void) | undefined,
|
||||
timeout: undefined as NodeJS.Timeout | undefined,
|
||||
run: 0,
|
||||
const schemeOptions: ("system" | "light" | "dark")[] = ["system", "light", "dark"]
|
||||
const fontSettings = {
|
||||
ui: {
|
||||
action: "settings-ui-font",
|
||||
title: "settings.general.row.uiFont.title",
|
||||
description: "settings.general.row.uiFont.description",
|
||||
font: "ui",
|
||||
input: "setUI",
|
||||
},
|
||||
code: {
|
||||
action: "settings-code-font",
|
||||
title: "settings.general.row.font.title",
|
||||
description: "settings.general.row.font.description",
|
||||
font: "code",
|
||||
input: "setCode",
|
||||
},
|
||||
terminal: {
|
||||
action: "settings-terminal-font",
|
||||
title: "settings.general.row.terminalFont.title",
|
||||
description: "settings.general.row.terminalFont.description",
|
||||
font: "terminal",
|
||||
input: "setTerminal",
|
||||
},
|
||||
} as const
|
||||
const soundSettings = {
|
||||
agent: {
|
||||
action: "settings-sounds-agent",
|
||||
title: "settings.general.sounds.agent.title",
|
||||
description: "settings.general.sounds.agent.description",
|
||||
},
|
||||
permissions: {
|
||||
action: "settings-sounds-permissions",
|
||||
title: "settings.general.sounds.permissions.title",
|
||||
description: "settings.general.sounds.permissions.description",
|
||||
},
|
||||
errors: {
|
||||
action: "settings-sounds-errors",
|
||||
title: "settings.general.sounds.errors.title",
|
||||
description: "settings.general.sounds.errors.description",
|
||||
},
|
||||
} as const
|
||||
|
||||
const PermissionScopeSetting: Component<{ controller: PermissionScopeController }> = (props) => {
|
||||
const language = useLanguage()
|
||||
return (
|
||||
<SettingsRowV2
|
||||
title={language.t("command.permissions.autoaccept.enable")}
|
||||
description={language.t("toast.permissions.autoaccept.on.description")}
|
||||
>
|
||||
<div data-action="settings-auto-accept-permissions">
|
||||
<Switch
|
||||
checked={props.controller.accepting()}
|
||||
disabled={!props.controller.enabled()}
|
||||
onChange={props.controller.set}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}
|
||||
|
||||
type ThemeOption = {
|
||||
id: string
|
||||
name: string
|
||||
const ShellSetting: Component<{ controller: ShellSettingsController }> = (props) => {
|
||||
const language = useLanguage()
|
||||
const options = createMemo(() =>
|
||||
createShellOptions({
|
||||
shells: props.controller.shells(),
|
||||
current: props.controller.current(),
|
||||
}),
|
||||
)
|
||||
return (
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.shell.title")}
|
||||
description={language.t("settings.general.row.shell.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-shell"
|
||||
options={options()}
|
||||
current={options().find((option) => option.value === props.controller.current()) ?? options()[0]}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
value={(option) => option.id}
|
||||
label={(option) => {
|
||||
if (option.id === "auto") return language.t("settings.general.row.shell.autoDefault")
|
||||
if (!option.terminalOnly) return option.name
|
||||
return `${option.name} (${language.t("settings.general.row.shell.terminalOnly")})`
|
||||
}}
|
||||
onSelect={(option) => option && props.controller.select(option.value)}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}
|
||||
|
||||
type ShellOption = {
|
||||
path: string
|
||||
name: string
|
||||
acceptable: boolean
|
||||
const AppearanceSection: Component<{ controller: AppearanceSettingsController }> = (props) => {
|
||||
const language = useLanguage()
|
||||
return (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.appearance")}</h3>
|
||||
<SettingsListV2>
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.colorScheme.title")}
|
||||
description={language.t("settings.general.row.colorScheme.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-color-scheme"
|
||||
options={schemeOptions}
|
||||
current={schemeOptions.find((option) => option === props.controller.scheme.current())}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
label={(option) => {
|
||||
if (option === "system") return language.t("theme.scheme.system")
|
||||
if (option === "light") return language.t("theme.scheme.light")
|
||||
return language.t("theme.scheme.dark")
|
||||
}}
|
||||
onSelect={(option) => option && props.controller.scheme.select(option)}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.theme.title")}
|
||||
description={
|
||||
<>
|
||||
{language.t("settings.general.row.theme.description")}{" "}
|
||||
<ExternalLink class="settings-v2-link" href="https://opencode.ai/docs/themes/">
|
||||
{language.t("common.learnMore")}
|
||||
</ExternalLink>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-theme"
|
||||
options={props.controller.theme.options()}
|
||||
current={props.controller.theme.current()}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
value={(option) => option.id}
|
||||
label={(option) => option.name}
|
||||
onSelect={props.controller.theme.select}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<FontSetting kind="ui" fonts={props.controller.fonts} />
|
||||
<FontSetting kind="code" fonts={props.controller.fonts} />
|
||||
<FontSetting kind="terminal" fonts={props.controller.fonts} />
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type ShellSelectOption = {
|
||||
id: string
|
||||
value: string
|
||||
label: string
|
||||
const FontSetting: Component<{
|
||||
kind: "ui" | "code" | "terminal"
|
||||
fonts: AppearanceSettingsController["fonts"]
|
||||
}> = (props) => {
|
||||
const language = useLanguage()
|
||||
const config = () => fontSettings[props.kind]
|
||||
return (
|
||||
<SettingsRowV2 title={language.t(config().title)} description={language.t(config().description)}>
|
||||
<div class="w-full sm:w-[220px]">
|
||||
<TextInputV2
|
||||
data-action={config().action}
|
||||
type="text"
|
||||
appearance="base"
|
||||
value={props.fonts[config().font]().value}
|
||||
onInput={(event) => props.fonts[config().input](event.currentTarget.value)}
|
||||
placeholder={props.fonts[config().font]().placeholder}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t(config().title)}
|
||||
style={{ "font-family": props.fonts[config().font]().family }}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}
|
||||
|
||||
// To prevent audio from overlapping/playing very quickly when navigating the settings menus,
|
||||
// delay the playback by 100ms during quick selection changes and pause existing sounds.
|
||||
const stopDemoSound = () => {
|
||||
demoSoundState.run += 1
|
||||
if (demoSoundState.cleanup) {
|
||||
demoSoundState.cleanup()
|
||||
}
|
||||
clearTimeout(demoSoundState.timeout)
|
||||
demoSoundState.cleanup = undefined
|
||||
const SoundsSection: Component<{ controller: SoundSettingsController }> = (props) => {
|
||||
const language = useLanguage()
|
||||
return (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.sounds")}</h3>
|
||||
<SettingsListV2>
|
||||
<SoundSetting kind="agent" channel={props.controller.agent} />
|
||||
<SoundSetting kind="permissions" channel={props.controller.permissions} />
|
||||
<SoundSetting kind="errors" channel={props.controller.errors} />
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const playDemoSound = (id: string | undefined) => {
|
||||
stopDemoSound()
|
||||
if (!id) return
|
||||
const SoundSetting: Component<{
|
||||
kind: "agent" | "permissions" | "errors"
|
||||
channel: SoundSettingsController["agent"]
|
||||
}> = (props) => {
|
||||
const language = useLanguage()
|
||||
const config = () => soundSettings[props.kind]
|
||||
return (
|
||||
<SettingsRowV2 title={language.t(config().title)} description={language.t(config().description)}>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action={config().action}
|
||||
options={soundOptions}
|
||||
current={props.channel.current()}
|
||||
value={(option) => option.id}
|
||||
label={(option) => language.t(option.label)}
|
||||
onHighlight={props.channel.highlight}
|
||||
onSelect={props.channel.select}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}
|
||||
|
||||
const run = ++demoSoundState.run
|
||||
demoSoundState.timeout = setTimeout(() => {
|
||||
void playSoundById(id).then((cleanup) => {
|
||||
if (demoSoundState.run !== run) {
|
||||
cleanup?.()
|
||||
return
|
||||
}
|
||||
demoSoundState.cleanup = cleanup
|
||||
})
|
||||
}, 100)
|
||||
const LanguageSetting = () => {
|
||||
const language = useLanguage()
|
||||
const options = createMemo(() =>
|
||||
language.locales.map((locale) => ({
|
||||
value: locale,
|
||||
label: language.label(locale),
|
||||
})),
|
||||
)
|
||||
return (
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.language.title")}
|
||||
description={language.t("settings.general.row.language.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-language"
|
||||
options={options()}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
current={options().find((option) => option.value === language.locale())}
|
||||
value={(option) => option.value}
|
||||
label={(option) => option.label}
|
||||
onSelect={(option) => option && language.setLocale(option.value)}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}
|
||||
|
||||
export const SettingsGeneralV2: Component<{
|
||||
sessionID?: string
|
||||
}> = (props) => {
|
||||
const theme = useTheme()
|
||||
const language = useLanguage()
|
||||
const permission = usePermission()
|
||||
const platform = usePlatform()
|
||||
const settings = useSettings()
|
||||
const serverSync = useServerSync()
|
||||
const mobile = createMediaQuery("(max-width: 767px)")
|
||||
|
||||
const updater = useUpdaterAction()
|
||||
|
||||
const dir = createMemo(() => {
|
||||
if (!props.sessionID) return undefined
|
||||
return serverSync().session.lineage.peek(props.sessionID)?.session.location.directory
|
||||
})
|
||||
const accepting = createMemo(() => {
|
||||
const value = dir()
|
||||
if (!value || !props.sessionID) return false
|
||||
return permission.isAutoAccepting(props.sessionID, value)
|
||||
})
|
||||
|
||||
const toggleAccept = (checked: boolean) => {
|
||||
const value = dir()
|
||||
if (!value || !props.sessionID) return
|
||||
|
||||
if (checked) {
|
||||
permission.enableAutoAccept(props.sessionID, value)
|
||||
return
|
||||
}
|
||||
|
||||
permission.disableAutoAccept(props.sessionID, value)
|
||||
}
|
||||
const permissionScope = createPermissionScopeController(() => props.sessionID)
|
||||
const shell = createShellSettingsController()
|
||||
const appearance = createAppearanceSettingsController()
|
||||
const sounds = createSoundSettingsController()
|
||||
const desktop = createMemo(() => platform.platform === "desktop")
|
||||
|
||||
const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
|
||||
|
||||
const [shells] = createResource(
|
||||
async () => {
|
||||
// TODO: Restore executable shell discovery when the V2 client exposes it.
|
||||
// return (await sdk.api.pty.shells()).data
|
||||
return [] as ShellOption[]
|
||||
},
|
||||
{ initialValue: [] as ShellOption[] },
|
||||
)
|
||||
|
||||
const [pinchZoom, { mutate: setPinchZoom }] = createResource(
|
||||
() => (desktop() && platform.getPinchZoomEnabled ? true : false),
|
||||
() => desktop() && "getPinchZoomEnabled" in platform,
|
||||
() => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false),
|
||||
{ initialValue: false },
|
||||
)
|
||||
|
||||
onMount(() => {
|
||||
void theme.loadThemes()
|
||||
})
|
||||
|
||||
const autoOption = { id: "auto", value: "", label: language.t("settings.general.row.shell.autoDefault") }
|
||||
const currentShell = createMemo(() => serverSync().data.config.shell ?? "")
|
||||
|
||||
const shellOptions = createMemo<ShellSelectOption[]>(() => {
|
||||
const list = shells.latest
|
||||
const current = serverSync().data.config.shell
|
||||
|
||||
const nameCounts = new Map<string, number>()
|
||||
for (const s of list) {
|
||||
nameCounts.set(s.name, (nameCounts.get(s.name) || 0) + 1)
|
||||
}
|
||||
|
||||
const options = [
|
||||
autoOption,
|
||||
...list.map((s) => {
|
||||
const ambiguousName = (nameCounts.get(s.name) || 0) > 1
|
||||
const text = ambiguousName ? s.path : s.name
|
||||
const label = s.acceptable ? text : `${text} (${language.t("settings.general.row.shell.terminalOnly")})`
|
||||
return {
|
||||
id: s.path,
|
||||
// Prefer name over path - "bash" is much cleaner than the explicit full route even when it may change due to PATH.
|
||||
value: ambiguousName ? s.path : s.name,
|
||||
label,
|
||||
}
|
||||
}),
|
||||
]
|
||||
|
||||
if (current && !options.some((o) => o.value === current)) {
|
||||
options.push({ id: current, value: current, label: current })
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
|
||||
const onPinchZoomChange = (checked: boolean) => {
|
||||
setPinchZoom(checked)
|
||||
const update = platform.setPinchZoomEnabled?.(checked)
|
||||
@@ -176,101 +296,14 @@ export const SettingsGeneralV2: Component<{
|
||||
void update.catch(() => setPinchZoom(!checked))
|
||||
}
|
||||
|
||||
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
|
||||
{ value: "system", label: language.t("theme.scheme.system") },
|
||||
{ value: "light", label: language.t("theme.scheme.light") },
|
||||
{ value: "dark", label: language.t("theme.scheme.dark") },
|
||||
])
|
||||
|
||||
const languageOptions = createMemo(() =>
|
||||
language.locales.map((locale) => ({
|
||||
value: locale,
|
||||
label: language.label(locale),
|
||||
})),
|
||||
)
|
||||
|
||||
const noneSound = { id: "none", label: "sound.option.none" } as const
|
||||
const soundOptions = [noneSound, ...SOUND_OPTIONS]
|
||||
const mono = () => monoInput(settings.appearance.font())
|
||||
const sans = () => sansInput(settings.appearance.uiFont())
|
||||
const terminal = () => terminalInput(settings.appearance.terminalFont())
|
||||
|
||||
const soundSelectProps = (
|
||||
enabled: () => boolean,
|
||||
current: () => string,
|
||||
setEnabled: (value: boolean) => void,
|
||||
set: (id: string) => void,
|
||||
) => ({
|
||||
options: soundOptions,
|
||||
current: enabled() ? (soundOptions.find((o) => o.id === current()) ?? noneSound) : noneSound,
|
||||
value: (o: (typeof soundOptions)[number]) => o.id,
|
||||
label: (o: (typeof soundOptions)[number]) => language.t(o.label),
|
||||
onHighlight: (option: (typeof soundOptions)[number] | undefined) => {
|
||||
if (!option) return
|
||||
playDemoSound(option.id === "none" ? undefined : option.id)
|
||||
},
|
||||
onSelect: (option: (typeof soundOptions)[number] | null) => {
|
||||
if (!option) return
|
||||
if (option.id === "none") {
|
||||
setEnabled(false)
|
||||
stopDemoSound()
|
||||
return
|
||||
}
|
||||
setEnabled(true)
|
||||
set(option.id)
|
||||
playDemoSound(option.id)
|
||||
},
|
||||
})
|
||||
|
||||
const GeneralSection = () => (
|
||||
<div class="settings-v2-section">
|
||||
<SettingsListV2>
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.language.title")}
|
||||
description={language.t("settings.general.row.language.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-language"
|
||||
options={languageOptions()}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
current={languageOptions().find((o) => o.value === language.locale())}
|
||||
value={(o) => o.value}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => option && language.setLocale(option.value)}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
<LanguageSetting />
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("command.permissions.autoaccept.enable")}
|
||||
description={language.t("toast.permissions.autoaccept.on.description")}
|
||||
>
|
||||
<div data-action="settings-auto-accept-permissions">
|
||||
<Switch checked={accepting()} disabled={!dir()} onChange={toggleAccept} />
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
<PermissionScopeSetting controller={permissionScope} />
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.shell.title")}
|
||||
description={language.t("settings.general.row.shell.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-shell"
|
||||
options={shellOptions()}
|
||||
current={shellOptions().find((o) => o.value === currentShell()) ?? autoOption}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
value={(o) => o.id}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => {
|
||||
if (!option) return
|
||||
if (option.value === currentShell()) return
|
||||
serverSync().updateConfig({ shell: option.value })
|
||||
}}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
<ShellSetting controller={shell} />
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.reasoningSummaries.title")}
|
||||
@@ -381,124 +414,6 @@ export const SettingsGeneralV2: Component<{
|
||||
</div>
|
||||
)
|
||||
|
||||
const AppearanceSection = () => (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.appearance")}</h3>
|
||||
|
||||
<SettingsListV2>
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.colorScheme.title")}
|
||||
description={language.t("settings.general.row.colorScheme.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-color-scheme"
|
||||
options={colorSchemeOptions()}
|
||||
current={colorSchemeOptions().find((o) => o.value === theme.colorScheme())}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
value={(o) => o.value}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => option && theme.setColorScheme(option.value)}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.theme.title")}
|
||||
description={
|
||||
<>
|
||||
{language.t("settings.general.row.theme.description")}{" "}
|
||||
<Link class="settings-v2-link" href="https://opencode.ai/docs/themes/">
|
||||
{language.t("common.learnMore")}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-theme"
|
||||
options={themeOptions()}
|
||||
current={themeOptions().find((o) => o.id === theme.themeId())}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
value={(o) => o.id}
|
||||
label={(o) => o.name}
|
||||
onSelect={(option) => {
|
||||
if (!option) return
|
||||
theme.setTheme(option.id)
|
||||
}}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.uiFont.title")}
|
||||
description={language.t("settings.general.row.uiFont.description")}
|
||||
>
|
||||
<div class="w-full sm:w-[220px]">
|
||||
<TextInputV2
|
||||
data-action="settings-ui-font"
|
||||
type="text"
|
||||
appearance="base"
|
||||
value={sans()}
|
||||
onInput={(event) => settings.appearance.setUIFont(event.currentTarget.value)}
|
||||
placeholder={sansDefault}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t("settings.general.row.uiFont.title")}
|
||||
style={{ "font-family": sansFontFamily(settings.appearance.uiFont()) }}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.font.title")}
|
||||
description={language.t("settings.general.row.font.description")}
|
||||
>
|
||||
<div class="w-full sm:w-[220px]">
|
||||
<TextInputV2
|
||||
data-action="settings-code-font"
|
||||
type="text"
|
||||
appearance="base"
|
||||
value={mono()}
|
||||
onInput={(event) => settings.appearance.setFont(event.currentTarget.value)}
|
||||
placeholder={monoDefault}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t("settings.general.row.font.title")}
|
||||
style={{ "font-family": monoFontFamily(settings.appearance.font()) }}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.row.terminalFont.title")}
|
||||
description={language.t("settings.general.row.terminalFont.description")}
|
||||
>
|
||||
<div class="w-full sm:w-[220px]">
|
||||
<TextInputV2
|
||||
data-action="settings-terminal-font"
|
||||
type="text"
|
||||
appearance="base"
|
||||
value={terminal()}
|
||||
onInput={(event) => settings.appearance.setTerminalFont(event.currentTarget.value)}
|
||||
placeholder={terminalDefault}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t("settings.general.row.terminalFont.title")}
|
||||
style={{ "font-family": terminalFontFamily(settings.appearance.terminalFont()) }}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
)
|
||||
|
||||
const NotificationsSection = () => (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.notifications")}</h3>
|
||||
@@ -543,68 +458,6 @@ export const SettingsGeneralV2: Component<{
|
||||
</div>
|
||||
)
|
||||
|
||||
const SoundsSection = () => (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.sounds")}</h3>
|
||||
|
||||
<SettingsListV2>
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.sounds.agent.title")}
|
||||
description={language.t("settings.general.sounds.agent.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-sounds-agent"
|
||||
{...soundSelectProps(
|
||||
() => settings.sounds.agentEnabled(),
|
||||
() => settings.sounds.agent(),
|
||||
(value) => settings.sounds.setAgentEnabled(value),
|
||||
(id) => settings.sounds.setAgent(id),
|
||||
)}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.sounds.permissions.title")}
|
||||
description={language.t("settings.general.sounds.permissions.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-sounds-permissions"
|
||||
{...soundSelectProps(
|
||||
() => settings.sounds.permissionsEnabled(),
|
||||
() => settings.sounds.permissions(),
|
||||
(value) => settings.sounds.setPermissionsEnabled(value),
|
||||
(id) => settings.sounds.setPermissions(id),
|
||||
)}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
|
||||
<SettingsRowV2
|
||||
title={language.t("settings.general.sounds.errors.title")}
|
||||
description={language.t("settings.general.sounds.errors.description")}
|
||||
>
|
||||
<SelectV2
|
||||
appearance="inline"
|
||||
data-action="settings-sounds-errors"
|
||||
{...soundSelectProps(
|
||||
() => settings.sounds.errorsEnabled(),
|
||||
() => settings.sounds.errors(),
|
||||
(value) => settings.sounds.setErrorsEnabled(value),
|
||||
(id) => settings.sounds.setErrors(id),
|
||||
)}
|
||||
placement="bottom-end"
|
||||
gutter={6}
|
||||
/>
|
||||
</SettingsRowV2>
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
)
|
||||
|
||||
const UpdatesSection = () => (
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t("settings.general.section.updates")}</h3>
|
||||
@@ -626,7 +479,7 @@ export const SettingsGeneralV2: Component<{
|
||||
title={language.t("settings.updates.row.check.title")}
|
||||
description={language.t("settings.updates.row.check.description")}
|
||||
>
|
||||
<ButtonV2 size="normal" variant="neutral" disabled={!updater.action().run} onClick={updater.run}>
|
||||
<ButtonV2 size="normal" variant="neutral" disabled={!updater.action().run} onClick={() => updater.run()}>
|
||||
{language.t(updater.action().label)}
|
||||
</ButtonV2>
|
||||
</SettingsRowV2>
|
||||
@@ -663,11 +516,11 @@ export const SettingsGeneralV2: Component<{
|
||||
<div class="settings-v2-tab-body">
|
||||
<GeneralSection />
|
||||
|
||||
<AppearanceSection />
|
||||
<AppearanceSection controller={appearance} />
|
||||
|
||||
<NotificationsSection />
|
||||
|
||||
<SoundsSection />
|
||||
<SoundsSection controller={sounds} />
|
||||
|
||||
<Show when={desktop()}>
|
||||
<UpdatesSection />
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ServerRowMenu } from "@/components/server/server-row-menu"
|
||||
import { ServerHealthIndicator } from "@/components/server/server-row"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { ServerConnection, serverName } from "@/context/server"
|
||||
import { useServerManagementController } from "../dialog-select-server"
|
||||
import { useServerCollectionController } from "../server/server-management-controller"
|
||||
import { DialogServerV2 } from "./dialog-server-v2"
|
||||
import { SettingsListV2 } from "./parts/list"
|
||||
import { AddServerMenu, isWslServer, useFilteredWslServers, WslServerSettings } from "@/wsl/settings"
|
||||
@@ -19,16 +19,16 @@ import "./settings-v2.css"
|
||||
export const SettingsServersV2: Component = () => {
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const controller = useServerManagementController()
|
||||
const controller = useServerCollectionController()
|
||||
const [store, setStore] = createStore({ filter: "" })
|
||||
const wslServers = useFilteredWslServers(() => store.filter)
|
||||
|
||||
const showSearch = createMemo(
|
||||
() => controller.sortedItems().filter((item) => !isWslServer(item)).length + wslServers().length > 1,
|
||||
() => controller.collection.items().filter((item) => !isWslServer(item)).length + wslServers().length > 1,
|
||||
)
|
||||
|
||||
const filtered = createMemo(() => {
|
||||
const items = controller.sortedItems().filter((item) => !isWslServer(item))
|
||||
const items = controller.collection.items().filter((item) => !isWslServer(item))
|
||||
const query = store.filter.trim()
|
||||
if (!query) return items
|
||||
return fuzzysort
|
||||
@@ -39,11 +39,11 @@ export const SettingsServersV2: Component = () => {
|
||||
})
|
||||
|
||||
const openAdd = () => {
|
||||
dialog.push(() => <DialogServerV2 mode="add" />)
|
||||
void dialog.push(() => <DialogServerV2 mode="add" />)
|
||||
}
|
||||
|
||||
const openEdit = (server: ServerConnection.Http) => {
|
||||
dialog.push(() => <DialogServerV2 mode="edit" server={server} />)
|
||||
void dialog.push(() => <DialogServerV2 mode="edit" server={server} />)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -97,12 +97,12 @@ export const SettingsServersV2: Component = () => {
|
||||
}
|
||||
>
|
||||
<SettingsListV2>
|
||||
<WslServerSettings controller={controller} servers={wslServers} />
|
||||
<WslServerSettings domain={controller} servers={wslServers} />
|
||||
<For each={filtered()}>
|
||||
{(item) => {
|
||||
const key = ServerConnection.key(item)
|
||||
const health = () => controller.status()[key]
|
||||
const isDefault = () => controller.defaultKey() === key
|
||||
const health = () => controller.collection.health()[key]
|
||||
const isDefault = () => controller.defaults.key() === key
|
||||
return (
|
||||
<div class="settings-v2-servers-row">
|
||||
<div class="settings-v2-servers-lead">
|
||||
@@ -122,10 +122,10 @@ export const SettingsServersV2: Component = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-v2-servers-actions">
|
||||
<Show when={controller.canDefault() && isDefault()}>
|
||||
<Show when={controller.defaults.available() && isDefault()}>
|
||||
<Tag>{language.t("dialog.server.status.default")}</Tag>
|
||||
</Show>
|
||||
<ServerRowMenu server={item} controller={controller} onEdit={openEdit} />
|
||||
<ServerRowMenu server={item} domain={controller} onEdit={openEdit} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
}
|
||||
|
||||
.settings-v2-provider-env-hint {
|
||||
padding-right: 12px;
|
||||
padding-inline-end: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 440;
|
||||
line-height: 1;
|
||||
@@ -311,7 +311,7 @@
|
||||
line-height: 1;
|
||||
color: var(--v2-text-text-accent);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.settings-v2-providers-view-all:hover {
|
||||
@@ -361,13 +361,13 @@
|
||||
}
|
||||
|
||||
.settings-v2-tab-search [data-slot="text-input-v2-input"] {
|
||||
padding-right: 28px;
|
||||
padding-inline-end: 28px;
|
||||
}
|
||||
|
||||
.settings-v2-tab-search-clear {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 6px;
|
||||
inset-inline-end: 6px;
|
||||
z-index: 1;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Switch } from "@opencode-ai/ui/switch"
|
||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import {
|
||||
type Accessor,
|
||||
createEffect,
|
||||
@@ -16,14 +12,12 @@ import {
|
||||
Show,
|
||||
} from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { ServerConnection, useServer } from "@/context/server"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { type ServerHealth } from "@/utils/server-health"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useMcpToggle } from "@/context/mcp"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
|
||||
@@ -106,16 +100,6 @@ const useDefaultServerKey = (
|
||||
}
|
||||
}
|
||||
|
||||
type ServerStatusState = {
|
||||
servers: () => ServerStatusItem[]
|
||||
defaultKey: () => ServerConnection.Key | undefined
|
||||
ariaLabel: string
|
||||
serversLabel: string
|
||||
defaultLabel: string
|
||||
manageLabel: string
|
||||
onManage: () => void
|
||||
}
|
||||
|
||||
type ServerStatusItem = {
|
||||
key: ServerConnection.Key
|
||||
conn: ServerConnection.Any
|
||||
@@ -125,149 +109,13 @@ type ServerStatusItem = {
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
export function StatusPopoverServerBody() {
|
||||
const global = useGlobal()
|
||||
const server = useServer()
|
||||
const platform = usePlatform()
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const navigate = useNavigate()
|
||||
let dialogRun = 0
|
||||
let dialogDead = false
|
||||
onCleanup(() => {
|
||||
dialogDead = true
|
||||
dialogRun += 1
|
||||
})
|
||||
|
||||
const sortedServers = createMemo(() => listServersByHealth(global.servers.list(), server.key, global.servers.health))
|
||||
const defaultServer = useDefaultServerKey(platform.getDefaultServer)
|
||||
const serverItems = createMemo(() =>
|
||||
sortedServers().map((conn) => {
|
||||
const key = ServerConnection.key(conn)
|
||||
return {
|
||||
key,
|
||||
conn,
|
||||
health: global.servers.health[key],
|
||||
blocked: global.servers.health[key]?.healthy === false,
|
||||
active: !!server.current && key === ServerConnection.key(server.current),
|
||||
onSelect: () => {
|
||||
navigate("/")
|
||||
queueMicrotask(() => server.setActive(key))
|
||||
},
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
return (
|
||||
<ServerStatusPopoverView
|
||||
state={{
|
||||
servers: serverItems,
|
||||
defaultKey: defaultServer.key,
|
||||
ariaLabel: language.t("status.popover.ariaLabel"),
|
||||
serversLabel: language.t("status.popover.tab.servers"),
|
||||
defaultLabel: language.t("common.default"),
|
||||
manageLabel: language.t("status.popover.action.manageServers"),
|
||||
onManage: () => {
|
||||
const run = ++dialogRun
|
||||
void import("./dialog-select-server").then((x) => {
|
||||
if (dialogDead || dialogRun !== run) return
|
||||
dialog.show(() => <x.DialogSelectServer />, defaultServer.refresh)
|
||||
})
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ServerStatusPopoverView(props: { state: ServerStatusState }) {
|
||||
return (
|
||||
<div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
|
||||
<Tabs
|
||||
aria-label={props.state.ariaLabel}
|
||||
class="tabs bg-background-strong rounded-xl overflow-hidden"
|
||||
data-component="tabs"
|
||||
data-active="servers"
|
||||
defaultValue="servers"
|
||||
variant="alt"
|
||||
>
|
||||
<Tabs.List data-slot="tablist" class="bg-transparent border-b-0 px-4 pt-2 pb-0 gap-4 h-10">
|
||||
<Tabs.Trigger value="servers" data-slot="tab" class="text-12-regular">
|
||||
{props.state.servers().length > 0 ? `${props.state.servers().length} ` : ""}
|
||||
{props.state.serversLabel}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="servers">
|
||||
<ServerStatusList state={props.state} />
|
||||
</Tabs.Content>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ServerStatusList(props: { state: ServerStatusState }) {
|
||||
return (
|
||||
<div class="flex flex-col px-2 pb-2">
|
||||
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
|
||||
<For each={props.state.servers()}>
|
||||
{(item) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-2 w-full h-8 pl-3 pr-1.5 py-1.5 rounded-md transition-colors text-left"
|
||||
classList={{
|
||||
"hover:bg-surface-raised-base-hover": !item.blocked,
|
||||
"cursor-not-allowed": item.blocked,
|
||||
}}
|
||||
aria-disabled={item.blocked}
|
||||
onClick={() => {
|
||||
if (item.blocked) return
|
||||
item.onSelect()
|
||||
}}
|
||||
>
|
||||
<ServerHealthIndicator health={item.health} />
|
||||
<ServerRow
|
||||
conn={item.conn}
|
||||
dimmed={item.blocked}
|
||||
status={item.health}
|
||||
class="flex items-center gap-2 w-full min-w-0"
|
||||
nameClass="text-14-regular text-text-base truncate"
|
||||
versionClass="text-12-regular text-text-weak truncate"
|
||||
badge={
|
||||
<Show when={item.key === props.state.defaultKey()}>
|
||||
<span class="text-11-regular text-text-base bg-surface-base px-1.5 py-0.5 rounded-md">
|
||||
{props.state.defaultLabel}
|
||||
</span>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<div class="flex-1" />
|
||||
<Show when={item.active}>
|
||||
<Icon name="check" size="small" class="text-icon-weak shrink-0" />
|
||||
</Show>
|
||||
</ServerRow>
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
|
||||
<Button variant="secondary" class="mt-3 self-start h-8 px-3 py-1.5" onClick={props.state.onManage}>
|
||||
{props.state.manageLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
||||
const sync = useSync()
|
||||
const sdk = useSDK()
|
||||
const global = useGlobal()
|
||||
const server = useServer()
|
||||
const platform = usePlatform()
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const navigate = useNavigate()
|
||||
const settings = useSettings()
|
||||
|
||||
const fail = (err: unknown) => {
|
||||
showToast({
|
||||
@@ -315,17 +163,11 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
||||
aria-label={language.t("status.popover.ariaLabel")}
|
||||
class="tabs bg-background-strong rounded-xl overflow-hidden"
|
||||
data-component="tabs"
|
||||
data-active={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
defaultValue={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
data-active="mcp"
|
||||
defaultValue="mcp"
|
||||
variant="alt"
|
||||
>
|
||||
<Tabs.List data-slot="tablist" class="bg-transparent border-b-0 px-4 pt-2 pb-0 gap-4 h-10">
|
||||
{!settings.general.newLayoutDesigns() && (
|
||||
<Tabs.Trigger value="servers" data-slot="tab" class="text-12-regular">
|
||||
{sortedServers().length > 0 ? `${sortedServers().length} ` : ""}
|
||||
{language.t("status.popover.tab.servers")}
|
||||
</Tabs.Trigger>
|
||||
)}
|
||||
<Tabs.Trigger value="mcp" data-slot="tab" class="text-12-regular">
|
||||
{mcpConnected() > 0 ? `${mcpConnected()} ` : ""}
|
||||
{language.t("status.popover.tab.mcp")}
|
||||
@@ -342,73 +184,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
||||
</Show>
|
||||
</Tabs.List>
|
||||
|
||||
{!settings.general.newLayoutDesigns() && (
|
||||
<Tabs.Content value="servers">
|
||||
<div class="flex flex-col px-2 pb-2">
|
||||
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
|
||||
<For each={sortedServers()}>
|
||||
{(s) => {
|
||||
const key = ServerConnection.key(s)
|
||||
const blocked = () => global.servers.health[key]?.healthy === false
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-2 w-full h-8 pl-3 pr-1.5 py-1.5 rounded-md transition-colors text-left"
|
||||
classList={{
|
||||
"hover:bg-surface-raised-base-hover": !blocked(),
|
||||
"cursor-not-allowed": blocked(),
|
||||
}}
|
||||
aria-disabled={blocked()}
|
||||
onClick={() => {
|
||||
if (blocked()) return
|
||||
navigate("/")
|
||||
queueMicrotask(() => server.setActive(key))
|
||||
}}
|
||||
>
|
||||
<ServerHealthIndicator health={global.servers.health[key]} />
|
||||
<ServerRow
|
||||
conn={s}
|
||||
dimmed={blocked()}
|
||||
status={global.servers.health[key]}
|
||||
class="flex items-center gap-2 w-full min-w-0"
|
||||
nameClass="text-14-regular text-text-base truncate"
|
||||
versionClass="text-12-regular text-text-weak truncate"
|
||||
badge={
|
||||
<Show when={key === defaultServer.key()}>
|
||||
<span class="text-11-regular text-text-base bg-surface-base px-1.5 py-0.5 rounded-md">
|
||||
{language.t("common.default")}
|
||||
</span>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<div class="flex-1" />
|
||||
<Show when={server.current && key === ServerConnection.key(server.current)}>
|
||||
<Icon name="check" size="small" class="text-icon-weak shrink-0" />
|
||||
</Show>
|
||||
</ServerRow>
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="mt-3 self-start h-8 px-3 py-1.5"
|
||||
onClick={() => {
|
||||
const run = ++dialogRun
|
||||
void import("./dialog-select-server").then((x) => {
|
||||
if (dialogDead || dialogRun !== run) return
|
||||
dialog.show(() => <x.DialogSelectServer />, defaultServer.refresh)
|
||||
})
|
||||
}}
|
||||
>
|
||||
{language.t("status.popover.action.manageServers")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
)}
|
||||
|
||||
<Tabs.Content value="mcp">
|
||||
<div class="flex flex-col px-2 pb-2">
|
||||
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
} from "./status-popover-indicator"
|
||||
|
||||
const Body = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverBody })))
|
||||
const ServerBody = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverServerBody })))
|
||||
|
||||
export function StatusPopover() {
|
||||
const language = useLanguage()
|
||||
@@ -82,8 +81,7 @@ export function StatusPopover() {
|
||||
)
|
||||
}
|
||||
|
||||
export function StatusPopoverV2(props: { scope?: "server" }) {
|
||||
if (props.scope === "server") return <ServerStatusPopover />
|
||||
export function StatusPopoverV2() {
|
||||
return <DirectoryStatusPopover />
|
||||
}
|
||||
|
||||
@@ -124,30 +122,6 @@ function DirectoryStatusPopover() {
|
||||
return <StatusPopoverView state={state()} />
|
||||
}
|
||||
|
||||
function ServerStatusPopover() {
|
||||
const language = useLanguage()
|
||||
const server = useServer()
|
||||
const global = useGlobal()
|
||||
const [shown, setShown] = createSignal(false)
|
||||
const serverHealth = () => global.servers.health[server.key]?.healthy
|
||||
const state = createMemo<StatusPopoverState>(() => ({
|
||||
shown: shown(),
|
||||
ready: serverHealth() !== undefined,
|
||||
serverHealth: serverHealth(),
|
||||
attention: false,
|
||||
issue: false,
|
||||
label: language.t("status.popover.trigger"),
|
||||
onOpenChange: setShown,
|
||||
body: () => (
|
||||
<StatusPopoverBody shown={shown()}>
|
||||
<ServerBody />
|
||||
</StatusPopoverBody>
|
||||
),
|
||||
}))
|
||||
|
||||
return <StatusPopoverView state={state()} />
|
||||
}
|
||||
|
||||
type StatusPopoverState = {
|
||||
shown: boolean
|
||||
ready: boolean
|
||||
|
||||
@@ -14,6 +14,7 @@ import { useSDK } from "@/context/sdk"
|
||||
import { terminalFontFamily, useSettings } from "@/context/settings"
|
||||
import type { LocalPTY } from "@/context/terminal"
|
||||
import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters"
|
||||
import { terminalConnectToken } from "@/utils/terminal-connect-token"
|
||||
import { terminalWriter } from "@/utils/terminal-writer"
|
||||
import { terminalWebSocketURL } from "@/utils/terminal-websocket-url"
|
||||
|
||||
@@ -364,7 +365,11 @@ export const Terminal = (props: TerminalProps) => {
|
||||
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
platform.openLink(text)
|
||||
if (URL.canParse(text) && new URL(text).protocol === "file:" && platform.openLocalFile) {
|
||||
platform.openLocalFile(text)
|
||||
return
|
||||
}
|
||||
platform.openExternal(text)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -526,8 +531,11 @@ export const Terminal = (props: TerminalProps) => {
|
||||
}
|
||||
|
||||
const connectToken = async () => {
|
||||
// TODO: Add PTY tickets when the V2 client exposes a connect-token API.
|
||||
return undefined
|
||||
const result = await terminalConnectToken({ url, id, directory })
|
||||
if (result.ticket) return result.ticket
|
||||
if (result.status === 404 || result.status === 405) return
|
||||
if (result.status === 403) throw new Error(language.t("terminal.connectTicket.csrfError"))
|
||||
throw new Error(language.t("terminal.connectTicket.statusError", { status: result.status }))
|
||||
}
|
||||
|
||||
const retry = (err: unknown) => {
|
||||
@@ -679,6 +687,7 @@ export const Terminal = (props: TerminalProps) => {
|
||||
<div
|
||||
ref={container}
|
||||
data-component="terminal"
|
||||
dir="ltr"
|
||||
data-prevent-autofocus
|
||||
tabIndex={-1}
|
||||
style={{ "background-color": terminalColors().background }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[data-titlebar-tab] [data-slot="tab-close"] {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
inset-inline-end: 4px;
|
||||
display: flex;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
@@ -9,58 +9,108 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[data-titlebar-tab] {
|
||||
--tab-base: var(--v2-background-bg-deep);
|
||||
--tab-overlay: transparent;
|
||||
background: linear-gradient(var(--tab-overlay), var(--tab-overlay)), var(--tab-base);
|
||||
}
|
||||
|
||||
[data-titlebar-tab]:is(:hover, :has(> [data-slot="tab-link"]:focus-visible)):not([data-state="pressed"]):not(
|
||||
[data-dragging="true"]
|
||||
):not([data-editing="true"]) {
|
||||
--tab-base: var(--v2-background-bg-layer-02);
|
||||
--tab-overlay: var(--v2-overlay-simple-overlay-hover);
|
||||
}
|
||||
|
||||
[data-titlebar-tab]:is([data-state="pressed"], [data-dragging="true"], [data-editing="true"]) {
|
||||
--tab-base: var(--v2-background-bg-layer-02);
|
||||
--tab-overlay: var(--v2-overlay-simple-overlay-pressed);
|
||||
}
|
||||
|
||||
[data-titlebar-tab]:is(:hover, [data-state="pressed"]) [data-slot="tab-close"] {
|
||||
background: linear-gradient(var(--tab-overlay), var(--tab-overlay)), var(--tab-base);
|
||||
}
|
||||
|
||||
[data-titlebar-tab][data-editing="true"] [data-slot="tab-close"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-titlebar-tab-list] {
|
||||
gap: 13.5px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
[data-titlebar-tab-slot] {
|
||||
--tab-separator: var(--v2-background-bg-layer-03);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-color-scheme="dark"] [data-titlebar-tab-slot] {
|
||||
--tab-separator: var(--v2-background-bg-layer-02);
|
||||
}
|
||||
|
||||
[data-titlebar-tab-slot]:not(:first-child):not([data-active="true"])::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: -6.75px;
|
||||
inset-inline-start: -3.75px;
|
||||
width: 1.5px;
|
||||
height: 12px;
|
||||
border-radius: 9999px;
|
||||
background: var(--v2-background-bg-layer-02);
|
||||
background: var(--tab-separator);
|
||||
}
|
||||
|
||||
[data-titlebar-tab-slot][data-active="true"] + [data-titlebar-tab-slot]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-titlebar-tab] [data-slot="tab-close"]::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 28px;
|
||||
width: 16px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(to right, transparent, var(--tab-bg));
|
||||
[data-titlebar-tab-slot]:not([data-active="true"]):has([data-titlebar-tab]:hover)::before,
|
||||
[data-titlebar-tab-slot]:not([data-active="true"]):has([data-titlebar-tab]:hover) + [data-titlebar-tab-slot]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-titlebar-tab][data-title-overflow="true"]:not([data-editing="true"]) [data-slot="tab-close"]::before {
|
||||
display: block;
|
||||
right: 0;
|
||||
[data-titlebar-tab][data-title-overflow="true"]:not([data-editing="true"]) [data-slot="tab-link"] {
|
||||
--tab-title-fade-offset: 4px;
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to right,
|
||||
black 0,
|
||||
black calc(100% - var(--tab-title-fade-offset) - 16px),
|
||||
transparent calc(100% - var(--tab-title-fade-offset)),
|
||||
transparent 100%
|
||||
);
|
||||
mask-image: linear-gradient(
|
||||
to right,
|
||||
black 0,
|
||||
black calc(100% - var(--tab-title-fade-offset) - 16px),
|
||||
transparent calc(100% - var(--tab-title-fade-offset)),
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-titlebar-tab][data-title-overflow="true"]:hover:not([data-editing="true"]) [data-slot="tab-close"]::before,
|
||||
[data-titlebar-tab][data-title-overflow="true"][data-active="true"]:not([data-editing="true"])
|
||||
[data-slot="tab-close"]::before {
|
||||
right: 100%;
|
||||
[data-titlebar-tab][data-title-overflow="true"]:not([data-editing="true"]):dir(rtl) [data-slot="tab-link"] {
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to left,
|
||||
black 0,
|
||||
black calc(100% - var(--tab-title-fade-offset) - 16px),
|
||||
transparent calc(100% - var(--tab-title-fade-offset)),
|
||||
transparent 100%
|
||||
);
|
||||
mask-image: linear-gradient(
|
||||
to left,
|
||||
black 0,
|
||||
black calc(100% - var(--tab-title-fade-offset) - 16px),
|
||||
transparent calc(100% - var(--tab-title-fade-offset)),
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-titlebar-tab][data-title-overflow="true"]:is(:hover, [data-active="true"]):not([data-editing="true"])
|
||||
[data-slot="tab-link"] {
|
||||
--tab-title-fade-offset: 24px;
|
||||
}
|
||||
|
||||
[data-titlebar-tab][data-title-overflow="true"]:not(:hover):not([data-active="true"]):not([data-editing="true"])
|
||||
[data-slot="tab-link"] {
|
||||
padding-right: 0;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
[data-titlebar-tab] [data-slot="tab-title"] {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createMutation } from "@tanstack/solid-query"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { ServerConnection, serverName } from "@/context/server"
|
||||
import { displayName, projectForSession } from "@/pages/layout/helpers"
|
||||
import { SessionTabAvatar } from "@/pages/layout/session-tab-avatar"
|
||||
@@ -184,11 +185,11 @@ export function TabNavItem(props: {
|
||||
data-slot="titlebar-tab-item"
|
||||
data-title-overflow={titleOverflowing()}
|
||||
data-editing={editing()}
|
||||
class="group relative flex h-7 w-full min-w-0 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
||||
class="group relative flex h-7 w-full min-w-0 select-none flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] px-1.5 [container-type:inline-size]"
|
||||
classList={{ invisible: props.hidden }}
|
||||
data-active={props.active}
|
||||
data-dragging={props.dragging}
|
||||
data-pressed={props.pressed}
|
||||
data-state={props.active || props.pressed ? "pressed" : undefined}
|
||||
onMouseDown={(event) => {
|
||||
if (event.button !== MIDDLE_MOUSE_BUTTON) return
|
||||
event.preventDefault()
|
||||
@@ -228,6 +229,7 @@ export function TabNavItem(props: {
|
||||
<span data-slot="project-avatar-slot" class="flex size-4 shrink-0 items-center justify-center">
|
||||
<Show
|
||||
when={props.session()}
|
||||
keyed
|
||||
fallback={
|
||||
<span class="block size-4 rounded-[3px] border border-v2-border-border-muted" aria-hidden="true" />
|
||||
}
|
||||
@@ -235,8 +237,8 @@ export function TabNavItem(props: {
|
||||
{(session) => (
|
||||
<SessionTabAvatar
|
||||
project={project()}
|
||||
directory={session().location.directory}
|
||||
sessionId={session().id}
|
||||
directory={session.location.directory}
|
||||
sessionId={session.id}
|
||||
server={props.server}
|
||||
/>
|
||||
)}
|
||||
@@ -280,7 +282,7 @@ export function TabNavItem(props: {
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div data-slot="tab-close" class="group-hover:bg-[var(--tab-bg)] group-data-[active=true]:bg-[var(--tab-bg)]">
|
||||
<div data-slot="tab-close">
|
||||
<IconButtonV2
|
||||
size="small"
|
||||
variant="ghost-muted"
|
||||
@@ -326,6 +328,7 @@ export function DraftTabItem(props: {
|
||||
pressed?: boolean
|
||||
hidden?: boolean
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const closeTab = (event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@@ -338,8 +341,8 @@ export function DraftTabItem(props: {
|
||||
data-slot="titlebar-tab-item"
|
||||
data-active={props.active}
|
||||
data-dragging={props.dragging}
|
||||
data-pressed={props.pressed}
|
||||
class="group relative flex h-7 w-full min-w-0 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--tab-bg)] px-1.5 [container-type:inline-size] whitespace-nowrap [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] has-[>a:focus-visible]:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[dragging='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[pressed='true']:[--tab-bg:var(--v2-background-bg-layer-02)] data-[editing='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
||||
data-state={props.active || props.pressed ? "pressed" : undefined}
|
||||
class="group relative flex h-7 w-full min-w-0 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] px-1.5 [container-type:inline-size] whitespace-nowrap"
|
||||
classList={{ invisible: props.hidden }}
|
||||
onMouseDown={(event) => {
|
||||
if (event.button !== MIDDLE_MOUSE_BUTTON) return
|
||||
@@ -385,7 +388,7 @@ export function DraftTabItem(props: {
|
||||
{props.title}
|
||||
</span>
|
||||
</a>
|
||||
<div data-slot="tab-close" class="group-hover:bg-[var(--tab-bg)] group-data-[active=true]:bg-[var(--tab-bg)]">
|
||||
<div data-slot="tab-close">
|
||||
<IconButtonV2
|
||||
size="small"
|
||||
variant="ghost-muted"
|
||||
@@ -400,7 +403,7 @@ export function DraftTabItem(props: {
|
||||
class="hover-reveal relative z-10 group-hover:opacity-100 group-data-[active=true]:opacity-100 group-data-[editing=true]:opacity-100"
|
||||
onClick={closeTab}
|
||||
icon={<IconV2 name="xmark-small" />}
|
||||
aria-label="Close tab"
|
||||
aria-label={language.t("common.closeTab")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import { createEffect, createMemo, createResource, createSignal, Match, onMount, Show, Switch, untrack } from "solid-js"
|
||||
import {
|
||||
createEffect,
|
||||
createMemo,
|
||||
createResource,
|
||||
createSignal,
|
||||
Match,
|
||||
on,
|
||||
onMount,
|
||||
Show,
|
||||
Switch,
|
||||
untrack,
|
||||
} from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useLocation, useNavigate, useParams } from "@solidjs/router"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
@@ -41,8 +52,11 @@ export type TitlebarUpdate = {
|
||||
}
|
||||
|
||||
export function useTitlebarRightMount() {
|
||||
const language = useLanguage()
|
||||
const [mount, setMount] = createSignal<HTMLElement | null>(null)
|
||||
onMount(() => setMount(document.getElementById("opencode-titlebar-right")))
|
||||
const sync = () => setMount(document.getElementById("opencode-titlebar-right"))
|
||||
onMount(sync)
|
||||
createEffect(on(language.direction, sync, { defer: true }))
|
||||
return mount
|
||||
}
|
||||
|
||||
@@ -112,9 +126,9 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
return {
|
||||
visible: version !== undefined || installing,
|
||||
installing,
|
||||
label: "Update",
|
||||
label: language.t("titlebar.update"),
|
||||
ariaLabel: language.t("toast.update.action.installRestart"),
|
||||
title: version ? `Update ${version}` : undefined,
|
||||
title: version ? language.t("titlebar.updateVersion", { version }) : undefined,
|
||||
onInstall: () => props.update?.install(),
|
||||
}
|
||||
})
|
||||
@@ -168,7 +182,8 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
"padding-left": macTrafficLights() ? `${macTrafficLightsBaseWidth / zoom()}px` : 0,
|
||||
width: windows() ? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))` : undefined,
|
||||
"max-width": windows() ? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))` : undefined,
|
||||
"align-self": windows() ? "flex-start" : undefined,
|
||||
// Native Windows caption controls remain on the physical right in both writing directions.
|
||||
"margin-right": windows() ? "auto" : undefined,
|
||||
}}
|
||||
data-tauri-drag-region
|
||||
>
|
||||
@@ -510,7 +525,7 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
<div
|
||||
class="flex items-center shrink-0"
|
||||
classList={{
|
||||
"-translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
|
||||
"ltr:-translate-x-[36px] rtl:translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
|
||||
"duration-180 ease-out": !layout.sidebar.opened(),
|
||||
"duration-180 ease-in": layout.sidebar.opened(),
|
||||
}}
|
||||
@@ -608,7 +623,7 @@ function TitlebarUpdateIconButton(props: { state: TitlebarUpdatePillState }) {
|
||||
aria-label={props.state.ariaLabel}
|
||||
>
|
||||
<span class="shrink-0 ml-[8px] mr-px text-[11px] text-v2-text-text-accent [font-weight:530] opacity-0 translate-x-2 motion-safe:transition-all duration-150 ease-out group-hover:opacity-100 group-hover:translate-x-0 group-focus-within:opacity-100 group-focus-within:translate-x-0 motion-reduce:translate-x-0">
|
||||
Update
|
||||
{props.state.label}
|
||||
</span>
|
||||
<span class="flex size-5 shrink-0 items-center justify-center">
|
||||
<Show
|
||||
|
||||
@@ -55,7 +55,7 @@ const DrawerContent = <T extends ValidComponent = "div">(props: DynamicProps<T,
|
||||
<DrawerPrimitive.Content
|
||||
class={props.class}
|
||||
classList={{
|
||||
"group/drawer-content fixed inset-y-[6px] right-[6px] left-auto z-[100] flex h-auto max-h-[calc(100vh-12px)] w-[560px] max-w-[calc(100vw-12px)] flex-col items-start rounded-[8px] bg-v2-background-bg-base p-0 shadow-[var(--v2-elevation-overlay)] data-[transitioning]:transition-transform data-[transitioning]:duration-300 md:select-none": true,
|
||||
"group/drawer-content fixed inset-y-[6px] end-[6px] start-auto z-[100] flex h-auto max-h-[calc(100vh-12px)] w-[560px] max-w-[calc(100vw-12px)] flex-col items-start rounded-[8px] bg-v2-background-bg-base p-0 shadow-[var(--v2-elevation-overlay)] data-[transitioning]:transition-transform data-[transitioning]:duration-300 md:select-none": true,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { useCommand } from "@/context/command"
|
||||
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
export function WindowsAppMenu(props: {
|
||||
command: ReturnType<typeof useCommand>
|
||||
@@ -15,6 +16,7 @@ export function WindowsAppMenu(props: {
|
||||
variant?: "legacy" | "v2"
|
||||
}) {
|
||||
let lastFocused: HTMLElement | undefined
|
||||
const language = useLanguage()
|
||||
|
||||
const rememberFocus = () => {
|
||||
const active = document.activeElement
|
||||
@@ -43,7 +45,7 @@ export function WindowsAppMenu(props: {
|
||||
runAction(entry.action)
|
||||
return
|
||||
}
|
||||
if (entry.href) props.platform.openLink(entry.href)
|
||||
if (entry.href) props.platform.openExternal(entry.href)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -58,7 +60,7 @@ export function WindowsAppMenu(props: {
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
icon={<IconV2 name="menu" />}
|
||||
aria-label="OpenCode menu"
|
||||
aria-label={language.t("desktop.menu.ariaLabel")}
|
||||
onPointerDown={rememberFocus}
|
||||
onKeyDown={rememberFocus}
|
||||
/>
|
||||
@@ -69,7 +71,7 @@ export function WindowsAppMenu(props: {
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md shrink-0"
|
||||
aria-label="OpenCode menu"
|
||||
aria-label={language.t("desktop.menu.ariaLabel")}
|
||||
onPointerDown={rememberFocus}
|
||||
onKeyDown={rememberFocus}
|
||||
/>
|
||||
@@ -79,7 +81,7 @@ export function WindowsAppMenu(props: {
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.GroupLabel class="desktop-app-menu-heading">OpenCode</DropdownMenu.GroupLabel>
|
||||
{DESKTOP_MENU.filter((menu) => desktopMenuVisible(menu, "windows")).map((menu) => (
|
||||
<DesktopMenuSubmenu label={menu.label}>
|
||||
<DesktopMenuSubmenu label={language.t(menu.labelKey)}>
|
||||
{menu.items
|
||||
?.filter((entry) => desktopMenuVisible(entry, "windows"))
|
||||
.map((entry) =>
|
||||
@@ -87,7 +89,7 @@ export function WindowsAppMenu(props: {
|
||||
<DropdownMenu.Separator />
|
||||
) : (
|
||||
<DesktopMenuItem
|
||||
label={entry.label ?? ""}
|
||||
label={entry.labelKey ? language.t(entry.labelKey) : ""}
|
||||
keybind={entry.command ? props.command.keybind(entry.command) : entry.accelerator?.windows}
|
||||
disabled={entry.command ? commandDisabled(entry.command) : false}
|
||||
onSelect={() => runEntry(entry)}
|
||||
|
||||
@@ -83,7 +83,7 @@ export const ACCEPTED_FILE_EXTENSIONS = Array.from(
|
||||
),
|
||||
).sort()
|
||||
|
||||
export function filePickerFilters(ext?: string[]) {
|
||||
export function filePickerFilters(name: string, ext?: string[]) {
|
||||
if (!ext || ext.length === 0) return undefined
|
||||
return [{ name: "Files", extensions: ext }]
|
||||
return [{ name, extensions: ext }]
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ const rootSession = (input: { id: string; parentID?: string; archived?: number }
|
||||
},
|
||||
}) as SessionInfo
|
||||
|
||||
const userMessage = (id: string, sessionID: string) =>
|
||||
const userMessage = (id: string, sessionID: string, created = 1) =>
|
||||
({
|
||||
id,
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: 1 },
|
||||
time: { created },
|
||||
agent: "assistant",
|
||||
model: { providerID: "openai", modelID: "gpt" },
|
||||
}) as Message
|
||||
@@ -371,13 +371,13 @@ describe("applyDirectoryEvent", () => {
|
||||
const sessionID = "ses_1"
|
||||
const [store, setStore] = createStore(
|
||||
baseState({
|
||||
message: { [sessionID]: [userMessage("msg_1", sessionID), userMessage("msg_3", sessionID)] },
|
||||
part: { msg_2: [textPart("prt_1", sessionID, "msg_2")] },
|
||||
message: { [sessionID]: [userMessage("msg_z", sessionID, 1), userMessage("msg_b", sessionID, 3)] },
|
||||
part: { msg_a: [textPart("prt_1", sessionID, "msg_a")] },
|
||||
}),
|
||||
)
|
||||
|
||||
applyDirectoryEvent({
|
||||
event: { type: "message.updated", properties: { info: userMessage("msg_2", sessionID) } },
|
||||
event: { type: "message.updated", properties: { info: userMessage("msg_a", sessionID, 2) } },
|
||||
store,
|
||||
setStore,
|
||||
push() {},
|
||||
@@ -385,14 +385,14 @@ describe("applyDirectoryEvent", () => {
|
||||
loadLsp() {},
|
||||
})
|
||||
|
||||
expect(store.message[sessionID]?.map((x) => x.id)).toEqual(["msg_1", "msg_2", "msg_3"])
|
||||
expect(store.message[sessionID]?.map((x) => x.id)).toEqual(["msg_z", "msg_a", "msg_b"])
|
||||
|
||||
applyDirectoryEvent({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
...userMessage("msg_2", sessionID),
|
||||
...userMessage("msg_a", sessionID, 2),
|
||||
role: "assistant",
|
||||
} as Message,
|
||||
},
|
||||
@@ -404,10 +404,10 @@ describe("applyDirectoryEvent", () => {
|
||||
loadLsp() {},
|
||||
})
|
||||
|
||||
expect(store.message[sessionID]?.find((x) => x.id === "msg_2")?.role).toBe("assistant")
|
||||
expect(store.message[sessionID]?.find((x) => x.id === "msg_a")?.role).toBe("assistant")
|
||||
|
||||
applyDirectoryEvent({
|
||||
event: { type: "message.removed", properties: { sessionID, messageID: "msg_2" } },
|
||||
event: { type: "message.removed", properties: { sessionID, messageID: "msg_a" } },
|
||||
store,
|
||||
setStore,
|
||||
push() {},
|
||||
@@ -415,8 +415,8 @@ describe("applyDirectoryEvent", () => {
|
||||
loadLsp() {},
|
||||
})
|
||||
|
||||
expect(store.message[sessionID]?.map((x) => x.id)).toEqual(["msg_1", "msg_3"])
|
||||
expect(store.part.msg_2).toBeUndefined()
|
||||
expect(store.message[sessionID]?.map((x) => x.id)).toEqual(["msg_z", "msg_b"])
|
||||
expect(store.part.msg_a).toBeUndefined()
|
||||
})
|
||||
|
||||
test("upserts and prunes message parts", () => {
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { State, VcsCache } from "./types"
|
||||
import { trimSessions } from "./session-trim"
|
||||
import { dropSessionCaches } from "./session-cache"
|
||||
import { diffs as list, message as clean } from "@/utils/diffs"
|
||||
import { messageKey } from "@/utils/session-message"
|
||||
|
||||
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
const SESSION_CONTENT_EVENTS = new Set([
|
||||
@@ -271,7 +272,7 @@ export function applyDirectoryEvent(input: {
|
||||
input.setStore("message", info.sessionID, [info])
|
||||
break
|
||||
}
|
||||
const result = Binary.search(messages, info.id, (m) => m.id)
|
||||
const result = Binary.search(messages, messageKey(info), messageKey)
|
||||
if (result.found) {
|
||||
input.setStore("message", info.sessionID, result.index, reconcile(info))
|
||||
break
|
||||
@@ -291,8 +292,8 @@ export function applyDirectoryEvent(input: {
|
||||
produce((draft) => {
|
||||
const messages = draft.message[props.sessionID]
|
||||
if (messages) {
|
||||
const result = Binary.search(messages, props.messageID, (m) => m.id)
|
||||
if (result.found) messages.splice(result.index, 1)
|
||||
const index = messages.findIndex((message) => message.id === props.messageID)
|
||||
if (index >= 0) messages.splice(index, 1)
|
||||
}
|
||||
const parts = draft.part[props.messageID]
|
||||
if (parts) {
|
||||
@@ -318,7 +319,7 @@ export function applyDirectoryEvent(input: {
|
||||
input.setStore("part", part.messageID, [part])
|
||||
break
|
||||
}
|
||||
const result = Binary.search(parts, part.id, (p) => p.id)
|
||||
const result = Binary.search(parts, part.id, (item) => item.id)
|
||||
if (result.found) {
|
||||
input.setStore("part", part.messageID, result.index, reconcile(part))
|
||||
break
|
||||
@@ -341,13 +342,13 @@ export function applyDirectoryEvent(input: {
|
||||
)
|
||||
const parts = input.store.part[props.messageID]
|
||||
if (!parts) break
|
||||
const result = Binary.search(parts, props.partID, (p) => p.id)
|
||||
const result = Binary.search(parts, props.partID, (part) => part.id)
|
||||
if (result.found) {
|
||||
input.setStore(
|
||||
produce((draft) => {
|
||||
const list = draft.part[props.messageID]
|
||||
if (!list) return
|
||||
const next = Binary.search(list, props.partID, (p) => p.id)
|
||||
const next = Binary.search(list, props.partID, (part) => part.id)
|
||||
if (!next.found) return
|
||||
list.splice(next.index, 1)
|
||||
if (list.length === 0) delete draft.part[props.messageID]
|
||||
@@ -360,7 +361,7 @@ export function applyDirectoryEvent(input: {
|
||||
const props = event.properties as { messageID: string; partID: string; field: string; delta: string }
|
||||
const parts = input.store.part[props.messageID]
|
||||
if (!parts) break
|
||||
const result = Binary.search(parts, props.partID, (p) => p.id)
|
||||
const result = Binary.search(parts, props.partID, (part) => part.id)
|
||||
if (!result.found) break
|
||||
const field = props.field as keyof (typeof parts)[number]
|
||||
const current = parts[result.index]?.[field]
|
||||
|
||||
@@ -2,100 +2,46 @@ import * as i18n from "@solid-primitives/i18n"
|
||||
import { createEffect, createMemo, createResource } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { pluralCategory, type UiI18nPluralKey } from "@opencode-ai/ui/context/i18n"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { dict as en } from "@/i18n/en"
|
||||
import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
|
||||
import {
|
||||
createDesktopNativeBundle,
|
||||
detectDesktopNativeLocale,
|
||||
DESKTOP_NATIVE_ENGLISH,
|
||||
DESKTOP_NATIVE_LABELS,
|
||||
DESKTOP_NATIVE_LOCALES,
|
||||
DESKTOP_NATIVE_LOCALE_TAGS,
|
||||
type DesktopNativeBundle,
|
||||
type DesktopNativeLocale,
|
||||
} from "@/i18n/desktop-native"
|
||||
|
||||
export type Locale =
|
||||
| "en"
|
||||
| "zh"
|
||||
| "zht"
|
||||
| "ko"
|
||||
| "de"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "da"
|
||||
| "ja"
|
||||
| "pl"
|
||||
| "ru"
|
||||
| "uk"
|
||||
| "ar"
|
||||
| "no"
|
||||
| "br"
|
||||
| "th"
|
||||
| "bs"
|
||||
| "tr"
|
||||
export type Locale = DesktopNativeLocale
|
||||
export type Direction = "ltr" | "rtl"
|
||||
|
||||
const RTL_LOCALES: ReadonlySet<Locale> = new Set(["ar", "ur", "pa", "fa", "dv"])
|
||||
|
||||
function localeDirection(locale: Locale): Direction {
|
||||
return RTL_LOCALES.has(locale) ? "rtl" : "ltr"
|
||||
}
|
||||
|
||||
type RawDictionary = typeof en & typeof uiEn
|
||||
type Dictionary = i18n.Flatten<RawDictionary>
|
||||
type PluralKey =
|
||||
| UiI18nPluralKey
|
||||
| "session.question.pending"
|
||||
| "session.followupDock.summary"
|
||||
| "session.revertDock.summary"
|
||||
type Source = { dict: Record<string, string> }
|
||||
|
||||
function cookie(locale: Locale) {
|
||||
return `oc_locale=${encodeURIComponent(locale)}; Path=/; Max-Age=31536000; SameSite=Lax`
|
||||
}
|
||||
|
||||
const LOCALES: readonly Locale[] = [
|
||||
"en",
|
||||
"zh",
|
||||
"zht",
|
||||
"ko",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"da",
|
||||
"ja",
|
||||
"pl",
|
||||
"ru",
|
||||
"uk",
|
||||
"bs",
|
||||
"ar",
|
||||
"no",
|
||||
"br",
|
||||
"th",
|
||||
"tr",
|
||||
]
|
||||
const LOCALES: readonly Locale[] = DESKTOP_NATIVE_LOCALES
|
||||
|
||||
const INTL: Record<Locale, string> = {
|
||||
en: "en",
|
||||
zh: "zh-Hans",
|
||||
zht: "zh-Hant",
|
||||
ko: "ko",
|
||||
de: "de",
|
||||
es: "es",
|
||||
fr: "fr",
|
||||
da: "da",
|
||||
ja: "ja",
|
||||
pl: "pl",
|
||||
ru: "ru",
|
||||
uk: "uk",
|
||||
ar: "ar",
|
||||
no: "nb-NO",
|
||||
br: "pt-BR",
|
||||
th: "th",
|
||||
bs: "bs",
|
||||
tr: "tr",
|
||||
}
|
||||
|
||||
const LABEL_KEY: Record<Locale, keyof Dictionary> = {
|
||||
en: "language.en",
|
||||
zh: "language.zh",
|
||||
zht: "language.zht",
|
||||
ko: "language.ko",
|
||||
de: "language.de",
|
||||
es: "language.es",
|
||||
fr: "language.fr",
|
||||
da: "language.da",
|
||||
ja: "language.ja",
|
||||
pl: "language.pl",
|
||||
ru: "language.ru",
|
||||
uk: "language.uk",
|
||||
ar: "language.ar",
|
||||
no: "language.no",
|
||||
br: "language.br",
|
||||
th: "language.th",
|
||||
bs: "language.bs",
|
||||
tr: "language.tr",
|
||||
}
|
||||
const INTL = DESKTOP_NATIVE_LOCALE_TAGS
|
||||
|
||||
const base = i18n.flatten({ ...en, ...uiEn })
|
||||
const dicts = new Map<Locale, Dictionary>([["en", base]])
|
||||
@@ -121,6 +67,50 @@ const loaders: Record<Exclude<Locale, "en">, () => Promise<Dictionary>> = {
|
||||
th: () => merge(import("@/i18n/th"), import("@opencode-ai/ui/i18n/th")),
|
||||
bs: () => merge(import("@/i18n/bs"), import("@opencode-ai/ui/i18n/bs")),
|
||||
tr: () => merge(import("@/i18n/tr"), import("@opencode-ai/ui/i18n/tr")),
|
||||
hi: () => merge(import("@/i18n/hi"), import("@opencode-ai/ui/i18n/hi")),
|
||||
nl: () => merge(import("@/i18n/nl"), import("@opencode-ai/ui/i18n/nl")),
|
||||
id: () => merge(import("@/i18n/id"), import("@opencode-ai/ui/i18n/id")),
|
||||
vi: () => merge(import("@/i18n/vi"), import("@opencode-ai/ui/i18n/vi")),
|
||||
it: () => merge(import("@/i18n/it"), import("@opencode-ai/ui/i18n/it")),
|
||||
ur: () => merge(import("@/i18n/ur"), import("@opencode-ai/ui/i18n/ur")),
|
||||
pa: () => merge(import("@/i18n/pa"), import("@opencode-ai/ui/i18n/pa")),
|
||||
az: () => merge(import("@/i18n/az"), import("@opencode-ai/ui/i18n/az")),
|
||||
fi: () => merge(import("@/i18n/fi"), import("@opencode-ai/ui/i18n/fi")),
|
||||
sv: () => merge(import("@/i18n/sv"), import("@opencode-ai/ui/i18n/sv")),
|
||||
am: () => merge(import("@/i18n/am"), import("@opencode-ai/ui/i18n/am")),
|
||||
bg: () => merge(import("@/i18n/bg"), import("@opencode-ai/ui/i18n/bg")),
|
||||
bn: () => merge(import("@/i18n/bn"), import("@opencode-ai/ui/i18n/bn")),
|
||||
ca: () => merge(import("@/i18n/ca"), import("@opencode-ai/ui/i18n/ca")),
|
||||
cs: () => merge(import("@/i18n/cs"), import("@opencode-ai/ui/i18n/cs")),
|
||||
dv: () => merge(import("@/i18n/dv"), import("@opencode-ai/ui/i18n/dv")),
|
||||
dz: () => merge(import("@/i18n/dz"), import("@opencode-ai/ui/i18n/dz")),
|
||||
el: () => merge(import("@/i18n/el"), import("@opencode-ai/ui/i18n/el")),
|
||||
et: () => merge(import("@/i18n/et"), import("@opencode-ai/ui/i18n/et")),
|
||||
fa: () => merge(import("@/i18n/fa"), import("@opencode-ai/ui/i18n/fa")),
|
||||
fo: () => merge(import("@/i18n/fo"), import("@opencode-ai/ui/i18n/fo")),
|
||||
hr: () => merge(import("@/i18n/hr"), import("@opencode-ai/ui/i18n/hr")),
|
||||
hu: () => merge(import("@/i18n/hu"), import("@opencode-ai/ui/i18n/hu")),
|
||||
hy: () => merge(import("@/i18n/hy"), import("@opencode-ai/ui/i18n/hy")),
|
||||
is: () => merge(import("@/i18n/is"), import("@opencode-ai/ui/i18n/is")),
|
||||
ka: () => merge(import("@/i18n/ka"), import("@opencode-ai/ui/i18n/ka")),
|
||||
km: () => merge(import("@/i18n/km"), import("@opencode-ai/ui/i18n/km")),
|
||||
lo: () => merge(import("@/i18n/lo"), import("@opencode-ai/ui/i18n/lo")),
|
||||
lt: () => merge(import("@/i18n/lt"), import("@opencode-ai/ui/i18n/lt")),
|
||||
lv: () => merge(import("@/i18n/lv"), import("@opencode-ai/ui/i18n/lv")),
|
||||
mk: () => merge(import("@/i18n/mk"), import("@opencode-ai/ui/i18n/mk")),
|
||||
mn: () => merge(import("@/i18n/mn"), import("@opencode-ai/ui/i18n/mn")),
|
||||
ms: () => merge(import("@/i18n/ms"), import("@opencode-ai/ui/i18n/ms")),
|
||||
my: () => merge(import("@/i18n/my"), import("@opencode-ai/ui/i18n/my")),
|
||||
ne: () => merge(import("@/i18n/ne"), import("@opencode-ai/ui/i18n/ne")),
|
||||
ro: () => merge(import("@/i18n/ro"), import("@opencode-ai/ui/i18n/ro")),
|
||||
si: () => merge(import("@/i18n/si"), import("@opencode-ai/ui/i18n/si")),
|
||||
sk: () => merge(import("@/i18n/sk"), import("@opencode-ai/ui/i18n/sk")),
|
||||
sl: () => merge(import("@/i18n/sl"), import("@opencode-ai/ui/i18n/sl")),
|
||||
sq: () => merge(import("@/i18n/sq"), import("@opencode-ai/ui/i18n/sq")),
|
||||
sr: () => merge(import("@/i18n/sr"), import("@opencode-ai/ui/i18n/sr")),
|
||||
tg: () => merge(import("@/i18n/tg"), import("@opencode-ai/ui/i18n/tg")),
|
||||
tk: () => merge(import("@/i18n/tk"), import("@opencode-ai/ui/i18n/tk")),
|
||||
uz: () => merge(import("@/i18n/uz"), import("@opencode-ai/ui/i18n/uz")),
|
||||
}
|
||||
|
||||
function loadDict(locale: Locale) {
|
||||
@@ -138,42 +128,9 @@ export function loadLocaleDict(locale: Locale) {
|
||||
return loadDict(locale).then(() => undefined)
|
||||
}
|
||||
|
||||
const localeMatchers: Array<{ locale: Locale; match: (language: string) => boolean }> = [
|
||||
{ locale: "en", match: (language) => language.startsWith("en") },
|
||||
{ locale: "zht", match: (language) => language.startsWith("zh") && language.includes("hant") },
|
||||
{ locale: "zh", match: (language) => language.startsWith("zh") },
|
||||
{ locale: "ko", match: (language) => language.startsWith("ko") },
|
||||
{ locale: "de", match: (language) => language.startsWith("de") },
|
||||
{ locale: "es", match: (language) => language.startsWith("es") },
|
||||
{ locale: "fr", match: (language) => language.startsWith("fr") },
|
||||
{ locale: "da", match: (language) => language.startsWith("da") },
|
||||
{ locale: "ja", match: (language) => language.startsWith("ja") },
|
||||
{ locale: "pl", match: (language) => language.startsWith("pl") },
|
||||
{ locale: "ru", match: (language) => language.startsWith("ru") },
|
||||
{ locale: "uk", match: (language) => language.startsWith("uk") },
|
||||
{ locale: "ar", match: (language) => language.startsWith("ar") },
|
||||
{
|
||||
locale: "no",
|
||||
match: (language) => language.startsWith("no") || language.startsWith("nb") || language.startsWith("nn"),
|
||||
},
|
||||
{ locale: "br", match: (language) => language.startsWith("pt") },
|
||||
{ locale: "th", match: (language) => language.startsWith("th") },
|
||||
{ locale: "bs", match: (language) => language.startsWith("bs") },
|
||||
{ locale: "tr", match: (language) => language.startsWith("tr") },
|
||||
]
|
||||
|
||||
function detectLocale(): Locale {
|
||||
if (typeof navigator !== "object") return "en"
|
||||
|
||||
const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
|
||||
for (const language of languages) {
|
||||
if (!language) continue
|
||||
const normalized = language.toLowerCase()
|
||||
const match = localeMatchers.find((entry) => entry.match(normalized))
|
||||
if (match) return match.locale
|
||||
}
|
||||
|
||||
return "en"
|
||||
return detectDesktopNativeLocale(navigator.languages?.length ? navigator.languages : [navigator.language])
|
||||
}
|
||||
|
||||
export function normalizeLocale(value: string): Locale {
|
||||
@@ -194,12 +151,22 @@ function readStoredLocale() {
|
||||
}
|
||||
|
||||
const warm = readStoredLocale() ?? detectLocale()
|
||||
if (warm !== "en") void loadDict(warm)
|
||||
const initialLocale =
|
||||
warm === "en"
|
||||
? Promise.resolve(warm)
|
||||
: loadDict(warm).then(
|
||||
() => warm,
|
||||
() => "en" as const,
|
||||
)
|
||||
|
||||
export function loadInitialLocale() {
|
||||
return initialLocale
|
||||
}
|
||||
|
||||
export const { use: useLanguage, provider: LanguageProvider } = createSimpleContext({
|
||||
name: "Language",
|
||||
gate: false,
|
||||
init: (props: { locale?: Locale }) => {
|
||||
init: (props: { locale?: Locale; onNativeTranslations?: (bundle: DesktopNativeBundle) => void }) => {
|
||||
const initial = props.locale ?? readStoredLocale() ?? detectLocale()
|
||||
const [store, setStore, _, ready] = persisted(
|
||||
Persist.global("language", ["language.v1"]),
|
||||
@@ -210,6 +177,13 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
||||
|
||||
const locale = createMemo<Locale>(() => normalizeLocale(store.locale))
|
||||
const intl = createMemo(() => INTL[locale()])
|
||||
const [layout, setLayout] = createStore({ direction: undefined as Direction | undefined })
|
||||
const direction = createMemo(() => layout.direction ?? localeDirection(locale()))
|
||||
const layoutLocale = createMemo(() => {
|
||||
if (!layout.direction) return intl()
|
||||
// Kobalte derives menu direction from locale rather than accepting a direction override.
|
||||
return layout.direction === "rtl" ? "ar" : "en"
|
||||
})
|
||||
|
||||
const [dict] = createResource(locale, loadDict, {
|
||||
initialValue: dicts.get(initial) ?? base,
|
||||
@@ -220,24 +194,49 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
||||
params?: Record<string, string | number | boolean>,
|
||||
) => string
|
||||
|
||||
const label = (value: Locale) => t(LABEL_KEY[value])
|
||||
const plural = (key: PluralKey, count: number, params?: Record<string, string | number | boolean>) => {
|
||||
const category = pluralCategory(intl(), count)
|
||||
const current = (dict.loading ? base : (dict() ?? base)) as Record<string, string>
|
||||
const candidate = `${key}.${category}`
|
||||
const fallback = `${key}.other`
|
||||
return i18n.resolveTemplate(current[candidate] ?? current[fallback] ?? fallback, { ...params, count })
|
||||
}
|
||||
|
||||
const label = (value: Locale) => DESKTOP_NATIVE_LABELS[value]
|
||||
|
||||
createEffect(() => {
|
||||
if (typeof document !== "object") return
|
||||
document.documentElement.lang = locale()
|
||||
document.cookie = cookie(locale())
|
||||
const value = locale()
|
||||
document.documentElement.lang = intl()
|
||||
document.documentElement.dir = direction()
|
||||
document.cookie = cookie(value)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!props.onNativeTranslations || dict.loading) return
|
||||
const current = dict()
|
||||
if (!current) return
|
||||
props.onNativeTranslations(
|
||||
createDesktopNativeBundle(locale(), (key) => current[key] ?? DESKTOP_NATIVE_ENGLISH[key]),
|
||||
)
|
||||
})
|
||||
|
||||
return {
|
||||
ready,
|
||||
locale,
|
||||
intl,
|
||||
direction,
|
||||
layoutLocale,
|
||||
locales: LOCALES,
|
||||
label,
|
||||
t,
|
||||
plural,
|
||||
setLocale(next: Locale) {
|
||||
setStore("locale", normalizeLocale(next))
|
||||
},
|
||||
setDirection(next: Direction) {
|
||||
setLayout("direction", next === localeDirection(locale()) ? undefined : next)
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
import { type Accessor, batch, createEffect, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
|
||||
import { useParams, useSearchParams } from "@solidjs/router"
|
||||
import { useNavigate, useParams, useSearchParams } from "@solidjs/router"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import type { ServerSDK } from "./server-sdk"
|
||||
import type { ServerSync } from "./server-sync"
|
||||
@@ -118,6 +118,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
||||
const global = useGlobal()
|
||||
const server = useServer()
|
||||
const tabs = useTabs()
|
||||
const navigate = useNavigate()
|
||||
const platform = usePlatform()
|
||||
const settings = useSettings()
|
||||
const language = useLanguage()
|
||||
@@ -153,6 +154,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
|
||||
platform,
|
||||
settings,
|
||||
language,
|
||||
navigate,
|
||||
}),
|
||||
}),
|
||||
owner ?? undefined,
|
||||
@@ -217,6 +219,7 @@ function createServerNotificationState(input: {
|
||||
platform: ReturnType<typeof usePlatform>
|
||||
settings: ReturnType<typeof useSettings>
|
||||
language: ReturnType<typeof useLanguage>
|
||||
navigate: (href: string) => void
|
||||
}) {
|
||||
const serverSDK = () => input.sdk
|
||||
const serverSync = () => input.sync
|
||||
@@ -353,7 +356,9 @@ function createServerNotificationState(input: {
|
||||
|
||||
const href = `/${base64Encode(directory)}/session/${sessionID}`
|
||||
if (settings.notifications.agent()) {
|
||||
void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, href)
|
||||
void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, () =>
|
||||
input.navigate(href),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -386,7 +391,7 @@ function createServerNotificationState(input: {
|
||||
(typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
|
||||
const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
|
||||
if (settings.notifications.errors()) {
|
||||
void platform.notify(language.t("notification.session.error.title"), description, href)
|
||||
void platform.notify(language.t("notification.session.error.title"), description, () => input.navigate(href))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { DesktopMenuAction } from "../desktop-menu"
|
||||
import { ServerConnection } from "./server"
|
||||
import type { WslServersPlatform } from "../wsl/types"
|
||||
import type { UpdaterPlatform } from "../updater"
|
||||
import type { DraftStore } from "@/utils/draft-store"
|
||||
|
||||
type PickerPaths = string | string[] | null
|
||||
type OpenDirectoryPickerOptions = { title?: string; multiple?: boolean }
|
||||
@@ -31,26 +32,23 @@ type PlatformBase = {
|
||||
/** App version */
|
||||
version?: string
|
||||
|
||||
/** Open a URL in the default browser */
|
||||
openLink(url: string): void
|
||||
/** Open a web or mail URL in the default system application */
|
||||
openExternal(url: string): void
|
||||
|
||||
/** Open a local path in a local app (desktop only) */
|
||||
openPath?(path: string, app?: string): Promise<void>
|
||||
|
||||
/** Open a local file URL in its default app (desktop only) */
|
||||
openLocalFile?(url: string): void
|
||||
|
||||
/** Reveal a local path in the system file manager; false when the path does not exist (desktop only) */
|
||||
revealPath?(path: string): Promise<boolean>
|
||||
|
||||
/** Restart the app */
|
||||
restart(): Promise<void>
|
||||
|
||||
/** Navigate back in history */
|
||||
back(): void
|
||||
|
||||
/** Navigate forward in history */
|
||||
forward(): void
|
||||
|
||||
/** Send a system notification (optional deep link) */
|
||||
notify(title: string, description?: string, href?: string): Promise<void>
|
||||
/** Send a system notification */
|
||||
notify(title: string, description?: string, onClick?: () => void): Promise<void>
|
||||
|
||||
/** Open a native attachment picker and read selected files sequentially (desktop only) */
|
||||
openAttachmentPickerDialog?(
|
||||
@@ -67,6 +65,9 @@ type PlatformBase = {
|
||||
/** Storage mechanism, defaults to localStorage */
|
||||
storage?: (name?: string) => SyncStorage | AsyncStorage
|
||||
|
||||
/** Prompt drafts, history, and their blobs. */
|
||||
draftStore?: DraftStore
|
||||
|
||||
/** Stable platform window identity for window-scoped persistence */
|
||||
windowID?: string
|
||||
|
||||
@@ -91,9 +92,6 @@ type PlatformBase = {
|
||||
/** Set the preferred display backend (desktop only) */
|
||||
setDisplayBackend?(backend: DisplayBackend): Promise<void>
|
||||
|
||||
/** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
|
||||
parseMarkdown?(markdown: string): Promise<string>
|
||||
|
||||
/** Webview zoom level (desktop only) */
|
||||
webviewZoom?: Accessor<number>
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import { createStore, type SetStoreFunction } from "solid-js/store"
|
||||
import type { FileSelection } from "@/context/file"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import type { ServerScope } from "@/utils/server-scope"
|
||||
import type { BlobReference } from "@/utils/draft-store"
|
||||
import type { Platform } from "@/context/platform"
|
||||
|
||||
interface PartBase {
|
||||
content: string
|
||||
@@ -37,7 +39,7 @@ export interface ImageAttachmentPart {
|
||||
filename: string
|
||||
sourcePath?: string
|
||||
mime: string
|
||||
dataUrl: string
|
||||
blob: BlobReference
|
||||
}
|
||||
|
||||
export type ContentPart = TextPart | FileAttachmentPart | AgentPart | ImageAttachmentPart
|
||||
@@ -168,9 +170,9 @@ function createPromptActions(setStore: SetStoreFunction<PromptStore>) {
|
||||
}
|
||||
|
||||
function promptTarget(serverScope: ServerScope, scope: PromptScope) {
|
||||
if ("draftID" in scope) return Persist.draft(scope.draftID, "prompt")
|
||||
if ("draftID" in scope) return Persist.prompt(Persist.draft(scope.draftID, "prompt"))
|
||||
const legacy = `${scope.dir}/prompt${scope.id ? "/" + scope.id : ""}.v2`
|
||||
return Persist.serverScoped(serverScope, scope.dir, scope.id, "prompt", [legacy])
|
||||
return Persist.prompt(Persist.serverScoped(serverScope, scope.dir, scope.id, "prompt", [legacy]))
|
||||
}
|
||||
|
||||
function promptStore(initial?: InitialPrompt): PromptStore {
|
||||
@@ -235,17 +237,22 @@ function createPromptStateValue(store: PromptStore, setStore: SetStoreFunction<P
|
||||
return value
|
||||
}
|
||||
|
||||
function createPersistedPrompt(target: ReturnType<typeof promptTarget>, initial?: InitialPrompt) {
|
||||
const [store, setStore, _, ready] = persisted(target, createStore<PromptStore>(promptStore(initial)))
|
||||
function createPersistedPrompt(target: ReturnType<typeof promptTarget>, initial?: InitialPrompt, platform?: Platform) {
|
||||
const [store, setStore, _, ready] = persisted(target, createStore<PromptStore>(promptStore(initial)), platform)
|
||||
return { ready, ...createPromptStateValue(store, setStore) }
|
||||
}
|
||||
|
||||
export function createPromptSession(serverScope: ServerScope, scope: PromptScope, initial?: InitialPrompt) {
|
||||
return createPersistedPrompt(promptTarget(serverScope, scope), initial)
|
||||
export function createPromptSession(
|
||||
serverScope: ServerScope,
|
||||
scope: PromptScope,
|
||||
initial?: InitialPrompt,
|
||||
platform?: Platform,
|
||||
) {
|
||||
return createPersistedPrompt(promptTarget(serverScope, scope), initial, platform)
|
||||
}
|
||||
|
||||
export function createDraftPromptSession(draftID: string, initial?: InitialPrompt) {
|
||||
return createPersistedPrompt(Persist.draft(draftID, "prompt"), initial)
|
||||
return createPersistedPrompt(Persist.prompt(Persist.draft(draftID, "prompt")), initial)
|
||||
}
|
||||
|
||||
export type PromptSession = ReturnType<typeof createPromptSession>
|
||||
|
||||
@@ -392,6 +392,7 @@ describe("server session", () => {
|
||||
|
||||
expect(requests).toEqual([{ sessionID: "root", limit: 20, order: "desc" }])
|
||||
expect(store.data.session_message.root.map((message) => message.id)).toEqual([user.id, assistant.id])
|
||||
expect(store.data.message.root.map((message) => message.id)).toEqual([user.id, assistant.id])
|
||||
})
|
||||
|
||||
test("extends a current page to include the user for split assistant turns", async () => {
|
||||
@@ -1573,7 +1574,7 @@ describe("server session", () => {
|
||||
|
||||
await store.sync("child", { force: true })
|
||||
|
||||
expect(store.data.message.child).toEqual([boundary, older])
|
||||
expect(store.data.message.child).toEqual([older, boundary])
|
||||
})
|
||||
|
||||
test("preserves a part update for a message being loaded from history", async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { FileDiffInfo, PermissionRequest, QuestionRequest, SessionStatus }
|
||||
import { batch } from "solid-js"
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { rootSession } from "@/utils/session-route"
|
||||
import { normalizeSessionMessages } from "@/utils/session-message"
|
||||
import { compareMessages, messageKey, normalizeSessionMessages } from "@/utils/session-message"
|
||||
import { dropSessionCaches, pickSessionCacheEvictions, SESSION_CACHE_LIMIT } from "./global-sync/session-cache"
|
||||
import { createV2SessionReducer, type V2SessionReduction } from "./server-session-v2-reducer"
|
||||
import type { ServerApi } from "@/utils/server"
|
||||
@@ -14,7 +14,6 @@ import type { ServerApi } from "@/utils/server"
|
||||
type MessageApi = ServerApi["message"]
|
||||
|
||||
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||
const cmpMessage = (a: Message, b: Message) => a.time.created - b.time.created || cmp(a.id, b.id)
|
||||
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
const initialMessagePageSize = 20
|
||||
const historyMessagePageSize = 200
|
||||
@@ -100,17 +99,16 @@ function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[]) {
|
||||
const part = new Map(page.part.map((item) => [item.id, item.part]))
|
||||
const observed: { messageID: string; parts: Part[] }[] = []
|
||||
for (const item of items) {
|
||||
const result = Binary.search(session, item.message.id, (message) => message.id)
|
||||
if (!result.found) session.splice(result.index, 0, item.message)
|
||||
const result = Binary.search(session, messageKey(item.message), messageKey)
|
||||
const found = result.found
|
||||
if (!found) session.splice(result.index, 0, item.message)
|
||||
const current = part.get(item.message.id)
|
||||
const confirmed = result.found
|
||||
? item.parts.filter((part) => Binary.search(current ?? [], part.id, (value) => value.id).found)
|
||||
: []
|
||||
if (result.found) observed.push({ messageID: item.message.id, parts: confirmed })
|
||||
const confirmed = found ? item.parts.filter((part) => current?.some((value) => value.id === part.id)) : []
|
||||
if (found) observed.push({ messageID: item.message.id, parts: confirmed })
|
||||
part.set(
|
||||
item.message.id,
|
||||
merge(
|
||||
result.found ? (current ?? []) : merge(item.confirmedParts ?? [], current ?? []),
|
||||
found ? (current ?? []) : merge(item.confirmedParts ?? [], current ?? []),
|
||||
item.parts.filter((part) => !confirmed.includes(part)),
|
||||
),
|
||||
)
|
||||
@@ -147,6 +145,7 @@ function reconcileFetched<T extends { id: string }>(
|
||||
retained?: ReadonlySet<string>
|
||||
removed?: ReadonlySet<string>
|
||||
preserveUnfetched?: boolean | ((item: T) => boolean)
|
||||
compare?: (a: T, b: T) => number
|
||||
} = {},
|
||||
) {
|
||||
const result = new Map(fetched.map((item) => [item.id, item]))
|
||||
@@ -169,7 +168,8 @@ function reconcileFetched<T extends { id: string }>(
|
||||
if (!item) result.delete(id)
|
||||
}
|
||||
for (const id of options.removed ?? emptyIDs) result.delete(id)
|
||||
return [...result.values()].sort((a, b) => cmp(a.id, b.id))
|
||||
const items = [...result.values()]
|
||||
return options.compare ? items.sort(options.compare) : items
|
||||
}
|
||||
|
||||
type ServerSessionOptions = { retry?: typeof retry }
|
||||
@@ -395,8 +395,7 @@ export function createServerSession(
|
||||
if (!load) return
|
||||
// A part event keeps an existing parent when the fetched page omits it without overriding fetched metadata.
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
if (messages?.some((message) => message.id === messageID)) load.retainedMessages.add(messageID)
|
||||
const parts = load.touchedParts.get(messageID)
|
||||
if (parts) {
|
||||
parts.add(partID)
|
||||
@@ -419,16 +418,14 @@ export function createServerSession(
|
||||
load.touchedParts.set(messageID, new Set(parts))
|
||||
load.carriedDeltaParts.set(messageID, new Set(parts))
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
if (messages?.some((message) => message.id === messageID)) load.retainedMessages.add(messageID)
|
||||
}
|
||||
for (const [messageID, parts] of load.removedParts) {
|
||||
const touched = load.touchedParts.get(messageID) ?? new Set<string>()
|
||||
parts.forEach((partID) => touched.add(partID))
|
||||
load.touchedParts.set(messageID, touched)
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
if (messages?.some((message) => message.id === messageID)) load.retainedMessages.add(messageID)
|
||||
}
|
||||
for (const [messageID, parts] of load.optimisticParts) {
|
||||
load.removedMessages.delete(messageID)
|
||||
@@ -536,7 +533,7 @@ export function createServerSession(
|
||||
const source = pages.flatMap((page) => page.data).toReversed()
|
||||
const normalized = normalizeSessionMessages(sessionID, source)
|
||||
return {
|
||||
session: normalized.messages.sort((a, b) => cmp(a.id, b.id)),
|
||||
session: normalized.messages.sort(compareMessages),
|
||||
part: [...normalized.parts.entries()]
|
||||
.map(([id, part]) => ({ id, part: part.sort((a, b) => cmp(a.id, b.id)) }))
|
||||
.sort((a, b) => cmp(a.id, b.id)),
|
||||
@@ -656,7 +653,7 @@ export function createServerSession(
|
||||
const normalized = normalizeSessionMessages(sessionID, source)
|
||||
return {
|
||||
...page,
|
||||
session: normalized.messages.sort((a, b) => cmp(a.id, b.id)),
|
||||
session: normalized.messages.sort(compareMessages),
|
||||
part: [...normalized.parts.entries()]
|
||||
.map(([id, part]) => ({ id, part: part.sort((a, b) => cmp(a.id, b.id)) }))
|
||||
.sort((a, b) => cmp(a.id, b.id)),
|
||||
@@ -673,6 +670,7 @@ export function createServerSession(
|
||||
retained: load?.retainedMessages,
|
||||
removed: load?.removedMessages,
|
||||
preserveUnfetched,
|
||||
compare: compareMessages,
|
||||
})
|
||||
batch(() => {
|
||||
if (source) setData("session_message", sessionID, reconcile(source))
|
||||
@@ -714,7 +712,7 @@ export function createServerSession(
|
||||
try {
|
||||
const page = await fetchMessages(sessionID, limit, before, () => resetMessageLoad(sessionID, load))
|
||||
const first = page.session.reduce<Message | undefined>(
|
||||
(oldest, message) => (!oldest || cmpMessage(message, oldest) < 0 ? message : oldest),
|
||||
(oldest, message) => (!oldest || compareMessages(message, oldest) < 0 ? message : oldest),
|
||||
undefined,
|
||||
)
|
||||
if (generations.get(sessionID) !== active) return
|
||||
@@ -764,14 +762,15 @@ export function createServerSession(
|
||||
session: merge(
|
||||
page.session,
|
||||
parents.map((parent) => parent.message),
|
||||
),
|
||||
).sort(compareMessages),
|
||||
part: merge(
|
||||
page.part,
|
||||
parents.map((parent) => ({ id: parent.message.id, part: parent.parts })),
|
||||
),
|
||||
}
|
||||
const preserveUnfetched =
|
||||
mode === "prepend" || (!result.complete && (!first || ((message: Message) => cmpMessage(message, first) < 0)))
|
||||
mode === "prepend" ||
|
||||
(!result.complete && (!first || ((message: Message) => compareMessages(message, first) < 0)))
|
||||
applyMessagePage(
|
||||
sessionID,
|
||||
result,
|
||||
@@ -888,7 +887,7 @@ export function createServerSession(
|
||||
.message({ sessionID, messageID })
|
||||
.then((message) => {
|
||||
const current = data.session_message[sessionID] ?? []
|
||||
const messages = [...current.filter((item) => item.id !== message.id), message].sort((a, b) => cmp(a.id, b.id))
|
||||
const messages = [...current.filter((item) => item.id !== message.id), message].sort(compareMessages)
|
||||
projectV2({ sessionID, messages, touched: [message.id] })
|
||||
})
|
||||
.catch(() => {})
|
||||
@@ -1011,7 +1010,7 @@ export function createServerSession(
|
||||
setData("message", info.sessionID, [info])
|
||||
return
|
||||
}
|
||||
const result = Binary.search(messages, info.id, (message) => message.id)
|
||||
const result = Binary.search(messages, messageKey(info), messageKey)
|
||||
if (result.found) setData("message", info.sessionID, result.index, reconcile(info))
|
||||
if (!result.found)
|
||||
setData("message", info.sessionID, (value = []) => {
|
||||
@@ -1044,8 +1043,8 @@ export function createServerSession(
|
||||
produce((draft) => {
|
||||
const messages = draft.message[props.sessionID]
|
||||
if (messages) {
|
||||
const result = Binary.search(messages, props.messageID, (message) => message.id)
|
||||
if (result.found) messages.splice(result.index, 1)
|
||||
const index = messages.findIndex((message) => message.id === props.messageID)
|
||||
if (index >= 0) messages.splice(index, 1)
|
||||
}
|
||||
deleteMessageParts(draft, props.messageID)
|
||||
}),
|
||||
@@ -1057,7 +1056,7 @@ export function createServerSession(
|
||||
if (SKIP_PARTS.has(part.type)) return
|
||||
const messages = data.message[part.sessionID]
|
||||
const load = messageLoads.get(part.sessionID)
|
||||
const missing = !messages || !Binary.search(messages, part.messageID, (message) => message.id).found
|
||||
const missing = !messages?.some((message) => message.id === part.messageID)
|
||||
// Outside a page load, accepting a part without its ordered parent event would create an unbounded orphan.
|
||||
if (
|
||||
missing &&
|
||||
@@ -1301,7 +1300,7 @@ export function createServerSession(
|
||||
if (items) items.set(input.message.id, { ...input, parts, confirmedParts: [] })
|
||||
if (!items)
|
||||
optimistic.set(input.sessionID, new Map([[input.message.id, { ...input, parts, confirmedParts: [] }]]))
|
||||
setData("message", input.sessionID, (messages = []) => merge(messages, [input.message]))
|
||||
setData("message", input.sessionID, (messages = []) => merge(messages, [input.message]).sort(compareMessages))
|
||||
setData(
|
||||
"part_text_accum_delta",
|
||||
produce((draft) => {
|
||||
|
||||
@@ -662,16 +662,15 @@ export function createServerSyncContextInner(serverSDK: ServerSDK) {
|
||||
integrationID: server.integrationID,
|
||||
location: { directory: key },
|
||||
})
|
||||
const method = integration.data?.methods.find((item) => item.type === "oauth" && !item.prompts?.length)
|
||||
const method = integration.data?.methods.find((item) => item.type === "oauth" && !item.form?.length)
|
||||
if (!method || method.type !== "oauth")
|
||||
throw new Error(`MCP server ${name} requires an interactive authentication form`)
|
||||
const attempt = await serverSDK.api.integration.oauth.connect({
|
||||
integrationID: server.integrationID,
|
||||
methodID: method.id,
|
||||
inputs: {},
|
||||
location: { directory: key },
|
||||
})
|
||||
platform.openLink(attempt.data.url)
|
||||
platform.openExternal(attempt.data.url)
|
||||
},
|
||||
refresh: async () => {
|
||||
await queryClient.refetchQueries(queryOptionsApi.mcp(key))
|
||||
|
||||
@@ -4,11 +4,11 @@ import { applyOptimisticAdd, applyOptimisticRemove, mergeOptimisticPage } from "
|
||||
|
||||
type Text = Extract<Part, { type: "text" }>
|
||||
|
||||
const userMessage = (id: string, sessionID: string): Message => ({
|
||||
const userMessage = (id: string, sessionID: string, created = 1): Message => ({
|
||||
id,
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: 1 },
|
||||
time: { created },
|
||||
agent: "assistant",
|
||||
model: { providerID: "openai", modelID: "gpt" },
|
||||
})
|
||||
@@ -22,21 +22,21 @@ const textPart = (id: string, sessionID: string, messageID: string): Text => ({
|
||||
})
|
||||
|
||||
describe("sync optimistic reducers", () => {
|
||||
test("applyOptimisticAdd inserts message in sorted order and stores parts", () => {
|
||||
test("applyOptimisticAdd inserts by creation time", () => {
|
||||
const sessionID = "ses_1"
|
||||
const draft = {
|
||||
message: { [sessionID]: [userMessage("msg_2", sessionID)] },
|
||||
message: { [sessionID]: [userMessage("msg_z", sessionID, 1)] },
|
||||
part: {} as Record<string, Part[] | undefined>,
|
||||
}
|
||||
|
||||
applyOptimisticAdd(draft, {
|
||||
sessionID,
|
||||
message: userMessage("msg_1", sessionID),
|
||||
parts: [textPart("prt_2", sessionID, "msg_1"), textPart("prt_1", sessionID, "msg_1")],
|
||||
message: userMessage("msg_a", sessionID, 2),
|
||||
parts: [textPart("prt_2", sessionID, "msg_a"), textPart("prt_1", sessionID, "msg_a")],
|
||||
})
|
||||
|
||||
expect(draft.message[sessionID]?.map((x) => x.id)).toEqual(["msg_1", "msg_2"])
|
||||
expect(draft.part.msg_1?.map((x) => x.id)).toEqual(["prt_1", "prt_2"])
|
||||
expect(draft.message[sessionID]?.map((x) => x.id)).toEqual(["msg_z", "msg_a"])
|
||||
expect(draft.part.msg_a?.map((x) => x.id)).toEqual(["prt_1", "prt_2"])
|
||||
})
|
||||
|
||||
test("applyOptimisticRemove removes message and part entries", () => {
|
||||
@@ -60,19 +60,33 @@ describe("sync optimistic reducers", () => {
|
||||
const sessionID = "ses_1"
|
||||
const page = mergeOptimisticPage(
|
||||
{
|
||||
session: [userMessage("msg_1", sessionID)],
|
||||
part: [{ id: "msg_1", part: [textPart("prt_1", sessionID, "msg_1")] }],
|
||||
session: [userMessage("msg_z", sessionID, 1)],
|
||||
part: [{ id: "msg_z", part: [textPart("prt_1", sessionID, "msg_z")] }],
|
||||
complete: true,
|
||||
},
|
||||
[{ message: userMessage("msg_2", sessionID), parts: [textPart("prt_2", sessionID, "msg_2")] }],
|
||||
[{ message: userMessage("msg_a", sessionID, 2), parts: [textPart("prt_2", sessionID, "msg_a")] }],
|
||||
)
|
||||
|
||||
expect(page.session.map((x) => x.id)).toEqual(["msg_1", "msg_2"])
|
||||
expect(page.part.find((x) => x.id === "msg_2")?.part.map((x) => x.id)).toEqual(["prt_2"])
|
||||
expect(page.session.map((x) => x.id)).toEqual(["msg_z", "msg_a"])
|
||||
expect(page.part.find((x) => x.id === "msg_a")?.part.map((x) => x.id)).toEqual(["prt_2"])
|
||||
expect(page.confirmed).toEqual([])
|
||||
expect(page.complete).toBe(true)
|
||||
})
|
||||
|
||||
test("mergeOptimisticPage uses IDs only to break equal-time ties", () => {
|
||||
const sessionID = "ses_1"
|
||||
const page = mergeOptimisticPage(
|
||||
{
|
||||
session: [userMessage("msg_z", sessionID, 1)],
|
||||
part: [],
|
||||
complete: true,
|
||||
},
|
||||
[{ message: userMessage("msg_a", sessionID, 1), parts: [] }],
|
||||
)
|
||||
|
||||
expect(page.session.map((message) => message.id)).toEqual(["msg_a", "msg_z"])
|
||||
})
|
||||
|
||||
test("mergeOptimisticPage keeps missing optimistic parts until the server has them", () => {
|
||||
const sessionID = "ses_1"
|
||||
const page = mergeOptimisticPage(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createMemo } from "solid-js"
|
||||
import { useServerSync } from "./server-sync"
|
||||
import { useSDK } from "./sdk"
|
||||
import type { Message, Part } from "@/types"
|
||||
import { messageKey } from "@/utils/session-message"
|
||||
|
||||
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
|
||||
@@ -67,7 +68,7 @@ export function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[])
|
||||
const confirmed: string[] = []
|
||||
|
||||
for (const item of items) {
|
||||
const result = Binary.search(session, item.message.id, (message) => message.id)
|
||||
const result = Binary.search(session, messageKey(item.message), messageKey)
|
||||
const found = result.found
|
||||
if (!found) session.splice(result.index, 0, item.message)
|
||||
|
||||
@@ -92,7 +93,7 @@ export function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[])
|
||||
export function applyOptimisticAdd(draft: OptimisticStore, input: OptimisticAddInput) {
|
||||
const messages = draft.message[input.sessionID]
|
||||
if (messages) {
|
||||
const result = Binary.search(messages, input.message.id, (m) => m.id)
|
||||
const result = Binary.search(messages, messageKey(input.message), messageKey)
|
||||
messages.splice(result.index, 0, input.message)
|
||||
} else {
|
||||
draft.message[input.sessionID] = [input.message]
|
||||
@@ -103,8 +104,8 @@ export function applyOptimisticAdd(draft: OptimisticStore, input: OptimisticAddI
|
||||
export function applyOptimisticRemove(draft: OptimisticStore, input: OptimisticRemoveInput) {
|
||||
const messages = draft.message[input.sessionID]
|
||||
if (messages) {
|
||||
const result = Binary.search(messages, input.messageID, (m) => m.id)
|
||||
if (result.found) messages.splice(result.index, 1)
|
||||
const index = messages.findIndex((message) => message.id === input.messageID)
|
||||
if (index >= 0) messages.splice(index, 1)
|
||||
}
|
||||
delete draft.part[input.messageID]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { ServerConnection } from "./server"
|
||||
import type { Tab } from "./tabs"
|
||||
|
||||
export function migrateTabs(value: unknown, fallback: ServerConnection.Key): Tab[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
return value.flatMap<Tab>((tab) => {
|
||||
if (!tab || typeof tab !== "object") return []
|
||||
if ("server" in tab && typeof tab.server !== "string") return []
|
||||
const server = ("server" in tab ? tab.server : fallback) as ServerConnection.Key
|
||||
if (tab.type === "session" && typeof tab.sessionId === "string") {
|
||||
return [{ type: tab.type, server, sessionId: tab.sessionId }]
|
||||
}
|
||||
if (
|
||||
tab.type === "draft" &&
|
||||
typeof tab.draftID === "string" &&
|
||||
typeof tab.directory === "string" &&
|
||||
(tab.worktree === undefined || typeof tab.worktree === "string")
|
||||
) {
|
||||
return [{ type: tab.type, server, draftID: tab.draftID, directory: tab.directory, worktree: tab.worktree }]
|
||||
}
|
||||
return []
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { createRoot, getOwner, onCleanup } from "solid-js"
|
||||
import { createTabMemory } from "./tab-memory"
|
||||
import { nextTabAfterClose, pushClosedTab, removeClosedTabs, takeClosedTab, type ClosedTab } from "./closed-tabs"
|
||||
import type { SessionTab, Tab } from "./tabs"
|
||||
import { migrateTabs } from "./tab-migration"
|
||||
import type { ServerConnection } from "./server"
|
||||
|
||||
const server = "local\nhttp://localhost:4096" as ServerConnection.Key
|
||||
@@ -11,6 +12,23 @@ function sessionTab(sessionId: string): SessionTab {
|
||||
return { type: "session", server, sessionId }
|
||||
}
|
||||
|
||||
describe("tab migration", () => {
|
||||
test("drops null and malformed persisted tabs", () => {
|
||||
expect(
|
||||
migrateTabs([null, sessionTab("a"), { type: "session", server }, { type: "unknown", server }, "invalid"], server),
|
||||
).toEqual([sessionTab("a")])
|
||||
})
|
||||
|
||||
test("adds the fallback server to valid legacy tabs", () => {
|
||||
expect(migrateTabs([{ type: "session", sessionId: "a", dirBase64: "legacy" }], server)).toEqual([sessionTab("a")])
|
||||
})
|
||||
|
||||
test("replaces invalid top-level persisted data", () => {
|
||||
expect(migrateTabs(null, server)).toEqual([])
|
||||
expect(migrateTabs({}, server)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("tab memory", () => {
|
||||
test("keeps state until its tab is removed", () => {
|
||||
createRoot((dispose) => {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { sessionHref } from "@/utils/session-route"
|
||||
import { createTabMemory } from "./tab-memory"
|
||||
import { nextTabAfterClose, pushClosedTab, removeClosedTabs, takeClosedTab, type ClosedTab } from "./closed-tabs"
|
||||
import { createDraftPromptSession, type PromptModel } from "./prompt-state"
|
||||
import { migrateTabs } from "./tab-migration"
|
||||
|
||||
export type SessionTab = {
|
||||
type: "session"
|
||||
@@ -59,13 +60,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
||||
const [store, setStore, _, ready] = persisted(
|
||||
{
|
||||
...Persist.window("tabs"),
|
||||
migrate: (value: unknown) => {
|
||||
if (!Array.isArray(value)) return value
|
||||
return value.map((tab) => {
|
||||
if (!tab || typeof tab !== "object" || "server" in tab) return tab
|
||||
return { ...tab, server: fallback }
|
||||
})
|
||||
},
|
||||
migrate: (value: unknown) => migrateTabs(value, fallback),
|
||||
},
|
||||
createStore<Tab[]>([]),
|
||||
)
|
||||
@@ -106,7 +101,10 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
||||
}
|
||||
|
||||
const removeDraftPersisted = (draftID: string) => {
|
||||
for (const key of draftPersistedKeys()) removePersisted(Persist.draft(draftID, key), platform)
|
||||
for (const key of draftPersistedKeys()) {
|
||||
const target = Persist.draft(draftID, key)
|
||||
removePersisted(key === "prompt" ? Persist.prompt(target) : target, platform)
|
||||
}
|
||||
}
|
||||
|
||||
const removeInfo = (key: string) => {
|
||||
|
||||
@@ -4,10 +4,20 @@ import { DESKTOP_MENU } from "./desktop-menu"
|
||||
describe("desktop menu", () => {
|
||||
test("exports logs through the desktop command registry", () => {
|
||||
const items = DESKTOP_MENU.flatMap((menu) => menu.items ?? []).filter(
|
||||
(item) => item.type === "item" && item.label === "Export Logs...",
|
||||
(item) => item.type === "item" && item.labelKey === "desktop.menu.exportLogs",
|
||||
)
|
||||
|
||||
expect(items).toHaveLength(2)
|
||||
expect(items.every((item) => item.type === "item" && item.command === "logs.export" && !item.action)).toBe(true)
|
||||
})
|
||||
|
||||
test("provides translated labels for role-backed entries", () => {
|
||||
const windowMenu = DESKTOP_MENU.find((menu) => menu.role === "windowMenu")
|
||||
const roleItems = DESKTOP_MENU.flatMap((menu) => menu.items ?? []).filter(
|
||||
(item) => item.type === "item" && item.role && item.labelKey,
|
||||
)
|
||||
|
||||
expect(windowMenu?.labelKey).toBe("desktop.menu.window")
|
||||
expect(roleItems.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { DesktopNativeKey } from "./i18n/desktop-native"
|
||||
|
||||
export type DesktopMenuPlatform = "macos" | "windows"
|
||||
|
||||
export type DesktopMenuAction =
|
||||
@@ -44,7 +46,7 @@ export type DesktopMenuRole =
|
||||
|
||||
export type DesktopMenuItem = {
|
||||
type: "item"
|
||||
label?: string
|
||||
labelKey?: DesktopNativeKey
|
||||
command?: string
|
||||
action?: DesktopMenuAction
|
||||
role?: DesktopMenuRole
|
||||
@@ -63,7 +65,7 @@ export type DesktopMenuEntry = DesktopMenuItem | DesktopMenuSeparator
|
||||
|
||||
export type DesktopMenu = {
|
||||
id: string
|
||||
label: string
|
||||
labelKey: DesktopNativeKey
|
||||
role?: DesktopMenuRole
|
||||
items?: DesktopMenuEntry[]
|
||||
platforms?: DesktopMenuPlatform[]
|
||||
@@ -72,15 +74,20 @@ export type DesktopMenu = {
|
||||
export const DESKTOP_MENU: DesktopMenu[] = [
|
||||
{
|
||||
id: "app",
|
||||
label: "OpenCode",
|
||||
labelKey: "desktop.menu.app",
|
||||
platforms: ["macos"],
|
||||
items: [
|
||||
{ type: "item", role: "about" },
|
||||
{ type: "item", label: "Check for Updates...", action: "app.checkForUpdates", enabled: "updater" },
|
||||
{ type: "item", label: "Settings", command: "settings.open", accelerator: { macos: "Cmd+," } },
|
||||
{ type: "item", label: "Reload Webview", action: "view.reload" },
|
||||
{ type: "item", label: "Restart", action: "app.relaunch" },
|
||||
{ type: "item", label: "Export Logs...", command: "logs.export" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.checkForUpdates",
|
||||
action: "app.checkForUpdates",
|
||||
enabled: "updater",
|
||||
},
|
||||
{ type: "item", labelKey: "desktop.menu.settings", command: "settings.open", accelerator: { macos: "Cmd+," } },
|
||||
{ type: "item", labelKey: "desktop.menu.reloadWebview", action: "view.reload" },
|
||||
{ type: "item", labelKey: "desktop.menu.restart", action: "app.relaunch" },
|
||||
{ type: "item", labelKey: "desktop.menu.exportLogs", command: "logs.export" },
|
||||
{ type: "separator" },
|
||||
{ type: "item", role: "hide" },
|
||||
{ type: "item", role: "hideOthers" },
|
||||
@@ -91,46 +98,81 @@ export const DESKTOP_MENU: DesktopMenu[] = [
|
||||
},
|
||||
{
|
||||
id: "file",
|
||||
label: "File",
|
||||
labelKey: "desktop.menu.file",
|
||||
items: [
|
||||
{
|
||||
type: "item",
|
||||
label: "New Session",
|
||||
labelKey: "desktop.menu.newSession",
|
||||
command: "session.new",
|
||||
accelerator: { macos: "Shift+Cmd+S" },
|
||||
},
|
||||
{ type: "item", label: "Open Project...", command: "project.open", accelerator: { macos: "Cmd+O" } },
|
||||
{
|
||||
type: "item",
|
||||
label: "Settings",
|
||||
labelKey: "desktop.menu.openProject",
|
||||
command: "project.open",
|
||||
accelerator: { macos: "Cmd+O" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.settings",
|
||||
command: "settings.open",
|
||||
accelerator: { windows: "Ctrl+," },
|
||||
platforms: ["windows"],
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
label: "New Window",
|
||||
labelKey: "desktop.menu.newWindow",
|
||||
action: "window.new",
|
||||
accelerator: { macos: "Cmd+Shift+N", windows: "Ctrl+Shift+N" },
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Close Window", action: "window.close", role: "close" },
|
||||
{ type: "item", labelKey: "desktop.menu.closeWindow", action: "window.close", role: "close" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "edit",
|
||||
label: "Edit",
|
||||
labelKey: "desktop.menu.edit",
|
||||
items: [
|
||||
{ type: "item", label: "Undo", action: "edit.undo", role: "undo", accelerator: { windows: "Ctrl+Z" } },
|
||||
{ type: "item", label: "Redo", action: "edit.redo", role: "redo", accelerator: { windows: "Ctrl+Y" } },
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Cut", action: "edit.cut", role: "cut", accelerator: { windows: "Ctrl+X" } },
|
||||
{ type: "item", label: "Copy", action: "edit.copy", role: "copy", accelerator: { windows: "Ctrl+C" } },
|
||||
{ type: "item", label: "Paste", action: "edit.paste", role: "paste", accelerator: { windows: "Ctrl+V" } },
|
||||
{ type: "item", label: "Delete", action: "edit.delete" },
|
||||
{
|
||||
type: "item",
|
||||
label: "Select All",
|
||||
labelKey: "desktop.menu.undo",
|
||||
action: "edit.undo",
|
||||
role: "undo",
|
||||
accelerator: { windows: "Ctrl+Z" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.redo",
|
||||
action: "edit.redo",
|
||||
role: "redo",
|
||||
accelerator: { windows: "Ctrl+Y" },
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.cut",
|
||||
action: "edit.cut",
|
||||
role: "cut",
|
||||
accelerator: { windows: "Ctrl+X" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.copy",
|
||||
action: "edit.copy",
|
||||
role: "copy",
|
||||
accelerator: { windows: "Ctrl+C" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.paste",
|
||||
action: "edit.paste",
|
||||
role: "paste",
|
||||
accelerator: { windows: "Ctrl+V" },
|
||||
},
|
||||
{ type: "item", labelKey: "desktop.menu.delete", action: "edit.delete" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.selectAll",
|
||||
action: "edit.selectAll",
|
||||
role: "selectAll",
|
||||
accelerator: { windows: "Ctrl+A" },
|
||||
@@ -139,47 +181,84 @@ export const DESKTOP_MENU: DesktopMenu[] = [
|
||||
},
|
||||
{
|
||||
id: "view",
|
||||
label: "View",
|
||||
labelKey: "desktop.menu.view",
|
||||
items: [
|
||||
{ type: "item", label: "Toggle Sidebar", command: "sidebar.toggle" },
|
||||
{ type: "item", label: "Toggle Terminal", command: "terminal.toggle", accelerator: { macos: "Ctrl+`" } },
|
||||
{ type: "item", label: "Toggle File Tree", command: "fileTree.toggle" },
|
||||
{ type: "item", labelKey: "desktop.menu.toggleSidebar", command: "sidebar.toggle" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.toggleTerminal",
|
||||
command: "terminal.toggle",
|
||||
accelerator: { macos: "Ctrl+`" },
|
||||
},
|
||||
{ type: "item", labelKey: "desktop.menu.toggleFileTree", command: "fileTree.toggle" },
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Reload", action: "view.reload", role: "reload" },
|
||||
{ type: "item", label: "Toggle Developer Tools", action: "view.toggleDevTools", role: "toggleDevTools" },
|
||||
{ type: "item", labelKey: "desktop.menu.reload", action: "view.reload", role: "reload" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.toggleDeveloperTools",
|
||||
action: "view.toggleDevTools",
|
||||
role: "toggleDevTools",
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "item",
|
||||
label: "Actual Size",
|
||||
labelKey: "desktop.menu.actualSize",
|
||||
action: "view.resetZoom",
|
||||
role: "resetZoom",
|
||||
accelerator: { windows: "Ctrl+0" },
|
||||
},
|
||||
{ type: "item", label: "Zoom In", action: "view.zoomIn", role: "zoomIn", accelerator: { windows: "Ctrl++" } },
|
||||
{ type: "item", label: "Zoom Out", action: "view.zoomOut", role: "zoomOut", accelerator: { windows: "Ctrl+-" } },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.zoomIn",
|
||||
action: "view.zoomIn",
|
||||
role: "zoomIn",
|
||||
accelerator: { windows: "Ctrl++" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.zoomOut",
|
||||
action: "view.zoomOut",
|
||||
role: "zoomOut",
|
||||
accelerator: { windows: "Ctrl+-" },
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Toggle Full Screen", action: "view.toggleFullscreen", role: "togglefullscreen" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.toggleFullScreen",
|
||||
action: "view.toggleFullscreen",
|
||||
role: "togglefullscreen",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "go",
|
||||
label: "Go",
|
||||
labelKey: "desktop.menu.go",
|
||||
items: [
|
||||
{ type: "item", label: "Back", command: "common.goBack", accelerator: { macos: "Cmd+[" } },
|
||||
{ type: "item", label: "Forward", command: "common.goForward", accelerator: { macos: "Cmd+]" } },
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Previous Session", command: "session.previous", accelerator: { macos: "Option+Up" } },
|
||||
{ type: "item", label: "Next Session", command: "session.next", accelerator: { macos: "Option+Down" } },
|
||||
{ type: "item", labelKey: "desktop.menu.back", command: "common.goBack", accelerator: { macos: "Cmd+[" } },
|
||||
{ type: "item", labelKey: "desktop.menu.forward", command: "common.goForward", accelerator: { macos: "Cmd+]" } },
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "item",
|
||||
label: "Previous Project",
|
||||
labelKey: "desktop.menu.previousSession",
|
||||
command: "session.previous",
|
||||
accelerator: { macos: "Option+Up" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.nextSession",
|
||||
command: "session.next",
|
||||
accelerator: { macos: "Option+Down" },
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "item",
|
||||
labelKey: "desktop.menu.previousProject",
|
||||
command: "project.previous",
|
||||
accelerator: { macos: "Cmd+Option+Up" },
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
label: "Next Project",
|
||||
labelKey: "desktop.menu.nextProject",
|
||||
command: "project.next",
|
||||
accelerator: { macos: "Cmd+Option+Down" },
|
||||
},
|
||||
@@ -187,31 +266,31 @@ export const DESKTOP_MENU: DesktopMenu[] = [
|
||||
},
|
||||
{
|
||||
id: "window",
|
||||
label: "Window",
|
||||
labelKey: "desktop.menu.window",
|
||||
role: "windowMenu",
|
||||
items: [
|
||||
{ type: "item", label: "Minimize", action: "window.minimize" },
|
||||
{ type: "item", label: "Maximize", action: "window.toggleMaximize" },
|
||||
{ type: "item", labelKey: "desktop.menu.minimize", action: "window.minimize" },
|
||||
{ type: "item", labelKey: "desktop.menu.maximize", action: "window.toggleMaximize" },
|
||||
{ type: "separator" },
|
||||
{ type: "item", label: "Close Window", action: "window.close" },
|
||||
{ type: "item", labelKey: "desktop.menu.closeWindow", action: "window.close" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "help",
|
||||
label: "Help",
|
||||
labelKey: "desktop.menu.help",
|
||||
items: [
|
||||
{ type: "item", label: "OpenCode Documentation", href: "https://opencode.ai/docs" },
|
||||
{ type: "item", label: "Support Forum", href: "https://discord.com/invite/opencode" },
|
||||
{ type: "item", label: "Export Logs...", command: "logs.export" },
|
||||
{ type: "item", labelKey: "desktop.menu.documentation", href: "https://opencode.ai/docs" },
|
||||
{ type: "item", labelKey: "desktop.menu.supportForum", href: "https://discord.com/invite/opencode" },
|
||||
{ type: "item", labelKey: "desktop.menu.exportLogs", command: "logs.export" },
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "item",
|
||||
label: "Share Feedback",
|
||||
labelKey: "desktop.menu.shareFeedback",
|
||||
href: "https://github.com/anomalyco/opencode/issues/new?template=feature_request.yml",
|
||||
},
|
||||
{
|
||||
type: "item",
|
||||
label: "Report a Bug",
|
||||
labelKey: "desktop.menu.reportBug",
|
||||
href: "https://github.com/anomalyco/opencode/issues/new?template=bug_report.yml",
|
||||
},
|
||||
],
|
||||
|
||||
+39
-41
@@ -3,10 +3,11 @@
|
||||
import * as Sentry from "@sentry/solid"
|
||||
import { render } from "solid-js/web"
|
||||
import { AppBaseProviders, AppInterface } from "@/app"
|
||||
import { loadInitialLocale } from "@/context/language"
|
||||
import { type Platform, PlatformProvider } from "@/context/platform"
|
||||
import { createBrowserDraftStore } from "@/utils/draft-store"
|
||||
import { dict as en } from "@/i18n/en"
|
||||
import { dict as zh } from "@/i18n/zh"
|
||||
import { handleNotificationClick } from "@/utils/notification-click"
|
||||
import { authFromToken } from "@/utils/server"
|
||||
import pkg from "../package.json"
|
||||
import { ServerConnection } from "./context/server"
|
||||
@@ -54,7 +55,7 @@ const setStorage = (key: string, value: string | null) => {
|
||||
const readDefaultServerUrl = () => getStorage(DEFAULT_SERVER_URL_KEY)
|
||||
const writeDefaultServerUrl = (url: string | null) => setStorage(DEFAULT_SERVER_URL_KEY, url)
|
||||
|
||||
const notify: Platform["notify"] = async (title, description, href) => {
|
||||
const notify: Platform["notify"] = async (title, description, onClick) => {
|
||||
if (!("Notification" in window)) return
|
||||
|
||||
const permission =
|
||||
@@ -73,21 +74,17 @@ const notify: Platform["notify"] = async (title, description, href) => {
|
||||
})
|
||||
|
||||
notification.onclick = () => {
|
||||
handleNotificationClick(href)
|
||||
window.focus()
|
||||
onClick?.()
|
||||
notification.close()
|
||||
}
|
||||
}
|
||||
|
||||
const openLink: Platform["openLink"] = (url) => {
|
||||
window.open(url, "_blank")
|
||||
}
|
||||
|
||||
const back: Platform["back"] = () => {
|
||||
window.history.back()
|
||||
}
|
||||
|
||||
const forward: Platform["forward"] = () => {
|
||||
window.history.forward()
|
||||
const openExternal: Platform["openExternal"] = (value) => {
|
||||
if (!URL.canParse(value)) return
|
||||
const url = new URL(value)
|
||||
if (url.protocol !== "http:" && url.protocol !== "https:" && url.protocol !== "mailto:") return
|
||||
window.open(url.href, "_blank", "noopener,noreferrer")
|
||||
}
|
||||
|
||||
const restart: Platform["restart"] = async () => {
|
||||
@@ -121,10 +118,9 @@ const clearAuthToken = () => {
|
||||
|
||||
const platform: Platform = {
|
||||
platform: "web",
|
||||
draftStore: createBrowserDraftStore(),
|
||||
version: pkg.version,
|
||||
openLink,
|
||||
back,
|
||||
forward,
|
||||
openExternal,
|
||||
restart,
|
||||
notify,
|
||||
getDefaultServer: async () => {
|
||||
@@ -154,29 +150,31 @@ if (import.meta.env.VITE_SENTRY_DSN) {
|
||||
}
|
||||
|
||||
if (root instanceof HTMLElement) {
|
||||
const auth = authFromToken(new URLSearchParams(location.search).get("auth_token"))
|
||||
clearAuthToken()
|
||||
const server: ServerConnection.Http = {
|
||||
type: "http",
|
||||
authToken: !!auth,
|
||||
http: {
|
||||
url: getCurrentUrl(),
|
||||
...auth,
|
||||
},
|
||||
}
|
||||
render(
|
||||
() => (
|
||||
<PlatformProvider value={platform}>
|
||||
<AppBaseProviders>
|
||||
<AppInterface
|
||||
defaultServer={ServerConnection.Key.make(getDefaultUrl())}
|
||||
canonicalLocalServer={ServerConnection.key(server)}
|
||||
servers={[server]}
|
||||
disableHealthCheck
|
||||
/>
|
||||
</AppBaseProviders>
|
||||
</PlatformProvider>
|
||||
),
|
||||
root,
|
||||
)
|
||||
void loadInitialLocale().then((locale) => {
|
||||
const auth = authFromToken(new URLSearchParams(location.search).get("auth_token"))
|
||||
clearAuthToken()
|
||||
const server: ServerConnection.Http = {
|
||||
type: "http",
|
||||
authToken: !!auth,
|
||||
http: {
|
||||
url: getCurrentUrl(),
|
||||
...auth,
|
||||
},
|
||||
}
|
||||
render(
|
||||
() => (
|
||||
<PlatformProvider value={platform}>
|
||||
<AppBaseProviders locale={locale}>
|
||||
<AppInterface
|
||||
defaultServer={ServerConnection.Key.make(getDefaultUrl())}
|
||||
canonicalLocalServer={ServerConnection.key(server)}
|
||||
servers={[server]}
|
||||
disableHealthCheck
|
||||
/>
|
||||
</AppBaseProviders>
|
||||
</PlatformProvider>
|
||||
),
|
||||
root,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,1130 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "ፋይል",
|
||||
"desktop.menu.edit": "አርትዕ",
|
||||
"desktop.menu.view": "እይታ",
|
||||
"desktop.menu.go": "ሂድ",
|
||||
"desktop.menu.window": "መስኮት",
|
||||
"desktop.menu.help": "እገዛ",
|
||||
"desktop.menu.checkForUpdates": "ዝማኔዎችን ይመልከቱ...",
|
||||
"desktop.menu.settings": "ቅንጅቶች",
|
||||
"desktop.menu.reloadWebview": "ዳግም ጫን Webview",
|
||||
"desktop.menu.restart": "ዳግም አስጀምር",
|
||||
"desktop.menu.exportLogs": "የመላክ ምዝግብ ማስታወሻዎች...",
|
||||
"desktop.menu.newSession": "አዲስ ክፍለ ጊዜ",
|
||||
"desktop.menu.openProject": "ክፍት ፕሮጀክት...",
|
||||
"desktop.menu.newWindow": "አዲስ መስኮት",
|
||||
"desktop.menu.closeWindow": "መስኮትን ዝጋ",
|
||||
"desktop.menu.undo": "ቀልብስ",
|
||||
"desktop.menu.redo": "ድገም",
|
||||
"desktop.menu.cut": "የተቆረጠ",
|
||||
"desktop.menu.copy": "ኮፒ",
|
||||
"desktop.menu.paste": "ለጥፍ",
|
||||
"desktop.menu.delete": "ሰርዝ",
|
||||
"desktop.menu.selectAll": "ሁሉንም ምረጥ",
|
||||
"desktop.menu.toggleSidebar": "የጎን አሞሌን ቀይር",
|
||||
"desktop.menu.toggleTerminal": "መቀያየር ተርሚናል",
|
||||
"desktop.menu.toggleFileTree": "ፋይል ዛፍን ቀይር",
|
||||
"desktop.menu.reload": "ዳግም ጫን",
|
||||
"desktop.menu.toggleDeveloperTools": "የገንቢ መሣሪያዎችን ቀይር",
|
||||
"desktop.menu.actualSize": "ትክክለኛው መጠን",
|
||||
"desktop.menu.zoomIn": "አጉላ",
|
||||
"desktop.menu.zoomOut": "አጉላ",
|
||||
"desktop.menu.toggleFullScreen": "ሙሉ ስክሪን ቀይር",
|
||||
"desktop.menu.back": "ተመለስ",
|
||||
"desktop.menu.forward": "ወደፊት",
|
||||
"desktop.menu.previousSession": "የቀድሞው ክፍለ ጊዜ",
|
||||
"desktop.menu.nextSession": "ቀጣይ ክፍለ ጊዜ",
|
||||
"desktop.menu.previousProject": "የቀድሞው ፕሮጀክት",
|
||||
"desktop.menu.nextProject": "ቀጣይ ፕሮጀክት",
|
||||
"desktop.menu.minimize": "አሳንስ",
|
||||
"desktop.menu.maximize": "ከፍተኛ",
|
||||
"desktop.menu.documentation": "የOpenCode ሰነድ",
|
||||
"desktop.menu.supportForum": "የድጋፍ መድረክ",
|
||||
"desktop.menu.shareFeedback": "አጋራ ግብረመልስ",
|
||||
"desktop.menu.reportBug": "ስህተትን ሪፖርት አድርግ",
|
||||
"desktop.menu.ariaLabel": "OpenCodeምናሌ",
|
||||
"desktop.updater.dialog.checkFailed.message": "የዝማኔ ማረጋገጫ አልተሳካም።",
|
||||
"desktop.updater.dialog.checkFailed.title": "ስህተት ማዘመን",
|
||||
"desktop.updater.dialog.upToDate.message": "ወቅታዊ ነዎት።",
|
||||
"desktop.updater.dialog.upToDate.title": "ምንም ማሻሻያ የለም",
|
||||
"desktop.updater.dialog.ready.message": "ዝማኔ {{version}} ወርዷል። አሁን እንደገና ይጀመር?",
|
||||
"desktop.updater.dialog.ready.title": "አዘምን ዝግጁ",
|
||||
"desktop.updater.dialog.restart": "ዳግም አስጀምር",
|
||||
"desktop.updater.dialog.later": "በኋላ",
|
||||
"desktop.recovery.action.relaunch": "ዳግም አስጀምር",
|
||||
"desktop.recovery.action.exportLogs": "የመላክ ምዝግብ ማስታወሻዎች",
|
||||
"desktop.recovery.action.keepWaiting": "መጠበቅዎን ይቀጥሉ",
|
||||
"desktop.recovery.action.quit": "አቁም",
|
||||
"desktop.recovery.loadFailed": "OpenCode መጫን አልቻለም",
|
||||
"desktop.recovery.terminated": "OpenCode መስኮት በድንገት ተቋርጧል",
|
||||
"desktop.recovery.unresponsive": "OpenCode ምላሽ እየሰጠ አይደለም",
|
||||
"desktop.recovery.unresponsive.detail": "መተግበሪያውን እንደገና ማስጀመር፣ መዝገቦችን መክፈት ወይም መጠበቅ ትችላለህ።",
|
||||
"desktop.recovery.loadFailed.detail": "መስኮት፡ {{window}}\nURL፡ {{url}}\nስህተት፡ {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "መስኮት፡ {{window}}\nምክንያት፡ {{reason}}\nኮድ፡ {{code}}",
|
||||
"desktop.recovery.unknown": "<unknown>",
|
||||
"desktop.dialog.chooseFolder": "አቃፊ ምረጥ",
|
||||
"desktop.dialog.chooseFile": "ፋይል ምረጥ",
|
||||
"desktop.dialog.saveFile": "ፋይሉን አስቀምጥ",
|
||||
"desktop.dialog.files": "ፋይሎች",
|
||||
"desktop.server.local": "አካባቢያዊ አገልጋይ",
|
||||
"desktop.wsl.error.windowsOnly": "WSL የሚገኘው በWindows",
|
||||
"desktop.wsl.error.unavailable": "WSL አይገኝም",
|
||||
"desktop.wsl.error.listInstalled": "የተጫኑትን WSL distros መዘርዘር አልተሳካም",
|
||||
"desktop.wsl.error.listOnline": "ኦንላይን WSL distros መዘርዘር አልተሳካም",
|
||||
"desktop.wsl.error.executeDistro": "በዲስትሮ ውስጥ ትዕዛዞችን ማስፈጸም አይቻልም",
|
||||
"desktop.wsl.error.installWsl": "WSLመጫኑ አልተሳካም",
|
||||
"desktop.wsl.error.installDistro": "distroን መጫን አልተቻለም፡ {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "OpenCodeመጫኑ አልተሳካም",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} አስቀድሞ ታክሏል",
|
||||
"desktop.wsl.error.opencodeMissing": "ክፍት ኮድ በዚህ ዲስትሪ ውስጥ አልተጫነም",
|
||||
"desktop.wsl.error.opencodeCannotRun": "ክፍት ኮድ ተጭኗል ግን ማሄድ አልቻለም",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode በ{{distro}}] ውስጥ አልተጫነም",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode ዝማኔ አልቋል ግን {{distro}} አሁንም ሪፖርት ያደርጋል {{installed}}; የሚጠበቀው {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "ምንም ስሪት የለም",
|
||||
"desktop.wsl.error.serverExited": "WSL አገልጋይ ከተጀመረ በኋላ ወጥቷል (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"WSL አገልጋይ ጤናማ ከመሆኑ በፊት ወጥቷል (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout": "Sidecar for {{distro}} የጤና ፍተሻ ጊዜው አልፎበታል ከ{{timeout}}ሚሴ በኋላ",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} ጊዜው አልፎበታል ከ{{timeout}}ሚሴ በኋላ",
|
||||
"desktop.wsl.error.failedPort": "ወደብ ማግኘት አልተሳካም",
|
||||
"desktop.picker.error.notSelected": "ፋይሉ በመራጭው አልተመረጠም",
|
||||
"desktop.picker.error.sizeLimit": "የተመረጡት ዓባሪዎች ከ{{limit}}ሜባ ገደቡ በላይ",
|
||||
"command.category.suggested": "የሚመከር",
|
||||
"command.category.view": "እይታ",
|
||||
"command.category.project": "ፕሮጀክት",
|
||||
"command.category.provider": "አቅራቢ",
|
||||
"command.category.server": "አገልጋይ",
|
||||
"command.category.session": "ክፍለ ጊዜ",
|
||||
"command.category.theme": "ገጽታ",
|
||||
"command.category.language": "ቋንቋ",
|
||||
"command.category.file": "ፋይል",
|
||||
"command.category.context": "አውድ",
|
||||
"command.category.terminal": "ተርሚናል",
|
||||
"command.category.model": "ሞዴል",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "ወኪል",
|
||||
"command.category.permissions": "ፍቃዶች",
|
||||
"command.category.workspace": "Workspace",
|
||||
"command.category.settings": "ቅንጅቶች",
|
||||
"command.logs.export": "የመላክ ምዝግብ ማስታወሻዎች",
|
||||
"theme.scheme.system": "ስርዓት",
|
||||
"theme.scheme.light": "ብርሃን",
|
||||
"theme.scheme.dark": "ጨለማ",
|
||||
"command.sidebar.toggle": "የጎን አሞሌን ቀይር",
|
||||
"command.project.open": "ክፍት ፕሮጀክት",
|
||||
"command.project.previous": "የቀድሞው ፕሮጀክት",
|
||||
"command.project.next": "ቀጣይ ፕሮጀክት",
|
||||
"command.project.index": "ወደ ፕሮጀክት {{index}}] ቀይር",
|
||||
"command.provider.connect": "አቅራቢን አገናኝ",
|
||||
"command.server.switch": "አገልጋይ ቀይር",
|
||||
"command.settings.open": "ክፍት ቅንብሮች",
|
||||
"command.session.previous": "የቀድሞው ክፍለ ጊዜ",
|
||||
"command.session.next": "ቀጣይ ክፍለ ጊዜ",
|
||||
"command.session.previous.unseen": "ያለፈው ያልተነበበ ክፍለ ጊዜ",
|
||||
"command.session.next.unseen": "ቀጣይ ያልተነበበ ክፍለ ጊዜ",
|
||||
"command.session.archive": "ክፍለ ጊዜን በማህደር አስቀምጥ",
|
||||
"command.palette": "የትእዛዝ ቤተ-ስዕል",
|
||||
"command.theme.cycle": "የዑደት ጭብጥ",
|
||||
"command.theme.set": "ገጽታ ይጠቀሙ፡ {{theme}}",
|
||||
"command.theme.scheme.cycle": "የዑደት ቀለም ንድፍ",
|
||||
"command.theme.scheme.set": "የቀለም ንድፍ ተጠቀም፡ {{scheme}}",
|
||||
"command.language.cycle": "ሳይክል ቋንቋ",
|
||||
"command.language.set": "ቋንቋን ተጠቀም፡ {{language}}",
|
||||
"command.session.new": "አዲስ ክፍለ ጊዜ",
|
||||
"command.file.open": "ክፍት ፋይል",
|
||||
"command.tab.close": "ትርፉን ዝጋ",
|
||||
"command.tab.reopenClosed": "የተዘጋውን ትር እንደገና ክፈት",
|
||||
"command.context.addSelection": "ምርጫ ወደ አውድ አክል",
|
||||
"command.context.addSelection.description": "የተመረጡትን መስመሮች ከአሁኑ ፋይል ያክሉ",
|
||||
"command.input.focus": "የትኩረት ግቤት",
|
||||
"command.terminal.toggle": "ተርሚናል ቀይር",
|
||||
"command.fileTree.toggle": "ፋይል ዛፍን ቀይር",
|
||||
"command.review.toggle": "ግምገማውን ቀይር",
|
||||
"command.terminal.new": "አዲስ ተርሚናል",
|
||||
"command.terminal.new.description": "አዲስ ተርሚናል ትር ፍጠር",
|
||||
"command.steps.toggle": "ደረጃዎችን ቀይር",
|
||||
"command.steps.toggle.description": "ለአሁኑ መልእክት ደረጃዎችን አሳይ ወይም ደብቅ",
|
||||
"command.message.previous": "የቀድሞ መልእክት",
|
||||
"command.message.previous.description": "ወደ ቀዳሚው የተጠቃሚ መልእክት ይሂዱ",
|
||||
"command.message.next": "ቀጣይ መልእክት",
|
||||
"command.message.next.description": "ወደሚቀጥለው የተጠቃሚ መልእክት ይሂዱ",
|
||||
"command.model.choose": "ሞዴል ምረጥ",
|
||||
"command.model.choose.description": "የተለየ ሞዴል ይምረጡ",
|
||||
"command.mcp.toggle": "MCPs",
|
||||
"command.mcp.toggle.description": "MCPs",
|
||||
"command.agent.cycle": "ወደሚቀጥለው ወኪል ቀይር",
|
||||
"command.agent.cycle.description": "ወደሚቀጥለው ወኪል ቀይር",
|
||||
"command.agent.cycle.reverse": "ወደቀድሞው ወኪል ቀይር",
|
||||
"command.agent.cycle.reverse.description": "ወደ ቀዳሚው ወኪል ቀይር",
|
||||
"command.model.variant.cycle": "የማሰብ ጥረት ደረጃን ቀይር",
|
||||
"command.model.variant.cycle.description": "ወደሚቀጥለው የጥረት ደረጃ ቀይር",
|
||||
"command.prompt.mode.shell": "ሼል",
|
||||
"command.prompt.mode.normal": "ፕሮምፕት",
|
||||
"command.permissions.autoaccept.enable": "በራስ-ተቀበል ፈቃዶች",
|
||||
"command.permissions.autoaccept.disable": "በራስ-መቀበል ፍቃዶችን አቁም",
|
||||
"command.workspace.toggle": "Workspace ቀይር",
|
||||
"command.workspace.toggle.description": "በጎን አሞሌው ውስጥ ብዙ workspace-ዎችን አንቃ ወይም አሰናክል",
|
||||
"command.session.undo": "ቀልብስ",
|
||||
"command.session.undo.description": "የመጨረሻውን መልእክት ቀልብስ",
|
||||
"command.session.redo": "ድገም",
|
||||
"command.session.redo.description": "የመጨረሻውን የተቀለበሰ መልእክት ይድገሙት",
|
||||
"command.session.compact": "የታመቀ ክፍለ ጊዜ",
|
||||
"command.session.compact.description": "የአውድ መጠንን ለመቀነስ ክፍለ-ጊዜውን ያጠቃልሉት",
|
||||
"command.session.fork": "ከመልዕክት አዲስ ቅርንጫፍ ፍጠር",
|
||||
"command.session.fork.description": "ከቀደመው መልእክት አዲስ ክፍለ ጊዜ ፍጠር",
|
||||
"command.session.share": "አጋራ ክፍለ ጊዜ",
|
||||
"command.session.share.description": "ይህን ክፍለ ጊዜ ያጋሩ እና URLን ወደ ቅንጥብ ሰሌዳ ይቅዱ",
|
||||
"command.session.unshare": "ክፍለ-ጊዜን አታጋራ",
|
||||
"command.session.unshare.description": "ይህን ክፍለ ጊዜ ማጋራት አቁም",
|
||||
"command.session.export": "ክፍለ ጊዜን ወደ ውጭ ላክ",
|
||||
"command.session.export.description": "ሙሉውን የክፍለ ጊዜ ግልባጭ እንደ JSON",
|
||||
"palette.search.placeholder": "ፋይሎችን፣ ትዕዛዞችን እና ክፍለ-ጊዜዎችን ይፈልጉ",
|
||||
"palette.search.placeholder.home": "የፍለጋ ትዕዛዞችን እና ክፍለ-ጊዜዎችን",
|
||||
"palette.empty": "ምንም ውጤቶች አልተገኙም",
|
||||
"palette.group.commands": "ትዕዛዞች",
|
||||
"palette.group.files": "ፋይሎች",
|
||||
"dialog.provider.search.placeholder": "የፍለጋ አቅራቢዎች",
|
||||
"dialog.provider.empty": "ምንም አቅራቢዎች አልተገኙም",
|
||||
"dialog.provider.group.popular": "ታዋቂ",
|
||||
"dialog.provider.group.other": "ሌላ",
|
||||
"dialog.provider.custom.label": "ብጁ OpenAIተኳሃኝ አቅራቢ",
|
||||
"dialog.provider.tag.recommended": "የሚመከር",
|
||||
"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 ለፈጣን፣ የተዋቀሩ ምላሾች ሞዴሎች",
|
||||
"dialog.provider.openrouter.note": "ሁሉንም የሚደገፉ ሞዴሎች ከአንድ አቅራቢ ይድረሱ",
|
||||
"dialog.provider.vercel.note": "የተዋሃደ የ AI ሞዴሎች ከስማርት ማዘዋወር ጋር",
|
||||
"dialog.model.select.title": "ሞዴል ምረጥ",
|
||||
"dialog.model.search.placeholder": "የፍለጋ ሞዴሎች",
|
||||
"dialog.model.empty": "ምንም የሞዴል ውጤቶች የሉም",
|
||||
"dialog.model.manage": "ሞዴሎችን አስተዳድር",
|
||||
"dialog.model.manage.description": "በሞዴል መራጭ ውስጥ የትኞቹ ሞዴሎች እንደሚታዩ አብጅ።",
|
||||
"dialog.model.manage.provider.toggle": "ሁሉንም {{provider}} ሞዴሎች ቀያይር",
|
||||
"dialog.model.unpaid.freeModels.title": "ነጻ ሞዴሎች በOpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "ተጨማሪ ሞዴሎችን ከታዋቂ አቅራቢዎች ያክሉ",
|
||||
"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": "አሳሽ",
|
||||
"provider.connect.method.headless": "ያለ ግራፊክ በይነገጽ",
|
||||
"provider.connect.status.inProgress": "ፍቃድ በሂደት ላይ...",
|
||||
"provider.connect.status.waiting": "ፈቀዳን በመጠበቅ ላይ...",
|
||||
"provider.connect.status.failed": "ፍቃድ መስጠት አልተሳካም፡ {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"መለያዎን ለማገናኘት የ{{provider}} API ቁልፍዎን ያስገቡ እና {{provider}} ሞዴሎችን በOpenCode ይጠቀሙ።",
|
||||
"provider.connect.apiKey.label": "{{provider}} API ቁልፍ",
|
||||
"provider.connect.apiKey.placeholder": "API ቁልፍ",
|
||||
"provider.connect.apiKey.required": "API ቁልፍ ያስፈልጋል",
|
||||
"provider.connect.opencodeZen.line1": "OpenCode ዜን ለኮድ ወኪሎች የተመቻቹ አስተማማኝ ሞዴሎች ስብስብ መዳረሻ ይሰጥዎታል።",
|
||||
"provider.connect.opencodeZen.line2": "በአንድ የAPI ቁልፍ እንደ Claude፣ GPT፣ Gemini፣ GLM እና ሌሎችም ያሉ ሞዴሎችን መድረስ ይችላሉ።",
|
||||
"provider.connect.opencodeZen.visit.prefix": "ጎብኝ ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " የእርስዎን API ቁልፍ ለመሰብሰብ።",
|
||||
"provider.connect.oauth.code.visit.prefix": "ጎብኝ ",
|
||||
"provider.connect.oauth.code.visit.link": "ይህ ሊንክ",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" መለያዎን ለማገናኘት የፈቀዳ ኮድዎን ለመሰብሰብ እና {{provider}} ሞዴሎችን በOpenCode ውስጥ ይጠቀሙ።",
|
||||
"provider.connect.oauth.code.label": "{{method}}የፈቀዳ ኮድ",
|
||||
"provider.connect.oauth.code.placeholder": "የፈቃድ ኮድ",
|
||||
"provider.connect.oauth.code.required": "የፈቃድ ኮድ ያስፈልጋል",
|
||||
"provider.connect.oauth.code.invalid": "ልክ ያልሆነ የፈቀዳ ኮድ",
|
||||
"provider.connect.oauth.auto.visit.prefix": "ጎብኝ ",
|
||||
"provider.connect.oauth.auto.visit.link": "ይህ ሊንክ",
|
||||
"provider.connect.oauth.auto.visit.suffix": " እና መለያዎን ለማገናኘት ከታች ያለውን ኮድ ያስገቡ እና {{provider}} ሞዴሎችን በOpenCode ይጠቀሙ።",
|
||||
"provider.connect.oauth.auto.confirmationCode": "የማረጋገጫ ኮድ",
|
||||
"provider.connect.toast.connected.title": "{{provider}}ተገናኝቷል",
|
||||
"provider.connect.toast.connected.description": "{{provider}} ሞዴሎች አሁን ለመጠቀም ይገኛሉ።",
|
||||
"provider.custom.title": "ብጁ አቅራቢ",
|
||||
"provider.custom.unavailable": "ብጁ አቅራቢዎች በዚህ አገልጋይ ላይ አይገኙም",
|
||||
"provider.custom.description.prefix": "OpenAI-ተኳሃኝ አቅራቢን ያዋቅሩ። ይመልከቱ ",
|
||||
"provider.custom.description.link": "የአቅራቢ ውቅረት ሰነዶች",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "የአቅራቢ መታወቂያ",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "ዝቅተኛ ፊደሎች፣ ቁጥሮች፣ ሰረዞች፣ ወይም ከስር ያሉ ምልክቶች",
|
||||
"provider.custom.field.name.label": "የማሳያ ስም",
|
||||
"provider.custom.field.name.placeholder": "የእኔ AI አቅራቢ",
|
||||
"provider.custom.field.baseURL.label": "መሰረት URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API ቁልፍ",
|
||||
"provider.custom.field.apiKey.placeholder": "API ቁልፍ",
|
||||
"provider.custom.field.apiKey.description": "አማራጭ. በራስጌዎች በኩል ማተምን የሚያስተዳድሩ ከሆነ ባዶ ይተዉት።",
|
||||
"provider.custom.models.label": "ሞዴሎች",
|
||||
"provider.custom.models.id.label": "መታወቂያ",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
"provider.custom.models.name.label": "ስም",
|
||||
"provider.custom.models.name.placeholder": "የማሳያ ስም",
|
||||
"provider.custom.models.remove": "ሞዴሉን አስወግድ",
|
||||
"provider.custom.models.add": "ሞዴል አክል",
|
||||
"provider.custom.headers.label": "ራስጌዎች (አማራጭ)",
|
||||
"provider.custom.headers.key.label": "ራስጌ",
|
||||
"provider.custom.headers.key.placeholder": "ራስጌ-ስም",
|
||||
"provider.custom.headers.value.label": "ዋጋ",
|
||||
"provider.custom.headers.value.placeholder": "ዋጋ",
|
||||
"provider.custom.headers.remove": "ራስጌ አስወግድ",
|
||||
"provider.custom.headers.add": "ራስጌ አክል",
|
||||
"provider.custom.error.providerID.required": "የአቅራቢ መታወቂያ ያስፈልጋል",
|
||||
"provider.custom.error.providerID.format": "ትንሽ ሆሄያትን፣ ቁጥሮችን፣ ሰረዞችን ወይም የስር ምልክቶችን ተጠቀም",
|
||||
"provider.custom.error.providerID.exists": "ያ የአቅራቢ መታወቂያ አስቀድሞ አለ",
|
||||
"provider.custom.error.name.required": "የማሳያ ስም ያስፈልጋል",
|
||||
"provider.custom.error.baseURL.required": "መሠረት URL ያስፈልጋል",
|
||||
"provider.custom.error.baseURL.format": "በhttp:// ወይም https://] መጀመር አለበት",
|
||||
"provider.custom.error.required": "የሚያስፈልግ",
|
||||
"provider.custom.error.duplicate": "የተባዛ",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} ተቋርጧል",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} ሞዴሎች ከአሁን በኋላ አይገኙም።",
|
||||
"model.tag.free": "ነጻ",
|
||||
"model.tag.latest": "የቅርብ",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "ጽሑፍ",
|
||||
"model.input.image": "ምስል",
|
||||
"model.input.audio": "ድምጽ",
|
||||
"model.input.video": "ቪዲዮ",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "ይፈቅዳል፡ {{inputs}}",
|
||||
"model.tooltip.context.label": "አውድ",
|
||||
"model.tooltip.inputs": "ግቤቶች",
|
||||
"model.tooltip.model": "ሞዴል",
|
||||
"model.tooltip.provider": "አቅራቢ",
|
||||
"model.tooltip.reasoning": "ምክንያት",
|
||||
"model.tooltip.reasoning.allowed": "ማመዛዘን ይፈቅዳል",
|
||||
"model.tooltip.reasoning.none": "ምክንያት የለም",
|
||||
"model.tooltip.context": "የአውድ ገደብ {{limit}}",
|
||||
"common.search.placeholder": "ፍለጋ",
|
||||
"common.goBack": "ወደ ኋላ ዳስስ",
|
||||
"common.goForward": "ወደ ፊት ዳስስ",
|
||||
"common.loading": "በመጫን ላይ",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "ሰርዝ",
|
||||
"common.clear": "ግልጽ",
|
||||
"common.open": "ክፍት",
|
||||
"common.connect": "አገናኝ",
|
||||
"common.disconnect": "ግንኙነቱን አቋርጥ",
|
||||
"common.continue": "ቀጥል",
|
||||
"common.submit": "አስረክብ",
|
||||
"common.save": "አስቀምጥ",
|
||||
"common.saving": "በማስቀመጥ ላይ...",
|
||||
"common.default": "ነባሪ",
|
||||
"common.attachment": "አባሪ",
|
||||
"prompt.placeholder.shell": "የሼል ትዕዛዝ አስገባ... {{example}}",
|
||||
"prompt.placeholder.normal": 'ማንኛውንም ነገር ይጠይቁ... "{{example}}"',
|
||||
"prompt.placeholder.simple": "ማንኛውንም ነገር ይጠይቁ...",
|
||||
"prompt.placeholder.summarizeComments": "አስተያየቶችን ማጠቃለል…",
|
||||
"prompt.placeholder.summarizeComment": "አስተያየቱን ማጠቃለል…",
|
||||
"prompt.mode.shell": "ሼል",
|
||||
"prompt.mode.normal": "ፕሮምፕት",
|
||||
"prompt.mode.shell.exit": "esc ለመውጣት",
|
||||
"session.child.promptDisabled": "ንዑስ ክፍለ ጊዜዎች ሊጠየቁ አይችሉም።",
|
||||
"session.child.backToParent": "ወደ ዋናው ክፍለ ጊዜ ተመለስ",
|
||||
"prompt.example.1": "TODOን በኮድ ቤዝ ውስጥ ያስተካክሉ",
|
||||
"prompt.example.2": "የዚህ ፕሮጀክት የቴክኖሎጂ ቁልል ምንድን ነው?",
|
||||
"prompt.example.3": "የተበላሹ ሙከራዎችን ያስተካክሉ",
|
||||
"prompt.example.4": "ማረጋገጫ እንዴት እንደሚሰራ ያብራሩ",
|
||||
"prompt.example.5": "የደህንነት ተጋላጭነቶችን ፈልግ እና ያስተካክሉ",
|
||||
"prompt.example.6": "ለተጠቃሚው አገልግሎት የክፍል ሙከራዎችን ያክሉ",
|
||||
"prompt.example.7": "ይህን ተግባር የበለጠ እንዲነበብ አስተካክል",
|
||||
"prompt.example.8": "ይህ ስህተት ምን ማለት ነው?",
|
||||
"prompt.example.9": "ይህን ችግር እንዳስተካክል እርዳኝ",
|
||||
"prompt.example.10": "APIሰነድ ፍጠር",
|
||||
"prompt.example.11": "ዳታቤዝ መጠይቆችን ያመቻቹ",
|
||||
"prompt.example.12": "የግቤት ማረጋገጫን አክል",
|
||||
"prompt.example.13": "አዲስ አካል ለ...",
|
||||
"prompt.example.14": "ይህን ፕሮጀክት እንዴት ማሰማራት እችላለሁ?",
|
||||
"prompt.example.15": "ለምርጥ ልምዶች የእኔን ኮድ ይገምግሙ",
|
||||
"prompt.example.16": "የስህተት አያያዝን ወደዚህ ተግባር ያክሉ",
|
||||
"prompt.example.17": "ይህንን regex pattern ያብራሩ",
|
||||
"prompt.example.18": "ይህንን ወደ TypeScript ቀይር",
|
||||
"prompt.example.19": "መመዝገብን በኮድ ቤዝ ውስጥ አክል",
|
||||
"prompt.example.20": "ምን ዓይነት ጥገኛዎች ጊዜ ያለፈባቸው ናቸው?",
|
||||
"prompt.example.21": "የስደት ስክሪፕት እንድጽፍ እርዳኝ",
|
||||
"prompt.example.22": "ለዚህ የመጨረሻ ነጥብ መሸጎጫ ተግባራዊ ያድርጉ",
|
||||
"prompt.example.23": "ወደዚህ ዝርዝር ገፅ ጨምር",
|
||||
"prompt.example.24": "CLI ትዕዛዝ ፍጠር ለ...",
|
||||
"prompt.example.25": "አካባቢያዊ ተለዋዋጮች እዚህ እንዴት ይሰራሉ?",
|
||||
"prompt.popover.emptyResults": "ምንም ተዛማጅ ውጤቶች የሉም",
|
||||
"prompt.popover.emptyCommands": "ምንም ተዛማጅ ትዕዛዞች የሉም",
|
||||
"prompt.dropzone.label": "ምስሎችን፣ ፒዲኤፎችን ወይም የጽሑፍ ፋይሎችን እዚህ ጣል",
|
||||
"prompt.dropzone.file.label": "ወደ @mention ፋይል ጣል",
|
||||
"prompt.slash.badge.custom": "ብጁ",
|
||||
"prompt.slash.badge.skill": "ችሎታ",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "ንቁ",
|
||||
"prompt.context.includeActiveFile": "ገቢር ፋይልን ያካትቱ",
|
||||
"prompt.context.removeActiveFile": "ገቢር ፋይልን ከአውድ አስወግድ",
|
||||
"prompt.context.removeFile": "ፋይሉን ከአውድ አስወግድ",
|
||||
"prompt.action.attachFile": "ፋይሎችን አክል",
|
||||
"prompt.menu.addImagesAndFiles": "ፋይሎችን አክል እና ተጨማሪ",
|
||||
"prompt.menu.imagesAndFiles": "ምስሎች እና ፋይሎች",
|
||||
"prompt.menu.commands": "ትዕዛዞች",
|
||||
"prompt.menu.context": "አውድ",
|
||||
"prompt.menu.shellCommand": "የሼል ትዕዛዝ",
|
||||
"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": "ፕሮምፕት ከመላክዎ በፊት ወኪል እና ሞዴል ይምረጡ።",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Git worktree መፍጠር አልተሳካም",
|
||||
"prompt.toast.sessionCreateFailed.title": "ክፍለ ጊዜ መፍጠር አልተሳካም",
|
||||
"prompt.toast.shellSendFailed.title": "የሼል ትዕዛዝ መላክ አልተሳካም",
|
||||
"prompt.toast.commandSendFailed.title": "ትዕዛዝ መላክ አልተሳካም",
|
||||
"prompt.toast.promptSendFailed.title": "ፕሮምፕት መላክ አልተሳካም",
|
||||
"prompt.toast.promptSendFailed.description": "ክፍለ ጊዜን ሰርስሮ ማውጣት አልተቻለም",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{enabled}} ከ{{total}} ነቅቷል",
|
||||
"dialog.mcp.empty": "ምንም MCPs አልተዋቀረም",
|
||||
"dialog.lsp.empty": "LSPs በራስ-ሰር ከፋይል አይነቶች የተገኘ",
|
||||
"dialog.plugins.empty": "plugins በ opencode.json የተዋቀሩ",
|
||||
"mcp.status.connected": "የተገናኘ",
|
||||
"mcp.status.failed": "ያልተሳካ",
|
||||
"mcp.status.needs_auth": "auth ያስፈልገዋል",
|
||||
"mcp.status.disabled": "የተሰናከለ",
|
||||
"mcp.auth.clickToAuthenticate": "ለማረጋገጥ ጠቅ ያድርጉ",
|
||||
"dialog.fork.empty": "አዲስ ቅርንጫፍ ለመፍጠር መልዕክት የለም",
|
||||
"dialog.directory.search.placeholder": "የአቃፊዎችን ፍለጋ",
|
||||
"dialog.directory.empty": "ምንም አቃፊዎች አልተገኙም",
|
||||
"dialog.directory.action.selectFile": "ፋይል ምረጥ",
|
||||
"dialog.directory.action.selectFolder": "አቃፊን ምረጥ",
|
||||
"dialog.directory.root": "ሥር",
|
||||
"dialog.directory.parent": "ወላጅ",
|
||||
"dialog.directory.readError": "ይህን አቃፊ ማንበብ አልተቻለም",
|
||||
"app.server.unreachable": "{{server}}ን መድረስ አልተቻለም",
|
||||
"app.server.retrying": "በራስ ሰር እንደገና በመሞከር ላይ...",
|
||||
"app.server.otherServers": "ሌሎች አገልጋዮች",
|
||||
"dialog.server.title": "አገልጋዮች",
|
||||
"dialog.server.description": "ይህ መተግበሪያ ከየትኛው OpenCode አገልጋይ ጋር እንደሚገናኝ ይቀይሩ።",
|
||||
"dialog.server.search.placeholder": "የፍለጋ አገልጋዮች",
|
||||
"dialog.server.empty": "ገና ምንም አገልጋይ የለም",
|
||||
"dialog.server.add.title": "አገልጋይ አክል",
|
||||
"dialog.server.add.url": "የአገልጋይ አድራሻ",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "ከአገልጋይ ጋር መገናኘት አልተቻለም",
|
||||
"dialog.server.add.checking": "በማጣራት ላይ...",
|
||||
"dialog.server.add.button": "አገልጋይ አክል",
|
||||
"dialog.server.add.name": "የአገልጋይ ስም (አማራጭ)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "የተጠቃሚ ስም (አማራጭ)",
|
||||
"dialog.server.add.usernamePlaceholder": "የተጠቃሚ ስም",
|
||||
"dialog.server.add.password": "የይለፍ ቃል (አማራጭ)",
|
||||
"dialog.server.add.passwordPlaceholder": "የይለፍ ቃል",
|
||||
"dialog.server.edit.title": "አገልጋይ አርትዕ",
|
||||
"dialog.server.default.title": "ነባሪ አገልጋይ",
|
||||
"dialog.server.default.description": "አካባቢያዊ አገልጋይን ከመጀመር ይልቅ ከዚህ አገልጋይ ጋር ይገናኙ። እንደገና መጀመር ያስፈልገዋል።",
|
||||
"dialog.server.default.none": "ምንም አገልጋይ አልተመረጠም",
|
||||
"dialog.server.default.set": "የአሁኑን አገልጋይ እንደ ነባሪ አዘጋጅ",
|
||||
"dialog.server.default.clear": "ግልጽ",
|
||||
"dialog.server.action.remove": "አገልጋይ አስወግድ",
|
||||
"dialog.server.menu.edit": "አርትዕ",
|
||||
"dialog.server.menu.default": "ነባሪ አዘጋጅ",
|
||||
"dialog.server.menu.defaultRemove": "ነባሪውን አስወግድ",
|
||||
"dialog.server.menu.delete": "ሰርዝ",
|
||||
"dialog.server.current": "የአሁኑ አገልጋይ",
|
||||
"dialog.server.status.default": "ነባሪ",
|
||||
"wsl.server.add": "WSL አገልጋይ አክል",
|
||||
"wsl.server.addShort": "WSL አክል",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL አገልጋይ",
|
||||
"wsl.server.retryStart": "እንደገና ጀምር",
|
||||
"wsl.server.updating": "በማዘመን ላይ...",
|
||||
"wsl.onboarding.step.distro": "distro ምረጥ",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "መፈተሽ WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows WSLን መጫኑን ለመጨረስ ዳግም መጀመር ያስፈልገዋል።",
|
||||
"wsl.onboarding.ready": "WSL ዝግጁ ነው።",
|
||||
"wsl.onboarding.required": "WSLለመቀጠል ያስፈልጋል።",
|
||||
"wsl.onboarding.checkingDistros": "Distros በመፈተሽ ላይ...",
|
||||
"wsl.onboarding.installingDistro": "በመጫን ላይ {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "መፈተሽ {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "የዝርዝር ዲስትሮስ...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} ዝግጁ ነው።",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ገና አልተጫነም።",
|
||||
"wsl.onboarding.openDistroOnce": "ማዋቀሩን ለመጨረስ {{distro}} አንድ ጊዜ ይክፈቱ።",
|
||||
"wsl.onboarding.finishingDistro": "ማዋቀሩን ለ{{distro}} በማጠናቀቅ ላይ።",
|
||||
"wsl.onboarding.pickDistro": "distro ይምረጡ ወይም ከታች ይጫኑ።",
|
||||
"wsl.onboarding.checkingOpencode": "መፈተሽ OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "OpenCodeን በ{{distro}} ውስጥ በመፈተሽ ላይ...",
|
||||
"wsl.onboarding.updatingOpencode": "በማዘመን ላይ OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "በማዘመን ላይ OpenCode በ{{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "አዘምን OpenCode በ{{distro}}።",
|
||||
"wsl.onboarding.updateOpencode": "አዘምን OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode በ{{distro}} ውስጥ ዝግጁ ነው።",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode ዝግጁ ነው።",
|
||||
"wsl.onboarding.installOpencodeIn": "መጫን OpenCode በ{{distro}} ውስጥ።",
|
||||
"wsl.onboarding.installOpencode": "ጫን OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "መጀመሪያ ዳይስትሮ ይምረጡ።",
|
||||
"wsl.onboarding.loadFailed": "WSL ሁኔታን መጫን አልተሳካም።",
|
||||
"wsl.onboarding.loading": "በመጫን ላይ...",
|
||||
"wsl.onboarding.installedDistros": "የተጫነው distros",
|
||||
"wsl.onboarding.checkAgain": "እንደገና ያረጋግጡ",
|
||||
"wsl.onboarding.distroStatus.ready": "ዝግጁ",
|
||||
"wsl.onboarding.distroStatus.checking": "በማጣራት ላይ...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCodeአልተጫነም",
|
||||
"wsl.onboarding.distroStatus.missingTools": "የጠፋ bash፣ curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "የማይደገፍ · WSL 2] ተጠቀም",
|
||||
"wsl.onboarding.needAnotherDistro": "ሌላ አስተላላፊ ይፈልጋሉ?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Linux ስርጭት ከWSL ካታሎግ ጫን",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSLአልተጫነም",
|
||||
"wsl.onboarding.wslNotInstalled.description": "WSL (Windowsንዑስ ስርዓት ለLinux) OpenCode WSL አገልጋይ ከመጨመራቸው በፊት ያስፈልጋል",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL አይገኝም",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode በዚህ ማሽን ላይ WSLን ማረጋገጥ አልቻለም።",
|
||||
"wsl.onboarding.installWsl": "ጫን WSL",
|
||||
"wsl.onboarding.windowsRestartRequired": "WSLን መጫኑን ለመጨረስ Windows እንደገና ያስጀምሩ እና ከዚያ እንደገና ይክፈቱ OpenCode።",
|
||||
"wsl.onboarding.next": "ቀጣይ",
|
||||
"wsl.onboarding.refresh": "አድስ",
|
||||
"wsl.onboarding.allDistrosAdded": "ሁሉም የተጫኑ ዳይስትሮዎች ቀድሞ ተጨምረዋል።",
|
||||
"wsl.onboarding.noDistros": "ገና ምንም distros አልተገኘም።",
|
||||
"wsl.onboarding.install": "ጫን",
|
||||
"wsl.onboarding.installing": "በመጫን ላይ...",
|
||||
"wsl.onboarding.installDistro": "distro ጫን",
|
||||
"wsl.onboarding.searchDistros": "የፍለጋ distros",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 ያስፈልጋል።",
|
||||
"wsl.onboarding.toolsRequired": "ይህ ዲስትሮ bash እና curl ያስፈልገዋል።",
|
||||
"wsl.onboarding.openTerminal": "ተርሚናል ክፈት",
|
||||
"wsl.onboarding.path": "መንገድ፡ {{path}}",
|
||||
"wsl.onboarding.notFound": "አልተገኘም",
|
||||
"wsl.onboarding.version": "ስሪት፡ {{version}}",
|
||||
"wsl.onboarding.unknown": "ያልታወቀ",
|
||||
"wsl.onboarding.desktopVersion": "ዴስክቶፕ {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "የተጫነው ሥሪት ከዴስክቶፕ መተግበሪያ ሥሪት ጋር አይዛመድም።",
|
||||
"wsl.onboarding.adding": "በማከል ላይ...",
|
||||
"help.tabs.toast.ariaLabel": "ትሮችን በማስተዋወቅ ላይ። ስራዎን እና ንቁ ክፍለ ጊዜዎን በትሮች ያደራጁ",
|
||||
"help.tabs.toast.dismiss": "የትርኮችን መረጃ አሰናብት",
|
||||
"help.tabs.title": "ትሮችን በማስተዋወቅ ላይ",
|
||||
"help.tabs.description": "ሥራዎን እና ንቁ ክፍለ ጊዜዎችን በትሮች ያደራጁ",
|
||||
"help.tabs.date": "ሐምሌ 14",
|
||||
"help.tabs.introduction": "OpenCode ዴስክቶፕ አሁን በትሮች ዙሪያ ነው የተሰራው።",
|
||||
"help.tabs.sessions":
|
||||
"አዲስ ክፍለ ጊዜ በትሩ ውስጥ ጀምር፣ወይም ከማንኛቸውም ፕሮጄክቶችህ ነባር ክፍለ ጊዜ ክፈት። አዲስ ነገር ሲጀምሩ አዲስ ትር ይክፈቱ፣ እና ሲጨርሱ ይዝጉት።",
|
||||
"help.tabs.organize": "ትንሽ ትሮችን መክፈት ንቁ ክፍለ ጊዜዎችን ማደራጀት ቀላል ያደርገዋል። ትሮችን በአካባቢያቸው ለማቆየት ካሰቡ የማይረሳ ነገርን እንደገና ይሰይሙ።",
|
||||
"help.tabs.home": "ሁሉንም ክፍለ ጊዜዎችዎን እና ፕሮጀክቶችዎን በአዲሱ የመነሻ ማያ ገጽ ላይ ያገኛሉ። ክፍለ ጊዜ መምረጥ በትር ውስጥ ይከፍታል።",
|
||||
"help.tabs.persistence": "መተግበሪያውን እንደገና ሲከፍቱት ትሮችዎ አሁንም ክፍት ናቸው።",
|
||||
"help.tabs.worktrees":
|
||||
"አዲሱ ንድፍ እስካሁን Git Worktreesን አይደግፍም፣ በቅርቡ ይመጣል። ስለዚህ የቀደመውን አቀማመጥ በመጠቀም መቀጠል ከፈለግክ በቅንብሮች ውስጥ በአቀማመጦች መካከል መቀያየር ትችላለህ። አዲሱ አቀማመጥ በጥቂት ሳምንታት ውስጥ ዘላቂ እንደሚሆን ብቻ ያስታውሱ።",
|
||||
"server.row.noUsername": "የተጠቃሚ ስም የለም",
|
||||
"dialog.project.edit.title": "ፕሮጀክት አርትዕ",
|
||||
"dialog.project.edit.name": "ስም",
|
||||
"dialog.project.edit.icon": "አዶ",
|
||||
"dialog.project.edit.icon.alt": "የፕሮጀክት አዶ",
|
||||
"dialog.project.edit.icon.hint": "ምስሉን ጠቅ ያድርጉ ወይም ይጎትቱ",
|
||||
"dialog.project.edit.icon.recommended": "የሚመከር፡ 128x128 ፒክስል",
|
||||
"dialog.project.edit.color": "ቀለም",
|
||||
"dialog.project.edit.color.select": "{{color}} ቀለም ይምረጡ",
|
||||
"dialog.project.edit.worktree.startup": "የworkspace ማስጀመሪያ ስክሪፕት",
|
||||
"dialog.project.edit.worktree.startup.description": "አዲስ workspace (Git worktree) ከፈጠረ በኋላ ይሰራል",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "ለምሳሌ፡. ጥቅል",
|
||||
"dialog.releaseNotes.action.getStarted": "ጀምር",
|
||||
"dialog.releaseNotes.action.next": "ቀጣይ",
|
||||
"dialog.releaseNotes.action.hideFuture": "እነዚህን ወደፊት አታሳይ",
|
||||
"dialog.releaseNotes.media.alt": "የልቀት ቅድመ እይታ",
|
||||
"context.breakdown.title": "የአውድ ዝርዝር መግለጫ",
|
||||
"context.breakdown.note": 'የግቤት ቶከኖች ግምታዊ ብልሽት። "ሌላ" የመሳሪያ ትርጓሜዎችን እና ከላይ ያለውን ያካትታል።',
|
||||
"context.breakdown.system": "ስርዓት",
|
||||
"context.breakdown.user": "ተጠቃሚ",
|
||||
"context.breakdown.assistant": "ረዳት",
|
||||
"context.breakdown.tool": "የመሳሪያ ጥሪዎች",
|
||||
"context.breakdown.other": "ሌላ",
|
||||
"context.systemPrompt.title": "የስርዓት ፕሮምፕት",
|
||||
"context.rawMessages.title": "ጥሬ መልዕክቶች",
|
||||
"context.export.session": "ክፍለ ጊዜን ወደ ውጭ ላክ",
|
||||
"context.stats.session": "ክፍለ ጊዜ",
|
||||
"context.stats.messages": "መልእክቶች",
|
||||
"context.stats.provider": "አቅራቢ",
|
||||
"context.stats.model": "ሞዴል",
|
||||
"context.stats.limit": "የአውድ ገደብ",
|
||||
"context.stats.totalTokens": "አጠቃላይ ቶከኖች",
|
||||
"context.stats.usage": "አጠቃቀም",
|
||||
"context.stats.inputTokens": "የግቤት ቶከኖች",
|
||||
"context.stats.outputTokens": "የውጤት ቶከኖች",
|
||||
"context.stats.reasoningTokens": "የማመዛዘኛ ቶከኖች",
|
||||
"context.stats.cacheTokens": "መሸጎጫ ቶከኖች (ማንበብ/መጻፍ)",
|
||||
"context.stats.userMessages": "የተጠቃሚ መልዕክቶች",
|
||||
"context.stats.assistantMessages": "የረዳት መልዕክቶች",
|
||||
"context.stats.totalCost": "ጠቅላላ ወጪ",
|
||||
"context.stats.sessionCreated": "ክፍለ ተፈጠረ",
|
||||
"context.stats.lastActivity": "የመጨረሻው እንቅስቃሴ",
|
||||
"context.usage.tokens": "ቶከኖች",
|
||||
"context.usage.usage": "አጠቃቀም",
|
||||
"context.usage.cost": "ዋጋ",
|
||||
"context.usage.clickToView": "አውድ ለማየት ጠቅ ያድርጉ",
|
||||
"context.usage.view": "የአውድ አጠቃቀምን ይመልከቱ",
|
||||
"language.en": "እንግሊዝኛ",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "Deutsch",
|
||||
"language.es": "Español",
|
||||
"language.fr": "Français",
|
||||
"language.da": "ዳንስክ",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Polski",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "Português (ብራሲል)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "ቋንቋ",
|
||||
"toast.language.description": "ወደ {{language}} ተቀይሯል",
|
||||
"toast.theme.title": "ገጽታ ተቀይሯል",
|
||||
"toast.scheme.title": "የቀለም ንድፍ",
|
||||
"toast.workspace.enabled.title": "Workspace-ዎች ነቅተዋል",
|
||||
"toast.workspace.enabled.description": "በርካታ Git worktree-ዎች በጎን አሞሌ ላይ አሁን ይታያሉ",
|
||||
"toast.workspace.disabled.title": "Workspace-ዎች ተሰናክለዋል",
|
||||
"toast.workspace.disabled.description": "በጎን አሞሌው ላይ ዋናው Git worktree ብቻ ነው የሚታየው",
|
||||
"toast.permissions.autoaccept.on.title": "በራስ-ተቀባይ ፍቃዶች",
|
||||
"toast.permissions.autoaccept.on.description": "የፈቃድ ጥያቄዎች በራስ-ሰር ይጸድቃሉ",
|
||||
"toast.permissions.autoaccept.off.title": "በራስ-መቀበል ፍቃዶችን አቁሟል",
|
||||
"toast.permissions.autoaccept.off.description": "የፈቃድ ጥያቄዎች መጽደቅ ያስፈልጋቸዋል",
|
||||
"toast.model.none.title": "ምንም ሞዴል አልተመረጠም",
|
||||
"toast.model.none.description": "ይህን ክፍለ ጊዜ ለማጠቃለል አቅራቢን ያገናኙ",
|
||||
"toast.file.loadFailed.title": "ፋይሉን መጫን አልተቻለም",
|
||||
"toast.file.listFailed.title": "ፋይሎችን መዘርዘር አልተሳካም",
|
||||
"toast.context.noLineSelection.title": "የመስመር ምርጫ የለም",
|
||||
"toast.context.noLineSelection.description": "በመጀመሪያ በፋይል ትር ውስጥ የመስመር ክልልን ይምረጡ።",
|
||||
"toast.session.share.copyFailed.title": "URLን ወደ ቅንጥብ ሰሌዳ መቅዳት አልተሳካም",
|
||||
"toast.session.share.success.title": "ክፍል የተጋራ",
|
||||
"toast.session.share.success.description": "አጋራ URL ወደ ቅንጥብ ሰሌዳ ተቀድቷል!",
|
||||
"toast.session.share.failed.title": "ክፍለ ጊዜን ማጋራት አልተሳካም",
|
||||
"toast.session.share.failed.description": "ክፍለ-ጊዜውን በማጋራት ላይ ስህተት ተፈጥሯል",
|
||||
"toast.session.unshare.success.title": "ክፍል ያልተጋራ",
|
||||
"toast.session.unshare.success.description": "ክፍለ ጊዜው በተሳካ ሁኔታ አልተጋራም!",
|
||||
"toast.session.unshare.failed.title": "ክፍለ ጊዜን አለማጋራት",
|
||||
"toast.session.unshare.failed.description": "ክፍለ-ጊዜውን በማላቀቅ ላይ ሳለ ስህተት ተፈጥሯል",
|
||||
"toast.session.export.success.title": "ክፍለ ጊዜ ወደ ውጭ የተላከ",
|
||||
"toast.session.export.success.description": "የተቀመጠው ክፍለ ጊዜ ወደ {{filename}}",
|
||||
"toast.session.export.failed.title": "ክፍለ-ጊዜን ወደ ውጭ መላክ አልተሳካም",
|
||||
"toast.session.export.failed.description": "ክፍለ-ጊዜውን ወደ ውጭ በመላክ ላይ ስህተት ተፈጥሯል",
|
||||
"toast.session.listFailed.title": "ለ{{project}}] ክፍለ-ጊዜዎችን መጫን አልተሳካም",
|
||||
"toast.project.reloadFailed.title": "{{project}}ን እንደገና መጫን አልተሳካም",
|
||||
"toast.update.title": "ዝማኔ አለ",
|
||||
"toast.update.description": "አዲሱ የOpenCode ({{version}}) ስሪት አሁን ለመጫን አለ።",
|
||||
"toast.update.action.installRestart": "ጫን እና እንደገና አስጀምር",
|
||||
"toast.update.action.notYet": "ገና",
|
||||
"error.page.title": "የሆነ ችግር ተፈጥሯል",
|
||||
"error.page.description": "መተግበሪያውን በመጫን ላይ ሳለ ስህተት ተፈጥሯል።",
|
||||
"error.page.description.localServerStartup": "የአገር ውስጥ አገልጋይ ሲጀመር ስህተት ተፈጥሯል።",
|
||||
"error.page.details.label": "የስህተት ዝርዝሮች",
|
||||
"error.page.action.restart": "ዳግም አስጀምር",
|
||||
"error.page.action.report": "ስህተት ሪፖርት አድርግ",
|
||||
"error.page.action.reported": "ስህተት ሪፖርት ተደርጓል",
|
||||
"error.page.action.exportLogs": "የመላክ ምዝግብ ማስታወሻዎች",
|
||||
"error.page.action.checking": "በማጣራት ላይ...",
|
||||
"error.page.action.checkUpdates": "ዝማኔዎችን ይመልከቱ",
|
||||
"error.page.action.updateTo": "ወደ {{version}} አዘምን",
|
||||
"error.page.circular": "[Circular]",
|
||||
"error.page.report.prefix": "እባክዎ ይህንን ስህተት ለOpenCode ቡድን ያሳውቁ",
|
||||
"error.page.report.discord": "በDiscord",
|
||||
"error.page.version": "ስሪት፡ {{version}}",
|
||||
"error.dev.rootNotFound": "ሥርወ አካል አልተገኘም። ወደ የእርስዎ index.html ማከልን ረስተዋል? ወይም የመታወቂያ ባህሪው የተሳሳተ ፊደል ተጽፎ ሊሆን ይችላል?",
|
||||
"error.serverSync.connectFailed": "ከአገልጋይ ጋር መገናኘት አልተቻለም። `{{url}}` ላይ የሚሰራ አገልጋይ አለ?",
|
||||
"error.serverSDK.noServerAvailable": "ምንም አገልጋይ የለም",
|
||||
"error.serverSDK.serverNotAvailable": "አገልጋይ አይገኝም",
|
||||
"error.childStore.persistedCacheCreateFailed": "ቋሚ መሸጎጫ መፍጠር አልተሳካም",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "የቀጠለ የፕሮጀክት ዲበ ውሂብ መፍጠር አልተሳካም",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "የቀጠለ የፕሮጀክት አዶ መፍጠር አልተሳካም",
|
||||
"error.childStore.storeCreateFailed": "መደብር መፍጠር አልተሳካም",
|
||||
"directory.error.invalidUrl": "ልክ ያልሆነ ማውጫ በURL ውስጥ።",
|
||||
"error.chain.unknown": "ያልታወቀ ስህተት",
|
||||
"error.server.invalidConfiguration": "ልክ ያልሆነ ውቅር",
|
||||
"error.chain.causedBy": "የተከሰተ፡",
|
||||
"error.chain.apiError": "APIስህተት",
|
||||
"error.chain.status": "ሁኔታ፡ {{status}}",
|
||||
"error.chain.retryable": "እንደገና ሊሞከር የሚችል፡ {{retryable}}",
|
||||
"error.chain.responseBody": "የምላሽ አካል፡\n{{body}}",
|
||||
"error.chain.didYouMean": "{{suggestions}}] ማለትዎ ነው",
|
||||
"error.chain.modelNotFound": "ሞዴል አልተገኘም፦ {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "የእርስዎን ውቅር (opencode.json) አቅራቢ/የሞዴል ስሞችን ያረጋግጡ",
|
||||
"error.chain.mcpFailed": 'MCP አገልጋይ "{{name}}" አልተሳካም። ማስታወሻ፣ OpenCode እስካሁን MCP ማረጋገጫን አይደግፍም።',
|
||||
"error.chain.providerAuthFailed": "የአቅራቢውን ማረጋገጥ አልተሳካም ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed": 'አቅራቢውን "{{provider}}" ማስጀመር አልተሳካም። ምስክርነቶችን እና ውቅረትን ያረጋግጡ።',
|
||||
"error.chain.configJsonInvalid": "ማዋቀር በ{{path}} ላይ የሚሰራ አይደለም JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "ማዋቀር በ{{path}} ላይ የሚሰራ አይደለም JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'ማውጫ "{{dir}}" በ{{path}} ውስጥ የሚሰራ አይደለም። ማውጫውን ወደ «{{suggestion}}» እንደገና ይሰይሙት ወይም ያስወግዱት። ይህ የተለመደ የትየባ ነው።',
|
||||
"error.chain.configFrontmatterError": "የፊት ጉዳይን በ{{path}} ውስጥ መተንተን አልተሳካም፦\n{{message}}",
|
||||
"error.chain.configInvalid": "ማዋቀር ፋይል በ{{path}} ልክ ያልሆነ ነው",
|
||||
"error.chain.configInvalidWithMessage": "የማዋቀር ፋይል በ{{path}} ልክ ያልሆነ ነው፡ {{message}}",
|
||||
"notification.permission.title": "ፈቃድ ያስፈልጋል",
|
||||
"notification.permission.description": "{{sessionTitle}} በ{{projectName}} ውስጥ ፈቃድ ያስፈልገዋል",
|
||||
"notification.question.title": "ጥያቄ",
|
||||
"notification.question.description": "{{sessionTitle}} በ{{projectName}} ውስጥ ጥያቄ አለው",
|
||||
"notification.action.goToSession": "ወደ ክፍለ-ጊዜ ሂድ",
|
||||
"notification.session.responseReady.title": "ምላሽ ዝግጁ",
|
||||
"notification.session.error.title": "የክፍለ ጊዜ ስህተት",
|
||||
"notification.session.error.fallbackDescription": "ስህተት ተፈጥሯል",
|
||||
"home.recentProjects": "የቅርብ ጊዜ ፕሮጀክቶች",
|
||||
"home.empty.title": "ምንም የቅርብ ጊዜ ፕሮጀክቶች የሉም",
|
||||
"home.empty.description": "የአካባቢውን ፕሮጀክት በመክፈት ጀምር",
|
||||
"home.title": "ቤት",
|
||||
"home.projects": "ፕሮጀክቶች",
|
||||
"home.project.add": "ፕሮጀክት አክል",
|
||||
"home.recentlyClosed": "በቅርቡ የተዘጋ",
|
||||
"home.server.collapse": "የአገልጋይ ፕሮጀክቶችን ሰብስብ",
|
||||
"home.server.expand": "የአገልጋይ ፕሮጀክቶችን ዘርጋ",
|
||||
"home.sessions.search.placeholder": "የፍለጋ ክፍለ-ጊዜዎች",
|
||||
"home.sessions.search.placeholder.scoped": "የፍለጋ ክፍለ ጊዜዎች በ{{scope}}",
|
||||
"home.sessions.search.sessions": "ክፍለ-ጊዜዎች",
|
||||
"home.sessions.search.noResults": "ለ{{query}}] ምንም ክፍለ ጊዜዎች አልተገኙም",
|
||||
"home.sessions.empty": "አሁን ምንም እዚህ የለም",
|
||||
"home.sessions.empty.description": "ለመጀመር ክፍለ ጊዜ ፍጠር",
|
||||
"home.sessions.group.today": "ዛሬ",
|
||||
"home.sessions.group.yesterday": "ትላንት",
|
||||
"home.sessions.group.older": "የቆየ",
|
||||
"home.providerTip": "Claude፣ GPT፣ Gemini፣ ወዘተ] ጨምሮ ሌሎች ሞዴሎችን ለመጠቀም ከ75+ አቅራቢዎች ጋር ይገናኙ",
|
||||
"session.tab.session": "ክፍለ ጊዜ",
|
||||
"session.tab.review": "ግምገማ",
|
||||
"session.tab.context": "አውድ",
|
||||
"session.tab.unknown": "ያልታወቀ ክፍለ ጊዜ",
|
||||
"session.panel.reviewAndFiles": "ግምገማ እና ፋይሎች",
|
||||
"session.error.notFound": "ይህ ክፍለ ጊዜ ሊገኝ አይችልም",
|
||||
"session.error.notFound.description": "ይህ ትር የሚያመለክተው በዚህ አገልጋይ ላይ ወደሌለው ክፍለ ጊዜ ነው።",
|
||||
"session.error.notFound.closeTab": "ትርን ዝጋ",
|
||||
"session.error.serverConnection": "ከዚህ አገልጋይ ጋር መገናኘት አልተቻለም",
|
||||
"session.review.filesChanged": "የተቀየሩ ፋይሎች {{count}}",
|
||||
"session.review.change.one": "ለውጥ",
|
||||
"session.review.change.other": "ለውጦች",
|
||||
"session.review.loadingChanges": "ለውጦችን በመጫን ላይ...",
|
||||
"session.review.empty": "በዚህ ክፍለ ጊዜ ምንም ለውጦች የሉም",
|
||||
"session.review.noVcs": "ምንም Git የስሪት ቁጥጥር ስርዓት አልተገኘም፣ ለውጦች አይታዩም",
|
||||
"session.review.noVcs.createGit.title": "Git ማከማቻ ፍጠር",
|
||||
"session.review.noVcs.createGit.description": "በዚህ ፕሮጀክት ላይ ለውጦችን ይከታተሉ፣ ይገምግሙ እና ይቀልብሱ",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git ማከማቻ በመፍጠር ላይ...",
|
||||
"session.review.noVcs.createGit.action": "Git ማከማቻ ፍጠር",
|
||||
"session.review.noSnapshot": "የቅጽበታዊ ገጽ እይታን መከታተል በውቅረት ውስጥ ተሰናክሏል፣ስለዚህ የክፍለ-ጊዜ ለውጦች አይገኙም",
|
||||
"session.review.noChanges": "ምንም ለውጦች የሉም",
|
||||
"session.review.noUncommittedChanges": "ገና ምንም ያልተደረጉ ለውጦች የሉም",
|
||||
"session.review.noBranchChanges": "ገና ምንም ቅርንጫፍ ምንም ለውጥ የለም",
|
||||
"session.files.selectToOpen": "ለመክፈት ፋይል ምረጥ",
|
||||
"session.files.all": "ሁሉም ፋይሎች",
|
||||
"session.files.empty": "ምንም ፋይሎች",
|
||||
"session.files.binaryContent": "ሁለትዮሽ ፋይል (ይዘት ሊታይ አይችልም)",
|
||||
"session.messages.renderEarlier": "ቀደምት መልዕክቶችን ይስሩ",
|
||||
"session.messages.loadingEarlier": "የቀድሞ መልዕክቶችን በመጫን ላይ...",
|
||||
"session.messages.loadEarlier": "የቀድሞ መልዕክቶችን ጫን",
|
||||
"session.messages.loading": "መልእክቶችን በመጫን ላይ...",
|
||||
"session.messages.jumpToLatest": "ወደ የቅርብ ጊዜ ዝለል",
|
||||
"session.context.addToContext": "{{selection}}ን ወደ አውድ አክል",
|
||||
"session.todo.title": "የሚደረጉ ሥራዎች",
|
||||
"session.todo.collapse": "ስብስብ",
|
||||
"session.todo.expand": "ዘርጋ",
|
||||
"session.todo.progress": "{{done}} ከ{{total}} ስራ ተጠናቋል",
|
||||
"session.question.progress": "{{current}} ከ{{total}} ጥያቄዎች",
|
||||
"session.question.minimize": "ጥያቄን አሳንስ",
|
||||
"session.question.restore": "ጥያቄን ወደነበረበት መልስ",
|
||||
"session.question.pending.one": "{{count}} በመጠባበቅ ላይ ያለ ጥያቄ",
|
||||
"session.question.pending.other": "{{count}} በመጠባበቅ ላይ ያሉ ጥያቄዎች",
|
||||
"session.followupDock.summary.one": "{{count}}የተሰለፈ መልእክት",
|
||||
"session.followupDock.summary.other": "{{count}}የተሰለፉ መልዕክቶች",
|
||||
"session.followupDock.sendNow": "አሁን ላክ",
|
||||
"session.followupDock.edit": "አርትዕ",
|
||||
"session.followupDock.collapse": "የተሰለፉ መልዕክቶችን ሰብስብ",
|
||||
"session.followupDock.expand": "የተሰለፉ መልዕክቶችን ዘርጋ",
|
||||
"session.revertDock.summary.one": "{{count}}የተመለሰ መልዕክት",
|
||||
"session.revertDock.summary.other": "{{count}}የተመለሱ መልዕክቶች",
|
||||
"session.revertDock.collapse": "የተመለሱ መልዕክቶችን ሰብስብ",
|
||||
"session.revertDock.expand": "የተጠቀለሉ መልዕክቶችን ዘርጋ",
|
||||
"session.revertDock.restore": "መልዕክት እነበረበት መልስ",
|
||||
"session.new.title": "ማንኛውም ነገር ገንባ",
|
||||
"session.new.project.new": "አዲስ ፕሮጀክት",
|
||||
"session.new.project.search": "የፍለጋ ፕሮጀክቶች",
|
||||
"session.new.project.add": "ፕሮጀክት አክል",
|
||||
"session.new.worktree.main": "ዋና ቅርንጫፍ",
|
||||
"session.new.worktree.mainWithBranch": "ዋና ቅርንጫፍ ({{branch}})",
|
||||
"session.new.worktree.create": "አዲስ Git worktree ፍጠር",
|
||||
"session.new.workspace.runIn": "ክፍለ ጊዜን በ ውስጥ ያሂዱ",
|
||||
"session.new.workspace.triggerLocal": "አካባቢ",
|
||||
"session.new.workspace.local": "አካባቢያዊ ማከማቻ",
|
||||
"session.new.workspace.existing": "Workspace…",
|
||||
"session.new.git.none": "ያለ Git",
|
||||
"session.new.lastModified": "መጨረሻ የተሻሻለው",
|
||||
"session.header.search.placeholder": "ፍለጋ {{project}}",
|
||||
"session.header.searchFiles": "ፋይሎችን ይፈልጉ",
|
||||
"session.header.openIn": "ክፍት ውስጥ",
|
||||
"session.header.open.action": "ክፈት {{app}}",
|
||||
"session.header.open.ariaLabel": "በ{{app}} ውስጥ ክፈት",
|
||||
"session.header.open.menu": "ክፍት አማራጮች",
|
||||
"session.header.open.copyPath": "መገልበጥ መንገድ",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "ፋይል አሳሽ",
|
||||
"session.header.open.fileManager": "ፋይል አስተዳዳሪ",
|
||||
"session.header.reveal.finder": "መገለጥ በFinder",
|
||||
"session.header.reveal.fileExplorer": "ፋይል ኤክስፕሎረር ውስጥ ይገለጣል",
|
||||
"session.header.reveal.containingFolder": "አቃፊን የያዘ ክፈት",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "ተርሚናል",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "ሁኔታ",
|
||||
"status.popover.ariaLabel": "የአገልጋይ ውቅሮች",
|
||||
"status.popover.tab.servers": "አገልጋዮች",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "ፕለጊኖች",
|
||||
"status.popover.action.manageServers": "አገልጋዮችን አስተዳድር",
|
||||
"session.share.popover.title": "በድር ላይ አትም",
|
||||
"session.share.popover.description.shared": "ይህ ክፍለ ጊዜ በድር ላይ ይፋዊ ነው። አገናኙ ላለው ለማንኛውም ሰው ተደራሽ ነው።",
|
||||
"session.share.popover.description.unshared": "ክፍለጊዜውን በይፋ በድሩ ላይ አጋራ። አገናኙ ላለው ለማንኛውም ሰው ተደራሽ ይሆናል።",
|
||||
"session.share.action.share": "አጋራ",
|
||||
"session.share.action.publish": "አትም",
|
||||
"session.share.action.publishing": "በህትመት ላይ...",
|
||||
"session.share.action.unpublish": "አትታተም",
|
||||
"session.share.action.unpublishing": "ያለመታተም...",
|
||||
"session.share.action.view": "እይታ",
|
||||
"session.share.copy.copied": "የተገለበጠ",
|
||||
"session.share.copy.copyLink": "መገልበጥ አገናኝ",
|
||||
"lsp.tooltip.none": "ምንም LSP አገልጋዮች",
|
||||
"lsp.label.connected": "{{count}}LSP",
|
||||
"prompt.loading": "ፕሮምፕትን በመጫን ላይ...",
|
||||
"terminal.loading": "በመጫን ላይ ተርሚናል...",
|
||||
"terminal.title": "ተርሚናል",
|
||||
"terminal.title.numbered": "ተርሚናል {{number}}",
|
||||
"terminal.close": "ተርሚናል ዝጋ",
|
||||
"terminal.connectionLost.title": "ግንኙነቱ ተቋርጧል",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket ባልተለመደ ሁኔታ ተዘግቷል፡ {{code}}",
|
||||
"terminal.connectionLost.description": "የተርሚናል ግንኙነቱ ተቋርጧል። ይህ አገልጋዩ እንደገና ሲጀምር ሊከሰት ይችላል።",
|
||||
"terminal.connectTicket.csrfError": "PTY ትኬት ማገናኘት በመነሻ ወይም በCSRF ቼኮች ውድቅ ተደርጓል። አገልጋዩን ይመልከቱ CORS ውቅር",
|
||||
"terminal.connectTicket.statusError": "PTY የግንኙነት ቲኬት ከ{{status}}] ጋር አልተሳካም",
|
||||
"titlebar.update": "አዘምን",
|
||||
"titlebar.updateVersion": "አዘምን {{version}}",
|
||||
"common.closeTab": "ትርፉን ዝጋ",
|
||||
"common.dismiss": "አሰናብት",
|
||||
"common.moreCountSuffix": " (+{{count}} ተጨማሪ)",
|
||||
"common.requestFailed": "ጥያቄው አልተሳካም",
|
||||
"common.moreOptions": "ተጨማሪ አማራጮች",
|
||||
"common.learnMore": "ተጨማሪ ለመረዳት",
|
||||
"common.rename": "ዳግም ስም",
|
||||
"common.export": "ላክ",
|
||||
"common.reset": "ዳግም አስጀምር",
|
||||
"common.archive": "በማህደር አስቀምጥ",
|
||||
"common.delete": "ሰርዝ",
|
||||
"common.close": "ዝጋ",
|
||||
"common.edit": "አርትዕ",
|
||||
"common.loadMore": "ተጨማሪ ጫን",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Space",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "End",
|
||||
"common.key.pageUp": "Page Up",
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "ያልታወቀ",
|
||||
"common.time.justNow": "አሁን",
|
||||
"common.time.minutesAgo.short": "{{count}}ደቂቃ በፊት",
|
||||
"common.time.hoursAgo.short": "{{count}}ከሰዓ በፊት",
|
||||
"common.time.daysAgo.short": "ከ{{count}} ቀን በፊት",
|
||||
"sidebar.menu.toggle": "ሜኑ ቀያይር",
|
||||
"sidebar.nav.projectsAndSessions": "ፕሮጀክቶች እና ክፍለ-ጊዜዎች",
|
||||
"sidebar.settings": "ቅንጅቶች",
|
||||
"sidebar.help": "እገዛ",
|
||||
"sidebar.workspaces.enable": "Workspace-ዎችን አንቃ",
|
||||
"sidebar.workspaces.disable": "Workspace-ዎችን አሰናክል",
|
||||
"sidebar.gettingStarted.title": "መጀመር",
|
||||
"sidebar.gettingStarted.line1": "OpenCode ነፃ ሞዴሎችን ያካትታል ስለዚህ ወዲያውኑ መጀመር ይችላሉ።",
|
||||
"sidebar.gettingStarted.line2": "ሞዴሎችን ለመጠቀም ማንኛውንም አቅራቢ ያገናኙ፣ Inc. Claude፣ GPT፣ Gemini ወዘተ.",
|
||||
"sidebar.project.recentSessions": "የቅርብ ጊዜ ክፍለ ጊዜዎች",
|
||||
"sidebar.project.viewAllSessions": "ሁሉንም ክፍለ ጊዜዎች ይመልከቱ",
|
||||
"sidebar.project.clearNotifications": "ማሳወቂያዎችን አጽዳ",
|
||||
"sidebar.empty.title": "ምንም ክፍት ፕሮጀክቶች የሉም",
|
||||
"sidebar.empty.description": "ለመጀመር ፕሮጀክት ይክፈቱ",
|
||||
"debugBar.ariaLabel": "የልማት አፈጻጸም ምርመራዎች",
|
||||
"debugBar.na": "n/a",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip": "የክፍለ ጊዜ ገጽን የነካው የመጨረሻው የተጠናቀቀ የመንገድ ሽግግር፣ ከራውተር ጅምር እስከ ከተረጋጋ በኋላ የመጀመሪያው ስዕል ድረስ የሚለካ።",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "ባለፉት 5 ሰከንዶች ውስጥ የሚሽከረከሩ ክፈፎች በሰከንድ።",
|
||||
"debugBar.frame.label": "ፍሬም",
|
||||
"debugBar.frame.tip": "ባለፉት 5 ሰከንዶች ውስጥ በጣም መጥፎው የፍሬም ጊዜ።",
|
||||
"debugBar.jank.label": "መንቀጥቀጥ",
|
||||
"debugBar.jank.tip": "ባለፉት 5 ሰከንዶች ውስጥ ከ32ሚሴ በላይ ክፈፎች።",
|
||||
"debugBar.long.label": "ረጅም",
|
||||
"debugBar.long.tip": "የታገደው ጊዜ እና የረዥም ጊዜ ብዛት በመጨረሻዎቹ 5 ሰከንዶች ውስጥ። ከፍተኛው ተግባር፡ {{max}}",
|
||||
"debugBar.delay.label": "መዘግየት",
|
||||
"debugBar.delay.tip": "በመጨረሻዎቹ 5 ሰከንዶች ውስጥ በጣም መጥፎው የታየ የግቤት መዘግየት።",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip": "በመጨረሻዎቹ 5 ሰከንዶች ውስጥ ግምታዊ የግንኙነቶች ቆይታ። ይህ INP የሚመስል ነው፣ ይፋዊው የድር ቪታሎች INP አይደለም።",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "ለአሁኑ መተግበሪያ የህይወት ዘመን ድምር አቀማመጥ ለውጥ።",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "ያገለገለ JS ክምር vs ክምር ገደብ። Chromium ብቻ።",
|
||||
"debugBar.mem.tip": "ያገለገለ JS ክምር vs ክምር ገደብ። {{used}} ከ {{limit}}",
|
||||
"debugBar.focus.label": "ትኩረት",
|
||||
"debugBar.focus.tip": "በሁሉም በይነተገናኝ አካላት ላይ የማተኮር ቅጦችን አስገድድ",
|
||||
"debugBar.focus.on": "በርቷል",
|
||||
"debugBar.focus.off": "ጠፍቷል",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "የተመረጠውን ቋንቋ ሳይቀይሩ ሙሉውን የመተግበሪያ አቀማመጥ አቅጣጫ ያስገድዱ",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode ዴስክቶፕ",
|
||||
"settings.section.desktop": "ዴስክቶፕ",
|
||||
"settings.section.server": "አገልጋይ",
|
||||
"settings.tab.general": "አጠቃላይ",
|
||||
"settings.tab.shortcuts": "አቋራጮች",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL ውህደት",
|
||||
"settings.desktop.wsl.description": "የOpenCode አገልጋዩን በWSL ውስጥ በWindows ላይ ያሂዱ።",
|
||||
"settings.general.section.appearance": "መልክ",
|
||||
"settings.general.section.advanced": "የላቀ",
|
||||
"settings.general.section.notifications": "የስርዓት ማሳወቂያዎች",
|
||||
"settings.general.section.updates": "ዝማኔዎች",
|
||||
"settings.general.section.sounds": "የድምጽ ተጽዕኖዎች",
|
||||
"settings.general.section.feed": "ምግብ",
|
||||
"settings.general.section.display": "ማሳያ",
|
||||
"settings.general.row.language.title": "ቋንቋ",
|
||||
"settings.general.row.language.description": "የማሳያ ቋንቋውን ለOpenCode",
|
||||
"settings.general.row.shell.title": "ተርሚናል ሼል",
|
||||
"settings.general.row.shell.description": "ሼል በተርሚናል እና ወኪል መሳሪያዎች ጥቅም ላይ የዋለ",
|
||||
"settings.general.row.shell.autoDefault": "ራስ-ሰር (ነባሪ)",
|
||||
"settings.general.row.shell.terminalOnly": "ተርሚናል ብቻ",
|
||||
"settings.general.row.appearance.title": "መልክ",
|
||||
"settings.general.row.appearance.description": "OpenCodeበመሣሪያዎ ላይ እንዴት እንደሚታይ ብጁ ያድርጉ",
|
||||
"settings.general.row.colorScheme.title": "የቀለም ንድፍ",
|
||||
"settings.general.row.colorScheme.description": "OpenCode ስርዓቱን፣ ብርሃንን ወይም ጨለማውን ገጽታ የሚከተል እንደሆነ ምረጥ",
|
||||
"settings.general.row.theme.title": "ገጽታ",
|
||||
"settings.general.row.theme.description": "OpenCode ገጽታ እንዴት እንደሆነ አብጅ።",
|
||||
"settings.general.row.font.title": "የኮድ ቅርጸ ቁምፊ",
|
||||
"settings.general.row.font.description": "በኮድ ብሎኮች ውስጥ ጥቅም ላይ የዋለውን ቅርጸ-ቁምፊ አብጅ",
|
||||
"settings.general.row.terminalFont.title": "ተርሚናል ፊደል",
|
||||
"settings.general.row.terminalFont.description": "በተርሚናል ውስጥ ጥቅም ላይ የዋለውን ቅርጸ-ቁምፊ አብጅ",
|
||||
"settings.general.row.uiFont.title": "የUI ቅርጸ ቁምፊ",
|
||||
"settings.general.row.uiFont.description": "በመገናኛው ሁሉ ጥቅም ላይ የዋለውን ቅርጸ-ቁምፊ አብጅ",
|
||||
"settings.general.row.followup.title": "መከታተያ ባህሪ",
|
||||
"settings.general.row.followup.description": "ክትትል የሚጠይቅ ከሆነ ወዲያውኑ ይመራ እንደሆነ ይምረጡ ወይም በሰልፍ ይጠብቁ",
|
||||
"settings.general.row.followup.option.queue": "ወረፋ",
|
||||
"settings.general.row.followup.option.steer": "መሪ",
|
||||
"settings.general.row.showFileTree.title": "ፋይል ዛፍ",
|
||||
"settings.general.row.showFileTree.description": "የፋይል ዛፍ ፓነልን በክፍሎች ውስጥ አሳይ",
|
||||
"settings.general.row.showNavigation.title": "የአሰሳ መቆጣጠሪያዎች",
|
||||
"settings.general.row.showNavigation.description": "በዴስክቶፕ የርዕስ አሞሌ ላይ የኋላ እና ወደፊት ቁልፎችን አሳይ",
|
||||
"settings.general.row.showSearch.title": "የትእዛዝ ቤተ-ስዕል",
|
||||
"settings.general.row.showSearch.description": "በርዕስ አሞሌው ላይ የፍለጋ እና የትእዛዝ ቤተ-ስዕል ቁልፍ አሳይ",
|
||||
"settings.general.row.showTerminal.title": "ተርሚናል",
|
||||
"settings.general.row.showTerminal.description": "የተርሚናል አዝራሩን በዴስክቶፕ ርዕስ አሞሌ ላይ አሳይ",
|
||||
"settings.general.row.showStatus.title": "የአገልጋይ ሁኔታ",
|
||||
"settings.general.row.showStatus.description": "የአገልጋዩን ሁኔታ ቁልፍ በርዕስ አሞሌው ላይ አሳይ",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "የታችኛው ዳሰሳ",
|
||||
"settings.general.row.mobileTitlebarBottom.description": "የርዕስ አሞሌውን እና የክፍለ ጊዜ ትሮችን በማያ ገጹ ግርጌ በሞባይል ላይ ያስቀምጡ",
|
||||
"settings.general.row.showCustomAgents.title": "ወኪሉን አሳይ",
|
||||
"settings.general.row.showCustomAgents.description": "በአቀናባሪው ውስጥ ባሉ ወኪሎች መካከል ይቀያይሩ። ሲደበቅ፣ ለግንባታ ወኪል ነባሪዎች ይሆናሉ።",
|
||||
"settings.general.row.reasoningSummaries.title": "የምክንያት ማጠቃለያዎችን አሳይ",
|
||||
"settings.general.row.reasoningSummaries.description": "የሞዴል ማመዛዘኛ ማጠቃለያዎችን በጊዜ መስመር አሳይ",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "የሼል መሣሪያ ክፍሎችን ዘርጋ",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "የሼል መሣሪያ ክፍሎችን በጊዜ መስመር በነባሪነት ያሳዩ",
|
||||
"settings.general.row.editToolPartsExpanded.title": "የመሳሪያ ክፍሎችን ዘርጋ",
|
||||
"settings.general.row.editToolPartsExpanded.description": "በጊዜ መስመር በነባሪነት የተዘረጉትን የመሳሪያ ክፍሎችን አርትዕ፣ ጻፍ እና ጠጋኝ አሳይ",
|
||||
"settings.general.row.newInterface.title": "አዲስ አቀማመጥ",
|
||||
"settings.general.row.newInterface.badge": "አዲስ",
|
||||
"settings.general.row.newInterface.description": "አዲሱን ትሮች እና የቤት አቀማመጥ ይጠቀሙ። ለተወሰነ ጊዜ በአቀማመጦች መካከል ይቀያይሩ።",
|
||||
"settings.general.row.newInterfaceNotice.title": "አሁን አዲስ አቀማመጥ እየተጠቀሙ ነው",
|
||||
"settings.general.row.newInterfaceNotice.description": "የቀድሞው አቀማመጥ ከአሁን በኋላ አይገኝም",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "አሰናብት",
|
||||
"settings.general.row.pinchZoom.title": "ለማጉላት ቆንጥጦ",
|
||||
"settings.general.row.pinchZoom.description": "ትራክፓድ መቆንጠጥ እና የCtrl-ማሸብለል ምልክቶችን እንዲያሳድጉ ፍቀድ",
|
||||
"settings.general.row.wayland.title": "ቤተኛ Wayland",
|
||||
"settings.general.row.wayland.description": "X11መመለስን በWayland አሰናክል። እንደገና መጀመር ያስፈልገዋል።",
|
||||
"settings.general.row.wayland.tooltip": "በLinux ላይ በተደባለቀ የማደስ-ተመን ማሳያዎች፣ ቤተኛ Wayland የበለጠ የተረጋጋ ሊሆን ይችላል።",
|
||||
"settings.general.row.releaseNotes.title": "የልቀት ማስታወሻዎች",
|
||||
"settings.general.row.releaseNotes.description": "ከዝማኔዎች በኋላ ምን አዲስ ብቅ-ባዮችን አሳይ",
|
||||
"settings.updates.row.startup.title": "በጅማሬ ላይ ዝማኔዎችን ይመልከቱ",
|
||||
"settings.updates.row.startup.description": "OpenCode ሲጀመር ዝማኔዎችን በራስ-ሰር ያረጋግጡ",
|
||||
"settings.updates.row.check.title": "ዝማኔዎችን ይመልከቱ",
|
||||
"settings.updates.row.check.description": "ዝማኔዎችን ለማግኘት በእጅ ያረጋግጡ እና ካለ ይጫኑ",
|
||||
"settings.updates.action.checkNow": "አሁን ያረጋግጡ",
|
||||
"settings.updates.action.checking": "በማጣራት ላይ...",
|
||||
"settings.updates.action.downloading": "በማውረድ ላይ...",
|
||||
"settings.updates.action.installing": "በመጫን ላይ...",
|
||||
"settings.updates.toast.latest.title": "ዘመኑን ጠብቀዋል",
|
||||
"settings.updates.toast.latest.description": "የቅርብ ጊዜውን የOpenCode እትም እያሄዱ ነው።",
|
||||
"sound.option.none": "ምንም",
|
||||
"sound.option.alert01": "Alert 01",
|
||||
"sound.option.alert02": "Alert 02",
|
||||
"sound.option.alert03": "Alert 03",
|
||||
"sound.option.alert04": "Alert 04",
|
||||
"sound.option.alert05": "Alert 05",
|
||||
"sound.option.alert06": "Alert 06",
|
||||
"sound.option.alert07": "Alert 07",
|
||||
"sound.option.alert08": "Alert 08",
|
||||
"sound.option.alert09": "Alert 09",
|
||||
"sound.option.alert10": "Alert 10",
|
||||
"sound.option.bipbop01": "Bip-bop 01",
|
||||
"sound.option.bipbop02": "Bip-bop 02",
|
||||
"sound.option.bipbop03": "Bip-bop 03",
|
||||
"sound.option.bipbop04": "Bip-bop 04",
|
||||
"sound.option.bipbop05": "Bip-bop 05",
|
||||
"sound.option.bipbop06": "Bip-bop 06",
|
||||
"sound.option.bipbop07": "Bip-bop 07",
|
||||
"sound.option.bipbop08": "Bip-bop 08",
|
||||
"sound.option.bipbop09": "Bip-bop 09",
|
||||
"sound.option.bipbop10": "Bip-bop 10",
|
||||
"sound.option.staplebops01": "Staplebops 01",
|
||||
"sound.option.staplebops02": "Staplebops 02",
|
||||
"sound.option.staplebops03": "Staplebops 03",
|
||||
"sound.option.staplebops04": "Staplebops 04",
|
||||
"sound.option.staplebops05": "Staplebops 05",
|
||||
"sound.option.staplebops06": "Staplebops 06",
|
||||
"sound.option.staplebops07": "Staplebops 07",
|
||||
"sound.option.nope01": "Nope 01",
|
||||
"sound.option.nope02": "Nope 02",
|
||||
"sound.option.nope03": "Nope 03",
|
||||
"sound.option.nope04": "Nope 04",
|
||||
"sound.option.nope05": "Nope 05",
|
||||
"sound.option.nope06": "Nope 06",
|
||||
"sound.option.nope07": "Nope 07",
|
||||
"sound.option.nope08": "Nope 08",
|
||||
"sound.option.nope09": "Nope 09",
|
||||
"sound.option.nope10": "Nope 10",
|
||||
"sound.option.nope11": "Nope 11",
|
||||
"sound.option.nope12": "Nope 12",
|
||||
"sound.option.yup01": "Yup 01",
|
||||
"sound.option.yup02": "Yup 02",
|
||||
"sound.option.yup03": "Yup 03",
|
||||
"sound.option.yup04": "Yup 04",
|
||||
"sound.option.yup05": "Yup 05",
|
||||
"sound.option.yup06": "Yup 06",
|
||||
"settings.general.notifications.agent.title": "ወኪል",
|
||||
"settings.general.notifications.agent.description": "ወኪሉ ሲጠናቀቅ ወይም ትኩረት ሲፈልግ የስርዓት ማሳወቂያን አሳይ",
|
||||
"settings.general.notifications.permissions.title": "ፍቃዶች",
|
||||
"settings.general.notifications.permissions.description": "ፍቃድ በሚያስፈልግበት ጊዜ የስርዓት ማሳወቂያን አሳይ",
|
||||
"settings.general.notifications.errors.title": "ስህተት",
|
||||
"settings.general.notifications.errors.description": "ስህተት ሲከሰት የስርዓት ማሳወቂያን አሳይ",
|
||||
"settings.general.sounds.agent.title": "ወኪል",
|
||||
"settings.general.sounds.agent.description": "ወኪሉ ሲጠናቀቅ ወይም ትኩረት ሲፈልግ ድምጽ ያጫውቱ",
|
||||
"settings.general.sounds.permissions.title": "ፍቃዶች",
|
||||
"settings.general.sounds.permissions.description": "ፍቃድ በሚያስፈልግበት ጊዜ ድምጽ ያጫውቱ",
|
||||
"settings.general.sounds.errors.title": "ስህተት",
|
||||
"settings.general.sounds.errors.description": "ስህተት ሲከሰት ድምጽ ያጫውቱ",
|
||||
"settings.shortcuts.title": "የቁልፍ ሰሌዳ አቋራጮች",
|
||||
"settings.shortcuts.reset.button": "ወደ ነባሪዎች ዳግም አስጀምር",
|
||||
"settings.shortcuts.reset.toast.title": "የአቋራጮች ዳግም ማስጀመር",
|
||||
"settings.shortcuts.reset.toast.description": "የቁልፍ ሰሌዳ አቋራጮች ወደ ነባሪዎች ተቀይረዋል።",
|
||||
"settings.shortcuts.conflict.title": "አቋራጭ አስቀድሞ ጥቅም ላይ ውሏል",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} አስቀድሞ ለ{{titles}} ተመድቧል።",
|
||||
"settings.shortcuts.unassigned": "ያልተመደበ",
|
||||
"settings.shortcuts.pressKeys": "ቁልፎችን ይጫኑ",
|
||||
"settings.shortcuts.search.placeholder": "አቋራጮችን ይፈልጉ",
|
||||
"settings.shortcuts.search.empty": "ምንም አቋራጮች አልተገኙም",
|
||||
"settings.shortcuts.group.general": "አጠቃላይ",
|
||||
"settings.shortcuts.group.session": "ክፍለ ጊዜ",
|
||||
"settings.shortcuts.group.navigation": "ዳሰሳ",
|
||||
"settings.shortcuts.group.modelAndAgent": "ሞዴል እና ወኪል",
|
||||
"settings.shortcuts.group.terminal": "ተርሚናል",
|
||||
"settings.shortcuts.group.prompt": "ፕሮምፕት",
|
||||
"settings.providers.title": "አቅራቢዎች",
|
||||
"settings.providers.description": "የአቅራቢዎች መቼቶች እዚህ ሊዋቀሩ ይችላሉ።",
|
||||
"settings.providers.section.connected": "የተገናኙ አቅራቢዎች",
|
||||
"settings.providers.connected.empty": "ምንም የተገናኙ አቅራቢዎች የሉም",
|
||||
"settings.providers.connected.environmentDescription": "ከአካባቢዎ ተለዋዋጮች ጋር የተገናኘ",
|
||||
"settings.providers.section.popular": "ታዋቂ አቅራቢዎች",
|
||||
"settings.providers.custom.description": "OpenAI-ተኳሃኝ አቅራቢን በመሠረት URL ያክሉ።",
|
||||
"settings.providers.tag.environment": "አካባቢ",
|
||||
"settings.providers.tag.config": "ውቅር",
|
||||
"settings.providers.tag.custom": "ብጁ",
|
||||
"settings.providers.tag.other": "ሌላ",
|
||||
"settings.models.title": "ሞዴሎች",
|
||||
"settings.models.description": "የሞዴል ቅንብሮች እዚህ ሊዋቀሩ ይችላሉ።",
|
||||
"settings.agents.title": "ወኪሎች",
|
||||
"settings.agents.description": "የወኪል ቅንብሮች እዚህ ይዋቀራሉ።",
|
||||
"settings.commands.title": "ትዕዛዞች",
|
||||
"settings.commands.description": "የትእዛዝ ቅንብሮች እዚህ ይዋቀራሉ።",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "MCPቅንብሮች እዚህ ይዋቀራሉ።",
|
||||
"settings.permissions.title": "ፍቃዶች",
|
||||
"settings.permissions.description": "አገልጋዩ በነባሪነት ምን ዓይነት መሳሪያዎችን መጠቀም እንደሚችል ይቆጣጠሩ።",
|
||||
"settings.permissions.section.tools": "መሳሪያዎች",
|
||||
"settings.permissions.toast.updateFailed.title": "ፍቃዶችን ማዘመን አልተሳካም",
|
||||
"settings.permissions.action.allow": "ፍቀድ",
|
||||
"settings.permissions.action.ask": "ጥያቄ",
|
||||
"settings.permissions.action.deny": "ክድ",
|
||||
"settings.permissions.tool.read.title": "አንብብ",
|
||||
"settings.permissions.tool.read.description": "ፋይል ማንበብ (ከፋይል ዱካ ጋር ይዛመዳል)",
|
||||
"settings.permissions.tool.edit.title": "አርትዕ",
|
||||
"settings.permissions.tool.edit.description": "አርትዖቶችን፣ ጽሁፎችን እና መጠገኛዎችን ጨምሮ ፋይሎችን ይቀይሩ",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "የግሎብ ቅጦችን በመጠቀም ፋይሎችን አዛምድ",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "መደበኛ መግለጫዎችን በመጠቀም የፋይል ይዘቶችን ይፈልጉ",
|
||||
"settings.permissions.tool.list.title": "ዝርዝር",
|
||||
"settings.permissions.tool.list.description": "ፋይሎችን በማውጫ ውስጥ ይዘርዝሩ",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "የሼል ትዕዛዞችን አሂድ",
|
||||
"settings.permissions.tool.task.title": "ተግባር",
|
||||
"settings.permissions.tool.task.description": "ንዑስ ወኪሎችን አስጀምር",
|
||||
"settings.permissions.tool.skill.title": "ክህሎት",
|
||||
"settings.permissions.tool.skill.description": "ችሎታ በስም ጫን",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "የቋንቋ አገልጋይ ጥያቄዎችን አሂድ",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "የሚከናወኑ ሥራዎችን ዝርዝር ያዘምኑ",
|
||||
"settings.permissions.tool.webfetch.title": "ድር ፈልጎ",
|
||||
"settings.permissions.tool.webfetch.description": "ይዘትን ከURL አምጣ",
|
||||
"settings.permissions.tool.websearch.title": "ድር ፍለጋ",
|
||||
"settings.permissions.tool.websearch.description": "ድሩን ይፈልጉ",
|
||||
"settings.permissions.tool.external_directory.title": "የውጭ ማውጫ",
|
||||
"settings.permissions.tool.external_directory.description": "ፋይሎችን ከፕሮጀክት ማውጫ ውጭ ይድረሱ",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "ተመሳሳይ ግብዓት ያላቸው ተደጋጋሚ የመሳሪያ ጥሪዎችን አግኝ",
|
||||
"session.delete.failed.title": "ክፍለ-ጊዜን መሰረዝ አልተሳካም",
|
||||
"session.delete.title": "ክፍለ ጊዜን ሰርዝ",
|
||||
"session.delete.confirm": 'ክፍለ-ጊዜውን ይሰርዙ"{{name}}?',
|
||||
"session.delete.button": "ክፍለ ጊዜን ሰርዝ",
|
||||
"workspace.new": "አዲስ workspace",
|
||||
"workspace.type.local": "አካባቢ",
|
||||
"workspace.type.sandbox": "ማጠሪያ",
|
||||
"workspace.create.failed.title": "Workspace መፍጠር አልተሳካም",
|
||||
"workspace.delete.failed.title": "Workspace መሰረዝ አልተሳካም",
|
||||
"workspace.resetting.title": "Workspaceን ዳግም በማስጀመር ላይ",
|
||||
"workspace.resetting.description": "ይህ አንድ ደቂቃ ሊወስድ ይችላል።",
|
||||
"workspace.reset.failed.title": "Workspaceን ዳግም ማስጀመር አልተቻለም",
|
||||
"workspace.reset.success.title": "Workspace ዳግም ተጀምሯል",
|
||||
"workspace.reset.success.description": "Workspace አሁን ከነባሪው ቅርንጫፍ ጋር ይዛመዳል።",
|
||||
"workspace.error.stillPreparing": "Workspace አሁንም በመዘጋጀት ላይ ነው",
|
||||
"workspace.status.checking": "ያልተጣመሩ ለውጦችን በመፈተሽ ላይ...",
|
||||
"workspace.status.error": "git ሁኔታን ማረጋገጥ አልተቻለም።",
|
||||
"workspace.status.clean": "ምንም ያልተዋሃዱ ለውጦች አልተገኙም።",
|
||||
"workspace.status.dirty": "ያልተቀላቀሉ ለውጦች በዚህ workspace ላይ ተገኝተዋል።",
|
||||
"workspace.delete.title": "Workspaceን ሰርዝ",
|
||||
"workspace.delete.confirm": 'Workspace "{{name}}" ይሰረዝ?',
|
||||
"workspace.delete.button": "Workspaceን ሰርዝ",
|
||||
"workspace.reset.title": "Workspaceን ዳግም አስጀምር",
|
||||
"workspace.reset.confirm": 'Workspace "{{name}}" ዳግም ይጀመር?',
|
||||
"workspace.reset.button": "Workspaceን ዳግም አስጀምር",
|
||||
"workspace.reset.archived.none": "ምንም ንቁ ክፍለ ጊዜዎች አይቀመጡም።",
|
||||
"workspace.reset.archived.one": "1 ክፍለ ጊዜ በማህደር ይቀመጣል።",
|
||||
"workspace.reset.archived.many": "{{count}} ክፍለ-ጊዜዎች በማህደር ይቀመጣሉ።",
|
||||
"workspace.reset.note": "ይህ workspaceን ከነባሪው ቅርንጫፍ ጋር እንዲመሳሰል ዳግም ያስጀምረዋል።",
|
||||
"dialog.usageExceeded.dontShowAgain": "እንደገና አታሳይ",
|
||||
}
|
||||
+245
-82
@@ -1,4 +1,102 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "ملف",
|
||||
"desktop.menu.edit": "تحرير",
|
||||
"desktop.menu.view": "عرض",
|
||||
"desktop.menu.go": "انتقال",
|
||||
"desktop.menu.window": "نافذة",
|
||||
"desktop.menu.help": "مساعدة",
|
||||
"desktop.menu.checkForUpdates": "التحقق من وجود تحديثات...",
|
||||
"desktop.menu.settings": "الإعدادات",
|
||||
"desktop.menu.reloadWebview": "إعادة تحميل Webview",
|
||||
"desktop.menu.restart": "إعادة التشغيل",
|
||||
"desktop.menu.exportLogs": "تصدير السجلات...",
|
||||
"desktop.menu.newSession": "جلسة جديدة",
|
||||
"desktop.menu.openProject": "فتح مشروع...",
|
||||
"desktop.menu.newWindow": "نافذة جديدة",
|
||||
"desktop.menu.closeWindow": "إغلاق النافذة",
|
||||
"desktop.menu.undo": "تراجع",
|
||||
"desktop.menu.redo": "إعادة",
|
||||
"desktop.menu.cut": "قص",
|
||||
"desktop.menu.copy": "نسخ",
|
||||
"desktop.menu.paste": "لصق",
|
||||
"desktop.menu.delete": "حذف",
|
||||
"desktop.menu.selectAll": "تحديد الكل",
|
||||
"desktop.menu.toggleSidebar": "إظهار الشريط الجانبي أو إخفاؤه",
|
||||
"desktop.menu.toggleTerminal": "إظهار المحطة الطرفية أو إخفاؤها",
|
||||
"desktop.menu.toggleFileTree": "إظهار شجرة الملفات أو إخفاؤها",
|
||||
"desktop.menu.reload": "إعادة التحميل",
|
||||
"desktop.menu.toggleDeveloperTools": "إظهار أدوات المطور أو إخفاؤها",
|
||||
"desktop.menu.actualSize": "الحجم الفعلي",
|
||||
"desktop.menu.zoomIn": "تكبير",
|
||||
"desktop.menu.zoomOut": "تصغير",
|
||||
"desktop.menu.toggleFullScreen": "الدخول إلى ملء الشاشة أو الخروج منه",
|
||||
"desktop.menu.back": "رجوع",
|
||||
"desktop.menu.forward": "تقدم",
|
||||
"desktop.menu.previousSession": "الجلسة السابقة",
|
||||
"desktop.menu.nextSession": "الجلسة التالية",
|
||||
"desktop.menu.previousProject": "المشروع السابق",
|
||||
"desktop.menu.nextProject": "المشروع التالي",
|
||||
"desktop.menu.minimize": "تصغير النافذة",
|
||||
"desktop.menu.maximize": "تكبير النافذة",
|
||||
"desktop.menu.documentation": "وثائق OpenCode",
|
||||
"desktop.menu.supportForum": "منتدى الدعم",
|
||||
"desktop.menu.shareFeedback": "إرسال الملاحظات",
|
||||
"desktop.menu.reportBug": "الإبلاغ عن خلل",
|
||||
"desktop.menu.ariaLabel": "قائمة OpenCode",
|
||||
|
||||
"desktop.updater.dialog.checkFailed.message": "فشل التحقق من وجود تحديثات.",
|
||||
"desktop.updater.dialog.checkFailed.title": "خطأ في التحديث",
|
||||
"desktop.updater.dialog.upToDate.message": "لديك أحدث إصدار.",
|
||||
"desktop.updater.dialog.upToDate.title": "لا توجد تحديثات",
|
||||
"desktop.updater.dialog.ready.message": "تم تنزيل التحديث {{version}}. هل تريد إعادة التشغيل الآن؟",
|
||||
"desktop.updater.dialog.ready.title": "التحديث جاهز",
|
||||
"desktop.updater.dialog.restart": "إعادة التشغيل",
|
||||
"desktop.updater.dialog.later": "لاحقًا",
|
||||
|
||||
"desktop.recovery.action.relaunch": "إعادة تشغيل التطبيق",
|
||||
"desktop.recovery.action.exportLogs": "تصدير السجلات",
|
||||
"desktop.recovery.action.keepWaiting": "متابعة الانتظار",
|
||||
"desktop.recovery.action.quit": "إنهاء",
|
||||
"desktop.recovery.loadFailed": "فشل تحميل OpenCode",
|
||||
"desktop.recovery.terminated": "توقفت نافذة OpenCode بشكل غير متوقع",
|
||||
"desktop.recovery.unresponsive": "OpenCode لا يستجيب",
|
||||
"desktop.recovery.unresponsive.detail": "يمكنك إعادة تشغيل التطبيق أو فتح السجلات أو متابعة الانتظار.",
|
||||
"desktop.recovery.loadFailed.detail": "النافذة: {{window}}\nعنوان URL: {{url}}\nالخطأ: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "النافذة: {{window}}\nالسبب: {{reason}}\nالرمز: {{code}}",
|
||||
"desktop.recovery.unknown": "<غير معروف>",
|
||||
|
||||
"desktop.dialog.chooseFolder": "اختيار مجلد",
|
||||
"desktop.dialog.chooseFile": "اختيار ملف",
|
||||
"desktop.dialog.saveFile": "حفظ الملف",
|
||||
"desktop.dialog.files": "الملفات",
|
||||
"desktop.server.local": "الخادم المحلي",
|
||||
|
||||
"desktop.wsl.error.windowsOnly": "لا يتوفر WSL إلا على Windows",
|
||||
"desktop.wsl.error.unavailable": "WSL غير متاح",
|
||||
"desktop.wsl.error.listInstalled": "فشل سرد توزيعات WSL المثبتة",
|
||||
"desktop.wsl.error.listOnline": "فشل سرد توزيعات WSL المتاحة عبر الإنترنت",
|
||||
"desktop.wsl.error.executeDistro": "تعذر تنفيذ الأوامر في التوزيعة",
|
||||
"desktop.wsl.error.installWsl": "فشل تثبيت WSL",
|
||||
"desktop.wsl.error.installDistro": "فشل تثبيت التوزيعة: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "فشل تثبيت OpenCode",
|
||||
"desktop.wsl.error.alreadyAdded": "تمت إضافة {{distro}} بالفعل",
|
||||
"desktop.wsl.error.opencodeMissing": "opencode غير مثبت في هذه التوزيعة",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode مثبت، لكن تعذر تشغيله",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode غير مثبت في {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"اكتمل تحديث OpenCode، لكن {{distro}} ما زالت تعرض الإصدار {{installed}}؛ الإصدار المتوقع هو {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "لا يوجد إصدار",
|
||||
"desktop.wsl.error.serverExited": "خرج خادم WSL بعد بدء التشغيل (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"خرج خادم WSL قبل أن يصبح سليمًا (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout": "انتهت مهلة فحص سلامة العملية المصاحبة للتوزيعة {{distro}} بعد {{timeout}}ms",
|
||||
"desktop.wsl.error.commandTimeout": "انتهت مهلة {{command}} {{args}} بعد {{timeout}}ms",
|
||||
"desktop.wsl.error.failedPort": "فشل الحصول على المنفذ",
|
||||
|
||||
"desktop.picker.error.notSelected": "لم يحدد منتقي الملفات ملفًا",
|
||||
"desktop.picker.error.sizeLimit": "تتجاوز المرفقات المحددة الحد البالغ {{limit}} MB",
|
||||
|
||||
"command.category.suggested": "مقترح",
|
||||
"command.category.view": "عرض",
|
||||
"command.category.project": "مشروع",
|
||||
@@ -16,6 +114,7 @@ export const dict = {
|
||||
"command.category.permissions": "أذونات",
|
||||
"command.category.workspace": "مساحة عمل",
|
||||
"command.category.settings": "إعدادات",
|
||||
"command.logs.export": "تصدير السجلات",
|
||||
"theme.scheme.system": "نظام",
|
||||
"theme.scheme.light": "فاتح",
|
||||
"theme.scheme.dark": "داكن",
|
||||
@@ -59,16 +158,16 @@ export const dict = {
|
||||
"command.message.next.description": "انتقل إلى رسالة المستخدم التالية",
|
||||
"command.model.choose": "اختيار نموذج",
|
||||
"command.model.choose.description": "حدد نموذجًا مختلفًا",
|
||||
"command.mcp.toggle": "تبديل MCPs",
|
||||
"command.mcp.toggle.description": "تبديل MCPs",
|
||||
"command.mcp.toggle": "تبديل خوادم MCP",
|
||||
"command.mcp.toggle.description": "تبديل خوادم MCP",
|
||||
"command.agent.cycle": "تغيير الوكيل",
|
||||
"command.agent.cycle.description": "التبديل إلى الوكيل التالي",
|
||||
"command.agent.cycle.reverse": "تغيير الوكيل للخلف",
|
||||
"command.agent.cycle.reverse.description": "التبديل إلى الوكيل السابق",
|
||||
"command.model.variant.cycle": "تغيير جهد التفكير",
|
||||
"command.model.variant.cycle": "تغيير جهد الاستدلال",
|
||||
"command.model.variant.cycle.description": "التبديل إلى مستوى الجهد التالي",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Prompt",
|
||||
"command.prompt.mode.normal": "موجّه",
|
||||
"command.permissions.autoaccept.enable": "قبول الأذونات تلقائيًا",
|
||||
"command.permissions.autoaccept.disable": "إيقاف قبول الأذونات تلقائيًا",
|
||||
"command.workspace.toggle": "تبديل مساحات العمل",
|
||||
@@ -85,6 +184,9 @@ export const dict = {
|
||||
"command.session.share.description": "مشاركة هذه الجلسة ونسخ الرابط إلى الحافظة",
|
||||
"command.session.unshare": "إلغاء مشاركة الجلسة",
|
||||
"command.session.unshare.description": "إيقاف مشاركة هذه الجلسة",
|
||||
"command.session.export": "تصدير الجلسة",
|
||||
"command.session.export.description": "تصدير النص الكامل للجلسة بصيغة JSON",
|
||||
|
||||
"palette.search.placeholder": "البحث في الملفات والأوامر والجلسات",
|
||||
"palette.search.placeholder.home": "البحث في الأوامر والجلسات",
|
||||
"palette.empty": "لا توجد نتائج",
|
||||
@@ -94,13 +196,14 @@ export const dict = {
|
||||
"dialog.provider.empty": "لم يتم العثور على موفرين",
|
||||
"dialog.provider.group.popular": "شائع",
|
||||
"dialog.provider.group.other": "آخر",
|
||||
"dialog.provider.custom.label": "موفر مخصص متوافق مع OpenAI",
|
||||
"dialog.provider.tag.recommended": "موصى به",
|
||||
"dialog.provider.opencode.note": "نماذج مختارة تتضمن Claude و GPT و Gemini والمزيد",
|
||||
"dialog.provider.opencode.tagline": "نماذج موثوقة ومحسنة",
|
||||
"dialog.provider.opencodeGo.tagline": "اشتراك منخفض التكلفة للجميع",
|
||||
"dialog.provider.anthropic.note": "اتصل باستخدام Claude Pro/Max أو مفتاح API",
|
||||
"dialog.provider.copilot.note": "اتصل باستخدام Copilot أو مفتاح API",
|
||||
"dialog.provider.openai.note": "اتصل باستخدام ChatGPT Pro/Plus أو مفتاح API",
|
||||
"dialog.provider.anthropic.note": "وصول مباشر إلى نماذج Claude، بما فيها Pro و Max",
|
||||
"dialog.provider.copilot.note": "نماذج ذكاء اصطناعي للمساعدة في البرمجة عبر GitHub Copilot",
|
||||
"dialog.provider.openai.note": "نماذج GPT لمهام الذكاء الاصطناعي العامة السريعة والمتقدمة",
|
||||
"dialog.provider.google.note": "نماذج Gemini لاستجابات سريعة ومنظمة",
|
||||
"dialog.provider.openrouter.note": "الوصول إلى جميع النماذج المدعومة من موفر واحد",
|
||||
"dialog.provider.vercel.note": "وصول موحد إلى نماذج الذكاء الاصطناعي مع توجيه ذكي",
|
||||
@@ -112,17 +215,20 @@ export const dict = {
|
||||
"dialog.model.manage.provider.toggle": "تبديل جميع نماذج {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "نماذج مجانية مقدمة من OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "إضافة المزيد من النماذج من موفرين مشهورين",
|
||||
"dialog.model.unpaid.viewMoreProviders": "عرض أكثر من 70 موفرًا إضافيًا",
|
||||
"dialog.provider.viewAll": "عرض المزيد من الموفرين",
|
||||
"provider.connect.title": "اتصال {{provider}}",
|
||||
"provider.connect.title": "الاتصال بـ {{provider}}",
|
||||
"provider.connect.title.anthropicProMax": "تسجيل الدخول باستخدام Claude Pro/Max",
|
||||
"provider.connect.selectMethod": "حدد طريقة تسجيل الدخول لـ {{provider}}.",
|
||||
"provider.connect.method.apiKey": "مفتاح API",
|
||||
"provider.connect.method.browser": "المتصفح",
|
||||
"provider.connect.method.headless": "بدون واجهة رسومية",
|
||||
"provider.connect.status.inProgress": "جارٍ التفويض...",
|
||||
"provider.connect.status.waiting": "في انتظار التفويض...",
|
||||
"provider.connect.status.failed": "فشل التفويض: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"أدخل مفتاح واجهة برمجة تطبيقات {{provider}} الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.apiKey.label": "مفتاح واجهة برمجة تطبيقات {{provider}}",
|
||||
"أدخل مفتاح API الخاص بـ {{provider}} لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.",
|
||||
"provider.connect.apiKey.label": "مفتاح API لـ {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "مفتاح API",
|
||||
"provider.connect.apiKey.required": "مفتاح API مطلوب",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
@@ -148,6 +254,7 @@ export const dict = {
|
||||
"provider.connect.toast.connected.title": "تم توصيل {{provider}}",
|
||||
"provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.",
|
||||
"provider.custom.title": "موفر مخصص",
|
||||
"provider.custom.unavailable": "الموفرون المخصصون غير متاحين على هذا الخادم",
|
||||
"provider.custom.description.prefix": "تكوين موفر متوافق مع OpenAI. راجع ",
|
||||
"provider.custom.description.link": "وثائق تكوين الموفر",
|
||||
"provider.custom.description.suffix": ".",
|
||||
@@ -196,26 +303,26 @@ export const dict = {
|
||||
"model.input.image": "صورة",
|
||||
"model.input.audio": "صوت",
|
||||
"model.input.video": "فيديو",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.input.pdf": "PDF",
|
||||
"model.tooltip.allows": "يسمح: {{inputs}}",
|
||||
"model.tooltip.context.label": "السياق",
|
||||
"model.tooltip.inputs": "المدخلات",
|
||||
"model.tooltip.model": "النموذج",
|
||||
"model.tooltip.provider": "الموفر",
|
||||
"model.tooltip.reasoning": "الاستنتاج",
|
||||
"model.tooltip.reasoning.allowed": "يسمح بالاستنتاج",
|
||||
"model.tooltip.reasoning.none": "بدون استنتاج",
|
||||
"model.tooltip.reasoning": "الاستدلال",
|
||||
"model.tooltip.reasoning.allowed": "يدعم الاستدلال",
|
||||
"model.tooltip.reasoning.none": "بدون استدلال",
|
||||
"model.tooltip.context": "حد السياق {{limit}}",
|
||||
"common.search.placeholder": "بحث",
|
||||
"common.goBack": "رجوع",
|
||||
"common.goForward": "انتقل للأمام",
|
||||
"common.goForward": "التقدم",
|
||||
"common.loading": "جارٍ التحميل",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "إلغاء",
|
||||
"common.clear": "مسح",
|
||||
"common.connect": "اتصال",
|
||||
"common.disconnect": "قطع الاتصال",
|
||||
"common.continue": "إرسال",
|
||||
"common.continue": "متابعة",
|
||||
"common.submit": "إرسال",
|
||||
"common.save": "حفظ",
|
||||
"common.saving": "جارٍ الحفظ...",
|
||||
@@ -227,7 +334,7 @@ export const dict = {
|
||||
"prompt.placeholder.summarizeComments": "لخّص التعليقات…",
|
||||
"prompt.placeholder.summarizeComment": "لخّص التعليق…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.normal": "موجّه",
|
||||
"prompt.mode.shell.exit": "esc للخروج",
|
||||
"session.child.promptDisabled": "لا يمكن إرسال موجّهات إلى جلسات الوكلاء الفرعيين.",
|
||||
"session.child.backToParent": "العودة إلى الجلسة الرئيسية.",
|
||||
@@ -249,17 +356,17 @@ export const dict = {
|
||||
"prompt.example.16": "إضافة معالجة الأخطاء لهذه الدالة",
|
||||
"prompt.example.17": "اشرح نمط regex هذا",
|
||||
"prompt.example.18": "تحويل هذا إلى TypeScript",
|
||||
"prompt.example.19": "إضافة تسجيل الدخول (logging) في جميع أنحاء قاعدة التعليمات البرمجية",
|
||||
"prompt.example.19": "إضافة التسجيل في جميع أنحاء قاعدة التعليمات البرمجية",
|
||||
"prompt.example.20": "ما هي التبعيات القديمة؟",
|
||||
"prompt.example.21": "ساعدني في كتابة برنامج نصي للهجرة",
|
||||
"prompt.example.22": "تنفيذ التخزين المؤقت لهذه النقطة النهائية",
|
||||
"prompt.example.21": "ساعدني في كتابة برنامج نصي للترحيل",
|
||||
"prompt.example.22": "تنفيذ التخزين المؤقت لنقطة النهاية هذه",
|
||||
"prompt.example.23": "إضافة ترقيم الصفحات إلى هذه القائمة",
|
||||
"prompt.example.24": "إنشاء أمر CLI لـ...",
|
||||
"prompt.example.25": "كيف تعمل متغيرات البيئة هنا؟",
|
||||
"prompt.popover.emptyResults": "لا توجد نتائج مطابقة",
|
||||
"prompt.popover.emptyCommands": "لا توجد أوامر مطابقة",
|
||||
"prompt.dropzone.label": "أفلت الصور أو ملفات PDF أو الملفات النصية هنا",
|
||||
"prompt.dropzone.file.label": "أفلت لإشارة @ للملف",
|
||||
"prompt.dropzone.file.label": "أفلت الملف للإشارة إليه باستخدام @",
|
||||
"prompt.slash.badge.custom": "مخصص",
|
||||
"prompt.slash.badge.skill": "مهارة",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
@@ -267,7 +374,7 @@ export const dict = {
|
||||
"prompt.context.includeActiveFile": "تضمين الملف النشط",
|
||||
"prompt.context.removeActiveFile": "إزالة الملف النشط من السياق",
|
||||
"prompt.context.removeFile": "إزالة الملف من السياق",
|
||||
"prompt.action.attachFile": "إرفاق ملف",
|
||||
"prompt.action.attachFile": "إضافة ملفات",
|
||||
"prompt.menu.addImagesAndFiles": "إضافة ملفات والمزيد",
|
||||
"prompt.menu.imagesAndFiles": "الصور والملفات",
|
||||
"prompt.menu.commands": "الأوامر",
|
||||
@@ -275,8 +382,9 @@ export const dict = {
|
||||
"prompt.menu.shellCommand": "أمر shell",
|
||||
"prompt.attachment.remove": "إزالة المرفق",
|
||||
"prompt.action.send": "إرسال",
|
||||
"prompt.action.stop": "توقف",
|
||||
"prompt.action.stop": "إيقاف",
|
||||
"prompt.toast.pasteUnsupported.title": "مرفق غير مدعوم",
|
||||
"prompt.toast.attachmentDuplicate.title": "تم تحميل هذا الملف بالفعل",
|
||||
"prompt.toast.pasteUnsupported.description": "يمكن إرفاق الصور أو ملفات PDF أو الملفات النصية فقط هنا.",
|
||||
"prompt.toast.modelAgentRequired.title": "حدد وكيلاً ونموذجاً",
|
||||
"prompt.toast.modelAgentRequired.description": "اختر وكيلاً ونموذجاً قبل إرسال الموجه.",
|
||||
@@ -286,10 +394,10 @@ export const dict = {
|
||||
"prompt.toast.commandSendFailed.title": "فشل إرسال الأمر",
|
||||
"prompt.toast.promptSendFailed.title": "فشل إرسال الموجه",
|
||||
"prompt.toast.promptSendFailed.description": "تعذر استرداد الجلسة",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{enabled}} من {{total}} مفعل",
|
||||
"dialog.mcp.empty": "لم يتم تكوين MCPs",
|
||||
"dialog.lsp.empty": "تم الكشف تلقائيًا عن LSPs من أنواع الملفات",
|
||||
"dialog.mcp.title": "خوادم MCP",
|
||||
"dialog.mcp.description": "تم تفعيل {{enabled}} من أصل {{total}}",
|
||||
"dialog.mcp.empty": "لم يتم تكوين أي خوادم MCP",
|
||||
"dialog.lsp.empty": "تُكتشف خوادم LSP تلقائيًا من أنواع الملفات",
|
||||
"dialog.plugins.empty": "الإضافات المكونة في opencode.json",
|
||||
"mcp.status.connected": "متصل",
|
||||
"mcp.status.failed": "فشل",
|
||||
@@ -400,6 +508,21 @@ export const dict = {
|
||||
"wsl.onboarding.desktopVersion": "سطح المكتب {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "لا يتطابق الإصدار المثبت مع إصدار تطبيق سطح المكتب.",
|
||||
"wsl.onboarding.adding": "جارٍ الإضافة...",
|
||||
"help.tabs.toast.ariaLabel": "نقدّم علامات التبويب. نظّم عملك وجلساتك النشطة باستخدام علامات التبويب",
|
||||
"help.tabs.toast.dismiss": "إغلاق معلومات علامات التبويب",
|
||||
"help.tabs.title": "نقدّم علامات التبويب",
|
||||
"help.tabs.description": "نظّم عملك وجلساتك النشطة باستخدام علامات التبويب",
|
||||
"help.tabs.date": "14 يوليو",
|
||||
"help.tabs.introduction": "أصبح OpenCode Desktop الآن قائمًا على علامات التبويب.",
|
||||
"help.tabs.sessions":
|
||||
"ابدأ جلسة جديدة في علامة تبويب، أو افتح جلسة حالية من أي من مشاريعك. افتح علامة تبويب جديدة عندما تبدأ عملاً جديدًا، وأغلقها عند الانتهاء.",
|
||||
"help.tabs.organize":
|
||||
"يسهّل إبقاء بضع علامات تبويب مفتوحة تنظيم جلساتك النشطة. أعد تسمية علامات التبويب بأسماء يسهل تذكرها إذا كنت تنوي إبقاءها مفتوحة.",
|
||||
"help.tabs.home":
|
||||
"ستجد جميع جلساتك ومشاريعك في شاشة الصفحة الرئيسية الجديدة. يؤدي تحديد جلسة إلى فتحها في علامة تبويب.",
|
||||
"help.tabs.persistence": "عندما تعيد فتح التطبيق، تظل علامات تبويبك مفتوحة.",
|
||||
"help.tabs.worktrees":
|
||||
"لا يدعم التصميم الجديد أشجار عمل Git حتى الآن، وستتوفر قريبًا. لذلك، إذا كنت تفضل مواصلة استخدام التخطيط السابق، فيمكنك التبديل بين التخطيطات في الإعدادات. ضع في اعتبارك أن التخطيط الجديد سيصبح دائمًا خلال بضعة أسابيع.",
|
||||
"dialog.project.edit.title": "تحرير المشروع",
|
||||
"dialog.project.edit.name": "الاسم",
|
||||
"dialog.project.edit.icon": "أيقونة",
|
||||
@@ -408,35 +531,39 @@ export const dict = {
|
||||
"dialog.project.edit.icon.recommended": "موصى به: 128x128px",
|
||||
"dialog.project.edit.color": "لون",
|
||||
"dialog.project.edit.color.select": "اختر لون {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "سكريبت بدء تشغيل مساحة العمل",
|
||||
"dialog.project.edit.worktree.startup": "برنامج نصي لبدء تشغيل مساحة العمل",
|
||||
"dialog.project.edit.worktree.startup.description": "يتم تشغيله بعد إنشاء مساحة عمل جديدة (شجرة عمل).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "مثال: bun install",
|
||||
"dialog.usageExceeded.dontShowAgain": "عدم الإظهار مرة أخرى",
|
||||
|
||||
"context.breakdown.title": "تفصيل السياق",
|
||||
"context.breakdown.note": 'تفصيل تقريبي لرموز الإدخال. يشمل "أخرى" تعريفات الأدوات والنفقات العامة.',
|
||||
"context.breakdown.note": 'تفصيل تقريبي لرموز الإدخال المميزة. يشمل "أخرى" تعريفات الأدوات والأعباء الإضافية.',
|
||||
"context.breakdown.system": "النظام",
|
||||
"context.breakdown.user": "المستخدم",
|
||||
"context.breakdown.assistant": "المساعد",
|
||||
"context.breakdown.tool": "استدعاءات الأداة",
|
||||
"context.breakdown.tool": "استدعاءات الأدوات",
|
||||
"context.breakdown.other": "أخرى",
|
||||
"context.systemPrompt.title": "موجه النظام",
|
||||
"context.systemPrompt.title": "موجّه النظام",
|
||||
"context.rawMessages.title": "الرسائل الخام",
|
||||
"context.export.session": "تصدير الجلسة",
|
||||
|
||||
"context.stats.session": "جلسة",
|
||||
"context.stats.messages": "رسائل",
|
||||
"context.stats.provider": "موفر",
|
||||
"context.stats.model": "نموذج",
|
||||
"context.stats.limit": "حد السياق",
|
||||
"context.stats.totalTokens": "إجمالي الرموز",
|
||||
"context.stats.totalTokens": "إجمالي الرموز المميزة",
|
||||
"context.stats.usage": "استخدام",
|
||||
"context.stats.inputTokens": "رموز الإدخال",
|
||||
"context.stats.outputTokens": "رموز الإخراج",
|
||||
"context.stats.reasoningTokens": "رموز الاستنتاج",
|
||||
"context.stats.cacheTokens": "رموز التخزين المؤقت (قراءة/كتابة)",
|
||||
"context.stats.inputTokens": "رموز الإدخال المميزة",
|
||||
"context.stats.outputTokens": "رموز الإخراج المميزة",
|
||||
"context.stats.reasoningTokens": "رموز الاستدلال المميزة",
|
||||
"context.stats.cacheTokens": "رموز التخزين المؤقت المميزة (قراءة/كتابة)",
|
||||
"context.stats.userMessages": "رسائل المستخدم",
|
||||
"context.stats.assistantMessages": "رسائل المساعد",
|
||||
"context.stats.totalCost": "التكلفة الإجمالية",
|
||||
"context.stats.sessionCreated": "تم إنشاء الجلسة",
|
||||
"context.stats.lastActivity": "آخر نشاط",
|
||||
"context.usage.tokens": "رموز",
|
||||
"context.usage.tokens": "الرموز المميزة",
|
||||
"context.usage.usage": "استخدام",
|
||||
"context.usage.cost": "تكلفة",
|
||||
"context.usage.clickToView": "انقر لعرض السياق",
|
||||
@@ -464,9 +591,9 @@ export const dict = {
|
||||
"toast.theme.title": "تم تبديل السمة",
|
||||
"toast.scheme.title": "مخطط الألوان",
|
||||
"toast.workspace.enabled.title": "تم تمكين مساحات العمل",
|
||||
"toast.workspace.enabled.description": "الآن يتم عرض عدة worktrees في الشريط الجانبي",
|
||||
"toast.workspace.enabled.description": "تُعرض الآن عدة أشجار عمل في الشريط الجانبي",
|
||||
"toast.workspace.disabled.title": "تم تعطيل مساحات العمل",
|
||||
"toast.workspace.disabled.description": "يتم عرض worktree الرئيسي فقط في الشريط الجانبي",
|
||||
"toast.workspace.disabled.description": "تُعرض شجرة العمل الرئيسية فقط في الشريط الجانبي",
|
||||
"toast.permissions.autoaccept.on.title": "يتم قبول الأذونات تلقائيًا",
|
||||
"toast.permissions.autoaccept.on.description": "ستتم الموافقة على طلبات الأذونات تلقائيًا",
|
||||
"toast.permissions.autoaccept.off.title": "تم إيقاف قبول الأذونات تلقائيًا",
|
||||
@@ -486,9 +613,14 @@ export const dict = {
|
||||
"toast.session.unshare.success.description": "تم إلغاء مشاركة الجلسة بنجاح!",
|
||||
"toast.session.unshare.failed.title": "فشل إلغاء مشاركة الجلسة",
|
||||
"toast.session.unshare.failed.description": "حدث خطأ أثناء إلغاء مشاركة الجلسة",
|
||||
"toast.session.export.success.title": "تم تصدير الجلسة",
|
||||
"toast.session.export.success.description": "تم حفظ الجلسة في \u2068{{filename}}\u2069",
|
||||
"toast.session.export.failed.title": "فشل تصدير الجلسة",
|
||||
"toast.session.export.failed.description": "حدث خطأ أثناء تصدير الجلسة",
|
||||
|
||||
"toast.session.listFailed.title": "فشل تحميل الجلسات لـ {{project}}",
|
||||
"toast.update.title": "تحديث متاح",
|
||||
"toast.update.description": "نسخة جديدة من OpenCode ({{version}}) متاحة الآن للتثبيت.",
|
||||
"toast.update.description": "يتوفر الآن إصدار جديد من OpenCode ({{version}}) للتثبيت.",
|
||||
"toast.update.action.installRestart": "تثبيت وإعادة تشغيل",
|
||||
"toast.update.action.notYet": "ليس الآن",
|
||||
"error.page.title": "حدث خطأ ما",
|
||||
@@ -508,7 +640,7 @@ export const dict = {
|
||||
"error.dev.rootNotFound":
|
||||
"لم يتم العثور على العنصر الجذري. هل نسيت إضافته إلى index.html؟ أو ربما تمت كتابة سمة id بشكل خاطئ؟",
|
||||
"error.serverSync.connectFailed": "تعذر الاتصال بالخادم. هل هناك خادم يعمل في `{{url}}`؟",
|
||||
"directory.error.invalidUrl": "دليل غير صالح في عنوان URL.",
|
||||
"directory.error.invalidUrl": "مجلد غير صالح في عنوان URL.",
|
||||
"error.chain.unknown": "خطأ غير معروف",
|
||||
"error.chain.causedBy": "بسبب:",
|
||||
"error.chain.apiError": "خطأ API",
|
||||
@@ -536,7 +668,7 @@ export const dict = {
|
||||
"notification.session.responseReady.title": "الاستجابة جاهزة",
|
||||
"notification.session.error.title": "خطأ في الجلسة",
|
||||
"notification.session.error.fallbackDescription": "حدث خطأ",
|
||||
"home.recentProjects": "المشاريع الحديثة",
|
||||
"home.recentProjects": "المشاريع الأخيرة",
|
||||
"home.empty.title": "لا توجد مشاريع حديثة",
|
||||
"home.empty.description": "ابدأ بفتح مشروع محلي",
|
||||
"home.title": "الرئيسية",
|
||||
@@ -554,7 +686,7 @@ export const dict = {
|
||||
"home.sessions.group.today": "اليوم",
|
||||
"home.sessions.group.yesterday": "أمس",
|
||||
"home.sessions.group.older": "الأقدم",
|
||||
"home.providerTip": "اتصل بأكثر من 75 مزودًا لاستخدام نماذج أخرى، بما فيها Claude وGPT وGemini وغيرها",
|
||||
"home.providerTip": "اتصل بأكثر من 75 موفرًا لاستخدام نماذج أخرى، بما فيها Claude وGPT وGemini وغيرها",
|
||||
"session.tab.session": "جلسة",
|
||||
"session.tab.review": "مراجعة",
|
||||
"session.tab.context": "سياق",
|
||||
@@ -564,7 +696,7 @@ export const dict = {
|
||||
"session.error.notFound.description": "يشير هذا التبويب إلى جلسة لم تعد موجودة على هذا الخادم.",
|
||||
"session.error.notFound.closeTab": "إغلاق علامة التبويب",
|
||||
"session.error.serverConnection": "تعذر الاتصال بهذا الخادم",
|
||||
"session.review.filesChanged": "تم تغيير {{count}} ملفات",
|
||||
"session.review.filesChanged": "الملفات المتغيرة: {{count}}",
|
||||
"session.review.change.one": "تغيير",
|
||||
"session.review.change.other": "تغييرات",
|
||||
"session.review.loadingChanges": "جارٍ تحميل التغييرات...",
|
||||
@@ -572,7 +704,7 @@ export const dict = {
|
||||
"session.review.noChanges": "لا توجد تغييرات",
|
||||
"session.review.noUncommittedChanges": "لا توجد تغييرات غير مُودَعة بعد",
|
||||
"session.review.noBranchChanges": "لا توجد تغييرات في الفرع بعد",
|
||||
"session.review.noVcs": "لم يتم اكتشاف نظام التحكم في الإصدار Git، لن يتم عرض التغييرات",
|
||||
"session.review.noVcs": "لم يُكتشف نظام Git للتحكم في الإصدارات، لذا لن تُعرض التغييرات",
|
||||
"session.review.noSnapshot": "تم تعطيل تتبع اللقطات في التكوين، لذا فإن تغييرات الجلسة غير متوفرة",
|
||||
"session.files.selectToOpen": "اختر ملفًا لفتحه",
|
||||
"session.files.all": "كل الملفات",
|
||||
@@ -590,14 +722,26 @@ export const dict = {
|
||||
"session.question.minimize": "تصغير السؤال",
|
||||
"session.question.restore": "استعادة السؤال",
|
||||
"session.question.pending.one": "{{count}} سؤال معلق",
|
||||
"session.question.pending.zero": "عدد الأسئلة المعلقة: {{count}}",
|
||||
"session.question.pending.two": "عدد الأسئلة المعلقة: {{count}}",
|
||||
"session.question.pending.few": "{{count}} أسئلة معلقة",
|
||||
"session.question.pending.many": "{{count}} سؤالًا معلقًا",
|
||||
"session.question.pending.other": "الأسئلة المعلقة: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} رسالة في الانتظار",
|
||||
"session.followupDock.summary.other": "{{count}} رسائل في الانتظار",
|
||||
"session.followupDock.summary.one": "{{count}} رسالة في قائمة الانتظار",
|
||||
"session.followupDock.summary.zero": "عدد الرسائل في قائمة الانتظار: {{count}}",
|
||||
"session.followupDock.summary.two": "عدد الرسائل في قائمة الانتظار: {{count}}",
|
||||
"session.followupDock.summary.few": "{{count}} رسائل في قائمة الانتظار",
|
||||
"session.followupDock.summary.many": "{{count}} رسالةً في قائمة الانتظار",
|
||||
"session.followupDock.summary.other": "{{count}} رسائل في قائمة الانتظار",
|
||||
"session.followupDock.sendNow": "إرسال الآن",
|
||||
"session.followupDock.edit": "تحرير",
|
||||
"session.followupDock.collapse": "طي الرسائل المنتظرة",
|
||||
"session.followupDock.expand": "توسيع الرسائل المنتظرة",
|
||||
"session.revertDock.summary.one": "{{count}} رسالة تم التراجع عنها",
|
||||
"session.revertDock.summary.zero": "عدد الرسائل التي تم التراجع عنها: {{count}}",
|
||||
"session.revertDock.summary.two": "عدد الرسائل التي تم التراجع عنها: {{count}}",
|
||||
"session.revertDock.summary.few": "{{count}} رسائل تم التراجع عنها",
|
||||
"session.revertDock.summary.many": "{{count}} رسالةً تم التراجع عنها",
|
||||
"session.revertDock.summary.other": "{{count}} رسائل تم التراجع عنها",
|
||||
"session.revertDock.collapse": "طي الرسائل التي تم التراجع عنها",
|
||||
"session.revertDock.expand": "توسيع الرسائل التي تم التراجع عنها",
|
||||
@@ -649,12 +793,19 @@ export const dict = {
|
||||
"terminal.close": "إغلاق المحطة الطرفية",
|
||||
"terminal.connectionLost.title": "فقد الاتصال",
|
||||
"terminal.connectionLost.description": "انقطع اتصال المحطة الطرفية. يمكن أن يحدث هذا عند إعادة تشغيل الخادم.",
|
||||
"terminal.connectTicket.csrfError": "رُفضت تذكرة اتصال PTY بسبب فحوصات الأصل أو CSRF. تحقق من إعداد CORS للخادم.",
|
||||
"terminal.connectTicket.statusError": "فشلت تذكرة اتصال PTY بالحالة {{status}}",
|
||||
|
||||
"titlebar.update": "تحديث",
|
||||
"titlebar.updateVersion": "تحديث {{version}}",
|
||||
|
||||
"common.closeTab": "إغلاق علامة التبويب",
|
||||
"common.dismiss": "رفض",
|
||||
"common.dismiss": "إهمال",
|
||||
"common.requestFailed": "فشل الطلب",
|
||||
"common.moreOptions": "مزيد من الخيارات",
|
||||
"common.learnMore": "اعرف المزيد",
|
||||
"common.learnMore": "اطّلع على المزيد",
|
||||
"common.rename": "إعادة تسمية",
|
||||
"common.export": "تصدير",
|
||||
"common.reset": "إعادة تعيين",
|
||||
"common.archive": "أرشفة",
|
||||
"common.delete": "حذف",
|
||||
@@ -671,7 +822,7 @@ export const dict = {
|
||||
"sidebar.gettingStarted.title": "البدء",
|
||||
"sidebar.gettingStarted.line1": "يتضمن OpenCode نماذج مجانية حتى تتمكن من البدء فورًا.",
|
||||
"sidebar.gettingStarted.line2": "قم بتوصيل أي موفر لاستخدام النماذج، بما في ذلك Claude و GPT و Gemini وما إلى ذلك.",
|
||||
"sidebar.project.recentSessions": "الجلسات الحديثة",
|
||||
"sidebar.project.recentSessions": "الجلسات الأخيرة",
|
||||
"sidebar.project.viewAllSessions": "عرض جميع الجلسات",
|
||||
"sidebar.project.clearNotifications": "مسح الإشعارات",
|
||||
"sidebar.empty.title": "لا توجد مشاريع مفتوحة",
|
||||
@@ -690,7 +841,7 @@ export const dict = {
|
||||
"settings.general.section.updates": "التحديثات",
|
||||
"settings.general.section.sounds": "المؤثرات الصوتية",
|
||||
"settings.general.section.feed": "الخلاصة",
|
||||
"settings.general.section.display": "شاشة العرض",
|
||||
"settings.general.section.display": "العرض",
|
||||
"settings.general.row.language.title": "اللغة",
|
||||
"settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode",
|
||||
"settings.general.row.shell.title": "Shell المحطة الطرفية",
|
||||
@@ -727,10 +878,11 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "التنقل السفلي",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"وضع شريط العنوان وعلامات تبويب الجلسات أسفل الشاشة على الأجهزة المحمولة",
|
||||
"settings.general.row.showCustomAgents.title": "الوكلاء المخصصون",
|
||||
"settings.general.row.showCustomAgents.description": "إظهار محدد الوكلاء في محرر الرسائل",
|
||||
"settings.general.row.reasoningSummaries.title": "إظهار ملخصات الاستنتاج",
|
||||
"settings.general.row.reasoningSummaries.description": "عرض ملخصات استنتاج النموذج في الشريط الزمني",
|
||||
"settings.general.row.showCustomAgents.title": "إظهار الوكيل",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"التبديل بين الوكلاء في محرر الرسائل. عند إخفائه، يُستخدم وكيل Build افتراضيًا.",
|
||||
"settings.general.row.reasoningSummaries.title": "إظهار ملخصات الاستدلال",
|
||||
"settings.general.row.reasoningSummaries.description": "عرض ملخصات استدلال النموذج في الشريط الزمني",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "توسيع أجزاء أداة shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"إظهار أجزاء أداة shell موسعة بشكل افتراضي في الشريط الزمني",
|
||||
@@ -743,11 +895,12 @@ export const dict = {
|
||||
"استخدم علامات التبويب الجديدة وتخطيط الصفحة الرئيسية. يمكنك التبديل بين التخطيطات لفترة محدودة.",
|
||||
"settings.general.row.newInterfaceNotice.title": "أنت تستخدم الآن التخطيط الجديد",
|
||||
"settings.general.row.newInterfaceNotice.description": "التخطيط السابق لم يعد متاحًا",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "رفض",
|
||||
"settings.general.row.pinchZoom.title": "التكبير بإيماءة القرص",
|
||||
"settings.general.row.pinchZoom.description": "السماح بإيماءة القرص على لوحة اللمس وإيماءة Ctrl-scroll للتكبير",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "إهمال",
|
||||
"settings.general.row.pinchZoom.title": "التكبير بالمقاربة والمباعدة",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"السماح بإيماءات مقاربة الأصابع ومباعدتها على لوحة التعقب والتمرير مع الضغط على Ctrl للتكبير",
|
||||
"settings.general.row.wayland.title": "استخدام Wayland الأصلي",
|
||||
"settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
|
||||
"settings.general.row.wayland.description": "تعطيل الرجوع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"على Linux مع شاشات بمعدلات تحديث مختلطة، يمكن أن يكون Wayland الأصلي أكثر استقرارًا.",
|
||||
"settings.general.row.releaseNotes.title": "ملاحظات الإصدار",
|
||||
@@ -760,7 +913,7 @@ export const dict = {
|
||||
"settings.updates.action.checking": "جارٍ التحقق...",
|
||||
"settings.updates.action.downloading": "جارٍ التنزيل...",
|
||||
"settings.updates.action.installing": "جارٍ التثبيت...",
|
||||
"settings.updates.toast.latest.title": "أنت على آخر إصدار",
|
||||
"settings.updates.toast.latest.title": "لديك أحدث إصدار",
|
||||
"settings.updates.toast.latest.description": "أنت تستخدم أحدث إصدار من OpenCode.",
|
||||
"sound.option.none": "بلا",
|
||||
"sound.option.alert01": "تنبيه 01",
|
||||
@@ -822,17 +975,17 @@ export const dict = {
|
||||
"settings.general.sounds.errors.description": "تشغيل صوت عند حدوث خطأ",
|
||||
"settings.shortcuts.title": "اختصارات لوحة المفاتيح",
|
||||
"settings.shortcuts.reset.button": "إعادة التعيين إلى الافتراضيات",
|
||||
"settings.shortcuts.reset.toast.title": "تم إعادة تعيين الاختصارات",
|
||||
"settings.shortcuts.reset.toast.description": "تم إعادة تعيين اختصارات لوحة المفاتيح إلى الافتراضيات.",
|
||||
"settings.shortcuts.reset.toast.title": "أُعيد تعيين الاختصارات",
|
||||
"settings.shortcuts.reset.toast.description": "أُعيد تعيين اختصارات لوحة المفاتيح إلى الإعدادات الافتراضية.",
|
||||
"settings.shortcuts.conflict.title": "الاختصار قيد الاستخدام بالفعل",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} معين بالفعل لـ {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "غير معين",
|
||||
"settings.shortcuts.conflict.description": "الاختصار {{keybind}} مخصص بالفعل لـ {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "غير مخصص",
|
||||
"settings.shortcuts.pressKeys": "اضغط على المفاتيح",
|
||||
"settings.shortcuts.search.placeholder": "البحث في الاختصارات",
|
||||
"settings.shortcuts.search.empty": "لم يتم العثور على اختصارات",
|
||||
"settings.shortcuts.group.general": "عام",
|
||||
"settings.shortcuts.group.session": "جلسة",
|
||||
"settings.shortcuts.group.navigation": "تصفح",
|
||||
"settings.shortcuts.group.navigation": "التنقل",
|
||||
"settings.shortcuts.group.modelAndAgent": "النموذج والوكيل",
|
||||
"settings.shortcuts.group.terminal": "المحطة الطرفية",
|
||||
"settings.shortcuts.group.prompt": "موجه",
|
||||
@@ -857,19 +1010,18 @@ export const dict = {
|
||||
"settings.permissions.description": "تحكم في الأدوات التي يمكن للخادم استخدامها بشكل افتراضي.",
|
||||
"settings.permissions.section.tools": "الأدوات",
|
||||
"settings.permissions.toast.updateFailed.title": "فشل تحديث الأذونات",
|
||||
"settings.permissions.action.allow": "سماح",
|
||||
"settings.permissions.action.ask": "سؤال",
|
||||
"settings.permissions.action.deny": "رفض",
|
||||
"settings.permissions.action.allow": "السماح",
|
||||
"settings.permissions.action.ask": "طلب الإذن",
|
||||
"settings.permissions.action.deny": "الرفض",
|
||||
"settings.permissions.tool.read.title": "قراءة",
|
||||
"settings.permissions.tool.read.description": "قراءة ملف (يطابق مسار الملف)",
|
||||
"settings.permissions.tool.edit.title": "تحرير",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"تعديل الملفات، بما في ذلك التحرير والكتابة والتصحيحات والتحرير المتعدد",
|
||||
"settings.permissions.tool.edit.description": "تعديل الملفات، بما في ذلك عمليات التحرير والكتابة وتطبيق الرقع",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "مطابقة الملفات باستخدام أنماط glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "البحث في محتويات الملف باستخدام التعبيرات العادية",
|
||||
"settings.permissions.tool.list.title": "قائمة",
|
||||
"settings.permissions.tool.list.title": "سرد",
|
||||
"settings.permissions.tool.list.description": "سرد الملفات داخل دليل",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "تشغيل أوامر shell",
|
||||
@@ -887,7 +1039,7 @@ export const dict = {
|
||||
"settings.permissions.tool.websearch.description": "البحث في الويب",
|
||||
"settings.permissions.tool.external_directory.title": "دليل خارجي",
|
||||
"settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع",
|
||||
"settings.permissions.tool.doom_loop.title": "حلقة الموت",
|
||||
"settings.permissions.tool.doom_loop.title": "حلقة مفرغة",
|
||||
"settings.permissions.tool.doom_loop.description": "اكتشاف استدعاءات الأدوات المتكررة بمدخلات متطابقة",
|
||||
"session.delete.failed.title": "فشل حذف الجلسة",
|
||||
"session.delete.title": "حذف الجلسة",
|
||||
@@ -895,7 +1047,7 @@ export const dict = {
|
||||
"session.delete.button": "حذف الجلسة",
|
||||
"workspace.new": "مساحة عمل جديدة",
|
||||
"workspace.type.local": "محلي",
|
||||
"workspace.type.sandbox": "صندوق رمل",
|
||||
"workspace.type.sandbox": "بيئة معزولة",
|
||||
"workspace.create.failed.title": "فشل إنشاء مساحة العمل",
|
||||
"workspace.delete.failed.title": "فشل حذف مساحة العمل",
|
||||
"workspace.resetting.title": "إعادة تعيين مساحة العمل",
|
||||
@@ -905,7 +1057,7 @@ export const dict = {
|
||||
"workspace.reset.success.description": "مساحة العمل تطابق الآن الفرع الافتراضي.",
|
||||
"workspace.error.stillPreparing": "مساحة العمل لا تزال قيد الإعداد",
|
||||
"workspace.status.checking": "التحقق من التغييرات غير المدمجة...",
|
||||
"workspace.status.error": "تعذر التحقق من حالة git.",
|
||||
"workspace.status.error": "تعذر التحقق من حالة Git.",
|
||||
"workspace.status.clean": "لم يتم اكتشاف تغييرات غير مدمجة.",
|
||||
"workspace.status.dirty": "تم اكتشاف تغييرات غير مدمجة في مساحة العمل هذه.",
|
||||
"workspace.delete.title": "حذف مساحة العمل",
|
||||
@@ -921,7 +1073,7 @@ export const dict = {
|
||||
"common.open": "فتح",
|
||||
"dialog.releaseNotes.action.getStarted": "البدء",
|
||||
"dialog.releaseNotes.action.next": "التالي",
|
||||
"dialog.releaseNotes.action.hideFuture": "عدم إظهار هذا في المستقبل",
|
||||
"dialog.releaseNotes.action.hideFuture": "عدم إظهارها مستقبلاً",
|
||||
"dialog.releaseNotes.media.alt": "معاينة الإصدار",
|
||||
"toast.project.reloadFailed.title": "فشل في إعادة تحميل {{project}}",
|
||||
"error.server.invalidConfiguration": "تكوين غير صالح",
|
||||
@@ -931,22 +1083,25 @@ export const dict = {
|
||||
"common.time.hoursAgo.short": "قبل {{count}} س",
|
||||
"common.time.daysAgo.short": "قبل {{count}} ي",
|
||||
"settings.providers.connected.environmentDescription": "متصل من متغيرات البيئة الخاصة بك",
|
||||
"settings.providers.custom.description": "أضف مزود متوافق مع OpenAI بواسطة عنوان URL الأساسي.",
|
||||
"settings.providers.custom.description": "أضف موفرًا متوافقًا مع OpenAI باستخدام عنوان URL الأساسي.",
|
||||
|
||||
"app.server.unreachable": "تعذر الوصول إلى {{server}}",
|
||||
"app.server.retrying": "جاري إعادة المحاولة تلقائيًا...",
|
||||
"app.server.retrying": "جارٍ إعادة المحاولة تلقائيًا...",
|
||||
"app.server.otherServers": "خوادم أخرى",
|
||||
"dialog.server.add.usernamePlaceholder": "اسم المستخدم",
|
||||
"dialog.server.add.passwordPlaceholder": "كلمة المرور",
|
||||
"server.row.noUsername": "لا يوجد اسم مستخدم",
|
||||
"session.review.noVcs.createGit.title": "إنشاء مستودع Git",
|
||||
"session.review.noVcs.createGit.description": "تتبع ومراجعة والتراجع عن التغييرات في هذا المشروع",
|
||||
"session.review.noVcs.createGit.actionLoading": "جاري إنشاء مستودع Git...",
|
||||
"session.review.noVcs.createGit.actionLoading": "جارٍ إنشاء مستودع Git...",
|
||||
"session.review.noVcs.createGit.action": "إنشاء مستودع Git",
|
||||
"session.todo.progress": "تم إكمال {{done}} من {{total}} مهام",
|
||||
"session.question.progress": "{{current}} من {{total}} أسئلة",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "مستكشف الملفات",
|
||||
"session.header.reveal.finder": "إظهار في Finder",
|
||||
"session.header.reveal.fileExplorer": "إظهار في مستكشف الملفات",
|
||||
"session.header.reveal.containingFolder": "فتح المجلد الحاوي",
|
||||
"session.header.open.fileManager": "مدير الملفات",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -964,9 +1119,9 @@ export const dict = {
|
||||
"debugBar.ariaLabel": "تشخيص أداء التطوير",
|
||||
"debugBar.na": "غير متاح",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip": "آخر انتقال مكتمل للمسار يمس صفحة جلسة، مُقاسًا من بدء التوجيه حتى أول رسم بعد استقراره.",
|
||||
"debugBar.nav.tip": "آخر انتقال مكتمل للمسار يتضمن صفحة جلسة، مُقاسًا من بدء التوجيه حتى أول رسم بعد استقراره.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "الإطارات المتجددة في الثانية خلال آخر 5 ثوانٍ.",
|
||||
"debugBar.fps.tip": "متوسط متحرك لمعدل الإطارات في الثانية خلال آخر 5 ثوانٍ.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "أسوأ وقت للإطار خلال آخر 5 ثوانٍ.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
@@ -978,10 +1133,18 @@ export const dict = {
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip": "مدة التفاعل التقريبية خلال آخر 5 ثوانٍ. هذا يشبه INP، وليس Web Vitals INP الرسمي.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "التحول التخطيطي التراكمي لعمر التطبيق الحالي.",
|
||||
"debugBar.cls.tip": "التحول التخطيطي التراكمي منذ تشغيل التطبيق الحالي.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "كومة JS المستخدمة مقابل حد الكومة. Chromium فقط.",
|
||||
"debugBar.mem.tip": "كومة JS المستخدمة مقابل حد الكومة. {{used}} من {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "فرض أنماط التركيز على جميع العناصر التفاعلية",
|
||||
"debugBar.focus.on": "تشغيل",
|
||||
"debugBar.focus.off": "إيقاف",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "فرض اتجاه التخطيط على التطبيق بالكامل دون تغيير اللغة المحددة",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
@@ -997,7 +1160,7 @@ export const dict = {
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "غير معروف",
|
||||
"error.page.circular": "[دائري]",
|
||||
"error.page.circular": "[مرجع دائري]",
|
||||
"error.serverSDK.noServerAvailable": "لا يوجد خادم متاح",
|
||||
"error.serverSDK.serverNotAvailable": "الخادم غير متاح",
|
||||
"error.childStore.persistedCacheCreateFailed": "فشل إنشاء ذاكرة التخزين المؤقت الدائمة",
|
||||
|
||||
@@ -0,0 +1,1168 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Fayl",
|
||||
"desktop.menu.edit": "Redaktə",
|
||||
"desktop.menu.view": "Görünüş",
|
||||
"desktop.menu.go": "Keçid",
|
||||
"desktop.menu.window": "Pəncərə",
|
||||
"desktop.menu.help": "Kömək",
|
||||
"desktop.menu.checkForUpdates": "Yeniləmələri yoxla...",
|
||||
"desktop.menu.settings": "Tənzimləmələr",
|
||||
"desktop.menu.reloadWebview": "Webview-u yenidən yüklə",
|
||||
"desktop.menu.restart": "Yenidən başlat",
|
||||
"desktop.menu.exportLogs": "Jurnalları ixrac et...",
|
||||
"desktop.menu.newSession": "Yeni sessiya",
|
||||
"desktop.menu.openProject": "Layihəni aç...",
|
||||
"desktop.menu.newWindow": "Yeni pəncərə",
|
||||
"desktop.menu.closeWindow": "Pəncərəni bağla",
|
||||
"desktop.menu.undo": "Geri al",
|
||||
"desktop.menu.redo": "Yenidən et",
|
||||
"desktop.menu.cut": "Kəs",
|
||||
"desktop.menu.copy": "Kopyala",
|
||||
"desktop.menu.paste": "Yapışdır",
|
||||
"desktop.menu.delete": "Sil",
|
||||
"desktop.menu.selectAll": "Hamısını seç",
|
||||
"desktop.menu.toggleSidebar": "Yan paneli aç/bağla",
|
||||
"desktop.menu.toggleTerminal": "Terminalı aç/bağla",
|
||||
"desktop.menu.toggleFileTree": "Fayl ağacını aç/bağla",
|
||||
"desktop.menu.reload": "Yenidən yüklə",
|
||||
"desktop.menu.toggleDeveloperTools": "Tərtibatçı alətlərini aç/bağla",
|
||||
"desktop.menu.actualSize": "Faktiki ölçü",
|
||||
"desktop.menu.zoomIn": "Böyüt",
|
||||
"desktop.menu.zoomOut": "Kiçilt",
|
||||
"desktop.menu.toggleFullScreen": "Tam ekran rejimini aç/bağla",
|
||||
"desktop.menu.back": "Geri",
|
||||
"desktop.menu.forward": "İrəli",
|
||||
"desktop.menu.previousSession": "Əvvəlki sessiya",
|
||||
"desktop.menu.nextSession": "Növbəti sessiya",
|
||||
"desktop.menu.previousProject": "Əvvəlki layihə",
|
||||
"desktop.menu.nextProject": "Növbəti layihə",
|
||||
"desktop.menu.minimize": "Kiçilt",
|
||||
"desktop.menu.maximize": "Böyüt",
|
||||
"desktop.menu.documentation": "OpenCode sənədləri",
|
||||
"desktop.menu.supportForum": "Dəstək forumu",
|
||||
"desktop.menu.shareFeedback": "Rəy bildir",
|
||||
"desktop.menu.reportBug": "Xəta bildir",
|
||||
"desktop.menu.ariaLabel": "OpenCode menyusu",
|
||||
"desktop.updater.dialog.checkFailed.message": "Yeniləmələri yoxlamaq mümkün olmadı.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Yeniləmə xətası",
|
||||
"desktop.updater.dialog.upToDate.message": "Ən son versiyadan istifadə edirsiniz.",
|
||||
"desktop.updater.dialog.upToDate.title": "Yeniləmə yoxdur",
|
||||
"desktop.updater.dialog.ready.message": "{{version}} yeniləməsi endirildi. İndi yenidən başlatmaq istəyirsiniz?",
|
||||
"desktop.updater.dialog.ready.title": "Yeniləmə hazırdır",
|
||||
"desktop.updater.dialog.restart": "Yenidən başlat",
|
||||
"desktop.updater.dialog.later": "Daha sonra",
|
||||
"desktop.recovery.action.relaunch": "Yenidən işə sal",
|
||||
"desktop.recovery.action.exportLogs": "Jurnalları ixrac et",
|
||||
"desktop.recovery.action.keepWaiting": "Gözləməyə davam et",
|
||||
"desktop.recovery.action.quit": "Çıx",
|
||||
"desktop.recovery.loadFailed": "OpenCode yüklənə bilmədi",
|
||||
"desktop.recovery.terminated": "OpenCode pəncərəsinin işi gözlənilmədən dayandı",
|
||||
"desktop.recovery.unresponsive": "OpenCode cavab vermir",
|
||||
"desktop.recovery.unresponsive.detail":
|
||||
"Tətbiqi yenidən işə sala, jurnalları aça və ya gözləməyə davam edə bilərsiniz.",
|
||||
"desktop.recovery.loadFailed.detail": "Pəncərə: {{window}}\nURL: {{url}}\nXəta: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Pəncərə: {{window}}\nSəbəb: {{reason}}\nKod: {{code}}",
|
||||
"desktop.recovery.unknown": "<naməlum>",
|
||||
"desktop.dialog.chooseFolder": "Qovluq seçin",
|
||||
"desktop.dialog.chooseFile": "Fayl seçin",
|
||||
"desktop.dialog.saveFile": "Faylı saxla",
|
||||
"desktop.dialog.files": "Fayllar",
|
||||
"desktop.server.local": "Yerli server",
|
||||
"desktop.wsl.error.windowsOnly": "WSL yalnız Windows-da əlçatandır",
|
||||
"desktop.wsl.error.unavailable": "WSL əlçatan deyil",
|
||||
"desktop.wsl.error.listInstalled": "Quraşdırılmış WSL distribütivlərini siyahılamaq mümkün olmadı",
|
||||
"desktop.wsl.error.listOnline": "Onlayn WSL distribütivlərini siyahılamaq mümkün olmadı",
|
||||
"desktop.wsl.error.executeDistro": "Distribütivdə əmrləri icra etmək mümkün deyil",
|
||||
"desktop.wsl.error.installWsl": "WSL quraşdırılması uğursuz oldu",
|
||||
"desktop.wsl.error.installDistro": "Distribütivi quraşdırmaq mümkün olmadı: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "OpenCode quraşdırılması uğursuz oldu",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} artıq əlavə edilib",
|
||||
"desktop.wsl.error.opencodeMissing": "Bu distribütivdə opencode quraşdırılmayıb",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode quraşdırılıb, lakin onu işə salmaq mümkün olmadı",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode {{distro}} distribütivində quraşdırılmayıb",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode yeniləməsi tamamlandı, lakin {{distro}} hələ də {{installed}} versiyasını bildirir; gözlənilən versiya: {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "versiya yoxdur",
|
||||
"desktop.wsl.error.serverExited": "WSL serveri başladıqdan sonra dayandı (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"WSL serveri işlək vəziyyətə gəlməzdən əvvəl dayandı (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout":
|
||||
"{{distro}} üçün sidecar sağlamlıq yoxlamasının {{timeout}}ms gözləmə müddəti bitdi",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} üçün {{timeout}}ms gözləmə müddəti bitdi",
|
||||
"desktop.wsl.error.failedPort": "Portu əldə etmək mümkün olmadı",
|
||||
"desktop.picker.error.notSelected": "Seçim vasitəsində fayl seçilməyib",
|
||||
"desktop.picker.error.sizeLimit": "Seçilmiş əlavələr {{limit}} MB limitini aşır",
|
||||
"command.category.suggested": "Təklif olunan",
|
||||
"command.category.view": "Görünüş",
|
||||
"command.category.project": "Layihə",
|
||||
"command.category.provider": "Provayder",
|
||||
"command.category.server": "Server",
|
||||
"command.category.session": "Sessiya",
|
||||
"command.category.theme": "Mövzu",
|
||||
"command.category.language": "Dil",
|
||||
"command.category.file": "Fayl",
|
||||
"command.category.context": "Kontekst",
|
||||
"command.category.terminal": "Terminal",
|
||||
"command.category.model": "Model",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "Agent",
|
||||
"command.category.permissions": "İcazələr",
|
||||
"command.category.workspace": "İş sahəsi",
|
||||
"command.category.settings": "Tənzimləmələr",
|
||||
"command.logs.export": "Jurnalları ixrac et",
|
||||
"theme.scheme.system": "Sistem",
|
||||
"theme.scheme.light": "Açıq",
|
||||
"theme.scheme.dark": "Tünd",
|
||||
"command.sidebar.toggle": "Yan paneli aç/bağla",
|
||||
"command.project.open": "Layihəni aç",
|
||||
"command.project.previous": "Əvvəlki layihə",
|
||||
"command.project.next": "Növbəti layihə",
|
||||
"command.project.index": "{{index}} nömrəli layihəyə keç",
|
||||
"command.provider.connect": "Provayderi qoş",
|
||||
"command.server.switch": "Serveri dəyiş",
|
||||
"command.settings.open": "Tənzimləmələri aç",
|
||||
"command.session.previous": "Əvvəlki sessiya",
|
||||
"command.session.next": "Növbəti sessiya",
|
||||
"command.session.previous.unseen": "Əvvəlki oxunmamış sessiya",
|
||||
"command.session.next.unseen": "Növbəti oxunmamış sessiya",
|
||||
"command.session.archive": "Sessiyanı arxivlə",
|
||||
"command.palette": "Əmr paneli",
|
||||
"command.theme.cycle": "Mövzunu dəyiş",
|
||||
"command.theme.set": "Mövzudan istifadə et: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Rəng sxemini dəyiş",
|
||||
"command.theme.scheme.set": "Rəng sxemindən istifadə et: {{scheme}}",
|
||||
"command.language.cycle": "Dili dəyiş",
|
||||
"command.language.set": "Dildən istifadə et: {{language}}",
|
||||
"command.session.new": "Yeni sessiya",
|
||||
"command.file.open": "Faylı aç",
|
||||
"command.tab.close": "Tabı bağla",
|
||||
"command.tab.reopenClosed": "Bağlanmış tabı yenidən aç",
|
||||
"command.context.addSelection": "Seçimi kontekstə əlavə et",
|
||||
"command.context.addSelection.description": "Cari fayldan seçilmiş sətirləri əlavə et",
|
||||
"command.input.focus": "Giriş sahəsinə fokusla",
|
||||
"command.terminal.toggle": "Terminalı aç/bağla",
|
||||
"command.fileTree.toggle": "Fayl ağacını aç/bağla",
|
||||
"command.review.toggle": "İcmalı aç/bağla",
|
||||
"command.terminal.new": "Yeni terminal",
|
||||
"command.terminal.new.description": "Yeni terminal tabı yarat",
|
||||
"command.steps.toggle": "Addımları aç/bağla",
|
||||
"command.steps.toggle.description": "Cari mesaj üçün addımları göstər və ya gizlət",
|
||||
"command.message.previous": "Əvvəlki mesaj",
|
||||
"command.message.previous.description": "Əvvəlki istifadəçi mesajına keç",
|
||||
"command.message.next": "Növbəti mesaj",
|
||||
"command.message.next.description": "Növbəti istifadəçi mesajına keç",
|
||||
"command.model.choose": "Model seç",
|
||||
"command.model.choose.description": "Fərqli model seç",
|
||||
"command.mcp.toggle": "MCP-ləri aç/bağla",
|
||||
"command.mcp.toggle.description": "MCP-ləri aç/bağla",
|
||||
"command.agent.cycle": "Agenti dəyiş",
|
||||
"command.agent.cycle.description": "Növbəti agentə keç",
|
||||
"command.agent.cycle.reverse": "Agenti geri dəyiş",
|
||||
"command.agent.cycle.reverse.description": "Əvvəlki agentə keç",
|
||||
"command.model.variant.cycle": "Düşünmə səviyyəsini dəyiş",
|
||||
"command.model.variant.cycle.description": "Növbəti səviyyəyə keç",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Prompt",
|
||||
"command.permissions.autoaccept.enable": "İcazələri avtomatik qəbul et",
|
||||
"command.permissions.autoaccept.disable": "İcazələrin avtomatik qəbulunu dayandır",
|
||||
"command.workspace.toggle": "İş sahələrini aç/bağla",
|
||||
"command.workspace.toggle.description": "Yan paneldə birdən çox iş sahəsini aktiv və ya deaktiv et",
|
||||
"command.session.undo": "Geri al",
|
||||
"command.session.undo.description": "Son mesajı geri al",
|
||||
"command.session.redo": "Yenidən et",
|
||||
"command.session.redo.description": "Son geri alınmış mesajı yenidən et",
|
||||
"command.session.compact": "Sessiyanı sıxlaşdır",
|
||||
"command.session.compact.description": "Kontekst həcmini azaltmaq üçün sessiyanı xülasə et",
|
||||
"command.session.fork": "Mesajdan fork et",
|
||||
"command.session.fork.description": "Əvvəlki mesajdan yeni sessiya yarat",
|
||||
"command.session.share": "Sessiyanı paylaş",
|
||||
"command.session.share.description": "Bu sessiyanı paylaş və URL-ni buferə kopyala",
|
||||
"command.session.unshare": "Sessiyanın paylaşımını dayandır",
|
||||
"command.session.unshare.description": "Bu sessiyanın paylaşımını dayandır",
|
||||
"command.session.export": "Sessiyanı ixrac et",
|
||||
"command.session.export.description": "Sessiyanın tam transkriptini JSON formatında ixrac et",
|
||||
|
||||
"palette.search.placeholder": "Fayl, əmr və sessiya axtar",
|
||||
"palette.search.placeholder.home": "Əmr və sessiyaları axtar",
|
||||
"palette.empty": "Nəticə tapılmadı",
|
||||
"palette.group.commands": "Əmrlər",
|
||||
"palette.group.files": "Fayllar",
|
||||
"dialog.provider.search.placeholder": "Provayderləri axtar",
|
||||
"dialog.provider.empty": "Provayder tapılmadı",
|
||||
"dialog.provider.group.popular": "Populyar",
|
||||
"dialog.provider.group.other": "Digər",
|
||||
"dialog.provider.custom.label": "Xüsusi OpenAI-a uyğun provayder",
|
||||
"dialog.provider.tag.recommended": "Tövsiyə olunan",
|
||||
"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",
|
||||
"dialog.provider.openrouter.note": "Bir provayderdən bütün dəstəklənən modellərə giriş",
|
||||
"dialog.provider.vercel.note": "Ağıllı yönləndirmə ilə AI modellərinə vahid giriş",
|
||||
"dialog.model.select.title": "Model seç",
|
||||
"dialog.model.search.placeholder": "Modelləri axtar",
|
||||
"dialog.model.empty": "Model nəticəsi yoxdur",
|
||||
"dialog.model.manage": "Modelləri idarə et",
|
||||
"dialog.model.manage.description": "Model seçicisində hansı modellərin görünəcəyini fərdiləşdir.",
|
||||
"dialog.model.manage.provider.toggle": "Bütün {{provider}} modellərini aç/bağla",
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCode tərəfindən təqdim olunan pulsuz modellər",
|
||||
"dialog.model.unpaid.addMore.title": "Populyar provayderlərdən daha çox model əlavə edin",
|
||||
"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",
|
||||
"provider.connect.method.headless": "Başsız",
|
||||
"provider.connect.status.inProgress": "Avtorizasiya davam edir...",
|
||||
"provider.connect.status.waiting": "Avtorizasiya gözlənilir...",
|
||||
"provider.connect.status.failed": "Avtorizasiya uğursuz oldu: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Hesabınızı qoşmaq və OpenCode-da {{provider}} modellərindən istifadə etmək üçün {{provider}} API açarınızı daxil edin.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API açarı",
|
||||
"provider.connect.apiKey.placeholder": "API açarı",
|
||||
"provider.connect.apiKey.required": "API açarı tələb olunur",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen sizə kodlaşdırma agentləri üçün etibarlı optimallaşdırılmış seçilmiş modellər dəstinə giriş verir.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Tək bir API açarı ilə Claude, GPT, Gemini, GLM və daha çox modellərə giriş əldə edəcəksiniz.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " səhifəsinə daxil olub API açarınızı əldə edin.",
|
||||
"provider.connect.oauth.code.visit.prefix": "",
|
||||
"provider.connect.oauth.code.visit.link": "Bu linkə",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" daxil olub hesabınızı qoşmaq və OpenCode-da {{provider}} modellərindən istifadə etmək üçün avtorizasiya kodunuzu əldə edin.",
|
||||
"provider.connect.oauth.code.label": "{{method}} avtorizasiya kodu",
|
||||
"provider.connect.oauth.code.placeholder": "Avtorizasiya kodu",
|
||||
"provider.connect.oauth.code.required": "Avtorizasiya kodu tələb olunur",
|
||||
"provider.connect.oauth.code.invalid": "Yanlış avtorizasiya kodu",
|
||||
"provider.connect.oauth.auto.visit.prefix": "",
|
||||
"provider.connect.oauth.auto.visit.link": "Bu linkə",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" daxil olub aşağıdakı kodu daxil edərək hesabınızı qoşun və OpenCode-da {{provider}} modellərindən istifadə edin.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Təsdiq kodu",
|
||||
"provider.connect.toast.connected.title": "{{provider}} qoşuldu",
|
||||
"provider.connect.toast.connected.description": "{{provider}} modelləri artıq istifadə üçün mövcuddur.",
|
||||
"provider.custom.title": "Fərdi provayder",
|
||||
"provider.custom.unavailable": "Fərdi provayderlər bu serverdə əlçatan deyil",
|
||||
"provider.custom.description.prefix": "OpenAI ilə uyğun provayderi konfiqurasiya edin. ",
|
||||
"provider.custom.description.link": "Provayder konfiqurasiyası sənədləri",
|
||||
"provider.custom.description.suffix": "nə baxın.",
|
||||
"provider.custom.field.providerID.label": "Provayder ID",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "Kiçik hərflər, rəqəmlər, tire və ya alt xətt",
|
||||
"provider.custom.field.name.label": "Görünən ad",
|
||||
"provider.custom.field.name.placeholder": "Mənim AI Provayderim",
|
||||
"provider.custom.field.baseURL.label": "Əsas URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API açarı",
|
||||
"provider.custom.field.apiKey.placeholder": "API açarı",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"İstəyə bağlıdır. Autentifikasiyanı başlıqlar vasitəsilə idarə edirsinizsə, boş buraxın.",
|
||||
"provider.custom.models.label": "Modellər",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
"provider.custom.models.name.label": "Ad",
|
||||
"provider.custom.models.name.placeholder": "Görünən ad",
|
||||
"provider.custom.models.remove": "Modeli sil",
|
||||
"provider.custom.models.add": "Model əlavə et",
|
||||
"provider.custom.headers.label": "Başlıqlar (istəyə bağlı)",
|
||||
"provider.custom.headers.key.label": "Başlıq",
|
||||
"provider.custom.headers.key.placeholder": "Başlıq-Adı",
|
||||
"provider.custom.headers.value.label": "Dəyər",
|
||||
"provider.custom.headers.value.placeholder": "dəyər",
|
||||
"provider.custom.headers.remove": "Başlığı sil",
|
||||
"provider.custom.headers.add": "Başlıq əlavə et",
|
||||
"provider.custom.error.providerID.required": "Provayder ID tələb olunur",
|
||||
"provider.custom.error.providerID.format": "Kiçik hərflər, rəqəmlər, tire və ya alt xətt istifadə edin",
|
||||
"provider.custom.error.providerID.exists": "Bu provayder ID artıq mövcuddur",
|
||||
"provider.custom.error.name.required": "Görünən ad tələb olunur",
|
||||
"provider.custom.error.baseURL.required": "Əsas URL tələb olunur",
|
||||
"provider.custom.error.baseURL.format": "http:// və ya https:// ilə başlamalıdır",
|
||||
"provider.custom.error.required": "Tələb olunur",
|
||||
"provider.custom.error.duplicate": "Təkrar",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} ayrıldı",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} modelləri artıq mövcud deyil.",
|
||||
"model.tag.free": "Pulsuz",
|
||||
"model.tag.latest": "Ən son",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "mətn",
|
||||
"model.input.image": "şəkil",
|
||||
"model.input.audio": "səs",
|
||||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Dəstəkləyir: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontekst",
|
||||
"model.tooltip.inputs": "Girişlər",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Provayder",
|
||||
"model.tooltip.reasoning": "Düşünmə",
|
||||
"model.tooltip.reasoning.allowed": "Düşünməni dəstəkləyir",
|
||||
"model.tooltip.reasoning.none": "Düşünmə yoxdur",
|
||||
"model.tooltip.context": "Kontekst limiti {{limit}}",
|
||||
"common.search.placeholder": "Axtar",
|
||||
"common.goBack": "Geri keç",
|
||||
"common.goForward": "İrəli keç",
|
||||
"common.loading": "Yüklənir",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Ləğv et",
|
||||
"common.clear": "Təmizlə",
|
||||
"common.open": "Aç",
|
||||
"common.connect": "Qoş",
|
||||
"common.disconnect": "Ayır",
|
||||
"common.continue": "Davam et",
|
||||
"common.submit": "Göndər",
|
||||
"common.save": "Saxla",
|
||||
"common.saving": "Saxlanılır...",
|
||||
"common.default": "Standart",
|
||||
"common.attachment": "əlavə",
|
||||
"prompt.placeholder.shell": "Qabıq əmrini daxil edin... {{example}}",
|
||||
"prompt.placeholder.normal": 'Nə istəsəniz soruşun... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Nə istəsəniz soruşun...",
|
||||
"prompt.placeholder.summarizeComments": "Şərhləri xülasə et…",
|
||||
"prompt.placeholder.summarizeComment": "Şərhi xülasə et…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "çıxmaq üçün esc",
|
||||
"session.child.promptDisabled": "Alt agent sessiyalarına prompt göndərmək olmaz.",
|
||||
"session.child.backToParent": "Əsas sessiyaya qayıt.",
|
||||
"prompt.example.1": "Kod bazasında TODO-nu həll et",
|
||||
"prompt.example.2": "Bu layihənin texnologiya yığını nədir?",
|
||||
"prompt.example.3": "Xətalı testləri düzəlt",
|
||||
"prompt.example.4": "Autentifikasiyanın necə işlədiyini izah et",
|
||||
"prompt.example.5": "Təhlükəsizlik boşluqlarını tap və düzəlt",
|
||||
"prompt.example.6": "İstifadəçi xidməti üçün vahid testləri əlavə et",
|
||||
"prompt.example.7": "Bu funksiyanı daha oxunaqlı hala gətir",
|
||||
"prompt.example.8": "Bu xəta nə deməkdir?",
|
||||
"prompt.example.9": "Bu problemi sazlamağa kömək et",
|
||||
"prompt.example.10": "API sənədləri yarat",
|
||||
"prompt.example.11": "Verilənlər bazası sorğularını optimallaşdır",
|
||||
"prompt.example.12": "Giriş doğrulaması əlavə et",
|
||||
"prompt.example.13": "Bunun üçün yeni komponent yarat...",
|
||||
"prompt.example.14": "Bu layihəni necə yerləşdirə bilərəm?",
|
||||
"prompt.example.15": "Kodumu qabaqcıl təcrübələr baxımından nəzərdən keçir",
|
||||
"prompt.example.16": "Bu funksiyaya xəta idarəetməsi əlavə et",
|
||||
"prompt.example.17": "Bu regex nümunəsini izah et",
|
||||
"prompt.example.18": "Bunu TypeScript-ə çevir",
|
||||
"prompt.example.19": "Bütün kod bazasına jurnallaşdırma əlavə et",
|
||||
"prompt.example.20": "Hansı asılılıqlar köhnəlib?",
|
||||
"prompt.example.21": "Miqrasiya skripti yazmağıma kömək et",
|
||||
"prompt.example.22": "Bu endpoint üçün keşləmə tətbiq et",
|
||||
"prompt.example.23": "Bu siyahıya səhifələmə əlavə et",
|
||||
"prompt.example.24": "Bunun üçün CLI əmri yarat...",
|
||||
"prompt.example.25": "Burada mühit dəyişənləri necə işləyir?",
|
||||
"prompt.popover.emptyResults": "Uyğun nəticə yoxdur",
|
||||
"prompt.popover.emptyCommands": "Uyğun əmr yoxdur",
|
||||
"prompt.dropzone.label": "Şəkilləri, PDF-ləri və ya mətn fayllarını bura buraxın",
|
||||
"prompt.dropzone.file.label": "Faylı @ ilə qeyd etmək üçün buraxın",
|
||||
"prompt.slash.badge.custom": "fərdi",
|
||||
"prompt.slash.badge.skill": "bacarıq",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "aktiv",
|
||||
"prompt.context.includeActiveFile": "Aktiv faylı daxil et",
|
||||
"prompt.context.removeActiveFile": "Aktiv faylı kontekstdən sil",
|
||||
"prompt.context.removeFile": "Faylı kontekstdən sil",
|
||||
"prompt.action.attachFile": "Fayllar əlavə et",
|
||||
"prompt.menu.addImagesAndFiles": "Fayllar və daha çox əlavə edin",
|
||||
"prompt.menu.imagesAndFiles": "Şəkillər və fayllar",
|
||||
"prompt.menu.commands": "Əmrlər",
|
||||
"prompt.menu.context": "Kontekst",
|
||||
"prompt.menu.shellCommand": "Shell əmri",
|
||||
"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.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Worktree yaradıla bilmədi",
|
||||
"prompt.toast.sessionCreateFailed.title": "Sessiya yaradıla bilmədi",
|
||||
"prompt.toast.shellSendFailed.title": "Shell əmri göndərilə bilmədi",
|
||||
"prompt.toast.commandSendFailed.title": "Əmr göndərilə bilmədi",
|
||||
"prompt.toast.promptSendFailed.title": "Prompt göndərilə bilmədi",
|
||||
"prompt.toast.promptSendFailed.description": "Sessiya əldə edilə bilmədi",
|
||||
"dialog.mcp.title": "MCP-lər",
|
||||
"dialog.mcp.description": "{{total}}-dən {{enabled}} aktiv",
|
||||
"dialog.mcp.empty": "MCP konfiqurasiya edilməyib",
|
||||
"dialog.lsp.empty": "LSP-lər fayl növlərindən avtomatik aşkarlanır",
|
||||
"dialog.plugins.empty": "Plaginlər opencode.json-da konfiqurasiya edilib",
|
||||
"mcp.status.connected": "qoşulub",
|
||||
"mcp.status.failed": "uğursuz",
|
||||
"mcp.status.needs_auth": "avtorizasiya lazımdır",
|
||||
"mcp.status.disabled": "deaktiv",
|
||||
"mcp.auth.clickToAuthenticate": "Autentifikasiya üçün klikləyin",
|
||||
"dialog.fork.empty": "Yeni sessiyaya ayırmaq üçün mesaj yoxdur",
|
||||
"dialog.directory.search.placeholder": "Qovluqları axtar",
|
||||
"dialog.directory.empty": "Qovluq tapılmadı",
|
||||
"dialog.directory.action.selectFile": "Fayl seçin",
|
||||
"dialog.directory.action.selectFolder": "Qovluğu seçin",
|
||||
"dialog.directory.root": "Kök",
|
||||
"dialog.directory.parent": "Üst qovluq",
|
||||
"dialog.directory.readError": "Bu qovluğu oxumaq mümkün deyil",
|
||||
"app.server.unreachable": "{{server}}-ə çatmaq mümkün olmadı",
|
||||
"app.server.retrying": "Avtomatik olaraq yenidən cəhd edilir...",
|
||||
"app.server.otherServers": "Digər serverlər",
|
||||
"dialog.server.title": "Serverlər",
|
||||
"dialog.server.description": "Bu tətbiqin qoşulduğu OpenCode serverini dəyişdirin.",
|
||||
"dialog.server.search.placeholder": "Serverləri axtar",
|
||||
"dialog.server.empty": "Hələ server yoxdur",
|
||||
"dialog.server.add.title": "Server əlavə et",
|
||||
"dialog.server.add.url": "Server ünvanı",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Serverə qoşulmaq mümkün olmadı",
|
||||
"dialog.server.add.checking": "Yoxlanılır...",
|
||||
"dialog.server.add.button": "Server əlavə et",
|
||||
"dialog.server.add.name": "Server adı (istəyə bağlı)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "İstifadəçi adı (istəyə bağlı)",
|
||||
"dialog.server.add.usernamePlaceholder": "istifadəçi adı",
|
||||
"dialog.server.add.password": "Şifrə (istəyə bağlı)",
|
||||
"dialog.server.add.passwordPlaceholder": "parol",
|
||||
"dialog.server.edit.title": "Serveri redaktə et",
|
||||
"dialog.server.default.title": "Standart server",
|
||||
"dialog.server.default.description":
|
||||
"Tətbiq açıldıqda yerli server başlatmaq əvəzinə bu serverə qoşulun. Yenidən başlatma tələb olunur.",
|
||||
"dialog.server.default.none": "Server seçilməyib",
|
||||
"dialog.server.default.set": "Cari serveri standart olaraq təyin et",
|
||||
"dialog.server.default.clear": "Təmizlə",
|
||||
"dialog.server.action.remove": "Serveri sil",
|
||||
"dialog.server.menu.edit": "Redaktə et",
|
||||
"dialog.server.menu.default": "Standart olaraq təyin et",
|
||||
"dialog.server.menu.defaultRemove": "Standartı sil",
|
||||
"dialog.server.menu.delete": "Sil",
|
||||
"dialog.server.current": "Cari server",
|
||||
"dialog.server.status.default": "Standart",
|
||||
"wsl.server.add": "WSL server əlavə edin",
|
||||
"wsl.server.addShort": "WSL əlavə edin",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL server",
|
||||
"wsl.server.retryStart": "Yenidən başlamağa cəhd edin",
|
||||
"wsl.server.updating": "Yenilənir...",
|
||||
"wsl.onboarding.step.distro": "Distribütiv seçin",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL yoxlanılır...",
|
||||
"wsl.onboarding.restartRequired":
|
||||
"WSL-nin quraşdırılmasını başa çatdırmaq üçün Windows-un yenidən başlaması lazımdır.",
|
||||
"wsl.onboarding.ready": "WSL hazırdır.",
|
||||
"wsl.onboarding.required": "Davam etmək üçün WSL tələb olunur.",
|
||||
"wsl.onboarding.checkingDistros": "Distribütivlər yoxlanılır...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} quraşdırılır...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} yoxlanılır...",
|
||||
"wsl.onboarding.listingDistros": "Distribütivlər siyahılanır...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} hazırdır.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} hələ quraşdırılmayıb.",
|
||||
"wsl.onboarding.openDistroOnce": "Quraşdırmanı bitirmək üçün {{distro}}-ni bir dəfə açın.",
|
||||
"wsl.onboarding.finishingDistro": "{{distro}} üçün quraşdırma tamamlanır.",
|
||||
"wsl.onboarding.pickDistro": "Distribütiv seçin və ya aşağıdakılardan birini quraşdırın.",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode yoxlanılır...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "{{distro}}-də OpenCode yoxlanılır...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode yenilənir...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "{{distro}}-də OpenCode yenilənir...",
|
||||
"wsl.onboarding.updateOpencodeIn": "{{distro}}-də OpenCode-u yeniləyin.",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode-u yeniləyin",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode {{distro}}-də hazırdır.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode hazırdır.",
|
||||
"wsl.onboarding.installOpencodeIn": "OpenCode-u {{distro}}-də quraşdırın.",
|
||||
"wsl.onboarding.installOpencode": "OpenCode quraşdırın",
|
||||
"wsl.onboarding.chooseDistroFirst": "Əvvəlcə distribütiv seçin.",
|
||||
"wsl.onboarding.loadFailed": "WSL vəziyyətini yükləmək alınmadı.",
|
||||
"wsl.onboarding.loading": "Yüklənir...",
|
||||
"wsl.onboarding.installedDistros": "Quraşdırılmış distribütivlər",
|
||||
"wsl.onboarding.checkAgain": "Yenidən yoxlayın",
|
||||
"wsl.onboarding.distroStatus.ready": "Hazır",
|
||||
"wsl.onboarding.distroStatus.checking": "Yoxlanır...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode quraşdırılmayıb",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Çatışmayan bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Dəstəklənmir · WSL 2 istifadə edin",
|
||||
"wsl.onboarding.needAnotherDistro": "Başqa distribütiv lazımdır?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "WSL kataloqundan Linux paylamasını quraşdırın",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL quraşdırılmayıb",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"OpenCode WSL serveri əlavə etməzdən əvvəl WSL (Linux üçün Windows alt sistemi) tələb olunur",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL mövcud deyil",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode bu maşında WSL-i doğrulaya bilmədi.",
|
||||
"wsl.onboarding.installWsl": "WSL quraşdırın",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"WSL-nin quraşdırılmasını başa çatdırmaq üçün Windows-u yenidən başladın, sonra OpenCode-u yenidən açın.",
|
||||
"wsl.onboarding.next": "Növbəti",
|
||||
"wsl.onboarding.refresh": "Yeniləyin",
|
||||
"wsl.onboarding.allDistrosAdded": "Bütün quraşdırılmış distribütivlər artıq əlavə edilib.",
|
||||
"wsl.onboarding.noDistros": "Hələ distribütiv aşkarlanmayıb.",
|
||||
"wsl.onboarding.install": "Quraşdırın",
|
||||
"wsl.onboarding.installing": "Quraşdırılır...",
|
||||
"wsl.onboarding.installDistro": "Distribütiv quraşdırın",
|
||||
"wsl.onboarding.searchDistros": "Distribütivləri axtar",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 tələb olunur.",
|
||||
"wsl.onboarding.toolsRequired": "Bu distribütiv üçün bash və curl tələb olunur.",
|
||||
"wsl.onboarding.openTerminal": "Terminalı aç",
|
||||
"wsl.onboarding.path": "Yol: {{path}}",
|
||||
"wsl.onboarding.notFound": "tapılmadı",
|
||||
"wsl.onboarding.version": "Versiya: {{version}}",
|
||||
"wsl.onboarding.unknown": "naməlum",
|
||||
"wsl.onboarding.desktopVersion": "masaüstü {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Quraşdırılmış versiya masaüstü proqram versiyasına uyğun gəlmir.",
|
||||
"wsl.onboarding.adding": "Əlavə edilir...",
|
||||
"help.tabs.toast.ariaLabel": "Tablarla tanışlıq. İşinizi və aktiv sessiyalarınızı tablarla təşkil edin",
|
||||
"help.tabs.toast.dismiss": "Tablar haqqında məlumatı bağla",
|
||||
"help.tabs.title": "Tablarla tanışlıq",
|
||||
"help.tabs.description": "İşinizi və aktiv sessiyalarınızı tablarla təşkil edin",
|
||||
"help.tabs.date": "14 iyul",
|
||||
"help.tabs.introduction": "OpenCode Desktop indi tablar əsasında qurulub.",
|
||||
"help.tabs.sessions":
|
||||
"Yeni sessiyanı tabda başladın və ya layihələrinizdən mövcud sessiyanı açın. Yeni işə başlayanda yeni tab açın, işi bitirdikdə isə onu bağlayın.",
|
||||
"help.tabs.organize":
|
||||
"Bir neçə tabı açıq saxlamaq aktiv sessiyalarınızı təşkil etməyi asanlaşdırır. Onları saxlamağı planlaşdırırsınızsa, tablara yadda qalan adlar verin.",
|
||||
"help.tabs.home":
|
||||
"Bütün sessiya və layihələrinizi yeni Ana səhifədə tapa bilərsiniz. Sessiyanı seçdikdə o, tabda açılır.",
|
||||
"help.tabs.persistence": "Tətbiqi yenidən açdıqda tablarınız açıq qalır.",
|
||||
"help.tabs.worktrees":
|
||||
"Yeni dizayn hələ Git Worktrees-i dəstəkləmir, lakin bu imkan tezliklə əlavə olunacaq. Əvvəlki tərtibatdan istifadə etməyə davam etmək istəyirsinizsə, Tənzimləmələrdə tərtibatlar arasında keçid edə bilərsiniz. Nəzərə alın ki, yeni tərtibat bir neçə həftədən sonra daimi olacaq.",
|
||||
"server.row.noUsername": "istifadəçi adı yoxdur",
|
||||
"dialog.project.edit.title": "Layihəni redaktə et",
|
||||
"dialog.project.edit.name": "Ad",
|
||||
"dialog.project.edit.icon": "İkon",
|
||||
"dialog.project.edit.icon.alt": "Layihə ikonu",
|
||||
"dialog.project.edit.icon.hint": "Şəkilə klikləyin və ya onu sürükləyin",
|
||||
"dialog.project.edit.icon.recommended": "Tövsiyə: 128x128px",
|
||||
"dialog.project.edit.color": "Rəng",
|
||||
"dialog.project.edit.color.select": "{{color}} rəngini seçin",
|
||||
"dialog.project.edit.worktree.startup": "İş sahəsi başlanğıc skripti",
|
||||
"dialog.project.edit.worktree.startup.description": "Yeni iş sahəsi (worktree) yaradıldıqdan sonra işləyir.",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "məs. bun install",
|
||||
"dialog.releaseNotes.action.getStarted": "Başla",
|
||||
"dialog.releaseNotes.action.next": "Növbəti",
|
||||
"dialog.releaseNotes.action.hideFuture": "Gələcəkdə göstərmə",
|
||||
"dialog.releaseNotes.media.alt": "Buraxılış önbaxışı",
|
||||
"dialog.usageExceeded.dontShowAgain": "Bir daha göstərmə",
|
||||
|
||||
"context.breakdown.title": "Kontekst bölgüsü",
|
||||
"context.breakdown.note": 'Giriş tokenlərinin təxmini bölgüsü. "Digər" alət təriflərini və əlavə yükü əhatə edir.',
|
||||
"context.breakdown.system": "Sistem",
|
||||
"context.breakdown.user": "İstifadəçi",
|
||||
"context.breakdown.assistant": "Köməkçi",
|
||||
"context.breakdown.tool": "Alət çağırışları",
|
||||
"context.breakdown.other": "Digər",
|
||||
"context.systemPrompt.title": "Sistem promptu",
|
||||
"context.rawMessages.title": "Xam mesajlar",
|
||||
"context.export.session": "Sessiyanı ixrac et",
|
||||
|
||||
"context.stats.session": "Sessiya",
|
||||
"context.stats.messages": "Mesajlar",
|
||||
"context.stats.provider": "Provayder",
|
||||
"context.stats.model": "Model",
|
||||
"context.stats.limit": "Kontekst limiti",
|
||||
"context.stats.totalTokens": "Ümumi tokenlər",
|
||||
"context.stats.usage": "İstifadə",
|
||||
"context.stats.inputTokens": "Giriş tokenləri",
|
||||
"context.stats.outputTokens": "Çıxış tokenləri",
|
||||
"context.stats.reasoningTokens": "Düşünmə tokenləri",
|
||||
"context.stats.cacheTokens": "Keş tokenləri (oxuma/yazma)",
|
||||
"context.stats.userMessages": "İstifadəçi mesajları",
|
||||
"context.stats.assistantMessages": "Köməkçi mesajları",
|
||||
"context.stats.totalCost": "Ümumi xərc",
|
||||
"context.stats.sessionCreated": "Sessiya yaradıldı",
|
||||
"context.stats.lastActivity": "Son fəaliyyət",
|
||||
"context.usage.tokens": "Tokenlər",
|
||||
"context.usage.usage": "İstifadə",
|
||||
"context.usage.cost": "Xərc",
|
||||
"context.usage.clickToView": "Kontekstə baxmaq üçün klikləyin",
|
||||
"context.usage.view": "Kontekst istifadəsinə bax",
|
||||
"language.en": "English",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "Deutsch",
|
||||
"language.es": "Español",
|
||||
"language.fr": "Français",
|
||||
"language.da": "Dansk",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Polski",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Dil",
|
||||
"toast.language.description": "{{language}} dilinə keçildi",
|
||||
"toast.theme.title": "Mövzu dəyişdirildi",
|
||||
"toast.scheme.title": "Rəng sxemi",
|
||||
"toast.workspace.enabled.title": "İş sahələri aktivləşdirildi",
|
||||
"toast.workspace.enabled.description": "Birdən çox worktree indi yan paneldə göstərilir",
|
||||
"toast.workspace.disabled.title": "İş sahələri deaktiv edildi",
|
||||
"toast.workspace.disabled.description": "Yan paneldə yalnız əsas worktree göstərilir",
|
||||
"toast.permissions.autoaccept.on.title": "İcazələr avtomatik qəbul edilir",
|
||||
"toast.permissions.autoaccept.on.description": "İcazə sorğuları avtomatik təsdiqlənəcək",
|
||||
"toast.permissions.autoaccept.off.title": "İcazələrin avtomatik qəbulu dayandırıldı",
|
||||
"toast.permissions.autoaccept.off.description": "İcazə sorğuları təsdiq tələb edəcək",
|
||||
"toast.model.none.title": "Model seçilməyib",
|
||||
"toast.model.none.description": "Bu sessiyanı xülasə etmək üçün provayder qoşun",
|
||||
"toast.file.loadFailed.title": "Fayl yüklənə bilmədi",
|
||||
"toast.file.listFailed.title": "Fayllar siyahılana bilmədi",
|
||||
"toast.context.noLineSelection.title": "Sətir seçimi yoxdur",
|
||||
"toast.context.noLineSelection.description": "Əvvəlcə fayl tabında sətir aralığı seçin.",
|
||||
"toast.session.share.copyFailed.title": "URL buferə kopyalana bilmədi",
|
||||
"toast.session.share.success.title": "Sessiya paylaşıldı",
|
||||
"toast.session.share.success.description": "Paylaşma URL-si buferə kopyalandı!",
|
||||
"toast.session.share.failed.title": "Sessiya paylaşıla bilmədi",
|
||||
"toast.session.share.failed.description": "Sessiyanı paylaşarkən xəta baş verdi",
|
||||
"toast.session.unshare.success.title": "Sessiyanın paylaşımı dayandırıldı",
|
||||
"toast.session.unshare.success.description": "Sessiyanın paylaşımı uğurla dayandırıldı!",
|
||||
"toast.session.unshare.failed.title": "Sessiyanın paylaşımı dayandırıla bilmədi",
|
||||
"toast.session.unshare.failed.description": "Sessiyanın paylaşımını dayandırarkən xəta baş verdi",
|
||||
"toast.session.export.success.title": "Sessiya ixrac edildi",
|
||||
"toast.session.export.success.description": "Sessiya {{filename}} faylına saxlanıldı",
|
||||
"toast.session.export.failed.title": "Sessiya ixrac edilə bilmədi",
|
||||
"toast.session.export.failed.description": "Sessiyanı ixrac edərkən xəta baş verdi",
|
||||
|
||||
"toast.session.listFailed.title": "{{project}} üçün sessiyalar yüklənə bilmədi",
|
||||
"toast.project.reloadFailed.title": "{{project}} yenidən yüklənə bilmədi",
|
||||
"toast.update.title": "Yeniləmə mövcuddur",
|
||||
"toast.update.description": "OpenCode-un yeni versiyası ({{version}}) quraşdırma üçün mövcuddur.",
|
||||
"toast.update.action.installRestart": "Quraşdır və yenidən başlat",
|
||||
"toast.update.action.notYet": "Hələ yox",
|
||||
"error.page.title": "Xəta baş verdi",
|
||||
"error.page.description": "Tətbiq yüklənərkən xəta baş verdi.",
|
||||
"error.page.description.localServerStartup": "Lokal serveri işə salarkən xəta baş verdi.",
|
||||
"error.page.details.label": "Xəta detalları",
|
||||
"error.page.action.restart": "Yenidən başlat",
|
||||
"error.page.action.report": "Xəta bildirin",
|
||||
"error.page.action.reported": "Xəta bildirildi",
|
||||
"error.page.action.exportLogs": "Qeydləri ixrac edin",
|
||||
"error.page.action.checking": "Yoxlanılır...",
|
||||
"error.page.action.checkUpdates": "Yeniləmələri yoxla",
|
||||
"error.page.action.updateTo": "{{version}} versiyasına yenilə",
|
||||
"error.page.circular": "[Dairəvi]",
|
||||
"error.page.report.prefix": "Bu xətanı OpenCode komandasına",
|
||||
"error.page.report.discord": "Discord-da bildirin",
|
||||
"error.page.version": "Versiya: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"Kök element tapılmadı. index.html-ə əlavə etməyi unutmusunuz? Yoxsa id atributu səhv yazılıb?",
|
||||
"error.serverSync.connectFailed": "Serverə qoşulmaq mümkün olmadı. `{{url}}`-də işləyən server varmı?",
|
||||
"error.serverSDK.noServerAvailable": "Əlçatan server yoxdur",
|
||||
"error.serverSDK.serverNotAvailable": "Server mövcud deyil",
|
||||
"error.childStore.persistedCacheCreateFailed": "Qalıcı keş yaradıla bilmədi",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "Qalıcı layihə metadatası yaradıla bilmədi",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Qalıcı layihə nişanı yaradıla bilmədi",
|
||||
"error.childStore.storeCreateFailed": "Saxlanc yaradıla bilmədi",
|
||||
"directory.error.invalidUrl": "URL-də yanlış qovluq.",
|
||||
"error.chain.unknown": "Naməlum xəta",
|
||||
"error.server.invalidConfiguration": "Yanlış konfiqurasiya",
|
||||
"error.chain.causedBy": "Səbəb:",
|
||||
"error.chain.apiError": "API xətası",
|
||||
"error.chain.status": "Status: {{status}}",
|
||||
"error.chain.retryable": "Təkrar cəhd mümkündür: {{retryable}}",
|
||||
"error.chain.responseBody": "Cavab gövdəsi:\n{{body}}",
|
||||
"error.chain.didYouMean": "Bunu nəzərdə tuturdunuz: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model tapılmadı: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Konfiqurasiyanızdakı (opencode.json) provayder/model adlarını yoxlayın",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP server "{{name}}" uğursuz oldu. Qeyd: OpenCode hələ MCP autentifikasiyasını dəstəkləmir.',
|
||||
"error.chain.providerAuthFailed": "Provayder autentifikasiyası uğursuz oldu ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'"{{provider}}" provayderinin işə salınması uğursuz oldu. Giriş məlumatlarını və konfiqurasiyanı yoxlayın.',
|
||||
"error.chain.configJsonInvalid": "{{path}} ünvanındakı konfiqurasiya faylı etibarlı JSON(C) deyil",
|
||||
"error.chain.configJsonInvalidWithMessage":
|
||||
"{{path}} ünvanındakı konfiqurasiya faylı etibarlı JSON(C) deyil: {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'{{path}} içindəki "{{dir}}" qovluğu etibarlı deyil. Qovluğu "{{suggestion}}" adına dəyişdirin və ya silin. Bu ümumi bir yazım xətasıdır.',
|
||||
"error.chain.configFrontmatterError": "{{path}} faylındakı frontmatter təhlil edilə bilmədi:\n{{message}}",
|
||||
"error.chain.configInvalid": "{{path}} ünvanındakı konfiqurasiya faylı etibarsızdır",
|
||||
"error.chain.configInvalidWithMessage": "{{path}} ünvanındakı konfiqurasiya faylı etibarsızdır: {{message}}",
|
||||
"notification.permission.title": "İcazə tələb olunur",
|
||||
"notification.permission.description": "{{projectName}} layihəsində {{sessionTitle}} icazə tələb edir",
|
||||
"notification.question.title": "Sual",
|
||||
"notification.question.description": "{{projectName}} layihəsində {{sessionTitle}} sualı var",
|
||||
"notification.action.goToSession": "Sessiyaya keç",
|
||||
"notification.session.responseReady.title": "Cavab hazırdır",
|
||||
"notification.session.error.title": "Sessiya xətası",
|
||||
"notification.session.error.fallbackDescription": "Xəta baş verdi",
|
||||
"home.recentProjects": "Son layihələr",
|
||||
"home.empty.title": "Son layihə yoxdur",
|
||||
"home.empty.description": "Yerli layihə açaraq başlayın",
|
||||
"home.title": "Ana səhifə",
|
||||
"home.projects": "Layihələr",
|
||||
"home.project.add": "Layihə əlavə edin",
|
||||
"home.recentlyClosed": "Bu yaxınlarda bağlandı",
|
||||
"home.server.collapse": "Server layihələrini yığışdırın",
|
||||
"home.server.expand": "Server layihələrini genişləndirin",
|
||||
"home.sessions.search.placeholder": "Sessiyaları axtar",
|
||||
"home.sessions.search.placeholder.scoped": "{{scope}}-də sessiyaları axtarın",
|
||||
"home.sessions.search.sessions": "Sessiyalar",
|
||||
"home.sessions.search.noResults": "{{query}} üçün sessiya tapılmadı",
|
||||
"home.sessions.empty": "Burada hələ heç nə yoxdur",
|
||||
"home.sessions.empty.description": "Başlamaq üçün sessiya yaradın",
|
||||
"home.sessions.group.today": "Bu gün",
|
||||
"home.sessions.group.yesterday": "Dünən",
|
||||
"home.sessions.group.older": "Daha əvvəl",
|
||||
"home.providerTip":
|
||||
"Claude, GPT, Gemini və s. daxil olmaqla, digər modellərdən istifadə etmək üçün 75+ provayderə qoşulun",
|
||||
"session.tab.session": "Sessiya",
|
||||
"session.tab.review": "İcmal",
|
||||
"session.tab.context": "Kontekst",
|
||||
"session.tab.unknown": "Naməlum sessiya",
|
||||
"session.panel.reviewAndFiles": "İcmal və fayllar",
|
||||
"session.error.notFound": "Bu sessiyanı tapmaq mümkün deyil",
|
||||
"session.error.notFound.description": "Bu tab artıq bu serverdə olmayan sessiyaya işarə edir.",
|
||||
"session.error.notFound.closeTab": "Tabı bağla",
|
||||
"session.error.serverConnection": "Bu serverə qoşulmaq mümkün deyil",
|
||||
"session.review.filesChanged": "{{count}} fayl dəyişdi",
|
||||
"session.review.change.one": "Dəyişiklik",
|
||||
"session.review.change.other": "Dəyişikliklər",
|
||||
"session.review.loadingChanges": "Dəyişikliklər yüklənir...",
|
||||
"session.review.empty": "Bu sessiyada hələ dəyişiklik yoxdur",
|
||||
"session.review.noVcs": "Git versiyaya nəzarət sistemi aşkarlanmadı; sessiya dəyişiklikləri göstərilməyəcək",
|
||||
"session.review.noVcs.createGit.title": "Git repozitoriyası yaradın",
|
||||
"session.review.noVcs.createGit.description": "Bu layihədə dəyişiklikləri izləyin, nəzərdən keçirin və ləğv edin",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git repozitoriyası yaradılır...",
|
||||
"session.review.noVcs.createGit.action": "Git repozitoriyası yaradın",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot izləmə konfiqurasiyada deaktiv edilib, ona görə də sessiya dəyişiklikləri əlçatan deyil",
|
||||
"session.review.noChanges": "Dəyişiklik yoxdur",
|
||||
"session.review.noUncommittedChanges": "Hələ commit edilməmiş dəyişiklik yoxdur",
|
||||
"session.review.noBranchChanges": "Hələ branch dəyişikliyi yoxdur",
|
||||
"session.files.selectToOpen": "Açmaq üçün fayl seçin",
|
||||
"session.files.all": "Bütün fayllar",
|
||||
"session.files.empty": "Fayl yoxdur",
|
||||
"session.files.binaryContent": "İkili fayl (məzmun göstərilə bilmir)",
|
||||
"session.messages.renderEarlier": "Əvvəlki mesajları göstər",
|
||||
"session.messages.loadingEarlier": "Əvvəlki mesajlar yüklənir...",
|
||||
"session.messages.loadEarlier": "Əvvəlki mesajları yüklə",
|
||||
"session.messages.loading": "Mesajlar yüklənir...",
|
||||
"session.messages.jumpToLatest": "Ən sonuncuya keç",
|
||||
"session.context.addToContext": "{{selection}} kontekstə əlavə et",
|
||||
"session.todo.title": "Tapşırıqlar",
|
||||
"session.todo.collapse": "Yığ",
|
||||
"session.todo.expand": "Genişlət",
|
||||
"session.todo.progress": "{{total}} tapşırıqdan {{done}}-i tamamlandı",
|
||||
"session.question.progress": "{{total}} sualdan {{current}}-i",
|
||||
"session.question.minimize": "Sualı kiçilt",
|
||||
"session.question.restore": "Sualı bərpa edin",
|
||||
"session.question.pending.one": "{{count}} cavab gözləyən sual",
|
||||
"session.question.pending.other": "{{count}} cavab gözləyən sual",
|
||||
"session.followupDock.summary.one": "{{count}} növbəyə qoyulmuş mesaj",
|
||||
"session.followupDock.summary.other": "{{count}} növbəyə qoyulmuş mesajlar",
|
||||
"session.followupDock.sendNow": "İndi göndər",
|
||||
"session.followupDock.edit": "Redaktə et",
|
||||
"session.followupDock.collapse": "Növbəyə qoyulmuş mesajları yığcamlaşdırın",
|
||||
"session.followupDock.expand": "Növbəyə qoyulmuş mesajları genişləndirin",
|
||||
"session.revertDock.summary.one": "{{count}} geri alınmış mesaj",
|
||||
"session.revertDock.summary.other": "{{count}} geri alınmış mesaj",
|
||||
"session.revertDock.collapse": "Geri alınmış mesajları yığcamlaşdırın",
|
||||
"session.revertDock.expand": "Geri qaytarılmış mesajları genişləndirin",
|
||||
"session.revertDock.restore": "Mesajı bərpa edin",
|
||||
"session.new.title": "İstədiyinizi qurun",
|
||||
"session.new.project.new": "Yeni layihə",
|
||||
"session.new.project.search": "Layihələri axtarın",
|
||||
"session.new.project.add": "Layihə əlavə edin",
|
||||
"session.new.worktree.main": "Əsas branch",
|
||||
"session.new.worktree.mainWithBranch": "Əsas branch ({{branch}})",
|
||||
"session.new.worktree.create": "Yeni worktree yarat",
|
||||
"session.new.workspace.runIn": "Sessiyanı burada işə sal",
|
||||
"session.new.workspace.triggerLocal": "Yerli",
|
||||
"session.new.workspace.local": "Yerli depo",
|
||||
"session.new.workspace.existing": "İş sahəsi…",
|
||||
"session.new.git.none": "Git yoxdur",
|
||||
"session.new.lastModified": "Son dəyişiklik",
|
||||
"session.header.search.placeholder": "{{project}} axtar",
|
||||
"session.header.searchFiles": "Faylları axtar",
|
||||
"session.header.openIn": "Burada aç:",
|
||||
"session.header.open.action": "{{app}} tətbiqini aç",
|
||||
"session.header.open.ariaLabel": "{{app}} tətbiqində aç",
|
||||
"session.header.open.menu": "Seçimləri aç",
|
||||
"session.header.open.copyPath": "Yolu kopyala",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.open.fileManager": "Fayl meneceri",
|
||||
"session.header.reveal.finder": "Finder-də göstər",
|
||||
"session.header.reveal.fileExplorer": "File Explorer-də göstər",
|
||||
"session.header.reveal.containingFolder": "Tərkibindəki qovluğu açın",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "Terminal",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.ariaLabel": "Server konfiqurasiyaları",
|
||||
"status.popover.tab.servers": "Serverlər",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Plaginlər",
|
||||
"status.popover.action.manageServers": "Serverləri idarə et",
|
||||
"session.share.popover.title": "Vebdə dərc et",
|
||||
"session.share.popover.description.shared": "Bu sessiya vebdə açıqdır. Linkə sahib olan hər kəs daxil ola bilər.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Sessiyanı vebdə açıq paylaşın. Linkə sahib olan hər kəs daxil ola biləcək.",
|
||||
"session.share.action.share": "Paylaş",
|
||||
"session.share.action.publish": "Dərc et",
|
||||
"session.share.action.publishing": "Dərc edilir...",
|
||||
"session.share.action.unpublish": "Dərcdən çıxar",
|
||||
"session.share.action.unpublishing": "Dərcdən çıxarılır...",
|
||||
"session.share.action.view": "Bax",
|
||||
"session.share.copy.copied": "Kopyalandı",
|
||||
"session.share.copy.copyLink": "Linki kopyala",
|
||||
"lsp.tooltip.none": "LSP server yoxdur",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "Prompt yüklənir...",
|
||||
"terminal.loading": "Terminal yüklənir...",
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Terminalı bağla",
|
||||
"terminal.connectionLost.title": "Bağlantı kəsildi",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket anormal bağlandı: {{code}}",
|
||||
"terminal.connectionLost.description": "Terminal bağlantısı kəsildi. Bu server yenidən başladıqda baş verə bilər.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY bağlantı bileti mənşə və ya CSRF yoxlaması zamanı rədd edildi. Serverin CORS konfiqurasiyasını yoxlayın.",
|
||||
"terminal.connectTicket.statusError": "PTY bağlantı bileti {{status}} statusu ilə uğursuz oldu",
|
||||
"titlebar.update": "Yenilə",
|
||||
"titlebar.updateVersion": "Yenilə {{version}}",
|
||||
"common.closeTab": "Tabı bağla",
|
||||
"common.dismiss": "Bağla",
|
||||
"common.moreCountSuffix": " (+{{count}} daha)",
|
||||
"common.requestFailed": "Sorğu uğursuz oldu",
|
||||
"common.moreOptions": "Daha çox seçim",
|
||||
"common.learnMore": "Ətraflı öyrən",
|
||||
"common.rename": "Adını dəyiş",
|
||||
"common.export": "İxrac et",
|
||||
"common.reset": "Sıfırla",
|
||||
"common.archive": "Arxivlə",
|
||||
"common.delete": "Sil",
|
||||
"common.close": "Bağla",
|
||||
"common.edit": "Redaktə et",
|
||||
"common.loadMore": "Daha çox yüklə",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Boşluq",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Sil",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "Son",
|
||||
"common.key.pageUp": "Səhifə Yuxarı",
|
||||
"common.key.pageDown": "Səhifə Aşağı",
|
||||
"common.key.insert": "Daxil et",
|
||||
"common.unknown": "naməlum",
|
||||
"common.time.justNow": "İndicə",
|
||||
"common.time.minutesAgo.short": "{{count}}d əvvəl",
|
||||
"common.time.hoursAgo.short": "{{count}}s əvvəl",
|
||||
"common.time.daysAgo.short": "{{count}}g əvvəl",
|
||||
"sidebar.menu.toggle": "Menyunu aç/bağla",
|
||||
"sidebar.nav.projectsAndSessions": "Layihələr və sessiyalar",
|
||||
"sidebar.settings": "Tənzimləmələr",
|
||||
"sidebar.help": "Kömək",
|
||||
"sidebar.workspaces.enable": "İş sahələrini aktivləşdir",
|
||||
"sidebar.workspaces.disable": "İş sahələrini deaktiv et",
|
||||
"sidebar.gettingStarted.title": "Başlanğıc",
|
||||
"sidebar.gettingStarted.line1": "OpenCode-a pulsuz modellər daxildir, buna görə dərhal başlaya bilərsiniz.",
|
||||
"sidebar.gettingStarted.line2":
|
||||
"Claude, GPT, Gemini və s. kimi modellərdən istifadə etmək üçün istənilən provayderi qoşun.",
|
||||
"sidebar.project.recentSessions": "Son sessiyalar",
|
||||
"sidebar.project.viewAllSessions": "Bütün sessiyalara bax",
|
||||
"sidebar.project.clearNotifications": "Bildirişləri təmizlə",
|
||||
"sidebar.empty.title": "Heç bir layihə açılmayıb",
|
||||
"sidebar.empty.description": "Başlamaq üçün bir layihə açın",
|
||||
"debugBar.ariaLabel": "Tərtibatçı performans diaqnostikası",
|
||||
"debugBar.na": "yox",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Sessiya səhifəsinə aid son tamamlanmış marşrut keçidi; marşrutlaşdırıcının başladığı andan keçid tamamlandıqdan sonrakı ilk göstərilməyədək ölçülür.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Son 5 saniyə üzrə saniyədə kadrların hərəkətli orta qiyməti.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Son 5 saniyədə ən uzun kadr müddəti.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Son 5 saniyədə 32 ms-dən uzun çəkən kadrlar.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Son 5 saniyədə bloklanma müddəti və uzun tapşırıqların sayı. Ən uzun tapşırıq: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "Son 5 saniyədə müşahidə olunan ən uzun daxiletmə gecikməsi.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Son 5 saniyədə təxmini qarşılıqlı əlaqə müddəti. Bu, rəsmi Web Vitals INP göstəricisi deyil, ona bənzər göstəricidir.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Tətbiqin cari işləmə müddəti üzrə məcmu tərtibat sürüşməsi.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "İstifadə olunmuş JS yığını və yığın limiti. Yalnız Chromium.",
|
||||
"debugBar.mem.tip": "İstifadə olunmuş JS yığını və yığın limiti. {{used}} və {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Bütün interaktiv elementlərə fokus üslublarını məcburi tətbiq et",
|
||||
"debugBar.focus.on": "AÇIQ",
|
||||
"debugBar.focus.off": "QAPALI",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "Seçilmiş dili dəyişmədən bütün tətbiq üçün tərtibat istiqamətini məcburi təyin et",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Masaüstü",
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Ümumi",
|
||||
"settings.tab.shortcuts": "Qısa yollar",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL inteqrasiyası",
|
||||
"settings.desktop.wsl.description": "Windows-da WSL daxilində OpenCode serverini işlət.",
|
||||
"settings.general.section.appearance": "Görünüş",
|
||||
"settings.general.section.advanced": "Qabaqcıl",
|
||||
"settings.general.section.notifications": "Sistem bildirişləri",
|
||||
"settings.general.section.updates": "Yeniləmələr",
|
||||
"settings.general.section.sounds": "Səs effektləri",
|
||||
"settings.general.section.feed": "Lenta",
|
||||
"settings.general.section.display": "Ekran",
|
||||
"settings.general.row.language.title": "Dil",
|
||||
"settings.general.row.language.description": "OpenCode üçün ekran dilini dəyişdirin",
|
||||
"settings.general.row.shell.title": "Terminal qabığı",
|
||||
"settings.general.row.shell.description": "Terminal və agent alətləri tərəfindən istifadə edilən qabıq",
|
||||
"settings.general.row.shell.autoDefault": "Avtomatik (Standart)",
|
||||
"settings.general.row.shell.terminalOnly": "yalnız terminal",
|
||||
"settings.general.row.appearance.title": "Görünüş",
|
||||
"settings.general.row.appearance.description": "OpenCode-un cihazınızda necə göründüyünü fərdiləşdirin",
|
||||
"settings.general.row.colorScheme.title": "Rəng sxemi",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"OpenCode-un sistem, açıq və ya tünd mövzudan istifadə etməsini seçin",
|
||||
"settings.general.row.theme.title": "Mövzu",
|
||||
"settings.general.row.theme.description": "OpenCode-un mövzusunu fərdiləşdirin.",
|
||||
"settings.general.row.font.title": "Kod şrifti",
|
||||
"settings.general.row.font.description": "Kod bloklarında istifadə olunan mono şrifti fərdiləşdirin",
|
||||
"settings.general.row.terminalFont.title": "Terminal şrifti",
|
||||
"settings.general.row.terminalFont.description": "Terminalda istifadə olunan şrifti fərdiləşdirin",
|
||||
"settings.general.row.uiFont.title": "İnterfeys şrifti",
|
||||
"settings.general.row.uiFont.description": "Bütün interfeysdə istifadə olunan şrifti fərdiləşdirin",
|
||||
"settings.general.row.followup.title": "Sonrakı sorğuların davranışı",
|
||||
"settings.general.row.followup.description":
|
||||
"Sonrakı sorğuların dərhal yönləndirilməsini və ya növbədə gözləməsini seçin",
|
||||
"settings.general.row.followup.option.queue": "Növbə",
|
||||
"settings.general.row.followup.option.steer": "Yönləndir",
|
||||
"settings.general.row.showFileTree.title": "Fayl ağacı",
|
||||
"settings.general.row.showFileTree.description": "Seanslarda fayl ağacı panelini göstərin",
|
||||
"settings.general.row.showNavigation.title": "Naviqasiya nəzarətləri",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Masaüstü tətbiqin başlıq zolağında geri və irəli düymələrini göstərin",
|
||||
"settings.general.row.showSearch.title": "Komanda palitrası",
|
||||
"settings.general.row.showSearch.description": "Başlıq çubuğunda axtarış və əmr palitrası düyməsini göstərin",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Masaüstü tətbiqin başlıq zolağında terminal düyməsini göstərin",
|
||||
"settings.general.row.showStatus.title": "Server statusu",
|
||||
"settings.general.row.showStatus.description": "Başlıq çubuğunda server statusu düyməsini göstərin",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Aşağı naviqasiya",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Başlıq çubuğunu və sessiya tablarını mobil cihazda ekranın aşağısına yerləşdirin",
|
||||
"settings.general.row.showCustomAgents.title": "Agenti göstər",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Mesaj sahəsində agentlər arasında keçid edin. Gizlədildikdə Build agenti seçilir.",
|
||||
"settings.general.row.reasoningSummaries.title": "Düşünmə xülasələrini göstər",
|
||||
"settings.general.row.reasoningSummaries.description": "Zaman xəttində modelin düşünmə xülasələrini göstər",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Shell alət hissələrini genişlət",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Zaman xəttində shell alət hissələrini standart olaraq genişlədilmiş göstər",
|
||||
"settings.general.row.editToolPartsExpanded.title": "Redaktə alət hissələrini genişlət",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Zaman xəttində redaktə, yazma və patch alət hissələrini standart olaraq genişlədilmiş göstər",
|
||||
"settings.general.row.newInterface.title": "Yeni tərtibat",
|
||||
"settings.general.row.newInterface.badge": "Yeni",
|
||||
"settings.general.row.newInterface.description":
|
||||
"Yeni tab və ana səhifə tərtibatından istifadə edin. Məhdud müddət ərzində tərtibatlar arasında keçid edin.",
|
||||
"settings.general.row.newInterfaceNotice.title": "İndi yeni tərtibatdan istifadə edirsiniz",
|
||||
"settings.general.row.newInterfaceNotice.description": "Əvvəlki tərtibat artıq mövcud deyil",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Rədd et",
|
||||
"settings.general.row.pinchZoom.title": "Böyütmək üçün çimdikləyin",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Trekpadda çimdikləmə və Ctrl-sürüşdürmə jestləri ilə miqyaslamağa icazə verin",
|
||||
"settings.general.row.wayland.title": "Nativ Wayland-dan istifadə et",
|
||||
"settings.general.row.wayland.description": "Wayland-da X11 geri dönüşünü deaktiv et. Yenidən başlatma tələb olunur.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Qarışıq yeniləmə tezlikli monitorlu Linux-da nativ Wayland daha sabit ola bilər.",
|
||||
"settings.general.row.releaseNotes.title": "Buraxılış qeydləri",
|
||||
"settings.general.row.releaseNotes.description": "Yeniləmələrdən sonra yenilikləri göstər",
|
||||
"settings.updates.row.startup.title": "Başlanğıcda yeniləmələri yoxla",
|
||||
"settings.updates.row.startup.description": "OpenCode açıldıqda avtomatik yeniləmələri yoxla",
|
||||
"settings.updates.row.check.title": "Yeniləmələri yoxla",
|
||||
"settings.updates.row.check.description": "Əl ilə yeniləmələri yoxla və mövcud olduqda quraşdır",
|
||||
"settings.updates.action.checkNow": "İndi yoxla",
|
||||
"settings.updates.action.checking": "Yoxlanılır...",
|
||||
"settings.updates.action.downloading": "Yüklənir...",
|
||||
"settings.updates.action.installing": "Quraşdırılır...",
|
||||
"settings.updates.toast.latest.title": "Ən son versiyadan istifadə edirsiniz",
|
||||
"settings.updates.toast.latest.description": "OpenCode-un ən son versiyasından istifadə edirsiniz.",
|
||||
"sound.option.none": "Yoxdur",
|
||||
"sound.option.alert01": "Alert 01",
|
||||
"sound.option.alert02": "Alert 02",
|
||||
"sound.option.alert03": "Alert 03",
|
||||
"sound.option.alert04": "Alert 04",
|
||||
"sound.option.alert05": "Alert 05",
|
||||
"sound.option.alert06": "Alert 06",
|
||||
"sound.option.alert07": "Alert 07",
|
||||
"sound.option.alert08": "Alert 08",
|
||||
"sound.option.alert09": "Alert 09",
|
||||
"sound.option.alert10": "Alert 10",
|
||||
"sound.option.bipbop01": "Bip-bop 01",
|
||||
"sound.option.bipbop02": "Bip-bop 02",
|
||||
"sound.option.bipbop03": "Bip-bop 03",
|
||||
"sound.option.bipbop04": "Bip-bop 04",
|
||||
"sound.option.bipbop05": "Bip-bop 05",
|
||||
"sound.option.bipbop06": "Bip-bop 06",
|
||||
"sound.option.bipbop07": "Bip-bop 07",
|
||||
"sound.option.bipbop08": "Bip-bop 08",
|
||||
"sound.option.bipbop09": "Bip-bop 09",
|
||||
"sound.option.bipbop10": "Bip-bop 10",
|
||||
"sound.option.staplebops01": "Staplebops 01",
|
||||
"sound.option.staplebops02": "Staplebops 02",
|
||||
"sound.option.staplebops03": "Staplebops 03",
|
||||
"sound.option.staplebops04": "Staplebops 04",
|
||||
"sound.option.staplebops05": "Staplebops 05",
|
||||
"sound.option.staplebops06": "Staplebops 06",
|
||||
"sound.option.staplebops07": "Staplebops 07",
|
||||
"sound.option.nope01": "Nope 01",
|
||||
"sound.option.nope02": "Nope 02",
|
||||
"sound.option.nope03": "Nope 03",
|
||||
"sound.option.nope04": "Nope 04",
|
||||
"sound.option.nope05": "Nope 05",
|
||||
"sound.option.nope06": "Nope 06",
|
||||
"sound.option.nope07": "Nope 07",
|
||||
"sound.option.nope08": "Nope 08",
|
||||
"sound.option.nope09": "Nope 09",
|
||||
"sound.option.nope10": "Nope 10",
|
||||
"sound.option.nope11": "Nope 11",
|
||||
"sound.option.nope12": "Nope 12",
|
||||
"sound.option.yup01": "Yup 01",
|
||||
"sound.option.yup02": "Yup 02",
|
||||
"sound.option.yup03": "Yup 03",
|
||||
"sound.option.yup04": "Yup 04",
|
||||
"sound.option.yup05": "Yup 05",
|
||||
"sound.option.yup06": "Yup 06",
|
||||
"settings.general.notifications.agent.title": "Agent",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Agent tamamlandıqda və ya diqqət tələb etdikdə sistem bildirişi göstər",
|
||||
"settings.general.notifications.permissions.title": "İcazələr",
|
||||
"settings.general.notifications.permissions.description": "İcazə tələb olunduqda sistem bildirişi göstər",
|
||||
"settings.general.notifications.errors.title": "Xətalar",
|
||||
"settings.general.notifications.errors.description": "Xəta baş verdikdə sistem bildirişi göstər",
|
||||
"settings.general.sounds.agent.title": "Agent",
|
||||
"settings.general.sounds.agent.description": "Agent tamamlandıqda və ya diqqət tələb etdikdə səs çal",
|
||||
"settings.general.sounds.permissions.title": "İcazələr",
|
||||
"settings.general.sounds.permissions.description": "İcazə tələb olunduqda səs çal",
|
||||
"settings.general.sounds.errors.title": "Xətalar",
|
||||
"settings.general.sounds.errors.description": "Xəta baş verdikdə səs çal",
|
||||
"settings.shortcuts.title": "Klaviatura qısa yolları",
|
||||
"settings.shortcuts.reset.button": "Standartlara sıfırla",
|
||||
"settings.shortcuts.reset.toast.title": "Qısa yollar sıfırlandı",
|
||||
"settings.shortcuts.reset.toast.description": "Klaviatura qısa yolları standartlara sıfırlandı.",
|
||||
"settings.shortcuts.conflict.title": "Qısa yol artıq istifadə olunur",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} artıq {{titles}} üçün təyin edilib.",
|
||||
"settings.shortcuts.unassigned": "Təyin edilməyib",
|
||||
"settings.shortcuts.pressKeys": "Düymələrə basın",
|
||||
"settings.shortcuts.search.placeholder": "Qısa yolları axtar",
|
||||
"settings.shortcuts.search.empty": "Qısa yol tapılmadı",
|
||||
"settings.shortcuts.group.general": "Ümumi",
|
||||
"settings.shortcuts.group.session": "Sessiya",
|
||||
"settings.shortcuts.group.navigation": "Naviqasiya",
|
||||
"settings.shortcuts.group.modelAndAgent": "Model və agent",
|
||||
"settings.shortcuts.group.terminal": "Terminal",
|
||||
"settings.shortcuts.group.prompt": "Prompt",
|
||||
"settings.providers.title": "Provayderlər",
|
||||
"settings.providers.description": "Provayder tənzimləmələri burada konfiqurasiya edilə biləcək.",
|
||||
"settings.providers.section.connected": "Qoşulmuş provayderlər",
|
||||
"settings.providers.connected.empty": "Qoşulmuş provayder yoxdur",
|
||||
"settings.providers.connected.environmentDescription": "Mühit dəyişənlərinizdən qoşulub",
|
||||
"settings.providers.section.popular": "Populyar provayderlər",
|
||||
"settings.providers.custom.description": "Əsas URL ilə OpenAI-uyğun provayder əlavə edin.",
|
||||
"settings.providers.tag.environment": "Mühit",
|
||||
"settings.providers.tag.config": "Konfiqurasiya",
|
||||
"settings.providers.tag.custom": "Fərdi",
|
||||
"settings.providers.tag.other": "Digər",
|
||||
"settings.models.title": "Modellər",
|
||||
"settings.models.description": "Model tənzimləmələri burada konfiqurasiya edilə biləcək.",
|
||||
"settings.agents.title": "Agentlər",
|
||||
"settings.agents.description": "Agent tənzimləmələri burada konfiqurasiya edilə biləcək.",
|
||||
"settings.commands.title": "Əmrlər",
|
||||
"settings.commands.description": "Əmr tənzimləmələri burada konfiqurasiya edilə biləcək.",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "MCP tənzimləmələri burada konfiqurasiya edilə biləcək.",
|
||||
"settings.permissions.title": "İcazələr",
|
||||
"settings.permissions.description": "Serverin standart olaraq hansı alətləri istifadə edə biləcəyini idarə edin.",
|
||||
"settings.permissions.section.tools": "Alətlər",
|
||||
"settings.permissions.toast.updateFailed.title": "İcazələr yenilənə bilmədi",
|
||||
"settings.permissions.action.allow": "İcazə ver",
|
||||
"settings.permissions.action.ask": "Soruş",
|
||||
"settings.permissions.action.deny": "Rədd et",
|
||||
"settings.permissions.tool.read.title": "Oxu",
|
||||
"settings.permissions.tool.read.description": "Fayl oxuma (fayl yoluna uyğun gəlir)",
|
||||
"settings.permissions.tool.edit.title": "Redaktə",
|
||||
"settings.permissions.tool.edit.description": "Redaktə, yazma və yamaq tətbiqi daxil olmaqla faylları dəyişdir",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Glob nümunələri ilə faylları uyğunlaşdır",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Müntəzəm ifadələrlə fayl məzmununu axtar",
|
||||
"settings.permissions.tool.list.title": "Siyahı",
|
||||
"settings.permissions.tool.list.description": "Qovluq daxilindəki faylları siyahıla",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Shell əmrlərini işlət",
|
||||
"settings.permissions.tool.task.title": "Tapşırıq",
|
||||
"settings.permissions.tool.task.description": "Alt agentləri işə sal",
|
||||
"settings.permissions.tool.skill.title": "Bacarıq",
|
||||
"settings.permissions.tool.skill.description": "Adla bacarıq yüklə",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Dil server sorğularını işlət",
|
||||
"settings.permissions.tool.todowrite.title": "Tapşırıq yaz",
|
||||
"settings.permissions.tool.todowrite.description": "Tapşırıq siyahısını yenilə",
|
||||
"settings.permissions.tool.webfetch.title": "Veb əldə et",
|
||||
"settings.permissions.tool.webfetch.description": "URL-dən məzmun əldə et",
|
||||
"settings.permissions.tool.websearch.title": "Veb axtarış",
|
||||
"settings.permissions.tool.websearch.description": "Vebdə axtar",
|
||||
"settings.permissions.tool.external_directory.title": "Xarici qovluq",
|
||||
"settings.permissions.tool.external_directory.description": "Layihə qovluğundan kənardakı fayllara daxil ol",
|
||||
"settings.permissions.tool.doom_loop.title": "Sonsuz dövr",
|
||||
"settings.permissions.tool.doom_loop.description": "Eyni girişlə təkrarlanan alət çağırışlarını aşkarla",
|
||||
"session.delete.failed.title": "Sessiya silinə bilmədi",
|
||||
"session.delete.title": "Sessiyanı sil",
|
||||
"session.delete.confirm": '"{{name}}" sessiyasını silmək istəyirsiniz?',
|
||||
"session.delete.button": "Sessiyanı sil",
|
||||
"workspace.new": "Yeni iş sahəsi",
|
||||
"workspace.type.local": "yerli",
|
||||
"workspace.type.sandbox": "sandbox",
|
||||
"workspace.create.failed.title": "İş sahəsi yaradıla bilmədi",
|
||||
"workspace.delete.failed.title": "İş sahəsi silinə bilmədi",
|
||||
"workspace.resetting.title": "İş sahəsi sıfırlanır",
|
||||
"workspace.resetting.description": "Bu bir dəqiqə çəkə bilər.",
|
||||
"workspace.reset.failed.title": "İş sahəsi sıfırlana bilmədi",
|
||||
"workspace.reset.success.title": "İş sahəsi sıfırlandı",
|
||||
"workspace.reset.success.description": "İş sahəsi indi standart branch ilə uyğunlaşır.",
|
||||
"workspace.error.stillPreparing": "İş sahəsi hələ hazırlanır",
|
||||
"workspace.status.checking": "Birləşdirilməmiş dəyişikliklər yoxlanılır...",
|
||||
"workspace.status.error": "Git statusu doğrulana bilmədi.",
|
||||
"workspace.status.clean": "Birləşdirilməmiş dəyişiklik aşkarlanmadı.",
|
||||
"workspace.status.dirty": "Bu iş sahəsində birləşdirilməmiş dəyişikliklər aşkarlandı.",
|
||||
"workspace.delete.title": "İş sahəsini sil",
|
||||
"workspace.delete.confirm": '"{{name}}" iş sahəsini silmək istəyirsiniz?',
|
||||
"workspace.delete.button": "İş sahəsini sil",
|
||||
"workspace.reset.title": "İş sahəsini sıfırla",
|
||||
"workspace.reset.confirm": '"{{name}}" iş sahəsini sıfırlamaq istəyirsiniz?',
|
||||
"workspace.reset.button": "İş sahəsini sıfırla",
|
||||
"workspace.reset.archived.none": "Heç bir aktiv sessiya arxivlənməyəcək.",
|
||||
"workspace.reset.archived.one": "1 sessiya arxivlənəcək.",
|
||||
"workspace.reset.archived.many": "{{count}} sessiya arxivlənəcək.",
|
||||
"workspace.reset.note": "Bu iş sahəsini standart branch ilə uyğunlaşdırmaq üçün sıfırlayacaq.",
|
||||
}
|
||||
@@ -0,0 +1,1170 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Файл",
|
||||
"desktop.menu.edit": "Редактиране",
|
||||
"desktop.menu.view": "Преглед",
|
||||
"desktop.menu.go": "върви",
|
||||
"desktop.menu.window": "прозорец",
|
||||
"desktop.menu.help": "Помощ",
|
||||
"desktop.menu.checkForUpdates": "Проверете за актуализации...",
|
||||
"desktop.menu.settings": "Настройки",
|
||||
"desktop.menu.reloadWebview": "Презареди Webview",
|
||||
"desktop.menu.restart": "Рестартирайте",
|
||||
"desktop.menu.exportLogs": "Експортиране на регистрационни файлове...",
|
||||
"desktop.menu.newSession": "Нова сесия",
|
||||
"desktop.menu.openProject": "Отворете проекта...",
|
||||
"desktop.menu.newWindow": "Нов прозорец",
|
||||
"desktop.menu.closeWindow": "Затваряне на прозореца",
|
||||
"desktop.menu.undo": "Отмяна",
|
||||
"desktop.menu.redo": "Повторете",
|
||||
"desktop.menu.cut": "Изрежете",
|
||||
"desktop.menu.copy": "копие",
|
||||
"desktop.menu.paste": "Залепете",
|
||||
"desktop.menu.delete": "Изтриване",
|
||||
"desktop.menu.selectAll": "Изберете Всички",
|
||||
"desktop.menu.toggleSidebar": "Превключване на страничната лента",
|
||||
"desktop.menu.toggleTerminal": "Превключване на терминала",
|
||||
"desktop.menu.toggleFileTree": "Превключване на файловото дърво",
|
||||
"desktop.menu.reload": "Презареди",
|
||||
"desktop.menu.toggleDeveloperTools": "Превключване на инструментите за разработчици",
|
||||
"desktop.menu.actualSize": "Действителен размер",
|
||||
"desktop.menu.zoomIn": "Увеличете",
|
||||
"desktop.menu.zoomOut": "Намаляване",
|
||||
"desktop.menu.toggleFullScreen": "Превключване на цял екран",
|
||||
"desktop.menu.back": "Назад",
|
||||
"desktop.menu.forward": "Напред",
|
||||
"desktop.menu.previousSession": "Предишна сесия",
|
||||
"desktop.menu.nextSession": "Следваща сесия",
|
||||
"desktop.menu.previousProject": "Предишен проект",
|
||||
"desktop.menu.nextProject": "Следващ проект",
|
||||
"desktop.menu.minimize": "Минимизиране",
|
||||
"desktop.menu.maximize": "Увеличете максимално",
|
||||
"desktop.menu.documentation": "OpenCode Документация",
|
||||
"desktop.menu.supportForum": "Форум за поддръжка",
|
||||
"desktop.menu.shareFeedback": "Споделете обратна връзка",
|
||||
"desktop.menu.reportBug": "Докладване за грешка",
|
||||
"desktop.menu.ariaLabel": "OpenCode меню",
|
||||
"desktop.updater.dialog.checkFailed.message": "Проверката на актуализацията е неуспешна.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Грешка при актуализиране",
|
||||
"desktop.updater.dialog.upToDate.message": "Вие сте в крак с времето.",
|
||||
"desktop.updater.dialog.upToDate.title": "Няма актуализации",
|
||||
"desktop.updater.dialog.ready.message": "Актуализацията {{version}} е изтеглена. Рестартиране сега?",
|
||||
"desktop.updater.dialog.ready.title": "Актуализацията е готова",
|
||||
"desktop.updater.dialog.restart": "Рестартирайте",
|
||||
"desktop.updater.dialog.later": "по-късно",
|
||||
"desktop.recovery.action.relaunch": "Рестартирайте",
|
||||
"desktop.recovery.action.exportLogs": "Експортиране на регистрационни файлове",
|
||||
"desktop.recovery.action.keepWaiting": "Продължете да чакате",
|
||||
"desktop.recovery.action.quit": "Откажете се",
|
||||
"desktop.recovery.loadFailed": "OpenCode не успя да се зареди",
|
||||
"desktop.recovery.terminated": "Прозорецът OpenCode приключи неочаквано",
|
||||
"desktop.recovery.unresponsive": "OpenCode не отговаря",
|
||||
"desktop.recovery.unresponsive.detail":
|
||||
"Можете да рестартирате приложението, да отворите регистрационните файлове или да продължите да чакате.",
|
||||
"desktop.recovery.loadFailed.detail": "Прозорец: {{window}}\nURL: {{url}}\nГрешка: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Прозорец: {{window}}\nПричина: {{reason}}\nКод: {{code}}",
|
||||
"desktop.recovery.unknown": "<неизвестен>",
|
||||
"desktop.dialog.chooseFolder": "Изберете папка",
|
||||
"desktop.dialog.chooseFile": "Изберете файл",
|
||||
"desktop.dialog.saveFile": "Запазете файла",
|
||||
"desktop.dialog.files": "файлове",
|
||||
"desktop.server.local": "Локален сървър",
|
||||
"desktop.wsl.error.windowsOnly": "WSL се предлага само на Windows",
|
||||
"desktop.wsl.error.unavailable": "WSL не е наличен",
|
||||
"desktop.wsl.error.listInstalled": "Неуспешно показване на инсталирани WSL дистрибуции",
|
||||
"desktop.wsl.error.listOnline": "Неуспешно изброяване на онлайн WSL дистрибуции",
|
||||
"desktop.wsl.error.executeDistro": "Не може да изпълнява команди в дистрибуцията",
|
||||
"desktop.wsl.error.installWsl": "Инсталирането на WSL е неуспешно",
|
||||
"desktop.wsl.error.installDistro": "Неуспешно инсталиране на дистрибуция: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "Инсталирането на OpenCode е неуспешно",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} вече е добавен",
|
||||
"desktop.wsl.error.opencodeMissing": "opencode не е инсталиран в тази дистрибуция",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode е инсталиран, но не може да стартира",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode не е инсталиран в {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode актуализацията приключи, но {{distro}} все още съобщава {{installed}}; очаквано {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "няма версия",
|
||||
"desktop.wsl.error.serverExited": "WSL сървърът излезе след стартиране (код={{code}} сигнал={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"WSL сървърът излезе, преди да стане здрав (код={{code}} сигнал={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout":
|
||||
"Времето за изчакване на допълнителната кола за {{distro}} проверка на здравето изтече след {{timeout}}ms",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} времето за изчакване изтече след {{timeout}}ms",
|
||||
"desktop.wsl.error.failedPort": "Неуспешно получаване на порт",
|
||||
"desktop.picker.error.notSelected": "Файлът не е избран от инструмента за избор",
|
||||
"desktop.picker.error.sizeLimit": "Избраните прикачени файлове надхвърлят ограничението от {{limit}} MB",
|
||||
"command.category.suggested": "Предложено",
|
||||
"command.category.view": "Преглед",
|
||||
"command.category.project": "Проект",
|
||||
"command.category.provider": "Доставчик",
|
||||
"command.category.server": "сървър",
|
||||
"command.category.session": "Сесия",
|
||||
"command.category.theme": "Тема",
|
||||
"command.category.language": "език",
|
||||
"command.category.file": "Файл",
|
||||
"command.category.context": "Контекст",
|
||||
"command.category.terminal": "Терминал",
|
||||
"command.category.model": "Модел",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "агент",
|
||||
"command.category.permissions": "Разрешения",
|
||||
"command.category.workspace": "Работно пространство",
|
||||
"command.category.settings": "Настройки",
|
||||
"command.logs.export": "Експортиране на регистрационни файлове",
|
||||
"theme.scheme.system": "система",
|
||||
"theme.scheme.light": "светлина",
|
||||
"theme.scheme.dark": "Тъмно",
|
||||
"command.sidebar.toggle": "Превключване на страничната лента",
|
||||
"command.project.open": "Отворен проект",
|
||||
"command.project.previous": "Предишен проект",
|
||||
"command.project.next": "Следващ проект",
|
||||
"command.project.index": "Превключване към проект {{index}}",
|
||||
"command.provider.connect": "Свържете доставчика",
|
||||
"command.server.switch": "Превключете сървъра",
|
||||
"command.settings.open": "Отворете настройките",
|
||||
"command.session.previous": "Предишна сесия",
|
||||
"command.session.next": "Следваща сесия",
|
||||
"command.session.previous.unseen": "Предишна непрочетена сесия",
|
||||
"command.session.next.unseen": "Следваща непрочетена сесия",
|
||||
"command.session.archive": "Архивна сесия",
|
||||
"command.palette": "Командна палитра",
|
||||
"command.theme.cycle": "Тема за цикъл",
|
||||
"command.theme.set": "Използвайте тема: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Цикъл на цветовата схема",
|
||||
"command.theme.scheme.set": "Използвайте цветова схема: {{scheme}}",
|
||||
"command.language.cycle": "Цикличен език",
|
||||
"command.language.set": "Използвайте език: {{language}}",
|
||||
"command.session.new": "Нова сесия",
|
||||
"command.file.open": "Отворете файла",
|
||||
"command.tab.close": "Затваряне на раздела",
|
||||
"command.tab.reopenClosed": "Повторно отваряне на затворен раздел",
|
||||
"command.context.addSelection": "Добавете селекция към контекста",
|
||||
"command.context.addSelection.description": "Добавяне на избрани редове от текущия файл",
|
||||
"command.input.focus": "Въвеждане на фокус",
|
||||
"command.terminal.toggle": "Превключване на терминала",
|
||||
"command.fileTree.toggle": "Превключване на файловото дърво",
|
||||
"command.review.toggle": "Превключване на прегледа",
|
||||
"command.terminal.new": "Нов терминал",
|
||||
"command.terminal.new.description": "Създайте нов раздел на терминала",
|
||||
"command.steps.toggle": "Превключване на стъпки",
|
||||
"command.steps.toggle.description": "Показване или скриване на стъпки за текущото съобщение",
|
||||
"command.message.previous": "Предишно съобщение",
|
||||
"command.message.previous.description": "Отидете на предишното потребителско съобщение",
|
||||
"command.message.next": "Следващо съобщение",
|
||||
"command.message.next.description": "Отидете на следващото потребителско съобщение",
|
||||
"command.model.choose": "Изберете модел",
|
||||
"command.model.choose.description": "Изберете друг модел",
|
||||
"command.mcp.toggle": "Превключване на MCPs",
|
||||
"command.mcp.toggle.description": "Превключване на MCPs",
|
||||
"command.agent.cycle": "Следващ агент",
|
||||
"command.agent.cycle.description": "Преминете към следващия агент",
|
||||
"command.agent.cycle.reverse": "Предишен агент",
|
||||
"command.agent.cycle.reverse.description": "Преминете към предишния агент",
|
||||
"command.model.variant.cycle": "Следващо ниво на разсъждение",
|
||||
"command.model.variant.cycle.description": "Преминете към следващото ниво на усилие",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "подкана",
|
||||
"command.permissions.autoaccept.enable": "Автоматично приемане на разрешения",
|
||||
"command.permissions.autoaccept.disable": "Спрете автоматичното приемане на разрешения",
|
||||
"command.workspace.toggle": "Превключване на работните пространства",
|
||||
"command.workspace.toggle.description":
|
||||
"Активирайте или деактивирайте няколко работни пространства в страничната лента",
|
||||
"command.session.undo": "Отмяна",
|
||||
"command.session.undo.description": "Отмяна на последното съобщение",
|
||||
"command.session.redo": "Повторете",
|
||||
"command.session.redo.description": "Повторете последното отменено съобщение",
|
||||
"command.session.compact": "Компактна сесия",
|
||||
"command.session.compact.description": "Обобщете сесията, за да намалите размера на контекста",
|
||||
"command.session.fork": "Разклонение от съобщение",
|
||||
"command.session.fork.description": "Създайте нова сесия от предишно съобщение",
|
||||
"command.session.share": "Споделяне на сесия",
|
||||
"command.session.share.description": "Споделете тази сесия и копирайте URL в клипборда",
|
||||
"command.session.unshare": "Прекратяване на споделянето на сесията",
|
||||
"command.session.unshare.description": "Спрете да споделяте тази сесия",
|
||||
"command.session.export": "Експортиране на сесия",
|
||||
"command.session.export.description": "Експортирайте пълния препис на сесията като JSON",
|
||||
"palette.search.placeholder": "Търсене на файлове, команди и сесии",
|
||||
"palette.search.placeholder.home": "Команди за търсене и сесии",
|
||||
"palette.empty": "Няма намерени резултати",
|
||||
"palette.group.commands": "Команди",
|
||||
"palette.group.files": "файлове",
|
||||
"dialog.provider.search.placeholder": "Доставчици на търсене",
|
||||
"dialog.provider.empty": "Няма намерени доставчици",
|
||||
"dialog.provider.group.popular": "Популярни",
|
||||
"dialog.provider.group.other": "други",
|
||||
"dialog.provider.custom.label": "Персонализиран OpenAI-съвместим доставчик",
|
||||
"dialog.provider.tag.recommended": "Препоръчва се",
|
||||
"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 модели за бързи, структурирани отговори",
|
||||
"dialog.provider.openrouter.note": "Достъп до всички поддържани модели от един доставчик",
|
||||
"dialog.provider.vercel.note": "Унифициран достъп до AI модели с интелигентно маршрутизиране",
|
||||
"dialog.model.select.title": "Изберете модел",
|
||||
"dialog.model.search.placeholder": "Търсене на модели",
|
||||
"dialog.model.empty": "Няма резултати за модели",
|
||||
"dialog.model.manage": "Управление на модели",
|
||||
"dialog.model.manage.description": "Персонализирайте кои модели да се показват в селектора на модели.",
|
||||
"dialog.model.manage.provider.toggle": "Превключване на всички {{provider}} модели",
|
||||
"dialog.model.unpaid.freeModels.title": "Безплатни модели, предоставени от OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Добавете още модели от популярни доставчици",
|
||||
"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": "Браузър",
|
||||
"provider.connect.method.headless": "Без глава",
|
||||
"provider.connect.status.inProgress": "В ход е оторизация...",
|
||||
"provider.connect.status.waiting": "Чака се оторизация...",
|
||||
"provider.connect.status.failed": "Упълномощаването е неуспешно: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Въведете своя {{provider}} API ключ, за да свържете акаунта си и да използвате {{provider}} модели в OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API ключ",
|
||||
"provider.connect.apiKey.placeholder": "API ключ",
|
||||
"provider.connect.apiKey.required": "Изисква се ключ API",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen ви дава достъп до подбран набор от надеждни оптимизирани модели за кодиращи агенти.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"С един ключ API ще получите достъп до модели като Claude, GPT, Gemini, GLM и други.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Посетете",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": "за да вземете своя ключ API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Посетете",
|
||||
"provider.connect.oauth.code.visit.link": "тази връзка",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
"за да вземете вашия код за оторизация, за да свържете акаунта си и да използвате {{provider}} модели в OpenCode.",
|
||||
"provider.connect.oauth.code.label": "{{method}} код за оторизация",
|
||||
"provider.connect.oauth.code.placeholder": "Код за оторизация",
|
||||
"provider.connect.oauth.code.required": "Изисква се код за оторизация",
|
||||
"provider.connect.oauth.code.invalid": "Невалиден код за оторизация",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Посетете",
|
||||
"provider.connect.oauth.auto.visit.link": "тази връзка",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
"и въведете кода по-долу, за да свържете акаунта си и да използвате {{provider}} модели в OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Код за потвърждение",
|
||||
"provider.connect.toast.connected.title": "{{provider}} свързан",
|
||||
"provider.connect.toast.connected.description": "{{provider}} модела вече са налични за използване.",
|
||||
"provider.custom.title": "Персонализиран доставчик",
|
||||
"provider.custom.unavailable": "Персонализираните доставчици не са достъпни на този сървър",
|
||||
"provider.custom.description.prefix": "Конфигурирайте OpenAI-съвместим доставчик. Вижте",
|
||||
"provider.custom.description.link": "документи за конфигурация на доставчика",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "ID на доставчика",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "Малки букви, цифри, тирета или долни черти",
|
||||
"provider.custom.field.name.label": "Екранно име",
|
||||
"provider.custom.field.name.placeholder": "Моят доставчик на AI",
|
||||
"provider.custom.field.baseURL.label": "Основа URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API ключ",
|
||||
"provider.custom.field.apiKey.placeholder": "API ключ",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Не е задължително. Оставете празно, ако управлявате удостоверяването чрез заглавки.",
|
||||
"provider.custom.models.label": "Модели",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "модел-id",
|
||||
"provider.custom.models.name.label": "Име",
|
||||
"provider.custom.models.name.placeholder": "Екранно име",
|
||||
"provider.custom.models.remove": "Премахване на модела",
|
||||
"provider.custom.models.add": "Добавете модел",
|
||||
"provider.custom.headers.label": "Заглавки (по избор)",
|
||||
"provider.custom.headers.key.label": "Заглавка",
|
||||
"provider.custom.headers.key.placeholder": "Заглавие-Име",
|
||||
"provider.custom.headers.value.label": "Стойност",
|
||||
"provider.custom.headers.value.placeholder": "стойност",
|
||||
"provider.custom.headers.remove": "Премахване на заглавката",
|
||||
"provider.custom.headers.add": "Добавете заглавка",
|
||||
"provider.custom.error.providerID.required": "Изисква се ID на доставчика",
|
||||
"provider.custom.error.providerID.format": "Използвайте малки букви, цифри, тирета или долни черти",
|
||||
"provider.custom.error.providerID.exists": "Този идентификатор на доставчик вече съществува",
|
||||
"provider.custom.error.name.required": "Изисква се екранно име",
|
||||
"provider.custom.error.baseURL.required": "Изисква се база URL",
|
||||
"provider.custom.error.baseURL.format": "Трябва да започва с http:// или https://",
|
||||
"provider.custom.error.required": "Задължително",
|
||||
"provider.custom.error.duplicate": "Дубликат",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} прекъснат",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} модели вече не са налични.",
|
||||
"model.tag.free": "безплатно",
|
||||
"model.tag.latest": "Последни",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "текст",
|
||||
"model.input.image": "изображение",
|
||||
"model.input.audio": "аудио",
|
||||
"model.input.video": "видео",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Позволява: {{inputs}}",
|
||||
"model.tooltip.context.label": "Контекст",
|
||||
"model.tooltip.inputs": "Входове",
|
||||
"model.tooltip.model": "Модел",
|
||||
"model.tooltip.provider": "Доставчик",
|
||||
"model.tooltip.reasoning": "Разсъждение",
|
||||
"model.tooltip.reasoning.allowed": "Позволява разсъждение",
|
||||
"model.tooltip.reasoning.none": "Без разсъждения",
|
||||
"model.tooltip.context": "Контекстно ограничение {{limit}}",
|
||||
"common.search.placeholder": "Търсене",
|
||||
"common.goBack": "Навигирайте назад",
|
||||
"common.goForward": "Навигирайте напред",
|
||||
"common.loading": "Зарежда се",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Отказ",
|
||||
"common.clear": "ясно",
|
||||
"common.open": "Отворете",
|
||||
"common.connect": "Свържете се",
|
||||
"common.disconnect": "Прекъснете връзката",
|
||||
"common.continue": "Продължи",
|
||||
"common.submit": "Изпратете",
|
||||
"common.save": "Запазване",
|
||||
"common.saving": "Запазва се...",
|
||||
"common.default": "По подразбиране",
|
||||
"common.attachment": "прикачен файл",
|
||||
"prompt.placeholder.shell": "Въведете команда shell... {{example}}",
|
||||
"prompt.placeholder.normal": 'Попитайте каквото и да е... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Питайте нещо...",
|
||||
"prompt.placeholder.summarizeComments": "Обобщете коментарите...",
|
||||
"prompt.placeholder.summarizeComment": "Обобщение на коментара...",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "подкана",
|
||||
"prompt.mode.shell.exit": "esc за изход",
|
||||
"session.child.promptDisabled": "Субагентните сесии не могат да бъдат подканени.",
|
||||
"session.child.backToParent": "Назад към основната сесия.",
|
||||
"prompt.example.1": "Поправете TODO в кодовата база",
|
||||
"prompt.example.2": "Какъв е техническият стек на този проект?",
|
||||
"prompt.example.3": "Поправете повредени тестове",
|
||||
"prompt.example.4": "Обяснете как работи удостоверяването",
|
||||
"prompt.example.5": "Намерете и поправете уязвимости в сигурността",
|
||||
"prompt.example.6": "Добавяне на модулни тестове за потребителската услуга",
|
||||
"prompt.example.7": "Преработете тази функция, за да бъде по-четима",
|
||||
"prompt.example.8": "Какво означава тази грешка?",
|
||||
"prompt.example.9": "Помогнете ми да коригирам този проблем",
|
||||
"prompt.example.10": "Генерирайте API документация",
|
||||
"prompt.example.11": "Оптимизирайте заявките към базата данни",
|
||||
"prompt.example.12": "Добавете проверка на входа",
|
||||
"prompt.example.13": "Създайте нов компонент за...",
|
||||
"prompt.example.14": "Как да разположа този проект?",
|
||||
"prompt.example.15": "Прегледайте моя код за най-добри практики",
|
||||
"prompt.example.16": "Добавете обработка на грешки към тази функция",
|
||||
"prompt.example.17": "Обяснете този модел на регулярен израз",
|
||||
"prompt.example.18": "Преобразувайте това в TypeScript",
|
||||
"prompt.example.19": "Добавете регистриране в цялата кодова база",
|
||||
"prompt.example.20": "Какви зависимости са остарели?",
|
||||
"prompt.example.21": "Помогнете ми да напиша скрипт за миграция",
|
||||
"prompt.example.22": "Приложете кеширане за тази крайна точка",
|
||||
"prompt.example.23": "Добавете пагинация към този списък",
|
||||
"prompt.example.24": "Създайте команда CLI за...",
|
||||
"prompt.example.25": "Как работят променливите на средата тук?",
|
||||
"prompt.popover.emptyResults": "Няма съответстващи резултати",
|
||||
"prompt.popover.emptyCommands": "Няма съответстващи команди",
|
||||
"prompt.dropzone.label": "Пуснете тук изображения, PDF или текстови файлове",
|
||||
"prompt.dropzone.file.label": "Пуснете до файла @mention",
|
||||
"prompt.slash.badge.custom": "обичай",
|
||||
"prompt.slash.badge.skill": "умение",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "активен",
|
||||
"prompt.context.includeActiveFile": "Включете активен файл",
|
||||
"prompt.context.removeActiveFile": "Премахнете активния файл от контекста",
|
||||
"prompt.context.removeFile": "Премахване на файла от контекста",
|
||||
"prompt.action.attachFile": "Добавете файлове",
|
||||
"prompt.menu.addImagesAndFiles": "Добавете файлове и др",
|
||||
"prompt.menu.imagesAndFiles": "Изображения и файлове",
|
||||
"prompt.menu.commands": "Команди",
|
||||
"prompt.menu.context": "Контекст",
|
||||
"prompt.menu.shellCommand": "Shell команда",
|
||||
"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.toast.worktreeCreateFailed.title": "Неуспешно създаване на работно дърво",
|
||||
"prompt.toast.sessionCreateFailed.title": "Неуспешно създаване на сесия",
|
||||
"prompt.toast.shellSendFailed.title": "Неуспешно изпращане на команда shell",
|
||||
"prompt.toast.commandSendFailed.title": "Неуспешно изпращане на команда",
|
||||
"prompt.toast.promptSendFailed.title": "Неуспешно изпращане на подкана",
|
||||
"prompt.toast.promptSendFailed.description": "Не може да се извлече сесия",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{enabled}} от {{total}} активирани",
|
||||
"dialog.mcp.empty": "Няма конфигурирани MCP",
|
||||
"dialog.lsp.empty": "LSPs автоматично разпознати от типове файлове",
|
||||
"dialog.plugins.empty": "Добавки, конфигурирани в opencode.json",
|
||||
"mcp.status.connected": "свързан",
|
||||
"mcp.status.failed": "неуспешно",
|
||||
"mcp.status.needs_auth": "се нуждае от авт",
|
||||
"mcp.status.disabled": "деактивиран",
|
||||
"mcp.auth.clickToAuthenticate": "Щракнете за удостоверяване",
|
||||
"dialog.fork.empty": "Няма съобщения за разклонение",
|
||||
"dialog.directory.search.placeholder": "Папки за търсене",
|
||||
"dialog.directory.empty": "Няма намерени папки",
|
||||
"dialog.directory.action.selectFile": "Изберете файл",
|
||||
"dialog.directory.action.selectFolder": "Изберете папка",
|
||||
"dialog.directory.root": "корен",
|
||||
"dialog.directory.parent": "Родител",
|
||||
"dialog.directory.readError": "Тази папка не може да се прочете",
|
||||
"app.server.unreachable": "Не може да се достигне до {{server}}",
|
||||
"app.server.retrying": "Опитва се автоматично...",
|
||||
"app.server.otherServers": "Други сървъри",
|
||||
"dialog.server.title": "Сървъри",
|
||||
"dialog.server.description": "Превключете към кой OpenCode сървър се свързва това приложение.",
|
||||
"dialog.server.search.placeholder": "Сървъри за търсене",
|
||||
"dialog.server.empty": "Все още няма сървъри",
|
||||
"dialog.server.add.title": "Добавете сървър",
|
||||
"dialog.server.add.url": "Адрес на сървъра",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Не може да се свърже със сървъра",
|
||||
"dialog.server.add.checking": "Проверка...",
|
||||
"dialog.server.add.button": "Добавете сървър",
|
||||
"dialog.server.add.name": "Име на сървъра (по избор)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "потребителско име (по избор)",
|
||||
"dialog.server.add.usernamePlaceholder": "потребителско име",
|
||||
"dialog.server.add.password": "Парола (по избор)",
|
||||
"dialog.server.add.passwordPlaceholder": "парола",
|
||||
"dialog.server.edit.title": "Редактиране на сървъра",
|
||||
"dialog.server.default.title": "Сървър по подразбиране",
|
||||
"dialog.server.default.description":
|
||||
"Свържете се с този сървър при стартиране на приложението, вместо да стартирате локален сървър. Изисква рестартиране.",
|
||||
"dialog.server.default.none": "Няма избран сървър",
|
||||
"dialog.server.default.set": "Задаване на текущия сървър по подразбиране",
|
||||
"dialog.server.default.clear": "ясно",
|
||||
"dialog.server.action.remove": "Премахване на сървъра",
|
||||
"dialog.server.menu.edit": "Редактиране",
|
||||
"dialog.server.menu.default": "Задайте по подразбиране",
|
||||
"dialog.server.menu.defaultRemove": "Премахване на подразбиране",
|
||||
"dialog.server.menu.delete": "Изтриване",
|
||||
"dialog.server.current": "Текущ сървър",
|
||||
"dialog.server.status.default": "По подразбиране",
|
||||
"wsl.server.add": "Добавяне на WSL сървър",
|
||||
"wsl.server.addShort": "Добавете WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL сървър",
|
||||
"wsl.server.retryStart": "Повторен старт",
|
||||
"wsl.server.updating": "Актуализиране...",
|
||||
"wsl.onboarding.step.distro": "Изберете дистрибуция",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Проверява се WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows се нуждае от рестартиране, за да завърши инсталирането на WSL.",
|
||||
"wsl.onboarding.ready": "WSL е готов.",
|
||||
"wsl.onboarding.required": "WSL се изисква, за да продължите.",
|
||||
"wsl.onboarding.checkingDistros": "Дистрибуции се проверяват...",
|
||||
"wsl.onboarding.installingDistro": "Инсталиране на {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Проверява се {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Списък на дистрибуции...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} е готов.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} все още не е инсталиран.",
|
||||
"wsl.onboarding.openDistroOnce": "Отворете {{distro}} веднъж, за да завършите настройката.",
|
||||
"wsl.onboarding.finishingDistro": "Завършва настройката за {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Изберете дистрибуция или инсталирайте такава по-долу.",
|
||||
"wsl.onboarding.checkingOpencode": "Проверява се OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Проверява се OpenCode в {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Актуализиране на OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Актуализиране на OpenCode в {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Актуализирайте OpenCode в {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Актуализация OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode е готов в {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode е готов.",
|
||||
"wsl.onboarding.installOpencodeIn": "Инсталирайте OpenCode в {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Инсталирайте OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Първо изберете дистрибуция.",
|
||||
"wsl.onboarding.loadFailed": "Неуспешно зареждане на състояние WSL.",
|
||||
"wsl.onboarding.loading": "Зареждане...",
|
||||
"wsl.onboarding.installedDistros": "Инсталирани дистрибуции",
|
||||
"wsl.onboarding.checkAgain": "Проверете отново",
|
||||
"wsl.onboarding.distroStatus.ready": "Готови",
|
||||
"wsl.onboarding.distroStatus.checking": "Проверка...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode не е инсталиран",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Липсват bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Не се поддържа · Използвайте WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Нуждаете се от друга дистрибуция?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Инсталирайте дистрибуция Linux от каталога WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL не е инсталиран",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystem for Linux) се изисква, преди OpenCode да може да добави WSL сървър",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL не е наличен",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode не можа да провери WSL на тази машина.",
|
||||
"wsl.onboarding.installWsl": "Инсталирайте WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Рестартирайте Windows, за да завършите инсталирането на WSL, след което отворете отново OpenCode.",
|
||||
"wsl.onboarding.next": "Следваща",
|
||||
"wsl.onboarding.refresh": "Опресняване",
|
||||
"wsl.onboarding.allDistrosAdded": "Всички инсталирани дистрибуции вече са добавени.",
|
||||
"wsl.onboarding.noDistros": "Все още няма открити дистрибуции.",
|
||||
"wsl.onboarding.install": "Инсталирайте",
|
||||
"wsl.onboarding.installing": "Инсталира се...",
|
||||
"wsl.onboarding.installDistro": "Инсталирайте дистрибуция",
|
||||
"wsl.onboarding.searchDistros": "Търсете дистрибуции",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 е задължително.",
|
||||
"wsl.onboarding.toolsRequired": "Тази дистрибуция се нуждае от bash и curl.",
|
||||
"wsl.onboarding.openTerminal": "Отворете терминала",
|
||||
"wsl.onboarding.path": "Път: {{path}}",
|
||||
"wsl.onboarding.notFound": "не е намерено",
|
||||
"wsl.onboarding.version": "Версия: {{version}}",
|
||||
"wsl.onboarding.unknown": "неизвестен",
|
||||
"wsl.onboarding.desktopVersion": "работен плот {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Инсталираната версия не съответства на версията на настолното приложение.",
|
||||
"wsl.onboarding.adding": "Добавяне...",
|
||||
"help.tabs.toast.ariaLabel": "Представяме ви раздели. Организирайте работата и активните си сесии с раздели",
|
||||
"help.tabs.toast.dismiss": "Отхвърляне на информация за раздели",
|
||||
"help.tabs.title": "Представяме ви раздели",
|
||||
"help.tabs.description": "Организирайте работата и активните си сесии с раздели",
|
||||
"help.tabs.date": "14 юли",
|
||||
"help.tabs.introduction": "OpenCode Desktop вече е изграден около раздели.",
|
||||
"help.tabs.sessions":
|
||||
"Започнете нова сесия в раздел или отворете съществуваща сесия от някой от вашите проекти. Отворете нов раздел, когато започвате нещо ново, и го затворете, когато сте готови.",
|
||||
"help.tabs.organize":
|
||||
"Поддържането на няколко раздела отворени улеснява организирането на вашите активни сесии. Преименувайте раздели на нещо запомнящо се, ако планирате да ги запазите.",
|
||||
"help.tabs.home":
|
||||
"Ще намерите всичките си сесии и проекти на новия начален екран. Избирането на сесия я отваря в раздел.",
|
||||
"help.tabs.persistence": "Когато отворите отново приложението, вашите раздели са все още отворени.",
|
||||
"help.tabs.worktrees":
|
||||
"Новият дизайн все още не поддържа Git Worktrees, идва скоро. Така че, ако предпочитате да продължите да използвате предишното оформление, можете да превключвате между оформления в Настройки. Само имайте предвид, че новото оформление ще стане постоянно след няколко седмици.",
|
||||
"server.row.noUsername": "няма потребителско име",
|
||||
"dialog.project.edit.title": "Редактиране на проекта",
|
||||
"dialog.project.edit.name": "Име",
|
||||
"dialog.project.edit.icon": "Икона",
|
||||
"dialog.project.edit.icon.alt": "Икона на проекта",
|
||||
"dialog.project.edit.icon.hint": "Щракнете или плъзнете изображение",
|
||||
"dialog.project.edit.icon.recommended": "Препоръчително: 128x128px",
|
||||
"dialog.project.edit.color": "Цвят",
|
||||
"dialog.project.edit.color.select": "Изберете цвят {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "Скрипт за стартиране на работното пространство",
|
||||
"dialog.project.edit.worktree.startup.description":
|
||||
"Изпълнява се след създаване на ново работно пространство (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "напр. bun install",
|
||||
"dialog.releaseNotes.action.getStarted": "Започнете",
|
||||
"dialog.releaseNotes.action.next": "Следваща",
|
||||
"dialog.releaseNotes.action.hideFuture": "Не ги показвайте в бъдеще",
|
||||
"dialog.releaseNotes.media.alt": "Визуализация на изданието",
|
||||
"context.breakdown.title": "Разбивка на контекста",
|
||||
"context.breakdown.note":
|
||||
"Приблизителна разбивка на входните токени. „Други“ включва дефиниции на инструменти и режийни разходи.",
|
||||
"context.breakdown.system": "система",
|
||||
"context.breakdown.user": "Потребител",
|
||||
"context.breakdown.assistant": "Помощник",
|
||||
"context.breakdown.tool": "Извиквания на инструменти",
|
||||
"context.breakdown.other": "други",
|
||||
"context.systemPrompt.title": "Системен ред",
|
||||
"context.rawMessages.title": "Сурови съобщения",
|
||||
"context.export.session": "Експортиране на сесия",
|
||||
"context.stats.session": "Сесия",
|
||||
"context.stats.messages": "Съобщения",
|
||||
"context.stats.provider": "Доставчик",
|
||||
"context.stats.model": "Модел",
|
||||
"context.stats.limit": "Ограничение на контекста",
|
||||
"context.stats.totalTokens": "Общо токени",
|
||||
"context.stats.usage": "Използване",
|
||||
"context.stats.inputTokens": "Входни токени",
|
||||
"context.stats.outputTokens": "Изходни токени",
|
||||
"context.stats.reasoningTokens": "Разсъждаващи токени",
|
||||
"context.stats.cacheTokens": "Кеш токени (четене/запис)",
|
||||
"context.stats.userMessages": "Потребителски съобщения",
|
||||
"context.stats.assistantMessages": "Асистентски съобщения",
|
||||
"context.stats.totalCost": "Обща цена",
|
||||
"context.stats.sessionCreated": "Сесията е създадена",
|
||||
"context.stats.lastActivity": "Последна активност",
|
||||
"context.usage.tokens": "Токени",
|
||||
"context.usage.usage": "Използване",
|
||||
"context.usage.cost": "цена",
|
||||
"context.usage.clickToView": "Кликнете, за да видите контекста",
|
||||
"context.usage.view": "Преглед на използването на контекста",
|
||||
"language.en": "английски",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "Deutsch",
|
||||
"language.es": "Español",
|
||||
"language.fr": "Français",
|
||||
"language.da": "Dansk",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Полски",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "португалски (Бразилия)",
|
||||
"language.bs": "Босански",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "турски",
|
||||
"toast.language.title": "език",
|
||||
"toast.language.description": "Превключихте към {{language}}",
|
||||
"toast.theme.title": "Темата е сменена",
|
||||
"toast.scheme.title": "Цветова схема",
|
||||
"toast.workspace.enabled.title": "Работните пространства са активирани",
|
||||
"toast.workspace.enabled.description": "Множество работни дървета вече се показват в страничната лента",
|
||||
"toast.workspace.disabled.title": "Работните пространства са деактивирани",
|
||||
"toast.workspace.disabled.description": "В страничната лента се показва само основното работно дърво",
|
||||
"toast.permissions.autoaccept.on.title": "Автоматично приемане на разрешения",
|
||||
"toast.permissions.autoaccept.on.description": "Заявките за разрешение ще бъдат автоматично одобрени",
|
||||
"toast.permissions.autoaccept.off.title": "Автоматичното приемане на разрешения е спряно",
|
||||
"toast.permissions.autoaccept.off.description": "Исканията за разрешение ще изискват одобрение",
|
||||
"toast.model.none.title": "Няма избран модел",
|
||||
"toast.model.none.description": "Свържете доставчик, за да обобщите тази сесия",
|
||||
"toast.file.loadFailed.title": "Неуспешно зареждане на файла",
|
||||
"toast.file.listFailed.title": "Неуспешно изброяване на файлове",
|
||||
"toast.context.noLineSelection.title": "Няма избор на линия",
|
||||
"toast.context.noLineSelection.description": "Първо изберете диапазон от редове в раздел на файл.",
|
||||
"toast.session.share.copyFailed.title": "Неуспешно копиране на URL в клипборда",
|
||||
"toast.session.share.success.title": "Сесията е споделена",
|
||||
"toast.session.share.success.description": "Споделяне на URL копирано в клипборда!",
|
||||
"toast.session.share.failed.title": "Неуспешно споделяне на сесията",
|
||||
"toast.session.share.failed.description": "Възникна грешка при споделяне на сесията",
|
||||
"toast.session.unshare.success.title": "Сесията е прекратена",
|
||||
"toast.session.unshare.success.description": "Сесията бе прекратена успешно!",
|
||||
"toast.session.unshare.failed.title": "Прекратяването на споделянето на сесията не бе успешно",
|
||||
"toast.session.unshare.failed.description": "Възникна грешка при прекратяване на споделянето на сесията",
|
||||
"toast.session.export.success.title": "Сесията е експортирана",
|
||||
"toast.session.export.success.description": "Запазена сесия в {{filename}}",
|
||||
"toast.session.export.failed.title": "Неуспешно експортиране на сесия",
|
||||
"toast.session.export.failed.description": "Възникна грешка при експортиране на сесията",
|
||||
"toast.session.listFailed.title": "Неуспешно зареждане на сесии за {{project}}",
|
||||
"toast.project.reloadFailed.title": "Неуспешно презареждане на {{project}}",
|
||||
"toast.update.title": "Налична актуализация",
|
||||
"toast.update.description": "Вече е достъпна за инсталиране нова версия на OpenCode ({{version}}).",
|
||||
"toast.update.action.installRestart": "Инсталирайте и рестартирайте",
|
||||
"toast.update.action.notYet": "още не",
|
||||
"error.page.title": "Нещо се обърка",
|
||||
"error.page.description": "Възникна грешка при зареждане на приложението.",
|
||||
"error.page.description.localServerStartup": "Възникна грешка при стартиране на локалния сървър.",
|
||||
"error.page.details.label": "Подробности за грешката",
|
||||
"error.page.action.restart": "Рестартирайте",
|
||||
"error.page.action.report": "Докладване за грешка",
|
||||
"error.page.action.reported": "Съобщава се за грешка",
|
||||
"error.page.action.exportLogs": "Експортиране на регистрационни файлове",
|
||||
"error.page.action.checking": "Проверка...",
|
||||
"error.page.action.checkUpdates": "Проверете за актуализации",
|
||||
"error.page.action.updateTo": "Актуализиране до {{version}}",
|
||||
"error.page.circular": "[Кръгло]",
|
||||
"error.page.report.prefix": "Моля, докладвайте тази грешка на екипа на OpenCode",
|
||||
"error.page.report.discord": "на Discord",
|
||||
"error.page.version": "Версия: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"Основният елемент не е намерен. Забравихте ли да го добавите към вашия index.html? Или може би атрибутът id е изписан неправилно?",
|
||||
"error.serverSync.connectFailed": "Не може да се свърже със сървъра. Има ли сървър, работещ на `{{url}}`?",
|
||||
"error.serverSDK.noServerAvailable": "Няма наличен сървър",
|
||||
"error.serverSDK.serverNotAvailable": "Сървърът не е наличен",
|
||||
"error.childStore.persistedCacheCreateFailed": "Неуспешно създаване на постоянен кеш",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "Неуспешно създаване на постоянни метаданни на проекта",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Неуспешно създаване на икона на постоянен проект",
|
||||
"error.childStore.storeCreateFailed": "Неуспешно създаване на магазин",
|
||||
"directory.error.invalidUrl": "Невалидна директория в URL.",
|
||||
"error.chain.unknown": "Неизвестна грешка",
|
||||
"error.server.invalidConfiguration": "Невалидна конфигурация",
|
||||
"error.chain.causedBy": "Причинено от:",
|
||||
"error.chain.apiError": "API грешка",
|
||||
"error.chain.status": "Статус: {{status}}",
|
||||
"error.chain.retryable": "Повторен опит: {{retryable}}",
|
||||
"error.chain.responseBody": "Тяло на отговора:\n{{body}}",
|
||||
"error.chain.didYouMean": "Може би имахте предвид: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Моделът не е намерен: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Проверете имената на доставчика/модела на вашата конфигурация (opencode.json).",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP сървър "{{name}}" не успя. Имайте предвид, че OpenCode все още не поддържа MCP удостоверяване.',
|
||||
"error.chain.providerAuthFailed": "Неуспешно удостоверяване на доставчика ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
"Неуспешно инициализиране на доставчика „{{provider}}“. Проверете идентификационните данни и конфигурацията.",
|
||||
"error.chain.configJsonInvalid": "Конфигурационният файл на адрес {{path}} не е валиден JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "Конфигурационният файл на {{path}} не е валиден JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Директория "{{dir}}" в {{path}} не е валидна. Преименувайте директорията на "{{suggestion}}" или я премахнете. Това е често срещана печатна грешка.',
|
||||
"error.chain.configFrontmatterError": "Неуспешно анализиране на frontmatter в {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "Конфигурационният файл в {{path}} е невалиден",
|
||||
"error.chain.configInvalidWithMessage": "Конфигурационният файл в {{path}} е невалиден: {{message}}",
|
||||
"notification.permission.title": "Изисква се разрешение",
|
||||
"notification.permission.description": "{{sessionTitle}} в {{projectName}} се нуждае от разрешение",
|
||||
"notification.question.title": "Въпрос",
|
||||
"notification.question.description": "{{sessionTitle}} в {{projectName}} има въпрос",
|
||||
"notification.action.goToSession": "Отидете на сесия",
|
||||
"notification.session.responseReady.title": "Готов отговор",
|
||||
"notification.session.error.title": "Грешка в сесията",
|
||||
"notification.session.error.fallbackDescription": "Възникна грешка",
|
||||
"home.recentProjects": "Последни проекти",
|
||||
"home.empty.title": "Няма скорошни проекти",
|
||||
"home.empty.description": "Започнете, като отворите местен проект",
|
||||
"home.title": "Начало",
|
||||
"home.projects": "проекти",
|
||||
"home.project.add": "Добавете проект",
|
||||
"home.recentlyClosed": "Наскоро затворен",
|
||||
"home.server.collapse": "Свиване на сървърни проекти",
|
||||
"home.server.expand": "Разширете сървърните проекти",
|
||||
"home.sessions.search.placeholder": "Сесии за търсене",
|
||||
"home.sessions.search.placeholder.scoped": "Търсене на сесии в {{scope}}",
|
||||
"home.sessions.search.sessions": "Сесии",
|
||||
"home.sessions.search.noResults": "Няма намерени сесии за {{query}}",
|
||||
"home.sessions.empty": "Тук още няма нищо",
|
||||
"home.sessions.empty.description": "Създайте сесия, за да започнете",
|
||||
"home.sessions.group.today": "Днес",
|
||||
"home.sessions.group.yesterday": "Вчера",
|
||||
"home.sessions.group.older": "По-стари",
|
||||
"home.providerTip":
|
||||
"Свържете се с над 75 доставчици, за да използвате други модели, включително Claude, GPT, Gemini и т.н.",
|
||||
"session.tab.session": "Сесия",
|
||||
"session.tab.review": "Преглед",
|
||||
"session.tab.context": "Контекст",
|
||||
"session.tab.unknown": "Неизвестна сесия",
|
||||
"session.panel.reviewAndFiles": "Преглед и файлове",
|
||||
"session.error.notFound": "Тази сесия не може да бъде намерена",
|
||||
"session.error.notFound.description": "Този раздел сочи към сесия, която вече не съществува на този сървър.",
|
||||
"session.error.notFound.closeTab": "Затваряне на раздела",
|
||||
"session.error.serverConnection": "Не може да се свърже с този сървър",
|
||||
"session.review.filesChanged": "Променени файлове {{count}}",
|
||||
"session.review.change.one": "промяна",
|
||||
"session.review.change.other": "Промени",
|
||||
"session.review.loadingChanges": "Промените се зареждат...",
|
||||
"session.review.empty": "Все още няма промени в тази сесия",
|
||||
"session.review.noVcs": "Не е открита Git Система за контрол на версиите, промените не се показват",
|
||||
"session.review.noVcs.createGit.title": "Създайте Git хранилище",
|
||||
"session.review.noVcs.createGit.description": "Проследявайте, преглеждайте и отменяйте промените в този проект",
|
||||
"session.review.noVcs.createGit.actionLoading": "Създаване на Git хранилище...",
|
||||
"session.review.noVcs.createGit.action": "Създайте Git хранилище",
|
||||
"session.review.noSnapshot":
|
||||
"Проследяването на моментна снимка е деактивирано в конфигурацията, така че промените в сесията не са налични",
|
||||
"session.review.noChanges": "Без промени",
|
||||
"session.review.noUncommittedChanges": "Все още няма незавършени промени",
|
||||
"session.review.noBranchChanges": "Все още няма промени в клона",
|
||||
"session.files.selectToOpen": "Изберете файл за отваряне",
|
||||
"session.files.all": "Всички файлове",
|
||||
"session.files.empty": "Няма файлове",
|
||||
"session.files.binaryContent": "Двоичен файл (съдържанието не може да се покаже)",
|
||||
"session.messages.renderEarlier": "Изобразяване на по-ранни съобщения",
|
||||
"session.messages.loadingEarlier": "Зареждат се предишни съобщения...",
|
||||
"session.messages.loadEarlier": "Зареди по-ранни съобщения",
|
||||
"session.messages.loading": "Съобщенията се зареждат...",
|
||||
"session.messages.jumpToLatest": "Преминете към най-новото",
|
||||
"session.context.addToContext": "Добавете {{selection}} към контекста",
|
||||
"session.todo.title": "Задачи",
|
||||
"session.todo.collapse": "Свиване",
|
||||
"session.todo.expand": "Разширяване",
|
||||
"session.todo.progress": "{{done}} от {{total}} задачи са изпълнени",
|
||||
"session.question.progress": "{{current}} от {{total}} въпроса",
|
||||
"session.question.minimize": "Минимизиране на въпроса",
|
||||
"session.question.restore": "Възстановете въпроса",
|
||||
"session.question.pending.one": "{{count}} чакащ въпрос",
|
||||
"session.question.pending.other": "{{count}} висящи въпроси",
|
||||
"session.followupDock.summary.one": "{{count}} съобщение в опашка",
|
||||
"session.followupDock.summary.other": "{{count}} съобщения в опашка",
|
||||
"session.followupDock.sendNow": "Изпратете сега",
|
||||
"session.followupDock.edit": "Редактиране",
|
||||
"session.followupDock.collapse": "Свиване на съобщенията в опашката",
|
||||
"session.followupDock.expand": "Разгъване на съобщенията в опашката",
|
||||
"session.revertDock.summary.one": "{{count}} върнато съобщение",
|
||||
"session.revertDock.summary.other": "{{count}} отменени съобщения",
|
||||
"session.revertDock.collapse": "Свиване на върнатите съобщения",
|
||||
"session.revertDock.expand": "Разгъване на върнатите съобщения",
|
||||
"session.revertDock.restore": "Възстановяване на съобщението",
|
||||
"session.new.title": "Изградете каквото и да било",
|
||||
"session.new.project.new": "Нов проект",
|
||||
"session.new.project.search": "Търсене на проекти",
|
||||
"session.new.project.add": "Добавете проект",
|
||||
"session.new.worktree.main": "Основен клон",
|
||||
"session.new.worktree.mainWithBranch": "Основен клон ({{branch}})",
|
||||
"session.new.worktree.create": "Създайте ново работно дърво",
|
||||
"session.new.workspace.runIn": "Стартирайте сесията",
|
||||
"session.new.workspace.triggerLocal": "Местен",
|
||||
"session.new.workspace.local": "Локално хранилище",
|
||||
"session.new.workspace.existing": "Работно пространство…",
|
||||
"session.new.git.none": "Не Git",
|
||||
"session.new.lastModified": "Последна промяна",
|
||||
"session.header.search.placeholder": "Търсене {{project}}",
|
||||
"session.header.searchFiles": "Търсене на файлове",
|
||||
"session.header.openIn": "Отвори в",
|
||||
"session.header.open.action": "Отворете {{app}}",
|
||||
"session.header.open.ariaLabel": "Отворете в {{app}}",
|
||||
"session.header.open.menu": "Отворени опции",
|
||||
"session.header.open.copyPath": "Копиране на пътя",
|
||||
"session.header.open.finder": "Търсач",
|
||||
"session.header.open.fileExplorer": "Файлов мениджър",
|
||||
"session.header.open.fileManager": "Файлов мениджър",
|
||||
"session.header.reveal.finder": "Разкриване във Finder",
|
||||
"session.header.reveal.fileExplorer": "Разкриване в File Explorer",
|
||||
"session.header.reveal.containingFolder": "Отворете съдържащата папка",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "Терминал",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "Статус",
|
||||
"status.popover.ariaLabel": "Сървърни конфигурации",
|
||||
"status.popover.tab.servers": "Сървъри",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Плъгини",
|
||||
"status.popover.action.manageServers": "Управление на сървъри",
|
||||
"session.share.popover.title": "Публикувайте в мрежата",
|
||||
"session.share.popover.description.shared": "Тази сесия е публична в мрежата. Достъпен е за всеки с връзката.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Споделете сесията публично в мрежата. Тя ще бъде достъпна за всеки с връзката.",
|
||||
"session.share.action.share": "Споделете",
|
||||
"session.share.action.publish": "Публикувай",
|
||||
"session.share.action.publishing": "Публикуване...",
|
||||
"session.share.action.unpublish": "Отмяна на публикуването",
|
||||
"session.share.action.unpublishing": "Отменя се публикуването...",
|
||||
"session.share.action.view": "Преглед",
|
||||
"session.share.copy.copied": "Копирано",
|
||||
"session.share.copy.copyLink": "Копиране на връзката",
|
||||
"lsp.tooltip.none": "Няма LSP сървъри",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "Подканата се зарежда...",
|
||||
"terminal.loading": "Терминалът се зарежда...",
|
||||
"terminal.title": "Терминал",
|
||||
"terminal.title.numbered": "Терминал {{number}}",
|
||||
"terminal.close": "Затворете терминала",
|
||||
"terminal.connectionLost.title": "Загубена връзка",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket затворено необичайно: {{code}}",
|
||||
"terminal.connectionLost.description":
|
||||
"Терминалната връзка беше прекъсната. Това може да се случи, когато сървърът се рестартира.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY билет за свързване е отхвърлен от произход или CSRF проверки. Проверете конфигурацията на сървъра CORS.",
|
||||
"terminal.connectTicket.statusError": "PTY неуспешно свързване на билет с {{status}}",
|
||||
"titlebar.update": "Актуализация",
|
||||
"titlebar.updateVersion": "Актуализация {{version}}",
|
||||
"common.closeTab": "Затваряне на раздела",
|
||||
"common.dismiss": "Отхвърляне",
|
||||
"common.moreCountSuffix": "(+{{count}} още)",
|
||||
"common.requestFailed": "Неуспешна заявка",
|
||||
"common.moreOptions": "Още опции",
|
||||
"common.learnMore": "Научете повече",
|
||||
"common.rename": "Преименуване",
|
||||
"common.export": "Експортиране",
|
||||
"common.reset": "Нулиране",
|
||||
"common.archive": "Архив",
|
||||
"common.delete": "Изтриване",
|
||||
"common.close": "затвори",
|
||||
"common.edit": "Редактиране",
|
||||
"common.loadMore": "Заредете повече",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Space",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "End",
|
||||
"common.key.pageUp": "Page Up",
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "неизвестен",
|
||||
"common.time.justNow": "Току-що",
|
||||
"common.time.minutesAgo.short": "преди {{count}}м",
|
||||
"common.time.hoursAgo.short": "преди {{count}}ч",
|
||||
"common.time.daysAgo.short": "преди {{count}} дни",
|
||||
"sidebar.menu.toggle": "Превключване на менюто",
|
||||
"sidebar.nav.projectsAndSessions": "Проекти и сесии",
|
||||
"sidebar.settings": "Настройки",
|
||||
"sidebar.help": "Помощ",
|
||||
"sidebar.workspaces.enable": "Активиране на работни пространства",
|
||||
"sidebar.workspaces.disable": "Деактивирайте работните пространства",
|
||||
"sidebar.gettingStarted.title": "Първи стъпки",
|
||||
"sidebar.gettingStarted.line1": "OpenCode включва безплатни модели, така че можете да започнете веднага.",
|
||||
"sidebar.gettingStarted.line2": "Свържете всеки доставчик, за да използвате модели, вкл. Claude, GPT, Gemini и т.н.",
|
||||
"sidebar.project.recentSessions": "Последни сесии",
|
||||
"sidebar.project.viewAllSessions": "Вижте всички сесии",
|
||||
"sidebar.project.clearNotifications": "Изчистване на известията",
|
||||
"sidebar.empty.title": "Няма отворени проекти",
|
||||
"sidebar.empty.description": "Отворете проект, за да започнете",
|
||||
"debugBar.ariaLabel": "Диагностика на ефективността на разработката",
|
||||
"debugBar.na": "няма",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Последен завършен преход на маршрута, докосващ страница на сесия, измерен от началото на рутера до първото боядисване след установяването му.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Подвижни кадри в секунда през последните 5 секунди.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Най-лошото време за кадър през последните 5 секунди.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Кадри над 32 ms през последните 5 секунди.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Блокирано време и брой дълги задачи през последните 5 секунди. Максимална задача: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "Най-лошото наблюдавано забавяне на въвеждане през последните 5 секунди.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Приблизителна продължителност на взаимодействието през последните 5 секунди. Това е подобно на INP, а не на официалните уеб показатели INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Кумулативна промяна на оформлението за текущия живот на приложението.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Използван JS heap срещу ограничение на heap. само Chromium.",
|
||||
"debugBar.mem.tip": "Използван JS heap срещу ограничение на heap. {{used}} от {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Принудително фокусиране на стилове върху всички интерактивни елементи",
|
||||
"debugBar.focus.on": "ВКЛ",
|
||||
"debugBar.focus.off": "ИЗКЛ",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip":
|
||||
"Принудително насочване на цялата посока на оформлението на приложението, без да променя избрания език",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Работен плот",
|
||||
"settings.section.server": "сървър",
|
||||
"settings.tab.general": "генерал",
|
||||
"settings.tab.shortcuts": "Преки пътища",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL интеграция",
|
||||
"settings.desktop.wsl.description": "Стартирайте сървъра OpenCode вътре в WSL на Windows.",
|
||||
"settings.general.section.appearance": "Външен вид",
|
||||
"settings.general.section.advanced": "Разширено",
|
||||
"settings.general.section.notifications": "Системни известия",
|
||||
"settings.general.section.updates": "Актуализации",
|
||||
"settings.general.section.sounds": "Звукови ефекти",
|
||||
"settings.general.section.feed": "Храна",
|
||||
"settings.general.section.display": "Дисплей",
|
||||
"settings.general.row.language.title": "език",
|
||||
"settings.general.row.language.description": "Променете езика на дисплея за OpenCode",
|
||||
"settings.general.row.shell.title": "Терминал shell",
|
||||
"settings.general.row.shell.description": "Shell използвани от инструментите на терминала и агента",
|
||||
"settings.general.row.shell.autoDefault": "Автоматично (по подразбиране)",
|
||||
"settings.general.row.shell.terminalOnly": "само терминал",
|
||||
"settings.general.row.appearance.title": "Външен вид",
|
||||
"settings.general.row.appearance.description": "Персонализирайте как изглежда OpenCode на вашето устройство",
|
||||
"settings.general.row.colorScheme.title": "Цветова схема",
|
||||
"settings.general.row.colorScheme.description": "Изберете дали OpenCode следва системната, светла или тъмна тема",
|
||||
"settings.general.row.theme.title": "Тема",
|
||||
"settings.general.row.theme.description": "Персонализирайте как е тематично OpenCode.",
|
||||
"settings.general.row.font.title": "Шрифт на кода",
|
||||
"settings.general.row.font.description": "Персонализирайте шрифта, използван в кодовите блокове",
|
||||
"settings.general.row.terminalFont.title": "Терминален шрифт",
|
||||
"settings.general.row.terminalFont.description": "Персонализирайте шрифта, използван в терминала",
|
||||
"settings.general.row.uiFont.title": "UI шрифт",
|
||||
"settings.general.row.uiFont.description": "Персонализирайте шрифта, използван в целия интерфейс",
|
||||
"settings.general.row.followup.title": "Последващо поведение",
|
||||
"settings.general.row.followup.description":
|
||||
"Изберете дали последващите подкани да се управляват незабавно или да чакат на опашка",
|
||||
"settings.general.row.followup.option.queue": "Опашка",
|
||||
"settings.general.row.followup.option.steer": "Насочвайте",
|
||||
"settings.general.row.showFileTree.title": "Файлово дърво",
|
||||
"settings.general.row.showFileTree.description": "Показване на панела на файловото дърво в сесии",
|
||||
"settings.general.row.showNavigation.title": "Контроли за навигация",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Показване на бутоните за връщане назад и напред в заглавната лента на работния плот",
|
||||
"settings.general.row.showSearch.title": "Командна палитра",
|
||||
"settings.general.row.showSearch.description":
|
||||
"Показване на бутона за палитра за търсене и команди в заглавната лента",
|
||||
"settings.general.row.showTerminal.title": "Терминал",
|
||||
"settings.general.row.showTerminal.description": "Покажете бутона на терминала в заглавната лента на работния плот",
|
||||
"settings.general.row.showStatus.title": "Състояние на сървъра",
|
||||
"settings.general.row.showStatus.description": "Показване на бутона за състояние на сървъра в заглавната лента",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Долна навигация",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Поставете заглавната лента и разделите на сесиите в долната част на екрана на мобилно устройство",
|
||||
"settings.general.row.showCustomAgents.title": "Показване на агента",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Превключвайте между агенти в композитора. Когато е скрит, по подразбиране е Build agent.",
|
||||
"settings.general.row.reasoningSummaries.title": "Показване на обобщения на разсъжденията",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Показване на обобщения на разсъжденията на модела във времевата линия",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Разгънете shell части на инструмента",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Показване на shell части от инструмента, разширени по подразбиране във времевата линия",
|
||||
"settings.general.row.editToolPartsExpanded.title": "Разгънете части за редактиране на инструмента",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Показване на части от инструмента за редактиране, писане и корекция, разширени по подразбиране във времевата линия",
|
||||
"settings.general.row.newInterface.title": "Ново оформление",
|
||||
"settings.general.row.newInterface.badge": "Нов",
|
||||
"settings.general.row.newInterface.description":
|
||||
"Използвайте новите раздели и началното оформление. Превключвайте между оформления за ограничено време.",
|
||||
"settings.general.row.newInterfaceNotice.title": "Вече използвате ново оформление",
|
||||
"settings.general.row.newInterfaceNotice.description": "Предишното оформление вече не е налично",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Отхвърляне",
|
||||
"settings.general.row.pinchZoom.title": "Щипнете, за да увеличите",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Разрешаване на жестове с щипване и Ctrl-превъртане на тракпада за мащабиране",
|
||||
"settings.general.row.wayland.title": "Използвайте естествен Wayland",
|
||||
"settings.general.row.wayland.description": "Деактивирайте X11 резервен вариант на Wayland. Изисква рестартиране.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"На Linux с монитори със смесена честота на опресняване естественият Wayland може да бъде по-стабилен.",
|
||||
"settings.general.row.releaseNotes.title": "Бележки по изданието",
|
||||
"settings.general.row.releaseNotes.description": "Показване на изскачащи прозорци с новости след актуализации",
|
||||
"settings.updates.row.startup.title": "Проверете за актуализации при стартиране",
|
||||
"settings.updates.row.startup.description": "Автоматично проверявайте за актуализации при стартиране на OpenCode",
|
||||
"settings.updates.row.check.title": "Проверете за актуализации",
|
||||
"settings.updates.row.check.description": "Проверете ръчно за актуализации и инсталирайте, ако има такива",
|
||||
"settings.updates.action.checkNow": "Проверете сега",
|
||||
"settings.updates.action.checking": "Проверка...",
|
||||
"settings.updates.action.downloading": "Изтегля се...",
|
||||
"settings.updates.action.installing": "Инсталира се...",
|
||||
"settings.updates.toast.latest.title": "Вие сте в крак с времето",
|
||||
"settings.updates.toast.latest.description": "Вие използвате най-новата версия на OpenCode.",
|
||||
"sound.option.none": "Няма",
|
||||
"sound.option.alert01": "Сигнал 01",
|
||||
"sound.option.alert02": "Сигнал 02",
|
||||
"sound.option.alert03": "Сигнал 03",
|
||||
"sound.option.alert04": "Сигнал 04",
|
||||
"sound.option.alert05": "Сигнал 05",
|
||||
"sound.option.alert06": "Сигнал 06",
|
||||
"sound.option.alert07": "Сигнал 07",
|
||||
"sound.option.alert08": "Сигнал 08",
|
||||
"sound.option.alert09": "Сигнал 09",
|
||||
"sound.option.alert10": "Сигнал 10",
|
||||
"sound.option.bipbop01": "Бип-боп 01",
|
||||
"sound.option.bipbop02": "Бип-боп 02",
|
||||
"sound.option.bipbop03": "Бип-боп 03",
|
||||
"sound.option.bipbop04": "Бип-боп 04",
|
||||
"sound.option.bipbop05": "Бип-боп 05",
|
||||
"sound.option.bipbop06": "Бип-боп 06",
|
||||
"sound.option.bipbop07": "Бип-боп 07",
|
||||
"sound.option.bipbop08": "Бип-боп 08",
|
||||
"sound.option.bipbop09": "Бип-боп 09",
|
||||
"sound.option.bipbop10": "Бип-боп 10",
|
||||
"sound.option.staplebops01": "Стейпълбопс 01",
|
||||
"sound.option.staplebops02": "Стейпълбопс 02",
|
||||
"sound.option.staplebops03": "Стейпълбопс 03",
|
||||
"sound.option.staplebops04": "Стейпълбопс 04",
|
||||
"sound.option.staplebops05": "Стейпълбопс 05",
|
||||
"sound.option.staplebops06": "Стейпълбопс 06",
|
||||
"sound.option.staplebops07": "Стейпълбопс 07",
|
||||
"sound.option.nope01": "Не 01",
|
||||
"sound.option.nope02": "Не 02",
|
||||
"sound.option.nope03": "Не 03",
|
||||
"sound.option.nope04": "Не 04",
|
||||
"sound.option.nope05": "Не 05",
|
||||
"sound.option.nope06": "Не 06",
|
||||
"sound.option.nope07": "Не 07",
|
||||
"sound.option.nope08": "Не 08",
|
||||
"sound.option.nope09": "Не 09",
|
||||
"sound.option.nope10": "Не 10",
|
||||
"sound.option.nope11": "Не 11",
|
||||
"sound.option.nope12": "Не 12",
|
||||
"sound.option.yup01": "Да 01",
|
||||
"sound.option.yup02": "Да 02",
|
||||
"sound.option.yup03": "Да 03",
|
||||
"sound.option.yup04": "Да 04",
|
||||
"sound.option.yup05": "Да 05",
|
||||
"sound.option.yup06": "Да 06",
|
||||
"settings.general.notifications.agent.title": "агент",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Показване на системно известие, когато агентът е завършен или се нуждае от внимание",
|
||||
"settings.general.notifications.permissions.title": "Разрешения",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Показване на системно известие, когато е необходимо разрешение",
|
||||
"settings.general.notifications.errors.title": "Грешки",
|
||||
"settings.general.notifications.errors.description": "Показване на системно известие при възникване на грешка",
|
||||
"settings.general.sounds.agent.title": "агент",
|
||||
"settings.general.sounds.agent.description":
|
||||
"Възпроизвеждане на звук, когато агентът е завършен или се нуждае от внимание",
|
||||
"settings.general.sounds.permissions.title": "Разрешения",
|
||||
"settings.general.sounds.permissions.description": "Възпроизвеждане на звук, когато е необходимо разрешение",
|
||||
"settings.general.sounds.errors.title": "Грешки",
|
||||
"settings.general.sounds.errors.description": "Възпроизвеждане на звук при възникване на грешка",
|
||||
"settings.shortcuts.title": "Клавишни комбинации",
|
||||
"settings.shortcuts.reset.button": "Възстановяване на настройките по подразбиране",
|
||||
"settings.shortcuts.reset.toast.title": "Нулиране на преките пътища",
|
||||
"settings.shortcuts.reset.toast.description": "Клавишните комбинации са нулирани до стойностите по подразбиране.",
|
||||
"settings.shortcuts.conflict.title": "Пряк път вече се използва",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} вече е присвоен на {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Неприсвоен",
|
||||
"settings.shortcuts.pressKeys": "Натиснете клавишите",
|
||||
"settings.shortcuts.search.placeholder": "Преки пътища за търсене",
|
||||
"settings.shortcuts.search.empty": "Няма намерени преки пътища",
|
||||
"settings.shortcuts.group.general": "генерал",
|
||||
"settings.shortcuts.group.session": "Сесия",
|
||||
"settings.shortcuts.group.navigation": "Навигация",
|
||||
"settings.shortcuts.group.modelAndAgent": "Модел и агент",
|
||||
"settings.shortcuts.group.terminal": "Терминал",
|
||||
"settings.shortcuts.group.prompt": "подкана",
|
||||
"settings.providers.title": "Доставчици",
|
||||
"settings.providers.description": "Настройките на доставчика ще могат да се конфигурират тук.",
|
||||
"settings.providers.section.connected": "Свързани доставчици",
|
||||
"settings.providers.connected.empty": "Няма свързани доставчици",
|
||||
"settings.providers.connected.environmentDescription": "Свързан от променливите на вашата среда",
|
||||
"settings.providers.section.popular": "Популярни доставчици",
|
||||
"settings.providers.custom.description": "Добавете OpenAI-съвместим доставчик по база URL.",
|
||||
"settings.providers.tag.environment": "Околна среда",
|
||||
"settings.providers.tag.config": "Конфиг",
|
||||
"settings.providers.tag.custom": "По поръчка",
|
||||
"settings.providers.tag.other": "други",
|
||||
"settings.models.title": "Модели",
|
||||
"settings.models.description": "Настройките на модела ще могат да се конфигурират тук.",
|
||||
"settings.agents.title": "Агенти",
|
||||
"settings.agents.description": "Настройките на агента ще могат да се конфигурират тук.",
|
||||
"settings.commands.title": "Команди",
|
||||
"settings.commands.description": "Настройките на командите ще могат да се конфигурират тук.",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "Настройките на MCP ще могат да се конфигурират тук.",
|
||||
"settings.permissions.title": "Разрешения",
|
||||
"settings.permissions.description": "Контролирайте какви инструменти сървърът може да използва по подразбиране.",
|
||||
"settings.permissions.section.tools": "Инструменти",
|
||||
"settings.permissions.toast.updateFailed.title": "Неуспешно актуализиране на разрешенията",
|
||||
"settings.permissions.action.allow": "Разрешете",
|
||||
"settings.permissions.action.ask": "Попитайте",
|
||||
"settings.permissions.action.deny": "Откажи",
|
||||
"settings.permissions.tool.read.title": "Прочетете",
|
||||
"settings.permissions.tool.read.description": "Четене на файл (съвпада с пътя на файла)",
|
||||
"settings.permissions.tool.edit.title": "Редактиране",
|
||||
"settings.permissions.tool.edit.description": "Променете файлове, включително редакции, записи и корекции",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Съвпадение на файлове с помощта на glob модели",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Търсете съдържанието на файла с помощта на регулярни изрази",
|
||||
"settings.permissions.tool.list.title": "списък",
|
||||
"settings.permissions.tool.list.description": "Избройте файлове в директория",
|
||||
"settings.permissions.tool.bash.title": "Баш",
|
||||
"settings.permissions.tool.bash.description": "Изпълнете shell команди",
|
||||
"settings.permissions.tool.task.title": "Задача",
|
||||
"settings.permissions.tool.task.description": "Стартиране на подагенти",
|
||||
"settings.permissions.tool.skill.title": "Умение",
|
||||
"settings.permissions.tool.skill.description": "Заредете умение по име",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Изпълнявайте заявки за езиков сървър",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "Актуализирайте списъка със задачи",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Извличане на съдържание от URL",
|
||||
"settings.permissions.tool.websearch.title": "Търсене в мрежата",
|
||||
"settings.permissions.tool.websearch.description": "Търсете в мрежата",
|
||||
"settings.permissions.tool.external_directory.title": "Външен указател",
|
||||
"settings.permissions.tool.external_directory.description": "Достъп до файлове извън директорията на проекта",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description":
|
||||
"Откриване на повтарящи се извиквания на инструменти с идентичен вход",
|
||||
"session.delete.failed.title": "Неуспешно изтриване на сесията",
|
||||
"session.delete.title": "Изтриване на сесия",
|
||||
"session.delete.confirm": "Да се изтрие ли сесията „{{name}}“?",
|
||||
"session.delete.button": "Изтриване на сесия",
|
||||
"workspace.new": "Ново работно пространство",
|
||||
"workspace.type.local": "местен",
|
||||
"workspace.type.sandbox": "пясъчник",
|
||||
"workspace.create.failed.title": "Неуспешно създаване на работно пространство",
|
||||
"workspace.delete.failed.title": "Неуспешно изтриване на работното пространство",
|
||||
"workspace.resetting.title": "Нулиране на работното пространство",
|
||||
"workspace.resetting.description": "Това може да отнеме минута.",
|
||||
"workspace.reset.failed.title": "Неуспешно нулиране на работното пространство",
|
||||
"workspace.reset.success.title": "Нулиране на работното пространство",
|
||||
"workspace.reset.success.description": "Работното пространство вече съответства на клона по подразбиране.",
|
||||
"workspace.error.stillPreparing": "Работното пространство все още се подготвя",
|
||||
"workspace.status.checking": "Проверка за необединени промени...",
|
||||
"workspace.status.error": "Не може да се провери състоянието на git.",
|
||||
"workspace.status.clean": "Не са открити необединени промени.",
|
||||
"workspace.status.dirty": "Открити са необединени промени в това работно пространство.",
|
||||
"workspace.delete.title": "Изтриване на работното пространство",
|
||||
"workspace.delete.confirm": "Да се изтрие ли работното пространство „{{name}}“?",
|
||||
"workspace.delete.button": "Изтриване на работното пространство",
|
||||
"workspace.reset.title": "Нулирайте работното пространство",
|
||||
"workspace.reset.confirm": "Нулиране на работното пространство „{{name}}“?",
|
||||
"workspace.reset.button": "Нулирайте работното пространство",
|
||||
"workspace.reset.archived.none": "Няма да се архивират активни сесии.",
|
||||
"workspace.reset.archived.one": "1 сесия ще бъде архивирана.",
|
||||
"workspace.reset.archived.many": "{{count}} сесии ще бъдат архивирани.",
|
||||
"workspace.reset.note": "Това ще нулира работното пространство, за да съответства на клона по подразбиране.",
|
||||
"dialog.usageExceeded.dontShowAgain": "Не показвай отново",
|
||||
}
|
||||
@@ -0,0 +1,1152 @@
|
||||
export const dict: Record<string, string> = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "ফাইল",
|
||||
"desktop.menu.edit": "সম্পাদনা করুন",
|
||||
"desktop.menu.view": "দেখুন",
|
||||
"desktop.menu.go": "যাও",
|
||||
"desktop.menu.window": "জানালা",
|
||||
"desktop.menu.help": "সাহায্য",
|
||||
"desktop.menu.checkForUpdates": "আপডেটের জন্য চেক করুন...",
|
||||
"desktop.menu.settings": "সেটিংস",
|
||||
"desktop.menu.reloadWebview": "Webview পুনরায় লোড করুন",
|
||||
"desktop.menu.restart": "রিস্টার্ট করুন",
|
||||
"desktop.menu.exportLogs": "লগ রপ্তানি করুন...",
|
||||
"desktop.menu.newSession": "নতুন সেশন",
|
||||
"desktop.menu.openProject": "প্রকল্প খুলুন...",
|
||||
"desktop.menu.newWindow": "নতুন উইন্ডো",
|
||||
"desktop.menu.closeWindow": "উইন্ডো বন্ধ করুন",
|
||||
"desktop.menu.undo": "পূর্বাবস্থায় ফেরান",
|
||||
"desktop.menu.redo": "আবার করুন",
|
||||
"desktop.menu.cut": "কাটা",
|
||||
"desktop.menu.copy": "কপি",
|
||||
"desktop.menu.paste": "পেস্ট করুন",
|
||||
"desktop.menu.delete": "মুছে দিন",
|
||||
"desktop.menu.selectAll": "সব নির্বাচন করুন",
|
||||
"desktop.menu.toggleSidebar": "সাইডবার টগল করুন",
|
||||
"desktop.menu.toggleTerminal": "টার্মিনাল টগল করুন",
|
||||
"desktop.menu.toggleFileTree": "ফাইল ট্রি টগল করুন",
|
||||
"desktop.menu.reload": "পুনরায় লোড করুন",
|
||||
"desktop.menu.toggleDeveloperTools": "ডেভেলপার টুল টগল করুন",
|
||||
"desktop.menu.actualSize": "প্রকৃত আকার",
|
||||
"desktop.menu.zoomIn": "জুম ইন করুন",
|
||||
"desktop.menu.zoomOut": "জুম আউট করুন",
|
||||
"desktop.menu.toggleFullScreen": "ফুল স্ক্রীন টগল করুন",
|
||||
"desktop.menu.back": "ব্যাক",
|
||||
"desktop.menu.forward": "ফরোয়ার্ড",
|
||||
"desktop.menu.previousSession": "পূর্ববর্তী সেশন",
|
||||
"desktop.menu.nextSession": "পরবর্তী সেশন",
|
||||
"desktop.menu.previousProject": "পূর্ববর্তী প্রকল্প",
|
||||
"desktop.menu.nextProject": "পরবর্তী প্রকল্প",
|
||||
"desktop.menu.minimize": "ছোট করুন",
|
||||
"desktop.menu.maximize": "সর্বাধিক করুন",
|
||||
"desktop.menu.documentation": "OpenCode ডকুমেন্টেশন",
|
||||
"desktop.menu.supportForum": "সমর্থন ফোরাম",
|
||||
"desktop.menu.shareFeedback": "মতামত শেয়ার করুন",
|
||||
"desktop.menu.reportBug": "একটি বাগ রিপোর্ট করুন",
|
||||
"desktop.menu.ariaLabel": "OpenCode মেনু",
|
||||
"desktop.updater.dialog.checkFailed.message": "আপডেট চেক ব্যর্থ হয়েছে.",
|
||||
"desktop.updater.dialog.checkFailed.title": "আপডেট ত্রুটি",
|
||||
"desktop.updater.dialog.upToDate.message": "আপনি আপ টু ডেট.",
|
||||
"desktop.updater.dialog.upToDate.title": "কোন আপডেট নেই",
|
||||
"desktop.updater.dialog.ready.message": "আপডেট {{version}} ডাউনলোড করা হয়েছে। এখন রিস্টার্ট করবেন?",
|
||||
"desktop.updater.dialog.ready.title": "আপডেট প্রস্তুত",
|
||||
"desktop.updater.dialog.restart": "রিস্টার্ট করুন",
|
||||
"desktop.updater.dialog.later": "পরে",
|
||||
"desktop.recovery.action.relaunch": "পুনরায় চালু করুন",
|
||||
"desktop.recovery.action.exportLogs": "রপ্তানি লগ",
|
||||
"desktop.recovery.action.keepWaiting": "অপেক্ষায় থাকুন",
|
||||
"desktop.recovery.action.quit": "প্রস্থান করুন",
|
||||
"desktop.recovery.loadFailed": "OpenCode লোড হতে ব্যর্থ হয়েছে৷",
|
||||
"desktop.recovery.terminated": "OpenCode উইন্ডো অপ্রত্যাশিতভাবে বন্ধ হয়ে গেছে",
|
||||
"desktop.recovery.unresponsive": "OpenCode সাড়া দিচ্ছে না",
|
||||
"desktop.recovery.unresponsive.detail":
|
||||
"আপনি অ্যাপটি পুনরায় চালু করতে পারেন, লগগুলি খুলতে পারেন বা অপেক্ষা করতে পারেন৷",
|
||||
"desktop.recovery.loadFailed.detail": "উইন্ডো: {{window}}\nURL: {{url}}\nত্রুটি: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "উইন্ডো: {{window}}\nকারণ: {{reason}}\nকোড: {{code}}",
|
||||
"desktop.recovery.unknown": "<অজানা>",
|
||||
"desktop.dialog.chooseFolder": "একটি ফোল্ডার নির্বাচন করুন",
|
||||
"desktop.dialog.chooseFile": "একটি ফাইল নির্বাচন করুন",
|
||||
"desktop.dialog.saveFile": "ফাইল সংরক্ষণ করুন",
|
||||
"desktop.dialog.files": "ফাইল",
|
||||
"desktop.server.local": "স্থানীয় সার্ভার",
|
||||
"desktop.wsl.error.windowsOnly": "WSL শুধুমাত্র Windows এ উপলব্ধ",
|
||||
"desktop.wsl.error.unavailable": "WSL অনুপলব্ধ",
|
||||
"desktop.wsl.error.listInstalled": "ইনস্টল করা WSL ডিস্ট্রো তালিকাভুক্ত করতে ব্যর্থ",
|
||||
"desktop.wsl.error.listOnline": "অনলাইন WSL ডিস্ট্রো তালিকাভুক্ত করতে ব্যর্থ",
|
||||
"desktop.wsl.error.executeDistro": "ডিস্ট্রোতে কমান্ড চালানো যাবে না",
|
||||
"desktop.wsl.error.installWsl": "WSL ইনস্টলেশন ব্যর্থ হয়েছে৷",
|
||||
"desktop.wsl.error.installDistro": "ডিস্ট্রো ইনস্টল করতে ব্যর্থ হয়েছে: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "OpenCode ইনস্টলেশন ব্যর্থ হয়েছে৷",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} ইতিমধ্যেই যোগ করা হয়েছে৷",
|
||||
"desktop.wsl.error.opencodeMissing": "এই ডিস্ট্রোতে opencode ইনস্টল করা নেই",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode ইনস্টল করা আছে কিন্তু চালানো যায়নি",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode {{distro}} এ ইনস্টল করা নেই",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode আপডেট শেষ হয়েছে কিন্তু {{distro}} এখনও রিপোর্ট করে {{installed}}; প্রত্যাশিত {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "কোন সংস্করণ নেই",
|
||||
"desktop.wsl.error.serverExited": "WSL সার্ভার স্টার্টআপের পরে প্রস্থান হয়েছে (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"WSL সার্ভার সুস্থ হওয়ার আগে প্রস্থান করেছে (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout": "{{distro}} স্বাস্থ্য পরীক্ষার জন্য সাইডকার {{timeout}}ms পরে সময় শেষ হয়েছে",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} {{timeout}}ms পরে সময় শেষ হয়েছে",
|
||||
"desktop.wsl.error.failedPort": "পোর্ট পেতে ব্যর্থ",
|
||||
"desktop.picker.error.notSelected": "ফাইল পিকার দ্বারা নির্বাচন করা হয়নি",
|
||||
"desktop.picker.error.sizeLimit": "নির্বাচিত সংযুক্তিগুলি {{limit}} MB সীমা অতিক্রম করেছে৷",
|
||||
"command.category.suggested": "প্রস্তাবিত",
|
||||
"command.category.view": "দেখুন",
|
||||
"command.category.project": "প্রকল্প",
|
||||
"command.category.provider": "প্রদানকারী",
|
||||
"command.category.server": "সার্ভার",
|
||||
"command.category.session": "সেশন",
|
||||
"command.category.theme": "থিম",
|
||||
"command.category.language": "ভাষা",
|
||||
"command.category.file": "ফাইল",
|
||||
"command.category.context": "প্রসঙ্গ",
|
||||
"command.category.terminal": "টার্মিনাল",
|
||||
"command.category.model": "মডেল",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "এজেন্ট",
|
||||
"command.category.permissions": "অনুমতি",
|
||||
"command.category.workspace": "ওয়ার্কস্পেস",
|
||||
"command.category.settings": "সেটিংস",
|
||||
"command.logs.export": "রপ্তানি লগ",
|
||||
"theme.scheme.system": "সিস্টেম",
|
||||
"theme.scheme.light": "আলো",
|
||||
"theme.scheme.dark": "অন্ধকার",
|
||||
"command.sidebar.toggle": "সাইডবার টগল করুন",
|
||||
"command.project.open": "প্রকল্প খুলুন",
|
||||
"command.project.previous": "আগের প্রকল্প",
|
||||
"command.project.next": "পরবর্তী প্রকল্প",
|
||||
"command.project.index": "প্রকল্পে স্যুইচ করুন {{index}}",
|
||||
"command.provider.connect": "সংযোগ প্রদানকারী",
|
||||
"command.server.switch": "সার্ভার পরিবর্তন করুন",
|
||||
"command.settings.open": "সেটিংস খুলুন",
|
||||
"command.session.previous": "আগের সেশন",
|
||||
"command.session.next": "পরবর্তী সেশন",
|
||||
"command.session.previous.unseen": "পূর্ববর্তী অপঠিত সেশন",
|
||||
"command.session.next.unseen": "পরবর্তী অপঠিত সেশন",
|
||||
"command.session.archive": "আর্কাইভ সেশন",
|
||||
"command.palette": "কমান্ড প্যালেট",
|
||||
"command.theme.cycle": "সাইকেল থিম",
|
||||
"command.theme.set": "থিম ব্যবহার করুন: {{theme}}",
|
||||
"command.theme.scheme.cycle": "সাইকেল রঙের স্কিম",
|
||||
"command.theme.scheme.set": "রঙের স্কিম ব্যবহার করুন: {{scheme}}",
|
||||
"command.language.cycle": "সাইকেল ভাষা",
|
||||
"command.language.set": "ভাষা ব্যবহার করুন: {{language}}",
|
||||
"command.session.new": "নতুন সেশন",
|
||||
"command.file.open": "ফাইল খুলুন",
|
||||
"command.tab.close": "ট্যাব বন্ধ করুন",
|
||||
"command.tab.reopenClosed": "বন্ধ ট্যাব আবার খুলুন",
|
||||
"command.context.addSelection": "প্রসঙ্গে নির্বাচন যোগ করুন",
|
||||
"command.context.addSelection.description": "বর্তমান ফাইল থেকে নির্বাচিত লাইন যোগ করুন",
|
||||
"command.input.focus": "ফোকাস ইনপুট",
|
||||
"command.terminal.toggle": "টার্মিনাল টগল করুন",
|
||||
"command.fileTree.toggle": "ফাইল ট্রি টগল করুন",
|
||||
"command.review.toggle": "পর্যালোচনা টগল করুন",
|
||||
"command.terminal.new": "নতুন টার্মিনাল",
|
||||
"command.terminal.new.description": "একটি নতুন টার্মিনাল ট্যাব তৈরি করুন",
|
||||
"command.steps.toggle": "পদক্ষেপ টগল করুন",
|
||||
"command.steps.toggle.description": "বর্তমান বার্তার জন্য ধাপগুলি দেখান বা লুকান৷",
|
||||
"command.message.previous": "আগের বার্তা",
|
||||
"command.message.previous.description": "আগের ব্যবহারকারী বার্তা যান",
|
||||
"command.message.next": "পরবর্তী বার্তা",
|
||||
"command.message.next.description": "পরবর্তী ব্যবহারকারী বার্তা যান",
|
||||
"command.model.choose": "মডেল নির্বাচন করুন",
|
||||
"command.model.choose.description": "একটি ভিন্ন মডেল নির্বাচন করুন",
|
||||
"command.mcp.toggle": "টগল করুন MCPs",
|
||||
"command.mcp.toggle.description": "টগল করুন MCPs",
|
||||
"command.agent.cycle": "সাইকেল এজেন্ট",
|
||||
"command.agent.cycle.description": "পরবর্তী এজেন্টে যান",
|
||||
"command.agent.cycle.reverse": "সাইকেল এজেন্ট পিছনের দিকে",
|
||||
"command.agent.cycle.reverse.description": "আগের এজেন্টে স্যুইচ করুন",
|
||||
"command.model.variant.cycle": "চক্র চিন্তা প্রচেষ্টা",
|
||||
"command.model.variant.cycle.description": "পরবর্তী প্রচেষ্টা স্তরে স্যুইচ করুন",
|
||||
"command.prompt.mode.shell": "শেল",
|
||||
"command.prompt.mode.normal": "প্রম্পট",
|
||||
"command.permissions.autoaccept.enable": "স্বয়ংক্রিয়ভাবে অনুমতি গ্রহণ করুন",
|
||||
"command.permissions.autoaccept.disable": "স্বয়ংক্রিয়ভাবে অনুমতি নেওয়া বন্ধ করুন",
|
||||
"command.workspace.toggle": "ওয়ার্কস্পেস টগল করুন",
|
||||
"command.workspace.toggle.description": "সাইডবারে একাধিক ওয়ার্কস্পেস সক্রিয় বা অক্ষম করুন",
|
||||
"command.session.undo": "পূর্বাবস্থায় ফেরান",
|
||||
"command.session.undo.description": "শেষ বার্তাটি পূর্বাবস্থায় ফেরান",
|
||||
"command.session.redo": "আবার করুন",
|
||||
"command.session.redo.description": "শেষ পূর্বাবস্থায় ফেরানো বার্তাটি আবার করুন",
|
||||
"command.session.compact": "কমপ্যাক্ট সেশন",
|
||||
"command.session.compact.description": "প্রসঙ্গ আকার কমাতে সেশনের সারসংক্ষেপ করুন",
|
||||
"command.session.fork": "বার্তা থেকে ফর্ক",
|
||||
"command.session.fork.description": "একটি পূর্ববর্তী বার্তা থেকে একটি নতুন সেশন তৈরি করুন",
|
||||
"command.session.share": "সেশন শেয়ার করুন",
|
||||
"command.session.share.description": "এই সেশনটি শেয়ার করুন এবং ক্লিপবোর্ডে URL অনুলিপি করুন",
|
||||
"command.session.unshare": "সেশন শেয়ার করা",
|
||||
"command.session.unshare.description": "এই সেশন শেয়ার করা বন্ধ করুন",
|
||||
"command.session.export": "রপ্তানি সেশন",
|
||||
"command.session.export.description": "JSON হিসাবে সম্পূর্ণ সেশন ট্রান্সক্রিপ্ট রপ্তানি করুন",
|
||||
"palette.search.placeholder": "অনুসন্ধান ফাইল, কমান্ড, এবং সেশন",
|
||||
"palette.search.placeholder.home": "অনুসন্ধান কমান্ড এবং সেশন",
|
||||
"palette.empty": "কোন ফলাফল পাওয়া যায়নি",
|
||||
"palette.group.commands": "কমান্ড",
|
||||
"palette.group.files": "ফাইল",
|
||||
"dialog.provider.search.placeholder": "অনুসন্ধান প্রদানকারী",
|
||||
"dialog.provider.empty": "কোন প্রদানকারী পাওয়া যায়নি",
|
||||
"dialog.provider.group.popular": "জনপ্রিয়",
|
||||
"dialog.provider.group.other": "অন্যান্য",
|
||||
"dialog.provider.custom.label": "কাস্টম OpenAI-সামঞ্জস্যপূর্ণ প্রদানকারী",
|
||||
"dialog.provider.tag.recommended": "প্রস্তাবিত",
|
||||
"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 মডেল",
|
||||
"dialog.provider.openrouter.note": "একটি প্রদানকারী থেকে সমস্ত সমর্থিত মডেল অ্যাক্সেস করুন",
|
||||
"dialog.provider.vercel.note": "স্মার্ট রাউটিং সহ AI মডেলগুলিতে ইউনিফাইড অ্যাক্সেস",
|
||||
"dialog.model.select.title": "মডেল নির্বাচন করুন",
|
||||
"dialog.model.search.placeholder": "মডেল অনুসন্ধান করুন",
|
||||
"dialog.model.empty": "কোন মডেল ফলাফল নেই",
|
||||
"dialog.model.manage": "মডেল পরিচালনা করুন",
|
||||
"dialog.model.manage.description": "মডেল নির্বাচকের মধ্যে কোন মডেলগুলি উপস্থিত হবে তা কাস্টমাইজ করুন৷",
|
||||
"dialog.model.manage.provider.toggle": "সমস্ত {{provider}} মডেল টগল করুন",
|
||||
"dialog.model.unpaid.freeModels.title": "বিনামূল্যের মডেলগুলি OpenCode দ্বারা প্রদত্ত",
|
||||
"dialog.model.unpaid.addMore.title": "জনপ্রিয় প্রদানকারীদের থেকে আরো মডেল যোগ করুন",
|
||||
"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": "ব্রাউজার",
|
||||
"provider.connect.method.headless": "মাথাবিহীন",
|
||||
"provider.connect.status.inProgress": "অনুমোদন চলছে...",
|
||||
"provider.connect.status.waiting": "অনুমোদনের জন্য অপেক্ষা করা হচ্ছে...",
|
||||
"provider.connect.status.failed": "অনুমোদন ব্যর্থ হয়েছে: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"আপনার অ্যাকাউন্ট সংযোগ করতে আপনার {{provider}} API কী লিখুন এবং OpenCode-এ {{provider}} মডেলগুলি ব্যবহার করুন৷",
|
||||
"provider.connect.apiKey.label": "{{provider}} API কী",
|
||||
"provider.connect.apiKey.placeholder": "API কী",
|
||||
"provider.connect.apiKey.required": "API কী প্রয়োজন",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen আপনাকে কোডিং এজেন্টের জন্য নির্ভরযোগ্য অপ্টিমাইজ করা মডেলের একটি কিউরেটেড সেটে অ্যাক্সেস দেয়।",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"একটি API কী দিয়ে আপনি Claude, GPT, Gemini, GLM এবং আরও অনেক কিছুর মতো মডেলগুলিতে অ্যাক্সেস পাবেন৷",
|
||||
"provider.connect.opencodeZen.visit.prefix": "ভিজিট করুন",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": "আপনার API কী সংগ্রহ করতে।",
|
||||
"provider.connect.oauth.code.visit.prefix": "ভিজিট করুন",
|
||||
"provider.connect.oauth.code.visit.link": "এই লিঙ্ক",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
"আপনার অ্যাকাউন্ট সংযোগ করতে আপনার অনুমোদন কোড সংগ্রহ করতে এবং {{provider}} মডেলগুলি OpenCode-এ ব্যবহার করতে।",
|
||||
"provider.connect.oauth.code.label": "{{method}} অনুমোদন কোড",
|
||||
"provider.connect.oauth.code.placeholder": "অনুমোদন কোড",
|
||||
"provider.connect.oauth.code.required": "অনুমোদন কোড প্রয়োজন",
|
||||
"provider.connect.oauth.code.invalid": "অবৈধ অনুমোদন কোড",
|
||||
"provider.connect.oauth.auto.visit.prefix": "ভিজিট করুন",
|
||||
"provider.connect.oauth.auto.visit.link": "এই লিঙ্ক",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
"এবং আপনার অ্যাকাউন্ট সংযোগ করতে নীচের কোডটি লিখুন এবং {{provider}} মডেলগুলি OpenCode-এ ব্যবহার করুন৷",
|
||||
"provider.connect.oauth.auto.confirmationCode": "নিশ্চিতকরণ কোড",
|
||||
"provider.connect.toast.connected.title": "{{provider}} সংযুক্ত",
|
||||
"provider.connect.toast.connected.description": "{{provider}} মডেলগুলি এখন ব্যবহারের জন্য উপলব্ধ৷",
|
||||
"provider.custom.title": "কাস্টম প্রদানকারী",
|
||||
"provider.custom.unavailable": "কাস্টম প্রদানকারী এই সার্ভারে অনুপলব্ধ",
|
||||
"provider.custom.description.prefix": "একটি OpenAI-সামঞ্জস্যপূর্ণ প্রদানকারী কনফিগার করুন। দেখুন",
|
||||
"provider.custom.description.link": "প্রদানকারী কনফিগার ডক্স",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "প্রদানকারী আইডি",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "ছোট হাতের অক্ষর, সংখ্যা, হাইফেন বা আন্ডারস্কোর",
|
||||
"provider.custom.field.name.label": "প্রদর্শনের নাম",
|
||||
"provider.custom.field.name.placeholder": "আমার AI প্রদানকারী",
|
||||
"provider.custom.field.baseURL.label": "ভিত্তি URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API কী",
|
||||
"provider.custom.field.apiKey.placeholder": "API কী",
|
||||
"provider.custom.field.apiKey.description": "ঐচ্ছিক। আপনি শিরোনামের মাধ্যমে প্রমাণীকরণ পরিচালনা করলে খালি ছেড়ে দিন।",
|
||||
"provider.custom.models.label": "মডেল",
|
||||
"provider.custom.models.id.label": "আইডি",
|
||||
"provider.custom.models.id.placeholder": "মডেল-আইডি",
|
||||
"provider.custom.models.name.label": "নাম",
|
||||
"provider.custom.models.name.placeholder": "প্রদর্শনের নাম",
|
||||
"provider.custom.models.remove": "মডেল সরান",
|
||||
"provider.custom.models.add": "মডেল যোগ করুন",
|
||||
"provider.custom.headers.label": "হেডার (ঐচ্ছিক)",
|
||||
"provider.custom.headers.key.label": "হেডার",
|
||||
"provider.custom.headers.key.placeholder": "হেডার-নাম",
|
||||
"provider.custom.headers.value.label": "মান",
|
||||
"provider.custom.headers.value.placeholder": "মান",
|
||||
"provider.custom.headers.remove": "হেডার সরান",
|
||||
"provider.custom.headers.add": "হেডার যোগ করুন",
|
||||
"provider.custom.error.providerID.required": "প্রদানকারী আইডি প্রয়োজন",
|
||||
"provider.custom.error.providerID.format": "ছোট হাতের অক্ষর, সংখ্যা, হাইফেন বা আন্ডারস্কোর ব্যবহার করুন",
|
||||
"provider.custom.error.providerID.exists": "সেই প্রদানকারী আইডি আগে থেকেই আছে",
|
||||
"provider.custom.error.name.required": "প্রদর্শন নাম প্রয়োজন",
|
||||
"provider.custom.error.baseURL.required": "বেস URL প্রয়োজন",
|
||||
"provider.custom.error.baseURL.format": "http:// অথবা https:// দিয়ে শুরু করতে হবে",
|
||||
"provider.custom.error.required": "প্রয়োজন",
|
||||
"provider.custom.error.duplicate": "ডুপ্লিকেট",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} সংযোগ বিচ্ছিন্ন",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} মডেল আর উপলব্ধ নেই৷",
|
||||
"model.tag.free": "বিনামূল্যে",
|
||||
"model.tag.latest": "সর্বশেষ",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "পাঠ্য",
|
||||
"model.input.image": "ইমেজ",
|
||||
"model.input.audio": "অডিও",
|
||||
"model.input.video": "ভিডিও",
|
||||
"model.input.pdf": "পিডিএফ",
|
||||
"model.tooltip.allows": "অনুমতি দেয়: {{inputs}}",
|
||||
"model.tooltip.context.label": "প্রসঙ্গ",
|
||||
"model.tooltip.inputs": "ইনপুট",
|
||||
"model.tooltip.model": "মডেল",
|
||||
"model.tooltip.provider": "প্রদানকারী",
|
||||
"model.tooltip.reasoning": "যুক্তি",
|
||||
"model.tooltip.reasoning.allowed": "যুক্তির অনুমতি দেয়",
|
||||
"model.tooltip.reasoning.none": "কোন যুক্তি নেই",
|
||||
"model.tooltip.context": "প্রসঙ্গ সীমা {{limit}}",
|
||||
"common.search.placeholder": "অনুসন্ধান করুন",
|
||||
"common.goBack": "ফিরে নেভিগেট করুন",
|
||||
"common.goForward": "সামনে নেভিগেট করুন",
|
||||
"common.loading": "লোড হচ্ছে",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "বাতিল করুন",
|
||||
"common.clear": "পরিষ্কার",
|
||||
"common.open": "খোলা",
|
||||
"common.connect": "সংযোগ করুন",
|
||||
"common.disconnect": "সংযোগ বিচ্ছিন্ন করুন",
|
||||
"common.continue": "চালিয়ে যান",
|
||||
"common.submit": "জমা দিন",
|
||||
"common.save": "সংরক্ষণ করুন",
|
||||
"common.saving": "সংরক্ষণ করা হচ্ছে...",
|
||||
"common.default": "ডিফল্ট",
|
||||
"common.attachment": "সংযুক্তি",
|
||||
"prompt.placeholder.shell": "শেল কমান্ড লিখুন... {{example}}",
|
||||
"prompt.placeholder.normal": 'কিছু জিজ্ঞাসা করুন... "{{example}}"',
|
||||
"prompt.placeholder.simple": "কিছু জিজ্ঞাসা করুন...",
|
||||
"prompt.placeholder.summarizeComments": "মন্তব্য সংক্ষিপ্ত করুন...",
|
||||
"prompt.placeholder.summarizeComment": "মন্তব্য সংক্ষিপ্ত করুন...",
|
||||
"prompt.mode.shell": "শেল",
|
||||
"prompt.mode.normal": "প্রম্পট",
|
||||
"prompt.mode.shell.exit": "প্রস্থান করতে esc",
|
||||
"session.child.promptDisabled": "সাবজেন্ট সেশন প্রম্পট করা যাবে না.",
|
||||
"session.child.backToParent": "মূল সেশনে ফিরে যান।",
|
||||
"prompt.example.1": "কোডবেসে একটি TODO ঠিক করুন",
|
||||
"prompt.example.2": "এই প্রকল্পের প্রযুক্তি স্ট্যাক কি?",
|
||||
"prompt.example.3": "ভাঙা পরীক্ষা ঠিক করুন",
|
||||
"prompt.example.4": "প্রমাণীকরণ কিভাবে কাজ করে তা ব্যাখ্যা করুন",
|
||||
"prompt.example.5": "নিরাপত্তা দুর্বলতা খুঁজুন এবং ঠিক করুন",
|
||||
"prompt.example.6": "ব্যবহারকারী পরিষেবার জন্য ইউনিট পরীক্ষা যোগ করুন",
|
||||
"prompt.example.7": "রিফ্যাক্টর এই ফাংশন আরো পঠনযোগ্য হতে",
|
||||
"prompt.example.8": "এই ত্রুটি মানে কি?",
|
||||
"prompt.example.9": "এই সমস্যাটি ডিবাগ করতে আমাকে সাহায্য করুন",
|
||||
"prompt.example.10": "API ডকুমেন্টেশন তৈরি করুন",
|
||||
"prompt.example.11": "ডাটাবেস কোয়েরি অপ্টিমাইজ করুন",
|
||||
"prompt.example.12": "ইনপুট বৈধতা যোগ করুন",
|
||||
"prompt.example.13": "এর জন্য একটি নতুন উপাদান তৈরি করুন...",
|
||||
"prompt.example.14": "আমি কিভাবে এই প্রকল্প স্থাপন করব?",
|
||||
"prompt.example.15": "সেরা অনুশীলনের জন্য আমার কোড পর্যালোচনা করুন",
|
||||
"prompt.example.16": "এই ফাংশনে ত্রুটি হ্যান্ডলিং যোগ করুন",
|
||||
"prompt.example.17": "এই রেজেক্স প্যাটার্ন ব্যাখ্যা করুন",
|
||||
"prompt.example.18": "এটিকে TypeScript-এ রূপান্তর করুন",
|
||||
"prompt.example.19": "কোডবেস জুড়ে লগিং যোগ করুন",
|
||||
"prompt.example.20": "কি নির্ভরতা পুরানো হয়?",
|
||||
"prompt.example.21": "আমাকে একটি মাইগ্রেশন স্ক্রিপ্ট লিখতে সাহায্য করুন",
|
||||
"prompt.example.22": "এই শেষ পয়েন্টের জন্য ক্যাশিং প্রয়োগ করুন",
|
||||
"prompt.example.23": "এই তালিকায় পৃষ্ঠা সংখ্যা যোগ করুন",
|
||||
"prompt.example.24": "এর জন্য একটি CLI কমান্ড তৈরি করুন...",
|
||||
"prompt.example.25": "পরিবেশ ভেরিয়েবল এখানে কিভাবে কাজ করে?",
|
||||
"prompt.popover.emptyResults": "কোন মিল ফলাফল",
|
||||
"prompt.popover.emptyCommands": "কোনো মিলিত কমান্ড নেই",
|
||||
"prompt.dropzone.label": "ছবি, পিডিএফ, বা টেক্সট ফাইল এখানে ড্রপ করুন",
|
||||
"prompt.dropzone.file.label": "@উল্লেখিত ফাইলে ড্রপ করুন",
|
||||
"prompt.slash.badge.custom": "কাস্টম",
|
||||
"prompt.slash.badge.skill": "দক্ষতা",
|
||||
"prompt.slash.badge.mcp": "এমসিপি",
|
||||
"prompt.context.active": "সক্রিয়",
|
||||
"prompt.context.includeActiveFile": "সক্রিয় ফাইল অন্তর্ভুক্ত করুন",
|
||||
"prompt.context.removeActiveFile": "প্রসঙ্গ থেকে সক্রিয় ফাইল সরান",
|
||||
"prompt.context.removeFile": "প্রসঙ্গ থেকে ফাইল সরান",
|
||||
"prompt.action.attachFile": "ফাইল যোগ করুন",
|
||||
"prompt.menu.addImagesAndFiles": "ফাইল এবং আরো যোগ করুন",
|
||||
"prompt.menu.imagesAndFiles": "ছবি এবং ফাইল",
|
||||
"prompt.menu.commands": "কমান্ড",
|
||||
"prompt.menu.context": "প্রসঙ্গ",
|
||||
"prompt.menu.shellCommand": "শেল কমান্ড",
|
||||
"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": "প্রম্পট পাঠানোর আগে একটি এজেন্ট এবং মডেল বেছে নিন।",
|
||||
"prompt.toast.worktreeCreateFailed.title": "ওয়ার্কট্রি তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"prompt.toast.sessionCreateFailed.title": "সেশন তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"prompt.toast.shellSendFailed.title": "শেল কমান্ড পাঠাতে ব্যর্থ হয়েছে",
|
||||
"prompt.toast.commandSendFailed.title": "কমান্ড পাঠাতে ব্যর্থ হয়েছে",
|
||||
"prompt.toast.promptSendFailed.title": "প্রম্পট পাঠাতে ব্যর্থ হয়েছে৷",
|
||||
"prompt.toast.promptSendFailed.description": "সেশন পুনরুদ্ধার করতে অক্ষম",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{total}} এর {{enabled}} সক্ষম",
|
||||
"dialog.mcp.empty": "কোনো MCPs কনফিগার করা নেই",
|
||||
"dialog.lsp.empty": "LSPs ফাইলের ধরন থেকে স্বয়ংক্রিয়ভাবে সনাক্ত করা হয়েছে",
|
||||
"dialog.plugins.empty": "opencode.json এ কনফিগার করা প্লাগইন",
|
||||
"mcp.status.connected": "সংযুক্ত",
|
||||
"mcp.status.failed": "ব্যর্থ",
|
||||
"mcp.status.needs_auth": "প্রমাণ প্রয়োজন",
|
||||
"mcp.status.disabled": "অক্ষম",
|
||||
"mcp.auth.clickToAuthenticate": "প্রমাণীকরণ করতে ক্লিক করুন",
|
||||
"dialog.fork.empty": "থেকে কাঁটাচামচ কোন বার্তা",
|
||||
"dialog.directory.search.placeholder": "ফোল্ডার অনুসন্ধান করুন",
|
||||
"dialog.directory.empty": "কোনো ফোল্ডার পাওয়া যায়নি",
|
||||
"dialog.directory.action.selectFile": "ফাইল নির্বাচন করুন",
|
||||
"dialog.directory.action.selectFolder": "ফোল্ডার নির্বাচন করুন",
|
||||
"dialog.directory.root": "রুট",
|
||||
"dialog.directory.parent": "অভিভাবক",
|
||||
"dialog.directory.readError": "এই ফোল্ডারটি পড়তে অক্ষম৷",
|
||||
"app.server.unreachable": "{{server}} এ পৌঁছানো যায়নি",
|
||||
"app.server.retrying": "স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করা হচ্ছে...",
|
||||
"app.server.otherServers": "অন্যান্য সার্ভার",
|
||||
"dialog.server.title": "সার্ভার",
|
||||
"dialog.server.description": "এই অ্যাপটি কোন OpenCode সার্ভারের সাথে সংযোগ করে তা পরিবর্তন করুন।",
|
||||
"dialog.server.search.placeholder": "সার্ভার সার্চ করুন",
|
||||
"dialog.server.empty": "এখনো কোনো সার্ভার নেই",
|
||||
"dialog.server.add.title": "সার্ভার যোগ করুন",
|
||||
"dialog.server.add.url": "সার্ভার ঠিকানা",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "সার্ভারের সাথে সংযোগ করা যায়নি",
|
||||
"dialog.server.add.checking": "পরীক্ষা করা হচ্ছে...",
|
||||
"dialog.server.add.button": "সার্ভার যোগ করুন",
|
||||
"dialog.server.add.name": "সার্ভারের নাম (ঐচ্ছিক)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "ব্যবহারকারীর নাম (ঐচ্ছিক)",
|
||||
"dialog.server.add.usernamePlaceholder": "ব্যবহারকারীর নাম",
|
||||
"dialog.server.add.password": "পাসওয়ার্ড (ঐচ্ছিক)",
|
||||
"dialog.server.add.passwordPlaceholder": "পাসওয়ার্ড",
|
||||
"dialog.server.edit.title": "সার্ভার সম্পাদনা করুন",
|
||||
"dialog.server.default.title": "ডিফল্ট সার্ভার",
|
||||
"dialog.server.default.description":
|
||||
"একটি স্থানীয় সার্ভার শুরু করার পরিবর্তে অ্যাপ লঞ্চে এই সার্ভারের সাথে সংযোগ করুন৷ পুনরায় চালু করা প্রয়োজন।",
|
||||
"dialog.server.default.none": "কোনো সার্ভার নির্বাচন করা হয়নি",
|
||||
"dialog.server.default.set": "বর্তমান সার্ভারকে ডিফল্ট হিসাবে সেট করুন",
|
||||
"dialog.server.default.clear": "পরিষ্কার",
|
||||
"dialog.server.action.remove": "সার্ভার সরান",
|
||||
"dialog.server.menu.edit": "সম্পাদনা করুন",
|
||||
"dialog.server.menu.default": "ডিফল্ট হিসেবে সেট করুন",
|
||||
"dialog.server.menu.defaultRemove": "ডিফল্ট সরান",
|
||||
"dialog.server.menu.delete": "মুছে দিন",
|
||||
"dialog.server.current": "বর্তমান সার্ভার",
|
||||
"dialog.server.status.default": "ডিফল্ট",
|
||||
"wsl.server.add": "WSL সার্ভার যোগ করুন",
|
||||
"wsl.server.addShort": "WSL যোগ করুন",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL সার্ভার",
|
||||
"wsl.server.retryStart": "আবার শুরু করার চেষ্টা করুন",
|
||||
"wsl.server.updating": "আপডেট হচ্ছে...",
|
||||
"wsl.onboarding.step.distro": "ডিস্ট্রো নির্বাচন করুন",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL পরীক্ষা করা হচ্ছে...",
|
||||
"wsl.onboarding.restartRequired": "WSL ইনস্টল করা শেষ করার জন্য Windows-এর একটি রিস্টার্ট প্রয়োজন।",
|
||||
"wsl.onboarding.ready": "WSL প্রস্তুত।",
|
||||
"wsl.onboarding.required": "WSL চালিয়ে যেতে হবে।",
|
||||
"wsl.onboarding.checkingDistros": "ডিস্ট্রো পরীক্ষা করা হচ্ছে...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} ইনস্টল করা হচ্ছে...",
|
||||
"wsl.onboarding.checkingDistro": "চেক করা হচ্ছে {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "ডিস্ট্রোদের তালিকা করা হচ্ছে...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} প্রস্তুত।",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} এখনও ইনস্টল করা হয়নি৷",
|
||||
"wsl.onboarding.openDistroOnce": "সেটআপ শেষ করতে একবার {{distro}} খুলুন।",
|
||||
"wsl.onboarding.finishingDistro": "{{distro}} এর জন্য সেটআপ শেষ করা হচ্ছে।",
|
||||
"wsl.onboarding.pickDistro": "একটি ডিস্ট্রো চয়ন করুন বা নীচে একটি ইনস্টল করুন৷",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode পরীক্ষা করা হচ্ছে...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "{{distro}} এ OpenCode চেক করা হচ্ছে...",
|
||||
"wsl.onboarding.updatingOpencode": "আপডেট করা হচ্ছে OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "{{distro}}-এ OpenCode আপডেট করা হচ্ছে...",
|
||||
"wsl.onboarding.updateOpencodeIn": "{{distro}}-এ OpenCode আপডেট করুন।",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode আপডেট করুন",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode {{distro}}-এ প্রস্তুত।",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode প্রস্তুত।",
|
||||
"wsl.onboarding.installOpencodeIn": "{{distro}}-এ OpenCode ইনস্টল করুন।",
|
||||
"wsl.onboarding.installOpencode": "OpenCode ইনস্টল করুন",
|
||||
"wsl.onboarding.chooseDistroFirst": "প্রথমে একটি ডিস্ট্রো চয়ন করুন।",
|
||||
"wsl.onboarding.loadFailed": "WSL অবস্থা লোড করতে ব্যর্থ।",
|
||||
"wsl.onboarding.loading": "লোড হচ্ছে...",
|
||||
"wsl.onboarding.installedDistros": "ডিস্ট্রো ইনস্টল করা হয়েছে",
|
||||
"wsl.onboarding.checkAgain": "আবার চেক করুন",
|
||||
"wsl.onboarding.distroStatus.ready": "প্রস্তুত",
|
||||
"wsl.onboarding.distroStatus.checking": "পরীক্ষা করা হচ্ছে...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode ইনস্টল করা নেই",
|
||||
"wsl.onboarding.distroStatus.missingTools": "অনুপস্থিত বাশ, কার্ল",
|
||||
"wsl.onboarding.distroStatus.unsupported": "অসমর্থিত · WSL 2 ব্যবহার করুন",
|
||||
"wsl.onboarding.needAnotherDistro": "অন্য ডিস্ট্রো প্রয়োজন?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "WSL ক্যাটালগ থেকে একটি Linux বিতরণ ইনস্টল করুন",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL ইনস্টল করা হয়নি",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Linux এর জন্য Windows সাবসিস্টেম) OpenCode একটি WSL সার্ভার যোগ করার আগে প্রয়োজন",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL অনুপলব্ধ",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode এই মেশিনে WSL যাচাই করতে পারেনি।",
|
||||
"wsl.onboarding.installWsl": "WSL ইনস্টল করুন",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"WSL ইনস্টল করা শেষ করতে Windows পুনরায় চালু করুন, তারপর OpenCode আবার খুলুন।",
|
||||
"wsl.onboarding.next": "পরবর্তী",
|
||||
"wsl.onboarding.refresh": "রিফ্রেশ",
|
||||
"wsl.onboarding.allDistrosAdded": "সমস্ত ইনস্টল করা ডিস্ট্রো ইতিমধ্যেই যোগ করা হয়েছে।",
|
||||
"wsl.onboarding.noDistros": "কোন ডিস্ট্রো এখনও সনাক্ত করা হয়নি.",
|
||||
"wsl.onboarding.install": "ইনস্টল করুন",
|
||||
"wsl.onboarding.installing": "ইনস্টল করা হচ্ছে...",
|
||||
"wsl.onboarding.installDistro": "ডিস্ট্রো ইনস্টল করুন",
|
||||
"wsl.onboarding.searchDistros": "ডিস্ট্রো অনুসন্ধান করুন",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 প্রয়োজন।",
|
||||
"wsl.onboarding.toolsRequired": "এই ডিস্ট্রো ব্যাশ এবং কার্ল প্রয়োজন।",
|
||||
"wsl.onboarding.openTerminal": "টার্মিনাল খুলুন",
|
||||
"wsl.onboarding.path": "পথ: {{path}}",
|
||||
"wsl.onboarding.notFound": "পাওয়া যায় নি",
|
||||
"wsl.onboarding.version": "সংস্করণ: {{version}}",
|
||||
"wsl.onboarding.unknown": "অজানা",
|
||||
"wsl.onboarding.desktopVersion": "ডেস্কটপ {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "ইনস্টল করা সংস্করণ ডেস্কটপ অ্যাপ সংস্করণের সাথে মেলে না।",
|
||||
"wsl.onboarding.adding": "যোগ করা হচ্ছে...",
|
||||
"help.tabs.toast.ariaLabel": "ট্যাব চালু করা হচ্ছে। ট্যাব দিয়ে আপনার কাজ এবং সক্রিয় সেশনগুলি সংগঠিত করুন",
|
||||
"help.tabs.toast.dismiss": "ট্যাব তথ্য খারিজ",
|
||||
"help.tabs.title": "ট্যাব চালু করা হচ্ছে",
|
||||
"help.tabs.description": "ট্যাব দিয়ে আপনার কাজ এবং সক্রিয় সেশনগুলি সংগঠিত করুন",
|
||||
"help.tabs.date": "14 জুলাই",
|
||||
"help.tabs.introduction": "OpenCode ডেস্কটপ এখন ট্যাবের চারপাশে তৈরি।",
|
||||
"help.tabs.sessions":
|
||||
"একটি ট্যাবে একটি নতুন সেশন শুরু করুন, বা আপনার যেকোনো প্রকল্প থেকে একটি বিদ্যমান সেশন খুলুন৷ আপনি যখন নতুন কিছু শুরু করছেন তখন একটি নতুন ট্যাব খুলুন এবং আপনার কাজ শেষ হলে এটি বন্ধ করুন।",
|
||||
"help.tabs.organize":
|
||||
"কয়েকটি ট্যাব খোলা রাখলে আপনার সক্রিয় সেশনগুলি সংগঠিত করা সহজ হয়৷ ট্যাবগুলিকে স্মরণীয় কিছুতে পুনঃনামকরণ করুন যদি আপনি সেগুলিকে কাছাকাছি রাখার পরিকল্পনা করেন৷",
|
||||
"help.tabs.home":
|
||||
"আপনি নতুন হোম স্ক্রিনে আপনার সমস্ত সেশন এবং প্রকল্পগুলি খুঁজে পাবেন৷ একটি সেশন নির্বাচন করা এটি একটি ট্যাবে খোলে।",
|
||||
"help.tabs.persistence": "আপনি যখন অ্যাপটি আবার খুলবেন, তখনও আপনার ট্যাবগুলি খোলা থাকবে৷",
|
||||
"help.tabs.worktrees":
|
||||
"নতুন ডিজাইন এখনও Git Worktrees সমর্থন করে না, এটি শীঘ্রই আসছে। সুতরাং আপনি যদি পূর্ববর্তী লেআউটটি ব্যবহার করা চালিয়ে যেতে চান তবে আপনি সেটিংসে লেআউটগুলির মধ্যে স্যুইচ করতে পারেন৷ শুধু মনে রাখবেন যে নতুন লেআউট কয়েক সপ্তাহের মধ্যে স্থায়ী হয়ে যাবে।",
|
||||
"server.row.noUsername": "ব্যবহারকারীর নাম নেই",
|
||||
"dialog.project.edit.title": "প্রকল্প সম্পাদনা করুন",
|
||||
"dialog.project.edit.name": "নাম",
|
||||
"dialog.project.edit.icon": "আইকন",
|
||||
"dialog.project.edit.icon.alt": "প্রকল্প আইকন",
|
||||
"dialog.project.edit.icon.hint": "একটি ছবি ক্লিক করুন বা টেনে আনুন",
|
||||
"dialog.project.edit.icon.recommended": "প্রস্তাবিত: 128x128px",
|
||||
"dialog.project.edit.color": "রঙ",
|
||||
"dialog.project.edit.color.select": "{{color}} রঙ নির্বাচন করুন",
|
||||
"dialog.project.edit.worktree.startup": "ওয়ার্কস্পেস স্টার্টআপ স্ক্রিপ্ট",
|
||||
"dialog.project.edit.worktree.startup.description": "একটি নতুন ওয়ার্কস্পেস (ওয়ার্কট্রি) তৈরি করার পরে চলে।",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "যেমন বান ইনস্টল",
|
||||
"dialog.releaseNotes.action.getStarted": "শুরু করুন",
|
||||
"dialog.releaseNotes.action.next": "পরবর্তী",
|
||||
"dialog.releaseNotes.action.hideFuture": "ভবিষ্যতে এগুলো দেখাবেন না",
|
||||
"dialog.releaseNotes.media.alt": "রিলিজ পূর্বরূপ",
|
||||
"context.breakdown.title": "প্রসঙ্গ ব্রেকডাউন",
|
||||
"context.breakdown.note": 'ইনপুট টোকেনগুলির আনুমানিক ভাঙ্গন। "অন্যান্য" টুল সংজ্ঞা এবং ওভারহেড অন্তর্ভুক্ত।',
|
||||
"context.breakdown.system": "সিস্টেম",
|
||||
"context.breakdown.user": "ব্যবহারকারী",
|
||||
"context.breakdown.assistant": "সহকারী",
|
||||
"context.breakdown.tool": "টুল কল",
|
||||
"context.breakdown.other": "অন্যান্য",
|
||||
"context.systemPrompt.title": "সিস্টেম প্রম্পট",
|
||||
"context.rawMessages.title": "কাঁচা বার্তা",
|
||||
"context.export.session": "রপ্তানি সেশন",
|
||||
"context.stats.session": "সেশন",
|
||||
"context.stats.messages": "বার্তা",
|
||||
"context.stats.provider": "প্রদানকারী",
|
||||
"context.stats.model": "মডেল",
|
||||
"context.stats.limit": "প্রসঙ্গ সীমা",
|
||||
"context.stats.totalTokens": "মোট টোকেন",
|
||||
"context.stats.usage": "ব্যবহার",
|
||||
"context.stats.inputTokens": "ইনপুট টোকেন",
|
||||
"context.stats.outputTokens": "আউটপুট টোকেন",
|
||||
"context.stats.reasoningTokens": "রিজনিং টোকেন",
|
||||
"context.stats.cacheTokens": "ক্যাশে টোকেন (পড়ুন/লিখুন)",
|
||||
"context.stats.userMessages": "ব্যবহারকারীর বার্তা",
|
||||
"context.stats.assistantMessages": "সহকারী বার্তা",
|
||||
"context.stats.totalCost": "মোট খরচ",
|
||||
"context.stats.sessionCreated": "সেশন তৈরি হয়েছে",
|
||||
"context.stats.lastActivity": "শেষ কার্যকলাপ",
|
||||
"context.usage.tokens": "টোকেন",
|
||||
"context.usage.usage": "ব্যবহার",
|
||||
"context.usage.cost": "খরচ",
|
||||
"context.usage.clickToView": "প্রসঙ্গ দেখতে ক্লিক করুন",
|
||||
"context.usage.view": "প্রসঙ্গ ব্যবহার দেখুন",
|
||||
"language.en": "English",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "Deutsch",
|
||||
"language.es": "Español",
|
||||
"language.fr": "Français",
|
||||
"language.da": "Dansk",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Polski",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "ভাষা",
|
||||
"toast.language.description": "{{language}} এ স্যুইচ করা হয়েছে",
|
||||
"toast.theme.title": "থিম পরিবর্তন করা হয়েছে",
|
||||
"toast.scheme.title": "রঙের স্কিম",
|
||||
"toast.workspace.enabled.title": "ওয়ার্কস্পেস সক্রিয় করা হয়েছে",
|
||||
"toast.workspace.enabled.description": "একাধিক ওয়ার্কট্রি এখন সাইডবারে দেখানো হয়েছে",
|
||||
"toast.workspace.disabled.title": "ওয়ার্কস্পেস অক্ষম করা হয়েছে",
|
||||
"toast.workspace.disabled.description": "সাইডবারে শুধুমাত্র প্রধান ওয়ার্কট্রি দেখানো হয়েছে",
|
||||
"toast.permissions.autoaccept.on.title": "স্বয়ংক্রিয়ভাবে গ্রহণযোগ্য অনুমতি",
|
||||
"toast.permissions.autoaccept.on.description": "অনুমতি অনুরোধ স্বয়ংক্রিয়ভাবে অনুমোদিত হবে",
|
||||
"toast.permissions.autoaccept.off.title": "স্বয়ংক্রিয়ভাবে গ্রহণযোগ্য অনুমতি বন্ধ করা হয়েছে৷",
|
||||
"toast.permissions.autoaccept.off.description": "অনুমতি অনুরোধ অনুমোদন প্রয়োজন হবে",
|
||||
"toast.model.none.title": "কোনো মডেল নির্বাচন করা হয়নি",
|
||||
"toast.model.none.description": "এই সেশনের সংক্ষিপ্ত বিবরণ দিতে একজন প্রদানকারীকে সংযুক্ত করুন",
|
||||
"toast.file.loadFailed.title": "ফাইল লোড করতে ব্যর্থ হয়েছে",
|
||||
"toast.file.listFailed.title": "ফাইল তালিকা করতে ব্যর্থ হয়েছে",
|
||||
"toast.context.noLineSelection.title": "কোন লাইন নির্বাচন নেই",
|
||||
"toast.context.noLineSelection.description": "প্রথমে একটি ফাইল ট্যাবে একটি লাইন পরিসর নির্বাচন করুন।",
|
||||
"toast.session.share.copyFailed.title": "ক্লিপবোর্ডে URL অনুলিপি করতে ব্যর্থ হয়েছে৷",
|
||||
"toast.session.share.success.title": "সেশন শেয়ার করা হয়েছে",
|
||||
"toast.session.share.success.description": "শেয়ার করুন URL ক্লিপবোর্ডে কপি করা হয়েছে!",
|
||||
"toast.session.share.failed.title": "সেশন শেয়ার করতে ব্যর্থ হয়েছে",
|
||||
"toast.session.share.failed.description": "সেশন শেয়ার করার সময় একটি ত্রুটি ঘটেছে৷",
|
||||
"toast.session.unshare.success.title": "সেশন শেয়ার করা বাদ দেওয়া হয়েছে",
|
||||
"toast.session.unshare.success.description": "সেশন সফলভাবে মুক্ত করা হয়েছে!",
|
||||
"toast.session.unshare.failed.title": "সেশন শেয়ার মুক্ত করতে ব্যর্থ হয়েছে",
|
||||
"toast.session.unshare.failed.description": "সেশনটি শেয়ার না করার সময় একটি ত্রুটি ঘটেছে৷",
|
||||
"toast.session.export.success.title": "সেশন এক্সপোর্ট করা হয়েছে",
|
||||
"toast.session.export.success.description": "{{filename}}-এ সেশন সেভ করা হয়েছে",
|
||||
"toast.session.export.failed.title": "সেশন এক্সপোর্ট করতে ব্যর্থ হয়েছে৷",
|
||||
"toast.session.export.failed.description": "সেশন এক্সপোর্ট করার সময় একটি ত্রুটি ঘটেছে৷",
|
||||
"toast.session.listFailed.title": "{{project}} এর জন্য সেশন লোড করতে ব্যর্থ হয়েছে৷",
|
||||
"toast.project.reloadFailed.title": "{{project}} পুনরায় লোড করতে ব্যর্থ হয়েছে৷",
|
||||
"toast.update.title": "আপডেট উপলব্ধ",
|
||||
"toast.update.description": "OpenCode ({{version}}) এর একটি নতুন সংস্করণ এখন ইনস্টল করার জন্য উপলব্ধ৷",
|
||||
"toast.update.action.installRestart": "ইনস্টল করুন এবং পুনরায় চালু করুন",
|
||||
"toast.update.action.notYet": "এখনো না",
|
||||
"error.page.title": "কিছু ভুল হয়েছে",
|
||||
"error.page.description": "অ্যাপ্লিকেশন লোড করার সময় একটি ত্রুটি ঘটেছে৷",
|
||||
"error.page.description.localServerStartup": "স্থানীয় সার্ভার শুরু করার সময় একটি ত্রুটি ঘটেছে৷",
|
||||
"error.page.details.label": "ত্রুটি বিবরণ",
|
||||
"error.page.action.restart": "রিস্টার্ট করুন",
|
||||
"error.page.action.report": "রিপোর্ট ত্রুটি",
|
||||
"error.page.action.reported": "ত্রুটি রিপোর্ট করা হয়েছে",
|
||||
"error.page.action.exportLogs": "রপ্তানি লগ",
|
||||
"error.page.action.checking": "পরীক্ষা করা হচ্ছে...",
|
||||
"error.page.action.checkUpdates": "আপডেটের জন্য চেক করুন",
|
||||
"error.page.action.updateTo": "{{version}}-এ আপডেট করুন",
|
||||
"error.page.circular": "[সার্কুলার]",
|
||||
"error.page.report.prefix": "অনুগ্রহ করে এই ত্রুটিটি OpenCode টিমের কাছে রিপোর্ট করুন",
|
||||
"error.page.report.discord": "Discord-এ",
|
||||
"error.page.version": "সংস্করণ: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"মূল উপাদান পাওয়া যায়নি. আপনি কি আপনার index.html এ যোগ করতে ভুলে গেছেন? অথবা হয়তো আইডি অ্যাট্রিবিউট ভুল বানান হয়েছে?",
|
||||
"error.serverSync.connectFailed": "সার্ভারের সাথে সংযোগ করা যায়নি। `{{url}}` এ কি কোনো সার্ভার চলছে?",
|
||||
"error.serverSDK.noServerAvailable": "কোন সার্ভার উপলব্ধ",
|
||||
"error.serverSDK.serverNotAvailable": "সার্ভার উপলব্ধ নয়",
|
||||
"error.childStore.persistedCacheCreateFailed": "স্থায়ী ক্যাশে তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "স্থায়ী প্রকল্প মেটাডেটা তৈরি করতে ব্যর্থ হয়েছে",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "স্থায়ী প্রকল্প আইকন তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"error.childStore.storeCreateFailed": "স্টোর তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"directory.error.invalidUrl": "URL-এ অবৈধ ডিরেক্টরি।",
|
||||
"error.chain.unknown": "অজানা ত্রুটি",
|
||||
"error.server.invalidConfiguration": "অবৈধ কনফিগারেশন",
|
||||
"error.chain.causedBy": "দ্বারা সৃষ্ট:",
|
||||
"error.chain.apiError": "API ত্রুটি",
|
||||
"error.chain.status": "স্থিতি: {{status}}",
|
||||
"error.chain.retryable": "পুনরায় চেষ্টা করা যায়: {{retryable}}",
|
||||
"error.chain.responseBody": "প্রতিক্রিয়ার অংশ:\n{{body}}৷",
|
||||
"error.chain.didYouMean": "আপনি কি বলতে চাইছেন: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "মডেল পাওয়া যায়নি: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "আপনার কনফিগারেশন (opencode.json) প্রদানকারী/মডেলের নাম পরীক্ষা করুন",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP সার্ভার "{{name}}" ব্যর্থ হয়েছে৷ মনে রাখবেন, OpenCode এখনও MCP প্রমাণীকরণ সমর্থন করে না।',
|
||||
"error.chain.providerAuthFailed": "প্রদানকারীর প্রমাণীকরণ ব্যর্থ হয়েছে ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'"{{provider}}" প্রদানকারী শুরু করতে ব্যর্থ হয়েছে৷ শংসাপত্র এবং কনফিগারেশন চেক করুন.',
|
||||
"error.chain.configJsonInvalid": "{{path}}-এ কনফিগার করা ফাইলটি বৈধ JSON(C) নয়",
|
||||
"error.chain.configJsonInvalidWithMessage": "{{path}}-এ কনফিগার ফাইল বৈধ নয় JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'{{path}}-এ "{{dir}}" ডিরেক্টরিটি বৈধ নয়৷ ডিরেক্টরিটির নাম পরিবর্তন করুন "{{suggestion}}" বা এটি সরান। এটি একটি সাধারণ টাইপো।',
|
||||
"error.chain.configFrontmatterError": "{{path}} এ ফ্রন্টম্যাটার পার্স করতে ব্যর্থ হয়েছে:\n{{message}}",
|
||||
"error.chain.configInvalid": "{{path}}-এ কনফিগ ফাইলটি অবৈধ",
|
||||
"error.chain.configInvalidWithMessage": "{{path}}-এ কনফিগ ফাইলটি অবৈধ: {{message}}",
|
||||
"notification.permission.title": "অনুমতি প্রয়োজন",
|
||||
"notification.permission.description": "{{projectName}}-এ {{sessionTitle}} অনুমতি প্রয়োজন",
|
||||
"notification.question.title": "প্রশ্ন",
|
||||
"notification.question.description": "{{projectName}}-এ {{sessionTitle}} একটি প্রশ্ন আছে",
|
||||
"notification.action.goToSession": "সেশনে যান",
|
||||
"notification.session.responseReady.title": "প্রতিক্রিয়া প্রস্তুত",
|
||||
"notification.session.error.title": "সেশন ত্রুটি",
|
||||
"notification.session.error.fallbackDescription": "একটি ত্রুটি ঘটেছে",
|
||||
"home.recentProjects": "সাম্প্রতিক প্রকল্প",
|
||||
"home.empty.title": "কোন সাম্প্রতিক প্রকল্প",
|
||||
"home.empty.description": "একটি স্থানীয় প্রকল্প খোলার মাধ্যমে শুরু করুন",
|
||||
"home.title": "বাড়ি",
|
||||
"home.projects": "প্রকল্প",
|
||||
"home.project.add": "প্রকল্প যোগ করুন",
|
||||
"home.recentlyClosed": "সম্প্রতি বন্ধ",
|
||||
"home.server.collapse": "সার্ভার প্রকল্প সঙ্কুচিত করুন",
|
||||
"home.server.expand": "সার্ভার প্রকল্প প্রসারিত",
|
||||
"home.sessions.search.placeholder": "অনুসন্ধান সেশন",
|
||||
"home.sessions.search.placeholder.scoped": "{{scope}}-এ অনুসন্ধান সেশন",
|
||||
"home.sessions.search.sessions": "সেশন",
|
||||
"home.sessions.search.noResults": "{{query}}-এর জন্য কোনো সেশন পাওয়া যায়নি",
|
||||
"home.sessions.empty": "এখানে এখনও কিছুই নেই",
|
||||
"home.sessions.empty.description": "শুরু করার জন্য একটি সেশন তৈরি করুন",
|
||||
"home.sessions.group.today": "আজ",
|
||||
"home.sessions.group.yesterday": "গতকাল",
|
||||
"home.sessions.group.older": "বয়স্ক",
|
||||
"home.providerTip": "Claude, GPT, Gemini, ইত্যাদি সহ অন্যান্য মডেল ব্যবহার করতে 75+ প্রদানকারীর সাথে সংযোগ করুন",
|
||||
"session.tab.session": "সেশন",
|
||||
"session.tab.review": "পর্যালোচনা",
|
||||
"session.tab.context": "প্রসঙ্গ",
|
||||
"session.tab.unknown": "অজানা সেশন",
|
||||
"session.panel.reviewAndFiles": "পর্যালোচনা এবং ফাইল",
|
||||
"session.error.notFound": "এই সেশন খুঁজে পাওয়া যাবে না",
|
||||
"session.error.notFound.description": "এই ট্যাবটি এমন একটি সেশনের দিকে নির্দেশ করে যা এই সার্ভারে আর বিদ্যমান নেই।",
|
||||
"session.error.notFound.closeTab": "ট্যাব বন্ধ করুন",
|
||||
"session.error.serverConnection": "এই সার্ভারের সাথে সংযোগ করা যাচ্ছে না৷",
|
||||
"session.review.filesChanged": "ফাইলগুলি পরিবর্তন করা হয়েছে {{count}}৷",
|
||||
"session.review.change.one": "পরিবর্তন",
|
||||
"session.review.change.other": "পরিবর্তন",
|
||||
"session.review.loadingChanges": "পরিবর্তনগুলি লোড হচ্ছে...",
|
||||
"session.review.empty": "এই সেশনে এখনও কোন পরিবর্তন নেই",
|
||||
"session.review.noVcs": "কোন Git সংস্করণ নিয়ন্ত্রণ সিস্টেম সনাক্ত করা যায়নি, পরিবর্তনগুলি প্রদর্শিত হয় না",
|
||||
"session.review.noVcs.createGit.title": "একটি Git সংগ্রহস্থল তৈরি করুন",
|
||||
"session.review.noVcs.createGit.description":
|
||||
"এই প্রকল্পের পরিবর্তনগুলি ট্র্যাক করুন, পর্যালোচনা করুন এবং পূর্বাবস্থায় ফেরান৷",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git সংগ্রহস্থল তৈরি করা হচ্ছে...",
|
||||
"session.review.noVcs.createGit.action": "Git সংগ্রহস্থল তৈরি করুন",
|
||||
"session.review.noSnapshot": "কনফিগারেশনে স্ন্যাপশট ট্র্যাকিং অক্ষম করা হয়েছে, তাই সেশন পরিবর্তনগুলি অনুপলব্ধ৷",
|
||||
"session.review.noChanges": "কোনো পরিবর্তন নেই",
|
||||
"session.review.noUncommittedChanges": "এখনও কোন অপ্রতিরোধ্য পরিবর্তন",
|
||||
"session.review.noBranchChanges": "এখনো কোনো শাখা পরিবর্তন হয়নি",
|
||||
"session.files.selectToOpen": "খুলতে একটি ফাইল নির্বাচন করুন",
|
||||
"session.files.all": "সমস্ত ফাইল",
|
||||
"session.files.empty": "কোনো ফাইল নেই",
|
||||
"session.files.binaryContent": "বাইনারি ফাইল (সামগ্রী প্রদর্শন করা যাবে না)",
|
||||
"session.messages.renderEarlier": "আগের বার্তা রেন্ডার করুন",
|
||||
"session.messages.loadingEarlier": "আগের বার্তা লোড হচ্ছে...",
|
||||
"session.messages.loadEarlier": "আগের বার্তা লোড করুন",
|
||||
"session.messages.loading": "বার্তা লোড হচ্ছে...",
|
||||
"session.messages.jumpToLatest": "সর্বশেষে ঝাঁপ দাও",
|
||||
"session.context.addToContext": "প্রসঙ্গে {{selection}} যোগ করুন",
|
||||
"session.todo.title": "টোডোস",
|
||||
"session.todo.collapse": "সঙ্কুচিত",
|
||||
"session.todo.expand": "প্রসারিত করুন",
|
||||
"session.todo.progress": "{{done}} এর মধ্যে {{total}} todos সম্পন্ন হয়েছে",
|
||||
"session.question.progress": "{{total}} প্রশ্নের {{current}}",
|
||||
"session.question.minimize": "প্রশ্ন ছোট করুন",
|
||||
"session.question.restore": "প্রশ্ন পুনরুদ্ধার করুন",
|
||||
"session.question.pending.one": "{{count}} মুলতুবি প্রশ্ন",
|
||||
"session.question.pending.other": "{{count}} মুলতুবি প্রশ্ন",
|
||||
"session.followupDock.summary.one": "{{count}} সারিবদ্ধ বার্তা",
|
||||
"session.followupDock.summary.other": "{{count}} সারিবদ্ধ বার্তা",
|
||||
"session.followupDock.sendNow": "এখন পাঠান",
|
||||
"session.followupDock.edit": "সম্পাদনা করুন",
|
||||
"session.followupDock.collapse": "সারিবদ্ধ বার্তাগুলি সঙ্কুচিত করুন৷",
|
||||
"session.followupDock.expand": "সারিবদ্ধ বার্তাগুলি প্রসারিত করুন",
|
||||
"session.revertDock.summary.one": "{{count}} রোল ব্যাক বার্তা৷",
|
||||
"session.revertDock.summary.other": "{{count}} রোল ব্যাক বার্তা",
|
||||
"session.revertDock.collapse": "রোল ব্যাক বার্তাগুলিকে আড়াল করুন৷",
|
||||
"session.revertDock.expand": "রোল ব্যাক বার্তা প্রসারিত করুন",
|
||||
"session.revertDock.restore": "বার্তা পুনরুদ্ধার করুন",
|
||||
"session.new.title": "যে কোনো কিছু তৈরি করুন",
|
||||
"session.new.project.new": "নতুন প্রকল্প",
|
||||
"session.new.project.search": "অনুসন্ধান প্রকল্প",
|
||||
"session.new.project.add": "প্রকল্প যোগ করুন",
|
||||
"session.new.worktree.main": "প্রধান শাখা",
|
||||
"session.new.worktree.mainWithBranch": "প্রধান শাখা ({{branch}})",
|
||||
"session.new.worktree.create": "নতুন ওয়ার্কট্রি তৈরি করুন",
|
||||
"session.new.workspace.runIn": "সেশন চালান",
|
||||
"session.new.workspace.triggerLocal": "স্থানীয়",
|
||||
"session.new.workspace.local": "স্থানীয় সংগ্রহস্থল",
|
||||
"session.new.workspace.existing": "ওয়ার্কস্পেস…",
|
||||
"session.new.git.none": "না Git",
|
||||
"session.new.lastModified": "সর্বশেষ সংশোধন করা হয়েছে",
|
||||
"session.header.search.placeholder": "অনুসন্ধান করুন {{project}}",
|
||||
"session.header.searchFiles": "ফাইল অনুসন্ধান করুন",
|
||||
"session.header.openIn": "খুলুন",
|
||||
"session.header.open.action": "{{app}} খুলুন",
|
||||
"session.header.open.ariaLabel": "{{app}}-এ খুলুন",
|
||||
"session.header.open.menu": "অপশন খুলুন",
|
||||
"session.header.open.copyPath": "পাথ কপি করুন",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.open.fileManager": "File Manager",
|
||||
"session.header.reveal.finder": "Finder-এ প্রকাশ করুন",
|
||||
"session.header.reveal.fileExplorer": "File Explorer-এ প্রকাশ করুন",
|
||||
"session.header.reveal.containingFolder": "ফোল্ডার ধারণকারী খুলুন",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "টার্মিনাল",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "স্ট্যাটাস",
|
||||
"status.popover.ariaLabel": "সার্ভার কনফিগারেশন",
|
||||
"status.popover.tab.servers": "সার্ভার",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "প্লাগইন",
|
||||
"status.popover.action.manageServers": "সার্ভার পরিচালনা করুন",
|
||||
"session.share.popover.title": "ওয়েবে প্রকাশ করুন",
|
||||
"session.share.popover.description.shared": "এই সেশনটি ওয়েবে সর্বজনীন। এটি লিঙ্ক সহ যে কেউ অ্যাক্সেসযোগ্য।",
|
||||
"session.share.popover.description.unshared":
|
||||
"ওয়েবে সর্বজনীনভাবে সেশন শেয়ার করুন। এটি লিঙ্ক সহ যে কেউ অ্যাক্সেসযোগ্য হবে।",
|
||||
"session.share.action.share": "শেয়ার করুন",
|
||||
"session.share.action.publish": "প্রকাশ করুন",
|
||||
"session.share.action.publishing": "প্রকাশ করা হচ্ছে...",
|
||||
"session.share.action.unpublish": "অপ্রকাশিত করুন",
|
||||
"session.share.action.unpublishing": "অপ্রকাশিত হচ্ছে...",
|
||||
"session.share.action.view": "দেখুন",
|
||||
"session.share.copy.copied": "কপি করা হয়েছে",
|
||||
"session.share.copy.copyLink": "লিঙ্ক কপি করুন",
|
||||
"lsp.tooltip.none": "কোনো LSP সার্ভার নেই",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "প্রম্পট লোড হচ্ছে...",
|
||||
"terminal.loading": "টার্মিনাল লোড হচ্ছে...",
|
||||
"terminal.title": "টার্মিনাল",
|
||||
"terminal.title.numbered": "টার্মিনাল {{number}}",
|
||||
"terminal.close": "টার্মিনাল বন্ধ করুন",
|
||||
"terminal.connectionLost.title": "সংযোগ হারিয়েছে",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket অস্বাভাবিকভাবে বন্ধ: {{code}}",
|
||||
"terminal.connectionLost.description": "টার্মিনাল সংযোগ বিঘ্নিত হয়েছে। সার্ভার পুনরায় চালু হলে এটি ঘটতে পারে।",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY সংযোগ টিকিট মূল বা CSRF চেক দ্বারা প্রত্যাখ্যাত। সার্ভার CORS কনফিগারেশন চেক করুন।",
|
||||
"terminal.connectTicket.statusError": "{{status}}-এর সাথে PTY সংযোগ টিকিট ব্যর্থ হয়েছে",
|
||||
"titlebar.update": "আপডেট",
|
||||
"titlebar.updateVersion": "{{version}} আপডেট করুন",
|
||||
"common.closeTab": "ট্যাব বন্ধ করুন",
|
||||
"common.dismiss": "খারিজ",
|
||||
"common.moreCountSuffix": "(+{{count}} আরো)",
|
||||
"common.requestFailed": "অনুরোধ ব্যর্থ হয়েছে",
|
||||
"common.moreOptions": "আরও বিকল্প",
|
||||
"common.learnMore": "আরও জানুন",
|
||||
"common.rename": "নাম পরিবর্তন করুন",
|
||||
"common.export": "রপ্তানি",
|
||||
"common.reset": "রিসেট করুন",
|
||||
"common.archive": "সংরক্ষণাগার",
|
||||
"common.delete": "মুছে দিন",
|
||||
"common.close": "বন্ধ",
|
||||
"common.edit": "সম্পাদনা করুন",
|
||||
"common.loadMore": "আরো লোড",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Space",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "End",
|
||||
"common.key.pageUp": "Page Up",
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "অজানা",
|
||||
"common.time.justNow": "এইমাত্র",
|
||||
"common.time.minutesAgo.short": "{{count}}মি আগে",
|
||||
"common.time.hoursAgo.short": "{{count}}ঘণ্টা আগে",
|
||||
"common.time.daysAgo.short": "{{count}}দিন আগে",
|
||||
"sidebar.menu.toggle": "মেনু টগল করুন",
|
||||
"sidebar.nav.projectsAndSessions": "প্রকল্প এবং সেশন",
|
||||
"sidebar.settings": "সেটিংস",
|
||||
"sidebar.help": "সাহায্য",
|
||||
"sidebar.workspaces.enable": "ওয়ার্কস্পেস সক্রিয় করুন",
|
||||
"sidebar.workspaces.disable": "ওয়ার্কস্পেস অক্ষম করুন",
|
||||
"sidebar.gettingStarted.title": "শুরু হচ্ছে",
|
||||
"sidebar.gettingStarted.line1": "OpenCode বিনামূল্যে মডেল অন্তর্ভুক্ত করে যাতে আপনি অবিলম্বে শুরু করতে পারেন।",
|
||||
"sidebar.gettingStarted.line2":
|
||||
"মডেল, inc ব্যবহার করতে যেকোনো প্রদানকারীকে সংযুক্ত করুন। Claude, GPT, Gemini ইত্যাদি।",
|
||||
"sidebar.project.recentSessions": "সাম্প্রতিক সেশন",
|
||||
"sidebar.project.viewAllSessions": "সব সেশন দেখুন",
|
||||
"sidebar.project.clearNotifications": "বিজ্ঞপ্তিগুলি সাফ করুন",
|
||||
"sidebar.empty.title": "কোনো প্রকল্প খোলা নেই",
|
||||
"sidebar.empty.description": "শুরু করতে একটি প্রকল্প খুলুন",
|
||||
"debugBar.ariaLabel": "উন্নয়ন কর্মক্ষমতা ডায়গনিস্টিক",
|
||||
"debugBar.na": "n/a",
|
||||
"debugBar.nav.label": "এনএভি",
|
||||
"debugBar.nav.tip":
|
||||
"একটি সেশন পৃষ্ঠা স্পর্শ করে শেষ সম্পন্ন রুট ট্রানজিশন, রাউটার শুরু থেকে এটি স্থির হওয়ার পরে প্রথম পেইন্ট পর্যন্ত পরিমাপ করা হয়।",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "শেষ 5 সেকেন্ডে প্রতি সেকেন্ডে ঘূর্ণায়মান ফ্রেম।",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "গত 5 সেকেন্ডের মধ্যে সবচেয়ে খারাপ ফ্রেম সময়।",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "শেষ 5 সেকেন্ডে 32ms এর বেশি ফ্রেম।",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "শেষ 5 সেকেন্ডে অবরুদ্ধ সময় এবং দীর্ঘ-টাস্ক গণনা। সর্বোচ্চ টাস্ক: {{max}}।",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "গত 5 সেকেন্ডের মধ্যে সবচেয়ে খারাপ পর্যবেক্ষণ করা ইনপুট বিলম্ব।",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip": "শেষ 5 সেকেন্ডে আনুমানিক মিথস্ক্রিয়া সময়কাল। এটি INP-এর মতো, অফিসিয়াল ওয়েব ভাইটাল INP নয়।",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "বর্তমান অ্যাপের জীবনকালের জন্য ক্রমবর্ধমান বিন্যাস স্থানান্তর।",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "ব্যবহৃত JS হিপ বনাম হিপ সীমা। শুধুমাত্র Chromium।",
|
||||
"debugBar.mem.tip": "ব্যবহৃত JS হিপ বনাম হিপ সীমা। {{limit}}-এর {{used}}৷",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "সমস্ত ইন্টারেক্টিভ উপাদানের উপর ফোকাস শৈলী জোর করুন",
|
||||
"debugBar.focus.on": "চালু",
|
||||
"debugBar.focus.off": "বন্ধ",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "নির্বাচিত ভাষা পরিবর্তন না করে সম্পূর্ণ অ্যাপ লেআউটের দিকনির্দেশ জোর করে",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode ডেস্কটপ",
|
||||
"settings.section.desktop": "ডেস্কটপ",
|
||||
"settings.section.server": "সার্ভার",
|
||||
"settings.tab.general": "সাধারণ",
|
||||
"settings.tab.shortcuts": "শর্টকাট",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL ইন্টিগ্রেশন",
|
||||
"settings.desktop.wsl.description": "Windows-এ WSL-এর ভিতরে OpenCode সার্ভার চালান।",
|
||||
"settings.general.section.appearance": "চেহারা",
|
||||
"settings.general.section.advanced": "উন্নত",
|
||||
"settings.general.section.notifications": "সিস্টেম বিজ্ঞপ্তি",
|
||||
"settings.general.section.updates": "আপডেট",
|
||||
"settings.general.section.sounds": "শব্দ প্রভাব",
|
||||
"settings.general.section.feed": "খাওয়ান",
|
||||
"settings.general.section.display": "প্রদর্শন",
|
||||
"settings.general.row.language.title": "ভাষা",
|
||||
"settings.general.row.language.description": "OpenCode-এর জন্য প্রদর্শনের ভাষা পরিবর্তন করুন",
|
||||
"settings.general.row.shell.title": "টার্মিনাল শেল",
|
||||
"settings.general.row.shell.description": "টার্মিনাল এবং এজেন্ট সরঞ্জাম দ্বারা ব্যবহৃত শেল",
|
||||
"settings.general.row.shell.autoDefault": "স্বয়ংক্রিয় (ডিফল্ট)",
|
||||
"settings.general.row.shell.terminalOnly": "শুধুমাত্র টার্মিনাল",
|
||||
"settings.general.row.appearance.title": "চেহারা",
|
||||
"settings.general.row.appearance.description": "আপনার ডিভাইসে OpenCode কেমন দেখায় তা কাস্টমাইজ করুন",
|
||||
"settings.general.row.colorScheme.title": "রঙের স্কিম",
|
||||
"settings.general.row.colorScheme.description": "OpenCode সিস্টেম, হালকা বা অন্ধকার থিম অনুসরণ করে কিনা তা বেছে নিন",
|
||||
"settings.general.row.theme.title": "থিম",
|
||||
"settings.general.row.theme.description": "কিভাবে OpenCode থিম করা হয় তা কাস্টমাইজ করুন।",
|
||||
"settings.general.row.font.title": "কোড ফন্ট",
|
||||
"settings.general.row.font.description": "কোড ব্লকে ব্যবহৃত ফন্ট কাস্টমাইজ করুন",
|
||||
"settings.general.row.terminalFont.title": "টার্মিনাল ফন্ট",
|
||||
"settings.general.row.terminalFont.description": "টার্মিনালে ব্যবহৃত ফন্টটি কাস্টমাইজ করুন",
|
||||
"settings.general.row.uiFont.title": "UI ফন্ট",
|
||||
"settings.general.row.uiFont.description": "ইন্টারফেস জুড়ে ব্যবহৃত ফন্ট কাস্টমাইজ করুন",
|
||||
"settings.general.row.followup.title": "ফলো-আপ আচরণ",
|
||||
"settings.general.row.followup.description": "ফলো-আপ প্রম্পট অবিলম্বে বাছা বা একটি সারিতে অপেক্ষা করুন চয়ন করুন",
|
||||
"settings.general.row.followup.option.queue": "সারি",
|
||||
"settings.general.row.followup.option.steer": "বাহা",
|
||||
"settings.general.row.showFileTree.title": "ফাইল গাছ",
|
||||
"settings.general.row.showFileTree.description": "সেশনে ফাইল ট্রি প্যানেল দেখান",
|
||||
"settings.general.row.showNavigation.title": "নেভিগেশন নিয়ন্ত্রণ",
|
||||
"settings.general.row.showNavigation.description": "ডেস্কটপ শিরোনাম বারে পিছনে এবং এগিয়ে বোতামগুলি দেখান৷",
|
||||
"settings.general.row.showSearch.title": "কমান্ড প্যালেট",
|
||||
"settings.general.row.showSearch.description": "টাইটেল বারে অনুসন্ধান এবং কমান্ড প্যালেট বোতামটি দেখান",
|
||||
"settings.general.row.showTerminal.title": "টার্মিনাল",
|
||||
"settings.general.row.showTerminal.description": "ডেস্কটপ শিরোনাম বারে টার্মিনাল বোতামটি দেখান",
|
||||
"settings.general.row.showStatus.title": "সার্ভারের অবস্থা",
|
||||
"settings.general.row.showStatus.description": "টাইটেল বারে সার্ভার স্ট্যাটাস বোতামটি দেখান",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "নীচের নেভিগেশন",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"মোবাইলে স্ক্রিনের নীচে শিরোনাম বার এবং সেশন ট্যাবগুলি রাখুন৷",
|
||||
"settings.general.row.showCustomAgents.title": "এজেন্ট দেখান",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"কম্পোজারে এজেন্টদের মধ্যে স্যুইচ করুন। লুকানো হলে, বিল্ড এজেন্টে ডিফল্ট।",
|
||||
"settings.general.row.reasoningSummaries.title": "যুক্তির সারাংশ দেখান",
|
||||
"settings.general.row.reasoningSummaries.description": "টাইমলাইনে মডেল যুক্তির সারাংশ প্রদর্শন করুন",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "শেল টুল অংশ প্রসারিত",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "টাইমলাইনে ডিফল্টরূপে প্রসারিত শেল টুল অংশগুলি দেখান",
|
||||
"settings.general.row.editToolPartsExpanded.title": "সম্পাদনা টুল অংশ প্রসারিত করুন",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"টাইমলাইনে ডিফল্টরূপে প্রসারিত সম্পাদনা, লিখুন এবং প্যাচ টুল অংশগুলি দেখান৷",
|
||||
"settings.general.row.newInterface.title": "নতুন লেআউট",
|
||||
"settings.general.row.newInterface.badge": "নতুন",
|
||||
"settings.general.row.newInterface.description":
|
||||
"নতুন ট্যাব এবং হোম লেআউট ব্যবহার করুন. একটি সীমিত সময়ের জন্য লেআউটগুলির মধ্যে স্যুইচ করুন৷",
|
||||
"settings.general.row.newInterfaceNotice.title": "আপনি এখন নতুন লেআউট ব্যবহার করছেন",
|
||||
"settings.general.row.newInterfaceNotice.description": "আগের লেআউট আর উপলব্ধ নেই৷",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "খারিজ",
|
||||
"settings.general.row.pinchZoom.title": "জুম করতে চিমটি করুন",
|
||||
"settings.general.row.pinchZoom.description": "ট্র্যাকপ্যাড পিঞ্চ এবং Ctrl - স্ক্রোল অঙ্গভঙ্গি জুম করার অনুমতি দিন",
|
||||
"settings.general.row.wayland.title": "স্থানীয় Wayland ব্যবহার করুন",
|
||||
"settings.general.row.wayland.description": "Wayland-এ X11 ফলব্যাক অক্ষম করুন। পুনরায় চালু করা প্রয়োজন।",
|
||||
"settings.general.row.wayland.tooltip": "মিশ্র রিফ্রেশ-রেট মনিটর সহ Linux এ, নেটিভ Wayland আরও স্থিতিশীল হতে পারে।",
|
||||
"settings.general.row.releaseNotes.title": "রিলিজ নোট",
|
||||
"settings.general.row.releaseNotes.description": "আপডেটের পর নতুন কি পপআপ দেখান",
|
||||
"settings.updates.row.startup.title": "স্টার্টআপে আপডেটের জন্য চেক করুন",
|
||||
"settings.updates.row.startup.description": "OpenCode চালু হলে স্বয়ংক্রিয়ভাবে আপডেটের জন্য চেক করুন",
|
||||
"settings.updates.row.check.title": "আপডেটের জন্য চেক করুন",
|
||||
"settings.updates.row.check.description": "আপডেটের জন্য ম্যানুয়ালি পরীক্ষা করুন এবং উপলব্ধ থাকলে ইনস্টল করুন",
|
||||
"settings.updates.action.checkNow": "এখন পরীক্ষা করুন",
|
||||
"settings.updates.action.checking": "পরীক্ষা করা হচ্ছে...",
|
||||
"settings.updates.action.downloading": "ডাউনলোড হচ্ছে...",
|
||||
"settings.updates.action.installing": "ইনস্টল করা হচ্ছে...",
|
||||
"settings.updates.toast.latest.title": "আপনি আপ টু ডেট",
|
||||
"settings.updates.toast.latest.description": "আপনি OpenCode এর সর্বশেষ সংস্করণ চালাচ্ছেন।",
|
||||
"sound.option.none": "কোনোটিই নয়",
|
||||
"sound.option.alert01": "Alert 01",
|
||||
"sound.option.alert02": "Alert 02",
|
||||
"sound.option.alert03": "Alert 03",
|
||||
"sound.option.alert04": "Alert 04",
|
||||
"sound.option.alert05": "Alert 05",
|
||||
"sound.option.alert06": "Alert 06",
|
||||
"sound.option.alert07": "Alert 07",
|
||||
"sound.option.alert08": "Alert 08",
|
||||
"sound.option.alert09": "Alert 09",
|
||||
"sound.option.alert10": "Alert 10",
|
||||
"sound.option.bipbop01": "Bip-bop 01",
|
||||
"sound.option.bipbop02": "Bip-bop 02",
|
||||
"sound.option.bipbop03": "Bip-bop 03",
|
||||
"sound.option.bipbop04": "Bip-bop 04",
|
||||
"sound.option.bipbop05": "Bip-bop 05",
|
||||
"sound.option.bipbop06": "Bip-bop 06",
|
||||
"sound.option.bipbop07": "Bip-bop 07",
|
||||
"sound.option.bipbop08": "Bip-bop 08",
|
||||
"sound.option.bipbop09": "Bip-bop 09",
|
||||
"sound.option.bipbop10": "Bip-bop 10",
|
||||
"sound.option.staplebops01": "Staplebops 01",
|
||||
"sound.option.staplebops02": "Staplebops 02",
|
||||
"sound.option.staplebops03": "Staplebops 03",
|
||||
"sound.option.staplebops04": "Staplebops 04",
|
||||
"sound.option.staplebops05": "Staplebops 05",
|
||||
"sound.option.staplebops06": "Staplebops 06",
|
||||
"sound.option.staplebops07": "Staplebops 07",
|
||||
"sound.option.nope01": "Nope 01",
|
||||
"sound.option.nope02": "Nope 02",
|
||||
"sound.option.nope03": "Nope 03",
|
||||
"sound.option.nope04": "Nope 04",
|
||||
"sound.option.nope05": "Nope 05",
|
||||
"sound.option.nope06": "Nope 06",
|
||||
"sound.option.nope07": "Nope 07",
|
||||
"sound.option.nope08": "Nope 08",
|
||||
"sound.option.nope09": "Nope 09",
|
||||
"sound.option.nope10": "Nope 10",
|
||||
"sound.option.nope11": "Nope 11",
|
||||
"sound.option.nope12": "Nope 12",
|
||||
"sound.option.yup01": "Yup 01",
|
||||
"sound.option.yup02": "Yup 02",
|
||||
"sound.option.yup03": "Yup 03",
|
||||
"sound.option.yup04": "Yup 04",
|
||||
"sound.option.yup05": "Yup 05",
|
||||
"sound.option.yup06": "Yup 06",
|
||||
"settings.general.notifications.agent.title": "এজেন্ট",
|
||||
"settings.general.notifications.agent.description":
|
||||
"এজেন্ট সম্পূর্ণ হলে বা মনোযোগের প্রয়োজন হলে সিস্টেম বিজ্ঞপ্তি দেখান",
|
||||
"settings.general.notifications.permissions.title": "অনুমতি",
|
||||
"settings.general.notifications.permissions.description": "একটি অনুমতি প্রয়োজন হলে সিস্টেম বিজ্ঞপ্তি দেখান",
|
||||
"settings.general.notifications.errors.title": "ত্রুটি",
|
||||
"settings.general.notifications.errors.description": "একটি ত্রুটি ঘটলে সিস্টেম বিজ্ঞপ্তি দেখান",
|
||||
"settings.general.sounds.agent.title": "এজেন্ট",
|
||||
"settings.general.sounds.agent.description": "এজেন্ট সম্পূর্ণ হলে বা মনোযোগের প্রয়োজন হলে শব্দ বাজান",
|
||||
"settings.general.sounds.permissions.title": "অনুমতি",
|
||||
"settings.general.sounds.permissions.description": "অনুমতির প্রয়োজন হলে শব্দ বাজান",
|
||||
"settings.general.sounds.errors.title": "ত্রুটি",
|
||||
"settings.general.sounds.errors.description": "একটি ত্রুটি ঘটলে শব্দ বাজান",
|
||||
"settings.shortcuts.title": "কীবোর্ড শর্টকাট",
|
||||
"settings.shortcuts.reset.button": "ডিফল্টে রিসেট করুন",
|
||||
"settings.shortcuts.reset.toast.title": "শর্টকাট রিসেট",
|
||||
"settings.shortcuts.reset.toast.description": "কীবোর্ড শর্টকাটগুলি ডিফল্টে রিসেট করা হয়েছে৷",
|
||||
"settings.shortcuts.conflict.title": "শর্টকাট ইতিমধ্যেই ব্যবহার করা হচ্ছে৷",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} ইতিমধ্যেই {{titles}} এ বরাদ্দ করা হয়েছে৷",
|
||||
"settings.shortcuts.unassigned": "আনঅ্যাসাইন করা হয়েছে",
|
||||
"settings.shortcuts.pressKeys": "কী টিপুন",
|
||||
"settings.shortcuts.search.placeholder": "শর্টকাট অনুসন্ধান করুন",
|
||||
"settings.shortcuts.search.empty": "কোন শর্টকাট পাওয়া যায়নি",
|
||||
"settings.shortcuts.group.general": "সাধারণ",
|
||||
"settings.shortcuts.group.session": "সেশন",
|
||||
"settings.shortcuts.group.navigation": "নেভিগেশন",
|
||||
"settings.shortcuts.group.modelAndAgent": "মডেল এবং এজেন্ট",
|
||||
"settings.shortcuts.group.terminal": "টার্মিনাল",
|
||||
"settings.shortcuts.group.prompt": "প্রম্পট",
|
||||
"settings.providers.title": "প্রদানকারী",
|
||||
"settings.providers.description": "প্রদানকারী সেটিংস এখানে কনফিগারযোগ্য হবে।",
|
||||
"settings.providers.section.connected": "সংযুক্ত প্রদানকারী",
|
||||
"settings.providers.connected.empty": "কোন সংযুক্ত প্রদানকারী",
|
||||
"settings.providers.connected.environmentDescription": "আপনার পরিবেশ ভেরিয়েবল থেকে সংযুক্ত",
|
||||
"settings.providers.section.popular": "জনপ্রিয় প্রদানকারী",
|
||||
"settings.providers.custom.description": "বেস URL দ্বারা একটি OpenAI-সামঞ্জস্যপূর্ণ প্রদানকারী যোগ করুন।",
|
||||
"settings.providers.tag.environment": "পরিবেশ",
|
||||
"settings.providers.tag.config": "কনফিগার",
|
||||
"settings.providers.tag.custom": "কাস্টম",
|
||||
"settings.providers.tag.other": "অন্যান্য",
|
||||
"settings.models.title": "মডেল",
|
||||
"settings.models.description": "মডেল সেটিংস এখানে কনফিগারযোগ্য হবে।",
|
||||
"settings.agents.title": "এজেন্ট",
|
||||
"settings.agents.description": "এজেন্ট সেটিংস এখানে কনফিগারযোগ্য হবে।",
|
||||
"settings.commands.title": "কমান্ড",
|
||||
"settings.commands.description": "কমান্ড সেটিংস এখানে কনফিগারযোগ্য হবে।",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "MCP সেটিংস এখানে কনফিগার করা যাবে।",
|
||||
"settings.permissions.title": "অনুমতি",
|
||||
"settings.permissions.description": "সার্ভার ডিফল্টরূপে কোন সরঞ্জামগুলি ব্যবহার করতে পারে তা নিয়ন্ত্রণ করুন।",
|
||||
"settings.permissions.section.tools": "টুলস",
|
||||
"settings.permissions.toast.updateFailed.title": "অনুমতি আপডেট করতে ব্যর্থ হয়েছে",
|
||||
"settings.permissions.action.allow": "অনুমতি দিন",
|
||||
"settings.permissions.action.ask": "জিজ্ঞাসা করুন",
|
||||
"settings.permissions.action.deny": "অস্বীকার করুন",
|
||||
"settings.permissions.tool.read.title": "পড়ুন",
|
||||
"settings.permissions.tool.read.description": "একটি ফাইল পড়া (ফাইল পাথ মেলে)",
|
||||
"settings.permissions.tool.edit.title": "সম্পাদনা করুন",
|
||||
"settings.permissions.tool.edit.description": "সম্পাদনা, লেখা এবং প্যাচ সহ ফাইলগুলি পরিবর্তন করুন",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "গ্লোব প্যাটার্ন ব্যবহার করে ফাইল মেলে",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "রেগুলার এক্সপ্রেশন ব্যবহার করে ফাইলের বিষয়বস্তু অনুসন্ধান করুন",
|
||||
"settings.permissions.tool.list.title": "তালিকা",
|
||||
"settings.permissions.tool.list.description": "একটি ডিরেক্টরির মধ্যে ফাইল তালিকা",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "শেল কমান্ড চালান",
|
||||
"settings.permissions.tool.task.title": "টাস্ক",
|
||||
"settings.permissions.tool.task.description": "সাব-এজেন্ট চালু করুন",
|
||||
"settings.permissions.tool.skill.title": "দক্ষতা",
|
||||
"settings.permissions.tool.skill.description": "নাম দ্বারা একটি দক্ষতা লোড",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "ভাষা সার্ভার প্রশ্ন চালান",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "করণীয় তালিকা আপডেট করুন",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "একটি URL থেকে সামগ্রী আনুন",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
"settings.permissions.tool.websearch.description": "ওয়েব অনুসন্ধান করুন",
|
||||
"settings.permissions.tool.external_directory.title": "বাহ্যিক ডিরেক্টরি",
|
||||
"settings.permissions.tool.external_directory.description": "প্রকল্প ডিরেক্টরির বাইরে ফাইল অ্যাক্সেস করুন",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "অভিন্ন ইনপুট সহ বারবার টুল কল সনাক্ত করুন",
|
||||
"session.delete.failed.title": "সেশন মুছে ফেলতে ব্যর্থ হয়েছে৷",
|
||||
"session.delete.title": "সেশন মুছুন",
|
||||
"session.delete.confirm": 'সেশন "{{name}}" মুছবেন?',
|
||||
"session.delete.button": "সেশন মুছুন",
|
||||
"workspace.new": "নতুন ওয়ার্কস্পেস",
|
||||
"workspace.type.local": "স্থানীয়",
|
||||
"workspace.type.sandbox": "স্যান্ডবক্স",
|
||||
"workspace.create.failed.title": "ওয়ার্কস্পেস তৈরি করতে ব্যর্থ হয়েছে৷",
|
||||
"workspace.delete.failed.title": "ওয়ার্কস্পেস মুছে ফেলতে ব্যর্থ হয়েছে৷",
|
||||
"workspace.resetting.title": "ওয়ার্কস্পেস রিসেট করা হচ্ছে",
|
||||
"workspace.resetting.description": "এতে এক মিনিট সময় লাগতে পারে।",
|
||||
"workspace.reset.failed.title": "ওয়ার্কস্পেস রিসেট করতে ব্যর্থ হয়েছে৷",
|
||||
"workspace.reset.success.title": "ওয়ার্কস্পেস রিসেট",
|
||||
"workspace.reset.success.description": "ওয়ার্কস্পেস এখন ডিফল্ট শাখার সাথে মেলে।",
|
||||
"workspace.error.stillPreparing": "ওয়ার্কস্পেস এখনও প্রস্তুত করা হয়",
|
||||
"workspace.status.checking": "একত্রিত না হওয়া পরিবর্তনগুলি পরীক্ষা করা হচ্ছে...",
|
||||
"workspace.status.error": "গিট স্ট্যাটাস যাচাই করতে অক্ষম।",
|
||||
"workspace.status.clean": "কোনো একীভূত পরিবর্তন সনাক্ত করা যায়নি.",
|
||||
"workspace.status.dirty": "এই ওয়ার্কস্পেসে একত্রিত না হওয়া পরিবর্তন শনাক্ত করা হয়েছে।",
|
||||
"workspace.delete.title": "ওয়ার্কস্পেস মুছুন",
|
||||
"workspace.delete.confirm": 'ওয়ার্কস্পেস "{{name}}" মুছবেন?',
|
||||
"workspace.delete.button": "ওয়ার্কস্পেস মুছুন",
|
||||
"workspace.reset.title": "ওয়ার্কস্পেস রিসেট করুন",
|
||||
"workspace.reset.confirm": 'ওয়ার্কস্পেস "{{name}}" রিসেট করবেন?',
|
||||
"workspace.reset.button": "ওয়ার্কস্পেস রিসেট করুন",
|
||||
"workspace.reset.archived.none": "কোনো সক্রিয় সেশন সংরক্ষণাগারভুক্ত করা হবে না.",
|
||||
"workspace.reset.archived.one": "1 সেশন আর্কাইভ করা হবে.",
|
||||
"workspace.reset.archived.many": "{{count}} সেশন আর্কাইভ করা হবে।",
|
||||
"workspace.reset.note": "এটি ডিফল্ট শাখার সাথে মেলে ওয়ার্কস্পেস রিসেট করবে।",
|
||||
"dialog.usageExceeded.dontShowAgain": "আবার দেখাবেন না",
|
||||
}
|
||||
+201
-44
@@ -1,4 +1,104 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Arquivo",
|
||||
"desktop.menu.edit": "Editar",
|
||||
"desktop.menu.view": "Visualizar",
|
||||
"desktop.menu.go": "Ir",
|
||||
"desktop.menu.window": "Janela",
|
||||
"desktop.menu.help": "Ajuda",
|
||||
"desktop.menu.checkForUpdates": "Verificar atualizações...",
|
||||
"desktop.menu.settings": "Configurações",
|
||||
"desktop.menu.reloadWebview": "Recarregar WebView",
|
||||
"desktop.menu.restart": "Reiniciar",
|
||||
"desktop.menu.exportLogs": "Exportar logs...",
|
||||
"desktop.menu.newSession": "Nova sessão",
|
||||
"desktop.menu.openProject": "Abrir projeto...",
|
||||
"desktop.menu.newWindow": "Nova janela",
|
||||
"desktop.menu.closeWindow": "Fechar janela",
|
||||
"desktop.menu.undo": "Desfazer",
|
||||
"desktop.menu.redo": "Refazer",
|
||||
"desktop.menu.cut": "Recortar",
|
||||
"desktop.menu.copy": "Copiar",
|
||||
"desktop.menu.paste": "Colar",
|
||||
"desktop.menu.delete": "Excluir",
|
||||
"desktop.menu.selectAll": "Selecionar tudo",
|
||||
"desktop.menu.toggleSidebar": "Alternar barra lateral",
|
||||
"desktop.menu.toggleTerminal": "Alternar terminal",
|
||||
"desktop.menu.toggleFileTree": "Alternar árvore de arquivos",
|
||||
"desktop.menu.reload": "Recarregar",
|
||||
"desktop.menu.toggleDeveloperTools": "Alternar ferramentas de desenvolvedor",
|
||||
"desktop.menu.actualSize": "Tamanho real",
|
||||
"desktop.menu.zoomIn": "Ampliar",
|
||||
"desktop.menu.zoomOut": "Reduzir",
|
||||
"desktop.menu.toggleFullScreen": "Alternar tela inteira",
|
||||
"desktop.menu.back": "Voltar",
|
||||
"desktop.menu.forward": "Avançar",
|
||||
"desktop.menu.previousSession": "Sessão anterior",
|
||||
"desktop.menu.nextSession": "Próxima sessão",
|
||||
"desktop.menu.previousProject": "Projeto anterior",
|
||||
"desktop.menu.nextProject": "Próximo projeto",
|
||||
"desktop.menu.minimize": "Minimizar",
|
||||
"desktop.menu.maximize": "Maximizar",
|
||||
"desktop.menu.documentation": "Documentação do OpenCode",
|
||||
"desktop.menu.supportForum": "Fórum de suporte",
|
||||
"desktop.menu.shareFeedback": "Enviar comentários",
|
||||
"desktop.menu.reportBug": "Relatar um bug",
|
||||
"desktop.menu.ariaLabel": "Menu do OpenCode",
|
||||
|
||||
"desktop.updater.dialog.checkFailed.message": "Falha ao verificar atualizações.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Erro de atualização",
|
||||
"desktop.updater.dialog.upToDate.message": "Você está em dia.",
|
||||
"desktop.updater.dialog.upToDate.title": "Nenhuma atualização",
|
||||
"desktop.updater.dialog.ready.message": "A atualização {{version}} foi baixada. Deseja reiniciar agora?",
|
||||
"desktop.updater.dialog.ready.title": "Atualização pronta",
|
||||
"desktop.updater.dialog.restart": "Reiniciar",
|
||||
"desktop.updater.dialog.later": "Mais tarde",
|
||||
|
||||
"desktop.recovery.action.relaunch": "Reabrir",
|
||||
"desktop.recovery.action.exportLogs": "Exportar logs",
|
||||
"desktop.recovery.action.keepWaiting": "Continuar aguardando",
|
||||
"desktop.recovery.action.quit": "Encerrar",
|
||||
"desktop.recovery.loadFailed": "Falha ao carregar o OpenCode",
|
||||
"desktop.recovery.terminated": "A janela do OpenCode foi encerrada inesperadamente",
|
||||
"desktop.recovery.unresponsive": "O OpenCode não está respondendo",
|
||||
"desktop.recovery.unresponsive.detail": "Você pode reabrir o aplicativo, abrir os logs ou continuar aguardando.",
|
||||
"desktop.recovery.loadFailed.detail": "Janela: {{window}}\nURL: {{url}}\nErro: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Janela: {{window}}\nMotivo: {{reason}}\nCódigo: {{code}}",
|
||||
"desktop.recovery.unknown": "<desconhecido>",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Escolher uma pasta",
|
||||
"desktop.dialog.chooseFile": "Escolher um arquivo",
|
||||
"desktop.dialog.saveFile": "Salvar arquivo",
|
||||
"desktop.dialog.files": "Arquivos",
|
||||
"desktop.server.local": "Servidor local",
|
||||
|
||||
"desktop.wsl.error.windowsOnly": "O WSL está disponível somente no Windows",
|
||||
"desktop.wsl.error.unavailable": "O WSL está indisponível",
|
||||
"desktop.wsl.error.listInstalled": "Falha ao listar as distribuições WSL instaladas",
|
||||
"desktop.wsl.error.listOnline": "Falha ao listar as distribuições WSL disponíveis online",
|
||||
"desktop.wsl.error.executeDistro": "Não é possível executar comandos na distribuição",
|
||||
"desktop.wsl.error.installWsl": "Falha na instalação do WSL",
|
||||
"desktop.wsl.error.installDistro": "Falha ao instalar a distribuição: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "Falha na instalação do OpenCode",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} já foi adicionada",
|
||||
"desktop.wsl.error.opencodeMissing": "O opencode não está instalado nesta distribuição",
|
||||
"desktop.wsl.error.opencodeCannotRun": "O opencode está instalado, mas não pôde ser executado",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "O OpenCode não está instalado em {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"A atualização do OpenCode foi concluída, mas {{distro}} ainda informa a versão {{installed}}; a versão esperada é {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "sem versão",
|
||||
"desktop.wsl.error.serverExited":
|
||||
"O servidor WSL foi encerrado após a inicialização (código={{code}} sinal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"O servidor WSL foi encerrado antes de ficar íntegro (código={{code}} sinal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout":
|
||||
"O tempo limite da verificação de integridade do sidecar de {{distro}} foi excedido após {{timeout}}ms",
|
||||
"desktop.wsl.error.commandTimeout": "O tempo limite de {{command}} {{args}} foi excedido após {{timeout}}ms",
|
||||
"desktop.wsl.error.failedPort": "Falha ao obter a porta",
|
||||
|
||||
"desktop.picker.error.notSelected": "O arquivo não foi selecionado pelo seletor",
|
||||
"desktop.picker.error.sizeLimit": "Os anexos selecionados excedem o limite de {{limit}} MB",
|
||||
|
||||
"command.category.suggested": "Sugerido",
|
||||
"command.category.view": "Visualizar",
|
||||
"command.category.project": "Projeto",
|
||||
@@ -16,6 +116,7 @@ export const dict = {
|
||||
"command.category.permissions": "Permissões",
|
||||
"command.category.workspace": "Espaço de trabalho",
|
||||
"command.category.settings": "Configurações",
|
||||
"command.logs.export": "Exportar logs",
|
||||
"theme.scheme.system": "Sistema",
|
||||
"theme.scheme.light": "Claro",
|
||||
"theme.scheme.dark": "Escuro",
|
||||
@@ -85,6 +186,9 @@ export const dict = {
|
||||
"command.session.share.description": "Compartilhar esta sessão e copiar a URL para a área de transferência",
|
||||
"command.session.unshare": "Parar de compartilhar sessão",
|
||||
"command.session.unshare.description": "Parar de compartilhar esta sessão",
|
||||
"command.session.export": "Exportar sessão",
|
||||
"command.session.export.description": "Exportar a transcrição completa da sessão como JSON",
|
||||
|
||||
"palette.search.placeholder": "Buscar arquivos, comandos e sessões",
|
||||
"palette.search.placeholder.home": "Buscar comandos e sessões",
|
||||
"palette.empty": "Nenhum resultado encontrado",
|
||||
@@ -94,13 +198,14 @@ export const dict = {
|
||||
"dialog.provider.empty": "Nenhum provedor encontrado",
|
||||
"dialog.provider.group.popular": "Popular",
|
||||
"dialog.provider.group.other": "Outro",
|
||||
"dialog.provider.custom.label": "Provedor personalizado compatível com OpenAI",
|
||||
"dialog.provider.tag.recommended": "Recomendado",
|
||||
"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": "Conectar com Claude Pro/Max ou chave de API",
|
||||
"dialog.provider.copilot.note": "Conectar com Copilot ou chave de API",
|
||||
"dialog.provider.openai.note": "Conectar com ChatGPT Pro/Plus ou chave de API",
|
||||
"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",
|
||||
"dialog.provider.openrouter.note": "Acesse todos os modelos suportados de um único provedor",
|
||||
"dialog.provider.vercel.note": "Acesso unificado a modelos de IA com roteamento inteligente",
|
||||
@@ -112,11 +217,14 @@ export const dict = {
|
||||
"dialog.model.manage.provider.toggle": "Alternar todos os modelos {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Modelos gratuitos fornecidos pelo OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Adicionar mais modelos de provedores populares",
|
||||
"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",
|
||||
"provider.connect.method.headless": "Sem interface gráfica",
|
||||
"provider.connect.status.inProgress": "Autorização em andamento...",
|
||||
"provider.connect.status.waiting": "Aguardando autorização...",
|
||||
"provider.connect.status.failed": "Autorização falhou: {{error}}",
|
||||
@@ -148,15 +256,16 @@ export const dict = {
|
||||
"provider.connect.toast.connected.title": "{{provider}} conectado",
|
||||
"provider.connect.toast.connected.description": "Modelos do {{provider}} agora estão disponíveis para uso.",
|
||||
"provider.custom.title": "Provedor personalizado",
|
||||
"provider.custom.unavailable": "Provedores personalizados não estão disponíveis neste servidor",
|
||||
"provider.custom.description.prefix": "Configure um provedor compatível com OpenAI. Veja a ",
|
||||
"provider.custom.description.link": "documentação de configuração do provedor",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "ID do Provedor",
|
||||
"provider.custom.field.providerID.label": "ID do provedor",
|
||||
"provider.custom.field.providerID.placeholder": "meuprovedor",
|
||||
"provider.custom.field.providerID.description": "Letras minúsculas, números, hifens ou sublinhados",
|
||||
"provider.custom.field.name.label": "Nome de exibição",
|
||||
"provider.custom.field.name.placeholder": "Meu Provedor de IA",
|
||||
"provider.custom.field.baseURL.label": "URL Base",
|
||||
"provider.custom.field.name.placeholder": "Meu provedor de IA",
|
||||
"provider.custom.field.baseURL.label": "URL base",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.meuprovedor.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Chave de API",
|
||||
"provider.custom.field.apiKey.placeholder": "Chave de API",
|
||||
@@ -165,7 +274,7 @@ export const dict = {
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "id-do-modelo",
|
||||
"provider.custom.models.name.label": "Nome",
|
||||
"provider.custom.models.name.placeholder": "Nome de Exibição",
|
||||
"provider.custom.models.name.placeholder": "Nome de exibição",
|
||||
"provider.custom.models.remove": "Remover modelo",
|
||||
"provider.custom.models.add": "Adicionar modelo",
|
||||
"provider.custom.headers.label": "Cabeçalhos (opcional)",
|
||||
@@ -175,11 +284,11 @@ export const dict = {
|
||||
"provider.custom.headers.value.placeholder": "valor",
|
||||
"provider.custom.headers.remove": "Remover cabeçalho",
|
||||
"provider.custom.headers.add": "Adicionar cabeçalho",
|
||||
"provider.custom.error.providerID.required": "ID do Provedor é obrigatório",
|
||||
"provider.custom.error.providerID.required": "O ID do provedor é obrigatório",
|
||||
"provider.custom.error.providerID.format": "Use letras minúsculas, números, hifens ou sublinhados",
|
||||
"provider.custom.error.providerID.exists": "Esse ID de provedor já existe",
|
||||
"provider.custom.error.name.required": "Nome de exibição é obrigatório",
|
||||
"provider.custom.error.baseURL.required": "URL Base é obrigatória",
|
||||
"provider.custom.error.name.required": "O nome de exibição é obrigatório",
|
||||
"provider.custom.error.baseURL.required": "A URL base é obrigatória",
|
||||
"provider.custom.error.baseURL.format": "Deve começar com http:// ou https://",
|
||||
"provider.custom.error.required": "Obrigatório",
|
||||
"provider.custom.error.duplicate": "Duplicado",
|
||||
@@ -215,7 +324,7 @@ export const dict = {
|
||||
"common.clear": "Limpar",
|
||||
"common.connect": "Conectar",
|
||||
"common.disconnect": "Desconectar",
|
||||
"common.continue": "Enviar",
|
||||
"common.continue": "Continuar",
|
||||
"common.submit": "Enviar",
|
||||
"common.save": "Salvar",
|
||||
"common.saving": "Salvando...",
|
||||
@@ -267,7 +376,7 @@ export const dict = {
|
||||
"prompt.context.includeActiveFile": "Incluir arquivo ativo",
|
||||
"prompt.context.removeActiveFile": "Remover arquivo ativo do contexto",
|
||||
"prompt.context.removeFile": "Remover arquivo do contexto",
|
||||
"prompt.action.attachFile": "Anexar arquivo",
|
||||
"prompt.action.attachFile": "Adicionar arquivos",
|
||||
"prompt.menu.addImagesAndFiles": "Adicionar arquivos e mais",
|
||||
"prompt.menu.imagesAndFiles": "Imagens e arquivos",
|
||||
"prompt.menu.commands": "Comandos",
|
||||
@@ -277,6 +386,7 @@ export const dict = {
|
||||
"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.",
|
||||
@@ -287,7 +397,7 @@ export const dict = {
|
||||
"prompt.toast.promptSendFailed.title": "Falha ao enviar prompt",
|
||||
"prompt.toast.promptSendFailed.description": "Não foi possível recuperar a sessão",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{enabled}} of {{total}} habilitados",
|
||||
"dialog.mcp.description": "{{enabled}} de {{total}} ativados",
|
||||
"dialog.mcp.empty": "Nenhum MCP configurado",
|
||||
"dialog.lsp.empty": "LSPs detectados automaticamente pelos tipos de arquivo",
|
||||
"dialog.plugins.empty": "Plugins configurados em opencode.json",
|
||||
@@ -309,11 +419,11 @@ export const dict = {
|
||||
"dialog.server.search.placeholder": "Buscar servidores",
|
||||
"dialog.server.empty": "Nenhum servidor ainda",
|
||||
"dialog.server.add.title": "Adicionar um servidor",
|
||||
"dialog.server.add.url": "URL do servidor",
|
||||
"dialog.server.add.url": "Endereço do servidor",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Não foi possível conectar ao servidor",
|
||||
"dialog.server.add.checking": "Verificando...",
|
||||
"dialog.server.add.button": "Adicionar",
|
||||
"dialog.server.add.button": "Adicionar servidor",
|
||||
"dialog.server.add.name": "Nome do servidor (opcional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nome de usuário (opcional)",
|
||||
@@ -321,7 +431,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Editar servidor",
|
||||
"dialog.server.default.title": "Servidor padrão",
|
||||
"dialog.server.default.description":
|
||||
"Conectar a este servidor na inicialização do aplicativo ao invés de iniciar um servidor local. Requer reinicialização.",
|
||||
"Conectar a este servidor na inicialização do aplicativo em vez de iniciar um servidor local. Requer reinicialização.",
|
||||
"dialog.server.default.none": "Nenhum servidor selecionado",
|
||||
"dialog.server.default.set": "Definir servidor atual como padrão",
|
||||
"dialog.server.default.clear": "Limpar",
|
||||
@@ -401,6 +511,21 @@ export const dict = {
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "A versão instalada não corresponde à versão do aplicativo para desktop.",
|
||||
"wsl.onboarding.adding": "Adicionando...",
|
||||
"help.tabs.toast.ariaLabel": "Apresentamos as abas. Organize seu trabalho e suas sessões ativas com abas",
|
||||
"help.tabs.toast.dismiss": "Dispensar informações sobre as abas",
|
||||
"help.tabs.title": "Apresentamos as abas",
|
||||
"help.tabs.description": "Organize seu trabalho e suas sessões ativas com abas",
|
||||
"help.tabs.date": "14 de julho",
|
||||
"help.tabs.introduction": "Agora, o OpenCode Desktop é organizado em abas.",
|
||||
"help.tabs.sessions":
|
||||
"Inicie uma nova sessão em uma aba ou abra uma sessão existente de qualquer um dos seus projetos. Abra uma nova aba quando começar algo novo e feche-a quando terminar.",
|
||||
"help.tabs.organize":
|
||||
"Manter algumas abas abertas facilita a organização das sessões ativas. Renomeie as abas com nomes fáceis de lembrar se pretende mantê-las abertas.",
|
||||
"help.tabs.home":
|
||||
"Você encontrará todas as suas sessões e projetos na nova tela Início. Ao selecionar uma sessão, ela será aberta em uma aba.",
|
||||
"help.tabs.persistence": "Quando você reabrir o aplicativo, suas abas ainda estarão abertas.",
|
||||
"help.tabs.worktrees":
|
||||
"O novo design ainda não oferece suporte a Git Worktrees, mas esse recurso estará disponível em breve. Se preferir continuar usando o layout anterior, você poderá alternar entre os layouts nas Configurações. Lembre-se apenas de que o novo layout se tornará permanente em algumas semanas.",
|
||||
"dialog.project.edit.title": "Editar projeto",
|
||||
"dialog.project.edit.name": "Nome",
|
||||
"dialog.project.edit.icon": "Ícone",
|
||||
@@ -411,33 +536,37 @@ export const dict = {
|
||||
"dialog.project.edit.color.select": "Selecionar cor {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "Script de inicialização do espaço de trabalho",
|
||||
"dialog.project.edit.worktree.startup.description": "Executa após criar um novo espaço de trabalho (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "ex: bun install",
|
||||
"context.breakdown.title": "Detalhamento do Contexto",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "Ex.: bun install",
|
||||
"dialog.usageExceeded.dontShowAgain": "Não mostrar novamente",
|
||||
|
||||
"context.breakdown.title": "Detalhamento do contexto",
|
||||
"context.breakdown.note":
|
||||
'Detalhamento aproximado dos tokens de entrada. "Outros" inclui definições de ferramentas e overhead.',
|
||||
'Detalhamento aproximado dos tokens de entrada. "Outros" inclui definições de ferramentas e sobrecarga.',
|
||||
"context.breakdown.system": "Sistema",
|
||||
"context.breakdown.user": "Usuário",
|
||||
"context.breakdown.assistant": "Assistente",
|
||||
"context.breakdown.tool": "Chamadas de Ferramentas",
|
||||
"context.breakdown.tool": "Chamadas de ferramentas",
|
||||
"context.breakdown.other": "Outros",
|
||||
"context.systemPrompt.title": "Prompt do Sistema",
|
||||
"context.systemPrompt.title": "Prompt do sistema",
|
||||
"context.rawMessages.title": "Mensagens brutas",
|
||||
"context.export.session": "Exportar sessão",
|
||||
|
||||
"context.stats.session": "Sessão",
|
||||
"context.stats.messages": "Mensagens",
|
||||
"context.stats.provider": "Provedor",
|
||||
"context.stats.model": "Modelo",
|
||||
"context.stats.limit": "Limite de Contexto",
|
||||
"context.stats.totalTokens": "Total de Tokens",
|
||||
"context.stats.limit": "Limite de contexto",
|
||||
"context.stats.totalTokens": "Total de tokens",
|
||||
"context.stats.usage": "Uso",
|
||||
"context.stats.inputTokens": "Tokens de Entrada",
|
||||
"context.stats.outputTokens": "Tokens de Saída",
|
||||
"context.stats.reasoningTokens": "Tokens de Raciocínio",
|
||||
"context.stats.cacheTokens": "Tokens de Cache (leitura/escrita)",
|
||||
"context.stats.userMessages": "Mensagens de Usuário",
|
||||
"context.stats.assistantMessages": "Mensagens do Assistente",
|
||||
"context.stats.totalCost": "Custo Total",
|
||||
"context.stats.sessionCreated": "Sessão Criada",
|
||||
"context.stats.lastActivity": "Última Atividade",
|
||||
"context.stats.inputTokens": "Tokens de entrada",
|
||||
"context.stats.outputTokens": "Tokens de saída",
|
||||
"context.stats.reasoningTokens": "Tokens de raciocínio",
|
||||
"context.stats.cacheTokens": "Tokens de cache (leitura/escrita)",
|
||||
"context.stats.userMessages": "Mensagens do usuário",
|
||||
"context.stats.assistantMessages": "Mensagens do assistente",
|
||||
"context.stats.totalCost": "Custo total",
|
||||
"context.stats.sessionCreated": "Sessão criada",
|
||||
"context.stats.lastActivity": "Última atividade",
|
||||
"context.usage.tokens": "Tokens",
|
||||
"context.usage.usage": "Uso",
|
||||
"context.usage.cost": "Custo",
|
||||
@@ -488,6 +617,11 @@ export const dict = {
|
||||
"toast.session.unshare.success.description": "Sessão deixou de ser compartilhada com sucesso!",
|
||||
"toast.session.unshare.failed.title": "Falha ao parar de compartilhar sessão",
|
||||
"toast.session.unshare.failed.description": "Ocorreu um erro ao parar de compartilhar a sessão",
|
||||
"toast.session.export.success.title": "Sessão exportada",
|
||||
"toast.session.export.success.description": "Sessão salva em {{filename}}",
|
||||
"toast.session.export.failed.title": "Falha ao exportar sessão",
|
||||
"toast.session.export.failed.description": "Ocorreu um erro ao exportar a sessão",
|
||||
|
||||
"toast.session.listFailed.title": "Falha ao carregar sessões para {{project}}",
|
||||
"toast.update.title": "Atualização disponível",
|
||||
"toast.update.description": "Uma nova versão do OpenCode ({{version}}) está disponível para instalação.",
|
||||
@@ -496,7 +630,7 @@ export const dict = {
|
||||
"error.page.title": "Algo deu errado",
|
||||
"error.page.description": "Ocorreu um erro ao carregar a aplicação.",
|
||||
"error.page.description.localServerStartup": "Ocorreu um erro ao iniciar o servidor local.",
|
||||
"error.page.details.label": "Detalhes do Erro",
|
||||
"error.page.details.label": "Detalhes do erro",
|
||||
"error.page.action.restart": "Reiniciar",
|
||||
"error.page.action.report": "Reportar erro",
|
||||
"error.page.action.reported": "Erro reportado",
|
||||
@@ -569,7 +703,7 @@ export const dict = {
|
||||
"session.error.notFound.description": "Esta aba aponta para uma sessão que não existe mais neste servidor.",
|
||||
"session.error.notFound.closeTab": "Fechar aba",
|
||||
"session.error.serverConnection": "Não é possível conectar a este servidor",
|
||||
"session.review.filesChanged": "{{count}} Arquivos Alterados",
|
||||
"session.review.filesChanged": "{{count}} arquivos alterados",
|
||||
"session.review.change.one": "Alteração",
|
||||
"session.review.change.other": "Alterações",
|
||||
"session.review.loadingChanges": "Carregando alterações...",
|
||||
@@ -596,14 +730,17 @@ export const dict = {
|
||||
"session.question.minimize": "Minimizar pergunta",
|
||||
"session.question.restore": "Restaurar pergunta",
|
||||
"session.question.pending.one": "{{count}} pergunta pendente",
|
||||
"session.question.pending.many": "{{count}} de perguntas pendentes",
|
||||
"session.question.pending.other": "{{count}} perguntas pendentes",
|
||||
"session.followupDock.summary.one": "{{count}} mensagem na fila",
|
||||
"session.followupDock.summary.many": "{{count}} de mensagens na fila",
|
||||
"session.followupDock.summary.other": "{{count}} mensagens na fila",
|
||||
"session.followupDock.sendNow": "Enviar agora",
|
||||
"session.followupDock.edit": "Editar",
|
||||
"session.followupDock.collapse": "Recolher mensagens na fila",
|
||||
"session.followupDock.expand": "Expandir mensagens na fila",
|
||||
"session.revertDock.summary.one": "{{count}} mensagem revertida",
|
||||
"session.revertDock.summary.many": "{{count}} de mensagens revertidas",
|
||||
"session.revertDock.summary.other": "{{count}} mensagens revertidas",
|
||||
"session.revertDock.collapse": "Recolher mensagens revertidas",
|
||||
"session.revertDock.expand": "Expandir mensagens revertidas",
|
||||
@@ -655,15 +792,23 @@ export const dict = {
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Fechar terminal",
|
||||
"terminal.connectionLost.title": "Conexão Perdida",
|
||||
"terminal.connectionLost.title": "Conexão perdida",
|
||||
"terminal.connectionLost.description":
|
||||
"A conexão do terminal foi interrompida. Isso pode acontecer quando o servidor reinicia.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"O tíquete de conexão PTY foi rejeitado pelas verificações de origem ou CSRF. Verifique a configuração de CORS do servidor.",
|
||||
"terminal.connectTicket.statusError": "O tíquete de conexão PTY falhou com o status {{status}}",
|
||||
|
||||
"titlebar.update": "Atualizar",
|
||||
"titlebar.updateVersion": "Atualizar {{version}}",
|
||||
|
||||
"common.closeTab": "Fechar aba",
|
||||
"common.dismiss": "Descartar",
|
||||
"common.requestFailed": "Requisição falhou",
|
||||
"common.moreOptions": "Mais opções",
|
||||
"common.learnMore": "Saiba mais",
|
||||
"common.rename": "Renomear",
|
||||
"common.export": "Exportar",
|
||||
"common.reset": "Redefinir",
|
||||
"common.archive": "Arquivar",
|
||||
"common.delete": "Excluir",
|
||||
@@ -705,7 +850,7 @@ export const dict = {
|
||||
"settings.general.row.shell.title": "Shell do terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Escolha o shell usado no terminal. Os shells compatíveis também são usados nas chamadas de ferramentas do agente.",
|
||||
"settings.general.row.shell.autoDefault": "Automático (Padrão)",
|
||||
"settings.general.row.shell.autoDefault": "Automático (padrão)",
|
||||
"settings.general.row.shell.terminalOnly": "somente no terminal",
|
||||
"settings.general.row.appearance.title": "Aparência",
|
||||
"settings.general.row.appearance.description": "Personalize como o OpenCode aparece no seu dispositivo",
|
||||
@@ -738,8 +883,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navegação inferior",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Posicionar a barra de título e as abas da sessão na parte inferior da tela em dispositivos móveis",
|
||||
"settings.general.row.showCustomAgents.title": "Agentes personalizados",
|
||||
"settings.general.row.showCustomAgents.description": "Mostrar o seletor de agentes na área de composição",
|
||||
"settings.general.row.showCustomAgents.title": "Mostrar agente",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Alternar entre agentes na área de composição. Quando oculto, usa o agente Build como padrão.",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resumos de raciocínio",
|
||||
"settings.general.row.reasoningSummaries.description": "Exibir resumos de raciocínio do modelo na linha do tempo",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Expandir partes da ferramenta shell",
|
||||
@@ -822,14 +968,15 @@ export const dict = {
|
||||
"sound.option.yup06": "Sim 06",
|
||||
"settings.general.notifications.agent.title": "Agente",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Mostrar notificação do sistema quando o agente estiver completo ou precisar de atenção",
|
||||
"Mostrar notificação do sistema quando o agente concluir o trabalho ou precisar de atenção",
|
||||
"settings.general.notifications.permissions.title": "Permissões",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Mostrar notificação do sistema quando uma permissão for necessária",
|
||||
"settings.general.notifications.errors.title": "Erros",
|
||||
"settings.general.notifications.errors.description": "Mostrar notificação do sistema quando ocorrer um erro",
|
||||
"settings.general.sounds.agent.title": "Agente",
|
||||
"settings.general.sounds.agent.description": "Reproduzir som quando o agente estiver completo ou precisar de atenção",
|
||||
"settings.general.sounds.agent.description":
|
||||
"Reproduzir som quando o agente concluir o trabalho ou precisar de atenção",
|
||||
"settings.general.sounds.permissions.title": "Permissões",
|
||||
"settings.general.sounds.permissions.description": "Reproduzir som quando uma permissão for necessária",
|
||||
"settings.general.sounds.errors.title": "Erros",
|
||||
@@ -877,8 +1024,7 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "Ler",
|
||||
"settings.permissions.tool.read.description": "Ler um arquivo (corresponde ao caminho do arquivo)",
|
||||
"settings.permissions.tool.edit.title": "Editar",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modificar arquivos, incluindo edições, escritas, patches e multi-edições",
|
||||
"settings.permissions.tool.edit.description": "Modificar arquivos, incluindo edições, escritas e patches",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Corresponder arquivos usando padrões glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -888,7 +1034,7 @@ export const dict = {
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Executar comandos shell",
|
||||
"settings.permissions.tool.task.title": "Tarefa",
|
||||
"settings.permissions.tool.task.description": "Lançar sub-agentes",
|
||||
"settings.permissions.tool.task.description": "Iniciar subagentes",
|
||||
"settings.permissions.tool.skill.title": "Habilidade",
|
||||
"settings.permissions.tool.skill.description": "Carregar uma habilidade por nome",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
@@ -945,7 +1091,7 @@ export const dict = {
|
||||
"common.time.hoursAgo.short": "{{count}}h atrás",
|
||||
"common.time.daysAgo.short": "{{count}}d atrás",
|
||||
"settings.providers.connected.environmentDescription": "Conectado a partir de suas variáveis de ambiente",
|
||||
"settings.providers.custom.description": "Adicionar um provedor compatível com a OpenAI através do URL base.",
|
||||
"settings.providers.custom.description": "Adicionar um provedor compatível com OpenAI por meio da URL base.",
|
||||
|
||||
"app.server.unreachable": "Não foi possível conectar a {{server}}",
|
||||
"app.server.retrying": "Tentando novamente automaticamente...",
|
||||
@@ -961,6 +1107,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} de {{total}} perguntas",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorador de Arquivos",
|
||||
"session.header.reveal.finder": "Revelar no Finder",
|
||||
"session.header.reveal.fileExplorer": "Revelar no Explorador de Arquivos",
|
||||
"session.header.reveal.containingFolder": "Abrir pasta que contém este item",
|
||||
"session.header.open.fileManager": "Gerenciador de Arquivos",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -998,6 +1147,14 @@ export const dict = {
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Heap JS usado vs limite de heap. Apenas Chromium.",
|
||||
"debugBar.mem.tip": "Heap JS usado vs limite de heap. {{used}} de {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Forçar estilos de foco em todos os elementos interativos",
|
||||
"debugBar.focus.on": "LIGADO",
|
||||
"debugBar.focus.off": "DESLIGADO",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "Forçar a direção do layout de todo o aplicativo sem alterar o idioma selecionado",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
|
||||
+199
-42
@@ -1,4 +1,104 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Datoteka",
|
||||
"desktop.menu.edit": "Uredi",
|
||||
"desktop.menu.view": "Prikaz",
|
||||
"desktop.menu.go": "Idi",
|
||||
"desktop.menu.window": "Prozor",
|
||||
"desktop.menu.help": "Pomoć",
|
||||
"desktop.menu.checkForUpdates": "Provjeri ima li ažuriranja...",
|
||||
"desktop.menu.settings": "Postavke",
|
||||
"desktop.menu.reloadWebview": "Ponovo učitaj Webview",
|
||||
"desktop.menu.restart": "Ponovo pokreni",
|
||||
"desktop.menu.exportLogs": "Izvezi zapisnike...",
|
||||
"desktop.menu.newSession": "Nova sesija",
|
||||
"desktop.menu.openProject": "Otvori projekat...",
|
||||
"desktop.menu.newWindow": "Novi prozor",
|
||||
"desktop.menu.closeWindow": "Zatvori prozor",
|
||||
"desktop.menu.undo": "Poništi",
|
||||
"desktop.menu.redo": "Vrati",
|
||||
"desktop.menu.cut": "Izreži",
|
||||
"desktop.menu.copy": "Kopiraj",
|
||||
"desktop.menu.paste": "Zalijepi",
|
||||
"desktop.menu.delete": "Izbriši",
|
||||
"desktop.menu.selectAll": "Odaberi sve",
|
||||
"desktop.menu.toggleSidebar": "Prikaži ili sakrij bočnu traku",
|
||||
"desktop.menu.toggleTerminal": "Prikaži ili sakrij terminal",
|
||||
"desktop.menu.toggleFileTree": "Prikaži ili sakrij stablo datoteka",
|
||||
"desktop.menu.reload": "Ponovo učitaj",
|
||||
"desktop.menu.toggleDeveloperTools": "Prikaži ili sakrij alate za programere",
|
||||
"desktop.menu.actualSize": "Stvarna veličina",
|
||||
"desktop.menu.zoomIn": "Uvećaj",
|
||||
"desktop.menu.zoomOut": "Umanji",
|
||||
"desktop.menu.toggleFullScreen": "Prikaži ili sakrij prikaz preko cijelog ekrana",
|
||||
"desktop.menu.back": "Nazad",
|
||||
"desktop.menu.forward": "Naprijed",
|
||||
"desktop.menu.previousSession": "Prethodna sesija",
|
||||
"desktop.menu.nextSession": "Sljedeća sesija",
|
||||
"desktop.menu.previousProject": "Prethodni projekat",
|
||||
"desktop.menu.nextProject": "Sljedeći projekat",
|
||||
"desktop.menu.minimize": "Minimiziraj",
|
||||
"desktop.menu.maximize": "Maksimiziraj",
|
||||
"desktop.menu.documentation": "Dokumentacija za OpenCode",
|
||||
"desktop.menu.supportForum": "Forum za podršku",
|
||||
"desktop.menu.shareFeedback": "Pošalji povratne informacije",
|
||||
"desktop.menu.reportBug": "Prijavi grešku",
|
||||
"desktop.menu.ariaLabel": "OpenCode meni",
|
||||
|
||||
"desktop.updater.dialog.checkFailed.message": "Provjera ažuriranja nije uspjela.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Greška pri ažuriranju",
|
||||
"desktop.updater.dialog.upToDate.message": "Sve je ažurno.",
|
||||
"desktop.updater.dialog.upToDate.title": "Nema ažuriranja",
|
||||
"desktop.updater.dialog.ready.message":
|
||||
"Ažuriranje {{version}} je preuzeto. Želiš li sada ponovo pokrenuti aplikaciju?",
|
||||
"desktop.updater.dialog.ready.title": "Ažuriranje je spremno",
|
||||
"desktop.updater.dialog.restart": "Ponovo pokreni",
|
||||
"desktop.updater.dialog.later": "Kasnije",
|
||||
|
||||
"desktop.recovery.action.relaunch": "Ponovo pokreni",
|
||||
"desktop.recovery.action.exportLogs": "Izvezi zapisnike",
|
||||
"desktop.recovery.action.keepWaiting": "Nastavi čekati",
|
||||
"desktop.recovery.action.quit": "Izađi",
|
||||
"desktop.recovery.loadFailed": "OpenCode se nije uspio učitati",
|
||||
"desktop.recovery.terminated": "Prozor OpenCode-a neočekivano je zatvoren",
|
||||
"desktop.recovery.unresponsive": "OpenCode ne reaguje",
|
||||
"desktop.recovery.unresponsive.detail": "Možeš ponovo pokrenuti aplikaciju, otvoriti zapisnike ili nastaviti čekati.",
|
||||
"desktop.recovery.loadFailed.detail": "Prozor: {{window}}\nURL: {{url}}\nGreška: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Prozor: {{window}}\nRazlog: {{reason}}\nKod: {{code}}",
|
||||
"desktop.recovery.unknown": "<nepoznato>",
|
||||
|
||||
"desktop.dialog.chooseFolder": "Odaberi fasciklu",
|
||||
"desktop.dialog.chooseFile": "Odaberi datoteku",
|
||||
"desktop.dialog.saveFile": "Sačuvaj datoteku",
|
||||
"desktop.dialog.files": "Datoteke",
|
||||
"desktop.server.local": "Lokalni server",
|
||||
|
||||
"desktop.wsl.error.windowsOnly": "WSL je dostupan samo u Windowsu",
|
||||
"desktop.wsl.error.unavailable": "WSL nije dostupan",
|
||||
"desktop.wsl.error.listInstalled": "Nije uspjelo prikazivanje instaliranih WSL distribucija",
|
||||
"desktop.wsl.error.listOnline": "Nije uspjelo prikazivanje dostupnih WSL distribucija",
|
||||
"desktop.wsl.error.executeDistro": "Nije moguće izvršiti komande u distribuciji",
|
||||
"desktop.wsl.error.installWsl": "Instalacija WSL-a nije uspjela",
|
||||
"desktop.wsl.error.installDistro": "Instalacija distribucije nije uspjela: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "Instalacija OpenCode-a nije uspjela",
|
||||
"desktop.wsl.error.alreadyAdded": "Distribucija {{distro}} je već dodana",
|
||||
"desktop.wsl.error.opencodeMissing": "opencode nije instaliran u ovoj distribuciji",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode je instaliran, ali se ne može pokrenuti",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode nije instaliran u distribuciji {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"Ažuriranje OpenCode-a je završeno, ali {{distro}} i dalje prijavljuje {{installed}}; očekivano je {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "nema verzije",
|
||||
"desktop.wsl.error.serverExited": "WSL server se zatvorio nakon pokretanja (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"WSL server se zatvorio prije nego što je postao dostupan (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout":
|
||||
"Isteklo je vrijeme provjere dostupnosti komponente Sidecar za {{distro}} nakon {{timeout}} ms",
|
||||
"desktop.wsl.error.commandTimeout": "Isteklo je vrijeme za {{command}} {{args}} nakon {{timeout}} ms",
|
||||
"desktop.wsl.error.failedPort": "Nije uspjelo dohvaćanje porta",
|
||||
|
||||
"desktop.picker.error.notSelected": "Datoteka nije odabrana u izborniku",
|
||||
"desktop.picker.error.sizeLimit": "Odabrani prilozi premašuju ograničenje od {{limit}} MB",
|
||||
|
||||
"command.category.suggested": "Predloženo",
|
||||
"command.category.view": "Prikaz",
|
||||
"command.category.project": "Projekat",
|
||||
@@ -16,6 +116,7 @@ export const dict = {
|
||||
"command.category.permissions": "Dozvole",
|
||||
"command.category.workspace": "Radni prostor",
|
||||
"command.category.settings": "Postavke",
|
||||
"command.logs.export": "Izvezi zapisnike",
|
||||
|
||||
"theme.scheme.system": "Sistem",
|
||||
"theme.scheme.light": "Svijetlo",
|
||||
@@ -92,6 +193,9 @@ export const dict = {
|
||||
"command.session.unshare": "Ukini dijeljenje sesije",
|
||||
"command.session.unshare.description": "Zaustavi dijeljenje ove sesije",
|
||||
|
||||
"command.session.export": "Izvezi sesiju",
|
||||
"command.session.export.description": "Izvezi cijeli zapis sesije u JSON formatu",
|
||||
|
||||
"palette.search.placeholder": "Pretraži datoteke, komande i sesije",
|
||||
"palette.search.placeholder.home": "Pretraži komande i sesije",
|
||||
"palette.empty": "Nema rezultata",
|
||||
@@ -102,6 +206,7 @@ export const dict = {
|
||||
"dialog.provider.empty": "Nema pronađenih provajdera",
|
||||
"dialog.provider.group.popular": "Popularno",
|
||||
"dialog.provider.group.other": "Ostalo",
|
||||
"dialog.provider.custom.label": "Prilagođeni provajder kompatibilan s OpenAI-jem",
|
||||
"dialog.provider.tag.recommended": "Preporučeno",
|
||||
"dialog.provider.opencode.note": "Kurirani modeli uključujući Claude, GPT, Gemini i druge",
|
||||
"dialog.provider.opencode.tagline": "Pouzdani optimizovani modeli",
|
||||
@@ -122,6 +227,7 @@ export const dict = {
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Besplatni modeli koje obezbjeđuje OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Dodaj još modela od popularnih provajdera",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Pogledaj još više od 70 provajdera",
|
||||
|
||||
"dialog.provider.viewAll": "Prikaži više provajdera",
|
||||
|
||||
@@ -129,6 +235,8 @@ export const dict = {
|
||||
"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",
|
||||
"provider.connect.method.headless": "Bez grafičkog interfejsa",
|
||||
"provider.connect.status.inProgress": "Autorizacija je u toku...",
|
||||
"provider.connect.status.waiting": "Čekanje na autorizaciju...",
|
||||
"provider.connect.status.failed": "Autorizacija nije uspjela: {{error}}",
|
||||
@@ -161,6 +269,7 @@ export const dict = {
|
||||
"provider.connect.toast.connected.description": "{{provider}} modeli su sada dostupni za korištenje.",
|
||||
|
||||
"provider.custom.title": "Prilagođeni provajder",
|
||||
"provider.custom.unavailable": "Prilagođeni provajderi nisu dostupni na ovom serveru",
|
||||
"provider.custom.description.prefix": "Konfiguriši OpenAI-kompatibilnog provajdera. Pogledaj ",
|
||||
"provider.custom.description.link": "dokumentaciju za konfiguraciju provajdera",
|
||||
"provider.custom.description.suffix": ".",
|
||||
@@ -232,7 +341,7 @@ export const dict = {
|
||||
"common.clear": "Očisti",
|
||||
"common.connect": "Poveži",
|
||||
"common.disconnect": "Prekini vezu",
|
||||
"common.continue": "Pošalji",
|
||||
"common.continue": "Nastavi",
|
||||
"common.submit": "Pošalji",
|
||||
"common.save": "Sačuvaj",
|
||||
"common.saving": "Čuvanje...",
|
||||
@@ -263,7 +372,7 @@ export const dict = {
|
||||
"prompt.example.11": "Optimizuj upite prema bazi podataka",
|
||||
"prompt.example.12": "Dodaj validaciju ulaza",
|
||||
"prompt.example.13": "Napravi novu komponentu za...",
|
||||
"prompt.example.14": "Kako da deployam ovaj projekat?",
|
||||
"prompt.example.14": "Kako da postavim ovaj projekat?",
|
||||
"prompt.example.15": "Pregledaj moj kod prema najboljim praksama",
|
||||
"prompt.example.16": "Dodaj obradu grešaka u ovu funkciju",
|
||||
"prompt.example.17": "Objasni ovaj regex obrazac",
|
||||
@@ -271,14 +380,14 @@ export const dict = {
|
||||
"prompt.example.19": "Dodaj logovanje kroz cijelu bazu koda",
|
||||
"prompt.example.20": "Koje su zavisnosti zastarjele?",
|
||||
"prompt.example.21": "Pomozi mi da napišem migracijsku skriptu",
|
||||
"prompt.example.22": "Implementiraj keširanje za ovaj endpoint",
|
||||
"prompt.example.22": "Implementiraj keširanje za ovu krajnju tačku",
|
||||
"prompt.example.23": "Dodaj paginaciju u ovu listu",
|
||||
"prompt.example.24": "Napravi CLI komandu za...",
|
||||
"prompt.example.25": "Kako ovdje rade varijable okruženja?",
|
||||
|
||||
"prompt.popover.emptyResults": "Nema rezultata",
|
||||
"prompt.popover.emptyCommands": "Nema komandi",
|
||||
"prompt.dropzone.label": "Ovdje prevucite slike, PDF-ove ili tekstualne datoteke",
|
||||
"prompt.dropzone.label": "Ovdje prevuci slike, PDF-ove ili tekstualne datoteke",
|
||||
"prompt.dropzone.file.label": "Spusti za @spominjanje datoteke",
|
||||
"prompt.slash.badge.custom": "prilagođeno",
|
||||
"prompt.slash.badge.skill": "skill",
|
||||
@@ -287,7 +396,7 @@ export const dict = {
|
||||
"prompt.context.includeActiveFile": "Uključi aktivnu datoteku",
|
||||
"prompt.context.removeActiveFile": "Ukloni aktivnu datoteku iz konteksta",
|
||||
"prompt.context.removeFile": "Ukloni datoteku iz konteksta",
|
||||
"prompt.action.attachFile": "Priloži datoteku",
|
||||
"prompt.action.attachFile": "Dodaj datoteke",
|
||||
"prompt.menu.addImagesAndFiles": "Dodaj datoteke i više",
|
||||
"prompt.menu.imagesAndFiles": "Slike i datoteke",
|
||||
"prompt.menu.commands": "Komande",
|
||||
@@ -298,6 +407,7 @@ export const dict = {
|
||||
"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.",
|
||||
@@ -310,7 +420,7 @@ export const dict = {
|
||||
|
||||
"dialog.mcp.title": "MCP-ovi",
|
||||
"dialog.mcp.description": "{{enabled}} od {{total}} omogućeno",
|
||||
"dialog.mcp.empty": "Nema konfigurisnih MCP-ova",
|
||||
"dialog.mcp.empty": "Nema konfigurisanih MCP-ova",
|
||||
|
||||
"dialog.lsp.empty": "LSP-ovi se automatski otkrivaju prema tipu datoteke",
|
||||
"dialog.plugins.empty": "Plugini su konfigurisani u opencode.json",
|
||||
@@ -318,18 +428,18 @@ export const dict = {
|
||||
"mcp.status.connected": "povezano",
|
||||
"mcp.status.failed": "neuspjelo",
|
||||
"mcp.status.needs_auth": "potrebna autentifikacija",
|
||||
"mcp.auth.clickToAuthenticate": "Kliknite za autentifikaciju",
|
||||
"mcp.auth.clickToAuthenticate": "Klikni za autentifikaciju",
|
||||
"mcp.status.disabled": "onemogućeno",
|
||||
|
||||
"dialog.fork.empty": "Nema poruka za fork",
|
||||
|
||||
"dialog.directory.search.placeholder": "Pretraži foldere",
|
||||
"dialog.directory.empty": "Nema pronađenih foldera",
|
||||
"dialog.directory.search.placeholder": "Pretraži fascikle",
|
||||
"dialog.directory.empty": "Nema pronađenih fascikli",
|
||||
"dialog.directory.action.selectFile": "Odaberi datoteku",
|
||||
"dialog.directory.action.selectFolder": "Odaberi folder",
|
||||
"dialog.directory.action.selectFolder": "Odaberi fasciklu",
|
||||
"dialog.directory.root": "Korijen",
|
||||
"dialog.directory.parent": "Nadređeni folder",
|
||||
"dialog.directory.readError": "Nije moguće pročitati ovaj folder",
|
||||
"dialog.directory.parent": "Nadređena fascikla",
|
||||
"dialog.directory.readError": "Nije moguće pročitati ovu fasciklu",
|
||||
|
||||
"dialog.server.title": "Serveri",
|
||||
"dialog.server.description": "Promijeni na koji se OpenCode server ova aplikacija povezuje.",
|
||||
@@ -348,7 +458,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Uredi server",
|
||||
"dialog.server.default.title": "Podrazumijevani server",
|
||||
"dialog.server.default.description":
|
||||
"Poveži se na ovaj server pri pokretanju aplikacije umjesto pokretanja lokalnog servera. Potreban je restart.",
|
||||
"Poveži se na ovaj server pri pokretanju aplikacije umjesto pokretanja lokalnog servera. Potrebno je ponovo pokrenuti aplikaciju.",
|
||||
"dialog.server.default.none": "Nije odabran server",
|
||||
"dialog.server.default.set": "Postavi trenutni server kao podrazumijevani",
|
||||
"dialog.server.default.clear": "Očisti",
|
||||
@@ -430,6 +540,22 @@ export const dict = {
|
||||
"wsl.onboarding.versionMismatch": "Instalirana verzija ne odgovara verziji desktop aplikacije.",
|
||||
"wsl.onboarding.adding": "Dodavanje...",
|
||||
|
||||
"help.tabs.toast.ariaLabel": "Predstavljamo kartice. Organizuj svoj rad i aktivne sesije pomoću kartica",
|
||||
"help.tabs.toast.dismiss": "Odbaci informacije o karticama",
|
||||
"help.tabs.title": "Predstavljamo kartice",
|
||||
"help.tabs.description": "Organizuj svoj rad i aktivne sesije pomoću kartica",
|
||||
"help.tabs.date": "14. juli",
|
||||
"help.tabs.introduction": "OpenCode Desktop sada je organizovan oko kartica.",
|
||||
"help.tabs.sessions":
|
||||
"Započni novu sesiju u kartici ili otvori postojeću sesiju iz bilo kojeg projekta. Otvori novu karticu kada započinješ nešto novo, a zatvori je kada završiš.",
|
||||
"help.tabs.organize":
|
||||
"Nekoliko otvorenih kartica olakšava organizovanje aktivnih sesija. Preimenuj kartice tako da ih možeš lako prepoznati ako ih namjeravaš zadržati.",
|
||||
"help.tabs.home":
|
||||
"Sve svoje sesije i projekte pronaći ćeš na novom početnom ekranu. Odabirom sesije ona se otvara u kartici.",
|
||||
"help.tabs.persistence": "Kada ponovo otvoriš aplikaciju, tvoje kartice ostaju otvorene.",
|
||||
"help.tabs.worktrees":
|
||||
"Novi dizajn još ne podržava Git Worktrees, ali podrška stiže uskoro. Ako želiš nastaviti koristiti prethodni raspored, možeš mijenjati rasporede u Postavkama. Imaj na umu da će novi raspored za nekoliko sedmica postati trajan.",
|
||||
|
||||
"dialog.project.edit.title": "Uredi projekat",
|
||||
"dialog.project.edit.name": "Naziv",
|
||||
"dialog.project.edit.icon": "Ikonica",
|
||||
@@ -442,9 +568,11 @@ export const dict = {
|
||||
"dialog.project.edit.worktree.startup.description": "Pokreće se nakon kreiranja novog radnog prostora (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "npr. bun install",
|
||||
|
||||
"dialog.usageExceeded.dontShowAgain": "Nemoj više prikazivati",
|
||||
|
||||
"context.breakdown.title": "Razlaganje konteksta",
|
||||
"context.breakdown.note":
|
||||
'Približna raspodjela ulaznih tokena. "Ostalo" uključuje definicije alata i dodatni overhead.',
|
||||
'Približna raspodjela ulaznih tokena. "Ostalo" uključuje definicije alata i dodatno opterećenje.',
|
||||
"context.breakdown.system": "Sistem",
|
||||
"context.breakdown.user": "Korisnik",
|
||||
"context.breakdown.assistant": "Asistent",
|
||||
@@ -454,6 +582,8 @@ export const dict = {
|
||||
"context.systemPrompt.title": "Sistemski prompt",
|
||||
"context.rawMessages.title": "Sirove poruke",
|
||||
|
||||
"context.export.session": "Izvezi sesiju",
|
||||
|
||||
"context.stats.session": "Sesija",
|
||||
"context.stats.messages": "Poruke",
|
||||
"context.stats.provider": "Provajder",
|
||||
@@ -464,7 +594,7 @@ export const dict = {
|
||||
"context.stats.inputTokens": "Ulazni tokeni",
|
||||
"context.stats.outputTokens": "Izlazni tokeni",
|
||||
"context.stats.reasoningTokens": "Tokeni za rasuđivanje",
|
||||
"context.stats.cacheTokens": "Cache tokeni (čitanje/pisanje)",
|
||||
"context.stats.cacheTokens": "Keširani tokeni (čitanje/pisanje)",
|
||||
"context.stats.userMessages": "Korisničke poruke",
|
||||
"context.stats.assistantMessages": "Poruke asistenta",
|
||||
"context.stats.totalCost": "Ukupni trošak",
|
||||
@@ -532,18 +662,23 @@ export const dict = {
|
||||
"toast.session.unshare.failed.title": "Neuspjelo ukidanje dijeljenja",
|
||||
"toast.session.unshare.failed.description": "Došlo je do greške prilikom ukidanja dijeljenja",
|
||||
|
||||
"toast.session.export.success.title": "Sesija izvezena",
|
||||
"toast.session.export.success.description": "Sesija je sačuvana kao {{filename}}",
|
||||
"toast.session.export.failed.title": "Izvoz sesije nije uspio",
|
||||
"toast.session.export.failed.description": "Došlo je do greške prilikom izvoza sesije",
|
||||
|
||||
"toast.session.listFailed.title": "Neuspjelo učitavanje sesija za {{project}}",
|
||||
|
||||
"toast.update.title": "Dostupno ažuriranje",
|
||||
"toast.update.description": "Nova verzija OpenCode-a ({{version}}) je dostupna za instalaciju.",
|
||||
"toast.update.action.installRestart": "Instaliraj i restartuj",
|
||||
"toast.update.action.installRestart": "Instaliraj i ponovo pokreni",
|
||||
"toast.update.action.notYet": "Ne još",
|
||||
|
||||
"error.page.title": "Nešto je pošlo po zlu",
|
||||
"error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.",
|
||||
"error.page.description.localServerStartup": "Došlo je do greške pri pokretanju lokalnog servera.",
|
||||
"error.page.details.label": "Detalji greške",
|
||||
"error.page.action.restart": "Restartuj",
|
||||
"error.page.action.restart": "Ponovo pokreni",
|
||||
"error.page.action.report": "Prijavi grešku",
|
||||
"error.page.action.reported": "Greška prijavljena",
|
||||
"error.page.action.exportLogs": "Izvezi logove",
|
||||
@@ -568,7 +703,7 @@ export const dict = {
|
||||
"error.chain.responseBody": "Tijelo odgovora:\n{{body}}",
|
||||
"error.chain.didYouMean": "Da li si mislio: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model nije pronađen: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Provjeri konfiguraciju (opencode.json) provider/model names",
|
||||
"error.chain.checkConfig": "Provjeri nazive provajdera/modela u konfiguraciji (opencode.json)",
|
||||
"error.chain.mcpFailed": 'MCP server "{{name}}" nije uspio. Napomena: OpenCode još ne podržava MCP autentifikaciju.',
|
||||
"error.chain.providerAuthFailed": "Autentifikacija provajdera nije uspjela ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
@@ -610,7 +745,7 @@ export const dict = {
|
||||
"home.sessions.group.yesterday": "Jučer",
|
||||
"home.sessions.group.older": "Starije",
|
||||
"home.providerTip":
|
||||
"Povežite se s više od 75 pružalaca usluga kako biste koristili druge modele, uključujući Claude, GPT, Gemini i druge",
|
||||
"Poveži se s više od 75 provajdera da koristiš druge modele, uključujući Claude, GPT, Gemini i druge",
|
||||
|
||||
"session.tab.session": "Sesija",
|
||||
"session.tab.review": "Pregled",
|
||||
@@ -626,11 +761,11 @@ export const dict = {
|
||||
"session.review.change.other": "Izmjene",
|
||||
"session.review.loadingChanges": "Učitavanje izmjena...",
|
||||
"session.review.empty": "Još nema izmjena u ovoj sesiji",
|
||||
"session.review.noVcs": "Nije detektovan Git sistem kontrole verzija, promjene se ne prikazuju",
|
||||
"session.review.noVcs": "Nije otkriven Git sistem za kontrolu verzija, pa se promjene ne prikazuju",
|
||||
"session.review.noSnapshot":
|
||||
"Praćenje snimaka (snapshot) je onemogućeno u konfiguraciji, pa promjene sesije nisu dostupne",
|
||||
"session.review.noChanges": "Nema izmjena",
|
||||
"session.review.noUncommittedChanges": "Još nema nepotvrđenih promjena",
|
||||
"session.review.noUncommittedChanges": "Još nema promjena koje nisu commitovane",
|
||||
"session.review.noBranchChanges": "Još nema promjena na grani",
|
||||
|
||||
"session.files.selectToOpen": "Odaberi datoteku za otvaranje",
|
||||
@@ -651,14 +786,17 @@ export const dict = {
|
||||
"session.question.minimize": "Minimiziraj pitanje",
|
||||
"session.question.restore": "Vrati pitanje",
|
||||
"session.question.pending.one": "{{count}} pitanje na čekanju",
|
||||
"session.question.pending.few": "{{count}} pitanja na čekanju",
|
||||
"session.question.pending.other": "{{count}} pitanja na čekanju",
|
||||
"session.followupDock.summary.one": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.summary.few": "{{count}} poruke na čekanju",
|
||||
"session.followupDock.summary.other": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.sendNow": "Pošalji sada",
|
||||
"session.followupDock.edit": "Uredi",
|
||||
"session.followupDock.collapse": "Sažmi poruke na čekanju",
|
||||
"session.followupDock.expand": "Proširi poruke na čekanju",
|
||||
"session.revertDock.summary.one": "{{count}} vraćena poruka",
|
||||
"session.revertDock.summary.few": "{{count}} vraćene poruke",
|
||||
"session.revertDock.summary.other": "{{count}} vraćenih poruka",
|
||||
"session.revertDock.collapse": "Sažmi vraćene poruke",
|
||||
"session.revertDock.expand": "Proširi vraćene poruke",
|
||||
@@ -716,7 +854,13 @@ export const dict = {
|
||||
"terminal.close": "Zatvori terminal",
|
||||
"terminal.connectionLost.title": "Veza prekinuta",
|
||||
"terminal.connectionLost.description":
|
||||
"Veza s terminalom je prekinuta. Ovo se može desiti kada se server restartuje.",
|
||||
"Veza s terminalom je prekinuta. Ovo se može desiti kada se server ponovo pokrene.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY tiket za povezivanje odbijen je zbog provjera porijekla ili CSRF-a. Provjeri CORS konfiguraciju servera.",
|
||||
"terminal.connectTicket.statusError": "PTY tiket za povezivanje nije uspio sa statusom {{status}}",
|
||||
|
||||
"titlebar.update": "Ažuriraj",
|
||||
"titlebar.updateVersion": "Ažuriraj {{version}}",
|
||||
|
||||
"common.closeTab": "Zatvori karticu",
|
||||
"common.dismiss": "Odbaci",
|
||||
@@ -724,7 +868,8 @@ export const dict = {
|
||||
"common.moreOptions": "Više opcija",
|
||||
"common.learnMore": "Saznaj više",
|
||||
"common.rename": "Preimenuj",
|
||||
"common.reset": "Resetuj",
|
||||
"common.export": "Izvezi",
|
||||
"common.reset": "Vrati na početno stanje",
|
||||
"common.archive": "Arhiviraj",
|
||||
"common.delete": "Izbriši",
|
||||
"common.close": "Zatvori",
|
||||
@@ -781,7 +926,7 @@ export const dict = {
|
||||
"settings.general.row.font.title": "Font za kod",
|
||||
"settings.general.row.font.description": "Prilagodi font koji se koristi u blokovima koda",
|
||||
"settings.general.row.terminalFont.title": "Font terminala",
|
||||
"settings.general.row.terminalFont.description": "Prilagodite font koji se koristi u terminalu",
|
||||
"settings.general.row.terminalFont.description": "Prilagodi font koji se koristi u terminalu",
|
||||
"settings.general.row.uiFont.title": "UI font",
|
||||
"settings.general.row.uiFont.description": "Prilagodi font koji se koristi u cijelom interfejsu",
|
||||
"settings.general.row.followup.title": "Ponašanje nadovezivanja",
|
||||
@@ -816,15 +961,16 @@ export const dict = {
|
||||
"settings.general.row.newInterface.title": "Novi raspored",
|
||||
"settings.general.row.newInterface.badge": "Novo",
|
||||
"settings.general.row.newInterface.description":
|
||||
"Koristite nove kartice i raspored početne stranice. Ograničeno vrijeme možete se prebacivati između rasporeda.",
|
||||
"settings.general.row.newInterfaceNotice.title": "Sada koristite novi raspored",
|
||||
"Koristi nove kartice i raspored početne stranice. Ograničeno vrijeme možeš se prebacivati između rasporeda.",
|
||||
"settings.general.row.newInterfaceNotice.title": "Sada koristiš novi raspored",
|
||||
"settings.general.row.newInterfaceNotice.description": "Prethodni raspored više nije dostupan",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Odbaci",
|
||||
"settings.general.row.pinchZoom.title": "Zumiranje štipanjem",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Dozvoli zumiranje gestom štipanja na dodirnoj ploči i pomoću Ctrl-pomjeranja",
|
||||
"settings.general.row.wayland.title": "Koristi nativni Wayland",
|
||||
"settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.",
|
||||
"settings.general.row.wayland.description":
|
||||
"Onemogući rezervni X11 način rada na Waylandu. Zahtijeva ponovno pokretanje.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Na Linuxu sa monitorima miješanih stopa osvježavanja, nativni Wayland može biti stabilniji.",
|
||||
|
||||
@@ -940,7 +1086,7 @@ export const dict = {
|
||||
"settings.mcp.description": "MCP postavke će se ovdje moći podešavati.",
|
||||
|
||||
"settings.permissions.title": "Dozvole",
|
||||
"settings.permissions.description": "Kontroliši koje alate server smije koristiti po defaultu.",
|
||||
"settings.permissions.description": "Kontroliši koje alate server smije koristiti zadano.",
|
||||
"settings.permissions.section.tools": "Alati",
|
||||
"settings.permissions.toast.updateFailed.title": "Neuspjelo ažuriranje dozvola",
|
||||
|
||||
@@ -952,7 +1098,7 @@ export const dict = {
|
||||
"settings.permissions.tool.read.description": "Čitanje datoteke (podudara se s putanjom datoteke)",
|
||||
"settings.permissions.tool.edit.title": "Uređivanje",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Mijenjanje datoteka, uključujući izmjene, pisanja, patch-eve i multi-izmjene",
|
||||
"Mijenjanje datoteka, uključujući uređivanje, pisanje, zakrpe i višestruke izmjene",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Podudaranje datoteka pomoću glob šablona",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -988,26 +1134,26 @@ export const dict = {
|
||||
"workspace.type.sandbox": "sandbox",
|
||||
"workspace.create.failed.title": "Neuspješno kreiranje radnog prostora",
|
||||
"workspace.delete.failed.title": "Neuspješno brisanje radnog prostora",
|
||||
"workspace.resetting.title": "Resetovanje radnog prostora",
|
||||
"workspace.resetting.title": "Vraćanje radnog prostora na početno stanje",
|
||||
"workspace.resetting.description": "Ovo može potrajati minut.",
|
||||
"workspace.reset.failed.title": "Neuspješno resetovanje radnog prostora",
|
||||
"workspace.reset.success.title": "Radni prostor resetovan",
|
||||
"workspace.reset.failed.title": "Neuspješno vraćanje radnog prostora na početno stanje",
|
||||
"workspace.reset.success.title": "Radni prostor vraćen na početno stanje",
|
||||
"workspace.reset.success.description": "Radni prostor sada odgovara podrazumijevanoj grani.",
|
||||
"workspace.error.stillPreparing": "Radni prostor se još priprema",
|
||||
"workspace.status.checking": "Provjera neobjedinjenih promjena...",
|
||||
"workspace.status.checking": "Provjera promjena koje nisu commitovane...",
|
||||
"workspace.status.error": "Nije moguće provjeriti git status.",
|
||||
"workspace.status.clean": "Nisu pronađene neobjedinjene promjene.",
|
||||
"workspace.status.dirty": "Pronađene su neobjedinjene promjene u ovom radnom prostoru.",
|
||||
"workspace.status.clean": "Nisu pronađene promjene koje nisu commitovane.",
|
||||
"workspace.status.dirty": "Pronađene su promjene koje nisu commitovane u ovom radnom prostoru.",
|
||||
"workspace.delete.title": "Izbriši radni prostor",
|
||||
"workspace.delete.confirm": 'Izbriši radni prostor "{{name}}"?',
|
||||
"workspace.delete.button": "Izbriši radni prostor",
|
||||
"workspace.reset.title": "Resetuj radni prostor",
|
||||
"workspace.reset.confirm": 'Resetuj radni prostor "{{name}}"?',
|
||||
"workspace.reset.button": "Resetuj radni prostor",
|
||||
"workspace.reset.title": "Vrati radni prostor na početno stanje",
|
||||
"workspace.reset.confirm": 'Vrati radni prostor "{{name}}" na početno stanje?',
|
||||
"workspace.reset.button": "Vrati radni prostor na početno stanje",
|
||||
"workspace.reset.archived.none": "Nijedna aktivna sesija neće biti arhivirana.",
|
||||
"workspace.reset.archived.one": "1 sesija će biti arhivirana.",
|
||||
"workspace.reset.archived.many": "Biće arhivirano {{count}} sesija.",
|
||||
"workspace.reset.note": "Ovo će resetovati radni prostor da odgovara podrazumijevanoj grani.",
|
||||
"workspace.reset.note": "Ovo će vratiti radni prostor na stanje podrazumijevane grane.",
|
||||
"common.open": "Otvori",
|
||||
"dialog.releaseNotes.action.getStarted": "Započni",
|
||||
"dialog.releaseNotes.action.next": "Sljedeće",
|
||||
@@ -1020,8 +1166,8 @@ export const dict = {
|
||||
"common.time.minutesAgo.short": "prije {{count}} min",
|
||||
"common.time.hoursAgo.short": "prije {{count}} h",
|
||||
"common.time.daysAgo.short": "prije {{count}} d",
|
||||
"settings.providers.connected.environmentDescription": "Povezano sa vašim varijablama okruženja",
|
||||
"settings.providers.custom.description": "Dodajte provajdera kompatibilnog s OpenAI putem osnovnog URL-a.",
|
||||
"settings.providers.connected.environmentDescription": "Povezano s tvojim varijablama okruženja",
|
||||
"settings.providers.custom.description": "Dodaj provajdera kompatibilnog s OpenAI putem baznog URL-a.",
|
||||
|
||||
"app.server.unreachable": "Nije moguće pristupiti {{server}}",
|
||||
"app.server.retrying": "Automatski ponovni pokušaj...",
|
||||
@@ -1030,13 +1176,16 @@ export const dict = {
|
||||
"dialog.server.add.passwordPlaceholder": "lozinka",
|
||||
"server.row.noUsername": "nema korisničkog imena",
|
||||
"session.review.noVcs.createGit.title": "Kreiraj Git repozitorij",
|
||||
"session.review.noVcs.createGit.description": "Pratite, pregledajte i poništite promjene u ovom projektu",
|
||||
"session.review.noVcs.createGit.description": "Prati, pregledaj i poništi promjene u ovom projektu",
|
||||
"session.review.noVcs.createGit.actionLoading": "Kreiranje Git repozitorija...",
|
||||
"session.review.noVcs.createGit.action": "Kreiraj Git repozitorij",
|
||||
"session.todo.progress": "{{done}} od {{total}} zadataka završeno",
|
||||
"session.question.progress": "{{current}} od {{total}} pitanja",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.reveal.finder": "Prikaži u Finderu",
|
||||
"session.header.reveal.fileExplorer": "Prikaži u File Exploreru",
|
||||
"session.header.reveal.containingFolder": "Otvori fasciklu koja sadrži ovu stavku",
|
||||
"session.header.open.fileManager": "File Manager",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -1074,6 +1223,14 @@ export const dict = {
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Korišteni JS heap naspram limita heapa. Samo Chromium.",
|
||||
"debugBar.mem.tip": "Korišteni JS heap naspram limita heapa. {{used}} od {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Prisilno primijeni stilove fokusa na sve interaktivne elemente",
|
||||
"debugBar.focus.on": "UKLJUČENO",
|
||||
"debugBar.focus.off": "ISKLJUČENO",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "Prisilno postavi smjer rasporeda cijele aplikacije bez promjene odabranog jezika",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
|
||||
@@ -0,0 +1,1173 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Fitxer",
|
||||
"desktop.menu.edit": "Edita",
|
||||
"desktop.menu.view": "Veure",
|
||||
"desktop.menu.go": "Vés",
|
||||
"desktop.menu.window": "Finestra",
|
||||
"desktop.menu.help": "Ajuda",
|
||||
"desktop.menu.checkForUpdates": "Comproveu si hi ha actualitzacions...",
|
||||
"desktop.menu.settings": "Configuració",
|
||||
"desktop.menu.reloadWebview": "Torna a carregar Webview",
|
||||
"desktop.menu.restart": "Reinicieu",
|
||||
"desktop.menu.exportLogs": "Exporta registres...",
|
||||
"desktop.menu.newSession": "Nova sessió",
|
||||
"desktop.menu.openProject": "Projecte obert...",
|
||||
"desktop.menu.newWindow": "Finestra nova",
|
||||
"desktop.menu.closeWindow": "Tanca la finestra",
|
||||
"desktop.menu.undo": "Desfer",
|
||||
"desktop.menu.redo": "Refer",
|
||||
"desktop.menu.cut": "Tallar",
|
||||
"desktop.menu.copy": "Còpia",
|
||||
"desktop.menu.paste": "Enganxa",
|
||||
"desktop.menu.delete": "Suprimeix",
|
||||
"desktop.menu.selectAll": "Seleccioneu Tot",
|
||||
"desktop.menu.toggleSidebar": "Commuta la barra lateral",
|
||||
"desktop.menu.toggleTerminal": "Commuta el terminal",
|
||||
"desktop.menu.toggleFileTree": "Commuta l'arbre de fitxers",
|
||||
"desktop.menu.reload": "Torna a carregar",
|
||||
"desktop.menu.toggleDeveloperTools": "Alterna Eines per a desenvolupadors",
|
||||
"desktop.menu.actualSize": "Mida real",
|
||||
"desktop.menu.zoomIn": "Apropa",
|
||||
"desktop.menu.zoomOut": "Allunya el zoom",
|
||||
"desktop.menu.toggleFullScreen": "Commuta la pantalla completa",
|
||||
"desktop.menu.back": "Enrere",
|
||||
"desktop.menu.forward": "Endavant",
|
||||
"desktop.menu.previousSession": "Sessió anterior",
|
||||
"desktop.menu.nextSession": "Propera sessió",
|
||||
"desktop.menu.previousProject": "Projecte anterior",
|
||||
"desktop.menu.nextProject": "Projecte següent",
|
||||
"desktop.menu.minimize": "Minimitzar",
|
||||
"desktop.menu.maximize": "Maximitzar",
|
||||
"desktop.menu.documentation": "OpenCode Documentació",
|
||||
"desktop.menu.supportForum": "Fòrum de suport",
|
||||
"desktop.menu.shareFeedback": "Comparteix comentaris",
|
||||
"desktop.menu.reportBug": "Informar d'un error",
|
||||
"desktop.menu.ariaLabel": "menú OpenCode.",
|
||||
"desktop.updater.dialog.checkFailed.message": "La comprovació d'actualització ha fallat.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Error d'actualització",
|
||||
"desktop.updater.dialog.upToDate.message": "Estàs al dia.",
|
||||
"desktop.updater.dialog.upToDate.title": "No hi ha actualitzacions",
|
||||
"desktop.updater.dialog.ready.message": "Actualització {{version}} baixada. Reiniciar ara?",
|
||||
"desktop.updater.dialog.ready.title": "Actualització llesta",
|
||||
"desktop.updater.dialog.restart": "Reinicieu",
|
||||
"desktop.updater.dialog.later": "Més tard",
|
||||
"desktop.recovery.action.relaunch": "Relançar",
|
||||
"desktop.recovery.action.exportLogs": "Exporta registres",
|
||||
"desktop.recovery.action.keepWaiting": "Seguiu esperant",
|
||||
"desktop.recovery.action.quit": "Surt",
|
||||
"desktop.recovery.loadFailed": "OpenCode no s'ha pogut carregar",
|
||||
"desktop.recovery.terminated": "La finestra OpenCode s'ha acabat inesperadament",
|
||||
"desktop.recovery.unresponsive": "OpenCode no respon",
|
||||
"desktop.recovery.unresponsive.detail": "Podeu reiniciar l'aplicació, obrir els registres o continuar esperant.",
|
||||
"desktop.recovery.loadFailed.detail": "Finestra: {{window}}\nURL: {{url}}\nError: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Finestra: {{window}}\nMotiu: {{reason}}\nCodi: {{code}}",
|
||||
"desktop.recovery.unknown": "<desconegut>",
|
||||
"desktop.dialog.chooseFolder": "Trieu una carpeta",
|
||||
"desktop.dialog.chooseFile": "Trieu un fitxer",
|
||||
"desktop.dialog.saveFile": "Desa el fitxer",
|
||||
"desktop.dialog.files": "Fitxers",
|
||||
"desktop.server.local": "Servidor local",
|
||||
"desktop.wsl.error.windowsOnly": "WSL només està disponible a Windows",
|
||||
"desktop.wsl.error.unavailable": "WSL no està disponible",
|
||||
"desktop.wsl.error.listInstalled": "No s'ha pogut llistar les WSL distribucions instal·lades",
|
||||
"desktop.wsl.error.listOnline": "No s'han pogut enumerar WSL distribucions en línia",
|
||||
"desktop.wsl.error.executeDistro": "No es poden executar ordres a la distribució",
|
||||
"desktop.wsl.error.installWsl": "La instal·lació de WSL ha fallat",
|
||||
"desktop.wsl.error.installDistro": "No s'ha pogut instal·lar la distribució: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "La instal·lació de OpenCode ha fallat",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} ja està afegit",
|
||||
"desktop.wsl.error.opencodeMissing": "opencode no està instal·lat en aquesta distribució",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode està instal·lat però no s'ha pogut executar",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode no està instal·lat a {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode actualització acabada però {{distro}} encara informa {{installed}}; esperat {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "cap versió",
|
||||
"desktop.wsl.error.serverExited":
|
||||
"El servidor WSL s'ha tancat després d'iniciar-se (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"El servidor WSL s'ha tancat abans d'estar operatiu (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout":
|
||||
"S'ha esgotat el temps d'espera del sidecar per a {{distro}} comprovació de salut després de {{timeout}}ms",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} s'ha esgotat després de {{timeout}}ms",
|
||||
"desktop.wsl.error.failedPort": "No s'ha pogut obtenir el port",
|
||||
"desktop.picker.error.notSelected": "El selector no ha seleccionat el fitxer",
|
||||
"desktop.picker.error.sizeLimit": "Els fitxers adjunts seleccionats superen el límit de {{limit}} MB",
|
||||
"command.category.suggested": "Suggerit",
|
||||
"command.category.view": "Veure",
|
||||
"command.category.project": "Projecte",
|
||||
"command.category.provider": "Proveïdor",
|
||||
"command.category.server": "Servidor",
|
||||
"command.category.session": "Sessió",
|
||||
"command.category.theme": "Tema",
|
||||
"command.category.language": "Llengua",
|
||||
"command.category.file": "Fitxer",
|
||||
"command.category.context": "Context",
|
||||
"command.category.terminal": "Terminal",
|
||||
"command.category.model": "Model",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "Agent",
|
||||
"command.category.permissions": "Permisos",
|
||||
"command.category.workspace": "Espai de treball",
|
||||
"command.category.settings": "Configuració",
|
||||
"command.logs.export": "Exportar registres",
|
||||
"theme.scheme.system": "Sistema",
|
||||
"theme.scheme.light": "Llum",
|
||||
"theme.scheme.dark": "Fosc",
|
||||
"command.sidebar.toggle": "Canvia la barra lateral",
|
||||
"command.project.open": "Projecte obert",
|
||||
"command.project.previous": "Projecte anterior",
|
||||
"command.project.next": "Projecte següent",
|
||||
"command.project.index": "Canvia al projecte {{index}}",
|
||||
"command.provider.connect": "Connecta el proveïdor",
|
||||
"command.server.switch": "Canvia de servidor",
|
||||
"command.settings.open": "Obre la configuració",
|
||||
"command.session.previous": "Sessió anterior",
|
||||
"command.session.next": "Sessió següent",
|
||||
"command.session.previous.unseen": "Sessió anterior sense llegir",
|
||||
"command.session.next.unseen": "Pròxima sessió sense llegir",
|
||||
"command.session.archive": "Sessió d'arxiu",
|
||||
"command.palette": "Paleta de comandaments",
|
||||
"command.theme.cycle": "Temàtica del cicle",
|
||||
"command.theme.set": "Utilitza el tema: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Esquema de colors del cicle",
|
||||
"command.theme.scheme.set": "Utilitza l'esquema de colors: {{scheme}}",
|
||||
"command.language.cycle": "Llenguatge de cicle",
|
||||
"command.language.set": "Utilitza l'idioma: {{language}}",
|
||||
"command.session.new": "Nova sessió",
|
||||
"command.file.open": "Obre el fitxer",
|
||||
"command.tab.close": "Tanca la pestanya",
|
||||
"command.tab.reopenClosed": "Torneu a obrir la pestanya tancada",
|
||||
"command.context.addSelection": "Afegeix la selecció al context",
|
||||
"command.context.addSelection.description": "Afegeix les línies seleccionades del fitxer actual",
|
||||
"command.input.focus": "Entrada de focus",
|
||||
"command.terminal.toggle": "Canvia el terminal",
|
||||
"command.fileTree.toggle": "Commuta l'arbre de fitxers",
|
||||
"command.review.toggle": "Commuta la revisió",
|
||||
"command.terminal.new": "Nou terminal",
|
||||
"command.terminal.new.description": "Creeu una nova pestanya de terminal",
|
||||
"command.steps.toggle": "Commuta els passos",
|
||||
"command.steps.toggle.description": "Mostra o amaga els passos del missatge actual",
|
||||
"command.message.previous": "Missatge anterior",
|
||||
"command.message.previous.description": "Aneu al missatge anterior de l'usuari",
|
||||
"command.message.next": "Següent missatge",
|
||||
"command.message.next.description": "Aneu al següent missatge de l'usuari",
|
||||
"command.model.choose": "Tria el model",
|
||||
"command.model.choose.description": "Seleccioneu un model diferent",
|
||||
"command.mcp.toggle": "Commuta els MCP",
|
||||
"command.mcp.toggle.description": "Commuta els MCP",
|
||||
"command.agent.cycle": "Agent de cicle",
|
||||
"command.agent.cycle.description": "Canvia al següent agent",
|
||||
"command.agent.cycle.reverse": "Cicle l'agent cap enrere",
|
||||
"command.agent.cycle.reverse.description": "Canvia a l'agent anterior",
|
||||
"command.model.variant.cycle": "Esforç de pensar en cicle",
|
||||
"command.model.variant.cycle.description": "Canvia al següent nivell d'esforç",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Indicació",
|
||||
"command.permissions.autoaccept.enable": "Permisos d'acceptació automàtica",
|
||||
"command.permissions.autoaccept.disable": "Atura l'acceptació automàtica de permisos",
|
||||
"command.workspace.toggle": "Commuta els espais de treball",
|
||||
"command.workspace.toggle.description": "Activa o desactiva diversos espais de treball a la barra lateral",
|
||||
"command.session.undo": "Desfer",
|
||||
"command.session.undo.description": "Desfer l'últim missatge",
|
||||
"command.session.redo": "Refer",
|
||||
"command.session.redo.description": "Torna a fer l'últim missatge desfet",
|
||||
"command.session.compact": "Sessió compacta",
|
||||
"command.session.compact.description": "Resumeix la sessió per reduir la mida del context",
|
||||
"command.session.fork": "Bifurcació del missatge",
|
||||
"command.session.fork.description": "Crea una sessió nova a partir d'un missatge anterior",
|
||||
"command.session.share": "Compartir sessió",
|
||||
"command.session.share.description": "Comparteix aquesta sessió i copia el URL al porta-retalls",
|
||||
"command.session.unshare": "Deixa de compartir la sessió",
|
||||
"command.session.unshare.description": "Deixa de compartir aquesta sessió",
|
||||
"command.session.export": "Sessió d'exportació",
|
||||
"command.session.export.description": "Exporta la transcripció completa de la sessió com a JSON",
|
||||
"palette.search.placeholder": "Cerca fitxers, ordres i sessions",
|
||||
"palette.search.placeholder.home": "Cerca ordres i sessions",
|
||||
"palette.empty": "No s'han trobat resultats",
|
||||
"palette.group.commands": "Ordres",
|
||||
"palette.group.files": "Fitxers",
|
||||
"dialog.provider.search.placeholder": "Cerca proveïdors",
|
||||
"dialog.provider.empty": "No s'han trobat proveïdors",
|
||||
"dialog.provider.group.popular": "Popular",
|
||||
"dialog.provider.group.other": "Altres",
|
||||
"dialog.provider.custom.label": "Proveïdor personalitzat compatible amb OpenAI.",
|
||||
"dialog.provider.tag.recommended": "Recomanat",
|
||||
"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",
|
||||
"dialog.provider.openrouter.note": "Accediu a tots els models compatibles des d'un proveïdor",
|
||||
"dialog.provider.vercel.note": "Accés unificat als models d'IA amb enrutament intel·ligent",
|
||||
"dialog.model.select.title": "Seleccioneu el model",
|
||||
"dialog.model.search.placeholder": "Cerca models",
|
||||
"dialog.model.empty": "No hi ha resultats del model",
|
||||
"dialog.model.manage": "Gestionar models",
|
||||
"dialog.model.manage.description": "Personalitza quins models apareixen al selector de models.",
|
||||
"dialog.model.manage.provider.toggle": "Canvia tots els {{provider}} models",
|
||||
"dialog.model.unpaid.freeModels.title": "Models gratuïts proporcionats per OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Afegiu més models de proveïdors populars",
|
||||
"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",
|
||||
"provider.connect.method.headless": "Sense cap",
|
||||
"provider.connect.status.inProgress": "Autorització en curs...",
|
||||
"provider.connect.status.waiting": "Esperant l'autorització...",
|
||||
"provider.connect.status.failed": "L'autorització ha fallat: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Introduïu la clau API de {{provider}} per connectar el compte i utilitzar els models de {{provider}} a OpenCode.",
|
||||
"provider.connect.apiKey.label": "Tecla {{provider}} API.",
|
||||
"provider.connect.apiKey.placeholder": "tecla API.",
|
||||
"provider.connect.apiKey.required": "La clau API és necessària",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen us ofereix accés a un conjunt seleccionat de models optimitzats fiables per a agents de codificació.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Amb una sola tecla API tindreu accés a models com ara Claude, GPT, Gemini, GLM i més.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Visita ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " per recollir la teva clau API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Visita ",
|
||||
"provider.connect.oauth.code.visit.link": "aquest enllaç",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" per recollir el vostre codi d'autorització per connectar el vostre compte i utilitzar {{provider}} models a OpenCode.",
|
||||
"provider.connect.oauth.code.label": "{{method}} codi d'autorització",
|
||||
"provider.connect.oauth.code.placeholder": "Codi d'autorització",
|
||||
"provider.connect.oauth.code.required": "Cal un codi d'autorització",
|
||||
"provider.connect.oauth.code.invalid": "Codi d'autorització no vàlid",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Visita ",
|
||||
"provider.connect.oauth.auto.visit.link": "aquest enllaç",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" i introduïu el codi següent per connectar el vostre compte i utilitzar {{provider}} models a OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Codi de confirmació",
|
||||
"provider.connect.toast.connected.title": "{{provider}} connectat",
|
||||
"provider.connect.toast.connected.description": "{{provider}} ara es poden utilitzar models.",
|
||||
"provider.custom.title": "Proveïdor personalitzat",
|
||||
"provider.custom.unavailable": "Els proveïdors personalitzats no estan disponibles en aquest servidor",
|
||||
"provider.custom.description.prefix": "Configureu un proveïdor compatible amb OpenAI. Veure el ",
|
||||
"provider.custom.description.link": "documents de configuració del proveïdor",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "Proveïdor ID",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "Lletres minúscules, números, guions o guions baixos",
|
||||
"provider.custom.field.name.label": "Nom de visualització",
|
||||
"provider.custom.field.name.placeholder": "El meu proveïdor d'IA",
|
||||
"provider.custom.field.baseURL.label": "URL base",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "tecla API.",
|
||||
"provider.custom.field.apiKey.placeholder": "tecla API.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Opcional. Deixeu-lo en blanc si gestioneu l'autenticació mitjançant les capçaleres.",
|
||||
"provider.custom.models.label": "Models",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
"provider.custom.models.name.label": "Nom",
|
||||
"provider.custom.models.name.placeholder": "Nom de visualització",
|
||||
"provider.custom.models.remove": "Elimina el model",
|
||||
"provider.custom.models.add": "Afegeix un model",
|
||||
"provider.custom.headers.label": "Capçaleres (opcional)",
|
||||
"provider.custom.headers.key.label": "Capçalera",
|
||||
"provider.custom.headers.key.placeholder": "Header-Name",
|
||||
"provider.custom.headers.value.label": "Valor",
|
||||
"provider.custom.headers.value.placeholder": "value",
|
||||
"provider.custom.headers.remove": "Elimina la capçalera",
|
||||
"provider.custom.headers.add": "Afegeix una capçalera",
|
||||
"provider.custom.error.providerID.required": "Es requereix el proveïdor ID.",
|
||||
"provider.custom.error.providerID.format": "Utilitzeu lletres minúscules, números, guions o guions baixos",
|
||||
"provider.custom.error.providerID.exists": "Aquest proveïdor ID ja existeix",
|
||||
"provider.custom.error.name.required": "El nom de visualització és obligatori",
|
||||
"provider.custom.error.baseURL.required": "La base URL és necessària",
|
||||
"provider.custom.error.baseURL.format": "Ha de començar per http:// o https://",
|
||||
"provider.custom.error.required": "Obligatori",
|
||||
"provider.custom.error.duplicate": "Duplicat",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} desconnectat",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} models ja no estan disponibles.",
|
||||
"model.tag.free": "Gratuït",
|
||||
"model.tag.latest": "Última",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "text",
|
||||
"model.input.image": "imatge",
|
||||
"model.input.audio": "àudio",
|
||||
"model.input.video": "vídeo",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Permet: {{inputs}}",
|
||||
"model.tooltip.context.label": "Context",
|
||||
"model.tooltip.inputs": "Entrades",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Proveïdor",
|
||||
"model.tooltip.reasoning": "Raonament",
|
||||
"model.tooltip.reasoning.allowed": "Permet raonar",
|
||||
"model.tooltip.reasoning.none": "Sense raonament",
|
||||
"model.tooltip.context": "Límit de context {{limit}}",
|
||||
"common.search.placeholder": "Cerca",
|
||||
"common.goBack": "Navega enrere",
|
||||
"common.goForward": "Navega cap endavant",
|
||||
"common.loading": "Carregant",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Cancel·la",
|
||||
"common.clear": "Clar",
|
||||
"common.open": "Obert",
|
||||
"common.connect": "Connecta't",
|
||||
"common.disconnect": "Desconnecta",
|
||||
"common.continue": "Continua",
|
||||
"common.submit": "Envia",
|
||||
"common.save": "Desa",
|
||||
"common.saving": "S'està desant...",
|
||||
"common.default": "Per defecte",
|
||||
"common.attachment": "adjunt",
|
||||
"prompt.placeholder.shell": "Introduïu una ordre de l'intèrpret d'ordres... {{example}}",
|
||||
"prompt.placeholder.normal": 'Pregunta qualsevol cosa... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Pregunta qualsevol cosa...",
|
||||
"prompt.placeholder.summarizeComments": "Resum dels comentaris...",
|
||||
"prompt.placeholder.summarizeComment": "Resumeix el comentari...",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Indicació",
|
||||
"prompt.mode.shell.exit": "esc per sortir",
|
||||
"session.child.promptDisabled": "No es poden demanar sessions de subagent.",
|
||||
"session.child.backToParent": "Torna a la sessió principal.",
|
||||
"prompt.example.1": "Arregleu una TODO a la base de codi",
|
||||
"prompt.example.2": "Quina és la pila tecnològica d'aquest projecte?",
|
||||
"prompt.example.3": "Arreglar les proves trencades",
|
||||
"prompt.example.4": "Explica com funciona l'autenticació",
|
||||
"prompt.example.5": "Trobeu i solucioneu les vulnerabilitats de seguretat",
|
||||
"prompt.example.6": "Afegiu proves unitàries per al servei d'usuari",
|
||||
"prompt.example.7": "Refactoritza aquesta funció per ser més llegible",
|
||||
"prompt.example.8": "Què significa aquest error?",
|
||||
"prompt.example.9": "Ajudeu-me a depurar aquest problema",
|
||||
"prompt.example.10": "Genera API documentació",
|
||||
"prompt.example.11": "Optimitzar les consultes a la base de dades",
|
||||
"prompt.example.12": "Afegiu la validació d'entrada",
|
||||
"prompt.example.13": "Crea un component nou per...",
|
||||
"prompt.example.14": "Com implemento aquest projecte?",
|
||||
"prompt.example.15": "Revisa el meu codi per conèixer les pràctiques recomanades",
|
||||
"prompt.example.16": "Afegiu la gestió d'errors a aquesta funció",
|
||||
"prompt.example.17": "Expliqueu aquest patró d'expressió regular",
|
||||
"prompt.example.18": "Converteix això a TypeScript",
|
||||
"prompt.example.19": "Afegiu registres a tota la base de codi",
|
||||
"prompt.example.20": "Quines dependències estan obsoletes?",
|
||||
"prompt.example.21": "Ajudeu-me a escriure un script de migració",
|
||||
"prompt.example.22": "Implementeu la memòria cau per a aquest punt final",
|
||||
"prompt.example.23": "Afegeix la paginació a aquesta llista",
|
||||
"prompt.example.24": "Creeu una ordre CLI per...",
|
||||
"prompt.example.25": "Com funcionen les variables d'entorn aquí?",
|
||||
"prompt.popover.emptyResults": "No hi ha resultats coincidents",
|
||||
"prompt.popover.emptyCommands": "No hi ha ordres coincidents",
|
||||
"prompt.dropzone.label": "Deixa anar imatges, PDFs o fitxers de text aquí",
|
||||
"prompt.dropzone.file.label": "Aneu al fitxer @menció",
|
||||
"prompt.slash.badge.custom": "costum",
|
||||
"prompt.slash.badge.skill": "habilitat",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "actiu",
|
||||
"prompt.context.includeActiveFile": "Inclou el fitxer actiu",
|
||||
"prompt.context.removeActiveFile": "Elimina el fitxer actiu del context",
|
||||
"prompt.context.removeFile": "Elimina el fitxer del context",
|
||||
"prompt.action.attachFile": "Afegeix fitxers",
|
||||
"prompt.menu.addImagesAndFiles": "Afegeix fitxers i més",
|
||||
"prompt.menu.imagesAndFiles": "Imatges i fitxers",
|
||||
"prompt.menu.commands": "Ordres",
|
||||
"prompt.menu.context": "Context",
|
||||
"prompt.menu.shellCommand": "Ordre de l'intèrpret d'ordres",
|
||||
"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.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "No s'ha pogut crear l'arbre de treball",
|
||||
"prompt.toast.sessionCreateFailed.title": "No s'ha pogut crear la sessió",
|
||||
"prompt.toast.shellSendFailed.title": "No s'ha pogut enviar l'ordre del shell",
|
||||
"prompt.toast.commandSendFailed.title": "No s'ha pogut enviar l'ordre",
|
||||
"prompt.toast.promptSendFailed.title": "No s'ha pogut enviar la sol·licitud",
|
||||
"prompt.toast.promptSendFailed.description": "No es pot recuperar la sessió",
|
||||
"dialog.mcp.title": "MCP",
|
||||
"dialog.mcp.description": "{{enabled}} de {{total}} activat",
|
||||
"dialog.mcp.empty": "No s'ha configurat cap MCP",
|
||||
"dialog.lsp.empty": "Els LSP s'han detectat automàticament a partir dels tipus de fitxers",
|
||||
"dialog.plugins.empty": "Connectors configurats a opencode.json",
|
||||
"mcp.status.connected": "connectat",
|
||||
"mcp.status.failed": "fracassat",
|
||||
"mcp.status.needs_auth": "necessita autenticació",
|
||||
"mcp.status.disabled": "discapacitat",
|
||||
"mcp.auth.clickToAuthenticate": "Feu clic per autenticar-vos",
|
||||
"dialog.fork.empty": "No hi ha missatges per bifurcar",
|
||||
"dialog.directory.search.placeholder": "Cerca carpetes",
|
||||
"dialog.directory.empty": "No s'han trobat carpetes",
|
||||
"dialog.directory.action.selectFile": "Seleccioneu el fitxer",
|
||||
"dialog.directory.action.selectFolder": "Seleccioneu la carpeta",
|
||||
"dialog.directory.root": "Arrel",
|
||||
"dialog.directory.parent": "Pare",
|
||||
"dialog.directory.readError": "No es pot llegir aquesta carpeta",
|
||||
"app.server.unreachable": "No s'ha pogut arribar a {{server}}",
|
||||
"app.server.retrying": "S'està tornant a provar automàticament...",
|
||||
"app.server.otherServers": "Altres servidors",
|
||||
"dialog.server.title": "Servidors",
|
||||
"dialog.server.description": "Canvia a quin OpenCode servidor es connecta aquesta aplicació.",
|
||||
"dialog.server.search.placeholder": "Servidors de cerca",
|
||||
"dialog.server.empty": "Encara no hi ha servidors",
|
||||
"dialog.server.add.title": "Afegeix servidor",
|
||||
"dialog.server.add.url": "Adreça del servidor",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "No s'ha pogut connectar al servidor",
|
||||
"dialog.server.add.checking": "S'està comprovant...",
|
||||
"dialog.server.add.button": "Afegeix servidor",
|
||||
"dialog.server.add.name": "Nom del servidor (opcional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nom d'usuari (opcional)",
|
||||
"dialog.server.add.usernamePlaceholder": "nom d'usuari",
|
||||
"dialog.server.add.password": "Contrasenya (opcional)",
|
||||
"dialog.server.add.passwordPlaceholder": "contrasenya",
|
||||
"dialog.server.edit.title": "Edita el servidor",
|
||||
"dialog.server.default.title": "Servidor per defecte",
|
||||
"dialog.server.default.description":
|
||||
"Connecteu-vos a aquest servidor en iniciar l'aplicació en lloc d'iniciar un servidor local. Requereix reinici.",
|
||||
"dialog.server.default.none": "No s'ha seleccionat cap servidor",
|
||||
"dialog.server.default.set": "Estableix el servidor actual com a predeterminat",
|
||||
"dialog.server.default.clear": "Clar",
|
||||
"dialog.server.action.remove": "Elimina el servidor",
|
||||
"dialog.server.menu.edit": "Edita",
|
||||
"dialog.server.menu.default": "Estableix com a predeterminat",
|
||||
"dialog.server.menu.defaultRemove": "Elimina el valor predeterminat",
|
||||
"dialog.server.menu.delete": "Suprimeix",
|
||||
"dialog.server.current": "Servidor actual",
|
||||
"dialog.server.status.default": "Per defecte",
|
||||
"wsl.server.add": "Afegeix el servidor WSL.",
|
||||
"wsl.server.addShort": "Afegeix WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL servidor",
|
||||
"wsl.server.retryStart": "Torna a intentar començar",
|
||||
"wsl.server.updating": "S'està actualitzant...",
|
||||
"wsl.onboarding.step.distro": "Trieu la distribució",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "S'està comprovant WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows necessita un reinici per acabar la instal·lació WSL.",
|
||||
"wsl.onboarding.ready": "WSL està llest.",
|
||||
"wsl.onboarding.required": "WSL és necessari per continuar.",
|
||||
"wsl.onboarding.checkingDistros": "S'estan comprovant les distribucions...",
|
||||
"wsl.onboarding.installingDistro": "S'està instal·lant {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "S'està comprovant {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Llistat de distribucions...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} està llest.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} encara no està instal·lat.",
|
||||
"wsl.onboarding.openDistroOnce": "Obriu {{distro}} una vegada per acabar la configuració.",
|
||||
"wsl.onboarding.finishingDistro": "S'està acabant la configuració de {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Trieu una distribució o instal·leu-ne una a continuació.",
|
||||
"wsl.onboarding.checkingOpencode": "S'està comprovant OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "S'està registrant OpenCode a {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "S'està actualitzant OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "S'està actualitzant OpenCode a {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Actualitza OpenCode a {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Actualització OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode està llest a {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode està llest.",
|
||||
"wsl.onboarding.installOpencodeIn": "Instal·leu OpenCode a {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Instal·la OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Trieu primer una distribució.",
|
||||
"wsl.onboarding.loadFailed": "No s'ha pogut carregar l'estat WSL.",
|
||||
"wsl.onboarding.loading": "Carregant...",
|
||||
"wsl.onboarding.installedDistros": "Distribucions instal·lades",
|
||||
"wsl.onboarding.checkAgain": "Comproveu de nou",
|
||||
"wsl.onboarding.distroStatus.ready": "A punt",
|
||||
"wsl.onboarding.distroStatus.checking": "S'està comprovant...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode no instal·lat",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Falta bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "No s'admet · Utilitzeu WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Necessites una altra distribució?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Instal·leu una distribució Linux del catàleg WSL.",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL no instal·lat",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsistema per a Linux) és necessari abans que OpenCode pugui afegir un WSL servidor",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL no disponible",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode no ha pogut verificar WSL en aquesta màquina.",
|
||||
"wsl.onboarding.installWsl": "Instal·la WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Reinicieu Windows per acabar d'instal·lar WSL i, a continuació, torneu a obrir OpenCode.",
|
||||
"wsl.onboarding.next": "A continuació",
|
||||
"wsl.onboarding.refresh": "Actualitza",
|
||||
"wsl.onboarding.allDistrosAdded": "Totes les distribucions instal·lades ja s'han afegit.",
|
||||
"wsl.onboarding.noDistros": "Encara no s'ha detectat cap distribució.",
|
||||
"wsl.onboarding.install": "Instal·lar",
|
||||
"wsl.onboarding.installing": "S'està instal·lant...",
|
||||
"wsl.onboarding.installDistro": "Instal·leu la distribució",
|
||||
"wsl.onboarding.searchDistros": "Cerca distribucions",
|
||||
"wsl.onboarding.wsl2Required": "WSL Es requereix 2.",
|
||||
"wsl.onboarding.toolsRequired": "Aquesta distribució necessita bash i curl.",
|
||||
"wsl.onboarding.openTerminal": "Terminal obert",
|
||||
"wsl.onboarding.path": "Camí: {{path}}",
|
||||
"wsl.onboarding.notFound": "no trobat",
|
||||
"wsl.onboarding.version": "Versió: {{version}}",
|
||||
"wsl.onboarding.unknown": "desconegut",
|
||||
"wsl.onboarding.desktopVersion": "escriptori {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "La versió instal·lada no coincideix amb la versió de l'aplicació d'escriptori.",
|
||||
"wsl.onboarding.adding": "S'està afegint...",
|
||||
"help.tabs.toast.ariaLabel":
|
||||
"Presentació de les pestanyes. Organitza el teu treball i les sessions actives amb pestanyes",
|
||||
"help.tabs.toast.dismiss": "Ignora la informació de les pestanyes",
|
||||
"help.tabs.title": "Presentació de les pestanyes",
|
||||
"help.tabs.description": "Organitza el teu treball i les sessions actives amb pestanyes",
|
||||
"help.tabs.date": "14 de juliol",
|
||||
"help.tabs.introduction": "OpenCode L'escriptori ara està construït al voltant de pestanyes.",
|
||||
"help.tabs.sessions":
|
||||
"Inicieu una sessió nova en una pestanya o obriu una sessió existent des de qualsevol dels vostres projectes. Obre una pestanya nova quan comencis una cosa nova i tanca-la quan hagis acabat.",
|
||||
"help.tabs.organize":
|
||||
"Mantenir unes quantes pestanyes obertes facilita l'organització de les sessions actives. Canvieu el nom de les pestanyes a alguna cosa memorable si teniu previst mantenir-les.",
|
||||
"help.tabs.home":
|
||||
"Trobareu totes les sessions i els projectes a la nova pantalla d'inici. En seleccionar una sessió, s'obre en una pestanya.",
|
||||
"help.tabs.persistence": "Quan torneu a obrir l'aplicació, les vostres pestanyes encara estan obertes.",
|
||||
"help.tabs.worktrees":
|
||||
"El nou disseny encara no és compatible amb Git Worktrees, arribarà aviat. Per tant, si preferiu continuar utilitzant el disseny anterior, podeu canviar entre els dissenys a Configuració. Tingueu en compte que el nou disseny esdevindrà permanent d'aquí a unes setmanes.",
|
||||
"server.row.noUsername": "sense nom d'usuari",
|
||||
"dialog.project.edit.title": "Edita el projecte",
|
||||
"dialog.project.edit.name": "Nom",
|
||||
"dialog.project.edit.icon": "Icona",
|
||||
"dialog.project.edit.icon.alt": "Icona del projecte",
|
||||
"dialog.project.edit.icon.hint": "Feu clic o arrossegueu una imatge",
|
||||
"dialog.project.edit.icon.recommended": "Recomanat: 128x128px",
|
||||
"dialog.project.edit.color": "Color",
|
||||
"dialog.project.edit.color.select": "Seleccioneu el color {{color}}.",
|
||||
"dialog.project.edit.worktree.startup": "Script d'inici de l'espai de treball",
|
||||
"dialog.project.edit.worktree.startup.description": "S'executa després de crear un nou espai de treball (worktree).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "p. ex. bun install",
|
||||
"dialog.releaseNotes.action.getStarted": "Comença",
|
||||
"dialog.releaseNotes.action.next": "A continuació",
|
||||
"dialog.releaseNotes.action.hideFuture": "No mostris aquestes en el futur",
|
||||
"dialog.releaseNotes.media.alt": "Allibera la vista prèvia",
|
||||
"context.breakdown.title": "Desglossament del context",
|
||||
"context.breakdown.note":
|
||||
"Desglossament aproximat de fitxes d'entrada. \"Altres\" inclou definicions d'eines i despeses generals.",
|
||||
"context.breakdown.system": "Sistema",
|
||||
"context.breakdown.user": "Usuari",
|
||||
"context.breakdown.assistant": "Assistent",
|
||||
"context.breakdown.tool": "Trucades d'eines",
|
||||
"context.breakdown.other": "Altres",
|
||||
"context.systemPrompt.title": "Indicació del sistema",
|
||||
"context.rawMessages.title": "Missatges crus",
|
||||
"context.export.session": "Sessió d'exportació",
|
||||
"context.stats.session": "Sessió",
|
||||
"context.stats.messages": "Missatges",
|
||||
"context.stats.provider": "Proveïdor",
|
||||
"context.stats.model": "Model",
|
||||
"context.stats.limit": "Límit de context",
|
||||
"context.stats.totalTokens": "Total de fitxes",
|
||||
"context.stats.usage": "Ús",
|
||||
"context.stats.inputTokens": "Fitxes d'entrada",
|
||||
"context.stats.outputTokens": "Fitxes de sortida",
|
||||
"context.stats.reasoningTokens": "Fitxes de raonament",
|
||||
"context.stats.cacheTokens": "Fitxes de memòria cau (lectura/escriptura)",
|
||||
"context.stats.userMessages": "Missatges d'usuari",
|
||||
"context.stats.assistantMessages": "Missatges de l'Assistent",
|
||||
"context.stats.totalCost": "Cost total",
|
||||
"context.stats.sessionCreated": "Sessió creada",
|
||||
"context.stats.lastActivity": "Última Activitat",
|
||||
"context.usage.tokens": "Fitxes",
|
||||
"context.usage.usage": "Ús",
|
||||
"context.usage.cost": "Cost",
|
||||
"context.usage.clickToView": "Feu clic per veure el context",
|
||||
"context.usage.view": "Veure l'ús del context",
|
||||
"language.en": "Anglès",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "Alemany",
|
||||
"language.es": "Espanyol",
|
||||
"language.fr": "Francès",
|
||||
"language.da": "Dansk",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Polski",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "Portuguès (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Llengua",
|
||||
"toast.language.description": "S'ha canviat a {{language}}",
|
||||
"toast.theme.title": "S'ha canviat el tema",
|
||||
"toast.scheme.title": "Esquema de colors",
|
||||
"toast.workspace.enabled.title": "Espais de treball habilitats",
|
||||
"toast.workspace.enabled.description": "Ara es mostren diversos arbres de treball a la barra lateral",
|
||||
"toast.workspace.disabled.title": "Espais de treball desactivats",
|
||||
"toast.workspace.disabled.description": "A la barra lateral només es mostra l'arbre de treball principal",
|
||||
"toast.permissions.autoaccept.on.title": "Permisos d'acceptació automàtica",
|
||||
"toast.permissions.autoaccept.on.description": "Les sol·licituds de permís s'aprovaran automàticament",
|
||||
"toast.permissions.autoaccept.off.title": "S'ha aturat l'acceptació automàtica de permisos",
|
||||
"toast.permissions.autoaccept.off.description": "Les sol·licituds de permís requeriran aprovació",
|
||||
"toast.model.none.title": "No s'ha seleccionat cap model",
|
||||
"toast.model.none.description": "Connecteu un proveïdor per resumir aquesta sessió",
|
||||
"toast.file.loadFailed.title": "No s'ha pogut carregar el fitxer",
|
||||
"toast.file.listFailed.title": "No s'han pogut llistar els fitxers",
|
||||
"toast.context.noLineSelection.title": "Sense selecció de línia",
|
||||
"toast.context.noLineSelection.description": "Seleccioneu primer un interval de línies en una pestanya de fitxer.",
|
||||
"toast.session.share.copyFailed.title": "No s'ha pogut copiar URL al porta-retalls",
|
||||
"toast.session.share.success.title": "Sessió compartida",
|
||||
"toast.session.share.success.description": "Comparteix URL copiat al porta-retalls!",
|
||||
"toast.session.share.failed.title": "No s'ha pogut compartir la sessió",
|
||||
"toast.session.share.failed.description": "S'ha produït un error en compartir la sessió",
|
||||
"toast.session.unshare.success.title": "S'ha deixat de compartir la sessió",
|
||||
"toast.session.unshare.success.description": "S'ha deixat de compartir la sessió correctament.",
|
||||
"toast.session.unshare.failed.title": "No s'ha pogut deixar de compartir la sessió",
|
||||
"toast.session.unshare.failed.description": "S'ha produït un error en deixar de compartir la sessió",
|
||||
"toast.session.export.success.title": "Sessió exportada",
|
||||
"toast.session.export.success.description": "Sessió desada a {{filename}}",
|
||||
"toast.session.export.failed.title": "No s'ha pogut exportar la sessió",
|
||||
"toast.session.export.failed.description": "S'ha produït un error en exportar la sessió",
|
||||
"toast.session.listFailed.title": "No s'han pogut carregar les sessions de {{project}}",
|
||||
"toast.project.reloadFailed.title": "No s'ha pogut tornar a carregar {{project}}",
|
||||
"toast.update.title": "Actualització disponible",
|
||||
"toast.update.description": "Una nova versió de OpenCode ({{version}}) ja està disponible per instal·lar-la.",
|
||||
"toast.update.action.installRestart": "Instal·leu i reinicieu",
|
||||
"toast.update.action.notYet": "Encara no",
|
||||
"error.page.title": "Alguna cosa va fallar",
|
||||
"error.page.description": "S'ha produït un error en carregar l'aplicació.",
|
||||
"error.page.description.localServerStartup": "S'ha produït un error en iniciar el servidor local.",
|
||||
"error.page.details.label": "Detalls de l'error",
|
||||
"error.page.action.restart": "Reinicieu",
|
||||
"error.page.action.report": "Informa d'un error",
|
||||
"error.page.action.reported": "S'ha informat d'un error",
|
||||
"error.page.action.exportLogs": "Exporta registres",
|
||||
"error.page.action.checking": "S'està comprovant...",
|
||||
"error.page.action.checkUpdates": "Comproveu si hi ha actualitzacions",
|
||||
"error.page.action.updateTo": "Actualitza a {{version}}",
|
||||
"error.page.circular": "[Circular]",
|
||||
"error.page.report.prefix": "Informeu aquest error a l'equip OpenCode.",
|
||||
"error.page.report.discord": "a Discord",
|
||||
"error.page.version": "Versió: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"No s'ha trobat l'element arrel. T'has oblidat d'afegir-lo al teu index.html? O potser l'atribut id s'ha escrit malament?",
|
||||
"error.serverSync.connectFailed":
|
||||
"No s'ha pogut connectar al servidor. Hi ha un servidor en funcionament a `{{url}}`?",
|
||||
"error.serverSDK.noServerAvailable": "No hi ha servidor disponible",
|
||||
"error.serverSDK.serverNotAvailable": "Servidor no disponible",
|
||||
"error.childStore.persistedCacheCreateFailed": "No s'ha pogut crear la memòria cau persistent",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "No s'han pogut crear metadades del projecte persistents",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "No s'ha pogut crear la icona del projecte persistent",
|
||||
"error.childStore.storeCreateFailed": "No s'ha pogut crear la botiga",
|
||||
"directory.error.invalidUrl": "Directori no vàlid a URL.",
|
||||
"error.chain.unknown": "Error desconegut",
|
||||
"error.server.invalidConfiguration": "Configuració no vàlida",
|
||||
"error.chain.causedBy": "Causat per:",
|
||||
"error.chain.apiError": "Error de l'API",
|
||||
"error.chain.status": "Estat: {{status}}",
|
||||
"error.chain.retryable": "Reintentable: {{retryable}}",
|
||||
"error.chain.responseBody": "Òrgan de resposta:\n{{body}}",
|
||||
"error.chain.didYouMean": "Volíeu dir: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "No s'ha trobat el model: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Comproveu els noms del vostre proveïdor/model de configuració (opencode.json).",
|
||||
"error.chain.mcpFailed":
|
||||
'El servidor MCP "{{name}}" ha fallat. Tingueu en compte que OpenCode encara no admet l\'autenticació MCP.',
|
||||
"error.chain.providerAuthFailed": "L'autenticació del proveïdor ha fallat ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'No s\'ha pogut inicialitzar el proveïdor "{{provider}}". Comproveu les credencials i la configuració.',
|
||||
"error.chain.configJsonInvalid": "El fitxer de configuració a {{path}} no és vàlid JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "El fitxer de configuració a {{path}} no és vàlid JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'El directori "{{dir}}" a {{path}} no és vàlid. Canvieu el nom del directori a "{{suggestion}}" o suprimiu-lo. Aquest és un error ortogràfic comú.',
|
||||
"error.chain.configFrontmatterError": "No s'ha pogut analitzar frontmatter a {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "El fitxer de configuració a {{path}} no és vàlid",
|
||||
"error.chain.configInvalidWithMessage": "El fitxer de configuració a {{path}} no és vàlid: {{message}}",
|
||||
"notification.permission.title": "Cal permís",
|
||||
"notification.permission.description": "{{sessionTitle}} a {{projectName}} necessita permís",
|
||||
"notification.question.title": "Pregunta",
|
||||
"notification.question.description": "{{sessionTitle}} a {{projectName}} té una pregunta",
|
||||
"notification.action.goToSession": "Anar a sessió",
|
||||
"notification.session.responseReady.title": "Resposta a punt",
|
||||
"notification.session.error.title": "Error de sessió",
|
||||
"notification.session.error.fallbackDescription": "S'ha produït un error",
|
||||
"home.recentProjects": "Projectes recents",
|
||||
"home.empty.title": "No hi ha projectes recents",
|
||||
"home.empty.description": "Comenceu obrint un projecte local",
|
||||
"home.title": "Inici",
|
||||
"home.projects": "Projectes",
|
||||
"home.project.add": "Afegeix projecte",
|
||||
"home.recentlyClosed": "Tancat recentment",
|
||||
"home.server.collapse": "Col·lapse els projectes del servidor",
|
||||
"home.server.expand": "Ampliar els projectes del servidor",
|
||||
"home.sessions.search.placeholder": "Sessions de cerca",
|
||||
"home.sessions.search.placeholder.scoped": "Cerca sessions a {{scope}}",
|
||||
"home.sessions.search.sessions": "Sessions",
|
||||
"home.sessions.search.noResults": "No s'han trobat sessions per a {{query}}",
|
||||
"home.sessions.empty": "Aquí encara no hi ha res",
|
||||
"home.sessions.empty.description": "Crea una sessió per començar",
|
||||
"home.sessions.group.today": "Avui",
|
||||
"home.sessions.group.yesterday": "Ahir",
|
||||
"home.sessions.group.older": "Més vell",
|
||||
"home.providerTip":
|
||||
"Connecteu-vos a més de 75 proveïdors per utilitzar altres models, inclosos Claude, GPT, Gemini, etc.",
|
||||
"session.tab.session": "Sessió",
|
||||
"session.tab.review": "Revisió",
|
||||
"session.tab.context": "Context",
|
||||
"session.tab.unknown": "Sessió desconeguda",
|
||||
"session.panel.reviewAndFiles": "Revisió i arxius",
|
||||
"session.error.notFound": "No es pot trobar aquesta sessió",
|
||||
"session.error.notFound.description": "Aquesta pestanya apunta a una sessió que ja no existeix en aquest servidor.",
|
||||
"session.error.notFound.closeTab": "Tanca la pestanya",
|
||||
"session.error.serverConnection": "No es pot connectar a aquest servidor",
|
||||
"session.review.filesChanged": "Fitxers canviats {{count}}",
|
||||
"session.review.change.one": "Canviar",
|
||||
"session.review.change.other": "Canvis",
|
||||
"session.review.loadingChanges": "S'estan carregant els canvis...",
|
||||
"session.review.empty": "Encara no hi ha canvis en aquesta sessió",
|
||||
"session.review.noVcs": "No Git S'ha detectat el sistema de control de versions, els canvis no es mostren",
|
||||
"session.review.noVcs.createGit.title": "Creeu un repositori Git.",
|
||||
"session.review.noVcs.createGit.description": "Feu un seguiment, reviseu i desfer els canvis en aquest projecte",
|
||||
"session.review.noVcs.createGit.actionLoading": "S'està creant un repositori Git...",
|
||||
"session.review.noVcs.createGit.action": "Crea un repositori Git",
|
||||
"session.review.noSnapshot":
|
||||
"El seguiment de la instantània està desactivat a la configuració, de manera que els canvis de sessió no estan disponibles",
|
||||
"session.review.noChanges": "Sense canvis",
|
||||
"session.review.noUncommittedChanges": "Encara no hi ha canvis no compromesos",
|
||||
"session.review.noBranchChanges": "Encara no hi ha canvis de branca",
|
||||
"session.files.selectToOpen": "Seleccioneu un fitxer per obrir",
|
||||
"session.files.all": "Tots els fitxers",
|
||||
"session.files.empty": "No hi ha fitxers",
|
||||
"session.files.binaryContent": "Fitxer binari (el contingut no es pot mostrar)",
|
||||
"session.messages.renderEarlier": "Mostra missatges anteriors",
|
||||
"session.messages.loadingEarlier": "S'estan carregant missatges anteriors...",
|
||||
"session.messages.loadEarlier": "Carregueu missatges anteriors",
|
||||
"session.messages.loading": "S'estan carregant els missatges...",
|
||||
"session.messages.jumpToLatest": "Ves a l'últim",
|
||||
"session.context.addToContext": "Afegeix {{selection}} al context",
|
||||
"session.todo.title": "Tots",
|
||||
"session.todo.collapse": "Col·lapse",
|
||||
"session.todo.expand": "Expandir",
|
||||
"session.todo.progress": "{{done}} de {{total}} tots completats",
|
||||
"session.question.progress": "{{current}} de {{total}} preguntes",
|
||||
"session.question.minimize": "Minimitzar la pregunta",
|
||||
"session.question.restore": "Pregunta de restauració",
|
||||
"session.question.pending.one": "{{count}} pregunta pendent",
|
||||
"session.question.pending.other": "{{count}} preguntes pendents",
|
||||
"session.question.pending.many": "{{count}} preguntes pendents",
|
||||
"session.followupDock.summary.one": "{{count}} missatge a la cua",
|
||||
"session.followupDock.summary.other": "{{count}} missatges a la cua",
|
||||
"session.followupDock.summary.many": "{{count}} missatges a la cua",
|
||||
"session.followupDock.sendNow": "Envia ara",
|
||||
"session.followupDock.edit": "Edita",
|
||||
"session.followupDock.collapse": "Replega els missatges a la cua",
|
||||
"session.followupDock.expand": "Amplieu els missatges a la cua",
|
||||
"session.revertDock.summary.one": "{{count}} missatge revertit",
|
||||
"session.revertDock.summary.other": "{{count}} missatges revertits",
|
||||
"session.revertDock.summary.many": "{{count}} missatges revertits",
|
||||
"session.revertDock.collapse": "Replega els missatges desfets",
|
||||
"session.revertDock.expand": "Amplieu els missatges revertits",
|
||||
"session.revertDock.restore": "Restaura el missatge",
|
||||
"session.new.title": "Construeix qualsevol cosa",
|
||||
"session.new.project.new": "Nou projecte",
|
||||
"session.new.project.search": "Cerca projectes",
|
||||
"session.new.project.add": "Afegeix projecte",
|
||||
"session.new.worktree.main": "Branca principal",
|
||||
"session.new.worktree.mainWithBranch": "Sucursal principal ({{branch}})",
|
||||
"session.new.worktree.create": "Crea un nou arbre de treball",
|
||||
"session.new.workspace.runIn": "Executeu la sessió",
|
||||
"session.new.workspace.triggerLocal": "Local",
|
||||
"session.new.workspace.local": "Repositori local",
|
||||
"session.new.workspace.existing": "Espai de treball…",
|
||||
"session.new.git.none": "Sense Git",
|
||||
"session.new.lastModified": "Última modificació",
|
||||
"session.header.search.placeholder": "Cerca {{project}}",
|
||||
"session.header.searchFiles": "Cerca fitxers",
|
||||
"session.header.openIn": "Obre dins",
|
||||
"session.header.open.action": "Obre {{app}}",
|
||||
"session.header.open.ariaLabel": "Obert a {{app}}",
|
||||
"session.header.open.menu": "Opcions obertes",
|
||||
"session.header.open.copyPath": "Copia el camí",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.open.fileManager": "Gestor de fitxers",
|
||||
"session.header.reveal.finder": "Revela a Finder",
|
||||
"session.header.reveal.fileExplorer": "Revela a File Explorer",
|
||||
"session.header.reveal.containingFolder": "Obre la carpeta que conté",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "Terminal",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "Estat",
|
||||
"status.popover.ariaLabel": "Configuracions del servidor",
|
||||
"status.popover.tab.servers": "Servidors",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Connectors",
|
||||
"status.popover.action.manageServers": "Gestionar servidors",
|
||||
"session.share.popover.title": "Publicar a la web",
|
||||
"session.share.popover.description.shared":
|
||||
"Aquesta sessió és pública a la web. És accessible per a qualsevol persona que tingui l'enllaç.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Comparteix la sessió públicament al web. Serà accessible per a qualsevol persona que tingui l'enllaç.",
|
||||
"session.share.action.share": "Comparteix",
|
||||
"session.share.action.publish": "Publicar",
|
||||
"session.share.action.publishing": "S'està publicant...",
|
||||
"session.share.action.unpublish": "Despublicar",
|
||||
"session.share.action.unpublishing": "S'està anul·lant la publicació...",
|
||||
"session.share.action.view": "Veure",
|
||||
"session.share.copy.copied": "Copiat",
|
||||
"session.share.copy.copyLink": "Copia l'enllaç",
|
||||
"lsp.tooltip.none": "No hi ha LSP servidors",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "S'està carregant el missatge...",
|
||||
"terminal.loading": "S'està carregant el terminal...",
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Tanca la terminal",
|
||||
"terminal.connectionLost.title": "Connexió perduda",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket tancat anormalment: {{code}}",
|
||||
"terminal.connectionLost.description":
|
||||
"La connexió del terminal s'ha interromput. Això pot passar quan es reinicia el servidor.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY bitllet de connexió rebutjat per origen o CSRF xecs. Comproveu la configuració del servidor CORS.",
|
||||
"terminal.connectTicket.statusError": "PTY el bitllet de connexió ha fallat amb {{status}}",
|
||||
"titlebar.update": "Actualització",
|
||||
"titlebar.updateVersion": "Actualització {{version}}",
|
||||
"common.closeTab": "Tanca la pestanya",
|
||||
"common.dismiss": "Descartar",
|
||||
"common.moreCountSuffix": " (+{{count}} més)",
|
||||
"common.requestFailed": "La sol·licitud ha fallat",
|
||||
"common.moreOptions": "Més opcions",
|
||||
"common.learnMore": "Més informació",
|
||||
"common.rename": "Canvia el nom",
|
||||
"common.export": "Exporta",
|
||||
"common.reset": "Restableix",
|
||||
"common.archive": "Arxiu",
|
||||
"common.delete": "Suprimeix",
|
||||
"common.close": "Tancar",
|
||||
"common.edit": "Edita",
|
||||
"common.loadMore": "Carregueu més",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Space",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "End",
|
||||
"common.key.pageUp": "Page Up",
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "desconegut",
|
||||
"common.time.justNow": "Just ara",
|
||||
"common.time.minutesAgo.short": "Fa {{count}}m",
|
||||
"common.time.hoursAgo.short": "Fa {{count}} h",
|
||||
"common.time.daysAgo.short": "Fa {{count}} dies",
|
||||
"sidebar.menu.toggle": "Canvia el menú",
|
||||
"sidebar.nav.projectsAndSessions": "Projectes i sessions",
|
||||
"sidebar.settings": "Configuració",
|
||||
"sidebar.help": "Ajuda",
|
||||
"sidebar.workspaces.enable": "Habilita els espais de treball",
|
||||
"sidebar.workspaces.disable": "Desactivar els espais de treball",
|
||||
"sidebar.gettingStarted.title": "Per començar",
|
||||
"sidebar.gettingStarted.line1": "OpenCode inclou models gratuïts perquè pugueu començar immediatament.",
|
||||
"sidebar.gettingStarted.line2": "Connecteu qualsevol proveïdor per utilitzar models, inc. Claude, GPT, Gemini etc.",
|
||||
"sidebar.project.recentSessions": "Sessions recents",
|
||||
"sidebar.project.viewAllSessions": "Veure totes les sessions",
|
||||
"sidebar.project.clearNotifications": "Esborra les notificacions",
|
||||
"sidebar.empty.title": "No hi ha projectes oberts",
|
||||
"sidebar.empty.description": "Obriu un projecte per començar",
|
||||
"debugBar.ariaLabel": "Diagnòstic de rendiment del desenvolupament",
|
||||
"debugBar.na": "n/a",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"L'última transició de ruta completada tocant una pàgina de sessió, mesurada des de l'inici de l'encaminador fins a la primera pintura després que s'instal·li.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Fotogrames en moviment per segon durant els darrers 5 segons.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "El pitjor temps de fotograma dels darrers 5 segons.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Fotogrames de més de 32 ms en els últims 5 segons.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Temps bloquejat i recompte de tasques llargues en els últims 5 segons. Tasca màxima: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "El pitjor retard d'entrada observat en els últims 5 segons.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Durada aproximada de la interacció durant els darrers 5 segons. Això és semblant a INP, no el Web Vitals oficial INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Canvi de disseny acumulat per a la vida útil actual de l'aplicació.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "S'ha utilitzat el límit d'emmagatzematge munt JS vs. Chromium només.",
|
||||
"debugBar.mem.tip": "S'ha utilitzat el límit d'emmagatzematge munt JS vs. {{used}} de {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Força els estils de focus en tots els elements interactius",
|
||||
"debugBar.focus.on": "ON",
|
||||
"debugBar.focus.off": "OFF",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "Força la direcció completa del disseny de l'aplicació sense canviar l'idioma seleccionat",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode Escriptori",
|
||||
"settings.section.desktop": "Escriptori",
|
||||
"settings.section.server": "Servidor",
|
||||
"settings.tab.general": "General",
|
||||
"settings.tab.shortcuts": "Dreceres",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integració",
|
||||
"settings.desktop.wsl.description": "Executeu el servidor OpenCode dins de WSL a Windows.",
|
||||
"settings.general.section.appearance": "Aparença",
|
||||
"settings.general.section.advanced": "Avançat",
|
||||
"settings.general.section.notifications": "Notificacions del sistema",
|
||||
"settings.general.section.updates": "Actualitzacions",
|
||||
"settings.general.section.sounds": "Efectes de so",
|
||||
"settings.general.section.feed": "Alimentació",
|
||||
"settings.general.section.display": "Mostra",
|
||||
"settings.general.row.language.title": "Llengua",
|
||||
"settings.general.row.language.description": "Canvia l'idioma de visualització per a OpenCode",
|
||||
"settings.general.row.shell.title": "Carcassa terminal",
|
||||
"settings.general.row.shell.description": "Shell utilitzat pel terminal i les eines d'agent",
|
||||
"settings.general.row.shell.autoDefault": "Automàtic (per defecte)",
|
||||
"settings.general.row.shell.terminalOnly": "només terminal",
|
||||
"settings.general.row.appearance.title": "Aparença",
|
||||
"settings.general.row.appearance.description": "Personalitza com es veu OpenCode al teu dispositiu",
|
||||
"settings.general.row.colorScheme.title": "Esquema de colors",
|
||||
"settings.general.row.colorScheme.description": "Trieu si OpenCode segueix el tema del sistema, clar o fosc",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Personalitza la temàtica de OpenCode.",
|
||||
"settings.general.row.font.title": "Font del codi",
|
||||
"settings.general.row.font.description": "Personalitzeu el tipus de lletra utilitzat als blocs de codi",
|
||||
"settings.general.row.terminalFont.title": "Tipus de lletra terminal",
|
||||
"settings.general.row.terminalFont.description": "Personalitzeu el tipus de lletra utilitzat al terminal",
|
||||
"settings.general.row.uiFont.title": "Tipus de lletra de la interfície",
|
||||
"settings.general.row.uiFont.description": "Personalitzeu el tipus de lletra utilitzat a tota la interfície",
|
||||
"settings.general.row.followup.title": "Comportament de seguiment",
|
||||
"settings.general.row.followup.description":
|
||||
"Trieu si les indicacions de seguiment es dirigeixen immediatament o esperen en una cua",
|
||||
"settings.general.row.followup.option.queue": "Cua",
|
||||
"settings.general.row.followup.option.steer": "Dirigeix",
|
||||
"settings.general.row.showFileTree.title": "Arbre de fitxers",
|
||||
"settings.general.row.showFileTree.description": "Mostra el tauler de l'arbre de fitxers a les sessions",
|
||||
"settings.general.row.showNavigation.title": "Controls de navegació",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Mostra els botons enrere i endavant a la barra de títol de l'escriptori",
|
||||
"settings.general.row.showSearch.title": "Paleta de comandaments",
|
||||
"settings.general.row.showSearch.description": "Mostra el botó de cerca i paleta d'ordres a la barra de títol",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description": "Mostra el botó del terminal a la barra de títol de l'escriptori",
|
||||
"settings.general.row.showStatus.title": "Estat del servidor",
|
||||
"settings.general.row.showStatus.description": "Mostra el botó d'estat del servidor a la barra de títol",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navegació inferior",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Col·loqueu la barra de títol i les pestanyes de sessió a la part inferior de la pantalla al mòbil",
|
||||
"settings.general.row.showCustomAgents.title": "Agent d'espectacles",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Canvia entre agents del compositor. Quan s'amaga, el valor predeterminat és Build agent.",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostra resums de raonament",
|
||||
"settings.general.row.reasoningSummaries.description": "Mostra els resums de raonament del model a la línia de temps",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Amplieu les peces de l'eina de closca",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Mostra les parts de l'eina d'intèrpret d'ordres ampliades per defecte a la línia de temps",
|
||||
"settings.general.row.editToolPartsExpanded.title": "Amplieu les parts d'eina d'edició",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Mostra les parts de l'eina d'edició, escriptura i pedaç ampliades de manera predeterminada a la línia de temps",
|
||||
"settings.general.row.newInterface.title": "Nou disseny",
|
||||
"settings.general.row.newInterface.badge": "Nou",
|
||||
"settings.general.row.newInterface.description":
|
||||
"Utilitzeu les noves pestanyes i el disseny de la casa. Canvia entre dissenys durant un temps limitat.",
|
||||
"settings.general.row.newInterfaceNotice.title": "Ara esteu utilitzant un disseny nou",
|
||||
"settings.general.row.newInterfaceNotice.description": "El disseny anterior ja no està disponible",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Descartar",
|
||||
"settings.general.row.pinchZoom.title": "Pessigueu per fer zoom",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Permet que els gestos de pessigar el ratolí tàctil i de desplaçament Ctrl s'ampliïn",
|
||||
"settings.general.row.wayland.title": "Utilitza el nadiu Wayland",
|
||||
"settings.general.row.wayland.description": "Desactiveu la alternativa X11 a Wayland. Requereix reinici.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"A Linux amb monitors de freqüència de refresc mixta, el nadiu Wayland pot ser més estable.",
|
||||
"settings.general.row.releaseNotes.title": "Notes de publicació",
|
||||
"settings.general.row.releaseNotes.description":
|
||||
"Mostra les finestres emergents Novetats després de les actualitzacions",
|
||||
"settings.updates.row.startup.title": "Comproveu si hi ha actualitzacions a l'inici",
|
||||
"settings.updates.row.startup.description":
|
||||
"Comprova automàticament si hi ha actualitzacions quan s'iniciï OpenCode.",
|
||||
"settings.updates.row.check.title": "Comproveu si hi ha actualitzacions",
|
||||
"settings.updates.row.check.description":
|
||||
"Comproveu manualment si hi ha actualitzacions i instal·leu si està disponible",
|
||||
"settings.updates.action.checkNow": "Comproveu ara",
|
||||
"settings.updates.action.checking": "S'està comprovant...",
|
||||
"settings.updates.action.downloading": "S'està baixant...",
|
||||
"settings.updates.action.installing": "S'està instal·lant...",
|
||||
"settings.updates.toast.latest.title": "Estàs al dia",
|
||||
"settings.updates.toast.latest.description": "Esteu executant la darrera versió de OpenCode.",
|
||||
"sound.option.none": "Cap",
|
||||
"sound.option.alert01": "Alerta 01",
|
||||
"sound.option.alert02": "Alerta 02",
|
||||
"sound.option.alert03": "Alerta 03",
|
||||
"sound.option.alert04": "Alerta 04",
|
||||
"sound.option.alert05": "Alerta 05",
|
||||
"sound.option.alert06": "Alerta 06",
|
||||
"sound.option.alert07": "Alerta 07",
|
||||
"sound.option.alert08": "Alerta 08",
|
||||
"sound.option.alert09": "Alerta 09",
|
||||
"sound.option.alert10": "Alerta 10",
|
||||
"sound.option.bipbop01": "Bip-bop 01",
|
||||
"sound.option.bipbop02": "Bip-bop 02",
|
||||
"sound.option.bipbop03": "Bip-bop 03",
|
||||
"sound.option.bipbop04": "Bip-bop 04",
|
||||
"sound.option.bipbop05": "Bip-bop 05",
|
||||
"sound.option.bipbop06": "Bip-bop 06",
|
||||
"sound.option.bipbop07": "Bip-bop 07",
|
||||
"sound.option.bipbop08": "Bip-bop 08",
|
||||
"sound.option.bipbop09": "Bip-bop 09",
|
||||
"sound.option.bipbop10": "Bip-bop 10",
|
||||
"sound.option.staplebops01": "Staplebops 01",
|
||||
"sound.option.staplebops02": "Staplebops 02",
|
||||
"sound.option.staplebops03": "Staplebops 03",
|
||||
"sound.option.staplebops04": "Staplebops 04",
|
||||
"sound.option.staplebops05": "Staplebops 05",
|
||||
"sound.option.staplebops06": "Staplebops 06",
|
||||
"sound.option.staplebops07": "Staplebops 07",
|
||||
"sound.option.nope01": "No 01",
|
||||
"sound.option.nope02": "No 02",
|
||||
"sound.option.nope03": "No 03",
|
||||
"sound.option.nope04": "No 04",
|
||||
"sound.option.nope05": "No 05",
|
||||
"sound.option.nope06": "No 06",
|
||||
"sound.option.nope07": "No 07",
|
||||
"sound.option.nope08": "No 08",
|
||||
"sound.option.nope09": "No 09",
|
||||
"sound.option.nope10": "No 10",
|
||||
"sound.option.nope11": "No 11",
|
||||
"sound.option.nope12": "No 12",
|
||||
"sound.option.yup01": "Sí 01",
|
||||
"sound.option.yup02": "Sí 02",
|
||||
"sound.option.yup03": "Sí 03",
|
||||
"sound.option.yup04": "Sí 04",
|
||||
"sound.option.yup05": "Sí 05",
|
||||
"sound.option.yup06": "Sí 06",
|
||||
"settings.general.notifications.agent.title": "Agent",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Mostra la notificació del sistema quan l'agent està complet o necessita atenció",
|
||||
"settings.general.notifications.permissions.title": "Permisos",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Mostra la notificació del sistema quan es requereix un permís",
|
||||
"settings.general.notifications.errors.title": "Errors",
|
||||
"settings.general.notifications.errors.description": "Mostra la notificació del sistema quan es produeix un error",
|
||||
"settings.general.sounds.agent.title": "Agent",
|
||||
"settings.general.sounds.agent.description": "Reprodueix el so quan l'agent està complet o necessita atenció",
|
||||
"settings.general.sounds.permissions.title": "Permisos",
|
||||
"settings.general.sounds.permissions.description": "Reprodueix so quan es requereix un permís",
|
||||
"settings.general.sounds.errors.title": "Errors",
|
||||
"settings.general.sounds.errors.description": "Reprodueix el so quan es produeix un error",
|
||||
"settings.shortcuts.title": "Dreceres de teclat",
|
||||
"settings.shortcuts.reset.button": "Restableix els valors predeterminats",
|
||||
"settings.shortcuts.reset.toast.title": "Es restableixen les dreceres",
|
||||
"settings.shortcuts.reset.toast.description": "Les dreceres de teclat s'han restablert als valors predeterminats.",
|
||||
"settings.shortcuts.conflict.title": "La drecera ja està en ús",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} ja està assignat a {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Sense assignar",
|
||||
"settings.shortcuts.pressKeys": "Premeu les tecles",
|
||||
"settings.shortcuts.search.placeholder": "Cerca dreceres",
|
||||
"settings.shortcuts.search.empty": "No s'han trobat dreceres",
|
||||
"settings.shortcuts.group.general": "General",
|
||||
"settings.shortcuts.group.session": "Sessió",
|
||||
"settings.shortcuts.group.navigation": "Navegació",
|
||||
"settings.shortcuts.group.modelAndAgent": "Model i agent",
|
||||
"settings.shortcuts.group.terminal": "Terminal",
|
||||
"settings.shortcuts.group.prompt": "Indicació",
|
||||
"settings.providers.title": "Proveïdors",
|
||||
"settings.providers.description": "La configuració del proveïdor es podrà configurar aquí.",
|
||||
"settings.providers.section.connected": "Proveïdors connectats",
|
||||
"settings.providers.connected.empty": "No hi ha proveïdors connectats",
|
||||
"settings.providers.connected.environmentDescription": "Connectat des de les vostres variables d'entorn",
|
||||
"settings.providers.section.popular": "Proveïdors populars",
|
||||
"settings.providers.custom.description": "Afegiu un proveïdor compatible amb OpenAI per base URL.",
|
||||
"settings.providers.tag.environment": "Medi ambient",
|
||||
"settings.providers.tag.config": "Config",
|
||||
"settings.providers.tag.custom": "Personalitzat",
|
||||
"settings.providers.tag.other": "Altres",
|
||||
"settings.models.title": "Models",
|
||||
"settings.models.description": "La configuració del model es podrà configurar aquí.",
|
||||
"settings.agents.title": "Agents",
|
||||
"settings.agents.description": "La configuració de l'agent es podrà configurar aquí.",
|
||||
"settings.commands.title": "Ordres",
|
||||
"settings.commands.description": "La configuració de les ordres es podrà configurar aquí.",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "La configuració MCP es podrà configurar aquí.",
|
||||
"settings.permissions.title": "Permisos",
|
||||
"settings.permissions.description": "Controleu quines eines pot utilitzar el servidor de manera predeterminada.",
|
||||
"settings.permissions.section.tools": "Eines",
|
||||
"settings.permissions.toast.updateFailed.title": "No s'han pogut actualitzar els permisos",
|
||||
"settings.permissions.action.allow": "Permetre",
|
||||
"settings.permissions.action.ask": "Pregunta",
|
||||
"settings.permissions.action.deny": "Negar",
|
||||
"settings.permissions.tool.read.title": "Llegeix",
|
||||
"settings.permissions.tool.read.description": "Llegint un fitxer (coincideix amb la ruta del fitxer)",
|
||||
"settings.permissions.tool.edit.title": "Edita",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modifiqueu fitxers, incloses les edicions, les escriptures i els pedaços",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Relaciona fitxers amb patrons globus",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Cerca continguts de fitxers mitjançant expressions regulars",
|
||||
"settings.permissions.tool.list.title": "Llista",
|
||||
"settings.permissions.tool.list.description": "Llista els fitxers dins d'un directori",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Executeu ordres de shell",
|
||||
"settings.permissions.tool.task.title": "Tasca",
|
||||
"settings.permissions.tool.task.description": "Llançar subagents",
|
||||
"settings.permissions.tool.skill.title": "Habilitat",
|
||||
"settings.permissions.tool.skill.description": "Carregueu una habilitat pel nom",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Executeu consultes del servidor d'idiomes",
|
||||
"settings.permissions.tool.todowrite.title": "Tot Escriure",
|
||||
"settings.permissions.tool.todowrite.description": "Actualitzeu la llista de tasques",
|
||||
"settings.permissions.tool.webfetch.title": "Recollida web",
|
||||
"settings.permissions.tool.webfetch.description": "Obteniu contingut d'un URL",
|
||||
"settings.permissions.tool.websearch.title": "Cerca web",
|
||||
"settings.permissions.tool.websearch.description": "Cerca al web",
|
||||
"settings.permissions.tool.external_directory.title": "Directori extern",
|
||||
"settings.permissions.tool.external_directory.description": "Accediu als fitxers fora del directori del projecte",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "Detecteu trucades d'eines repetides amb entrada idèntica",
|
||||
"session.delete.failed.title": "No s'ha pogut suprimir la sessió",
|
||||
"session.delete.title": "Suprimeix la sessió",
|
||||
"session.delete.confirm": 'Voleu suprimir la sessió "{{name}}"?',
|
||||
"session.delete.button": "Suprimeix la sessió",
|
||||
"workspace.new": "Nou espai de treball",
|
||||
"workspace.type.local": "locals",
|
||||
"workspace.type.sandbox": "caixa de sorra",
|
||||
"workspace.create.failed.title": "No s'ha pogut crear l'espai de treball",
|
||||
"workspace.delete.failed.title": "No s'ha pogut suprimir l'espai de treball",
|
||||
"workspace.resetting.title": "Restabliment de l'espai de treball",
|
||||
"workspace.resetting.description": "Això pot trigar un minut.",
|
||||
"workspace.reset.failed.title": "No s'ha pogut restablir l'espai de treball",
|
||||
"workspace.reset.success.title": "Restabliment de l'espai de treball",
|
||||
"workspace.reset.success.description": "L'espai de treball ara coincideix amb la branca predeterminada.",
|
||||
"workspace.error.stillPreparing": "L'espai de treball encara s'està preparant",
|
||||
"workspace.status.checking": "S'estan comprovant els canvis no combinats...",
|
||||
"workspace.status.error": "No es pot verificar l'estat de git.",
|
||||
"workspace.status.clean": "No s'han detectat canvis no combinats.",
|
||||
"workspace.status.dirty": "S'han detectat canvis no combinats en aquest espai de treball.",
|
||||
"workspace.delete.title": "Suprimeix l'espai de treball",
|
||||
"workspace.delete.confirm": 'Voleu suprimir l\'espai de treball "{{name}}"?',
|
||||
"workspace.delete.button": "Suprimeix l'espai de treball",
|
||||
"workspace.reset.title": "Restableix l'espai de treball",
|
||||
"workspace.reset.confirm": 'Restableix l\'espai de treball "{{name}}"?',
|
||||
"workspace.reset.button": "Restableix l'espai de treball",
|
||||
"workspace.reset.archived.none": "No s'arxivarà cap sessió activa.",
|
||||
"workspace.reset.archived.one": "S'arxivarà 1 sessió.",
|
||||
"workspace.reset.archived.many": "{{count}} sessions s'arxivaran.",
|
||||
"workspace.reset.note": "Això restablirà l'espai de treball perquè coincideixi amb la branca predeterminada.",
|
||||
"dialog.usageExceeded.dontShowAgain": "No ho tornis a mostrar",
|
||||
}
|
||||
@@ -0,0 +1,1163 @@
|
||||
export const dict = {
|
||||
"desktop.menu.app": "OpenCode",
|
||||
"desktop.menu.file": "Soubor",
|
||||
"desktop.menu.edit": "Upravit",
|
||||
"desktop.menu.view": "Zobrazit",
|
||||
"desktop.menu.go": "Jdi",
|
||||
"desktop.menu.window": "okno",
|
||||
"desktop.menu.help": "Pomoc",
|
||||
"desktop.menu.checkForUpdates": "Zkontrolovat aktualizace...",
|
||||
"desktop.menu.settings": "Nastavení",
|
||||
"desktop.menu.reloadWebview": "Znovu načíst Webview",
|
||||
"desktop.menu.restart": "Restartujte",
|
||||
"desktop.menu.exportLogs": "Exportovat protokoly...",
|
||||
"desktop.menu.newSession": "Nová relace",
|
||||
"desktop.menu.openProject": "Otevřít projekt...",
|
||||
"desktop.menu.newWindow": "Nové okno",
|
||||
"desktop.menu.closeWindow": "Zavřít okno",
|
||||
"desktop.menu.undo": "Vrátit zpět",
|
||||
"desktop.menu.redo": "Znovu",
|
||||
"desktop.menu.cut": "Řez",
|
||||
"desktop.menu.copy": "Kopírovat",
|
||||
"desktop.menu.paste": "Vložit",
|
||||
"desktop.menu.delete": "Odstranit",
|
||||
"desktop.menu.selectAll": "Vyberte Vše",
|
||||
"desktop.menu.toggleSidebar": "Přepnout postranní panel",
|
||||
"desktop.menu.toggleTerminal": "Přepnout terminál",
|
||||
"desktop.menu.toggleFileTree": "Přepnout strom souborů",
|
||||
"desktop.menu.reload": "Znovu načíst",
|
||||
"desktop.menu.toggleDeveloperTools": "Přepnout Vývojářské nástroje",
|
||||
"desktop.menu.actualSize": "Skutečná velikost",
|
||||
"desktop.menu.zoomIn": "Přiblížit",
|
||||
"desktop.menu.zoomOut": "Oddálit",
|
||||
"desktop.menu.toggleFullScreen": "Přepnout na celou obrazovku",
|
||||
"desktop.menu.back": "Zpět",
|
||||
"desktop.menu.forward": "Vpřed",
|
||||
"desktop.menu.previousSession": "Předchozí relace",
|
||||
"desktop.menu.nextSession": "Další relace",
|
||||
"desktop.menu.previousProject": "Předchozí projekt",
|
||||
"desktop.menu.nextProject": "Další projekt",
|
||||
"desktop.menu.minimize": "Minimalizovat",
|
||||
"desktop.menu.maximize": "Maximalizovat",
|
||||
"desktop.menu.documentation": "OpenCode Dokumentace",
|
||||
"desktop.menu.supportForum": "Fórum podpory",
|
||||
"desktop.menu.shareFeedback": "Sdílejte zpětnou vazbu",
|
||||
"desktop.menu.reportBug": "Nahlásit chybu",
|
||||
"desktop.menu.ariaLabel": "Nabídka OpenCode",
|
||||
"desktop.updater.dialog.checkFailed.message": "Kontrola aktualizace se nezdařila.",
|
||||
"desktop.updater.dialog.checkFailed.title": "Chyba aktualizace",
|
||||
"desktop.updater.dialog.upToDate.message": "Jste aktuální.",
|
||||
"desktop.updater.dialog.upToDate.title": "Žádné aktualizace",
|
||||
"desktop.updater.dialog.ready.message": "Aktualizace {{version}} stažena. Restartovat nyní?",
|
||||
"desktop.updater.dialog.ready.title": "Aktualizace připravena",
|
||||
"desktop.updater.dialog.restart": "Restartujte",
|
||||
"desktop.updater.dialog.later": "Později",
|
||||
"desktop.recovery.action.relaunch": "Znovu spustit",
|
||||
"desktop.recovery.action.exportLogs": "Exportovat protokoly",
|
||||
"desktop.recovery.action.keepWaiting": "Čekej dál",
|
||||
"desktop.recovery.action.quit": "Konec",
|
||||
"desktop.recovery.loadFailed": "OpenCode se nepodařilo načíst",
|
||||
"desktop.recovery.terminated": "OpenCode okno bylo neočekávaně ukončeno",
|
||||
"desktop.recovery.unresponsive": "OpenCode nereaguje",
|
||||
"desktop.recovery.unresponsive.detail": "Můžete znovu spustit aplikaci, otevřít protokoly nebo čekat.",
|
||||
"desktop.recovery.loadFailed.detail": "Okno: {{window}}\nURL: {{url}}\nChyba: {{code}} {{description}}",
|
||||
"desktop.recovery.terminated.detail": "Okno: {{window}}\nDůvod: {{reason}}\nKód: {{code}}",
|
||||
"desktop.recovery.unknown": "<neznámé>",
|
||||
"desktop.dialog.chooseFolder": "Vyberte složku",
|
||||
"desktop.dialog.chooseFile": "Vyberte soubor",
|
||||
"desktop.dialog.saveFile": "Uložit soubor",
|
||||
"desktop.dialog.files": "Soubory",
|
||||
"desktop.server.local": "Místní server",
|
||||
"desktop.wsl.error.windowsOnly": "WSL je k dispozici pouze na Windows",
|
||||
"desktop.wsl.error.unavailable": "WSL není k dispozici",
|
||||
"desktop.wsl.error.listInstalled": "Seznam nainstalovaných WSL distribucí se nezdařil",
|
||||
"desktop.wsl.error.listOnline": "Seznam online WSL distribucí se nezdařil",
|
||||
"desktop.wsl.error.executeDistro": "Nelze provádět příkazy v distro",
|
||||
"desktop.wsl.error.installWsl": "WSL instalace se nezdařila",
|
||||
"desktop.wsl.error.installDistro": "Nepodařilo se nainstalovat distribuci: {{distro}}",
|
||||
"desktop.wsl.error.installOpencode": "OpenCode instalace se nezdařila",
|
||||
"desktop.wsl.error.alreadyAdded": "{{distro}} je již přidáno",
|
||||
"desktop.wsl.error.opencodeMissing": "opencode není v této distribuci nainstalováno",
|
||||
"desktop.wsl.error.opencodeCannotRun": "opencode je nainstalován, ale nelze jej spustit",
|
||||
"desktop.wsl.error.opencodeNotInstalled": "OpenCode není nainstalován v {{distro}}",
|
||||
"desktop.wsl.error.updateVersion":
|
||||
"OpenCode aktualizace dokončena, ale {{distro}} stále hlásí {{installed}}; očekává se {{expected}}",
|
||||
"desktop.wsl.error.noVersion": "žádná verze",
|
||||
"desktop.wsl.error.serverExited": "Server WSL byl po spuštění ukončen (code={{code}} signal={{signal}})",
|
||||
"desktop.wsl.error.serverExitedBeforeHealthy":
|
||||
"Server WSL byl ukončen, než byl připraven (code={{code}} signal={{signal}}){{output}}",
|
||||
"desktop.wsl.error.healthTimeout": "Postranní vozík pro {{distro}} kontrolu stavu vypršel po {{timeout}}ms",
|
||||
"desktop.wsl.error.commandTimeout": "{{command}} {{args}} vypršel po {{timeout}}ms",
|
||||
"desktop.wsl.error.failedPort": "Nepodařilo se získat port",
|
||||
"desktop.picker.error.notSelected": "Soubor nebyl vybrán nástrojem pro výběr",
|
||||
"desktop.picker.error.sizeLimit": "Vybrané přílohy překračují limit {{limit}} MB",
|
||||
"command.category.suggested": "Doporučeno",
|
||||
"command.category.view": "Zobrazit",
|
||||
"command.category.project": "Projekt",
|
||||
"command.category.provider": "Poskytovatel",
|
||||
"command.category.server": "Server",
|
||||
"command.category.session": "Relace",
|
||||
"command.category.theme": "téma",
|
||||
"command.category.language": "Jazyk",
|
||||
"command.category.file": "Soubor",
|
||||
"command.category.context": "Kontext",
|
||||
"command.category.terminal": "Terminál",
|
||||
"command.category.model": "Model",
|
||||
"command.category.mcp": "MCP",
|
||||
"command.category.agent": "Agent",
|
||||
"command.category.permissions": "Oprávnění",
|
||||
"command.category.workspace": "Pracovní prostor",
|
||||
"command.category.settings": "Nastavení",
|
||||
"command.logs.export": "Exportovat protokoly",
|
||||
"theme.scheme.system": "Systém",
|
||||
"theme.scheme.light": "Světlo",
|
||||
"theme.scheme.dark": "Tmavý",
|
||||
"command.sidebar.toggle": "Přepnout postranní panel",
|
||||
"command.project.open": "Otevřete projekt",
|
||||
"command.project.previous": "Předchozí projekt",
|
||||
"command.project.next": "Další projekt",
|
||||
"command.project.index": "Přepnout na projekt {{index}}",
|
||||
"command.provider.connect": "Připojte poskytovatele",
|
||||
"command.server.switch": "Přepnout server",
|
||||
"command.settings.open": "Otevřete nastavení",
|
||||
"command.session.previous": "Předchozí relace",
|
||||
"command.session.next": "Další sezení",
|
||||
"command.session.previous.unseen": "Předchozí nepřečtená relace",
|
||||
"command.session.next.unseen": "Další nepřečtená relace",
|
||||
"command.session.archive": "Archivní relace",
|
||||
"command.palette": "Paleta příkazů",
|
||||
"command.theme.cycle": "Téma cyklu",
|
||||
"command.theme.set": "Použít motiv: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Barevné schéma cyklu",
|
||||
"command.theme.scheme.set": "Použít barevné schéma: {{scheme}}",
|
||||
"command.language.cycle": "Jazyk cyklu",
|
||||
"command.language.set": "Použít jazyk: {{language}}",
|
||||
"command.session.new": "Nová relace",
|
||||
"command.file.open": "Otevřít soubor",
|
||||
"command.tab.close": "Zavřít kartu",
|
||||
"command.tab.reopenClosed": "Znovu otevřete zavřenou kartu",
|
||||
"command.context.addSelection": "Přidejte výběr do kontextu",
|
||||
"command.context.addSelection.description": "Přidat vybrané řádky z aktuálního souboru",
|
||||
"command.input.focus": "Zaměření vstupu",
|
||||
"command.terminal.toggle": "Přepínací terminál",
|
||||
"command.fileTree.toggle": "Přepnout strom souborů",
|
||||
"command.review.toggle": "Přepnout recenzi",
|
||||
"command.terminal.new": "Nový terminál",
|
||||
"command.terminal.new.description": "Vytvořte novou kartu terminálu",
|
||||
"command.steps.toggle": "Přepínání kroků",
|
||||
"command.steps.toggle.description": "Zobrazit nebo skrýt kroky pro aktuální zprávu",
|
||||
"command.message.previous": "Předchozí zpráva",
|
||||
"command.message.previous.description": "Přejděte na předchozí zprávu uživatele",
|
||||
"command.message.next": "Další zpráva",
|
||||
"command.message.next.description": "Přejděte na další uživatelskou zprávu",
|
||||
"command.model.choose": "Vyberte model",
|
||||
"command.model.choose.description": "Vyberte jiný model",
|
||||
"command.mcp.toggle": "Přepnout MCP",
|
||||
"command.mcp.toggle.description": "Přepnout MCP",
|
||||
"command.agent.cycle": "Agent cyklu",
|
||||
"command.agent.cycle.description": "Přepněte na dalšího agenta",
|
||||
"command.agent.cycle.reverse": "Cyklujte agenta zpět",
|
||||
"command.agent.cycle.reverse.description": "Přepnout na předchozího agenta",
|
||||
"command.model.variant.cycle": "Cyklické myšlení",
|
||||
"command.model.variant.cycle.description": "Přepněte na další úroveň úsilí",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Prompt",
|
||||
"command.permissions.autoaccept.enable": "Automaticky přijímat oprávnění",
|
||||
"command.permissions.autoaccept.disable": "Zastavit automatické přijímání oprávnění",
|
||||
"command.workspace.toggle": "Přepnout pracovní prostory",
|
||||
"command.workspace.toggle.description": "Povolit nebo zakázat více pracovních ploch na postranním panelu",
|
||||
"command.session.undo": "Vrátit zpět",
|
||||
"command.session.undo.description": "Vrátit zpět poslední zprávu",
|
||||
"command.session.redo": "Znovu",
|
||||
"command.session.redo.description": "Opakujte poslední vrácenou zprávu",
|
||||
"command.session.compact": "Kompaktní relace",
|
||||
"command.session.compact.description": "Shrňte relaci, abyste snížili velikost kontextu",
|
||||
"command.session.fork": "Vytvořit větev ze zprávy",
|
||||
"command.session.fork.description": "Vytvořte novou relaci z předchozí zprávy",
|
||||
"command.session.share": "Sdílejte relaci",
|
||||
"command.session.share.description": "Sdílejte tuto relaci a zkopírujte URL do schránky",
|
||||
"command.session.unshare": "Zrušit sdílení relace",
|
||||
"command.session.unshare.description": "Přestat sdílet tuto relaci",
|
||||
"command.session.export": "Export relace",
|
||||
"command.session.export.description": "Exportovat celý přepis relace jako JSON",
|
||||
"palette.search.placeholder": "Prohledávejte soubory, příkazy a relace",
|
||||
"palette.search.placeholder.home": "Hledat příkazy a relace",
|
||||
"palette.empty": "Nebyly nalezeny žádné výsledky",
|
||||
"palette.group.commands": "Příkazy",
|
||||
"palette.group.files": "Soubory",
|
||||
"dialog.provider.search.placeholder": "Hledat poskytovatele",
|
||||
"dialog.provider.empty": "Nebyli nalezeni žádní poskytovatelé",
|
||||
"dialog.provider.group.popular": "Populární",
|
||||
"dialog.provider.group.other": "Jiné",
|
||||
"dialog.provider.custom.label": "Vlastní poskytovatel kompatibilní s OpenAI",
|
||||
"dialog.provider.tag.recommended": "Doporučeno",
|
||||
"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",
|
||||
"dialog.provider.openrouter.note": "Získejte přístup ke všem podporovaným modelům od jednoho poskytovatele",
|
||||
"dialog.provider.vercel.note": "Jednotný přístup k modelům AI s chytrým směrováním",
|
||||
"dialog.model.select.title": "Vyberte model",
|
||||
"dialog.model.search.placeholder": "Hledat modely",
|
||||
"dialog.model.empty": "Žádné výsledky modelu",
|
||||
"dialog.model.manage": "Správa modelů",
|
||||
"dialog.model.manage.description": "Upravte, které modely se zobrazí ve voliči modelů.",
|
||||
"dialog.model.manage.provider.toggle": "Přepnout všechny modely {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Bezplatné modely poskytované OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Přidejte další modely od oblíbených poskytovatelů",
|
||||
"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č",
|
||||
"provider.connect.method.headless": "Bezhlavě",
|
||||
"provider.connect.status.inProgress": "Probíhá autorizace...",
|
||||
"provider.connect.status.waiting": "Čekání na autorizaci...",
|
||||
"provider.connect.status.failed": "Autorizace se nezdařila: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Zadejte klíč API služby {{provider}}, připojte účet a používejte modely {{provider}} v OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API klíč",
|
||||
"provider.connect.apiKey.placeholder": "API klíč",
|
||||
"provider.connect.apiKey.required": "Je vyžadováno tlačítko API",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen vám poskytuje přístup k upravené sadě spolehlivých optimalizovaných modelů pro kódovací agenty.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Pomocí jediného tlačítka API získáte přístup k modelům jako Claude, GPT, Gemini, GLM a dalším.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Návštěva ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": " vyzvedněte svůj klíč API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Návštěva ",
|
||||
"provider.connect.oauth.code.visit.link": "tento odkaz",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" k vyzvednutí autorizačního kódu pro připojení vašeho účtu a používání {{provider}} modelů v OpenCode.",
|
||||
"provider.connect.oauth.code.label": "{{method}} autorizační kód",
|
||||
"provider.connect.oauth.code.placeholder": "Autorizační kód",
|
||||
"provider.connect.oauth.code.required": "Je vyžadován autorizační kód",
|
||||
"provider.connect.oauth.code.invalid": "Neplatný autorizační kód",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Návštěva ",
|
||||
"provider.connect.oauth.auto.visit.link": "tento odkaz",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" a zadejte níže uvedený kód pro připojení svého účtu a používání {{provider}} modelů v OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Potvrzovací kód",
|
||||
"provider.connect.toast.connected.title": "{{provider}} připojeno",
|
||||
"provider.connect.toast.connected.description": "Nyní jsou k dispozici modely {{provider}}.",
|
||||
"provider.custom.title": "Vlastní poskytovatel",
|
||||
"provider.custom.unavailable": "Vlastní poskytovatelé nejsou na tomto serveru k dispozici",
|
||||
"provider.custom.description.prefix": "Nakonfigurujte poskytovatele kompatibilního s OpenAI. Viz ",
|
||||
"provider.custom.description.link": "konfigurační dokumenty poskytovatele",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "Poskytovatel ID",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "Malá písmena, čísla, pomlčky nebo podtržítka",
|
||||
"provider.custom.field.name.label": "Zobrazovaný název",
|
||||
"provider.custom.field.name.placeholder": "Můj poskytovatel AI",
|
||||
"provider.custom.field.baseURL.label": "základ URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API klíč",
|
||||
"provider.custom.field.apiKey.placeholder": "API klíč",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Volitelné. Pokud autorizaci spravujete pomocí záhlaví, ponechte prázdné.",
|
||||
"provider.custom.models.label": "Modely",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "model-id",
|
||||
"provider.custom.models.name.label": "Jméno",
|
||||
"provider.custom.models.name.placeholder": "Zobrazovaný název",
|
||||
"provider.custom.models.remove": "Odebrat model",
|
||||
"provider.custom.models.add": "Přidat model",
|
||||
"provider.custom.headers.label": "Záhlaví (volitelné)",
|
||||
"provider.custom.headers.key.label": "Záhlaví",
|
||||
"provider.custom.headers.key.placeholder": "Header-Name",
|
||||
"provider.custom.headers.value.label": "Hodnota",
|
||||
"provider.custom.headers.value.placeholder": "value",
|
||||
"provider.custom.headers.remove": "Odstraňte záhlaví",
|
||||
"provider.custom.headers.add": "Přidat záhlaví",
|
||||
"provider.custom.error.providerID.required": "Poskytovatel ID je povinný",
|
||||
"provider.custom.error.providerID.format": "Používejte malá písmena, čísla, pomlčky nebo podtržítka",
|
||||
"provider.custom.error.providerID.exists": "Tento poskytovatel ID již existuje",
|
||||
"provider.custom.error.name.required": "Zobrazovaný název je povinný",
|
||||
"provider.custom.error.baseURL.required": "Je vyžadována základna URL",
|
||||
"provider.custom.error.baseURL.format": "Musí začínat http:// nebo https://",
|
||||
"provider.custom.error.required": "Povinné",
|
||||
"provider.custom.error.duplicate": "Duplicitní",
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} odpojeno",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} modely již nejsou k dispozici.",
|
||||
"model.tag.free": "Zdarma",
|
||||
"model.tag.latest": "Nejnovější",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
"model.provider.xai": "xAI",
|
||||
"model.provider.meta": "Meta",
|
||||
"model.input.text": "text",
|
||||
"model.input.image": "obrázek",
|
||||
"model.input.audio": "audio",
|
||||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Umožňuje: {{inputs}}",
|
||||
"model.tooltip.context.label": "Kontext",
|
||||
"model.tooltip.inputs": "Vstupy",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Poskytovatel",
|
||||
"model.tooltip.reasoning": "Zdůvodnění",
|
||||
"model.tooltip.reasoning.allowed": "Umožňuje uvažování",
|
||||
"model.tooltip.reasoning.none": "Žádné zdůvodnění",
|
||||
"model.tooltip.context": "Kontextový limit {{limit}}",
|
||||
"common.search.placeholder": "Hledat",
|
||||
"common.goBack": "Přejít zpět",
|
||||
"common.goForward": "Přejděte vpřed",
|
||||
"common.loading": "Načítání",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Zrušit",
|
||||
"common.clear": "Jasný",
|
||||
"common.open": "Otevřít",
|
||||
"common.connect": "Připojte se",
|
||||
"common.disconnect": "Odpojit",
|
||||
"common.continue": "Pokračujte",
|
||||
"common.submit": "Odeslat",
|
||||
"common.save": "Uložit",
|
||||
"common.saving": "Ukládání...",
|
||||
"common.default": "Výchozí",
|
||||
"common.attachment": "příloha",
|
||||
"prompt.placeholder.shell": "Zadejte příkaz shellu... {{example}}",
|
||||
"prompt.placeholder.normal": 'Zeptejte se na cokoliv... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Zeptejte se na cokoliv...",
|
||||
"prompt.placeholder.summarizeComments": "Shrnout komentáře…",
|
||||
"prompt.placeholder.summarizeComment": "Shrnout komentář…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc pro ukončení",
|
||||
"session.child.promptDisabled": "Nelze vyžádat relace podagenta.",
|
||||
"session.child.backToParent": "Zpět k hlavní relaci.",
|
||||
"prompt.example.1": "Opravte TODO v kódové základně",
|
||||
"prompt.example.2": "Jaký je technický stack tohoto projektu?",
|
||||
"prompt.example.3": "Opravte nefunkční testy",
|
||||
"prompt.example.4": "Vysvětlete, jak funguje ověřování",
|
||||
"prompt.example.5": "Najděte a opravte slabá místa zabezpečení",
|
||||
"prompt.example.6": "Přidejte testy jednotek pro uživatelskou službu",
|
||||
"prompt.example.7": "Refaktorujte tuto funkci, aby byla čitelnější",
|
||||
"prompt.example.8": "Co tato chyba znamená?",
|
||||
"prompt.example.9": "Pomozte mi odladit tento problém",
|
||||
"prompt.example.10": "Vygenerujte dokumentaci API",
|
||||
"prompt.example.11": "Optimalizujte databázové dotazy",
|
||||
"prompt.example.12": "Přidejte ověření vstupu",
|
||||
"prompt.example.13": "Vytvořit novou komponentu pro...",
|
||||
"prompt.example.14": "Jak nasadím tento projekt?",
|
||||
"prompt.example.15": "Osvědčené postupy naleznete v mém kódu",
|
||||
"prompt.example.16": "Přidejte k této funkci zpracování chyb",
|
||||
"prompt.example.17": "Vysvětlete tento vzor regulárního výrazu",
|
||||
"prompt.example.18": "Převést na TypeScript",
|
||||
"prompt.example.19": "Přidejte protokolování v celé kódové základně",
|
||||
"prompt.example.20": "Jaké závislosti jsou zastaralé?",
|
||||
"prompt.example.21": "Pomozte mi napsat migrační skript",
|
||||
"prompt.example.22": "Implementujte ukládání do mezipaměti pro tento koncový bod",
|
||||
"prompt.example.23": "Přidejte stránkování do tohoto seznamu",
|
||||
"prompt.example.24": "Vytvořte příkaz CLI pro...",
|
||||
"prompt.example.25": "Jak zde fungují proměnné prostředí?",
|
||||
"prompt.popover.emptyResults": "Žádné odpovídající výsledky",
|
||||
"prompt.popover.emptyCommands": "Žádné odpovídající příkazy",
|
||||
"prompt.dropzone.label": "Sem přetáhněte obrázky PDFs nebo textové soubory",
|
||||
"prompt.dropzone.file.label": "Přesuňte se na soubor @zmínka",
|
||||
"prompt.slash.badge.custom": "vlastní",
|
||||
"prompt.slash.badge.skill": "dovednost",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "aktivní",
|
||||
"prompt.context.includeActiveFile": "Zahrnout aktivní soubor",
|
||||
"prompt.context.removeActiveFile": "Odebrat aktivní soubor z kontextu",
|
||||
"prompt.context.removeFile": "Odebrat soubor z kontextu",
|
||||
"prompt.action.attachFile": "Přidat soubory",
|
||||
"prompt.menu.addImagesAndFiles": "Přidejte soubory a další",
|
||||
"prompt.menu.imagesAndFiles": "Obrázky a soubory",
|
||||
"prompt.menu.commands": "Příkazy",
|
||||
"prompt.menu.context": "Kontext",
|
||||
"prompt.menu.shellCommand": "Shell příkaz",
|
||||
"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.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Vytvoření pracovního stromu se nezdařilo",
|
||||
"prompt.toast.sessionCreateFailed.title": "Vytvoření relace se nezdařilo",
|
||||
"prompt.toast.shellSendFailed.title": "Odeslání příkazu shellu se nezdařilo",
|
||||
"prompt.toast.commandSendFailed.title": "Odeslání příkazu se nezdařilo",
|
||||
"prompt.toast.promptSendFailed.title": "Odeslání výzvy se nezdařilo",
|
||||
"prompt.toast.promptSendFailed.description": "Nelze načíst relaci",
|
||||
"dialog.mcp.title": "MCP",
|
||||
"dialog.mcp.description": "{{enabled}} z {{total}} povoleno",
|
||||
"dialog.mcp.empty": "Nejsou nakonfigurovány žádné MCP",
|
||||
"dialog.lsp.empty": "LSP automaticky detekovány z typů souborů",
|
||||
"dialog.plugins.empty": "Pluginy nakonfigurované v opencode.json",
|
||||
"mcp.status.connected": "připojeno",
|
||||
"mcp.status.failed": "nepodařilo",
|
||||
"mcp.status.needs_auth": "potřebuje autentizaci",
|
||||
"mcp.status.disabled": "zakázáno",
|
||||
"mcp.auth.clickToAuthenticate": "Klikněte pro ověření",
|
||||
"dialog.fork.empty": "Žádné zprávy k rozvětvení",
|
||||
"dialog.directory.search.placeholder": "Hledat složky",
|
||||
"dialog.directory.empty": "Nebyly nalezeny žádné složky",
|
||||
"dialog.directory.action.selectFile": "Vyberte soubor",
|
||||
"dialog.directory.action.selectFolder": "Vyberte složku",
|
||||
"dialog.directory.root": "Kořen",
|
||||
"dialog.directory.parent": "Rodič",
|
||||
"dialog.directory.readError": "Tuto složku nelze přečíst",
|
||||
"app.server.unreachable": "Nelze dosáhnout {{server}}",
|
||||
"app.server.retrying": "Automaticky opakování...",
|
||||
"app.server.otherServers": "Jiné servery",
|
||||
"dialog.server.title": "servery",
|
||||
"dialog.server.description": "Přepněte, ke kterému OpenCode serveru se tato aplikace připojuje.",
|
||||
"dialog.server.search.placeholder": "Vyhledávací servery",
|
||||
"dialog.server.empty": "Zatím žádné servery",
|
||||
"dialog.server.add.title": "Přidat server",
|
||||
"dialog.server.add.url": "Adresa serveru",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Nelze se připojit k serveru",
|
||||
"dialog.server.add.checking": "Kontrola...",
|
||||
"dialog.server.add.button": "Přidat server",
|
||||
"dialog.server.add.name": "Název serveru (volitelné)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "uživatelské jméno (volitelné)",
|
||||
"dialog.server.add.usernamePlaceholder": "uživatelské jméno",
|
||||
"dialog.server.add.password": "Heslo (volitelné)",
|
||||
"dialog.server.add.passwordPlaceholder": "heslo",
|
||||
"dialog.server.edit.title": "Upravit server",
|
||||
"dialog.server.default.title": "Výchozí server",
|
||||
"dialog.server.default.description":
|
||||
"Připojte se k tomuto serveru při spuštění aplikace namísto spuštění místního serveru. Vyžaduje restart.",
|
||||
"dialog.server.default.none": "Není vybrán žádný server",
|
||||
"dialog.server.default.set": "Nastavit aktuální server jako výchozí",
|
||||
"dialog.server.default.clear": "Jasný",
|
||||
"dialog.server.action.remove": "Odebrat server",
|
||||
"dialog.server.menu.edit": "Upravit",
|
||||
"dialog.server.menu.default": "Nastavit jako výchozí",
|
||||
"dialog.server.menu.defaultRemove": "Odebrat výchozí",
|
||||
"dialog.server.menu.delete": "Odstranit",
|
||||
"dialog.server.current": "Aktuální server",
|
||||
"dialog.server.status.default": "Výchozí",
|
||||
"wsl.server.add": "Přidat server WSL",
|
||||
"wsl.server.addShort": "Přidat WSL",
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL server",
|
||||
"wsl.server.retryStart": "Zkuste spustit znovu",
|
||||
"wsl.server.updating": "Aktualizace...",
|
||||
"wsl.onboarding.step.distro": "Vyberte distro",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "Kontrola WSL...",
|
||||
"wsl.onboarding.restartRequired": "Windows vyžaduje restart k dokončení instalace WSL.",
|
||||
"wsl.onboarding.ready": "WSL je připraven.",
|
||||
"wsl.onboarding.required": "Pro pokračování je vyžadováno WSL.",
|
||||
"wsl.onboarding.checkingDistros": "Kontrola distribucí...",
|
||||
"wsl.onboarding.installingDistro": "Instalace {{distro}}...",
|
||||
"wsl.onboarding.checkingDistro": "Kontrola {{distro}}...",
|
||||
"wsl.onboarding.listingDistros": "Výpis distribucí...",
|
||||
"wsl.onboarding.distroReady": "{{distro}} je připraven.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ještě není nainstalováno.",
|
||||
"wsl.onboarding.openDistroOnce": "Pro dokončení nastavení jednou otevřete {{distro}}.",
|
||||
"wsl.onboarding.finishingDistro": "Dokončování nastavení pro {{distro}}.",
|
||||
"wsl.onboarding.pickDistro": "Vyberte distribuci nebo ji nainstalujte níže.",
|
||||
"wsl.onboarding.checkingOpencode": "Kontrola OpenCode...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "Kontrola OpenCode v {{distro}}...",
|
||||
"wsl.onboarding.updatingOpencode": "Aktualizace OpenCode...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "Aktualizace OpenCode v {{distro}}...",
|
||||
"wsl.onboarding.updateOpencodeIn": "Aktualizace OpenCode v {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "Aktualizovat OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode je připraveno v {{distro}}.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode je připraven.",
|
||||
"wsl.onboarding.installOpencodeIn": "Nainstalujte OpenCode do {{distro}}.",
|
||||
"wsl.onboarding.installOpencode": "Instalovat OpenCode",
|
||||
"wsl.onboarding.chooseDistroFirst": "Nejprve si vyberte distro.",
|
||||
"wsl.onboarding.loadFailed": "Nepodařilo se načíst stav WSL.",
|
||||
"wsl.onboarding.loading": "Načítání...",
|
||||
"wsl.onboarding.installedDistros": "Nainstalované distribuce",
|
||||
"wsl.onboarding.checkAgain": "Zkontrolujte znovu",
|
||||
"wsl.onboarding.distroStatus.ready": "Připraveno",
|
||||
"wsl.onboarding.distroStatus.checking": "Kontrola...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode není nainstalováno",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Chybí bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nepodporováno · Použijte WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Potřebujete další distribuci?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Nainstalujte distribuci Linux z katalogu WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL není nainstalováno",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"WSL (Windows Subsystém pro Linux) je vyžadován předtím, než OpenCode může přidat server WSL",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL není k dispozici",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode nemohl ověřit WSL na tomto počítači.",
|
||||
"wsl.onboarding.installWsl": "Instalovat WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"Restartujte Windows pro dokončení instalace WSL a poté znovu otevřete OpenCode.",
|
||||
"wsl.onboarding.next": "Další",
|
||||
"wsl.onboarding.refresh": "Obnovit",
|
||||
"wsl.onboarding.allDistrosAdded": "Všechny nainstalované distribuce jsou již přidány.",
|
||||
"wsl.onboarding.noDistros": "Dosud nebyla zjištěna žádná distribuce.",
|
||||
"wsl.onboarding.install": "Instalovat",
|
||||
"wsl.onboarding.installing": "Instalace...",
|
||||
"wsl.onboarding.installDistro": "Nainstalujte distro",
|
||||
"wsl.onboarding.searchDistros": "Hledat distribuce",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 je vyžadováno.",
|
||||
"wsl.onboarding.toolsRequired": "Toto distro potřebuje bash a curl.",
|
||||
"wsl.onboarding.openTerminal": "Otevřete terminál",
|
||||
"wsl.onboarding.path": "Cesta: {{path}}",
|
||||
"wsl.onboarding.notFound": "nenalezen",
|
||||
"wsl.onboarding.version": "Verze: {{version}}",
|
||||
"wsl.onboarding.unknown": "neznámý",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Nainstalovaná verze neodpovídá verzi aplikace pro stolní počítače.",
|
||||
"wsl.onboarding.adding": "Přidávání...",
|
||||
"help.tabs.toast.ariaLabel": "Představujeme karty. Uspořádejte si práci a aktivní relace pomocí karet",
|
||||
"help.tabs.toast.dismiss": "Zavřít informace o kartách",
|
||||
"help.tabs.title": "Představujeme karty",
|
||||
"help.tabs.description": "Uspořádejte si práci a aktivní relace pomocí karet",
|
||||
"help.tabs.date": "14. července",
|
||||
"help.tabs.introduction": "OpenCode Plocha je nyní postavena na kartách.",
|
||||
"help.tabs.sessions":
|
||||
"Začněte novou relaci na kartě nebo otevřete existující relaci z libovolného z vašich projektů. Když začínáte s něčím novým, otevřete novou kartu a po dokončení ji zavřete.",
|
||||
"help.tabs.organize":
|
||||
"Ponechání několika otevřených karet usnadňuje organizaci aktivních relací. Přejmenujte karty na něco zapamatovatelného, pokud si je plánujete ponechat.",
|
||||
"help.tabs.home":
|
||||
"Všechny relace a projekty najdete na nové domovské obrazovce. Výběrem relace ji otevřete na kartě.",
|
||||
"help.tabs.persistence": "Když aplikaci znovu otevřete, vaše karty jsou stále otevřené.",
|
||||
"help.tabs.worktrees":
|
||||
"Nový design zatím nepodporuje Git Worktrees, brzy bude k dispozici. Pokud tedy chcete pokračovat v používání předchozího rozložení, můžete mezi rozloženími přepínat v Nastavení. Jen mějte na paměti, že nové rozložení se za několik týdnů stane trvalým.",
|
||||
"server.row.noUsername": "žádné uživatelské jméno",
|
||||
"dialog.project.edit.title": "Upravit projekt",
|
||||
"dialog.project.edit.name": "Jméno",
|
||||
"dialog.project.edit.icon": "ikona",
|
||||
"dialog.project.edit.icon.alt": "Ikona projektu",
|
||||
"dialog.project.edit.icon.hint": "Klikněte nebo přetáhněte obrázek",
|
||||
"dialog.project.edit.icon.recommended": "Doporučeno: 128x128px",
|
||||
"dialog.project.edit.color": "Barva",
|
||||
"dialog.project.edit.color.select": "Vyberte barvu {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "Spouštěcí skript pracovního prostoru",
|
||||
"dialog.project.edit.worktree.startup.description":
|
||||
"Spustí se po vytvoření nového pracovního prostoru (pracovního stromu).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "např. bun install",
|
||||
"dialog.releaseNotes.action.getStarted": "Začněte",
|
||||
"dialog.releaseNotes.action.next": "Další",
|
||||
"dialog.releaseNotes.action.hideFuture": "V budoucnu je nezobrazujte",
|
||||
"dialog.releaseNotes.media.alt": "Náhled vydání",
|
||||
"context.breakdown.title": "Rozdělení kontextu",
|
||||
"context.breakdown.note": 'Přibližný rozpis vstupních tokenů. "Jiné" zahrnuje definice nástrojů a režii.',
|
||||
"context.breakdown.system": "Systém",
|
||||
"context.breakdown.user": "Uživatel",
|
||||
"context.breakdown.assistant": "asistent",
|
||||
"context.breakdown.tool": "Volání nástroje",
|
||||
"context.breakdown.other": "Jiné",
|
||||
"context.systemPrompt.title": "Systémový prompt",
|
||||
"context.rawMessages.title": "Syrové zprávy",
|
||||
"context.export.session": "Export relace",
|
||||
"context.stats.session": "Relace",
|
||||
"context.stats.messages": "Zprávy",
|
||||
"context.stats.provider": "Poskytovatel",
|
||||
"context.stats.model": "Model",
|
||||
"context.stats.limit": "Kontextový limit",
|
||||
"context.stats.totalTokens": "Celkový počet tokenů",
|
||||
"context.stats.usage": "Využití",
|
||||
"context.stats.inputTokens": "Vstupní tokeny",
|
||||
"context.stats.outputTokens": "Výstupní tokeny",
|
||||
"context.stats.reasoningTokens": "Tokeny uvažování",
|
||||
"context.stats.cacheTokens": "Tokeny mezipaměti (čtení/zápis)",
|
||||
"context.stats.userMessages": "Uživatelské zprávy",
|
||||
"context.stats.assistantMessages": "Zprávy Asistenta",
|
||||
"context.stats.totalCost": "Celkové náklady",
|
||||
"context.stats.sessionCreated": "Relace vytvořena",
|
||||
"context.stats.lastActivity": "Poslední aktivita",
|
||||
"context.usage.tokens": "Tokeny",
|
||||
"context.usage.usage": "Využití",
|
||||
"context.usage.cost": "náklady",
|
||||
"context.usage.clickToView": "Kliknutím zobrazíte kontext",
|
||||
"context.usage.view": "Zobrazit kontextové použití",
|
||||
"language.en": "anglicky",
|
||||
"language.zh": "简体中文",
|
||||
"language.zht": "繁體中文",
|
||||
"language.ko": "한국어",
|
||||
"language.de": "německy",
|
||||
"language.es": "španělsky",
|
||||
"language.fr": "Français",
|
||||
"language.da": "Dansk",
|
||||
"language.ja": "日本語",
|
||||
"language.pl": "Polski",
|
||||
"language.ru": "Русский",
|
||||
"language.ar": "العربية",
|
||||
"language.no": "Norsk",
|
||||
"language.br": "português (Brazílie)",
|
||||
"language.bs": "Bosanský",
|
||||
"language.uk": "Українська",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Turecko",
|
||||
"toast.language.title": "Jazyk",
|
||||
"toast.language.description": "Přepnuto na {{language}}",
|
||||
"toast.theme.title": "Téma se změnilo",
|
||||
"toast.scheme.title": "Barevné schéma",
|
||||
"toast.workspace.enabled.title": "Pracovní prostory povoleny",
|
||||
"toast.workspace.enabled.description": "V postranním panelu se nyní zobrazuje více pracovních stromů",
|
||||
"toast.workspace.disabled.title": "Pracovní prostory jsou zakázány",
|
||||
"toast.workspace.disabled.description": "Na postranním panelu je zobrazen pouze hlavní pracovní strom",
|
||||
"toast.permissions.autoaccept.on.title": "Automatické přijímání oprávnění",
|
||||
"toast.permissions.autoaccept.on.description": "Žádosti o povolení budou automaticky schváleny",
|
||||
"toast.permissions.autoaccept.off.title": "Zastaveno automatické přijímání oprávnění",
|
||||
"toast.permissions.autoaccept.off.description": "Žádosti o povolení budou vyžadovat schválení",
|
||||
"toast.model.none.title": "Není vybrán žádný model",
|
||||
"toast.model.none.description": "Chcete-li tuto relaci shrnout, připojte poskytovatele",
|
||||
"toast.file.loadFailed.title": "Soubor se nepodařilo načíst",
|
||||
"toast.file.listFailed.title": "Seznam souborů se nezdařil",
|
||||
"toast.context.noLineSelection.title": "Žádný výběr řádku",
|
||||
"toast.context.noLineSelection.description": "Nejprve vyberte rozsah řádků na kartě souboru.",
|
||||
"toast.session.share.copyFailed.title": "Zkopírování URL do schránky se nezdařilo",
|
||||
"toast.session.share.success.title": "Relace sdílena",
|
||||
"toast.session.share.success.description": "Sdílet URL zkopírováno do schránky!",
|
||||
"toast.session.share.failed.title": "Sdílení relace se nezdařilo",
|
||||
"toast.session.share.failed.description": "Při sdílení relace došlo k chybě",
|
||||
"toast.session.unshare.success.title": "Relace byla zrušena",
|
||||
"toast.session.unshare.success.description": "Sdílení relace bylo úspěšně zrušeno!",
|
||||
"toast.session.unshare.failed.title": "Zrušení sdílení relace se nezdařilo",
|
||||
"toast.session.unshare.failed.description": "Při zrušení sdílení relace došlo k chybě",
|
||||
"toast.session.export.success.title": "Relace byla exportována",
|
||||
"toast.session.export.success.description": "Relace byla uložena do {{filename}}",
|
||||
"toast.session.export.failed.title": "Export relace se nezdařil",
|
||||
"toast.session.export.failed.description": "Při exportu relace došlo k chybě",
|
||||
"toast.session.listFailed.title": "Nepodařilo se načíst relace pro {{project}}",
|
||||
"toast.project.reloadFailed.title": "Nepodařilo se znovu načíst {{project}}",
|
||||
"toast.update.title": "Aktualizace k dispozici",
|
||||
"toast.update.description": "Nyní je k dispozici k instalaci nová verze OpenCode ({{version}}).",
|
||||
"toast.update.action.installRestart": "Nainstalujte a restartujte",
|
||||
"toast.update.action.notYet": "Ještě ne",
|
||||
"error.page.title": "Něco se pokazilo",
|
||||
"error.page.description": "Při načítání aplikace došlo k chybě.",
|
||||
"error.page.description.localServerStartup": "Při spouštění místního serveru došlo k chybě.",
|
||||
"error.page.details.label": "Podrobnosti o chybě",
|
||||
"error.page.action.restart": "Restartujte",
|
||||
"error.page.action.report": "Nahlásit chybu",
|
||||
"error.page.action.reported": "Nahlášena chyba",
|
||||
"error.page.action.exportLogs": "Exportovat protokoly",
|
||||
"error.page.action.checking": "Kontrola...",
|
||||
"error.page.action.checkUpdates": "Zkontrolujte aktualizace",
|
||||
"error.page.action.updateTo": "Aktualizovat na {{version}}",
|
||||
"error.page.circular": "[Kruhový]",
|
||||
"error.page.report.prefix": "Nahlaste tuto chybu týmu OpenCode",
|
||||
"error.page.report.discord": "dne Discord",
|
||||
"error.page.version": "Verze: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"Kořenový prvek nenalezen. Zapomněli jste to přidat do index.html? Nebo je možná chyba v atributu id?",
|
||||
"error.serverSync.connectFailed": "Nelze se připojit k serveru. Existuje server spuštěný na `{{url}}`?",
|
||||
"error.serverSDK.noServerAvailable": "Žádný dostupný server",
|
||||
"error.serverSDK.serverNotAvailable": "Server není dostupný",
|
||||
"error.childStore.persistedCacheCreateFailed": "Nepodařilo se vytvořit trvalou mezipaměť",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "Nepodařilo se vytvořit trvalá metadata projektu",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Nepodařilo se vytvořit trvalou ikonu projektu",
|
||||
"error.childStore.storeCreateFailed": "Obchod se nepodařilo vytvořit",
|
||||
"directory.error.invalidUrl": "Neplatný adresář v URL.",
|
||||
"error.chain.unknown": "Neznámá chyba",
|
||||
"error.server.invalidConfiguration": "Neplatná konfigurace",
|
||||
"error.chain.causedBy": "Způsobeno:",
|
||||
"error.chain.apiError": "API chyba",
|
||||
"error.chain.status": "Stav: {{status}}",
|
||||
"error.chain.retryable": "Opakovatelný pokus: {{retryable}}",
|
||||
"error.chain.responseBody": "Tělo odpovědi:\n{{body}}",
|
||||
"error.chain.didYouMean": "Měli jste na mysli: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model nenalezen: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Zkontrolujte název poskytovatele/modelu konfigurace (opencode.json).",
|
||||
"error.chain.mcpFailed": 'MCP server "{{name}}" selhal. Poznámka: OpenCode zatím nepodporuje ověřování MCP.',
|
||||
"error.chain.providerAuthFailed": "Ověření poskytovatele se nezdařilo ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'Nepodařilo se inicializovat poskytovatele "{{provider}}". Zkontrolujte přihlašovací údaje a konfiguraci.',
|
||||
"error.chain.configJsonInvalid": "Konfigurační soubor na {{path}} není platný JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage": "Konfigurační soubor na {{path}} není platný JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Adresář "{{dir}}" v {{path}} není platný. Přejmenujte adresář na "{{suggestion}}" nebo jej odeberte. Toto je běžný překlep.',
|
||||
"error.chain.configFrontmatterError": "Nepodařilo se analyzovat frontmatter v {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "Konfigurační soubor na {{path}} je neplatný",
|
||||
"error.chain.configInvalidWithMessage": "Konfigurační soubor na {{path}} je neplatný: {{message}}",
|
||||
"notification.permission.title": "Vyžaduje se povolení",
|
||||
"notification.permission.description": "{{sessionTitle}} v {{projectName}} potřebuje povolení",
|
||||
"notification.question.title": "Otázka",
|
||||
"notification.question.description": "{{sessionTitle}} v {{projectName}} má otázku",
|
||||
"notification.action.goToSession": "Přejít na relaci",
|
||||
"notification.session.responseReady.title": "Odpověď připravena",
|
||||
"notification.session.error.title": "Chyba relace",
|
||||
"notification.session.error.fallbackDescription": "Došlo k chybě",
|
||||
"home.recentProjects": "Nedávné projekty",
|
||||
"home.empty.title": "Žádné nedávné projekty",
|
||||
"home.empty.description": "Začněte otevřením místního projektu",
|
||||
"home.title": "Domů",
|
||||
"home.projects": "Projekty",
|
||||
"home.project.add": "Přidat projekt",
|
||||
"home.recentlyClosed": "Nedávno zavřeno",
|
||||
"home.server.collapse": "Sbalit projekty serveru",
|
||||
"home.server.expand": "Rozbalte projekty serveru",
|
||||
"home.sessions.search.placeholder": "Vyhledávání relací",
|
||||
"home.sessions.search.placeholder.scoped": "Hledat relace v {{scope}}",
|
||||
"home.sessions.search.sessions": "Relace",
|
||||
"home.sessions.search.noResults": "Pro {{query}} nebyly nalezeny žádné relace",
|
||||
"home.sessions.empty": "Tady zatím nic",
|
||||
"home.sessions.empty.description": "Začněte vytvořením relace",
|
||||
"home.sessions.group.today": "dnes",
|
||||
"home.sessions.group.yesterday": "včera",
|
||||
"home.sessions.group.older": "Starší",
|
||||
"home.providerTip":
|
||||
"Připojte se k více než 75 poskytovatelům a použijte další modely, včetně Claude, GPT, Gemini atd",
|
||||
"session.tab.session": "Relace",
|
||||
"session.tab.review": "Recenze",
|
||||
"session.tab.context": "Kontext",
|
||||
"session.tab.unknown": "Neznámá relace",
|
||||
"session.panel.reviewAndFiles": "Recenze a soubory",
|
||||
"session.error.notFound": "Tuto relaci nelze najít",
|
||||
"session.error.notFound.description": "Tato karta ukazuje na relaci, která již na tomto serveru neexistuje.",
|
||||
"session.error.notFound.closeTab": "Zavřít kartu",
|
||||
"session.error.serverConnection": "K tomuto serveru se nelze připojit",
|
||||
"session.review.filesChanged": "Soubory změněny {{count}}",
|
||||
"session.review.change.one": "Změna",
|
||||
"session.review.change.other": "Změny",
|
||||
"session.review.loadingChanges": "Načítání změn...",
|
||||
"session.review.empty": "V této relaci zatím žádné změny",
|
||||
"session.review.noVcs": "Ne Git Zjištěn systém správy verzí, změny se nezobrazují",
|
||||
"session.review.noVcs.createGit.title": "Vytvořte úložiště Git",
|
||||
"session.review.noVcs.createGit.description": "Sledujte, kontrolujte a vracejte změny v tomto projektu",
|
||||
"session.review.noVcs.createGit.actionLoading": "Vytváření Git úložiště...",
|
||||
"session.review.noVcs.createGit.action": "Vytvořit Git úložiště",
|
||||
"session.review.noSnapshot": "Sledování snímků je v konfiguraci zakázáno, takže změny relace nejsou dostupné",
|
||||
"session.review.noChanges": "Žádné změny",
|
||||
"session.review.noUncommittedChanges": "Zatím žádné neprovedené změny",
|
||||
"session.review.noBranchChanges": "Zatím žádné změny ve větvi",
|
||||
"session.files.selectToOpen": "Vyberte soubor, který chcete otevřít",
|
||||
"session.files.all": "Všechny soubory",
|
||||
"session.files.empty": "Žádné soubory",
|
||||
"session.files.binaryContent": "Binární soubor (obsah nelze zobrazit)",
|
||||
"session.messages.renderEarlier": "Vykreslit dřívější zprávy",
|
||||
"session.messages.loadingEarlier": "Načítání dřívějších zpráv...",
|
||||
"session.messages.loadEarlier": "Načíst dřívější zprávy",
|
||||
"session.messages.loading": "Načítání zpráv...",
|
||||
"session.messages.jumpToLatest": "Přejít na nejnovější",
|
||||
"session.context.addToContext": "Přidejte {{selection}} do kontextu",
|
||||
"session.todo.title": "Úkoly",
|
||||
"session.todo.collapse": "kolaps",
|
||||
"session.todo.expand": "Rozbalit",
|
||||
"session.todo.progress": "Dokončeno {{done}} z {{total}} úkolů",
|
||||
"session.question.progress": "{{current}} z {{total}} otázek",
|
||||
"session.question.minimize": "Minimalizujte otázku",
|
||||
"session.question.restore": "Obnovit otázku",
|
||||
"session.question.pending.one": "{{count}} nevyřízená otázka",
|
||||
"session.question.pending.other": "{{count}} nevyřízené otázky",
|
||||
"session.question.pending.few": "{{count}} nevyřízené otázky",
|
||||
"session.question.pending.many": "{{count}} nevyřízených otázek",
|
||||
"session.followupDock.summary.one": "{{count}} zpráva ve frontě",
|
||||
"session.followupDock.summary.other": "{{count}} zpráv ve frontě",
|
||||
"session.followupDock.summary.few": "{{count}} zprávy ve frontě",
|
||||
"session.followupDock.summary.many": "{{count}} zprávy ve frontě",
|
||||
"session.followupDock.sendNow": "Poslat nyní",
|
||||
"session.followupDock.edit": "Upravit",
|
||||
"session.followupDock.collapse": "Sbalit zprávy ve frontě",
|
||||
"session.followupDock.expand": "Rozbalte zprávy ve frontě",
|
||||
"session.revertDock.summary.one": "{{count}} vrácená zpráva",
|
||||
"session.revertDock.summary.other": "{{count}} vrátilo zpět zprávy",
|
||||
"session.revertDock.summary.few": "{{count}} vrácené zprávy",
|
||||
"session.revertDock.summary.many": "{{count}} vrácené zprávy",
|
||||
"session.revertDock.collapse": "Sbalit vrácené zprávy",
|
||||
"session.revertDock.expand": "Rozbalte vrácené zprávy",
|
||||
"session.revertDock.restore": "Obnovit zprávu",
|
||||
"session.new.title": "Postavte cokoliv",
|
||||
"session.new.project.new": "Nový projekt",
|
||||
"session.new.project.search": "Hledat projekty",
|
||||
"session.new.project.add": "Přidat projekt",
|
||||
"session.new.worktree.main": "Hlavní větev",
|
||||
"session.new.worktree.mainWithBranch": "Hlavní větev ({{branch}})",
|
||||
"session.new.worktree.create": "Vytvořte nový pracovní strom",
|
||||
"session.new.workspace.runIn": "Spustit relaci",
|
||||
"session.new.workspace.triggerLocal": "Místní",
|
||||
"session.new.workspace.local": "Místní úložiště",
|
||||
"session.new.workspace.existing": "Pracovní prostor…",
|
||||
"session.new.git.none": "ne Git",
|
||||
"session.new.lastModified": "Naposledy upraveno",
|
||||
"session.header.search.placeholder": "Hledat {{project}}",
|
||||
"session.header.searchFiles": "Hledat soubory",
|
||||
"session.header.openIn": "Otevřít v",
|
||||
"session.header.open.action": "Otevřít {{app}}",
|
||||
"session.header.open.ariaLabel": "Otevřít v {{app}}",
|
||||
"session.header.open.menu": "Otevřete možnosti",
|
||||
"session.header.open.copyPath": "Kopírovat cestu",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.open.fileManager": "Správce souborů",
|
||||
"session.header.reveal.finder": "Odhalit v Finder",
|
||||
"session.header.reveal.fileExplorer": "Odhalit v File Explorer",
|
||||
"session.header.reveal.containingFolder": "Otevřete složku obsahující",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
"session.header.open.app.zed": "Zed",
|
||||
"session.header.open.app.textmate": "TextMate",
|
||||
"session.header.open.app.antigravity": "Antigravity",
|
||||
"session.header.open.app.terminal": "Terminál",
|
||||
"session.header.open.app.iterm2": "iTerm2",
|
||||
"session.header.open.app.ghostty": "Ghostty",
|
||||
"session.header.open.app.warp": "Warp",
|
||||
"session.header.open.app.xcode": "Xcode",
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"status.popover.trigger": "Stav",
|
||||
"status.popover.ariaLabel": "Konfigurace serveru",
|
||||
"status.popover.tab.servers": "servery",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Pluginy",
|
||||
"status.popover.action.manageServers": "Správa serverů",
|
||||
"session.share.popover.title": "Publikovat na webu",
|
||||
"session.share.popover.description.shared": "Tato relace je veřejná na webu. Je přístupný komukoli s odkazem.",
|
||||
"session.share.popover.description.unshared": "Sdílejte relaci veřejně na webu. Bude přístupný komukoli s odkazem.",
|
||||
"session.share.action.share": "Sdílejte",
|
||||
"session.share.action.publish": "Publikovat",
|
||||
"session.share.action.publishing": "Publikování...",
|
||||
"session.share.action.unpublish": "Zrušit publikování",
|
||||
"session.share.action.unpublishing": "Rušení publikování...",
|
||||
"session.share.action.view": "Zobrazit",
|
||||
"session.share.copy.copied": "Zkopírováno",
|
||||
"session.share.copy.copyLink": "Zkopírujte odkaz",
|
||||
"lsp.tooltip.none": "Žádné LSP servery",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "Načítání výzvy...",
|
||||
"terminal.loading": "Načítání terminálu...",
|
||||
"terminal.title": "Terminál",
|
||||
"terminal.title.numbered": "Terminál {{number}}",
|
||||
"terminal.close": "Zavřete terminál",
|
||||
"terminal.connectionLost.title": "Připojení ztraceno",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket abnormálně uzavřeno: {{code}}",
|
||||
"terminal.connectionLost.description":
|
||||
"Spojení terminálu bylo přerušeno. To se může stát, když se server restartuje.",
|
||||
"terminal.connectTicket.csrfError":
|
||||
"PTY lístek připojení zamítnut původem nebo CSRF kontrolami. Zkontrolujte konfiguraci serveru CORS.",
|
||||
"terminal.connectTicket.statusError": "PTY lístek připojení se nezdařil s {{status}}",
|
||||
"titlebar.update": "Aktualizovat",
|
||||
"titlebar.updateVersion": "Aktualizovat {{version}}",
|
||||
"common.closeTab": "Zavřít kartu",
|
||||
"common.dismiss": "Odmítnout",
|
||||
"common.moreCountSuffix": " (+{{count}} další)",
|
||||
"common.requestFailed": "Požadavek se nezdařil",
|
||||
"common.moreOptions": "Více možností",
|
||||
"common.learnMore": "Zjistěte více",
|
||||
"common.rename": "Přejmenovat",
|
||||
"common.export": "Exportovat",
|
||||
"common.reset": "Resetovat",
|
||||
"common.archive": "Archiv",
|
||||
"common.delete": "Odstranit",
|
||||
"common.close": "Zavřít",
|
||||
"common.edit": "Upravit",
|
||||
"common.loadMore": "Načíst více",
|
||||
"common.key.esc": "ESC",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Space",
|
||||
"common.key.backspace": "Backspace",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
"common.key.home": "Home",
|
||||
"common.key.end": "End",
|
||||
"common.key.pageUp": "Page Up",
|
||||
"common.key.pageDown": "Page Down",
|
||||
"common.key.insert": "Insert",
|
||||
"common.unknown": "neznámý",
|
||||
"common.time.justNow": "Právě teď",
|
||||
"common.time.minutesAgo.short": "před {{count}}min",
|
||||
"common.time.hoursAgo.short": "před {{count}}h",
|
||||
"common.time.daysAgo.short": "před {{count}} d",
|
||||
"sidebar.menu.toggle": "Přepnout nabídku",
|
||||
"sidebar.nav.projectsAndSessions": "Projekty a relace",
|
||||
"sidebar.settings": "Nastavení",
|
||||
"sidebar.help": "Pomoc",
|
||||
"sidebar.workspaces.enable": "Povolit pracovní prostory",
|
||||
"sidebar.workspaces.disable": "Zakázat pracovní prostory",
|
||||
"sidebar.gettingStarted.title": "Začínáme",
|
||||
"sidebar.gettingStarted.line1": "OpenCode obsahuje bezplatné modely, takže můžete začít okamžitě.",
|
||||
"sidebar.gettingStarted.line2":
|
||||
"Připojte libovolného poskytovatele, abyste mohli používat modely, vč. Claude, GPT, Gemini atd.",
|
||||
"sidebar.project.recentSessions": "Nedávné relace",
|
||||
"sidebar.project.viewAllSessions": "Zobrazit všechny relace",
|
||||
"sidebar.project.clearNotifications": "Vymazat oznámení",
|
||||
"sidebar.empty.title": "Nejsou otevřeny žádné projekty",
|
||||
"sidebar.empty.description": "Chcete-li začít, otevřete projekt",
|
||||
"debugBar.ariaLabel": "Diagnostika vývoje výkonnosti",
|
||||
"debugBar.na": "n/a",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Poslední dokončený přechod trasy dotykem stránky relace, měřeno od spuštění routeru do prvního nátěru po jeho usazení.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Pohyblivé snímky za sekundu za posledních 5 sekund.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Nejhorší doba snímku za posledních 5 sekund.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Snímky delší než 32 ms za posledních 5 sekund.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Blokovaný čas a počet dlouhých úkolů za posledních 5 sekund. Maximální úkol: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "Nejhorší pozorované zpoždění vstupu za posledních 5 sekund.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Přibližná doba trvání interakce za posledních 5 sekund. Toto je INP, nikoli oficiální Web Vitals INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Kumulativní změna rozvržení pro aktuální životnost aplikace.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Použitá halda JS vs limit haldy. pouze Chromium.",
|
||||
"debugBar.mem.tip": "Použitá halda JS vs limit haldy. {{used}} z {{limit}}.",
|
||||
"debugBar.focus.label": "FOCUS",
|
||||
"debugBar.focus.tip": "Vynutit styly zaměření na všechny interaktivní prvky",
|
||||
"debugBar.focus.on": "ON",
|
||||
"debugBar.focus.off": "VYPNUTO",
|
||||
"debugBar.direction.label": "DIR",
|
||||
"debugBar.direction.tip": "Vynutit úplný směr rozvržení aplikace bez změny vybraného jazyka",
|
||||
"debugBar.direction.ltr": "LTR",
|
||||
"debugBar.direction.rtl": "RTL",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Desktop",
|
||||
"settings.section.server": "Server",
|
||||
"settings.tab.general": "Generál",
|
||||
"settings.tab.shortcuts": "Zkratky",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integrace",
|
||||
"settings.desktop.wsl.description": "Spusťte server OpenCode uvnitř WSL na Windows.",
|
||||
"settings.general.section.appearance": "Vzhled",
|
||||
"settings.general.section.advanced": "Pokročilé",
|
||||
"settings.general.section.notifications": "Systémová upozornění",
|
||||
"settings.general.section.updates": "Aktualizace",
|
||||
"settings.general.section.sounds": "Zvukové efekty",
|
||||
"settings.general.section.feed": "Krmivo",
|
||||
"settings.general.section.display": "Displej",
|
||||
"settings.general.row.language.title": "Jazyk",
|
||||
"settings.general.row.language.description": "Změnit jazyk zobrazení pro OpenCode",
|
||||
"settings.general.row.shell.title": "Shell terminálu",
|
||||
"settings.general.row.shell.description": "Shell používaný terminálem a nástroji agenta",
|
||||
"settings.general.row.shell.autoDefault": "Auto (výchozí)",
|
||||
"settings.general.row.shell.terminalOnly": "pouze terminál",
|
||||
"settings.general.row.appearance.title": "Vzhled",
|
||||
"settings.general.row.appearance.description": "Přizpůsobte si vzhled OpenCode na vašem zařízení",
|
||||
"settings.general.row.colorScheme.title": "Barevné schéma",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"Zvolte, zda bude OpenCode následovat systémové, světlé nebo tmavé téma",
|
||||
"settings.general.row.theme.title": "téma",
|
||||
"settings.general.row.theme.description": "Přizpůsobte, jak je téma OpenCode.",
|
||||
"settings.general.row.font.title": "Písmo kódu",
|
||||
"settings.general.row.font.description": "Přizpůsobte písmo používané v blocích kódu",
|
||||
"settings.general.row.terminalFont.title": "Terminálové písmo",
|
||||
"settings.general.row.terminalFont.description": "Přizpůsobte písmo použité v terminálu",
|
||||
"settings.general.row.uiFont.title": "UI Písmo",
|
||||
"settings.general.row.uiFont.description": "Přizpůsobte písmo používané v celém rozhraní",
|
||||
"settings.general.row.followup.title": "Následné chování",
|
||||
"settings.general.row.followup.description": "Zvolte, zda se mají následné výzvy řídit okamžitě nebo čekat ve frontě",
|
||||
"settings.general.row.followup.option.queue": "Fronta",
|
||||
"settings.general.row.followup.option.steer": "Řídit",
|
||||
"settings.general.row.showFileTree.title": "Strom souborů",
|
||||
"settings.general.row.showFileTree.description": "Zobrazit panel stromu souborů v relacích",
|
||||
"settings.general.row.showNavigation.title": "Ovládací prvky navigace",
|
||||
"settings.general.row.showNavigation.description": "Zobrazte tlačítka zpět a vpřed v záhlaví plochy",
|
||||
"settings.general.row.showSearch.title": "Paleta příkazů",
|
||||
"settings.general.row.showSearch.description": "Zobrazit tlačítko vyhledávací a příkazové palety v záhlaví",
|
||||
"settings.general.row.showTerminal.title": "Terminál",
|
||||
"settings.general.row.showTerminal.description": "Zobrazit tlačítko terminálu v záhlaví plochy",
|
||||
"settings.general.row.showStatus.title": "Stav serveru",
|
||||
"settings.general.row.showStatus.description": "Zobrazit tlačítko stavu serveru v záhlaví",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Spodní navigace",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Umístěte záhlaví a karty relací do spodní části obrazovky na mobilu",
|
||||
"settings.general.row.showCustomAgents.title": "Zobrazit agenta",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Přepínání mezi agenty ve skladateli. Když je skrytý, výchozí nastavení je Sestavit agenta.",
|
||||
"settings.general.row.reasoningSummaries.title": "Ukažte shrnutí odůvodnění",
|
||||
"settings.general.row.reasoningSummaries.description": "Zobrazte souhrny zdůvodnění modelu na časové ose",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Rozbalte části nástroje shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Ve výchozím nastavení zobrazit na časové ose rozbalené části nástroje shell",
|
||||
"settings.general.row.editToolPartsExpanded.title": "Rozbalte části nástroje pro úpravy",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Ve výchozím nastavení zobrazit na časové ose rozbalené části nástroje pro úpravy, zápis a opravy",
|
||||
"settings.general.row.newInterface.title": "Nové rozložení",
|
||||
"settings.general.row.newInterface.badge": "Nové",
|
||||
"settings.general.row.newInterface.description":
|
||||
"Použijte nové karty a domovské rozvržení. Přepínejte mezi rozvrženími po omezenou dobu.",
|
||||
"settings.general.row.newInterfaceNotice.title": "Nyní používáte nové rozvržení",
|
||||
"settings.general.row.newInterfaceNotice.description": "Předchozí rozložení již není k dispozici",
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Odmítnout",
|
||||
"settings.general.row.pinchZoom.title": "Stažením prstů přiblížíte",
|
||||
"settings.general.row.pinchZoom.description": "Povolit přiblížení pomocí gest trackpadu a gesta Ctrl posouvání",
|
||||
"settings.general.row.wayland.title": "Použít nativní Wayland",
|
||||
"settings.general.row.wayland.description": "Zakázat zálohu X11 na Wayland. Vyžaduje restart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Na Linux s monitory se smíšenou obnovovací frekvencí může být nativní Wayland stabilnější.",
|
||||
"settings.general.row.releaseNotes.title": "Poznámky k vydání",
|
||||
"settings.general.row.releaseNotes.description": "Zobrazit vyskakovací okna Co je nového po aktualizacích",
|
||||
"settings.updates.row.startup.title": "Zkontrolujte aktualizace při spuštění",
|
||||
"settings.updates.row.startup.description": "Automaticky kontrolovat aktualizace při spuštění OpenCode",
|
||||
"settings.updates.row.check.title": "Zkontrolujte aktualizace",
|
||||
"settings.updates.row.check.description": "Ručně zkontrolujte aktualizace a nainstalujte je, pokud jsou k dispozici",
|
||||
"settings.updates.action.checkNow": "Zkontrolujte nyní",
|
||||
"settings.updates.action.checking": "Kontrola...",
|
||||
"settings.updates.action.downloading": "Stahování...",
|
||||
"settings.updates.action.installing": "Instalace...",
|
||||
"settings.updates.toast.latest.title": "Jste aktuální",
|
||||
"settings.updates.toast.latest.description": "Používáte nejnovější verzi OpenCode.",
|
||||
"sound.option.none": "žádný",
|
||||
"sound.option.alert01": "Upozornění 01",
|
||||
"sound.option.alert02": "Upozornění 02",
|
||||
"sound.option.alert03": "Upozornění 03",
|
||||
"sound.option.alert04": "Upozornění 04",
|
||||
"sound.option.alert05": "Upozornění 05",
|
||||
"sound.option.alert06": "Upozornění 06",
|
||||
"sound.option.alert07": "Upozornění 07",
|
||||
"sound.option.alert08": "Upozornění 08",
|
||||
"sound.option.alert09": "Upozornění 09",
|
||||
"sound.option.alert10": "Upozornění 10",
|
||||
"sound.option.bipbop01": "Bip-bop 01",
|
||||
"sound.option.bipbop02": "Bip-bop 02",
|
||||
"sound.option.bipbop03": "Bip-bop 03",
|
||||
"sound.option.bipbop04": "Bip-bop 04",
|
||||
"sound.option.bipbop05": "Bip-bop 05",
|
||||
"sound.option.bipbop06": "Bip-bop 06",
|
||||
"sound.option.bipbop07": "Bip-bop 07",
|
||||
"sound.option.bipbop08": "Bip-bop 08",
|
||||
"sound.option.bipbop09": "Bip-bop 09",
|
||||
"sound.option.bipbop10": "Bip-bop 10",
|
||||
"sound.option.staplebops01": "Staplebops 01",
|
||||
"sound.option.staplebops02": "Staplebops 02",
|
||||
"sound.option.staplebops03": "Staplebops 03",
|
||||
"sound.option.staplebops04": "Staplebops 04",
|
||||
"sound.option.staplebops05": "Staplebops 05",
|
||||
"sound.option.staplebops06": "Staplebops 06",
|
||||
"sound.option.staplebops07": "Staplebops 07",
|
||||
"sound.option.nope01": "Ne 01",
|
||||
"sound.option.nope02": "Ne 02",
|
||||
"sound.option.nope03": "Ne 03",
|
||||
"sound.option.nope04": "Ne 04",
|
||||
"sound.option.nope05": "Ne 05",
|
||||
"sound.option.nope06": "Ne 06",
|
||||
"sound.option.nope07": "Ne 07",
|
||||
"sound.option.nope08": "Ne 08",
|
||||
"sound.option.nope09": "Ne 09",
|
||||
"sound.option.nope10": "Ne 10",
|
||||
"sound.option.nope11": "Ne 11",
|
||||
"sound.option.nope12": "Ne 12",
|
||||
"sound.option.yup01": "Ano 01",
|
||||
"sound.option.yup02": "Ano 02",
|
||||
"sound.option.yup03": "Ano 03",
|
||||
"sound.option.yup04": "Ano 04",
|
||||
"sound.option.yup05": "Ano 05",
|
||||
"sound.option.yup06": "Ano 06",
|
||||
"settings.general.notifications.agent.title": "Agent",
|
||||
"settings.general.notifications.agent.description":
|
||||
"Zobrazit systémové upozornění, když je agent kompletní nebo vyžaduje pozornost",
|
||||
"settings.general.notifications.permissions.title": "Oprávnění",
|
||||
"settings.general.notifications.permissions.description":
|
||||
"Zobrazit systémové upozornění, když je vyžadováno oprávnění",
|
||||
"settings.general.notifications.errors.title": "Chyby",
|
||||
"settings.general.notifications.errors.description": "Zobrazit systémové upozornění, když dojde k chybě",
|
||||
"settings.general.sounds.agent.title": "Agent",
|
||||
"settings.general.sounds.agent.description": "Přehrajte zvuk, když je agent kompletní nebo vyžaduje pozornost",
|
||||
"settings.general.sounds.permissions.title": "Oprávnění",
|
||||
"settings.general.sounds.permissions.description": "Přehrát zvuk, když je vyžadováno povolení",
|
||||
"settings.general.sounds.errors.title": "Chyby",
|
||||
"settings.general.sounds.errors.description": "Přehrát zvuk, když dojde k chybě",
|
||||
"settings.shortcuts.title": "Klávesové zkratky",
|
||||
"settings.shortcuts.reset.button": "Obnovit výchozí nastavení",
|
||||
"settings.shortcuts.reset.toast.title": "Zkratky byly resetovány",
|
||||
"settings.shortcuts.reset.toast.description": "Klávesové zkratky byly resetovány na výchozí hodnoty.",
|
||||
"settings.shortcuts.conflict.title": "Zástupce se již používá",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} je již přiřazeno {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Nepřiřazeno",
|
||||
"settings.shortcuts.pressKeys": "Stiskněte klávesy",
|
||||
"settings.shortcuts.search.placeholder": "Vyhledávací zkratky",
|
||||
"settings.shortcuts.search.empty": "Nebyli nalezeni žádní zástupci",
|
||||
"settings.shortcuts.group.general": "Generál",
|
||||
"settings.shortcuts.group.session": "Relace",
|
||||
"settings.shortcuts.group.navigation": "Navigace",
|
||||
"settings.shortcuts.group.modelAndAgent": "Model a agent",
|
||||
"settings.shortcuts.group.terminal": "Terminál",
|
||||
"settings.shortcuts.group.prompt": "Prompt",
|
||||
"settings.providers.title": "Poskytovatelé",
|
||||
"settings.providers.description": "Zde bude možné konfigurovat nastavení poskytovatele.",
|
||||
"settings.providers.section.connected": "Připojení poskytovatelé",
|
||||
"settings.providers.connected.empty": "Žádní připojení poskytovatelé",
|
||||
"settings.providers.connected.environmentDescription": "Připojeno z vašich proměnných prostředí",
|
||||
"settings.providers.section.popular": "Populární poskytovatelé",
|
||||
"settings.providers.custom.description": "Přidejte poskytovatele kompatibilního s OpenAI podle základny URL.",
|
||||
"settings.providers.tag.environment": "Životní prostředí",
|
||||
"settings.providers.tag.config": "Konfigurace",
|
||||
"settings.providers.tag.custom": "Vlastní",
|
||||
"settings.providers.tag.other": "Jiné",
|
||||
"settings.models.title": "Modely",
|
||||
"settings.models.description": "Zde bude možné konfigurovat nastavení modelu.",
|
||||
"settings.agents.title": "Agenti",
|
||||
"settings.agents.description": "Zde bude možné konfigurovat nastavení agenta.",
|
||||
"settings.commands.title": "Příkazy",
|
||||
"settings.commands.description": "Zde bude možné konfigurovat nastavení příkazů.",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "Zde bude možné konfigurovat nastavení MCP.",
|
||||
"settings.permissions.title": "Oprávnění",
|
||||
"settings.permissions.description": "Určete, jaké nástroje může server ve výchozím nastavení používat.",
|
||||
"settings.permissions.section.tools": "Nástroje",
|
||||
"settings.permissions.toast.updateFailed.title": "Aktualizace oprávnění se nezdařila",
|
||||
"settings.permissions.action.allow": "Povolit",
|
||||
"settings.permissions.action.ask": "Zeptejte se",
|
||||
"settings.permissions.action.deny": "Odmítnout",
|
||||
"settings.permissions.tool.read.title": "Přečtěte si",
|
||||
"settings.permissions.tool.read.description": "Čtení souboru (odpovídá cestě k souboru)",
|
||||
"settings.permissions.tool.edit.title": "Upravit",
|
||||
"settings.permissions.tool.edit.description": "Upravte soubory, včetně úprav, zápisů a oprav",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Porovnejte soubory pomocí vzorů glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Prohledávejte obsah souboru pomocí regulárních výrazů",
|
||||
"settings.permissions.tool.list.title": "Seznam",
|
||||
"settings.permissions.tool.list.description": "Seznam souborů v adresáři",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Spusťte příkazy shellu",
|
||||
"settings.permissions.tool.task.title": "Úkol",
|
||||
"settings.permissions.tool.task.description": "Spusťte dílčí agenty",
|
||||
"settings.permissions.tool.skill.title": "Dovednost",
|
||||
"settings.permissions.tool.skill.description": "Načtěte dovednost podle názvu",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Spusťte dotazy na jazykový server",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Napište",
|
||||
"settings.permissions.tool.todowrite.description": "Aktualizujte seznam úkolů",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Načíst obsah z URL",
|
||||
"settings.permissions.tool.websearch.title": "Vyhledávání na webu",
|
||||
"settings.permissions.tool.websearch.description": "Hledejte na webu",
|
||||
"settings.permissions.tool.external_directory.title": "Externí adresář",
|
||||
"settings.permissions.tool.external_directory.description": "Přístup k souborům mimo adresář projektu",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "Detekce opakovaných volání nástrojů se stejným zadáním",
|
||||
"session.delete.failed.title": "Smazání relace se nezdařilo",
|
||||
"session.delete.title": "Odstranit relaci",
|
||||
"session.delete.confirm": 'Odstranit relaci "{{name}}"?',
|
||||
"session.delete.button": "Odstranit relaci",
|
||||
"workspace.new": "Nový pracovní prostor",
|
||||
"workspace.type.local": "místní",
|
||||
"workspace.type.sandbox": "pískoviště",
|
||||
"workspace.create.failed.title": "Vytvoření pracovního prostoru se nezdařilo",
|
||||
"workspace.delete.failed.title": "Smazání pracovního prostoru se nezdařilo",
|
||||
"workspace.resetting.title": "Resetování pracovního prostoru",
|
||||
"workspace.resetting.description": "Může to chvíli trvat.",
|
||||
"workspace.reset.failed.title": "Obnovení pracovního prostoru se nezdařilo",
|
||||
"workspace.reset.success.title": "Obnovení pracovního prostoru",
|
||||
"workspace.reset.success.description": "Pracovní prostor nyní odpovídá výchozí větvi.",
|
||||
"workspace.error.stillPreparing": "Pracovní plocha se stále připravuje",
|
||||
"workspace.status.checking": "Kontrola nesloučených změn...",
|
||||
"workspace.status.error": "Nelze ověřit stav git.",
|
||||
"workspace.status.clean": "Nebyly zjištěny žádné nesloučené změny.",
|
||||
"workspace.status.dirty": "V tomto pracovním prostoru byly zjištěny nesloučené změny.",
|
||||
"workspace.delete.title": "Odstranit pracovní prostor",
|
||||
"workspace.delete.confirm": 'Odstranit pracovní prostor "{{name}}"?',
|
||||
"workspace.delete.button": "Odstranit pracovní prostor",
|
||||
"workspace.reset.title": "Obnovit pracovní prostor",
|
||||
"workspace.reset.confirm": "Resetovat pracovní prostor „{{name}}“?",
|
||||
"workspace.reset.button": "Obnovit pracovní prostor",
|
||||
"workspace.reset.archived.none": "Žádné aktivní relace nebudou archivovány.",
|
||||
"workspace.reset.archived.one": "1 relace bude archivována.",
|
||||
"workspace.reset.archived.many": "{{count}} relací bude archivováno.",
|
||||
"workspace.reset.note": "Tím se pracovní prostor resetuje tak, aby odpovídal výchozí větvi.",
|
||||
"dialog.usageExceeded.dontShowAgain": "Znovu nezobrazovat",
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user