mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-04 01:06:16 -04:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 393a8379ce | |||
| 638788f8d0 | |||
| b005a15a4a | |||
| 0efa33cf33 | |||
| 7e94ddafe8 | |||
| 7fe993879f | |||
| def14d96ef | |||
| b0b1149233 | |||
| 7becbbf711 | |||
| 87481f2bb6 | |||
| 9535a8f929 | |||
| e917c12c80 | |||
| 89130db6b0 | |||
| 9465a6c97d | |||
| 53fd02970d | |||
| 6ff0adef22 | |||
| 3307f99f1f | |||
| 8de7a3c739 | |||
| 28d0637406 | |||
| ef7f712867 | |||
| 3016830e25 | |||
| e9e7472456 | |||
| 9f9fbf815c | |||
| c88facb2cc | |||
| d4b85f8d83 | |||
| 1882c33827 | |||
| 01624c8c87 | |||
| 0891ecd8e0 | |||
| 32f278b48f | |||
| bb6bec9070 | |||
| 0eac0964af | |||
| f67e80c275 | |||
| 19231fce4b | |||
| d0a7ca6d1b | |||
| d80e86be9d | |||
| e4bd9757a3 | |||
| 4087cf1a97 | |||
| 14f0bf64a1 | |||
| 2df47ee394 | |||
| ad82616445 | |||
| d4ad650f73 | |||
| 2039c90c06 | |||
| da59457ca4 | |||
| db4dbaa289 | |||
| d3f30df180 | |||
| 7ea343cabd | |||
| e024e2ef92 | |||
| ceb4890ca3 | |||
| 3f239b3834 | |||
| c1ee3c6e34 | |||
| a1ab489e61 | |||
| 8c38d260eb | |||
| a4f25a94b4 | |||
| ff0382e971 | |||
| ac5380614f | |||
| c08350b83e | |||
| c5cf416633 | |||
| 3dbce3dade | |||
| a277557a5f | |||
| 005bf088c9 | |||
| eca5e68a5e | |||
| 5b11635d97 | |||
| a9eda2e99e | |||
| 73009f0b09 | |||
| f720490219 | |||
| 341c64cc97 | |||
| 1e17856ba4 | |||
| a6f7fe739c | |||
| 02f50a6e80 | |||
| 7565e03536 | |||
| 035a5a1a67 | |||
| e8b0992788 | |||
| 8cbea4fbb7 | |||
| f256a4c538 | |||
| a45c2b917e | |||
| f28d72d15e | |||
| d8a9d8a76d | |||
| 982a9044c5 | |||
| 7edefb3347 | |||
| 017a5977d2 | |||
| 7336cc5a7b | |||
| e1587588c4 | |||
| be93cec9ba | |||
| 7c898249a2 | |||
| 9e432a6785 | |||
| 1ead8d84a2 | |||
| 8c81f9a40b | |||
| 95636bd3ca | |||
| 7dae9a1083 | |||
| d46be565aa | |||
| 8021dbd80f | |||
| c8487bac54 | |||
| 484f00ebf4 | |||
| c3be6c4965 | |||
| 172d08cb98 | |||
| 5e39051183 | |||
| d50b9e8f9a | |||
| 921b1c6a34 | |||
| 9c8060d96d | |||
| 87db7a7f0a | |||
| 237e694df0 | |||
| 3f9dad3fd0 | |||
| 3cc70160de | |||
| b72d7b6fa2 | |||
| 029f5aa3a0 | |||
| 284e97068c | |||
| 970c38257c | |||
| c39ad38424 | |||
| 91508f6c88 | |||
| 1274408b89 | |||
| 7ae1be5bf6 | |||
| 40e4d730ca | |||
| 8dc539b4ae | |||
| 8e8d38c3cd | |||
| a9bebd3653 | |||
| 78f5764319 | |||
| 55b0211690 | |||
| 4f62295e44 | |||
| e1c41941a9 | |||
| 0b4edfc64c | |||
| 4469cecc4e | |||
| b06768aa8e | |||
| bc2d3df05f | |||
| 35075bb466 | |||
| a9afed0515 | |||
| 09afffeead | |||
| de78da8a23 | |||
| 9ca2b44235 | |||
| 7ffc22c0ef | |||
| 759f6afa80 | |||
| 1ad63cfc6c | |||
| 7d8195e8fe |
@@ -219,7 +219,6 @@ jobs:
|
||||
|
||||
build-electron:
|
||||
needs:
|
||||
- build-cli
|
||||
- version
|
||||
if: github.repository == 'anomalyco/opencode'
|
||||
continue-on-error: false
|
||||
@@ -316,10 +315,7 @@ 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
|
||||
@@ -343,8 +339,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 }}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"bin": {
|
||||
"lildax": "./bin/lildax.cjs",
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -194,7 +194,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -221,7 +221,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -287,7 +287,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -339,7 +339,7 @@
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "3.1.0",
|
||||
"gitlab-ai-provider": "6.11.1",
|
||||
"gitlab-ai-provider": "6.12.1",
|
||||
"glob": "13.0.5",
|
||||
"google-auth-library": "10.5.0",
|
||||
"gray-matter": "4.0.3",
|
||||
@@ -381,16 +381,16 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"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",
|
||||
@@ -435,7 +435,7 @@
|
||||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
@@ -461,7 +461,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -493,7 +493,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -509,7 +509,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "4.0.0-beta.83",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.83",
|
||||
@@ -540,7 +540,7 @@
|
||||
},
|
||||
"packages/llm": {
|
||||
"name": "@opencode-ai/llm",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
@@ -559,7 +559,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -632,7 +632,7 @@
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "3.1.0",
|
||||
"gitlab-ai-provider": "6.11.1",
|
||||
"gitlab-ai-provider": "6.12.1",
|
||||
"glob": "13.0.5",
|
||||
"google-auth-library": "10.5.0",
|
||||
"gray-matter": "4.0.3",
|
||||
@@ -690,7 +690,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -766,7 +766,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -781,7 +781,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
@@ -796,7 +796,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
|
||||
@@ -811,15 +811,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:",
|
||||
@@ -832,7 +828,6 @@
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/katex": "0.16.7",
|
||||
"@types/luxon": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -841,7 +836,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -854,7 +849,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -888,7 +883,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -907,7 +902,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -949,7 +944,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
@@ -976,7 +971,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -992,7 +987,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",
|
||||
@@ -1002,6 +996,7 @@
|
||||
"remend": "catalog:",
|
||||
"shiki": "catalog:",
|
||||
"solid-list": "catalog:",
|
||||
"solid-sonner": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1027,7 +1022,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1082,6 +1077,7 @@
|
||||
"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",
|
||||
"pacote@21.5.0": "patches/pacote@21.5.0.patch",
|
||||
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -1136,7 +1132,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",
|
||||
@@ -1145,6 +1141,7 @@
|
||||
"shiki": "4.2.0",
|
||||
"solid-js": "1.9.10",
|
||||
"solid-list": "0.3.0",
|
||||
"solid-sonner": "0.3.1",
|
||||
"sst": "4.13.1",
|
||||
"tailwindcss": "4.1.11",
|
||||
"typescript": "5.8.2",
|
||||
@@ -3854,7 +3851,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=="],
|
||||
|
||||
@@ -4316,9 +4313,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=="],
|
||||
|
||||
@@ -5126,6 +5121,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=="],
|
||||
@@ -6040,8 +6037,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/llm/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="],
|
||||
|
||||
+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-2suqkFAUeT2YgppKqP14s0C+GE3TR3uNKOOD8vWEiAg=",
|
||||
"aarch64-linux": "sha256-cJ/XYTodYmgs9ZFQhHQbfZPqxjIMmEvz062OsX6/6p4=",
|
||||
"aarch64-darwin": "sha256-tKyqHeI6r1aHEk4vBS39+eA6wh9KcIwsa2ctcktKkEc=",
|
||||
"x86_64-darwin": "sha256-1u7+dNjRcqKSvUTCEarVJ9fMrRUyg9y+4seZAm6Fmus="
|
||||
"x86_64-linux": "sha256-a7NyYa9vRUEqDfZNDPXXmFO58RDEgioyuGSl5CPBvxo=",
|
||||
"aarch64-linux": "sha256-l4OJtSEllHvRhktjcaJYwkXBSaJvsIoyoLusbZfYMcM=",
|
||||
"aarch64-darwin": "sha256-IIl0BQGs1/HLFh0auiQjiwfSQ2nfHcK2G2BAphYW59c=",
|
||||
"x86_64-darwin": "sha256-vVeuPyd4ZIRYrHouphTuEb4rkRZLKKTAHe840jNh9rU="
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -70,7 +70,7 @@
|
||||
"hono-openapi": "1.1.2",
|
||||
"fuzzysort": "3.1.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",
|
||||
@@ -90,6 +90,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"
|
||||
}
|
||||
@@ -143,6 +144,7 @@
|
||||
"@types/node": "catalog:"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
|
||||
"@ff-labs/fff-bun@0.9.3": "patches/@ff-labs%2Ffff-bun@0.9.3.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",
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { expectSessionTitle } from "../utils/waits"
|
||||
|
||||
const directory = "C:/OpenCode/OpenFileExpand"
|
||||
const projectID = "proj_open_file_expand"
|
||||
const sessionID = "ses_open_file_expand"
|
||||
const title = "Open file expand"
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
|
||||
test.use({ viewport: { width: 1440, height: 900 } })
|
||||
|
||||
test("expands a folder whose path has a trailing Windows separator", async ({ page }) => {
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
id: projectID,
|
||||
worktree: directory,
|
||||
vcs: "git",
|
||||
name: "open-file-expand",
|
||||
time: { created: 1700000000000, updated: 1700000000000 },
|
||||
sandboxes: [],
|
||||
},
|
||||
provider: {
|
||||
all: [
|
||||
{
|
||||
id: "opencode",
|
||||
name: "OpenCode",
|
||||
models: { test: { id: "test", name: "Test", limit: { context: 200_000 } } },
|
||||
},
|
||||
],
|
||||
connected: ["opencode"],
|
||||
default: { providerID: "opencode", modelID: "test" },
|
||||
},
|
||||
sessions: [
|
||||
{
|
||||
id: sessionID,
|
||||
slug: sessionID,
|
||||
projectID,
|
||||
directory,
|
||||
title,
|
||||
version: "dev",
|
||||
time: { created: 1700000000000, updated: 1700000000000 },
|
||||
},
|
||||
],
|
||||
vcsDiff: [],
|
||||
fileList: (path) => {
|
||||
if (path === "frontend\\" || path === "frontend") {
|
||||
return [
|
||||
{
|
||||
name: "app.ts",
|
||||
path: "frontend\\app.ts",
|
||||
absolute: `${directory}/frontend/app.ts`,
|
||||
type: "file" as const,
|
||||
ignored: false,
|
||||
},
|
||||
]
|
||||
}
|
||||
if (path) return []
|
||||
return [
|
||||
{
|
||||
name: "frontend",
|
||||
path: "frontend\\",
|
||||
absolute: `${directory}/frontend`,
|
||||
type: "directory" as const,
|
||||
ignored: false,
|
||||
},
|
||||
{
|
||||
name: "README.md",
|
||||
path: "README.md",
|
||||
absolute: `${directory}/README.md`,
|
||||
type: "file" as const,
|
||||
ignored: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
|
||||
pageMessages: () => ({ items: [] }),
|
||||
})
|
||||
|
||||
await page.addInitScript(
|
||||
({ directory, server, sessionID }) => {
|
||||
localStorage.setItem(
|
||||
"settings.v3",
|
||||
JSON.stringify({ general: { newLayoutDesigns: true, shouldDisplayTabsToast: false } }),
|
||||
)
|
||||
localStorage.setItem(
|
||||
"opencode.global.dat:server",
|
||||
JSON.stringify({
|
||||
projects: { local: [{ worktree: directory, expanded: true }] },
|
||||
lastProject: { local: directory },
|
||||
}),
|
||||
)
|
||||
localStorage.setItem(
|
||||
"opencode.global.dat:layout",
|
||||
JSON.stringify({ review: { diffStyle: "split", panelOpened: true } }),
|
||||
)
|
||||
localStorage.setItem(
|
||||
"opencode.global.dat:review-panel-v2",
|
||||
JSON.stringify({ sidebarOpened: true, sidebarWidth: 240, expandMode: "collapse" }),
|
||||
)
|
||||
localStorage.setItem(
|
||||
"opencode.window.browser.dat:tabs",
|
||||
JSON.stringify([{ type: "session", server, sessionId: sessionID }]),
|
||||
)
|
||||
},
|
||||
{ directory, server, sessionID },
|
||||
)
|
||||
|
||||
await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
|
||||
await expectSessionTitle(page, title)
|
||||
|
||||
const panel = page.locator("#review-panel")
|
||||
await panel.getByRole("button", { name: "Open file" }).click()
|
||||
await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
|
||||
|
||||
const sidebar = panel.locator('[data-component="session-review-v2-sidebar-root"]')
|
||||
await expect(sidebar).toBeVisible()
|
||||
|
||||
const frontendRow = panel.locator('[data-slot="file-tree-v2-row"][data-path="frontend"]')
|
||||
await expect(frontendRow).toBeVisible()
|
||||
await expect(frontendRow).toHaveAttribute("aria-expanded", "false")
|
||||
await frontendRow.click()
|
||||
await expect(frontendRow).toHaveAttribute("aria-expanded", "true")
|
||||
|
||||
const appRow = panel.locator('[data-slot="file-tree-v2-row"][data-path="frontend/app.ts"]')
|
||||
await expect(appRow).toBeVisible()
|
||||
await appRow.click()
|
||||
await expect(panel.getByRole("tab", { name: "app.ts" })).toHaveAttribute("data-selected", "")
|
||||
await expect(panel.getByText("contents:frontend/app.ts", { exact: true })).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)
|
||||
})
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { currentSession } from "../utils/mock-server"
|
||||
const server = "http://127.0.0.1:4096"
|
||||
const sessionA = session("ses_tab_a", "Tab A session")
|
||||
const sessionB = session("ses_tab_b", "Tab B session")
|
||||
const sessionC = session("ses_tab_c", "Tab C session")
|
||||
const unresolvedSessionID = "ses_tab_unresolved"
|
||||
|
||||
test("pressing mouse down on a tab navigates before mouse up", async ({ page }) => {
|
||||
await mockServer(page)
|
||||
@@ -40,6 +42,34 @@ test("pressing mouse down on a tab navigates before mouse up", async ({ page })
|
||||
await expect(page).toHaveURL(new RegExp(`${hrefB.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
|
||||
})
|
||||
|
||||
test("keyboard navigation follows the visible tab order", async ({ page }) => {
|
||||
await mockServer(page)
|
||||
await page.addInitScript(
|
||||
({ server, sessionA, unresolved, sessionC }) => {
|
||||
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
|
||||
localStorage.setItem(
|
||||
"opencode.window.browser.dat:tabs",
|
||||
JSON.stringify([
|
||||
{ type: "session", server, sessionId: sessionA },
|
||||
{ type: "session", server, sessionId: unresolved },
|
||||
{ type: "session", server, sessionId: sessionC },
|
||||
]),
|
||||
)
|
||||
},
|
||||
{ server, sessionA: sessionA.id, unresolved: unresolvedSessionID, sessionC: sessionC.id },
|
||||
)
|
||||
|
||||
const hrefA = `/server/${base64Encode(server)}/session/${sessionA.id}`
|
||||
const hrefC = `/server/${base64Encode(server)}/session/${sessionC.id}`
|
||||
await page.goto(hrefA)
|
||||
await expect(page.locator("[data-titlebar-tab-slot]:visible")).toHaveCount(2)
|
||||
await expect(page.locator(`[data-titlebar-tab-slot]:has(a[href="${hrefC}"])`)).toBeVisible()
|
||||
|
||||
await page.keyboard.press("Control+Alt+ArrowRight")
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`${hrefC.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
|
||||
})
|
||||
|
||||
function session(id: string, title: string) {
|
||||
return {
|
||||
id,
|
||||
@@ -53,10 +83,12 @@ function session(id: string, title: string) {
|
||||
}
|
||||
|
||||
async function mockServer(page: Page) {
|
||||
const sessions = [sessionA, sessionB]
|
||||
const sessions = [sessionA, sessionB, sessionC]
|
||||
await page.route("**/*", async (route) => {
|
||||
const url = new URL(route.request().url())
|
||||
if (url.origin !== server) return route.fallback()
|
||||
if ([`/api/session/${unresolvedSessionID}`, `/session/${unresolvedSessionID}`].includes(url.pathname))
|
||||
return new Promise(() => {})
|
||||
if (url.pathname === "/global/event" || url.pathname === "/event" || url.pathname === "/api/event")
|
||||
return sse(route)
|
||||
if (url.pathname === "/global/health") return json(route, { healthy: true })
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { expectAppVisible } from "../utils/waits"
|
||||
|
||||
const directory = "C:/OpenCode/NewProject"
|
||||
|
||||
test("creates a session in a new project, connects OpenCode Go, and selects its model", async ({ page }) => {
|
||||
let connectedGo = false
|
||||
let pendingGo = false
|
||||
const connections: Array<{ integrationID: string; body: unknown }> = []
|
||||
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
id: "proj_model_selection_flow",
|
||||
worktree: directory,
|
||||
vcs: "git",
|
||||
name: "NewProject",
|
||||
time: { created: 1_700_000_000_000, updated: 1_700_000_000_000 },
|
||||
sandboxes: [],
|
||||
},
|
||||
provider: () => ({
|
||||
all: [
|
||||
{
|
||||
id: "opencode",
|
||||
name: "OpenCode",
|
||||
models: {
|
||||
"free-model": {
|
||||
id: "free-model",
|
||||
name: "Free Model",
|
||||
cost: { input: 0, output: 0 },
|
||||
limit: { context: 200_000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "opencode-go",
|
||||
name: "OpenCode Go",
|
||||
models: {
|
||||
"go-model-1": {
|
||||
id: "go-model-1",
|
||||
name: "Go Model 1",
|
||||
cost: { input: 1, output: 1 },
|
||||
limit: { context: 200_000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connected: connectedGo ? ["opencode", "opencode-go"] : ["opencode"],
|
||||
default: { providerID: "opencode", modelID: "free-model" },
|
||||
}),
|
||||
integrationMethods: { "opencode-go": [{ type: "api", label: "API key" }] },
|
||||
onConnectKey: (input) => {
|
||||
connections.push(input)
|
||||
if (input.integrationID === "opencode-go") pendingGo = true
|
||||
},
|
||||
onInstanceDispose: () => {
|
||||
if (pendingGo) connectedGo = true
|
||||
},
|
||||
sessions: [],
|
||||
pageMessages: () => ({ items: [] }),
|
||||
fileList: (path) =>
|
||||
path ? [] : [{ name: "NewProject", path: "NewProject", absolute: directory, type: "directory", ignored: false }],
|
||||
findFiles: () => ["NewProject"],
|
||||
})
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
|
||||
localStorage.setItem("opencode.global.dat:server", JSON.stringify({ projects: { local: [] } }))
|
||||
})
|
||||
|
||||
await page.goto("/")
|
||||
const addProject = page.locator('[data-action="home-add-project-row"]')
|
||||
await expectAppVisible(addProject)
|
||||
await addProject.click()
|
||||
await page.locator("[data-directory-path]").click()
|
||||
|
||||
await page.locator('[data-action="home-new-session"]').click()
|
||||
await expectAppVisible(page.locator('[data-component="prompt-input-v2"]'))
|
||||
|
||||
const modelControl = page.locator('[data-action="prompt-model"]')
|
||||
await modelControl.click()
|
||||
await expect(page.locator('[data-section="free-models"]')).toContainText("Free models provided by OpenCode")
|
||||
|
||||
await page.locator('[data-provider-id="opencode-go"]').click()
|
||||
await page.locator('[data-input="provider-api-key"]').fill("mock-go-api-key")
|
||||
await page.locator('[data-action="provider-connect-submit"]').click()
|
||||
await expect(page.locator('[data-component="dialog-v2"]')).toHaveCount(0)
|
||||
expect(connections).toEqual([{ integrationID: "opencode-go", body: { type: "api", key: "mock-go-api-key" } }])
|
||||
|
||||
await expect(modelControl).toHaveAttribute("data-control-type", "popover")
|
||||
await modelControl.click()
|
||||
const goModel = page.locator('[data-option-key="opencode-go:go-model-1"]')
|
||||
await expect(goModel).toBeVisible()
|
||||
await goModel.click()
|
||||
|
||||
await expect(modelControl).toContainText("Go Model 1")
|
||||
})
|
||||
@@ -5,7 +5,10 @@ const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mc
|
||||
|
||||
export interface MockServerConfig {
|
||||
protocol?: "v1" | "v2"
|
||||
provider: unknown
|
||||
provider: unknown | (() => unknown)
|
||||
integrationMethods?: Record<string, unknown[]>
|
||||
onConnectKey?: (input: { integrationID: string; body: unknown }) => void
|
||||
onInstanceDispose?: () => void
|
||||
directory: string
|
||||
project: unknown
|
||||
sessions: ({ id: string } & Record<string, unknown>)[]
|
||||
@@ -31,7 +34,6 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
|
||||
const cursors = new Map<string, string>()
|
||||
let nextCursor = 0
|
||||
const staticRoutes: Record<string, unknown> = {
|
||||
"/provider": config.provider,
|
||||
"/path": {
|
||||
state: config.directory,
|
||||
config: config.directory,
|
||||
@@ -75,6 +77,18 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
|
||||
if (path === "/api/health" && config.protocol === "v2")
|
||||
return json(route, { healthy: true, version: "2.0.0", pid: 1 })
|
||||
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: true })
|
||||
if (path === "/provider")
|
||||
return json(route, typeof config.provider === "function" ? config.provider() : config.provider)
|
||||
if (path === "/provider/auth") return json(route, config.integrationMethods ?? {})
|
||||
const legacyAuth = path.match(/^\/auth\/([^/]+)$/)?.[1]
|
||||
if (legacyAuth && route.request().method() === "PUT") {
|
||||
config.onConnectKey?.({ integrationID: legacyAuth, body: route.request().postDataJSON() })
|
||||
return json(route, true)
|
||||
}
|
||||
if (path === "/instance/dispose" && route.request().method() === "POST") {
|
||||
config.onInstanceDispose?.()
|
||||
return json(route, true)
|
||||
}
|
||||
if (path === "/permission")
|
||||
return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
|
||||
if (path === "/question")
|
||||
@@ -130,8 +144,11 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
|
||||
location: location(config),
|
||||
data: { id: integration, name: integration, methods: [{ type: "key", label: "API key" }], connections: [] },
|
||||
})
|
||||
if (/^\/api\/integration\/[^/]+\/connect\/key$/.test(path) && route.request().method() === "POST")
|
||||
const integrationConnect = path.match(/^\/api\/integration\/([^/]+)\/connect\/key$/)?.[1]
|
||||
if (integrationConnect && route.request().method() === "POST") {
|
||||
config.onConnectKey?.({ integrationID: integrationConnect, body: route.request().postDataJSON() })
|
||||
return route.fulfill({ status: 204, headers: { "access-control-allow-origin": "*" } })
|
||||
}
|
||||
if (path === "/api/project") return json(route, [config.project])
|
||||
if (path === "/api/project/current")
|
||||
return json(route, { id: (config.project as { id?: string }).id, directory: config.directory })
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.6",
|
||||
"version": "1.18.12",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -19,9 +19,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"
|
||||
@@ -405,9 +404,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>
|
||||
|
||||
@@ -66,7 +66,7 @@ function Cell(props: {
|
||||
<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,
|
||||
@@ -74,21 +74,29 @@ function Cell(props: {
|
||||
>
|
||||
<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>
|
||||
)
|
||||
@@ -116,15 +124,25 @@ function FocusCell(props: { active: boolean; inline?: boolean; onClick: () => vo
|
||||
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">FOCUS</span>
|
||||
<span
|
||||
classList={{ "leading-none font-bold": true, "text-[11px]": !!props.inline, "text-[13px]": !props.inline }}
|
||||
>
|
||||
{props.active ? "ON" : "OFF"}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
@@ -28,9 +28,8 @@ import {
|
||||
Switch,
|
||||
} from "solid-js"
|
||||
import { createStore, produce } from "solid-js/store"
|
||||
import { useQueryClient } from "@tanstack/solid-query"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { Link } from "@/components/link"
|
||||
import { ExternalLink } from "@/components/external-link"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
@@ -38,7 +37,6 @@ import { useSettings } from "@/context/settings"
|
||||
import { popularProviders, useProviders } from "@/hooks/use-providers"
|
||||
import { CustomProviderForm } from "./dialog-custom-provider"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { pathKey } from "@/utils/path-key"
|
||||
|
||||
const CUSTOM_ID = "_custom"
|
||||
type ConnectMethod = Extract<IntegrationMethod, { type: "key" | "oauth" }>
|
||||
@@ -386,7 +384,6 @@ function ProviderConnection(props: {
|
||||
const dialog = useDialog()
|
||||
const serverSync = useServerSync()
|
||||
const serverSDK = useServerSDK()
|
||||
const queryClient = useQueryClient()
|
||||
const params = useParams()
|
||||
const language = useLanguage()
|
||||
const settings = useSettings()
|
||||
@@ -707,9 +704,8 @@ function ProviderConnection(props: {
|
||||
})
|
||||
|
||||
async function complete() {
|
||||
const value = directory()
|
||||
await queryClient
|
||||
.refetchQueries(serverSync().queryOptions.providers(value ? pathKey(value) : null))
|
||||
await serverSync()
|
||||
.refreshProviders()
|
||||
.catch(() => undefined)
|
||||
dialog.close()
|
||||
showToast({
|
||||
@@ -840,12 +836,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>
|
||||
@@ -857,6 +853,7 @@ function ProviderConnection(props: {
|
||||
ref={apiKey}
|
||||
class="!w-full"
|
||||
name="apiKey"
|
||||
data-input="provider-api-key"
|
||||
placeholder={language.t("provider.connect.apiKey.placeholder")}
|
||||
value={formStore.value}
|
||||
invalid={formStore.error !== undefined}
|
||||
@@ -873,7 +870,7 @@ function ProviderConnection(props: {
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
<ButtonV2 type="submit" variant="contrast">
|
||||
<ButtonV2 type="submit" variant="contrast" data-action="provider-connect-submit">
|
||||
{language.t("common.continue")}
|
||||
</ButtonV2>
|
||||
</form>
|
||||
@@ -889,9 +886,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>
|
||||
@@ -970,9 +967,9 @@ 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={store.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">
|
||||
@@ -1009,7 +1006,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={store.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">
|
||||
@@ -1080,7 +1079,9 @@ function ProviderConnection(props: {
|
||||
<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={store.authorization!.url}>
|
||||
{language.t("provider.connect.oauth.auto.visit.link")}
|
||||
</ExternalLink>
|
||||
{language.t("provider.connect.oauth.auto.visit.suffix", { provider: provider().name })}
|
||||
</div>
|
||||
<TextField
|
||||
|
||||
@@ -8,7 +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 { ExternalLink } from "@/components/external-link"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
@@ -185,9 +185,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>
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ export function DialogSelectDirectoryV2(props: DialogSelectDirectoryV2Props) {
|
||||
{(suggestion, index) => (
|
||||
<button
|
||||
id={`directory-picker-v2-suggestion-${index()}`}
|
||||
data-directory-path={suggestion.absolute}
|
||||
role="option"
|
||||
aria-selected={index() === activeSuggestion()}
|
||||
data-active={index() === activeSuggestion() ? "" : undefined}
|
||||
|
||||
@@ -18,6 +18,8 @@ interface DialogSelectDirectoryProps {
|
||||
server: ServerConnection.Any
|
||||
}
|
||||
|
||||
const RECENT_PROJECT_LIMIT = 5
|
||||
|
||||
type Row = {
|
||||
absolute: string
|
||||
search: string
|
||||
@@ -57,9 +59,9 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||
const [filter, setFilter] = createSignal("")
|
||||
let list: ListRef | undefined
|
||||
|
||||
const missingBase = createMemo(() => !(sync.data.path.home || sync.data.path.directory))
|
||||
const missingHome = createMemo(() => !sync.data.path.home)
|
||||
const [fallbackPath] = createResource(
|
||||
() => (missingBase() ? true : undefined),
|
||||
() => (missingHome() ? true : undefined),
|
||||
async (): Promise<Path | undefined> => {
|
||||
if ((await sdk.protocol) !== "v1") return
|
||||
return sdk.client.path
|
||||
@@ -102,7 +104,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)
|
||||
@@ -116,7 +117,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) {
|
||||
@@ -164,7 +168,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||
const path = displayPickerPath(item.absolute, filter(), home())
|
||||
if (path === "~") {
|
||||
return (
|
||||
<div class="w-full flex items-center justify-between rounded-md">
|
||||
<div data-directory-path={item.absolute} class="w-full flex items-center justify-between rounded-md">
|
||||
<div class="flex items-center gap-x-3 grow min-w-0">
|
||||
<FileIcon node={{ path: item.absolute, type: "directory" }} class="shrink-0 size-4" />
|
||||
<div class="flex items-center text-14-regular min-w-0">
|
||||
@@ -176,7 +180,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div class="w-full flex items-center justify-between rounded-md">
|
||||
<div data-directory-path={item.absolute} class="w-full flex items-center justify-between rounded-md">
|
||||
<div class="flex items-center gap-x-3 grow min-w-0">
|
||||
<FileIcon node={{ path: item.absolute, type: "directory" }} class="shrink-0 size-4" />
|
||||
<div class="flex items-center text-14-regular min-w-0">
|
||||
|
||||
@@ -76,7 +76,7 @@ export const DialogSelectModelUnpaidV2: Component<{ model?: ModelState }> = (pro
|
||||
</DialogHeader>
|
||||
<DialogBody class="max-h-[calc(100vh_-_68px)] min-h-0 flex-none gap-0 overflow-y-auto px-2 pb-2">
|
||||
<div ref={listEl} class="flex min-h-0 flex-col">
|
||||
<div class="flex w-full flex-col items-start pb-3">
|
||||
<div data-section="free-models" class="flex w-full flex-col items-start pb-3">
|
||||
<div class="flex h-8 w-full flex-none select-none flex-row items-center px-3 pb-2">
|
||||
<div class="flex h-5 items-center text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-muted [font-family:var(--v2-font-family-sans)] [font-variant-numeric:tabular-nums] [font-variation-settings:'slnt'_0]">
|
||||
{language.t("dialog.model.unpaid.freeModels.title")}
|
||||
@@ -134,6 +134,7 @@ export const DialogSelectModelUnpaidV2: Component<{ model?: ModelState }> = (pro
|
||||
{(provider) => (
|
||||
<button
|
||||
type="button"
|
||||
data-provider-id={provider.id}
|
||||
class="flex min-h-11 w-full scroll-my-3.5 flex-row items-start gap-2 rounded-md bg-v2-background-bg-base px-3 py-2.5 text-left text-[13px] font-[530] leading-5 tracking-[-0.04px] text-v2-text-text-base [font-family:var(--v2-font-family-sans)] [font-variation-settings:'slnt'_0] hover:bg-v2-background-bg-layer-01 focus:bg-v2-background-bg-layer-01 focus:outline-none"
|
||||
classList={{
|
||||
"border-[0.5px] border-transparent shadow-[var(--v2-elevation-raised)]":
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
import { Popover as Kobalte } from "@kobalte/core/popover"
|
||||
import {
|
||||
Component,
|
||||
ComponentProps,
|
||||
createEffect,
|
||||
createMemo,
|
||||
For,
|
||||
JSX,
|
||||
onCleanup,
|
||||
Show,
|
||||
ValidComponent,
|
||||
} from "solid-js"
|
||||
import { Component, ComponentProps, createEffect, createMemo, For, JSX, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useLocal } from "@/context/local"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
@@ -29,6 +19,7 @@ import { ModelTooltip } from "./model-tooltip"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { handleDocumentSearchKeydown } from "@/utils/search-keydown"
|
||||
import { createMenuDismissController } from "@/utils/menu-dismiss-controller"
|
||||
import { createEventListener } from "@solid-primitives/event-listener"
|
||||
import { matchesModelSearch } from "./dialog-select-model-search"
|
||||
|
||||
@@ -122,14 +113,13 @@ const ModelList: Component<{
|
||||
}
|
||||
|
||||
type ModelSelectorTriggerProps = Omit<ComponentProps<typeof Kobalte.Trigger>, "as" | "ref">
|
||||
type ModelSelectorTrigger = (props: ModelSelectorTriggerProps) => JSX.Element
|
||||
type Dismiss = "escape" | "outside" | "select" | "manage" | "provider"
|
||||
|
||||
export function ModelSelectorPopover(props: {
|
||||
provider?: string
|
||||
model?: ModelState
|
||||
children?: JSX.Element
|
||||
triggerAs?: ValidComponent
|
||||
triggerProps?: ModelSelectorTriggerProps
|
||||
trigger: ModelSelectorTrigger
|
||||
onClose?: (cause: "escape" | "select") => void
|
||||
}) {
|
||||
const [store, setStore] = createStore<{
|
||||
@@ -174,9 +164,7 @@ export function ModelSelectorPopover(props: {
|
||||
placement="top-start"
|
||||
gutter={4}
|
||||
>
|
||||
<Kobalte.Trigger as={props.triggerAs ?? "div"} {...props.triggerProps}>
|
||||
{props.children}
|
||||
</Kobalte.Trigger>
|
||||
<Kobalte.Trigger as={props.trigger} />
|
||||
<Kobalte.Portal>
|
||||
<Kobalte.Content
|
||||
class="w-72 h-80 flex flex-col p-2 rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden"
|
||||
@@ -237,66 +225,101 @@ export function ModelSelectorPopover(props: {
|
||||
export function ModelSelectorPopoverV2(props: {
|
||||
provider?: string
|
||||
model?: ModelState
|
||||
children?: JSX.Element
|
||||
triggerAs?: ValidComponent
|
||||
triggerProps?: ModelSelectorTriggerProps
|
||||
trigger: ModelSelectorTrigger
|
||||
onClose?: () => void
|
||||
}) {
|
||||
const model = props.model ?? useLocal().model
|
||||
const language = useLanguage()
|
||||
const dialog = useDialog()
|
||||
const [store, setStore] = createStore({ open: false, search: "", active: "" })
|
||||
let searchRef: HTMLInputElement | undefined
|
||||
let contentRef: HTMLDivElement | undefined
|
||||
let restoreTrigger = true
|
||||
const controller = createModelSelectorController({
|
||||
model: props.model,
|
||||
provider: () => props.provider,
|
||||
onSelect: () => props.onClose?.(),
|
||||
})
|
||||
|
||||
return (
|
||||
<ModelSelectorPopoverV2View
|
||||
trigger={props.trigger}
|
||||
models={controller.models}
|
||||
groups={controller.groups}
|
||||
current={controller.current}
|
||||
select={controller.select}
|
||||
onManage={() => {
|
||||
void import("./dialog-manage-models").then((module) => {
|
||||
void dialog.show(() => <module.DialogManageModelsV2 />)
|
||||
})
|
||||
}}
|
||||
onClose={() => props.onClose?.()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function createModelSelectorController(input: {
|
||||
provider: () => string | undefined
|
||||
model?: ModelState
|
||||
onSelect: () => void
|
||||
}) {
|
||||
const model = input.model ?? useLocal().model
|
||||
const allModels = createMemo(() =>
|
||||
model
|
||||
.list()
|
||||
.filter((item) => model.visible({ modelID: item.id, providerID: item.provider.id }))
|
||||
.filter((item) => (props.provider ? item.provider.id === props.provider : true)),
|
||||
.filter((item) => (input.provider() ? item.provider.id === input.provider() : true)),
|
||||
)
|
||||
const models = createMemo(() => {
|
||||
const search = store.search.trim()
|
||||
const filtered = search
|
||||
? allModels().filter((item) => matchesModelSearch(search, [item.name, item.id, item.provider.name]))
|
||||
: allModels()
|
||||
|
||||
return [...filtered].sort((a, b) => a.name.localeCompare(b.name))
|
||||
})
|
||||
const groups = createMemo(() => {
|
||||
const byProvider = new Map<string, ModelItem[]>()
|
||||
for (const item of models()) {
|
||||
byProvider.set(item.provider.id, [...(byProvider.get(item.provider.id) ?? []), item])
|
||||
}
|
||||
return Array.from(byProvider, ([category, items]) => ({ category, items })).sort(sortModelGroups)
|
||||
})
|
||||
const keys = () => [...models().map(modelKey), manageKey]
|
||||
const current = () => {
|
||||
const value = model.current()
|
||||
return value ? `${value.provider.id}:${value.id}` : undefined
|
||||
return {
|
||||
models: (search: string) => {
|
||||
const query = search.trim()
|
||||
const filtered = query
|
||||
? allModels().filter((item) => matchesModelSearch(query, [item.name, item.id, item.provider.name]))
|
||||
: allModels()
|
||||
return [...filtered].sort((a, b) => a.name.localeCompare(b.name))
|
||||
},
|
||||
groups: (models: ModelItem[]) => {
|
||||
const byProvider = new Map<string, ModelItem[]>()
|
||||
for (const item of models) {
|
||||
byProvider.set(item.provider.id, [...(byProvider.get(item.provider.id) ?? []), item])
|
||||
}
|
||||
return Array.from(byProvider, ([category, items]) => ({ category, items })).sort(sortModelGroups)
|
||||
},
|
||||
current: () => {
|
||||
const value = model.current()
|
||||
return value ? modelKey(value) : undefined
|
||||
},
|
||||
select: (item: ModelItem) => {
|
||||
model.set({ modelID: item.id, providerID: item.provider.id }, { recent: true })
|
||||
input.onSelect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function ModelSelectorPopoverV2View(props: {
|
||||
trigger: ModelSelectorTrigger
|
||||
models: (search: string) => ModelItem[]
|
||||
groups: (models: ModelItem[]) => { category: string; items: ModelItem[] }[]
|
||||
current: () => string | undefined
|
||||
select: (item: ModelItem) => void
|
||||
onManage: () => void
|
||||
onClose: () => void
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({ open: false, search: "", active: "" })
|
||||
let searchRef: HTMLInputElement | undefined
|
||||
let contentRef: HTMLDivElement | undefined
|
||||
const dismiss = createMenuDismissController(() => contentRef)
|
||||
|
||||
const models = createMemo(() => props.models(store.search))
|
||||
const groups = createMemo(() => props.groups(models()))
|
||||
const keys = () => [...models().map(modelKey), manageKey]
|
||||
const initialActive = () => {
|
||||
const selected = current()
|
||||
const selected = props.current()
|
||||
const options = keys()
|
||||
if (selected && options.includes(selected)) return selected
|
||||
return options[0] ?? ""
|
||||
}
|
||||
const activeItem = () =>
|
||||
store.active ? contentRef?.querySelector<HTMLElement>(`[data-option-key="${CSS.escape(store.active)}"]`) : undefined
|
||||
const afterClose = (callback: () => void) => {
|
||||
const complete = () => {
|
||||
if (contentRef?.isConnected) {
|
||||
requestAnimationFrame(complete)
|
||||
return
|
||||
}
|
||||
requestAnimationFrame(() => requestAnimationFrame(callback))
|
||||
}
|
||||
requestAnimationFrame(complete)
|
||||
}
|
||||
const setOpen = (open: boolean) => {
|
||||
if (open) {
|
||||
restoreTrigger = true
|
||||
dismiss.allowTriggerRestore()
|
||||
setStore({ open: true, active: initialActive() })
|
||||
setTimeout(() =>
|
||||
requestAnimationFrame(() => {
|
||||
@@ -308,23 +331,15 @@ export function ModelSelectorPopoverV2(props: {
|
||||
}
|
||||
setStore({ open: false, search: "", active: "" })
|
||||
}
|
||||
const select = (item: ModelItem) => {
|
||||
model.set({ modelID: item.id, providerID: item.provider.id }, { recent: true })
|
||||
props.onClose?.()
|
||||
}
|
||||
const selectModel = (item: ModelItem) => {
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
setOpen(false)
|
||||
afterClose(() => select(item))
|
||||
dismiss.afterClose(() => props.select(item))
|
||||
}
|
||||
const manage = () => {
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
setOpen(false)
|
||||
afterClose(() => {
|
||||
void import("./dialog-manage-models").then((x) => {
|
||||
dialog.show(() => <x.DialogManageModelsV2 />)
|
||||
})
|
||||
})
|
||||
dismiss.afterClose(props.onManage)
|
||||
}
|
||||
const selectActive = () => {
|
||||
const item = models().find((item) => modelKey(item) === store.active)
|
||||
@@ -343,10 +358,7 @@ export function ModelSelectorPopoverV2(props: {
|
||||
queueMicrotask(() => activeItem()?.scrollIntoView({ block: "nearest" }))
|
||||
}
|
||||
const setSearch = (value: string) => {
|
||||
const search = value.trim()
|
||||
const first = [...allModels()]
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.find((item) => matchesModelSearch(search, [item.name, item.id, item.provider.name]))
|
||||
const first = props.models(value)[0]
|
||||
setStore({ search: value, active: first ? modelKey(first) : manageKey })
|
||||
}
|
||||
|
||||
@@ -362,18 +374,14 @@ export function ModelSelectorPopoverV2(props: {
|
||||
|
||||
return (
|
||||
<MenuV2 open={store.open} modal={false} placement="top-start" gutter={6} onOpenChange={setOpen}>
|
||||
<MenuV2.Trigger as={props.triggerAs ?? "div"} {...props.triggerProps}>
|
||||
{props.children}
|
||||
</MenuV2.Trigger>
|
||||
<MenuV2.Trigger as={props.trigger} />
|
||||
<MenuV2.Portal>
|
||||
<MenuV2.Content
|
||||
ref={(el: HTMLDivElement) => (contentRef = el)}
|
||||
ref={(element: HTMLDivElement) => (contentRef = element)}
|
||||
class="w-[284px] overflow-hidden rounded-md border-0 bg-v2-background-bg-layer-01 !p-0 shadow-[var(--v2-elevation-floating)] focus:outline-none"
|
||||
onPointerDownOutside={() => (restoreTrigger = false)}
|
||||
onFocusOutside={() => (restoreTrigger = false)}
|
||||
onCloseAutoFocus={(event) => {
|
||||
if (!restoreTrigger) event.preventDefault()
|
||||
}}
|
||||
onPointerDownOutside={dismiss.preventTriggerRestore}
|
||||
onFocusOutside={dismiss.preventTriggerRestore}
|
||||
onCloseAutoFocus={dismiss.onCloseAutoFocus}
|
||||
>
|
||||
<div class="flex flex-col p-0.5">
|
||||
<div class="flex h-7 items-center gap-2 rounded-sm pl-3 pr-2.5 text-v2-icon-icon-muted">
|
||||
@@ -393,9 +401,9 @@ export function ModelSelectorPopoverV2(props: {
|
||||
event.stopPropagation()
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault()
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
setOpen(false)
|
||||
afterClose(() => props.onClose?.())
|
||||
dismiss.afterClose(props.onClose)
|
||||
return
|
||||
}
|
||||
if (event.altKey || event.metaKey) return
|
||||
@@ -445,7 +453,7 @@ export function ModelSelectorPopoverV2(props: {
|
||||
<MenuV2.GroupLabel class="gap-2 px-3">
|
||||
<span class="min-w-0 truncate">{group.items[0].provider.name}</span>
|
||||
</MenuV2.GroupLabel>
|
||||
<MenuV2.RadioGroup value={current()}>
|
||||
<MenuV2.RadioGroup value={props.current()}>
|
||||
<For each={group.items}>
|
||||
{(item) => (
|
||||
<TooltipV2
|
||||
@@ -465,7 +473,7 @@ export function ModelSelectorPopoverV2(props: {
|
||||
<MenuV2.RadioItem
|
||||
value={modelKey(item)}
|
||||
data-option-key={modelKey(item)}
|
||||
data-selected-model={current() === modelKey(item) ? true : undefined}
|
||||
data-selected-model={props.current() === modelKey(item) ? true : undefined}
|
||||
class="scroll-my-6 w-full"
|
||||
classList={{ "!bg-v2-overlay-simple-overlay-hover": store.active === modelKey(item) }}
|
||||
onMouseEnter={() => {
|
||||
|
||||
@@ -17,7 +17,7 @@ export function DialogUsageExceeded(props: DialogGoUpsellProps) {
|
||||
const platform = usePlatform()
|
||||
|
||||
const runAction = () => {
|
||||
if (props.link) platform.openLink(props.link)
|
||||
if (props.link) platform.openExternal(props.link)
|
||||
props.onClose?.()
|
||||
dialog.close()
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -215,6 +215,7 @@ export default function FileTreeV2(props: {
|
||||
<div
|
||||
ref={setRoot}
|
||||
data-component="file-tree-v2"
|
||||
dir="ltr"
|
||||
data-total-rows={live() ? rows().length : model()!.total}
|
||||
class="group/file-tree-v2"
|
||||
style={{ position: "relative", height: `${virtualizer.getTotalSize()}px` }}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import type { Prompt, ReferenceInfo } from "@opencode-ai/sdk/v2/client"
|
||||
import type { ReferenceInfo } from "@opencode-ai/sdk/v2/client"
|
||||
import { createEffect, createMemo, on, Show } from "solid-js"
|
||||
import { ModelSelectorPopoverV2 } from "@/components/dialog-select-model"
|
||||
import { DialogSelectModelUnpaidV2 } from "@/components/dialog-select-model-unpaid-v2"
|
||||
@@ -37,11 +37,9 @@ export type PromptInputV2ComposerProps = {
|
||||
class?: string
|
||||
controller: PromptInputV2ComposerController
|
||||
borderUnderlay?: boolean
|
||||
edit?: PromptInputProps["edit"]
|
||||
onEditLoaded?: PromptInputProps["onEditLoaded"]
|
||||
}
|
||||
|
||||
export type PromptInputV2ControllerProps = Omit<PromptInputProps, "class" | "edit" | "onEditLoaded" | "submission">
|
||||
export type PromptInputV2ControllerProps = Omit<PromptInputProps, "class" | "submission">
|
||||
export type PromptInputV2ComposerController = PromptInputV2Interaction & {
|
||||
readonly model: PromptInputProps["controls"]["model"]
|
||||
}
|
||||
@@ -51,15 +49,13 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
|
||||
useCommands(props)
|
||||
useEditHandler(props)
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-3">
|
||||
<PromptInputV2
|
||||
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={
|
||||
@@ -82,70 +78,6 @@ export function PromptInputV2Composer(props: PromptInputV2ComposerProps) {
|
||||
)
|
||||
}
|
||||
|
||||
const useEditHandler = (props: PromptInputV2ComposerProps) => {
|
||||
const prompt = usePrompt()
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => props.edit?.id,
|
||||
(id) => {
|
||||
const edit = props.edit
|
||||
if (!id || !edit) return
|
||||
prompt.context.items().forEach((item) => prompt.context.remove(item.key))
|
||||
edit.context.forEach((item) =>
|
||||
prompt.context.add({
|
||||
type: item.type,
|
||||
path: item.path,
|
||||
selection: item.selection,
|
||||
comment: item.comment,
|
||||
commentID: item.commentID,
|
||||
commentOrigin: item.commentOrigin,
|
||||
preview: item.preview,
|
||||
}),
|
||||
)
|
||||
props.controller.dispatch({ type: "mode.normal" })
|
||||
props.controller.resetHistory()
|
||||
prompt.set(edit.prompt, promptLength(edit.prompt))
|
||||
props.controller.restoreFocus()
|
||||
props.onEditLoaded?.()
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const useCommands = (props: PromptInputV2ComposerProps) => {
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
|
||||
command.register("prompt-input", () => [
|
||||
{
|
||||
id: "file.attach",
|
||||
title: language.t("prompt.action.attachFile"),
|
||||
category: language.t("command.category.file"),
|
||||
keybind: "mod+u",
|
||||
disabled: props.controller.state.mode !== "normal",
|
||||
onSelect: () => props.controller.attach(),
|
||||
},
|
||||
{
|
||||
id: "prompt.mode.shell",
|
||||
title: language.t("command.prompt.mode.shell"),
|
||||
category: language.t("command.category.session"),
|
||||
keybind: "mod+shift+x",
|
||||
disabled: props.controller.state.mode === "shell",
|
||||
onSelect: () => props.controller.dispatch({ type: "mode.shell" }),
|
||||
},
|
||||
{
|
||||
id: "prompt.mode.normal",
|
||||
title: language.t("command.prompt.mode.normal"),
|
||||
category: language.t("command.category.session"),
|
||||
keybind: "mod+shift+e",
|
||||
disabled: props.controller.state.mode === "normal",
|
||||
onSelect: () => props.controller.dispatch({ type: "mode.normal" }),
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
export function usePromptInputV2Controller(props: PromptInputV2ControllerProps): PromptInputV2ComposerController {
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
@@ -412,7 +344,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)
|
||||
@@ -436,6 +368,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",
|
||||
@@ -444,6 +377,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
}),
|
||||
readClipboardImage: platform.readClipboardImage,
|
||||
getPathForFile: platform.getPathForFile,
|
||||
store: platform.draftStore?.putBlob,
|
||||
},
|
||||
view: {
|
||||
placeholder: designPlaceholder,
|
||||
@@ -472,6 +406,62 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||
},
|
||||
})
|
||||
Object.defineProperty(controller, "model", { get: () => props.controls.model })
|
||||
|
||||
command.register("prompt-input", () => [
|
||||
{
|
||||
id: "file.attach",
|
||||
title: language.t("prompt.action.attachFile"),
|
||||
category: language.t("command.category.file"),
|
||||
keybind: "mod+u",
|
||||
disabled: controller.state.mode !== "normal",
|
||||
onSelect: () => controller.attach(),
|
||||
},
|
||||
{
|
||||
id: "prompt.mode.shell",
|
||||
title: language.t("command.prompt.mode.shell"),
|
||||
category: language.t("command.category.session"),
|
||||
keybind: "mod+shift+x",
|
||||
disabled: controller.state.mode === "shell",
|
||||
onSelect: () => controller.dispatch({ type: "mode.shell" }),
|
||||
},
|
||||
{
|
||||
id: "prompt.mode.normal",
|
||||
title: language.t("command.prompt.mode.normal"),
|
||||
category: language.t("command.category.session"),
|
||||
keybind: "mod+shift+e",
|
||||
disabled: controller.state.mode === "normal",
|
||||
onSelect: () => controller.dispatch({ type: "mode.normal" }),
|
||||
},
|
||||
])
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => props.edit?.id,
|
||||
(id) => {
|
||||
const edit = props.edit
|
||||
if (!id || !edit) return
|
||||
prompt.context.items().forEach((item) => prompt.context.remove(item.key))
|
||||
edit.context.forEach((item) =>
|
||||
prompt.context.add({
|
||||
type: item.type,
|
||||
path: item.path,
|
||||
selection: item.selection,
|
||||
comment: item.comment,
|
||||
commentID: item.commentID,
|
||||
commentOrigin: item.commentOrigin,
|
||||
preview: item.preview,
|
||||
}),
|
||||
)
|
||||
controller.dispatch({ type: "mode.normal" })
|
||||
controller.resetHistory()
|
||||
prompt.set(edit.prompt, promptLength(edit.prompt))
|
||||
controller.restoreFocus()
|
||||
props.onEditLoaded?.()
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
|
||||
return controller as PromptInputV2ComposerController
|
||||
}
|
||||
|
||||
@@ -521,6 +511,7 @@ function PromptInputV2ModelControl(props: {
|
||||
fallback={
|
||||
<ButtonV2
|
||||
data-action="prompt-model"
|
||||
data-control-type="dialog"
|
||||
variant="ghost-muted"
|
||||
size="normal"
|
||||
class="min-w-0 max-w-[220px] justify-start ![font-weight:440] group"
|
||||
@@ -534,19 +525,22 @@ function PromptInputV2ModelControl(props: {
|
||||
>
|
||||
<ModelSelectorPopoverV2
|
||||
model={props.model}
|
||||
triggerAs={ButtonV2}
|
||||
triggerProps={{
|
||||
variant: "ghost-muted",
|
||||
size: "normal",
|
||||
style: { height: "28px" },
|
||||
class: "min-w-0 max-w-[220px] justify-start ![font-weight:440] group",
|
||||
classList: { "animate-in fade-in": shouldAnimate() },
|
||||
"data-action": "prompt-model",
|
||||
}}
|
||||
trigger={(triggerProps) => (
|
||||
<ButtonV2
|
||||
{...triggerProps}
|
||||
variant="ghost-muted"
|
||||
size="normal"
|
||||
style={{ height: "28px" }}
|
||||
class="min-w-0 max-w-[220px] justify-start ![font-weight:440] group"
|
||||
classList={{ "animate-in fade-in": shouldAnimate() }}
|
||||
data-action="prompt-model"
|
||||
data-control-type="popover"
|
||||
>
|
||||
{content()}
|
||||
</ButtonV2>
|
||||
)}
|
||||
onClose={props.onClose}
|
||||
>
|
||||
{content()}
|
||||
</ModelSelectorPopoverV2>
|
||||
/>
|
||||
</Show>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
|
||||
@@ -1489,7 +1489,7 @@ 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")}
|
||||
@@ -1723,29 +1723,31 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
>
|
||||
<ModelSelectorPopover
|
||||
model={props.controls.model.selection}
|
||||
triggerAs={Button}
|
||||
triggerProps={{
|
||||
variant: "ghost",
|
||||
size: "normal",
|
||||
style: control(),
|
||||
class: "min-w-0 max-w-[320px] text-13-regular text-text-base group",
|
||||
"data-action": "prompt-model",
|
||||
}}
|
||||
trigger={(triggerProps) => (
|
||||
<Button
|
||||
{...triggerProps}
|
||||
variant="ghost"
|
||||
size="normal"
|
||||
style={control()}
|
||||
class="min-w-0 max-w-[320px] text-13-regular text-text-base group"
|
||||
data-action="prompt-model"
|
||||
>
|
||||
<Show when={props.controls.model.selection.current()?.provider?.id}>
|
||||
<ProviderIcon
|
||||
id={props.controls.model.selection.current()?.provider?.id ?? ""}
|
||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
||||
/>
|
||||
</Show>
|
||||
<span class="truncate">
|
||||
{props.controls.model.selection.current()?.name ??
|
||||
language.t("dialog.model.select.title")}
|
||||
</span>
|
||||
<Icon name="chevron-down" size="small" class="shrink-0" />
|
||||
</Button>
|
||||
)}
|
||||
onClose={restoreFocus}
|
||||
>
|
||||
<Show when={props.controls.model.selection.current()?.provider?.id}>
|
||||
<ProviderIcon
|
||||
id={props.controls.model.selection.current()?.provider?.id ?? ""}
|
||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
||||
/>
|
||||
</Show>
|
||||
<span class="truncate">
|
||||
{props.controls.model.selection.current()?.name ??
|
||||
language.t("dialog.model.select.title")}
|
||||
</span>
|
||||
<Icon name="chevron-down" size="small" class="shrink-0" />
|
||||
</ModelSelectorPopover>
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
</Show>
|
||||
</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({
|
||||
|
||||
@@ -22,7 +22,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)
|
||||
|
||||
@@ -100,7 +100,7 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={attachment.dataUrl}
|
||||
src={attachment.blob.url}
|
||||
alt={attachment.filename}
|
||||
class={props.newLayoutDesigns ? imageClassV2 : imageClass}
|
||||
onClick={() => props.onOpen(attachment)}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ScopedKey } from "@/utils/server-scope"
|
||||
import { createPromptSubmissionState } from "./submission-state"
|
||||
import { normalizeSessionInfo } from "@/utils/session"
|
||||
import { Event } from "@opencode-ai/schema/event"
|
||||
import { blobDataUrl } from "@/utils/draft-store"
|
||||
|
||||
type PendingPrompt = {
|
||||
abort: AbortController
|
||||
@@ -95,10 +96,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) {
|
||||
@@ -108,10 +111,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,
|
||||
@@ -516,10 +525,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" })
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useLanguage } from "@/context/language"
|
||||
import { displayName, getProjectAvatarSource } from "@/pages/layout/helpers"
|
||||
import { pathKey } from "@/utils/path-key"
|
||||
import { handleDocumentSearchKeydown } from "@/utils/search-keydown"
|
||||
import { createMenuDismissController } from "@/utils/menu-dismiss-controller"
|
||||
|
||||
export type PromptProject = {
|
||||
name?: string
|
||||
@@ -197,8 +198,8 @@ export function PromptProjectSelector(props: {
|
||||
}) {
|
||||
const [triggerReady, setTriggerReady] = createSignal(false)
|
||||
let contentRef: HTMLDivElement | undefined
|
||||
const dismiss = createMenuDismissController(() => contentRef)
|
||||
let triggerFrame: number | undefined
|
||||
let restoreTrigger = true
|
||||
|
||||
// Floating UI requires a connected anchor; route transitions can construct this trigger before adoption.
|
||||
const setTriggerRef = (element: HTMLButtonElement) => {
|
||||
@@ -221,25 +222,15 @@ export function PromptProjectSelector(props: {
|
||||
props.controller.active()
|
||||
? contentRef?.querySelector<HTMLElement>(`[data-option-key="${CSS.escape(props.controller.active())}"]`)
|
||||
: undefined
|
||||
const afterClose = (callback: () => void) => {
|
||||
const complete = () => {
|
||||
if (contentRef?.isConnected) {
|
||||
requestAnimationFrame(complete)
|
||||
return
|
||||
}
|
||||
requestAnimationFrame(() => requestAnimationFrame(callback))
|
||||
}
|
||||
requestAnimationFrame(complete)
|
||||
}
|
||||
const selectProject = (project: PromptProject) => {
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
props.controller.setOpen(false)
|
||||
afterClose(() => props.controller.select(project))
|
||||
dismiss.afterClose(() => props.controller.select(project))
|
||||
}
|
||||
const selectAction = (server?: string) => {
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
props.controller.setOpen(false)
|
||||
afterClose(() => props.controller.add(server))
|
||||
dismiss.afterClose(() => props.controller.add(server))
|
||||
}
|
||||
const selectActive = () => {
|
||||
const project = props.controller.activeProject()
|
||||
@@ -267,7 +258,7 @@ export function PromptProjectSelector(props: {
|
||||
)
|
||||
.filter((element) => !contentRef?.contains(element) && !element.hasAttribute("data-focus-trap"))
|
||||
.findLast((element) => element.offsetParent !== null)
|
||||
restoreTrigger = false
|
||||
dismiss.preventTriggerRestore()
|
||||
target?.focus()
|
||||
queueMicrotask(() => {
|
||||
if (props.controller.open()) props.controller.setOpen(false)
|
||||
@@ -291,7 +282,10 @@ export function PromptProjectSelector(props: {
|
||||
placement={props.placement ?? "bottom"}
|
||||
gutter={4}
|
||||
modal={false}
|
||||
onOpenChange={(open) => props.controller.setOpen(open)}
|
||||
onOpenChange={(open) => {
|
||||
if (open) dismiss.allowTriggerRestore()
|
||||
props.controller.setOpen(open)
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.Trigger as={ProjectTrigger} ref={setTriggerRef} controller={props.controller} />
|
||||
<DropdownMenu.Portal>
|
||||
@@ -300,11 +294,9 @@ export function PromptProjectSelector(props: {
|
||||
id="prompt-project-menu"
|
||||
class="w-[243px] overflow-hidden rounded-md border-0 bg-v2-background-bg-layer-01 p-0 shadow-[var(--v2-elevation-floating)] focus:outline-none [&[data-closed]]:!animate-none"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
onPointerDownOutside={() => (restoreTrigger = false)}
|
||||
onFocusOutside={() => (restoreTrigger = false)}
|
||||
onCloseAutoFocus={(event) => {
|
||||
if (!restoreTrigger) event.preventDefault()
|
||||
}}
|
||||
onPointerDownOutside={dismiss.preventTriggerRestore}
|
||||
onFocusOutside={dismiss.preventTriggerRestore}
|
||||
onCloseAutoFocus={dismiss.onCloseAutoFocus}
|
||||
>
|
||||
<div class="flex flex-col p-0.5">
|
||||
<div class="flex h-7 items-center gap-2 rounded-sm pl-3 pr-2.5 text-v2-icon-icon-muted">
|
||||
@@ -364,41 +356,45 @@ export function PromptProjectSelector(props: {
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
<Show
|
||||
when={props.controller.servers().length > 1}
|
||||
fallback={
|
||||
<DropdownMenu.RadioGroup value={selectedValue()}>
|
||||
<For each={props.controller.projects()}>
|
||||
{(project) => (
|
||||
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
|
||||
)}
|
||||
</For>
|
||||
</DropdownMenu.RadioGroup>
|
||||
}
|
||||
>
|
||||
<For
|
||||
each={props.controller
|
||||
.servers()
|
||||
.filter((server) =>
|
||||
props.controller.projects().some((project) => project.server?.key === server!.key),
|
||||
)}
|
||||
<div class="max-h-[224px] overflow-y-auto">
|
||||
<Show
|
||||
when={props.controller.servers().length > 1}
|
||||
fallback={
|
||||
<DropdownMenu.RadioGroup value={selectedValue()}>
|
||||
<For each={props.controller.projects()}>
|
||||
{(project) => (
|
||||
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
|
||||
)}
|
||||
</For>
|
||||
</DropdownMenu.RadioGroup>
|
||||
}
|
||||
>
|
||||
{(server) => (
|
||||
<div>
|
||||
<div class="flex h-7 select-none items-center pl-1.5 pr-3 text-[11px] font-[530] leading-none tracking-[0.05px] text-v2-text-text-faint">
|
||||
{server!.name}
|
||||
<For
|
||||
each={props.controller
|
||||
.servers()
|
||||
.filter((server) =>
|
||||
props.controller.projects().some((project) => project.server?.key === server!.key),
|
||||
)}
|
||||
>
|
||||
{(server) => (
|
||||
<div>
|
||||
<div class="flex h-7 select-none items-center pl-1.5 pr-3 text-[11px] font-[530] leading-none tracking-[0.05px] text-v2-text-text-faint">
|
||||
{server!.name}
|
||||
</div>
|
||||
<DropdownMenu.RadioGroup value={selectedValue()}>
|
||||
<For
|
||||
each={props.controller.projects().filter((project) => project.server?.key === server!.key)}
|
||||
>
|
||||
{(project) => (
|
||||
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
|
||||
)}
|
||||
</For>
|
||||
</DropdownMenu.RadioGroup>
|
||||
</div>
|
||||
<DropdownMenu.RadioGroup value={selectedValue()}>
|
||||
<For each={props.controller.projects().filter((project) => project.server?.key === server!.key)}>
|
||||
{(project) => (
|
||||
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
|
||||
)}
|
||||
</For>
|
||||
</DropdownMenu.RadioGroup>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-px bg-v2-border-border-muted" />
|
||||
<div class="flex flex-col p-0.5">
|
||||
|
||||
@@ -4,4 +4,3 @@ export { SortableTab, FileVisual } from "./session-sortable-tab"
|
||||
export { SortableTabV2 } from "./session-sortable-tab-v2"
|
||||
export { SortableTerminalTab } from "./session-sortable-terminal-tab"
|
||||
export { NewSessionView } from "./session-new-view"
|
||||
export { NewSessionDesignView } from "./session-new-design-view"
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { JSX } from "solid-js"
|
||||
import { WordmarkV2 } from "@opencode-ai/ui/v2/wordmark-v2"
|
||||
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"
|
||||
|
||||
export function NewSessionDesignView(props: { children: JSX.Element }) {
|
||||
return (
|
||||
<div data-component="session-new-design" class="relative size-full overflow-hidden bg-v2-background-bg-deep ">
|
||||
<div class="absolute inset-x-0 top-[25.375%] flex justify-center px-6">
|
||||
<div class={NEW_SESSION_CONTENT_WIDTH}>
|
||||
<WordmarkV2 class="h-auto w-full text-v2-background-bg-inverse" />
|
||||
<div class="mt-8">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from "@/context/settings"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
||||
import { Link } from "./link"
|
||||
import { ExternalLink } from "./external-link"
|
||||
import { SettingsList } from "./settings-list"
|
||||
|
||||
let demoSoundState = {
|
||||
@@ -482,7 +482,7 @@ export const SettingsGeneral: Component = () => {
|
||||
description={
|
||||
<>
|
||||
{language.t("settings.general.row.theme.description")}{" "}
|
||||
<Link href="https://opencode.ai/docs/themes/">{language.t("common.learnMore")}</Link>
|
||||
<ExternalLink href="https://opencode.ai/docs/themes/">{language.t("common.learnMore")}</ExternalLink>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -30,6 +30,8 @@ type KeybindMeta = {
|
||||
|
||||
type KeybindMap = Record<string, string | undefined>
|
||||
type CommandContext = ReturnType<typeof useCommand>
|
||||
type LanguageContext = ReturnType<typeof useLanguage>
|
||||
type SettingsContext = ReturnType<typeof useSettings>
|
||||
|
||||
const GROUPS: KeybindGroup[] = ["General", "Session", "Navigation", "Model and agent", "Terminal", "Prompt"]
|
||||
|
||||
@@ -122,7 +124,7 @@ function keybinds(value: unknown): KeybindMap {
|
||||
return value as KeybindMap
|
||||
}
|
||||
|
||||
function listFor(command: CommandContext, map: KeybindMap, palette: string) {
|
||||
function listFor(command: Pick<CommandContext, "catalog" | "options">, map: KeybindMap, palette: string) {
|
||||
const out = new Map<string, KeybindMeta>()
|
||||
out.set(PALETTE_ID, { title: palette, group: "General" })
|
||||
|
||||
@@ -262,7 +264,274 @@ function useKeyCapture(input: {
|
||||
})
|
||||
}
|
||||
|
||||
export function createKeybindSettingsController(
|
||||
input: {
|
||||
command: Pick<CommandContext, "catalog" | "options" | "keybinds">
|
||||
settings: {
|
||||
current: { keybinds: unknown }
|
||||
keybinds: Pick<SettingsContext["keybinds"], "get" | "set" | "resetAll">
|
||||
}
|
||||
target?: Document
|
||||
notify?: (toast: { title: string; description: string }) => void
|
||||
},
|
||||
language: Pick<LanguageContext, "locale" | "t"> = useLanguage(),
|
||||
) {
|
||||
const [store, setStore] = createStore({ active: null as string | null })
|
||||
const overrides = createMemo(() => keybinds(input.settings.current.keybinds))
|
||||
const list = createMemo(() => {
|
||||
language.locale()
|
||||
return listFor(input.command, overrides(), language.t("command.palette"))
|
||||
})
|
||||
const grouped = createMemo(() => groupedFor(list()))
|
||||
const title = (id: string) => list().get(id)?.title ?? ""
|
||||
const effective = (id: string) => {
|
||||
if (id === PALETTE_ID) return input.settings.keybinds.get(id) ?? DEFAULT_PALETTE_KEYBIND
|
||||
|
||||
const custom = input.settings.keybinds.get(id)
|
||||
if (typeof custom === "string") return custom
|
||||
|
||||
const live = input.command.options.find((item) => item.id === id)
|
||||
if (live?.keybind) return live.keybind
|
||||
return input.command.catalog.find((item) => item.id === id)?.keybind
|
||||
}
|
||||
const used = createMemo(() => {
|
||||
const value = new Map<string, { id: string; title: string }[]>()
|
||||
|
||||
for (const id of list().keys()) {
|
||||
for (const signature of signatures(effective(id))) {
|
||||
const items = value.get(signature)
|
||||
if (items) {
|
||||
items.push({ id, title: title(id) })
|
||||
continue
|
||||
}
|
||||
value.set(signature, [{ id, title: title(id) }])
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
const stop = () => {
|
||||
if (!store.active) return
|
||||
setStore("active", null)
|
||||
input.command.keybinds(true)
|
||||
}
|
||||
const toggle = (id: string) => {
|
||||
if (store.active === id) {
|
||||
stop()
|
||||
return
|
||||
}
|
||||
if (store.active) stop()
|
||||
setStore("active", id)
|
||||
input.command.keybinds(false)
|
||||
}
|
||||
const notify = input.notify ?? ((toast: { title: string; description: string }) => showToast(toast))
|
||||
|
||||
const handle = (event: KeyboardEvent) => {
|
||||
const id = store.active
|
||||
if (!id) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
if (event.key === "Escape") {
|
||||
stop()
|
||||
return
|
||||
}
|
||||
|
||||
const clear =
|
||||
(event.key === "Backspace" || event.key === "Delete") &&
|
||||
!event.ctrlKey &&
|
||||
!event.metaKey &&
|
||||
!event.altKey &&
|
||||
!event.shiftKey
|
||||
if (clear) {
|
||||
input.settings.keybinds.set(id, "none")
|
||||
stop()
|
||||
return
|
||||
}
|
||||
|
||||
const next = recordKeybind(event)
|
||||
if (!next) return
|
||||
|
||||
const conflicts = new Map<string, string>()
|
||||
for (const signature of signatures(next)) {
|
||||
for (const item of used().get(signature) ?? []) {
|
||||
if (item.id === id) continue
|
||||
conflicts.set(item.id, item.title)
|
||||
}
|
||||
}
|
||||
|
||||
if (conflicts.size > 0) {
|
||||
notify({
|
||||
title: language.t("settings.shortcuts.conflict.title"),
|
||||
description: language.t("settings.shortcuts.conflict.description", {
|
||||
keybind: formatKeybind(next, language.t),
|
||||
titles: [...conflicts.values()].join(", "),
|
||||
}),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
input.settings.keybinds.set(id, next)
|
||||
stop()
|
||||
}
|
||||
|
||||
const target = input.target ?? (typeof document === "object" ? document : undefined)
|
||||
if (target) makeEventListener(target, "keydown", handle, { capture: true })
|
||||
|
||||
onCleanup(() => {
|
||||
if (store.active) input.command.keybinds(true)
|
||||
})
|
||||
|
||||
return {
|
||||
catalog: {
|
||||
groups: GROUPS,
|
||||
filtered: (query: string) =>
|
||||
filteredFor(query, list(), grouped(), (id) => formatKeybind(effective(id) ?? "", language.t)),
|
||||
title,
|
||||
keybind: (id: string) => formatKeybind(effective(id) ?? "", language.t),
|
||||
},
|
||||
capture: {
|
||||
active: () => store.active,
|
||||
toggle,
|
||||
},
|
||||
settings: {
|
||||
hasOverrides: () => Object.values(overrides()).some((value) => typeof value === "string"),
|
||||
reset: () => {
|
||||
stop()
|
||||
input.settings.keybinds.resetAll()
|
||||
notify({
|
||||
title: language.t("settings.shortcuts.reset.toast.title"),
|
||||
description: language.t("settings.shortcuts.reset.toast.description"),
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function SettingsKeybindsV2() {
|
||||
const command = useCommand()
|
||||
const settings = useSettings()
|
||||
const controller = createKeybindSettingsController({
|
||||
command,
|
||||
settings,
|
||||
})
|
||||
|
||||
return (
|
||||
<SettingsKeybindsV2View
|
||||
groups={controller.catalog.groups}
|
||||
filtered={controller.catalog.filtered}
|
||||
title={controller.catalog.title}
|
||||
keybind={controller.catalog.keybind}
|
||||
active={controller.capture.active}
|
||||
onCapture={controller.capture.toggle}
|
||||
hasOverrides={controller.settings.hasOverrides}
|
||||
onReset={controller.settings.reset}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SettingsKeybindsV2View(props: {
|
||||
groups: KeybindGroup[]
|
||||
filtered: (query: string) => Map<KeybindGroup, string[]>
|
||||
title: (id: string) => string
|
||||
keybind: (id: string) => string
|
||||
active: () => string | null
|
||||
onCapture: (id: string) => void
|
||||
hasOverrides: () => boolean
|
||||
onReset: () => void
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({ filter: "" })
|
||||
const filtered = createMemo(() => props.filtered(store.filter))
|
||||
const hasResults = createMemo(() => props.groups.some((group) => (filtered().get(group)?.length ?? 0) > 0))
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="settings-v2-tab-header settings-v2-tab-header--stacked">
|
||||
<div class="settings-v2-tab-header-row">
|
||||
<h2 class="settings-v2-tab-title">{language.t("settings.shortcuts.title")}</h2>
|
||||
<ButtonV2 variant="ghost" onClick={props.onReset} disabled={!props.hasOverrides()}>
|
||||
{language.t("settings.shortcuts.reset.button")}
|
||||
</ButtonV2>
|
||||
</div>
|
||||
<div class="settings-v2-tab-search">
|
||||
<TextInputV2
|
||||
type="search"
|
||||
appearance="base"
|
||||
value={store.filter}
|
||||
onInput={(event) => setStore("filter", event.currentTarget.value)}
|
||||
placeholder={language.t("settings.shortcuts.search.placeholder")}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t("settings.shortcuts.search.placeholder")}
|
||||
/>
|
||||
<Show when={store.filter}>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="small"
|
||||
class="settings-v2-tab-search-clear"
|
||||
icon={<IconV2 name="close" size="large" class="text-v2-icon-icon-muted" />}
|
||||
onClick={() => setStore("filter", "")}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-v2-tab-body">
|
||||
<div class="settings-v2-shortcuts flex flex-col gap-8">
|
||||
<For each={props.groups}>
|
||||
{(group) => (
|
||||
<Show when={(filtered().get(group) ?? []).length > 0}>
|
||||
<div class="settings-v2-section">
|
||||
<h3 class="settings-v2-section-title">{language.t(groupKey[group])}</h3>
|
||||
<SettingsListV2>
|
||||
<For each={filtered().get(group) ?? []}>
|
||||
{(id) => (
|
||||
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<span>{props.title(id)}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-keybind-id={id}
|
||||
classList={{
|
||||
"settings-v2-keybind-button": true,
|
||||
"settings-v2-keybind-button--active": props.active() === id,
|
||||
}}
|
||||
onClick={() => props.onCapture(id)}
|
||||
>
|
||||
<Show
|
||||
when={props.active() === id}
|
||||
fallback={props.keybind(id) || language.t("settings.shortcuts.unassigned")}
|
||||
>
|
||||
{language.t("settings.shortcuts.pressKeys")}
|
||||
</Show>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
<Show when={store.filter && !hasResults()}>
|
||||
<div class="settings-v2-shortcuts-status">
|
||||
<span>{language.t("settings.shortcuts.search.empty")}</span>
|
||||
<span class="settings-v2-shortcuts-status-filter">"{store.filter}"</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const SettingsKeybinds: Component<{ v2?: boolean }> = (props) => {
|
||||
if (props.v2) return <SettingsKeybindsV2 />
|
||||
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
const settings = useSettings()
|
||||
@@ -476,78 +745,37 @@ export const SettingsKeybinds: Component<{ v2?: boolean }> = (props) => {
|
||||
)
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={props.v2}
|
||||
fallback={
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
|
||||
<Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
|
||||
{language.t("settings.shortcuts.reset.button")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base">
|
||||
<Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" />
|
||||
<TextField
|
||||
variant="ghost"
|
||||
type="text"
|
||||
value={store.filter}
|
||||
onChange={(v) => setStore("filter", v)}
|
||||
placeholder={language.t("settings.shortcuts.search.placeholder")}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
class="flex-1"
|
||||
/>
|
||||
<Show when={store.filter}>
|
||||
<IconButton icon="circle-x" variant="ghost" onClick={() => setStore("filter", "")} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{groups}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<div class="settings-v2-tab-header settings-v2-tab-header--stacked">
|
||||
<div class="settings-v2-tab-header-row">
|
||||
<h2 class="settings-v2-tab-title">{language.t("settings.shortcuts.title")}</h2>
|
||||
<ButtonV2 variant="ghost" onClick={resetAll} disabled={!hasOverrides()}>
|
||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
|
||||
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
|
||||
<Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
|
||||
{language.t("settings.shortcuts.reset.button")}
|
||||
</ButtonV2>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="settings-v2-tab-search">
|
||||
<TextInputV2
|
||||
type="search"
|
||||
appearance="base"
|
||||
|
||||
<div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base">
|
||||
<Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" />
|
||||
<TextField
|
||||
variant="ghost"
|
||||
type="text"
|
||||
value={store.filter}
|
||||
onInput={(event) => setStore("filter", event.currentTarget.value)}
|
||||
onChange={(v) => setStore("filter", v)}
|
||||
placeholder={language.t("settings.shortcuts.search.placeholder")}
|
||||
spellcheck={false}
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="off"
|
||||
aria-label={language.t("settings.shortcuts.search.placeholder")}
|
||||
class="flex-1"
|
||||
/>
|
||||
<Show when={store.filter}>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="small"
|
||||
class="settings-v2-tab-search-clear"
|
||||
icon={<IconV2 name="close" size="large" class="text-v2-icon-icon-muted" />}
|
||||
onClick={() => setStore("filter", "")}
|
||||
/>
|
||||
<IconButton icon="circle-x" variant="ghost" onClick={() => setStore("filter", "")} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-v2-tab-body">{groups}</div>
|
||||
</>
|
||||
</Show>
|
||||
</div>
|
||||
{groups}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,173 @@
|
||||
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 { useServerSDK } from "@/context/server-sdk"
|
||||
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.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 serverSdk = useServerSDK()
|
||||
const serverSync = useServerSync()
|
||||
const [shells] = createResource(
|
||||
async () => {
|
||||
const sdk = serverSdk()
|
||||
if ((await sdk.protocol) === "v1") return (await sdk.client.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
|
||||
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,182 +1,297 @@
|
||||
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 { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
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 { LayoutRetirementNotice, LayoutTransitionToggle } from "./interface-transition"
|
||||
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 dialog = useDialog()
|
||||
const settings = useSettings()
|
||||
const serverSync = useServerSync()
|
||||
const serverSdk = useServerSDK()
|
||||
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.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 () => {
|
||||
const sdk = serverSdk()
|
||||
if ((await sdk.protocol) === "v1") {
|
||||
return (await sdk.client.pty.shells()).data ?? []
|
||||
}
|
||||
// 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)
|
||||
@@ -184,52 +299,6 @@ 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 InterfaceSection = () => (
|
||||
<LayoutTransitionToggle
|
||||
title={language.t("settings.general.row.newInterface.title")}
|
||||
@@ -251,59 +320,18 @@ export const SettingsGeneralV2: Component<{
|
||||
title={language.t("settings.general.row.newInterfaceNotice.title")}
|
||||
description={language.t("settings.general.row.newInterfaceNotice.description")}
|
||||
dismiss={language.t("settings.general.row.newInterfaceNotice.dismiss")}
|
||||
onDismiss={settings.general.dismissNewInterfaceNotice}
|
||||
onDismiss={() => settings.general.dismissNewInterfaceNotice()}
|
||||
/>
|
||||
)
|
||||
|
||||
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")}
|
||||
@@ -414,124 +442,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>
|
||||
@@ -576,68 +486,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>
|
||||
@@ -659,7 +507,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>
|
||||
@@ -704,11 +552,11 @@ export const SettingsGeneralV2: Component<{
|
||||
|
||||
<GeneralSection />
|
||||
|
||||
<AppearanceSection />
|
||||
<AppearanceSection controller={appearance} />
|
||||
|
||||
<NotificationsSection />
|
||||
|
||||
<SoundsSection />
|
||||
<SoundsSection controller={sounds} />
|
||||
|
||||
<Show when={desktop()}>
|
||||
<UpdatesSection />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { type Component, For, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useModels } from "@/context/models"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { popularProviders } from "@/hooks/use-providers"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { SettingsListV2 } from "./parts/list"
|
||||
import { SettingsRowV2 } from "./parts/row"
|
||||
import "./settings-v2.css"
|
||||
@@ -19,6 +22,11 @@ const PROVIDER_ICON_SIZE = 16
|
||||
export const SettingsModelsV2: Component = () => {
|
||||
const language = useLanguage()
|
||||
const models = useModels()
|
||||
const serverSdk = useServerSDK()
|
||||
const [store, setStore] = persisted(
|
||||
Persist.serverGlobal(serverSdk().scope, "settings-v2.models.providers"),
|
||||
createStore({ collapsed: {} as Record<string, boolean> }),
|
||||
)
|
||||
|
||||
const list = useFilteredList<ModelItem>({
|
||||
items: (_filter) => models.list(),
|
||||
@@ -94,41 +102,82 @@ export const SettingsModelsV2: Component = () => {
|
||||
}
|
||||
>
|
||||
<For each={list.grouped.latest}>
|
||||
{(group) => (
|
||||
<div class="settings-v2-section" data-component="settings-models-provider">
|
||||
<div class="settings-v2-models-group-header">
|
||||
<ProviderIcon
|
||||
id={group.category}
|
||||
width={PROVIDER_ICON_SIZE}
|
||||
height={PROVIDER_ICON_SIZE}
|
||||
class="settings-v2-models-provider-icon shrink-0"
|
||||
/>
|
||||
<h3 class="settings-v2-section-title">{group.items[0].provider.name}</h3>
|
||||
{(group) => {
|
||||
const searching = () => list.filter().length > 0
|
||||
const expanded = () => searching() || !store.collapsed[group.category]
|
||||
|
||||
return (
|
||||
<div
|
||||
class="settings-v2-section"
|
||||
data-component="settings-models-provider"
|
||||
data-expanded={expanded() ? "" : undefined}
|
||||
>
|
||||
<h3 class="settings-v2-models-group-header">
|
||||
<button
|
||||
type="button"
|
||||
class="settings-v2-models-group-trigger"
|
||||
aria-expanded={expanded()}
|
||||
disabled={searching()}
|
||||
onClick={() => setStore("collapsed", group.category, expanded())}
|
||||
>
|
||||
<span class="settings-v2-models-group-chevron">
|
||||
<Show
|
||||
when={expanded()}
|
||||
fallback={
|
||||
<svg width="5" height="6" viewBox="0 0 5 6" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M0.75194 5.31663C0.41861 5.51103 0 5.27063 0 4.88473V0.500754C0 0.114854 0.41861 -0.125577 0.75194 0.0688635L4.5096 2.26084C4.8404 2.45378 4.8404 2.93168 4.5096 3.12462L0.75194 5.31663Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M5.37624 6.75194C5.18184 6.41861 5.42224 6 5.80814 6H10.1921C10.578 6 10.8184 6.41861 10.624 6.75194L8.43203 10.5096C8.23909 10.8404 7.76119 10.8404 7.56825 10.5096L5.37624 6.75194Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</Show>
|
||||
</span>
|
||||
<span class="settings-v2-models-group-label">
|
||||
<ProviderIcon
|
||||
id={group.category}
|
||||
width={PROVIDER_ICON_SIZE}
|
||||
height={PROVIDER_ICON_SIZE}
|
||||
class="settings-v2-models-provider-icon shrink-0"
|
||||
/>
|
||||
<span class="settings-v2-section-title">{group.items[0].provider.name}</span>
|
||||
</span>
|
||||
</button>
|
||||
</h3>
|
||||
<Show when={expanded()}>
|
||||
<SettingsListV2>
|
||||
<For each={group.items}>
|
||||
{(item) => {
|
||||
const key = { providerID: item.provider.id, modelID: item.id }
|
||||
return (
|
||||
<SettingsRowV2 title={item.name} description="">
|
||||
<div>
|
||||
<Switch
|
||||
checked={models.visible(key)}
|
||||
onChange={(checked) => {
|
||||
models.setVisibility(key, checked)
|
||||
}}
|
||||
hideLabel
|
||||
>
|
||||
{item.name}
|
||||
</Switch>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</SettingsListV2>
|
||||
</Show>
|
||||
</div>
|
||||
<SettingsListV2>
|
||||
<For each={group.items}>
|
||||
{(item) => {
|
||||
const key = { providerID: item.provider.id, modelID: item.id }
|
||||
return (
|
||||
<SettingsRowV2 title={item.name} description="">
|
||||
<div>
|
||||
<Switch
|
||||
checked={models.visible(key)}
|
||||
onChange={(checked) => {
|
||||
models.setVisibility(key, checked)
|
||||
}}
|
||||
hideLabel
|
||||
>
|
||||
{item.name}
|
||||
</Switch>
|
||||
</div>
|
||||
</SettingsRowV2>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</SettingsListV2>
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</Show>
|
||||
</Show>
|
||||
|
||||
@@ -373,14 +373,64 @@
|
||||
}
|
||||
|
||||
.settings-v2-models {
|
||||
gap: 24px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.settings-v2-models .settings-v2-section {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.settings-v2-models .settings-v2-section[data-expanded] {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.settings-v2-models-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.settings-v2-models-group-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
height: 28px;
|
||||
padding: 4px 8px 4px 4px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--v2-text-text-base);
|
||||
}
|
||||
|
||||
.settings-v2-models-group-trigger:focus-visible {
|
||||
outline: 2px solid var(--v2-border-border-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.settings-v2-models-group-trigger:not(:disabled):hover {
|
||||
background-color: var(--v2-background-bg-layer-02);
|
||||
}
|
||||
}
|
||||
|
||||
.settings-v2-models-group-trigger:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.settings-v2-models-group-chevron {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--v2-icon-icon-muted);
|
||||
}
|
||||
|
||||
.settings-v2-models-group-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.settings-v2-models .settings-v2-section-title {
|
||||
@@ -394,6 +444,10 @@
|
||||
color: var(--v2-icon-icon-base);
|
||||
}
|
||||
|
||||
.settings-v2-models [data-component="settings-v2-list"] {
|
||||
padding-inline: 16px;
|
||||
}
|
||||
|
||||
.settings-v2-models .settings-v2-section-title + [data-component="settings-v2-list"] {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { hasNonBlockingServiceIssue, serverStatusDotClass } from "./status-popover-indicator"
|
||||
import {
|
||||
hasNonBlockingServiceIssue,
|
||||
hasServiceNeedingAttention,
|
||||
serverStatusDotClass,
|
||||
} from "./status-popover-indicator"
|
||||
|
||||
describe("serverStatusDotClass", () => {
|
||||
test("uses the success token while the server and services are healthy", () => {
|
||||
expect(serverStatusDotClass({ ready: true, serverHealth: true, issue: false })).toBe("bg-icon-success-base")
|
||||
})
|
||||
|
||||
test("uses the session attention token when a service needs attention", () => {
|
||||
expect(serverStatusDotClass({ ready: true, serverHealth: true, attention: true, issue: true })).toBe(
|
||||
"bg-v2-background-bg-accent",
|
||||
)
|
||||
})
|
||||
|
||||
test("uses the warning token for non-blocking issues while the server is online", () => {
|
||||
expect(serverStatusDotClass({ ready: true, serverHealth: true, issue: true })).toBe("bg-icon-warning-base")
|
||||
})
|
||||
@@ -34,3 +44,15 @@ describe("hasNonBlockingServiceIssue", () => {
|
||||
expect(hasNonBlockingServiceIssue({ mcp: [], lsp: ["connected"] })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("hasServiceNeedingAttention", () => {
|
||||
test("detects MCP states that need user attention", () => {
|
||||
expect(hasServiceNeedingAttention({ mcp: ["needs_auth"] })).toBe(true)
|
||||
expect(hasServiceNeedingAttention({ mcp: ["needs_client_registration"] })).toBe(true)
|
||||
})
|
||||
|
||||
test("ignores states that do not need user attention", () => {
|
||||
expect(hasServiceNeedingAttention({ mcp: ["failed"] })).toBe(false)
|
||||
expect(hasServiceNeedingAttention({ mcp: ["connected", "pending", "disabled"] })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { LspStatus } from "@opencode-ai/sdk/v2/client"
|
||||
import type { McpServer } from "@opencode-ai/client/promise"
|
||||
|
||||
export function hasServiceNeedingAttention(input: { mcp: Array<McpServer["status"]["status"]> }) {
|
||||
return input.mcp.some((status) => status === "needs_auth" || status === "needs_client_registration")
|
||||
}
|
||||
|
||||
export function hasNonBlockingServiceIssue(input: {
|
||||
mcp: Array<McpServer["status"]["status"]>
|
||||
lsp: Array<LspStatus["status"]>
|
||||
@@ -11,9 +15,15 @@ export function hasNonBlockingServiceIssue(input: {
|
||||
)
|
||||
}
|
||||
|
||||
export function serverStatusDotClass(input: { ready: boolean; serverHealth: boolean | undefined; issue: boolean }) {
|
||||
export function serverStatusDotClass(input: {
|
||||
ready: boolean
|
||||
serverHealth: boolean | undefined
|
||||
attention?: boolean
|
||||
issue: boolean
|
||||
}) {
|
||||
if (input.serverHealth === false) return "bg-icon-critical-base"
|
||||
if (!input.ready || input.serverHealth === undefined) return "bg-border-weak-base"
|
||||
if (input.attention) return "bg-v2-background-bg-accent"
|
||||
if (input.issue) return "bg-icon-warning-base"
|
||||
if (input.serverHealth === true) return "bg-icon-success-base"
|
||||
return "bg-border-weak-base"
|
||||
|
||||
@@ -9,7 +9,11 @@ import { ServerConnection, useServer } from "@/context/server"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { hasNonBlockingServiceIssue, serverStatusDotClass } from "./status-popover-indicator"
|
||||
import {
|
||||
hasNonBlockingServiceIssue,
|
||||
hasServiceNeedingAttention,
|
||||
serverStatusDotClass,
|
||||
} 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 })))
|
||||
@@ -22,6 +26,11 @@ export function StatusPopover() {
|
||||
const [shown, setShown] = createSignal(false)
|
||||
const serverHealth = () => global.servers.health[server.key]?.healthy
|
||||
const ready = createMemo(() => serverHealth() === false || (sync().data.mcp_ready && sync().data.lsp_ready))
|
||||
const attention = createMemo(() =>
|
||||
hasServiceNeedingAttention({
|
||||
mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
||||
}),
|
||||
)
|
||||
const issue = createMemo(() =>
|
||||
hasNonBlockingServiceIssue({
|
||||
mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
||||
@@ -49,6 +58,7 @@ export function StatusPopover() {
|
||||
class={`absolute -top-px -right-px size-1.5 rounded-full ${serverStatusDotClass({
|
||||
ready: ready(),
|
||||
serverHealth: serverHealth(),
|
||||
attention: attention(),
|
||||
issue: issue(),
|
||||
})}`}
|
||||
/>
|
||||
@@ -85,6 +95,11 @@ function DirectoryStatusPopover() {
|
||||
const [shown, setShown] = createSignal(false)
|
||||
const serverHealth = () => global.servers.health[ServerConnection.key(server().server)]?.healthy
|
||||
const ready = createMemo(() => serverHealth() === false || (sync().data.mcp_ready && sync().data.lsp_ready))
|
||||
const attention = createMemo(() =>
|
||||
hasServiceNeedingAttention({
|
||||
mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
||||
}),
|
||||
)
|
||||
const issue = createMemo(() =>
|
||||
hasNonBlockingServiceIssue({
|
||||
mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
||||
@@ -95,6 +110,7 @@ function DirectoryStatusPopover() {
|
||||
shown: shown(),
|
||||
ready: ready(),
|
||||
serverHealth: serverHealth(),
|
||||
attention: attention(),
|
||||
issue: issue(),
|
||||
label: language.t("status.popover.trigger"),
|
||||
onOpenChange: setShown,
|
||||
@@ -118,6 +134,7 @@ function ServerStatusPopover() {
|
||||
shown: shown(),
|
||||
ready: serverHealth() !== undefined,
|
||||
serverHealth: serverHealth(),
|
||||
attention: false,
|
||||
issue: false,
|
||||
label: language.t("status.popover.trigger"),
|
||||
onOpenChange: setShown,
|
||||
@@ -135,6 +152,7 @@ type StatusPopoverState = {
|
||||
shown: boolean
|
||||
ready: boolean
|
||||
serverHealth: boolean | undefined
|
||||
attention: boolean
|
||||
issue: boolean
|
||||
label: string
|
||||
onOpenChange: (value: boolean) => void
|
||||
|
||||
@@ -382,7 +382,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(() => {
|
||||
@@ -738,6 +742,7 @@ export const Terminal = (props: TerminalProps) => {
|
||||
<div
|
||||
ref={container}
|
||||
data-component="terminal"
|
||||
dir="ltr"
|
||||
data-prevent-autofocus
|
||||
tabIndex={-1}
|
||||
style={{ "background-color": terminalColors().background }}
|
||||
|
||||
@@ -12,6 +12,6 @@ export function forwardTabRef(ref: Ref<HTMLDivElement> | undefined, element: HTM
|
||||
if (typeof ref === "function") ref(element)
|
||||
}
|
||||
|
||||
export function canOpenTabRename(dragging: boolean | undefined, editing: boolean, committing: boolean) {
|
||||
return !dragging && !editing && !committing
|
||||
export function canOpenTabRename(dragging: boolean | undefined, editing: boolean, pending: boolean) {
|
||||
return !dragging && !editing && !pending
|
||||
}
|
||||
|
||||
@@ -9,53 +9,86 @@
|
||||
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;
|
||||
left: -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"]: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"])
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { createEffect, createMemo, createSignal, onCleanup, Show, type Ref } from "solid-js"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
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"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import type { Session } from "@opencode-ai/sdk/v2"
|
||||
import { canOpenTabRename, forwardTabRef } from "./titlebar-tab-gesture"
|
||||
import { TabPreviewPopover } from "./titlebar-tab-popover"
|
||||
@@ -23,8 +22,7 @@ export function TabNavItem(props: {
|
||||
server: ServerConnection.Key
|
||||
session: () => Session | undefined
|
||||
fallbackTitle?: string
|
||||
onTitleChange?: (title: string) => void
|
||||
onTitleChangeFailed?: (title: string) => void
|
||||
onRename: (title: string) => Promise<void>
|
||||
onClose: () => void
|
||||
onNavigate: () => void
|
||||
active?: boolean
|
||||
@@ -34,13 +32,12 @@ export function TabNavItem(props: {
|
||||
pressed?: boolean
|
||||
hidden?: boolean
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [editing, setEditing] = createSignal(false)
|
||||
const [titleOverflowing, setTitleOverflowing] = createSignal(false)
|
||||
let tabRoot!: HTMLDivElement
|
||||
let titleEl!: HTMLSpanElement
|
||||
let committing = false
|
||||
let measureFrame: number | undefined
|
||||
const rename = createMutation(() => ({ mutationFn: props.onRename }))
|
||||
|
||||
const closeTab = (event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
@@ -116,40 +113,20 @@ export function TabNavItem(props: {
|
||||
selection?.addRange(range)
|
||||
}
|
||||
|
||||
const rename = async (title: string) => {
|
||||
const ctx = serverCtx()
|
||||
const session = props.session()
|
||||
if (!ctx || !session) return
|
||||
await ctx.sdk.api.session.rename({ sessionID: session.id, title })
|
||||
}
|
||||
|
||||
const closeRename = async (save: boolean) => {
|
||||
if (committing || !editing()) return
|
||||
committing = true
|
||||
if (rename.isPending || !editing()) return
|
||||
|
||||
const original = props.session()?.title ?? ""
|
||||
const next = (titleEl.textContent ?? "").trim()
|
||||
|
||||
titleEl.scrollLeft = 0
|
||||
if (save && next && next !== original) props.onTitleChange?.(next)
|
||||
setEditing(false)
|
||||
|
||||
if (!save || !next || next === original) {
|
||||
committing = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await rename(next)
|
||||
} catch (err) {
|
||||
props.onTitleChangeFailed?.(original)
|
||||
showToast({
|
||||
title: language.t("common.requestFailed"),
|
||||
description: err instanceof Error ? err.message : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
committing = false
|
||||
await rename.mutateAsync(next)
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
@@ -163,7 +140,7 @@ export function TabNavItem(props: {
|
||||
const openRename = (event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
if (!canOpenTabRename(props.dragging, editing(), committing)) return
|
||||
if (!canOpenTabRename(props.dragging, editing(), rename.isPending)) return
|
||||
const session = props.session()
|
||||
if (!session) return
|
||||
titleEl.textContent = session.title
|
||||
@@ -203,11 +180,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()
|
||||
@@ -218,90 +195,89 @@ export function TabNavItem(props: {
|
||||
closeTab(event)
|
||||
}}
|
||||
>
|
||||
<Show when={title() !== undefined}>
|
||||
<a
|
||||
data-slot="tab-link"
|
||||
data-titlebar-tab-link
|
||||
href={props.href}
|
||||
draggable={false}
|
||||
onDragStart={(event) => {
|
||||
<a
|
||||
data-slot="tab-link"
|
||||
data-titlebar-tab-link
|
||||
href={props.href}
|
||||
draggable={false}
|
||||
onDragStart={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
// Navigate on mousedown to shave the press-release delay off tab switches.
|
||||
if (event.button !== 0) return
|
||||
if (editing()) return
|
||||
if (props.suppressNavigation?.()) return
|
||||
props.onNavigate()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
// Mouse navigation already happened on mousedown; detail 0 means keyboard activation.
|
||||
if (event.detail > 0) return
|
||||
if (editing()) return
|
||||
if (props.suppressNavigation?.()) return
|
||||
props.onNavigate()
|
||||
}}
|
||||
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base group-data-[editing='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
||||
>
|
||||
<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" />
|
||||
}
|
||||
>
|
||||
{(session) => (
|
||||
<SessionTabAvatar
|
||||
project={project()}
|
||||
directory={session.directory}
|
||||
sessionId={session.id}
|
||||
server={props.server}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
</span>
|
||||
<span
|
||||
ref={(el) => {
|
||||
titleEl = el
|
||||
titleEl.textContent = title() ?? ""
|
||||
}}
|
||||
data-slot="tab-title"
|
||||
data-titlebar-tab-title
|
||||
class="min-w-0 flex-1 outline-none leading-4"
|
||||
classList={{
|
||||
"overflow-hidden text-clip whitespace-nowrap": !editing(),
|
||||
"select-text": editing(),
|
||||
}}
|
||||
contenteditable={editing() ? true : undefined}
|
||||
onDblClick={openRename}
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation()
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
void closeRename(true)
|
||||
return
|
||||
}
|
||||
if (event.key !== "Escape") return
|
||||
event.preventDefault()
|
||||
titleEl.textContent = props.session()?.title ?? ""
|
||||
void closeRename(false)
|
||||
}}
|
||||
onBlur={() => void closeRename(true)}
|
||||
onPointerDown={(event) => {
|
||||
if (!editing()) return
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
// Navigate on mousedown to shave the press-release delay off tab switches.
|
||||
if (event.button !== 0) return
|
||||
if (editing()) return
|
||||
if (props.suppressNavigation?.()) return
|
||||
props.onNavigate()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (!editing()) return
|
||||
event.preventDefault()
|
||||
// Mouse navigation already happened on mousedown; detail 0 means keyboard activation.
|
||||
if (event.detail > 0) return
|
||||
if (editing()) return
|
||||
if (props.suppressNavigation?.()) return
|
||||
props.onNavigate()
|
||||
}}
|
||||
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base group-data-[editing='true']:text-v2-text-text-base [-webkit-user-drag:none]"
|
||||
>
|
||||
<span data-slot="project-avatar-slot" class="flex size-4 shrink-0 items-center justify-center">
|
||||
<Show
|
||||
when={props.session()}
|
||||
fallback={
|
||||
<span class="block size-4 rounded-[3px] border border-v2-border-border-muted" aria-hidden="true" />
|
||||
}
|
||||
>
|
||||
{(session) => (
|
||||
<SessionTabAvatar
|
||||
project={project()}
|
||||
directory={session().directory}
|
||||
sessionId={session().id}
|
||||
server={props.server}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
</span>
|
||||
<span
|
||||
ref={(el) => {
|
||||
titleEl = el
|
||||
titleEl.textContent = title() ?? ""
|
||||
}}
|
||||
data-slot="tab-title"
|
||||
data-titlebar-tab-title
|
||||
class="min-w-0 flex-1 outline-none leading-4"
|
||||
classList={{
|
||||
"overflow-hidden text-clip whitespace-nowrap": !editing(),
|
||||
"select-text": editing(),
|
||||
}}
|
||||
contenteditable={editing() ? true : undefined}
|
||||
onDblClick={openRename}
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation()
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
void closeRename(true)
|
||||
return
|
||||
}
|
||||
if (event.key !== "Escape") return
|
||||
event.preventDefault()
|
||||
titleEl.textContent = props.session()?.title ?? ""
|
||||
void closeRename(false)
|
||||
}}
|
||||
onBlur={() => void closeRename(true)}
|
||||
onPointerDown={(event) => {
|
||||
if (!editing()) return
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (!editing()) return
|
||||
event.preventDefault()
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</Show>
|
||||
/>
|
||||
</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"
|
||||
@@ -359,8 +335,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
|
||||
@@ -406,7 +382,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"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { adjacentTabKey, mergeVisibleTabOrder } from "./titlebar-tab-order"
|
||||
|
||||
describe("adjacentTabKey", () => {
|
||||
test("follows the visible left-to-right order", () => {
|
||||
expect(adjacentTabKey(["c", "a", "b"], "c", 1)).toBe("a")
|
||||
expect(adjacentTabKey(["c", "a", "b"], "a", -1)).toBe("c")
|
||||
})
|
||||
|
||||
test("skips tabs omitted from the visible order", () => {
|
||||
expect(adjacentTabKey(["a", "c"], "a", 1)).toBe("c")
|
||||
expect(adjacentTabKey(["a", "c"], "c", 1)).toBe("a")
|
||||
})
|
||||
})
|
||||
|
||||
test("merges reordered visible tabs around hidden tabs", () => {
|
||||
expect(mergeVisibleTabOrder(["a", "hidden", "b", "c"], ["a", "b", "c"], ["c", "a", "b"])).toEqual([
|
||||
"c",
|
||||
"hidden",
|
||||
"a",
|
||||
"b",
|
||||
])
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
export function adjacentTabKey(order: string[], current: string | undefined, offset: -1 | 1) {
|
||||
if (!current || order.length === 0) return
|
||||
const index = order.indexOf(current)
|
||||
if (index === -1) return
|
||||
return order[(index + offset + order.length) % order.length]
|
||||
}
|
||||
|
||||
export function mergeVisibleTabOrder(all: string[], current: string[], next: string[]) {
|
||||
const visible = new Set(current)
|
||||
const reordered = next.values()
|
||||
return all.map((key) => (visible.has(key) ? (reordered.next().value ?? key) : key))
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createEffect, createMemo, createResource, createRoot, For, onCleanup, onMount } from "solid-js"
|
||||
import { createEffect, createMemo, createResource, createRoot, For, onCleanup, onMount, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||
import { DragDropProvider, PointerSensor } from "@dnd-kit/solid"
|
||||
import { isSortable, useSortable } from "@dnd-kit/solid/sortable"
|
||||
@@ -15,7 +16,10 @@ import { useCommand } from "@/context/command"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { createTabPromptState } from "@/context/prompt"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { canStartTabDrag, isTabCloseTarget } from "./titlebar-tab-gesture"
|
||||
import { adjacentTabKey, mergeVisibleTabOrder } from "./titlebar-tab-order"
|
||||
import type { Session } from "@opencode-ai/sdk/v2"
|
||||
|
||||
function SessionTabSlot(props: {
|
||||
tab: SessionTab
|
||||
@@ -23,12 +27,12 @@ function SessionTabSlot(props: {
|
||||
index: () => number
|
||||
active: () => boolean
|
||||
forceTruncate: boolean
|
||||
serverCtx: () => ServerCtx | undefined
|
||||
session: () => Session | undefined
|
||||
fallbackTitle?: string
|
||||
onRename: (title: string) => Promise<void>
|
||||
onNavigate: (element: HTMLDivElement) => void
|
||||
onClose: () => void
|
||||
}) {
|
||||
const tabs = useTabs()
|
||||
const language = useLanguage()
|
||||
const sortable = useSortable({
|
||||
get id() {
|
||||
return props.id
|
||||
@@ -38,6 +42,47 @@ function SessionTabSlot(props: {
|
||||
},
|
||||
})
|
||||
let ref!: HTMLDivElement
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={sortable.ref}
|
||||
data-titlebar-tab-slot
|
||||
data-tab-key={props.id}
|
||||
data-active={props.active()}
|
||||
class="relative flex w-56 min-w-7 max-w-56 flex-shrink"
|
||||
>
|
||||
<TabNavItem
|
||||
ref={(el) => {
|
||||
ref = el
|
||||
}}
|
||||
href={tabHref(props.tab)}
|
||||
server={props.tab.server}
|
||||
session={props.session}
|
||||
fallbackTitle={props.fallbackTitle}
|
||||
onRename={props.onRename}
|
||||
onNavigate={() => props.onNavigate(ref)}
|
||||
onClose={props.onClose}
|
||||
active={props.active()}
|
||||
forceTruncate={props.forceTruncate}
|
||||
dragging={sortable.isDragSource()}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SessionTabEntry(props: {
|
||||
tab: SessionTab
|
||||
id: string
|
||||
index: () => number
|
||||
active: () => boolean
|
||||
forceTruncate: boolean
|
||||
serverCtx: () => ServerCtx | undefined
|
||||
onVisibleChange: (visible: boolean) => void
|
||||
onNavigate: (element: HTMLDivElement) => void
|
||||
onClose: () => void
|
||||
}) {
|
||||
const tabs = useTabs()
|
||||
const language = useLanguage()
|
||||
const sdk = createMemo(() => props.serverCtx()?.sdk ?? null)
|
||||
const cachedSession = createMemo(() => props.serverCtx()?.sync.session.peek(props.tab.sessionId))
|
||||
const persisted = createMemo(() => tabs.info[props.id])
|
||||
@@ -50,8 +95,30 @@ function SessionTabSlot(props: {
|
||||
)
|
||||
const session = createMemo(() => cachedSession() ?? loadedSession())
|
||||
const missingSession = createMemo(() => !!props.serverCtx() && !loadedSession.loading && !session())
|
||||
const visible = createMemo(() => !!session() || missingSession() || !!persisted()?.title)
|
||||
let prefetched = false
|
||||
|
||||
const rename = async (title: string) => {
|
||||
const value = session()
|
||||
const ctx = props.serverCtx()
|
||||
if (!value || !ctx) return
|
||||
|
||||
ctx.sync.session.remember({ ...value, title })
|
||||
try {
|
||||
await ctx.sdk.api.session.rename({ sessionID: value.id, title })
|
||||
} catch (err) {
|
||||
const current = session()
|
||||
const currentCtx = props.serverCtx()
|
||||
if (current && currentCtx) currentCtx.sync.session.remember({ ...current, title: value.title })
|
||||
showToast({
|
||||
title: language.t("common.requestFailed"),
|
||||
description: err instanceof Error ? err.message : undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
createEffect(() => props.onVisibleChange(visible()))
|
||||
|
||||
createEffect(() => {
|
||||
const ctx = props.serverCtx()
|
||||
const value = session()
|
||||
@@ -83,39 +150,20 @@ function SessionTabSlot(props: {
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={sortable.ref}
|
||||
data-titlebar-tab-slot
|
||||
data-tab-key={props.id}
|
||||
data-active={props.active()}
|
||||
class="relative flex w-56 min-w-7 max-w-56 flex-shrink"
|
||||
classList={{ hidden: !session() && !missingSession() && !persisted()?.title }}
|
||||
>
|
||||
<TabNavItem
|
||||
ref={(el) => {
|
||||
ref = el
|
||||
}}
|
||||
href={tabHref(props.tab)}
|
||||
server={props.tab.server}
|
||||
<Show when={visible()}>
|
||||
<SessionTabSlot
|
||||
tab={props.tab}
|
||||
id={props.id}
|
||||
index={props.index}
|
||||
active={props.active}
|
||||
forceTruncate={props.forceTruncate}
|
||||
session={session}
|
||||
fallbackTitle={persisted()?.title ?? (missingSession() ? language.t("session.tab.unknown") : undefined)}
|
||||
onTitleChange={(title) => {
|
||||
const value = session()
|
||||
const ctx = props.serverCtx()
|
||||
if (value && ctx) ctx.sync.session.remember({ ...value, title })
|
||||
}}
|
||||
onTitleChangeFailed={(title) => {
|
||||
const value = session()
|
||||
const ctx = props.serverCtx()
|
||||
if (value && ctx) ctx.sync.session.remember({ ...value, title })
|
||||
}}
|
||||
onNavigate={() => props.onNavigate(ref)}
|
||||
onRename={rename}
|
||||
onNavigate={props.onNavigate}
|
||||
onClose={props.onClose}
|
||||
active={props.active()}
|
||||
forceTruncate={props.forceTruncate}
|
||||
dragging={sortable.isDragSource()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,11 +220,39 @@ export function TitlebarTabStrip(props: {
|
||||
}) {
|
||||
const global = useGlobal()
|
||||
const language = useLanguage()
|
||||
const command = useCommand()
|
||||
let scrollRef!: HTMLDivElement
|
||||
let listRef!: HTMLDivElement
|
||||
let resizeFrame: number | undefined
|
||||
const [visibility, setVisibility] = createStore<Record<string, boolean>>({})
|
||||
const visibleTabs = createMemo(() => props.tabs.filter((tab) => tab.type === "draft" || visibility[tabKey(tab)]))
|
||||
const visibleTabIds = () => visibleTabs().map(tabKey)
|
||||
|
||||
const tabIds = () => props.tabs.map(tabKey)
|
||||
command.register("titlebar-tab-cycle", () => [
|
||||
{
|
||||
id: `tab.prev`,
|
||||
category: "tab",
|
||||
title: "",
|
||||
keybind: `mod+option+ArrowLeft,ctrl+shift+tab`,
|
||||
hidden: true,
|
||||
onSelect: () => selectAdjacentTab(-1),
|
||||
},
|
||||
{
|
||||
id: `tab.next`,
|
||||
category: "tab",
|
||||
title: "",
|
||||
keybind: `mod+option+ArrowRight,ctrl+tab`,
|
||||
hidden: true,
|
||||
onSelect: () => selectAdjacentTab(1),
|
||||
},
|
||||
])
|
||||
|
||||
function selectAdjacentTab(offset: -1 | 1) {
|
||||
const current = props.currentTab()
|
||||
const key = adjacentTabKey(visibleTabIds(), current ? tabKey(current) : undefined, offset)
|
||||
const next = props.tabs.find((tab) => tabKey(tab) === key)
|
||||
if (next) props.onNavigate(next)
|
||||
}
|
||||
|
||||
function refreshOverflow() {
|
||||
if (!scrollRef) return
|
||||
@@ -204,7 +280,7 @@ export function TitlebarTabStrip(props: {
|
||||
|
||||
createEffect(() => {
|
||||
props.tabs.length
|
||||
tabIds()
|
||||
visibleTabIds()
|
||||
refreshOverflow()
|
||||
})
|
||||
|
||||
@@ -240,22 +316,29 @@ export function TitlebarTabStrip(props: {
|
||||
props.onNavigate(tab, tabEl ?? undefined)
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
const current = tabIds()
|
||||
const current = visibleTabIds()
|
||||
const source = event.operation.source
|
||||
if (event.canceled || !isSortable(source)) return
|
||||
|
||||
const { initialIndex, index } = source
|
||||
if (initialIndex !== index) {
|
||||
props.onReorder(arrayMove(current, source.initialIndex, source.index))
|
||||
props.onReorder(
|
||||
mergeVisibleTabOrder(
|
||||
props.tabs.map(tabKey),
|
||||
current,
|
||||
arrayMove(current, source.initialIndex, source.index),
|
||||
),
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div data-titlebar-tab-list class="flex w-full min-w-0 flex-row items-center" ref={listRef}>
|
||||
<For each={props.tabs}>
|
||||
{(tab, index) => {
|
||||
{(tab) => {
|
||||
const id = tabKey(tab)
|
||||
let ref!: HTMLDivElement
|
||||
useTabShortcut(index, () => props.onNavigate(tab, ref))
|
||||
const visibleIndex = () => visibleTabs().findIndex((item) => tabKey(item) === id)
|
||||
useTabShortcut(visibleIndex, () => props.onNavigate(tab, ref))
|
||||
const serverCtx = createMemo(() => {
|
||||
if (tab.type !== "session") return
|
||||
const conn = global.servers.list().find((item) => ServerConnection.key(item) === tab.server)
|
||||
@@ -264,13 +347,14 @@ export function TitlebarTabStrip(props: {
|
||||
|
||||
if (tab.type === "session") {
|
||||
return (
|
||||
<SessionTabSlot
|
||||
<SessionTabEntry
|
||||
tab={tab}
|
||||
id={id}
|
||||
index={index}
|
||||
index={visibleIndex}
|
||||
active={() => props.currentTab() === tab}
|
||||
forceTruncate={props.forceTruncate}
|
||||
serverCtx={serverCtx}
|
||||
onVisibleChange={(visible) => setVisibility(id, visible)}
|
||||
onNavigate={(element) => {
|
||||
ref = element
|
||||
props.onNavigate(tab, element)
|
||||
@@ -284,7 +368,7 @@ export function TitlebarTabStrip(props: {
|
||||
<DraftTabSlot
|
||||
tab={tab}
|
||||
id={id}
|
||||
index={index}
|
||||
index={visibleIndex}
|
||||
active={() => props.currentTab() === tab}
|
||||
title={language.t("command.session.new")}
|
||||
onNavigate={(element) => {
|
||||
@@ -318,7 +402,7 @@ function useTabShortcut(index: () => number, onSelect: () => void) {
|
||||
|
||||
command.register(() => {
|
||||
const number = index() + 1
|
||||
if (number > 9) return []
|
||||
if (number < 1 || number > 9) return []
|
||||
return [
|
||||
{
|
||||
id: `tab.${number}`,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||
import { useTheme } from "@opencode-ai/ui/theme/context"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
@@ -30,31 +29,11 @@ import "./titlebar.css"
|
||||
import { newTabTooltipKeybind } from "./command-tooltip-keybind"
|
||||
import { normalizeSessionInfo } from "@/utils/session"
|
||||
|
||||
type TauriDesktopWindow = {
|
||||
startDragging?: () => Promise<void>
|
||||
toggleMaximize?: () => Promise<void>
|
||||
}
|
||||
|
||||
type TauriThemeWindow = {
|
||||
setTheme?: (theme?: "light" | "dark" | null) => Promise<void>
|
||||
}
|
||||
|
||||
type TauriApi = {
|
||||
window?: {
|
||||
getCurrentWindow?: () => TauriDesktopWindow
|
||||
}
|
||||
webviewWindow?: {
|
||||
getCurrentWebviewWindow?: () => TauriThemeWindow
|
||||
}
|
||||
}
|
||||
|
||||
const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
|
||||
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
|
||||
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
|
||||
const legacyTitlebarHeight = 40
|
||||
const v2TitlebarHeight = 36
|
||||
const minTitlebarZoom = 0.25
|
||||
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
|
||||
const macTrafficLightsBaseWidth = 84
|
||||
|
||||
export type TitlebarUpdate = {
|
||||
version: () => string | undefined
|
||||
@@ -74,7 +53,6 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
const settings = useSettings()
|
||||
const theme = useTheme()
|
||||
const server = useServer()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
@@ -85,9 +63,9 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
|
||||
const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos")
|
||||
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
|
||||
const electronWindows = createMemo(() => windows() && !tauriApi())
|
||||
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
|
||||
const web = createMemo(() => platform.platform === "web")
|
||||
const macTrafficLights = createMemo(() => mac() && !platform.windowFullscreen?.())
|
||||
const zoom = () => platform.webviewZoom?.() ?? 1
|
||||
const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom())
|
||||
const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1)
|
||||
@@ -176,56 +154,6 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
},
|
||||
])
|
||||
|
||||
const getWin = () => {
|
||||
if (platform.platform !== "desktop") return
|
||||
return currentDesktopWindow()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (platform.platform !== "desktop") return
|
||||
|
||||
const scheme = theme.colorScheme()
|
||||
const value = scheme === "system" ? null : scheme
|
||||
|
||||
const win = currentThemeWindow()
|
||||
if (!win?.setTheme) return
|
||||
|
||||
void win.setTheme(value).catch(() => undefined)
|
||||
})
|
||||
|
||||
const interactive = (target: EventTarget | null) => {
|
||||
if (!(target instanceof Element)) return false
|
||||
|
||||
const selector =
|
||||
"button, a, input, textarea, select, option, [role='button'], [role='menuitem'], [contenteditable='true'], [contenteditable='']"
|
||||
|
||||
return !!target.closest(selector)
|
||||
}
|
||||
|
||||
const drag = (e: MouseEvent) => {
|
||||
if (platform.platform !== "desktop") return
|
||||
if (e.buttons !== 1) return
|
||||
if (interactive(e.target)) return
|
||||
|
||||
const win = getWin()
|
||||
if (!win?.startDragging) return
|
||||
|
||||
e.preventDefault()
|
||||
void win.startDragging().catch(() => undefined)
|
||||
}
|
||||
|
||||
const maximize = (e: MouseEvent) => {
|
||||
if (platform.platform !== "desktop") return
|
||||
if (interactive(e.target)) return
|
||||
if (e.target instanceof Element && e.target.closest("[data-tauri-decorum-tb]")) return
|
||||
|
||||
const win = getWin()
|
||||
if (!win?.toggleMaximize) return
|
||||
|
||||
e.preventDefault()
|
||||
void win.toggleMaximize().catch(() => undefined)
|
||||
}
|
||||
|
||||
return (
|
||||
<header
|
||||
data-slot={useV2Titlebar() ? "titlebar-v2" : undefined}
|
||||
@@ -238,16 +166,12 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
style={{
|
||||
"min-height": minHeight(),
|
||||
// Keep native macOS traffic lights clear even when the desktop window is narrow.
|
||||
"padding-left": mac() ? `${84 / zoom()}px` : 0,
|
||||
width: electronWindows() ? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))` : undefined,
|
||||
"max-width": electronWindows()
|
||||
? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))`
|
||||
: undefined,
|
||||
"align-self": electronWindows() ? "flex-start" : undefined,
|
||||
"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,
|
||||
}}
|
||||
data-tauri-drag-region
|
||||
onMouseDown={drag}
|
||||
onDblClick={maximize}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={useV2Titlebar()}>
|
||||
@@ -414,40 +338,6 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
keybind: "mod+shift+t",
|
||||
onSelect: () => tabsStoreActions.reopenClosedTab(),
|
||||
},
|
||||
{
|
||||
id: `tab.prev`,
|
||||
category: "tab",
|
||||
title: "",
|
||||
keybind: `mod+option+ArrowLeft,ctrl+shift+tab`,
|
||||
hidden: true,
|
||||
onSelect: () => {
|
||||
let index = tabsStore.findIndex((tab) => tab === currentTab())
|
||||
if (index === -1) return
|
||||
|
||||
index -= 1
|
||||
if (index === -1) index = tabsStore.length - 1
|
||||
|
||||
const next = tabsStore[index]
|
||||
if (next) tabs.select(next)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: `tab.next`,
|
||||
category: "tab",
|
||||
title: "",
|
||||
keybind: `mod+option+ArrowRight,ctrl+tab`,
|
||||
hidden: true,
|
||||
onSelect: () => {
|
||||
let index = tabsStore.findIndex((tab) => tab === currentTab())
|
||||
if (index === -1) return
|
||||
|
||||
index += 1
|
||||
if (index === tabsStore.length) index = 0
|
||||
|
||||
const next = tabsStore[index]
|
||||
if (next) tabs.select(next)
|
||||
},
|
||||
},
|
||||
].filter((v) => v !== undefined)
|
||||
})
|
||||
|
||||
@@ -459,8 +349,8 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
classList={{
|
||||
"pt-2": !bottom(),
|
||||
"pb-2": bottom(),
|
||||
"md:pl-2": mac(),
|
||||
"md:pl-4": !mac(),
|
||||
"md:pl-2": macTrafficLights(),
|
||||
"md:pl-4": !macTrafficLights(),
|
||||
}}
|
||||
>
|
||||
<ChannelIndicator debugTools={props.debugTools} />
|
||||
@@ -505,32 +395,27 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
}}
|
||||
onReorder={(keys) => tabsStoreActions.reorder(keys)}
|
||||
/>
|
||||
<Show when={!creating()}>
|
||||
<TooltipV2
|
||||
placement="bottom"
|
||||
value={
|
||||
<>
|
||||
{language.t("command.session.new")}
|
||||
<KeybindV2 keys={newTabTooltipKeybind(command)} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
class="shrink-0"
|
||||
icon={<IconV2 name="plus" />}
|
||||
onClick={openNewTab}
|
||||
aria-label={language.t("command.session.new")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
<TooltipV2
|
||||
placement="bottom"
|
||||
value={
|
||||
<>
|
||||
{language.t("command.session.new")}
|
||||
<KeybindV2 keys={newTabTooltipKeybind(command)} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<IconButtonV2
|
||||
type="button"
|
||||
variant="ghost-muted"
|
||||
size="large"
|
||||
class="shrink-0"
|
||||
icon={<IconV2 name="plus" />}
|
||||
onClick={openNewTab}
|
||||
aria-label={language.t("command.session.new")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
<div class="flex-1" />
|
||||
<TitlebarV2Right state={v2RightState()} />
|
||||
<Show when={windows() && !electronWindows()}>
|
||||
<div data-tauri-decorum-tb class="flex flex-row" />
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
@@ -543,14 +428,13 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
<div
|
||||
classList={{
|
||||
"flex items-center min-w-0": true,
|
||||
"pl-2": !mac(),
|
||||
"pl-2": !macTrafficLights(),
|
||||
}}
|
||||
>
|
||||
<Show when={windows() || linux()}>
|
||||
<WindowsAppMenu command={command} platform={platform} />
|
||||
</Show>
|
||||
<Show when={mac()}>
|
||||
{/*<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />*/}
|
||||
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
icon="menu"
|
||||
@@ -680,12 +564,10 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
|
||||
"pr-2": !windows(),
|
||||
}}
|
||||
data-tauri-drag-region
|
||||
onMouseDown={drag}
|
||||
>
|
||||
<div id="opencode-titlebar-right" class="flex items-center gap-1 shrink-0 justify-end" />
|
||||
<Show when={windows()}>
|
||||
{!tauriApi() && <div class="shrink-0" style={{ width: windowsControlsWidth() }} />}
|
||||
<div data-tauri-decorum-tb class="flex flex-row" />
|
||||
<div class="shrink-0" style={{ width: windowsControlsWidth() }} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,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 (
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { commandPaletteOptions, resolveKeybindOption, upsertCommandRegistration, type CommandOption } from "./command"
|
||||
import {
|
||||
activeCommandRegistrations,
|
||||
addCommandRegistration,
|
||||
commandPaletteOptions,
|
||||
resolveKeybindOption,
|
||||
type CommandOption,
|
||||
} from "./command"
|
||||
|
||||
const paletteOptions: CommandOption[] = [
|
||||
{ id: "settings.open", title: "Open settings" },
|
||||
@@ -15,22 +21,31 @@ describe("commandPaletteOptions", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("upsertCommandRegistration", () => {
|
||||
test("replaces keyed registrations", () => {
|
||||
describe("command registrations", () => {
|
||||
test("shadows keyed registrations while retaining the previous owner", () => {
|
||||
const one = () => [{ id: "one", title: "One" }]
|
||||
const two = () => [{ id: "two", title: "Two" }]
|
||||
|
||||
const next = upsertCommandRegistration([{ key: "layout", options: one }], { key: "layout", options: two })
|
||||
const registrations = addCommandRegistration([{ key: "layout", options: one }], {
|
||||
key: "layout",
|
||||
options: two,
|
||||
})
|
||||
const active = activeCommandRegistrations(registrations)
|
||||
|
||||
expect(next).toHaveLength(1)
|
||||
expect(next[0]?.options).toBe(two)
|
||||
expect(registrations).toHaveLength(2)
|
||||
expect(active).toHaveLength(1)
|
||||
expect(active[0]?.options).toBe(two)
|
||||
|
||||
const restored = activeCommandRegistrations(registrations.filter((entry) => entry.options !== two))
|
||||
expect(restored).toHaveLength(1)
|
||||
expect(restored[0]?.options).toBe(one)
|
||||
})
|
||||
|
||||
test("keeps unkeyed registrations additive", () => {
|
||||
const one = () => [{ id: "one", title: "One" }]
|
||||
const two = () => [{ id: "two", title: "Two" }]
|
||||
|
||||
const next = upsertCommandRegistration([{ options: one }], { options: two })
|
||||
const next = activeCommandRegistrations(addCommandRegistration([{ options: one }], { options: two }))
|
||||
|
||||
expect(next).toHaveLength(2)
|
||||
expect(next[0]?.options).toBe(two)
|
||||
|
||||
@@ -114,9 +114,18 @@ export type CommandRegistration = {
|
||||
options: Accessor<CommandOption[]>
|
||||
}
|
||||
|
||||
export function upsertCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) {
|
||||
if (entry.key === undefined) return [entry, ...registrations]
|
||||
return [entry, ...registrations.filter((x) => x.key !== entry.key)]
|
||||
export function addCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) {
|
||||
return [entry, ...registrations]
|
||||
}
|
||||
|
||||
export function activeCommandRegistrations(registrations: CommandRegistration[]) {
|
||||
const keys = new Set<string>()
|
||||
return registrations.filter((entry) => {
|
||||
if (entry.key === undefined) return true
|
||||
if (keys.has(entry.key)) return false
|
||||
keys.add(entry.key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
export function parseKeybind(config: string): Keybind[] {
|
||||
@@ -281,7 +290,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
||||
const seen = new Set<string>()
|
||||
const all: CommandOption[] = []
|
||||
|
||||
for (const reg of store.registrations) {
|
||||
for (const reg of activeCommandRegistrations(store.registrations)) {
|
||||
for (const opt of reg.options()) {
|
||||
if (seen.has(opt.id)) {
|
||||
if (import.meta.env.DEV && !warnedDuplicates.has(opt.id)) {
|
||||
@@ -425,7 +434,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
||||
key: id,
|
||||
options,
|
||||
}
|
||||
setStore("registrations", (arr) => upsertCommandRegistration(arr, entry))
|
||||
setStore("registrations", (arr) => addCommandRegistration(arr, entry))
|
||||
onCleanup(() => {
|
||||
setStore("registrations", (arr) => arr.filter((x) => x !== entry))
|
||||
})
|
||||
|
||||
@@ -21,6 +21,29 @@ describe("file path helpers", () => {
|
||||
expect(path.normalize("c:\\repo\\src\\app.ts")).toBe("src\\app.ts")
|
||||
})
|
||||
|
||||
test("normalizes Windows directory separators", () => {
|
||||
const path = createPathHelpers(() => "C:\\repo")
|
||||
expect(path.normalizeDir("frontend\\")).toBe("frontend")
|
||||
expect(path.normalizeDir("frontend\\src\\")).toBe("frontend/src")
|
||||
expect(path.normalizeDir("C:\\repo\\frontend\\")).toBe("frontend")
|
||||
})
|
||||
|
||||
test("normalizes separators for Windows roots written with forward slashes", () => {
|
||||
const path = createPathHelpers(() => "C:/repo")
|
||||
expect(path.normalizeDir("frontend\\src\\")).toBe("frontend/src")
|
||||
})
|
||||
|
||||
test("normalizes separators for Windows UNC roots", () => {
|
||||
const path = createPathHelpers(() => "\\\\server\\share")
|
||||
expect(path.normalizeDir("\\\\server\\share\\frontend\\")).toBe("frontend")
|
||||
})
|
||||
|
||||
test("preserves backslashes in POSIX directory names", () => {
|
||||
const path = createPathHelpers(() => "/repo")
|
||||
expect(path.normalizeDir("literal\\name\\")).toBe("literal\\name\\")
|
||||
expect(path.normalizeDir("literal\\name/")).toBe("literal\\name")
|
||||
})
|
||||
|
||||
test("keeps query/hash stripping behavior stable", () => {
|
||||
expect(stripQueryAndHash("a/b.ts#L12?x=1")).toBe("a/b.ts")
|
||||
expect(stripQueryAndHash("a/b.ts?x=1#L12")).toBe("a/b.ts")
|
||||
|
||||
@@ -140,7 +140,12 @@ export function createPathHelpers(scope: () => string) {
|
||||
return normalize(tabValue)
|
||||
}
|
||||
|
||||
const normalizeDir = (input: string) => normalize(input).replace(/\/+$/, "")
|
||||
const normalizeDir = (input: string) => {
|
||||
const path = normalize(input)
|
||||
const root = scope()
|
||||
const windows = /^[A-Za-z]:/.test(root) || root.startsWith("\\\\")
|
||||
return (windows ? path.replace(/\\/g, "/") : path).replace(/\/+$/, "")
|
||||
}
|
||||
|
||||
return {
|
||||
normalize,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
bootstrapDirectory,
|
||||
loadAgentsQuery,
|
||||
loadCommands,
|
||||
loadGlobalConfigQuery,
|
||||
loadPathQuery,
|
||||
loadProjectsQuery,
|
||||
loadProvidersQuery,
|
||||
@@ -76,6 +77,7 @@ function directoryState() {
|
||||
|
||||
describe("bootstrapDirectory", () => {
|
||||
test("uses legacy MCP endpoints while refreshing a v1 directory", async () => {
|
||||
const legacyConfigReads: string[] = []
|
||||
const mcpReads: string[] = []
|
||||
const [store, setStore] = directoryState()
|
||||
|
||||
@@ -91,7 +93,12 @@ describe("bootstrapDirectory", () => {
|
||||
},
|
||||
sdk: {
|
||||
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
|
||||
config: { get: async () => ({ data: {} }) },
|
||||
config: {
|
||||
get: async () => {
|
||||
legacyConfigReads.push("directory")
|
||||
return { data: {} }
|
||||
},
|
||||
},
|
||||
session: { status: async () => ({ data: {} }) },
|
||||
vcs: { get: async () => ({ data: undefined }) },
|
||||
command: {
|
||||
@@ -134,8 +141,88 @@ describe("bootstrapDirectory", () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 80))
|
||||
|
||||
expect(store.status).toBe("complete")
|
||||
expect(legacyConfigReads).toEqual(["directory"])
|
||||
expect(mcpReads.sort()).toEqual(["command", "resource", "status"])
|
||||
})
|
||||
|
||||
test("skips legacy config while refreshing a v2 directory", async () => {
|
||||
const [store, setStore] = directoryState()
|
||||
|
||||
await bootstrapDirectory({
|
||||
directory: "/project",
|
||||
scope: ServerScope.local,
|
||||
mcp: false,
|
||||
global: {
|
||||
config: {} satisfies Config,
|
||||
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
||||
project: [{ id: "project", worktree: "/project" } as Project],
|
||||
provider,
|
||||
},
|
||||
sdk: {
|
||||
config: {
|
||||
get: async () => {
|
||||
throw new Error("legacy directory config should not be called")
|
||||
},
|
||||
},
|
||||
} as unknown as OpencodeClient,
|
||||
api,
|
||||
store,
|
||||
setStore,
|
||||
vcsCache: { setStore() {} } as unknown as VcsCache,
|
||||
loadSessions() {},
|
||||
translate: (key) => key,
|
||||
queryClient: new QueryClient(),
|
||||
protocol: Promise.resolve("v2"),
|
||||
})
|
||||
|
||||
expect(store.status).toBe("partial")
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 80))
|
||||
|
||||
expect(store.status).toBe("complete")
|
||||
})
|
||||
})
|
||||
|
||||
describe("config queries", () => {
|
||||
test("skips legacy global config for v2 servers", async () => {
|
||||
const sdk = {
|
||||
global: {
|
||||
config: {
|
||||
get: async () => {
|
||||
throw new Error("legacy global config should not be called")
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpencodeClient
|
||||
|
||||
const result = await new QueryClient().fetchQuery(
|
||||
loadGlobalConfigQuery(ServerScope.local, sdk, Promise.resolve("v2")),
|
||||
)
|
||||
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
test("loads legacy global config for v1 servers", async () => {
|
||||
const calls: string[] = []
|
||||
const config = { shell: "zsh" } satisfies Config
|
||||
const sdk = {
|
||||
global: {
|
||||
config: {
|
||||
get: async () => {
|
||||
calls.push("global")
|
||||
return { data: config }
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpencodeClient
|
||||
|
||||
const result = await new QueryClient().fetchQuery(
|
||||
loadGlobalConfigQuery(ServerScope.local, sdk, Promise.resolve("v1")),
|
||||
)
|
||||
|
||||
expect(result).toEqual(config)
|
||||
expect(calls).toEqual(["global"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("query keys", () => {
|
||||
|
||||
@@ -105,10 +105,13 @@ function showErrors(input: {
|
||||
})
|
||||
}
|
||||
|
||||
export const loadGlobalConfigQuery = (scope: ServerScope, sdk: OpencodeClient) =>
|
||||
export const loadGlobalConfigQuery = (scope: ServerScope, sdk: OpencodeClient, protocol?: Promise<ServerProtocol>) =>
|
||||
queryOptions({
|
||||
queryKey: [scope, "config"],
|
||||
queryFn: () => retry(() => sdk.global.config.get().then((x) => x.data!)),
|
||||
queryFn: async () => {
|
||||
if ((await protocol) !== "v1") return {}
|
||||
return retry(() => sdk.global.config.get().then((x) => x.data!))
|
||||
},
|
||||
})
|
||||
|
||||
type ProjectApi = {
|
||||
@@ -149,7 +152,7 @@ export async function bootstrapGlobal(input: {
|
||||
queryClient: QueryClient
|
||||
}) {
|
||||
const slow = [
|
||||
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.scope, input.serverSDK)),
|
||||
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.scope, input.serverSDK, input.protocol)),
|
||||
() =>
|
||||
input.queryClient.fetchQuery(
|
||||
loadProvidersQuery(input.scope, null, input.serverAPI, input.serverSDK, input.protocol),
|
||||
@@ -376,7 +379,10 @@ export async function bootstrapDirectory(input: {
|
||||
.ensureQueryData(loadAgentsQuery(input.scope, input.directory, input.api.agent, input.sdk, input.protocol))
|
||||
.then((data) => input.setStore("agent", data)),
|
||||
() =>
|
||||
retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
|
||||
retry(async () => {
|
||||
if ((await input.protocol) !== "v1") return
|
||||
return input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))
|
||||
}),
|
||||
() =>
|
||||
retry(() =>
|
||||
(async () => {
|
||||
|
||||
@@ -25,6 +25,18 @@ export type Locale =
|
||||
| "th"
|
||||
| "bs"
|
||||
| "tr"
|
||||
| "hi"
|
||||
| "nl"
|
||||
| "id"
|
||||
| "vi"
|
||||
| "it"
|
||||
| "ur"
|
||||
| "pa"
|
||||
| "az"
|
||||
| "fi"
|
||||
| "sv"
|
||||
|
||||
const RTL_LOCALES: ReadonlySet<Locale> = new Set(["ar", "ur", "pa"])
|
||||
|
||||
type RawDictionary = typeof en & typeof uiEn
|
||||
type Dictionary = i18n.Flatten<RawDictionary>
|
||||
@@ -53,6 +65,16 @@ const LOCALES: readonly Locale[] = [
|
||||
"br",
|
||||
"th",
|
||||
"tr",
|
||||
"hi",
|
||||
"nl",
|
||||
"id",
|
||||
"vi",
|
||||
"it",
|
||||
"ur",
|
||||
"pa",
|
||||
"az",
|
||||
"fi",
|
||||
"sv",
|
||||
]
|
||||
|
||||
const INTL: Record<Locale, string> = {
|
||||
@@ -74,9 +96,19 @@ const INTL: Record<Locale, string> = {
|
||||
th: "th",
|
||||
bs: "bs",
|
||||
tr: "tr",
|
||||
hi: "hi-IN",
|
||||
nl: "nl-NL",
|
||||
id: "id-ID",
|
||||
vi: "vi-VN",
|
||||
it: "it-IT",
|
||||
ur: "ur-PK",
|
||||
pa: "pa-Arab-PK",
|
||||
az: "az-Latn-AZ",
|
||||
fi: "fi-FI",
|
||||
sv: "sv-SE",
|
||||
}
|
||||
|
||||
const LABEL_KEY: Record<Locale, keyof Dictionary> = {
|
||||
const LABEL_KEY: Partial<Record<Locale, keyof Dictionary>> = {
|
||||
en: "language.en",
|
||||
zh: "language.zh",
|
||||
zht: "language.zht",
|
||||
@@ -97,6 +129,19 @@ const LABEL_KEY: Record<Locale, keyof Dictionary> = {
|
||||
tr: "language.tr",
|
||||
}
|
||||
|
||||
const LABEL: Partial<Record<Locale, string>> = {
|
||||
hi: "हिन्दी",
|
||||
nl: "Nederlands",
|
||||
id: "Bahasa Indonesia",
|
||||
vi: "Tiếng Việt",
|
||||
it: "Italiano",
|
||||
ur: "اردو",
|
||||
pa: "پنجابی",
|
||||
az: "Azərbaycanca",
|
||||
fi: "Suomi",
|
||||
sv: "Svenska",
|
||||
}
|
||||
|
||||
const base = i18n.flatten({ ...en, ...uiEn })
|
||||
const dicts = new Map<Locale, Dictionary>([["en", base]])
|
||||
|
||||
@@ -121,6 +166,16 @@ 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")),
|
||||
}
|
||||
|
||||
function loadDict(locale: Locale) {
|
||||
@@ -140,7 +195,12 @@ export function loadLocaleDict(locale: Locale) {
|
||||
|
||||
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: "zht",
|
||||
match: (language) =>
|
||||
language.startsWith("zh") &&
|
||||
(language.includes("hant") || language.includes("-tw") || language.includes("-hk") || language.includes("-mo")),
|
||||
},
|
||||
{ locale: "zh", match: (language) => language.startsWith("zh") },
|
||||
{ locale: "ko", match: (language) => language.startsWith("ko") },
|
||||
{ locale: "de", match: (language) => language.startsWith("de") },
|
||||
@@ -160,6 +220,22 @@ const localeMatchers: Array<{ locale: Locale; match: (language: string) => boole
|
||||
{ locale: "th", match: (language) => language.startsWith("th") },
|
||||
{ locale: "bs", match: (language) => language.startsWith("bs") },
|
||||
{ locale: "tr", match: (language) => language.startsWith("tr") },
|
||||
{ locale: "hi", match: (language) => language.startsWith("hi") },
|
||||
{ locale: "nl", match: (language) => language.startsWith("nl") },
|
||||
{ locale: "id", match: (language) => language.startsWith("id") },
|
||||
{ locale: "vi", match: (language) => language.startsWith("vi") },
|
||||
{ locale: "it", match: (language) => language.startsWith("it") },
|
||||
{ locale: "ur", match: (language) => language.startsWith("ur") },
|
||||
{
|
||||
locale: "pa",
|
||||
match: (language) => language.startsWith("pa") && (language.includes("arab") || language.includes("-pk")),
|
||||
},
|
||||
{
|
||||
locale: "az",
|
||||
match: (language) => language.startsWith("az") && !language.includes("arab") && !language.includes("cyrl"),
|
||||
},
|
||||
{ locale: "fi", match: (language) => language.startsWith("fi") },
|
||||
{ locale: "sv", match: (language) => language.startsWith("sv") },
|
||||
]
|
||||
|
||||
function detectLocale(): Locale {
|
||||
@@ -220,12 +296,18 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
||||
params?: Record<string, string | number | boolean>,
|
||||
) => string
|
||||
|
||||
const label = (value: Locale) => t(LABEL_KEY[value])
|
||||
const label = (value: Locale) => {
|
||||
const key = LABEL_KEY[value]
|
||||
if (key) return t(key)
|
||||
return LABEL[value] ?? value
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (typeof document !== "object") return
|
||||
document.documentElement.lang = locale()
|
||||
document.cookie = cookie(locale())
|
||||
const value = locale()
|
||||
document.documentElement.lang = value
|
||||
document.documentElement.dir = RTL_LOCALES.has(value) ? "rtl" : "ltr"
|
||||
document.cookie = cookie(value)
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -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,12 +92,12 @@ 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>
|
||||
|
||||
/** Whether the native desktop window is fullscreen */
|
||||
windowFullscreen?: Accessor<boolean>
|
||||
|
||||
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
|
||||
getPinchZoomEnabled?(): Promise<boolean> | boolean
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ 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"
|
||||
|
||||
interface PartBase {
|
||||
content: string
|
||||
@@ -37,7 +38,7 @@ export interface ImageAttachmentPart {
|
||||
filename: string
|
||||
sourcePath?: string
|
||||
mime: string
|
||||
dataUrl: string
|
||||
blob: BlobReference
|
||||
}
|
||||
|
||||
export type ContentPart = TextPart | FileAttachmentPart | AgentPart | ImageAttachmentPart
|
||||
@@ -168,9 +169,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 {
|
||||
@@ -245,7 +246,7 @@ export function createPromptSession(serverScope: ServerScope, scope: PromptScope
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
@@ -184,7 +184,7 @@ function makeQueryOptionsApi(
|
||||
protocol: Promise<"v1" | "v2">,
|
||||
) {
|
||||
return {
|
||||
globalConfig: () => loadGlobalConfigQuery(scope, serverSDK()),
|
||||
globalConfig: () => loadGlobalConfigQuery(scope, serverSDK(), protocol),
|
||||
projects: () => loadProjectsQuery(scope, serverAPI.project),
|
||||
providers: (directory: PathKey | null) =>
|
||||
loadProvidersQuery(scope, directory, serverAPI, directory ? sdkFor(directory) : serverSDK(), protocol),
|
||||
@@ -290,6 +290,10 @@ export function createServerSyncContextInner(serverSDK: ServerSDK) {
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const homeSessions = createHomeSessionIndexCache(queryClient, ServerConnection.key(serverSDK.server))
|
||||
const refreshProviders = () =>
|
||||
queryClient.refetchQueries({
|
||||
predicate: (query) => query.queryKey[0] === serverSDK.scope && query.queryKey[2] === "providers",
|
||||
})
|
||||
|
||||
let bootedAt = 0
|
||||
let bootingRoot = false
|
||||
@@ -537,6 +541,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK) {
|
||||
homeSessions.apply(event)
|
||||
}
|
||||
homeSessions.refresh(event.type)
|
||||
if (eventType === "integration.connection.updated") void refreshProviders()
|
||||
|
||||
if (directory === "global") {
|
||||
if (eventType === "server.connected" && activeSessionsQuery.data === undefined && !activeSessionsQuery.isFetching)
|
||||
@@ -678,6 +683,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK) {
|
||||
peek: children.peek,
|
||||
disableMcp: children.disableMcp,
|
||||
queryOptions: queryOptionsApi,
|
||||
refreshProviders,
|
||||
// bootstrap,
|
||||
updateConfig: updateConfigMutation.mutateAsync,
|
||||
project: projectApi,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import {
|
||||
hasExistingWebState,
|
||||
initialAgentVisibility,
|
||||
isAppUpgrade,
|
||||
layoutTransitionState,
|
||||
maximumSunsetTimeout,
|
||||
@@ -11,6 +12,22 @@ import {
|
||||
shouldEnableNewLayout,
|
||||
} from "./settings"
|
||||
|
||||
describe("agent visibility", () => {
|
||||
test("shows the picker for existing profiles and hides it for first-time installs", () => {
|
||||
expect(initialAgentVisibility(undefined, true)).toBe(true)
|
||||
expect(initialAgentVisibility(undefined, false)).toBe(false)
|
||||
})
|
||||
|
||||
test("shows the picker when updating from a recent release", () => {
|
||||
expect(initialAgentVisibility(undefined, false, "1.18.8")).toBe(true)
|
||||
})
|
||||
|
||||
test("preserves the preference after initialization", () => {
|
||||
expect(initialAgentVisibility(true, true, "1.18.8")).toBeUndefined()
|
||||
expect(initialAgentVisibility(true, false)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("layout transition", () => {
|
||||
test("blank profiles default to the new layout", () => {
|
||||
expect(newLayoutDesignsDefault).toBe(true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||
import { batch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { persisted } from "@/utils/persist"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
@@ -36,6 +36,7 @@ export interface Settings {
|
||||
mobileTitlebarPosition: "top" | "bottom"
|
||||
newLayoutDesigns?: boolean
|
||||
layoutTransitionEligible?: boolean
|
||||
agentVisibilityInitialized?: boolean
|
||||
newInterfaceNoticeDismissed?: boolean
|
||||
shouldDisplayTabsToast?: boolean
|
||||
}
|
||||
@@ -93,6 +94,11 @@ export function hasExistingWebState(settings: Promise<string> | string | null, p
|
||||
return settings !== null || previousVersion !== undefined
|
||||
}
|
||||
|
||||
export function initialAgentVisibility(initialized: boolean | undefined, existing: boolean, previousVersion?: string) {
|
||||
if (initialized === true) return
|
||||
return existing || previousVersion !== undefined
|
||||
}
|
||||
|
||||
export function shouldEnableNewLayout(previous: string | undefined, current: string | undefined) {
|
||||
if (!current) return false
|
||||
const currentComparison = compareVersions(current, newLayoutDesignsUpgradeCutoff)
|
||||
@@ -271,6 +277,14 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
|
||||
)
|
||||
})
|
||||
const visible = (preference: () => boolean) => createMemo(() => !newLayoutDesigns() || preference())
|
||||
const initializeAgentVisibility = (existing: boolean) => {
|
||||
const initial = initialAgentVisibility(store.general?.agentVisibilityInitialized, existing, launchState.previous)
|
||||
if (initial === undefined) return
|
||||
batch(() => {
|
||||
setStore("general", "showCustomAgents", initial)
|
||||
setStore("general", "agentVisibilityInitialized", true)
|
||||
})
|
||||
}
|
||||
|
||||
if (sunset && !oldInterfaceRetired()) {
|
||||
const timeout = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
||||
@@ -299,8 +313,9 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
|
||||
|
||||
createEffect(() => {
|
||||
if (!ready() || !launchState.classified || platform.platform !== "web") return
|
||||
if (layoutTransitionClassified()) return
|
||||
setStore("general", "layoutTransitionEligible", hasExistingWebState(settingsInit, launchState.previous))
|
||||
const existing = hasExistingWebState(settingsInit, launchState.previous)
|
||||
if (!layoutTransitionClassified()) setStore("general", "layoutTransitionEligible", existing)
|
||||
initializeAgentVisibility(existing)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
@@ -426,6 +441,7 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
|
||||
if (typeof current === "boolean") return
|
||||
setStore("general", "layoutTransitionEligible", eligible)
|
||||
},
|
||||
initializeAgentVisibility,
|
||||
layoutTransitionAvailable: createMemo(() => ready() && layoutTransition().available),
|
||||
newInterfaceNoticeVisible: createMemo(() => ready() && layoutTransition().notice),
|
||||
dismissNewInterfaceNotice() {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
+11
-16
@@ -4,9 +4,9 @@ import * as Sentry from "@sentry/solid"
|
||||
import { render } from "solid-js/web"
|
||||
import { AppBaseProviders, AppInterface } from "@/app"
|
||||
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 +54,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 +73,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 +117,9 @@ const clearAuthToken = () => {
|
||||
|
||||
const platform: Platform = {
|
||||
platform: "web",
|
||||
draftStore: createBrowserDraftStore(),
|
||||
version: pkg.version,
|
||||
openLink,
|
||||
back,
|
||||
forward,
|
||||
openExternal,
|
||||
restart,
|
||||
notify,
|
||||
getDefaultServer: async () => {
|
||||
|
||||
+89
-82
@@ -59,16 +59,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": "تبديل مساحات العمل",
|
||||
@@ -94,13 +94,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,8 +113,9 @@ 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",
|
||||
@@ -121,8 +123,8 @@ export const dict = {
|
||||
"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":
|
||||
@@ -196,26 +198,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 +229,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 +251,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 +269,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 +277,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 +289,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": "فشل",
|
||||
@@ -408,35 +411,35 @@ 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",
|
||||
"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.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 +467,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": "تم إيقاف قبول الأذونات تلقائيًا",
|
||||
@@ -488,7 +491,7 @@ export const dict = {
|
||||
"toast.session.unshare.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 +511,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 +539,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 +557,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 +567,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 +575,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": "كل الملفات",
|
||||
@@ -591,8 +594,8 @@ export const dict = {
|
||||
"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.summary.one": "{{count}} رسالة في قائمة الانتظار",
|
||||
"session.followupDock.summary.other": "{{count}} رسائل في قائمة الانتظار",
|
||||
"session.followupDock.sendNow": "إرسال الآن",
|
||||
"session.followupDock.edit": "تحرير",
|
||||
"session.followupDock.collapse": "طي الرسائل المنتظرة",
|
||||
@@ -650,10 +653,10 @@ export const dict = {
|
||||
"terminal.connectionLost.title": "فقد الاتصال",
|
||||
"terminal.connectionLost.description": "انقطع اتصال المحطة الطرفية. يمكن أن يحدث هذا عند إعادة تشغيل الخادم.",
|
||||
"common.closeTab": "إغلاق علامة التبويب",
|
||||
"common.dismiss": "رفض",
|
||||
"common.dismiss": "إهمال",
|
||||
"common.requestFailed": "فشل الطلب",
|
||||
"common.moreOptions": "مزيد من الخيارات",
|
||||
"common.learnMore": "اعرف المزيد",
|
||||
"common.learnMore": "اطّلع على المزيد",
|
||||
"common.rename": "إعادة تسمية",
|
||||
"common.reset": "إعادة تعيين",
|
||||
"common.archive": "أرشفة",
|
||||
@@ -671,7 +674,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 +693,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 +730,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 +747,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 +765,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 +827,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 +862,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 +891,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 +899,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 +909,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 +925,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 +935,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 +971,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,7 +985,7 @@ 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}}.",
|
||||
@@ -997,7 +1004,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": "فشل إنشاء ذاكرة التخزين المؤقت الدائمة",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+51
-44
@@ -94,13 +94,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,6 +113,7 @@ 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",
|
||||
@@ -151,12 +153,12 @@ export const dict = {
|
||||
"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 +167,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 +177,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 +217,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 +269,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 +279,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 +290,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 +312,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 +324,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",
|
||||
@@ -411,33 +414,33 @@ 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",
|
||||
"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.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",
|
||||
@@ -496,7 +499,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 +572,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...",
|
||||
@@ -655,7 +658,7 @@ 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.",
|
||||
"common.closeTab": "Fechar aba",
|
||||
@@ -705,7 +708,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 +741,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 +826,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 +882,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 +892,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 +949,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 +965,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",
|
||||
|
||||
+49
-42
@@ -102,6 +102,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 +123,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",
|
||||
|
||||
@@ -232,7 +234,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 +265,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 +273,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 +289,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 +300,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 +313,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 +321,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 +351,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",
|
||||
@@ -444,7 +447,7 @@ export const dict = {
|
||||
|
||||
"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",
|
||||
@@ -464,7 +467,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",
|
||||
@@ -536,14 +539,14 @@ export const dict = {
|
||||
|
||||
"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 +571,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 +613,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 +629,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",
|
||||
@@ -716,7 +719,7 @@ 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.",
|
||||
|
||||
"common.closeTab": "Zatvori karticu",
|
||||
"common.dismiss": "Odbaci",
|
||||
@@ -724,7 +727,7 @@ export const dict = {
|
||||
"common.moreOptions": "Više opcija",
|
||||
"common.learnMore": "Saznaj više",
|
||||
"common.rename": "Preimenuj",
|
||||
"common.reset": "Resetuj",
|
||||
"common.reset": "Vrati na početno stanje",
|
||||
"common.archive": "Arhiviraj",
|
||||
"common.delete": "Izbriši",
|
||||
"common.close": "Zatvori",
|
||||
@@ -781,7 +784,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 +819,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 +944,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 +956,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 +992,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 +1024,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 +1034,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",
|
||||
|
||||
+46
-38
@@ -102,6 +102,7 @@ export const dict = {
|
||||
"dialog.provider.empty": "Ingen udbydere fundet",
|
||||
"dialog.provider.group.popular": "Populære",
|
||||
"dialog.provider.group.other": "Andre",
|
||||
"dialog.provider.custom.label": "Brugerdefineret OpenAI-kompatibel udbyder",
|
||||
"dialog.provider.tag.recommended": "Anbefalet",
|
||||
"dialog.provider.opencode.note": "Udvalgte modeller inklusive Claude, GPT, Gemini og flere",
|
||||
"dialog.provider.opencode.tagline": "Pålidelige optimerede modeller",
|
||||
@@ -122,6 +123,7 @@ export const dict = {
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Gratis modeller leveret af OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Tilføj flere modeller fra populære udbydere",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Se mere end 70 yderligere udbydere",
|
||||
|
||||
"dialog.provider.viewAll": "Vis flere udbydere",
|
||||
|
||||
@@ -133,8 +135,8 @@ export const dict = {
|
||||
"provider.connect.status.waiting": "Venter på godkendelse...",
|
||||
"provider.connect.status.failed": "Godkendelse mislykkedes: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Indtast din {{provider}} API-nøgle for at forbinde din konto og bruge {{provider}} modeller i OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API-nøgle",
|
||||
"Indtast din API-nøgle til {{provider}} for at forbinde din konto og bruge modeller fra {{provider}} i OpenCode.",
|
||||
"provider.connect.apiKey.label": "API-nøgle til {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "API-nøgle",
|
||||
"provider.connect.apiKey.required": "API-nøgle er påkrævet",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
@@ -147,7 +149,7 @@ export const dict = {
|
||||
"provider.connect.oauth.code.visit.prefix": "Besøg ",
|
||||
"provider.connect.oauth.code.visit.link": "dette link",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" for at hente din godkendelseskode for at forbinde din konto og bruge {{provider}} modeller i OpenCode.",
|
||||
" for at hente din godkendelseskode, forbinde din konto og bruge modeller fra {{provider}} i OpenCode.",
|
||||
"provider.connect.oauth.code.label": "{{method}} godkendelseskode",
|
||||
"provider.connect.oauth.code.placeholder": "Godkendelseskode",
|
||||
"provider.connect.oauth.code.required": "Godkendelseskode er påkrævet",
|
||||
@@ -155,10 +157,10 @@ export const dict = {
|
||||
"provider.connect.oauth.auto.visit.prefix": "Besøg ",
|
||||
"provider.connect.oauth.auto.visit.link": "dette link",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" og indtast koden nedenfor for at forbinde din konto og bruge {{provider}} modeller i OpenCode.",
|
||||
" og indtast koden nedenfor for at forbinde din konto og bruge modeller fra {{provider}} i OpenCode.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Bekræftelseskode",
|
||||
"provider.connect.toast.connected.title": "{{provider}} forbundet",
|
||||
"provider.connect.toast.connected.description": "{{provider}} modeller er nu tilgængelige.",
|
||||
"provider.connect.toast.connected.description": "Modeller fra {{provider}} er nu tilgængelige.",
|
||||
|
||||
"provider.custom.title": "Brugerdefineret udbyder",
|
||||
"provider.custom.description.prefix": "Konfigurer en OpenAI-kompatibel udbyder. Se ",
|
||||
@@ -217,9 +219,9 @@ export const dict = {
|
||||
"model.tooltip.inputs": "Input",
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Udbyder",
|
||||
"model.tooltip.reasoning": "Tænkning",
|
||||
"model.tooltip.reasoning.allowed": "Tillader tænkning",
|
||||
"model.tooltip.reasoning.none": "Ingen tænkning",
|
||||
"model.tooltip.reasoning": "Ræsonnering",
|
||||
"model.tooltip.reasoning.allowed": "Tillader ræsonnering",
|
||||
"model.tooltip.reasoning.none": "Ingen ræsonnering",
|
||||
"model.tooltip.context": "Kontekstgrænse {{limit}}",
|
||||
"common.search.placeholder": "Søg",
|
||||
"common.goBack": "Gå tilbage",
|
||||
@@ -230,7 +232,7 @@ export const dict = {
|
||||
"common.clear": "Ryd",
|
||||
"common.connect": "Forbind",
|
||||
"common.disconnect": "Frakobl",
|
||||
"common.continue": "Indsend",
|
||||
"common.continue": "Fortsæt",
|
||||
"common.submit": "Indsend",
|
||||
"common.save": "Gem",
|
||||
"common.saving": "Gemmer...",
|
||||
@@ -253,7 +255,7 @@ export const dict = {
|
||||
"prompt.example.3": "Ret ødelagte tests",
|
||||
"prompt.example.4": "Forklar hvordan godkendelse fungerer",
|
||||
"prompt.example.5": "Find og ret sikkerhedshuller",
|
||||
"prompt.example.6": "Tilføj enhedstests for brugerservice",
|
||||
"prompt.example.6": "Tilføj enhedstests for brugertjenesten",
|
||||
"prompt.example.7": "Refaktorer denne funktion så den er mere læsbar",
|
||||
"prompt.example.8": "Hvad betyder denne fejl?",
|
||||
"prompt.example.9": "Hjælp mig med at debugge dette problem",
|
||||
@@ -296,6 +298,7 @@ export const dict = {
|
||||
"prompt.action.stop": "Stop",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Ikke understøttet vedhæftning",
|
||||
"prompt.toast.attachmentDuplicate.title": "Denne fil er allerede uploadet",
|
||||
"prompt.toast.pasteUnsupported.description": "Kun billeder, PDF'er eller tekstfiler kan vedhæftes her.",
|
||||
"prompt.toast.modelAgentRequired.title": "Vælg en agent og model",
|
||||
"prompt.toast.modelAgentRequired.description": "Vælg en agent og model før du sender en forespørgsel.",
|
||||
@@ -334,7 +337,7 @@ export const dict = {
|
||||
"dialog.server.search.placeholder": "Søg servere",
|
||||
"dialog.server.empty": "Ingen servere endnu",
|
||||
"dialog.server.add.title": "Tilføj en server",
|
||||
"dialog.server.add.url": "Server URL",
|
||||
"dialog.server.add.url": "Serveradresse",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Kunne ikke forbinde til server",
|
||||
"dialog.server.add.checking": "Tjekker...",
|
||||
@@ -346,7 +349,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Rediger server",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description":
|
||||
"Forbind til denne server ved start af app i stedet for at starte en lokal server. Kræver genstart.",
|
||||
"Opret forbindelse til denne server, når appen starter, i stedet for at starte en lokal server. Kræver genstart.",
|
||||
"dialog.server.default.none": "Ingen server valgt",
|
||||
"dialog.server.default.set": "Sæt nuværende server som standard",
|
||||
"dialog.server.default.clear": "Ryd",
|
||||
@@ -461,8 +464,8 @@ export const dict = {
|
||||
"context.stats.usage": "Forbrug",
|
||||
"context.stats.inputTokens": "Input-tokens",
|
||||
"context.stats.outputTokens": "Output-tokens",
|
||||
"context.stats.reasoningTokens": "Tænke Tokens",
|
||||
"context.stats.cacheTokens": "Cache Tokens (læs/skriv)",
|
||||
"context.stats.reasoningTokens": "Ræsonneringstokens",
|
||||
"context.stats.cacheTokens": "Cachetokens (læsning/skrivning)",
|
||||
"context.stats.userMessages": "Brugerbeskeder",
|
||||
"context.stats.assistantMessages": "Assistentbeskeder",
|
||||
"context.stats.totalCost": "Samlede omkostninger",
|
||||
@@ -565,7 +568,7 @@ export const dict = {
|
||||
"error.chain.responseBody": "Svarindhold:\n{{body}}",
|
||||
"error.chain.didYouMean": "Mente du: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model ikke fundet: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Tjek dine konfigurations (opencode.json) udbyder/modelnavne",
|
||||
"error.chain.checkConfig": "Kontrollér udbyder- og modelnavnene i din konfiguration (opencode.json)",
|
||||
"error.chain.mcpFailed": 'MCP-server "{{name}}" fejlede. Bemærk, OpenCode understøtter ikke MCP-godkendelse endnu.',
|
||||
"error.chain.providerAuthFailed": "Udbydergodkendelse mislykkedes ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
@@ -618,12 +621,12 @@ export const dict = {
|
||||
"session.error.notFound.description": "Denne fane henviser til en session, der ikke længere findes på denne server.",
|
||||
"session.error.notFound.closeTab": "Luk fane",
|
||||
"session.error.serverConnection": "Kan ikke oprette forbindelse til denne server",
|
||||
"session.review.filesChanged": "{{count}} Filer ændret",
|
||||
"session.review.filesChanged": "{{count}} ændrede filer",
|
||||
"session.review.change.one": "Ændring",
|
||||
"session.review.change.other": "Ændringer",
|
||||
"session.review.loadingChanges": "Indlæser ændringer...",
|
||||
"session.review.empty": "Ingen ændringer i denne session endnu",
|
||||
"session.review.noVcs": "Intet Git versionsstyringssystem fundet, ændringer vises ikke",
|
||||
"session.review.noVcs": "Intet Git-versionsstyringssystem fundet, så ændringer vises ikke",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot-sporing er deaktiveret i konfigurationen, så sessionsændringer er ikke tilgængelige",
|
||||
"session.review.noChanges": "Ingen ændringer",
|
||||
@@ -749,7 +752,7 @@ export const dict = {
|
||||
"settings.tab.general": "Generelt",
|
||||
"settings.tab.shortcuts": "Genveje",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integration",
|
||||
"settings.desktop.wsl.title": "WSL-integration",
|
||||
"settings.desktop.wsl.description": "Kør OpenCode-serveren inde i WSL på Windows.",
|
||||
|
||||
"settings.general.section.appearance": "Udseende",
|
||||
@@ -770,15 +773,16 @@ export const dict = {
|
||||
"settings.general.row.appearance.title": "Udseende",
|
||||
"settings.general.row.appearance.description": "Tilpas hvordan OpenCode ser ud på din enhed",
|
||||
"settings.general.row.colorScheme.title": "Farveskema",
|
||||
"settings.general.row.colorScheme.description": "Vælg om OpenCode følger systemets, lyst eller mørkt tema",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"Vælg, om OpenCode skal følge systemtemaet eller bruge et lyst eller mørkt tema",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.",
|
||||
"settings.general.row.theme.description": "Tilpas OpenCodes tema.",
|
||||
"settings.general.row.font.title": "Kode-skrifttype",
|
||||
"settings.general.row.font.description": "Tilpas skrifttypen, der bruges i kodeblokke",
|
||||
"settings.general.row.terminalFont.title": "Terminalskrifttype",
|
||||
"settings.general.row.terminalFont.description": "Tilpas den skrifttype, der bruges i terminalen",
|
||||
"settings.general.row.uiFont.title": "UI-skrifttype",
|
||||
"settings.general.row.uiFont.description": "Tilpas skrifttypen, der bruges i hele brugerfladen",
|
||||
"settings.general.row.uiFont.description": "Tilpas skrifttypen, der bruges i hele brugergrænsefladen",
|
||||
"settings.general.row.followup.title": "Opfølgningsadfærd",
|
||||
"settings.general.row.followup.description": "Vælg om opfølgende forespørgsler skal styre straks eller vente i kø",
|
||||
"settings.general.row.followup.option.queue": "Kø",
|
||||
@@ -796,10 +800,11 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation nederst",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Placer titellinjen og sessionsfanerne nederst på skærmen på mobilenheder",
|
||||
"settings.general.row.showCustomAgents.title": "Brugerdefinerede agenter",
|
||||
"settings.general.row.showCustomAgents.description": "Vis agentvælgeren i promptfeltet",
|
||||
"settings.general.row.reasoningSummaries.title": "Vis tænkeoversigter",
|
||||
"settings.general.row.reasoningSummaries.description": "Vis model tænkeoversigter i tidslinjen",
|
||||
"settings.general.row.showCustomAgents.title": "Vis agent",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Skift mellem agenter i promptfeltet. Når vælgeren er skjult, bruges Build-agenten som standard.",
|
||||
"settings.general.row.reasoningSummaries.title": "Vis ræsonneringsoversigter",
|
||||
"settings.general.row.reasoningSummaries.description": "Vis oversigter over modellens ræsonnering på tidslinjen",
|
||||
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Udvid shell-værktøjsdele",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "Vis shell-værktøjsdele udvidet som standard i tidslinjen",
|
||||
@@ -815,13 +820,13 @@ export const dict = {
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Afvis",
|
||||
"settings.general.row.pinchZoom.title": "Knib for at zoome",
|
||||
"settings.general.row.pinchZoom.description": "Tillad knibebevægelser på pegefeltet og Ctrl-rulning for at zoome",
|
||||
"settings.general.row.wayland.title": "Brug native Wayland",
|
||||
"settings.general.row.wayland.title": "Brug indbygget Wayland",
|
||||
"settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"På Linux med skærme med blandet opdateringshastighed kan native Wayland være mere stabilt.",
|
||||
"På Linux med skærme med forskellige opdateringshastigheder kan indbygget Wayland være mere stabilt.",
|
||||
|
||||
"settings.general.row.releaseNotes.title": "Udgivelsesnoter",
|
||||
"settings.general.row.releaseNotes.description": 'Vis "Hvad er nyt"-popups efter opdateringer',
|
||||
"settings.general.row.releaseNotes.description": 'Vis pop op-vinduer med "Hvad er nyt" efter opdateringer',
|
||||
|
||||
"settings.updates.row.startup.title": "Tjek for opdateringer ved opstart",
|
||||
"settings.updates.row.startup.description": "Tjek automatisk for opdateringer, når OpenCode starter",
|
||||
@@ -944,9 +949,9 @@ export const dict = {
|
||||
"settings.permissions.tool.read.description": "Læsning af en fil (matcher filstien)",
|
||||
"settings.permissions.tool.edit.title": "Rediger",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Ændre filer, herunder redigeringer, skrivninger, patches og multi-redigeringer",
|
||||
"Rediger filer, herunder med redigeringer, skrivning, patches og multiredigeringer",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Match filer ved hjælp af glob-mønstre",
|
||||
"settings.permissions.tool.glob.description": "Find filer ved hjælp af globmønstre",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Søg i filindhold ved hjælp af regulære udtryk",
|
||||
"settings.permissions.tool.list.title": "Liste",
|
||||
@@ -959,8 +964,8 @@ export const dict = {
|
||||
"settings.permissions.tool.skill.description": "Indlæs en færdighed efter navn",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Kør sprogserverforespørgsler",
|
||||
"settings.permissions.tool.todowrite.title": "Skriv To-do",
|
||||
"settings.permissions.tool.todowrite.description": "Opdater to-do listen",
|
||||
"settings.permissions.tool.todowrite.title": "Skriv to-do",
|
||||
"settings.permissions.tool.todowrite.description": "Opdater to-do-listen",
|
||||
"settings.permissions.tool.webfetch.title": "Webhentning",
|
||||
"settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
|
||||
"settings.permissions.tool.websearch.title": "Websøgning",
|
||||
@@ -987,7 +992,7 @@ export const dict = {
|
||||
"workspace.reset.success.description": "Arbejdsområdet matcher nu hovedgrenen.",
|
||||
"workspace.error.stillPreparing": "Arbejdsområdet er stadig ved at blive klargjort",
|
||||
"workspace.status.checking": "Tjekker for uflettede ændringer...",
|
||||
"workspace.status.error": "Kunne ikke bekræfte git-status.",
|
||||
"workspace.status.error": "Kunne ikke bekræfte Git-status.",
|
||||
"workspace.status.clean": "Ingen uflettede ændringer fundet.",
|
||||
"workspace.status.dirty": "Uflettede ændringer fundet i dette arbejdsområde.",
|
||||
"workspace.delete.title": "Slet arbejdsområde",
|
||||
@@ -1029,6 +1034,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} af {{total}} spørgsmål",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Stifinder",
|
||||
"session.header.reveal.finder": "Vis i Finder",
|
||||
"session.header.reveal.fileExplorer": "Vis i Stifinder",
|
||||
"session.header.reveal.containingFolder": "Åbn den overordnede mappe",
|
||||
"session.header.open.fileManager": "Filhåndtering",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -1049,11 +1057,11 @@ export const dict = {
|
||||
"debugBar.nav.tip":
|
||||
"Sidste gennemførte ruteovergang, der berører en sessionsside, målt fra routerstart til den første optegning efter den falder til ro.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Rullende billeder pr. sekund over de sidste 5 sekunder.",
|
||||
"debugBar.fps.tip": "Rullende antal billedrammer pr. sekund over de sidste 5 sekunder.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Værste billedtid over de sidste 5 sekunder.",
|
||||
"debugBar.frame.tip": "Længste billedrammetid over de sidste 5 sekunder.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Billeder over 32ms i de sidste 5 sekunder.",
|
||||
"debugBar.jank.tip": "Billedrammer over 32 ms i de sidste 5 sekunder.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Blokeret tid og antal lange opgaver i de sidste 5 sekunder. Maks opgave: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
@@ -1064,8 +1072,8 @@ export const dict = {
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Kumulativt layoutskift for den nuværende app-levetid.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Brugt JS-heap vs heap-grænse. Kun Chromium.",
|
||||
"debugBar.mem.tip": "Brugt JS-heap vs heap-grænse. {{used}} af {{limit}}.",
|
||||
"debugBar.mem.tipUnavailable": "Brugt JS-heap sammenholdt med heapgrænsen. Kun Chromium.",
|
||||
"debugBar.mem.tip": "Brugt JS-heap sammenholdt med heapgrænsen. {{used}} af {{limit}}.",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
|
||||
+102
-94
@@ -98,13 +98,14 @@ export const dict = {
|
||||
"dialog.provider.empty": "Keine Anbieter gefunden",
|
||||
"dialog.provider.group.popular": "Beliebt",
|
||||
"dialog.provider.group.other": "Andere",
|
||||
"dialog.provider.custom.label": "Benutzerdefinierter OpenAI-kompatibler Anbieter",
|
||||
"dialog.provider.tag.recommended": "Empfohlen",
|
||||
"dialog.provider.opencode.note": "Kuratierte Modelle inklusive Claude, GPT, Gemini und mehr",
|
||||
"dialog.provider.opencode.tagline": "Zuverlässige, optimierte Modelle",
|
||||
"dialog.provider.opencodeGo.tagline": "Kostengünstiges Abo für alle",
|
||||
"dialog.provider.anthropic.note": "Mit Claude Pro/Max oder API-Schlüssel verbinden",
|
||||
"dialog.provider.copilot.note": "Mit Copilot oder API-Schlüssel verbinden",
|
||||
"dialog.provider.openai.note": "Mit ChatGPT Pro/Plus oder API-Schlüssel verbinden",
|
||||
"dialog.provider.anthropic.note": "Direkter Zugriff auf Claude-Modelle, einschließlich Pro und Max",
|
||||
"dialog.provider.copilot.note": "KI-Modelle zur Programmierunterstützung über GitHub Copilot",
|
||||
"dialog.provider.openai.note": "GPT-Modelle für schnelle, leistungsfähige allgemeine KI-Aufgaben",
|
||||
"dialog.provider.google.note": "Gemini-Modelle für schnelle, strukturierte Antworten",
|
||||
"dialog.provider.openrouter.note": "Zugriff auf alle unterstützten Modelle über einen Anbieter",
|
||||
"dialog.provider.vercel.note": "Einheitlicher Zugriff auf KI-Modelle mit intelligentem Routing",
|
||||
@@ -116,17 +117,18 @@ export const dict = {
|
||||
"dialog.model.manage.provider.toggle": "Alle {{provider}}-Modelle umschalten",
|
||||
"dialog.model.unpaid.freeModels.title": "Kostenlose Modelle von OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Weitere Modelle von beliebten Anbietern hinzufügen",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Über 70 weitere Anbieter anzeigen",
|
||||
"dialog.provider.viewAll": "Mehr Anbieter anzeigen",
|
||||
"provider.connect.title": "{{provider}} verbinden",
|
||||
"provider.connect.title.anthropicProMax": "Mit Claude Pro/Max anmelden",
|
||||
"provider.connect.selectMethod": "Anmeldemethode für {{provider}} auswählen.",
|
||||
"provider.connect.method.apiKey": "API-Schlüssel",
|
||||
"provider.connect.status.inProgress": "Autorisierung läuft...",
|
||||
"provider.connect.status.waiting": "Warten auf Autorisierung...",
|
||||
"provider.connect.status.inProgress": "Autorisierung läuft…",
|
||||
"provider.connect.status.waiting": "Warten auf Autorisierung…",
|
||||
"provider.connect.status.failed": "Autorisierung fehlgeschlagen: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API-Schlüssel",
|
||||
"Geben Sie Ihren API-Schlüssel für {{provider}} ein, um Ihr Konto zu verbinden und {{provider}}-Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.apiKey.label": "API-Schlüssel für {{provider}}",
|
||||
"provider.connect.apiKey.placeholder": "API-Schlüssel",
|
||||
"provider.connect.apiKey.required": "API-Schlüssel ist erforderlich",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
@@ -139,21 +141,21 @@ export const dict = {
|
||||
"provider.connect.oauth.code.visit.prefix": "Besuchen Sie ",
|
||||
"provider.connect.oauth.code.visit.link": "diesen Link",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.code.label": "{{method}} Autorisierungscode",
|
||||
", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}}-Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.code.label": "{{method}}-Autorisierungscode",
|
||||
"provider.connect.oauth.code.placeholder": "Autorisierungscode",
|
||||
"provider.connect.oauth.code.required": "Autorisierungscode ist erforderlich",
|
||||
"provider.connect.oauth.code.invalid": "Ungültiger Autorisierungscode",
|
||||
"provider.connect.oauth.auto.visit.prefix": "Besuchen Sie ",
|
||||
"provider.connect.oauth.auto.visit.link": "diesen Link",
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
|
||||
" und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}}-Modelle in OpenCode zu nutzen.",
|
||||
"provider.connect.oauth.auto.confirmationCode": "Bestätigungscode",
|
||||
"provider.connect.toast.connected.title": "{{provider}} verbunden",
|
||||
"provider.connect.toast.connected.description": "{{provider}} Modelle sind jetzt verfügbar.",
|
||||
"provider.connect.toast.connected.description": "{{provider}}-Modelle sind jetzt verfügbar.",
|
||||
"provider.custom.title": "Benutzerdefinierter Anbieter",
|
||||
"provider.custom.description.prefix": "Konfigurieren Sie einen OpenAI-kompatiblen Anbieter. Siehe die ",
|
||||
"provider.custom.description.link": "Anbieter-Konfigurationsdokumente",
|
||||
"provider.custom.description.link": "Dokumentation zur Anbieterkonfiguration",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "Anbieter-ID",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
@@ -208,32 +210,32 @@ export const dict = {
|
||||
"model.tooltip.model": "Modell",
|
||||
"model.tooltip.provider": "Anbieter",
|
||||
"model.tooltip.reasoning": "Reasoning",
|
||||
"model.tooltip.reasoning.allowed": "Erlaubt Reasoning",
|
||||
"model.tooltip.reasoning.allowed": "Reasoning möglich",
|
||||
"model.tooltip.reasoning.none": "Kein Reasoning",
|
||||
"model.tooltip.context": "Kontextlimit {{limit}}",
|
||||
"common.search.placeholder": "Suchen",
|
||||
"common.goBack": "Zurück",
|
||||
"common.goForward": "Vorwärts navigieren",
|
||||
"common.loading": "Laden",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.loading": "Wird geladen",
|
||||
"common.loading.ellipsis": "…",
|
||||
"common.cancel": "Abbrechen",
|
||||
"common.clear": "Leeren",
|
||||
"common.connect": "Verbinden",
|
||||
"common.disconnect": "Trennen",
|
||||
"common.continue": "Absenden",
|
||||
"common.continue": "Weiter",
|
||||
"common.submit": "Absenden",
|
||||
"common.save": "Speichern",
|
||||
"common.saving": "Speichert...",
|
||||
"common.saving": "Wird gespeichert…",
|
||||
"common.default": "Standard",
|
||||
"common.attachment": "Anhang",
|
||||
"prompt.placeholder.shell": "Shell-Befehl eingeben... {{example}}",
|
||||
"prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Fragen Sie alles...",
|
||||
"prompt.placeholder.shell": "Shell-Befehl eingeben… {{example}}",
|
||||
"prompt.placeholder.normal": 'Beliebige Frage stellen… "{{example}}"',
|
||||
"prompt.placeholder.simple": "Beliebige Frage stellen…",
|
||||
"prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…",
|
||||
"prompt.placeholder.summarizeComment": "Kommentar zusammenfassen…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc zum Verlassen",
|
||||
"prompt.mode.shell.exit": "Esc zum Verlassen",
|
||||
"session.child.promptDisabled": "Unteragenten-Sitzungen können keine Prompts erhalten.",
|
||||
"session.child.backToParent": "Zurück zur Hauptsitzung.",
|
||||
"prompt.example.1": "Ein TODO in der Codebasis beheben",
|
||||
@@ -248,8 +250,8 @@ export const dict = {
|
||||
"prompt.example.10": "API-Dokumentation generieren",
|
||||
"prompt.example.11": "Datenbankabfragen optimieren",
|
||||
"prompt.example.12": "Eingabevalidierung hinzufügen",
|
||||
"prompt.example.13": "Neue Komponente erstellen für...",
|
||||
"prompt.example.14": "Wie deploye ich dieses Projekt?",
|
||||
"prompt.example.13": "Neue Komponente erstellen für…",
|
||||
"prompt.example.14": "Wie stelle ich dieses Projekt bereit?",
|
||||
"prompt.example.15": "Meinen Code auf Best Practices überprüfen",
|
||||
"prompt.example.16": "Fehlerbehandlung zu dieser Funktion hinzufügen",
|
||||
"prompt.example.17": "Erkläre dieses Regex-Muster",
|
||||
@@ -259,12 +261,12 @@ export const dict = {
|
||||
"prompt.example.21": "Hilf mir, ein Migrationsskript zu schreiben",
|
||||
"prompt.example.22": "Caching für diesen Endpunkt implementieren",
|
||||
"prompt.example.23": "Paginierung zu dieser Liste hinzufügen",
|
||||
"prompt.example.24": "CLI-Befehl erstellen für...",
|
||||
"prompt.example.24": "CLI-Befehl erstellen für…",
|
||||
"prompt.example.25": "Wie funktionieren Umgebungsvariablen hier?",
|
||||
"prompt.popover.emptyResults": "Keine passenden Ergebnisse",
|
||||
"prompt.popover.emptyCommands": "Keine passenden Befehle",
|
||||
"prompt.dropzone.label": "Bilder, PDFs oder Textdateien hier ablegen",
|
||||
"prompt.dropzone.file.label": "Ablegen zum @Erwähnen der Datei",
|
||||
"prompt.dropzone.file.label": "Ablegen, um die Datei mit @ zu erwähnen",
|
||||
"prompt.slash.badge.custom": "benutzerdefiniert",
|
||||
"prompt.slash.badge.skill": "Skill",
|
||||
"prompt.slash.badge.mcp": "MCP",
|
||||
@@ -280,13 +282,14 @@ export const dict = {
|
||||
"prompt.menu.shellCommand": "Shell-Befehl",
|
||||
"prompt.attachment.remove": "Anhang entfernen",
|
||||
"prompt.action.send": "Senden",
|
||||
"prompt.action.stop": "Stopp",
|
||||
"prompt.action.stop": "Stoppen",
|
||||
"prompt.toast.pasteUnsupported.title": "Nicht unterstützter Anhang",
|
||||
"prompt.toast.attachmentDuplicate.title": "Diese Datei wurde bereits hochgeladen",
|
||||
"prompt.toast.pasteUnsupported.description": "Hier können nur Bilder, PDFs oder Textdateien angehängt werden.",
|
||||
"prompt.toast.modelAgentRequired.title": "Wählen Sie einen Agenten und ein Modell",
|
||||
"prompt.toast.modelAgentRequired.description":
|
||||
"Wählen Sie einen Agenten und ein Modell, bevor Sie eine Eingabe senden.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Worktree konnte nicht erstellt werden",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Der Worktree konnte nicht erstellt werden",
|
||||
"prompt.toast.sessionCreateFailed.title": "Sitzung konnte nicht erstellt werden",
|
||||
"prompt.toast.shellSendFailed.title": "Shell-Befehl konnte nicht gesendet werden",
|
||||
"prompt.toast.commandSendFailed.title": "Befehl konnte nicht gesendet werden",
|
||||
@@ -299,7 +302,7 @@ export const dict = {
|
||||
"dialog.plugins.empty": "In opencode.json konfigurierte Plugins",
|
||||
"mcp.status.connected": "verbunden",
|
||||
"mcp.status.failed": "fehlgeschlagen",
|
||||
"mcp.status.needs_auth": "benötigt Authentifizierung",
|
||||
"mcp.status.needs_auth": "Authentifizierung erforderlich",
|
||||
"mcp.auth.clickToAuthenticate": "Zum Authentifizieren klicken",
|
||||
"mcp.status.disabled": "deaktiviert",
|
||||
"dialog.fork.empty": "Keine Nachrichten zum Abzweigen vorhanden",
|
||||
@@ -315,10 +318,10 @@ export const dict = {
|
||||
"dialog.server.search.placeholder": "Server durchsuchen",
|
||||
"dialog.server.empty": "Noch keine Server",
|
||||
"dialog.server.add.title": "Server hinzufügen",
|
||||
"dialog.server.add.url": "Server-URL",
|
||||
"dialog.server.add.url": "Serveradresse",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Verbindung zum Server fehlgeschlagen",
|
||||
"dialog.server.add.checking": "Prüfen...",
|
||||
"dialog.server.add.checking": "Prüfen…",
|
||||
"dialog.server.add.button": "Server hinzufügen",
|
||||
"dialog.server.add.name": "Servername (optional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
@@ -327,7 +330,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Server bearbeiten",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description":
|
||||
"Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Erfordert Neustart.",
|
||||
"Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Ein Neustart ist erforderlich.",
|
||||
"dialog.server.default.none": "Kein Server ausgewählt",
|
||||
"dialog.server.default.set": "Aktuellen Server als Standard setzen",
|
||||
"dialog.server.default.clear": "Löschen",
|
||||
@@ -343,26 +346,26 @@ export const dict = {
|
||||
"wsl.server.label": "WSL",
|
||||
"wsl.server.menu.label": "WSL-Server",
|
||||
"wsl.server.retryStart": "Start erneut versuchen",
|
||||
"wsl.server.updating": "Wird aktualisiert...",
|
||||
"wsl.server.updating": "Wird aktualisiert…",
|
||||
"wsl.onboarding.step.distro": "Distribution auswählen",
|
||||
"wsl.onboarding.step.opencode": "OpenCode",
|
||||
"wsl.onboarding.checkingRuntime": "WSL wird geprüft...",
|
||||
"wsl.onboarding.checkingRuntime": "WSL wird geprüft…",
|
||||
"wsl.onboarding.restartRequired": "Windows muss neu gestartet werden, um die WSL-Installation abzuschließen.",
|
||||
"wsl.onboarding.ready": "WSL ist bereit.",
|
||||
"wsl.onboarding.required": "WSL ist erforderlich, um fortzufahren.",
|
||||
"wsl.onboarding.checkingDistros": "Distributionen werden geprüft...",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} wird installiert...",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} wird geprüft...",
|
||||
"wsl.onboarding.listingDistros": "Distributionen werden aufgelistet...",
|
||||
"wsl.onboarding.checkingDistros": "Distributionen werden geprüft…",
|
||||
"wsl.onboarding.installingDistro": "{{distro}} wird installiert…",
|
||||
"wsl.onboarding.checkingDistro": "{{distro}} wird geprüft…",
|
||||
"wsl.onboarding.listingDistros": "Distributionen werden aufgelistet…",
|
||||
"wsl.onboarding.distroReady": "{{distro}} ist bereit.",
|
||||
"wsl.onboarding.distroNotInstalled": "{{distro}} ist noch nicht installiert.",
|
||||
"wsl.onboarding.openDistroOnce": "Öffnen Sie {{distro}} einmal, um die Einrichtung abzuschließen.",
|
||||
"wsl.onboarding.finishingDistro": "Einrichtung von {{distro}} wird abgeschlossen.",
|
||||
"wsl.onboarding.pickDistro": "Wählen Sie eine Distribution aus oder installieren Sie unten eine.",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode wird geprüft...",
|
||||
"wsl.onboarding.checkingOpencodeIn": "OpenCode in {{distro}} wird geprüft...",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode wird aktualisiert...",
|
||||
"wsl.onboarding.updatingOpencodeIn": "OpenCode in {{distro}} wird aktualisiert...",
|
||||
"wsl.onboarding.checkingOpencode": "OpenCode wird geprüft…",
|
||||
"wsl.onboarding.checkingOpencodeIn": "OpenCode in {{distro}} wird geprüft…",
|
||||
"wsl.onboarding.updatingOpencode": "OpenCode wird aktualisiert…",
|
||||
"wsl.onboarding.updatingOpencodeIn": "OpenCode in {{distro}} wird aktualisiert…",
|
||||
"wsl.onboarding.updateOpencodeIn": "Aktualisieren Sie OpenCode in {{distro}}.",
|
||||
"wsl.onboarding.updateOpencode": "OpenCode aktualisieren",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode ist in {{distro}} bereit.",
|
||||
@@ -371,11 +374,11 @@ export const dict = {
|
||||
"wsl.onboarding.installOpencode": "OpenCode installieren",
|
||||
"wsl.onboarding.chooseDistroFirst": "Wählen Sie zuerst eine Distribution aus.",
|
||||
"wsl.onboarding.loadFailed": "WSL-Status konnte nicht geladen werden.",
|
||||
"wsl.onboarding.loading": "Wird geladen...",
|
||||
"wsl.onboarding.loading": "Wird geladen…",
|
||||
"wsl.onboarding.installedDistros": "Installierte Distributionen",
|
||||
"wsl.onboarding.checkAgain": "Erneut prüfen",
|
||||
"wsl.onboarding.distroStatus.ready": "Bereit",
|
||||
"wsl.onboarding.distroStatus.checking": "Wird geprüft...",
|
||||
"wsl.onboarding.distroStatus.checking": "Wird geprüft…",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode nicht installiert",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Fehlend: bash, curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nicht unterstützt · WSL 2 verwenden",
|
||||
@@ -394,7 +397,7 @@ export const dict = {
|
||||
"wsl.onboarding.allDistrosAdded": "Alle installierten Distributionen wurden bereits hinzugefügt.",
|
||||
"wsl.onboarding.noDistros": "Noch keine Distributionen erkannt.",
|
||||
"wsl.onboarding.install": "Installieren",
|
||||
"wsl.onboarding.installing": "Wird installiert...",
|
||||
"wsl.onboarding.installing": "Wird installiert…",
|
||||
"wsl.onboarding.installDistro": "Distribution installieren",
|
||||
"wsl.onboarding.searchDistros": "Distributionen durchsuchen",
|
||||
"wsl.onboarding.wsl2Required": "WSL 2 ist erforderlich.",
|
||||
@@ -406,13 +409,13 @@ export const dict = {
|
||||
"wsl.onboarding.unknown": "unbekannt",
|
||||
"wsl.onboarding.desktopVersion": "Desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "Die installierte Version entspricht nicht der Version der Desktop-App.",
|
||||
"wsl.onboarding.adding": "Wird hinzugefügt...",
|
||||
"wsl.onboarding.adding": "Wird hinzugefügt…",
|
||||
"dialog.project.edit.title": "Projekt bearbeiten",
|
||||
"dialog.project.edit.name": "Name",
|
||||
"dialog.project.edit.icon": "Icon",
|
||||
"dialog.project.edit.icon.alt": "Projekt-Icon",
|
||||
"dialog.project.edit.icon": "Symbol",
|
||||
"dialog.project.edit.icon.alt": "Projektsymbol",
|
||||
"dialog.project.edit.icon.hint": "Klicken oder Bild ziehen",
|
||||
"dialog.project.edit.icon.recommended": "Empfohlen: 128x128px",
|
||||
"dialog.project.edit.icon.recommended": "Empfohlen: 128 × 128 px",
|
||||
"dialog.project.edit.color": "Farbe",
|
||||
"dialog.project.edit.color.select": "{{color}}-Farbe auswählen",
|
||||
"dialog.project.edit.worktree.startup": "Startup-Skript für Arbeitsbereich",
|
||||
@@ -488,7 +491,7 @@ export const dict = {
|
||||
"toast.context.noLineSelection.description": "Wählen Sie zuerst einen Zeilenbereich in einem Datei-Tab aus.",
|
||||
"toast.session.share.copyFailed.title": "URL konnte nicht in die Zwischenablage kopiert werden",
|
||||
"toast.session.share.success.title": "Sitzung geteilt",
|
||||
"toast.session.share.success.description": "Teilen-URL in die Zwischenablage kopiert!",
|
||||
"toast.session.share.success.description": "URL zum Teilen in die Zwischenablage kopiert!",
|
||||
"toast.session.share.failed.title": "Sitzung konnte nicht geteilt werden",
|
||||
"toast.session.share.failed.description": "Beim Teilen der Sitzung ist ein Fehler aufgetreten",
|
||||
"toast.session.unshare.success.title": "Teilen der Sitzung aufgehoben",
|
||||
@@ -508,14 +511,14 @@ export const dict = {
|
||||
"error.page.action.report": "Fehler melden",
|
||||
"error.page.action.reported": "Fehler gemeldet",
|
||||
"error.page.action.exportLogs": "Protokolle exportieren",
|
||||
"error.page.action.checking": "Prüfen...",
|
||||
"error.page.action.checking": "Prüfen…",
|
||||
"error.page.action.checkUpdates": "Nach Updates suchen",
|
||||
"error.page.action.updateTo": "Auf {{version}} aktualisieren",
|
||||
"error.page.report.prefix": "Bitte melden Sie diesen Fehler dem OpenCode-Team",
|
||||
"error.page.report.discord": "auf Discord",
|
||||
"error.page.version": "Version: {{version}}",
|
||||
"error.dev.rootNotFound":
|
||||
"Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das id-Attribut falsch geschrieben?",
|
||||
"Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das ID-Attribut falsch geschrieben?",
|
||||
"error.serverSync.connectFailed": "Verbindung zum Server fehlgeschlagen. Läuft ein Server unter `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Ungültiges Verzeichnis in der URL.",
|
||||
"error.chain.unknown": "Unbekannter Fehler",
|
||||
@@ -523,10 +526,10 @@ export const dict = {
|
||||
"error.chain.apiError": "API-Fehler",
|
||||
"error.chain.status": "Status: {{status}}",
|
||||
"error.chain.retryable": "Wiederholbar: {{retryable}}",
|
||||
"error.chain.responseBody": "Antwort-Body:\n{{body}}",
|
||||
"error.chain.responseBody": "Antwortinhalt:\n{{body}}",
|
||||
"error.chain.didYouMean": "Meinten Sie: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Modell nicht gefunden: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Überprüfen Sie Ihre Konfiguration (opencode.json) auf Anbieter-/Modellnamen",
|
||||
"error.chain.checkConfig": "Überprüfen Sie die Anbieter- und Modellnamen in Ihrer Konfiguration (opencode.json)",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP-Server "{{name}}" fehlgeschlagen. Hinweis: OpenCode unterstützt noch keine MCP-Authentifizierung.',
|
||||
"error.chain.providerAuthFailed": "Anbieter-Authentifizierung fehlgeschlagen ({{provider}}): {{message}}",
|
||||
@@ -567,7 +570,7 @@ export const dict = {
|
||||
"home.sessions.group.yesterday": "Gestern",
|
||||
"home.sessions.group.older": "Älter",
|
||||
"home.providerTip":
|
||||
"Verbinde dich mit über 75 Anbietern, um weitere Modelle wie Claude, GPT, Gemini und andere zu nutzen",
|
||||
"OpenCode mit über 75 Anbietern verbinden, um weitere Modelle wie Claude, GPT, Gemini und andere zu nutzen",
|
||||
"session.tab.session": "Sitzung",
|
||||
"session.tab.review": "Überprüfung",
|
||||
"session.tab.context": "Kontext",
|
||||
@@ -578,12 +581,12 @@ export const dict = {
|
||||
"Dieser Tab verweist auf eine Sitzung, die auf diesem Server nicht mehr existiert.",
|
||||
"session.error.notFound.closeTab": "Tab schließen",
|
||||
"session.error.serverConnection": "Verbindung zu diesem Server nicht möglich",
|
||||
"session.review.filesChanged": "{{count}} Dateien geändert",
|
||||
"session.review.filesChanged": "Geänderte Dateien: {{count}}",
|
||||
"session.review.change.one": "Änderung",
|
||||
"session.review.change.other": "Änderungen",
|
||||
"session.review.loadingChanges": "Lade Änderungen...",
|
||||
"session.review.loadingChanges": "Änderungen werden geladen…",
|
||||
"session.review.empty": "Noch keine Änderungen in dieser Sitzung",
|
||||
"session.review.noVcs": "Kein Git-Versionskontrollsystem erkannt, Änderungen werden nicht angezeigt",
|
||||
"session.review.noVcs": "Kein Git-Versionskontrollsystem erkannt. Änderungen werden nicht angezeigt",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot-Tracking ist in der Konfiguration deaktiviert, daher sind Sitzungsänderungen nicht verfügbar",
|
||||
"session.review.noChanges": "Keine Änderungen",
|
||||
@@ -594,10 +597,10 @@ export const dict = {
|
||||
"session.files.empty": "Keine Dateien",
|
||||
"session.files.binaryContent": "Binärdatei (Inhalt kann nicht angezeigt werden)",
|
||||
"session.messages.renderEarlier": "Frühere Nachrichten rendern",
|
||||
"session.messages.loadingEarlier": "Lade frühere Nachrichten...",
|
||||
"session.messages.loadingEarlier": "Frühere Nachrichten werden geladen…",
|
||||
"session.messages.loadEarlier": "Frühere Nachrichten laden",
|
||||
"session.messages.loading": "Lade Nachrichten...",
|
||||
"session.messages.jumpToLatest": "Zum neuesten springen",
|
||||
"session.messages.loading": "Nachrichten werden geladen…",
|
||||
"session.messages.jumpToLatest": "Zur neuesten Nachricht springen",
|
||||
"session.context.addToContext": "{{selection}} zum Kontext hinzufügen",
|
||||
"session.todo.title": "Aufgaben",
|
||||
"session.todo.collapse": "Einklappen",
|
||||
@@ -617,7 +620,7 @@ export const dict = {
|
||||
"session.revertDock.collapse": "Zurückgesetzte Nachrichten einklappen",
|
||||
"session.revertDock.expand": "Zurückgesetzte Nachrichten ausklappen",
|
||||
"session.revertDock.restore": "Nachricht wiederherstellen",
|
||||
"session.new.title": "Baue, was du willst",
|
||||
"session.new.title": "Alles entwickeln",
|
||||
"session.new.project.new": "Neues Projekt",
|
||||
"session.new.project.search": "Projekte durchsuchen",
|
||||
"session.new.project.add": "Projekt hinzufügen",
|
||||
@@ -651,16 +654,16 @@ export const dict = {
|
||||
"Sitzung öffentlich im Web teilen. Sie wird für jeden mit dem Link zugänglich sein.",
|
||||
"session.share.action.share": "Teilen",
|
||||
"session.share.action.publish": "Veröffentlichen",
|
||||
"session.share.action.publishing": "Veröffentliche...",
|
||||
"session.share.action.publishing": "Wird veröffentlicht…",
|
||||
"session.share.action.unpublish": "Veröffentlichung aufheben",
|
||||
"session.share.action.unpublishing": "Hebe Veröffentlichung auf...",
|
||||
"session.share.action.unpublishing": "Veröffentlichung wird aufgehoben…",
|
||||
"session.share.action.view": "Ansehen",
|
||||
"session.share.copy.copied": "Kopiert",
|
||||
"session.share.copy.copyLink": "Link kopieren",
|
||||
"lsp.tooltip.none": "Keine LSP-Server",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "Lade Prompt...",
|
||||
"terminal.loading": "Lade Terminal...",
|
||||
"prompt.loading": "Prompt wird geladen…",
|
||||
"terminal.loading": "Terminal wird geladen…",
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Terminal schließen",
|
||||
@@ -727,12 +730,12 @@ export const dict = {
|
||||
"settings.general.row.font.title": "Code-Schriftart",
|
||||
"settings.general.row.font.description": "Die in Codeblöcken verwendete Schriftart anpassen",
|
||||
"settings.general.row.terminalFont.title": "Terminalschriftart",
|
||||
"settings.general.row.terminalFont.description": "Passe die im Terminal verwendete Schriftart an",
|
||||
"settings.general.row.terminalFont.description": "Die im Terminal verwendete Schriftart anpassen",
|
||||
"settings.general.row.uiFont.title": "UI-Schriftart",
|
||||
"settings.general.row.uiFont.description": "Die im gesamten Interface verwendete Schriftart anpassen",
|
||||
"settings.general.row.followup.title": "Verhalten bei Folgefragen",
|
||||
"settings.general.row.uiFont.description": "Die in der gesamten Benutzeroberfläche verwendete Schriftart anpassen",
|
||||
"settings.general.row.followup.title": "Verhalten bei Folgeeingaben",
|
||||
"settings.general.row.followup.description":
|
||||
"Wählen Sie, ob Folgefragen sofort steuern oder in einer Warteschlange warten",
|
||||
"Wählen Sie, ob Folgeeingaben die laufende Sitzung sofort steuern oder in einer Warteschlange warten",
|
||||
"settings.general.row.followup.option.queue": "Warteschlange",
|
||||
"settings.general.row.followup.option.steer": "Steuern",
|
||||
"settings.general.row.showFileTree.title": "Dateibaum",
|
||||
@@ -750,8 +753,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation unten",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Titelleiste und Sitzungs-Tabs auf Mobilgeräten am unteren Bildschirmrand platzieren",
|
||||
"settings.general.row.showCustomAgents.title": "Benutzerdefinierte Agenten",
|
||||
"settings.general.row.showCustomAgents.description": "Agentenauswahl im Eingabebereich anzeigen",
|
||||
"settings.general.row.showCustomAgents.title": "Agent anzeigen",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Im Eingabebereich zwischen Agenten wechseln. Ist die Auswahl ausgeblendet, wird standardmäßig der Build-Agent verwendet.",
|
||||
"settings.general.row.reasoningSummaries.title": "Reasoning-Zusammenfassungen anzeigen",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Zusammenfassungen des Modell-Reasonings in der Timeline anzeigen",
|
||||
@@ -771,7 +775,7 @@ export const dict = {
|
||||
"settings.general.row.pinchZoom.title": "Zoom per Fingergeste",
|
||||
"settings.general.row.pinchZoom.description": "Zoomen per Zwei-Finger- und Ctrl-Scroll-Geste erlauben",
|
||||
"settings.general.row.wayland.title": "Natives Wayland verwenden",
|
||||
"settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
|
||||
"settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Ein Neustart ist erforderlich.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Unter Linux mit Monitoren unterschiedlicher Bildwiederholraten kann natives Wayland stabiler sein.",
|
||||
"settings.general.row.releaseNotes.title": "Versionshinweise",
|
||||
@@ -781,11 +785,11 @@ export const dict = {
|
||||
"settings.updates.row.check.title": "Nach Updates suchen",
|
||||
"settings.updates.row.check.description": "Manuell nach Updates suchen und installieren, wenn verfügbar",
|
||||
"settings.updates.action.checkNow": "Jetzt prüfen",
|
||||
"settings.updates.action.checking": "Wird geprüft...",
|
||||
"settings.updates.action.downloading": "Wird heruntergeladen...",
|
||||
"settings.updates.action.installing": "Wird installiert...",
|
||||
"settings.updates.toast.latest.title": "Du bist auf dem neuesten Stand",
|
||||
"settings.updates.toast.latest.description": "Du verwendest die aktuelle Version von OpenCode.",
|
||||
"settings.updates.action.checking": "Wird geprüft…",
|
||||
"settings.updates.action.downloading": "Wird heruntergeladen…",
|
||||
"settings.updates.action.installing": "Wird installiert…",
|
||||
"settings.updates.toast.latest.title": "OpenCode ist auf dem neuesten Stand",
|
||||
"settings.updates.toast.latest.description": "Die aktuelle Version von OpenCode wird verwendet.",
|
||||
"sound.option.none": "Keine",
|
||||
"sound.option.alert01": "Alarm 01",
|
||||
"sound.option.alert02": "Alarm 02",
|
||||
@@ -901,8 +905,8 @@ export const dict = {
|
||||
"settings.permissions.tool.bash.description": "Shell-Befehle ausführen",
|
||||
"settings.permissions.tool.task.title": "Aufgabe",
|
||||
"settings.permissions.tool.task.description": "Unteragenten starten",
|
||||
"settings.permissions.tool.skill.title": "Fähigkeit",
|
||||
"settings.permissions.tool.skill.description": "Eine Fähigkeit nach Namen laden",
|
||||
"settings.permissions.tool.skill.title": "Skill",
|
||||
"settings.permissions.tool.skill.description": "Einen Skill anhand des Namens laden",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Language-Server-Abfragen ausführen",
|
||||
"settings.permissions.tool.todowrite.title": "Todo schreiben",
|
||||
@@ -930,7 +934,7 @@ export const dict = {
|
||||
"workspace.reset.success.title": "Arbeitsbereich zurückgesetzt",
|
||||
"workspace.reset.success.description": "Der Arbeitsbereich entspricht jetzt dem Standard-Branch.",
|
||||
"workspace.error.stillPreparing": "Arbeitsbereich wird noch vorbereitet",
|
||||
"workspace.status.checking": "Suche nach nicht zusammengeführten Änderungen...",
|
||||
"workspace.status.checking": "Suche nach nicht zusammengeführten Änderungen…",
|
||||
"workspace.status.error": "Git-Status konnte nicht überprüft werden.",
|
||||
"workspace.status.clean": "Keine nicht zusammengeführten Änderungen erkannt.",
|
||||
"workspace.status.dirty": "Nicht zusammengeführte Änderungen in diesem Arbeitsbereich erkannt.",
|
||||
@@ -960,7 +964,7 @@ export const dict = {
|
||||
"settings.providers.custom.description": "Fügen Sie einen OpenAI-kompatiblen Anbieter per Basis-URL hinzu.",
|
||||
|
||||
"app.server.unreachable": "Konnte {{server}} nicht erreichen",
|
||||
"app.server.retrying": "Automatische erneute Verbindung...",
|
||||
"app.server.retrying": "Verbindung wird automatisch erneut hergestellt…",
|
||||
"app.server.otherServers": "Andere Server",
|
||||
"dialog.server.add.usernamePlaceholder": "Benutzername",
|
||||
"dialog.server.add.passwordPlaceholder": "Passwort",
|
||||
@@ -968,12 +972,15 @@ export const dict = {
|
||||
"session.review.noVcs.createGit.title": "Git-Repository erstellen",
|
||||
"session.review.noVcs.createGit.description":
|
||||
"Änderungen in diesem Projekt verfolgen, überprüfen und rückgängig machen",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git-Repository wird erstellt...",
|
||||
"session.review.noVcs.createGit.actionLoading": "Git-Repository wird erstellt…",
|
||||
"session.review.noVcs.createGit.action": "Git-Repository erstellen",
|
||||
"session.todo.progress": "{{done}} von {{total}} Aufgaben erledigt",
|
||||
"session.question.progress": "{{current}} von {{total}} Fragen",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Datei-Explorer",
|
||||
"session.header.reveal.finder": "Im Finder anzeigen",
|
||||
"session.header.reveal.fileExplorer": "Im Datei-Explorer anzeigen",
|
||||
"session.header.reveal.containingFolder": "Übergeordneten Ordner öffnen",
|
||||
"session.header.open.fileManager": "Dateimanager",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -988,29 +995,30 @@ export const dict = {
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"debugBar.ariaLabel": "Entwicklungs-Leistungsdiagnose",
|
||||
"debugBar.ariaLabel": "Leistungsdiagnose für die Entwicklung",
|
||||
"debugBar.na": "n.v.",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Letzter abgeschlossener Routenübergang, der eine Sitzungsseite berührt, gemessen vom Start des Routers bis zum ersten Rendern nach dem Einschwingen.",
|
||||
"Letzter abgeschlossener Routenübergang zu einer Sitzungsseite, gemessen vom Start des Routers bis zum ersten Rendering nach Abschluss des Übergangs.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Gleitende Bilder pro Sekunde in den letzten 5 Sekunden.",
|
||||
"debugBar.fps.tip": "Gleitender Mittelwert der Frames pro Sekunde in den letzten 5 Sekunden.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Schlechteste Frame-Zeit in den letzten 5 Sekunden.",
|
||||
"debugBar.frame.tip": "Längste Framedauer in den letzten 5 Sekunden.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Frames über 32ms in den letzten 5 Sekunden.",
|
||||
"debugBar.jank.tip": "Frames über 32 ms in den letzten 5 Sekunden.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Blockierte Zeit und Anzahl langer Aufgaben in den letzten 5 Sekunden. Max Aufgabe: {{max}}.",
|
||||
"debugBar.long.tip":
|
||||
"Blockierte Zeit und Anzahl langer Aufgaben in den letzten 5 Sekunden. Längste Aufgabe: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "Schlechteste beobachtete Eingabeverzögerung in den letzten 5 Sekunden.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Ungefähre Interaktionsdauer in den letzten 5 Sekunden. Dies ist INP-ähnlich, nicht das offizielle Web Vitals INP.",
|
||||
"Ungefähre Interaktionsdauer in den letzten 5 Sekunden. Dies ist INP-ähnlich, nicht das offizielle Web-Vitals-INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Kumulative Layoutverschiebung für die aktuelle App-Lebensdauer.",
|
||||
"debugBar.cls.tip": "Kumulative Layoutverschiebung während der aktuellen App-Laufzeit.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Verwendeter JS-Heap vs Heap-Limit. Nur Chromium.",
|
||||
"debugBar.mem.tip": "Verwendeter JS-Heap vs Heap-Limit. {{used}} von {{limit}}.",
|
||||
"debugBar.mem.tipUnavailable": "Belegter JS-Heap im Verhältnis zum Heap-Limit. Nur Chromium.",
|
||||
"debugBar.mem.tip": "Belegter JS-Heap im Verhältnis zum Heap-Limit. {{used}} von {{limit}}.",
|
||||
"common.key.ctrl": "Strg",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Umschalt",
|
||||
@@ -1031,7 +1039,7 @@ export const dict = {
|
||||
"error.serverSDK.serverNotAvailable": "Server nicht verfügbar",
|
||||
"error.childStore.persistedCacheCreateFailed": "Dauerhafter Cache konnte nicht erstellt werden",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "Dauerhafte Projektmetadaten konnten nicht erstellt werden",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Dauerhaftes Projekticon konnte nicht erstellt werden",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Dauerhaftes Projektsymbol konnte nicht erstellt werden",
|
||||
"error.childStore.storeCreateFailed": "Speicher konnte nicht erstellt werden",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket abnormal geschlossen: {{code}}",
|
||||
"terminal.connectionLost.abnormalClose": "WebSocket unerwartet geschlossen: {{code}}",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -301,6 +301,7 @@ export const dict = {
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Unsupported attachment",
|
||||
"prompt.toast.pasteUnsupported.description": "Only images, PDFs, or text files can be attached here.",
|
||||
"prompt.toast.attachmentDuplicate.title": "This file has already been uploaded",
|
||||
"prompt.toast.modelAgentRequired.title": "Select an agent and model",
|
||||
"prompt.toast.modelAgentRequired.description": "Choose an agent and model before sending a prompt.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Failed to create worktree",
|
||||
|
||||
+108
-101
@@ -21,7 +21,7 @@ export const dict = {
|
||||
"theme.scheme.light": "Claro",
|
||||
"theme.scheme.dark": "Oscuro",
|
||||
|
||||
"command.sidebar.toggle": "Alternar barra lateral",
|
||||
"command.sidebar.toggle": "Mostrar u ocultar barra lateral",
|
||||
"command.project.open": "Abrir proyecto",
|
||||
"command.project.previous": "Proyecto anterior",
|
||||
"command.project.next": "Siguiente proyecto",
|
||||
@@ -37,12 +37,12 @@ export const dict = {
|
||||
|
||||
"command.palette": "Paleta de comandos",
|
||||
|
||||
"command.theme.cycle": "Alternar tema",
|
||||
"command.theme.cycle": "Cambiar tema",
|
||||
"command.theme.set": "Usar tema: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Alternar esquema de color",
|
||||
"command.theme.scheme.cycle": "Cambiar esquema de color",
|
||||
"command.theme.scheme.set": "Usar esquema de color: {{scheme}}",
|
||||
|
||||
"command.language.cycle": "Alternar idioma",
|
||||
"command.language.cycle": "Cambiar idioma",
|
||||
"command.language.set": "Usar idioma: {{language}}",
|
||||
|
||||
"command.session.new": "Nueva sesión",
|
||||
@@ -52,12 +52,12 @@ export const dict = {
|
||||
"command.context.addSelection": "Añadir selección al contexto",
|
||||
"command.context.addSelection.description": "Añadir las líneas seleccionadas del archivo actual",
|
||||
"command.input.focus": "Enfocar entrada",
|
||||
"command.terminal.toggle": "Alternar terminal",
|
||||
"command.fileTree.toggle": "Alternar árbol de archivos",
|
||||
"command.review.toggle": "Alternar revisión",
|
||||
"command.terminal.toggle": "Mostrar u ocultar terminal",
|
||||
"command.fileTree.toggle": "Mostrar u ocultar árbol de archivos",
|
||||
"command.review.toggle": "Mostrar u ocultar revisión",
|
||||
"command.terminal.new": "Nueva terminal",
|
||||
"command.terminal.new.description": "Crear una nueva pestaña de terminal",
|
||||
"command.steps.toggle": "Alternar pasos",
|
||||
"command.steps.toggle": "Mostrar u ocultar pasos",
|
||||
"command.steps.toggle.description": "Mostrar u ocultar pasos para el mensaje actual",
|
||||
"command.message.previous": "Mensaje anterior",
|
||||
"command.message.previous.description": "Ir al mensaje de usuario anterior",
|
||||
@@ -65,19 +65,19 @@ export const dict = {
|
||||
"command.message.next.description": "Ir al siguiente mensaje de usuario",
|
||||
"command.model.choose": "Elegir modelo",
|
||||
"command.model.choose.description": "Seleccionar un modelo diferente",
|
||||
"command.mcp.toggle": "Alternar MCPs",
|
||||
"command.mcp.toggle.description": "Alternar MCPs",
|
||||
"command.agent.cycle": "Alternar agente",
|
||||
"command.mcp.toggle": "Activar o desactivar servidores MCP",
|
||||
"command.mcp.toggle.description": "Activar o desactivar servidores MCP",
|
||||
"command.agent.cycle": "Cambiar de agente",
|
||||
"command.agent.cycle.description": "Cambiar al siguiente agente",
|
||||
"command.agent.cycle.reverse": "Alternar agente hacia atrás",
|
||||
"command.agent.cycle.reverse": "Cambiar al agente anterior",
|
||||
"command.agent.cycle.reverse.description": "Cambiar al agente anterior",
|
||||
"command.model.variant.cycle": "Alternar esfuerzo de pensamiento",
|
||||
"command.model.variant.cycle": "Cambiar esfuerzo de razonamiento",
|
||||
"command.model.variant.cycle.description": "Cambiar al siguiente nivel de esfuerzo",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Prompt",
|
||||
"command.permissions.autoaccept.enable": "Aceptar permisos automáticamente",
|
||||
"command.permissions.autoaccept.disable": "Dejar de aceptar permisos automáticamente",
|
||||
"command.workspace.toggle": "Alternar espacios de trabajo",
|
||||
"command.workspace.toggle": "Activar o desactivar espacios de trabajo",
|
||||
"command.workspace.toggle.description": "Habilitar o deshabilitar múltiples espacios de trabajo en la barra lateral",
|
||||
"command.session.undo": "Deshacer",
|
||||
"command.session.undo.description": "Deshacer el último mensaje",
|
||||
@@ -100,17 +100,18 @@ export const dict = {
|
||||
|
||||
"dialog.provider.search.placeholder": "Buscar proveedores",
|
||||
"dialog.provider.empty": "No se encontraron proveedores",
|
||||
"dialog.provider.group.popular": "Popular",
|
||||
"dialog.provider.group.other": "Otro",
|
||||
"dialog.provider.group.popular": "Populares",
|
||||
"dialog.provider.group.other": "Otros",
|
||||
"dialog.provider.custom.label": "Proveedor personalizado compatible con OpenAI",
|
||||
"dialog.provider.tag.recommended": "Recomendado",
|
||||
"dialog.provider.opencode.note": "Modelos seleccionados incluyendo Claude, GPT, Gemini y más",
|
||||
"dialog.provider.opencode.note": "Selección de modelos como Claude, GPT, Gemini y otros",
|
||||
"dialog.provider.opencode.tagline": "Modelos optimizados y fiables",
|
||||
"dialog.provider.opencodeGo.tagline": "Suscripción económica para todos",
|
||||
"dialog.provider.anthropic.note": "Acceso directo a modelos Claude, incluyendo Pro y Max",
|
||||
"dialog.provider.anthropic.note": "Acceso directo a modelos Claude, incluidos Pro y Max",
|
||||
"dialog.provider.copilot.note": "Modelos de IA para asistencia de codificación a través de GitHub Copilot",
|
||||
"dialog.provider.openai.note": "Modelos GPT para tareas de IA generales rápidas y capaces",
|
||||
"dialog.provider.google.note": "Modelos Gemini para respuestas rápidas y estructuradas",
|
||||
"dialog.provider.openrouter.note": "Accede a todos los modelos soportados desde un solo proveedor",
|
||||
"dialog.provider.openrouter.note": "Accede a todos los modelos compatibles desde un solo proveedor",
|
||||
"dialog.provider.vercel.note": "Acceso unificado a modelos de IA con enrutamiento inteligente",
|
||||
|
||||
"dialog.model.select.title": "Seleccionar modelo",
|
||||
@@ -118,10 +119,11 @@ export const dict = {
|
||||
"dialog.model.empty": "Sin resultados de modelos",
|
||||
"dialog.model.manage": "Gestionar modelos",
|
||||
"dialog.model.manage.description": "Personalizar qué modelos aparecen en el selector de modelos.",
|
||||
"dialog.model.manage.provider.toggle": "Alternar todos los modelos de {{provider}}",
|
||||
"dialog.model.manage.provider.toggle": "Mostrar u ocultar todos los modelos de {{provider}}",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Modelos gratuitos proporcionados por OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Añadir más modelos de proveedores populares",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Ver más de 70 proveedores",
|
||||
|
||||
"dialog.provider.viewAll": "Ver más proveedores",
|
||||
|
||||
@@ -173,7 +175,8 @@ export const dict = {
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.miproveedor.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Clave API",
|
||||
"provider.custom.field.apiKey.placeholder": "Clave API",
|
||||
"provider.custom.field.apiKey.description": "Opcional. Dejar vacío si gestionas la autenticación mediante cabeceras.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Opcional. Déjalo vacío si gestionas la autenticación mediante encabezados.",
|
||||
"provider.custom.models.label": "Modelos",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "id-modelo",
|
||||
@@ -181,13 +184,13 @@ export const dict = {
|
||||
"provider.custom.models.name.placeholder": "Nombre para mostrar",
|
||||
"provider.custom.models.remove": "Eliminar modelo",
|
||||
"provider.custom.models.add": "Añadir modelo",
|
||||
"provider.custom.headers.label": "Cabeceras (opcional)",
|
||||
"provider.custom.headers.key.label": "Cabecera",
|
||||
"provider.custom.headers.key.placeholder": "Nombre-Cabecera",
|
||||
"provider.custom.headers.label": "Encabezados (opcional)",
|
||||
"provider.custom.headers.key.label": "Encabezado",
|
||||
"provider.custom.headers.key.placeholder": "Nombre-Encabezado",
|
||||
"provider.custom.headers.value.label": "Valor",
|
||||
"provider.custom.headers.value.placeholder": "valor",
|
||||
"provider.custom.headers.remove": "Eliminar cabecera",
|
||||
"provider.custom.headers.add": "Añadir cabecera",
|
||||
"provider.custom.headers.remove": "Eliminar encabezado",
|
||||
"provider.custom.headers.add": "Añadir encabezado",
|
||||
"provider.custom.error.providerID.required": "El ID del proveedor es obligatorio",
|
||||
"provider.custom.error.providerID.format": "Usa letras minúsculas, números, guiones o guiones bajos",
|
||||
"provider.custom.error.providerID.exists": "Ese ID de proveedor ya existe",
|
||||
@@ -201,7 +204,7 @@ export const dict = {
|
||||
"provider.disconnect.toast.disconnected.description": "Los modelos de {{provider}} ya no están disponibles.",
|
||||
|
||||
"model.tag.free": "Gratis",
|
||||
"model.tag.latest": "Último",
|
||||
"model.tag.latest": "Más reciente",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
@@ -211,7 +214,7 @@ export const dict = {
|
||||
"model.input.image": "imagen",
|
||||
"model.input.audio": "audio",
|
||||
"model.input.video": "video",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.input.pdf": "PDF",
|
||||
"model.tooltip.allows": "Permite: {{inputs}}",
|
||||
"model.tooltip.context.label": "Contexto",
|
||||
"model.tooltip.inputs": "Entradas",
|
||||
@@ -231,7 +234,7 @@ export const dict = {
|
||||
"common.clear": "Limpiar",
|
||||
"common.connect": "Conectar",
|
||||
"common.disconnect": "Desconectar",
|
||||
"common.continue": "Enviar",
|
||||
"common.continue": "Continuar",
|
||||
"common.submit": "Enviar",
|
||||
"common.save": "Guardar",
|
||||
"common.saving": "Guardando...",
|
||||
@@ -250,7 +253,7 @@ export const dict = {
|
||||
"session.child.backToParent": "Volver a la sesión principal.",
|
||||
|
||||
"prompt.example.1": "Arreglar un TODO en el código",
|
||||
"prompt.example.2": "¿Cuál es el stack tecnológico de este proyecto?",
|
||||
"prompt.example.2": "¿Qué tecnologías usa este proyecto?",
|
||||
"prompt.example.3": "Arreglar pruebas rotas",
|
||||
"prompt.example.4": "Explicar cómo funciona la autenticación",
|
||||
"prompt.example.5": "Encontrar y arreglar vulnerabilidades de seguridad",
|
||||
@@ -263,22 +266,22 @@ export const dict = {
|
||||
"prompt.example.12": "Añadir validación de entrada",
|
||||
"prompt.example.13": "Crear un nuevo componente para...",
|
||||
"prompt.example.14": "¿Cómo despliego este proyecto?",
|
||||
"prompt.example.15": "Revisar mi código para mejores prácticas",
|
||||
"prompt.example.16": "Añadir manejo de errores a esta función",
|
||||
"prompt.example.17": "Explicar este patrón de regex",
|
||||
"prompt.example.15": "Revisar si mi código sigue las prácticas recomendadas",
|
||||
"prompt.example.16": "Añadir gestión de errores a esta función",
|
||||
"prompt.example.17": "Explicar este patrón de expresión regular",
|
||||
"prompt.example.18": "Convertir esto a TypeScript",
|
||||
"prompt.example.19": "Añadir logging en todo el código",
|
||||
"prompt.example.19": "Añadir registros en todo el código",
|
||||
"prompt.example.20": "¿Qué dependencias están desactualizadas?",
|
||||
"prompt.example.21": "Ayúdame a escribir un script de migración",
|
||||
"prompt.example.22": "Implementar caché para este endpoint",
|
||||
"prompt.example.22": "Implementar caché para este punto de conexión",
|
||||
"prompt.example.23": "Añadir paginación a esta lista",
|
||||
"prompt.example.24": "Crear un comando CLI para...",
|
||||
"prompt.example.25": "¿Cómo funcionan las variables de entorno aquí?",
|
||||
|
||||
"prompt.popover.emptyResults": "Sin resultados coincidentes",
|
||||
"prompt.popover.emptyCommands": "Sin comandos coincidentes",
|
||||
"prompt.dropzone.label": "Suelta imágenes, PDFs o archivos de texto aquí",
|
||||
"prompt.dropzone.file.label": "Suelta para @mencionar archivo",
|
||||
"prompt.dropzone.label": "Suelta aquí imágenes, archivos PDF o archivos de texto",
|
||||
"prompt.dropzone.file.label": "Suelta para @mencionar el archivo",
|
||||
"prompt.slash.badge.custom": "personalizado",
|
||||
"prompt.slash.badge.skill": "skill",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
@@ -286,7 +289,7 @@ export const dict = {
|
||||
"prompt.context.includeActiveFile": "Incluir archivo activo",
|
||||
"prompt.context.removeActiveFile": "Eliminar archivo activo del contexto",
|
||||
"prompt.context.removeFile": "Eliminar archivo del contexto",
|
||||
"prompt.action.attachFile": "Adjuntar archivo",
|
||||
"prompt.action.attachFile": "Añadir archivos",
|
||||
"prompt.menu.addImagesAndFiles": "Añadir archivos y más",
|
||||
"prompt.menu.imagesAndFiles": "Imágenes y archivos",
|
||||
"prompt.menu.commands": "Comandos",
|
||||
@@ -297,7 +300,9 @@ export const dict = {
|
||||
"prompt.action.stop": "Detener",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Adjunto no compatible",
|
||||
"prompt.toast.pasteUnsupported.description": "Solo se pueden adjuntar imágenes, PDFs o archivos de texto aquí.",
|
||||
"prompt.toast.attachmentDuplicate.title": "Este archivo ya se ha subido",
|
||||
"prompt.toast.pasteUnsupported.description":
|
||||
"Aquí solo se pueden adjuntar imágenes, archivos PDF o archivos de texto.",
|
||||
"prompt.toast.modelAgentRequired.title": "Selecciona un agente y modelo",
|
||||
"prompt.toast.modelAgentRequired.description": "Elige un agente y modelo antes de enviar un prompt.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Fallo al crear el árbol de trabajo",
|
||||
@@ -307,16 +312,16 @@ export const dict = {
|
||||
"prompt.toast.promptSendFailed.title": "Fallo al enviar prompt",
|
||||
"prompt.toast.promptSendFailed.description": "No se pudo recuperar la sesión",
|
||||
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.title": "Servidores MCP",
|
||||
"dialog.mcp.description": "{{enabled}} de {{total}} habilitados",
|
||||
"dialog.mcp.empty": "No hay MCPs configurados",
|
||||
"dialog.mcp.empty": "No hay servidores MCP configurados",
|
||||
|
||||
"dialog.lsp.empty": "LSPs detectados automáticamente por tipo de archivo",
|
||||
"dialog.lsp.empty": "Servidores LSP detectados automáticamente por tipo de archivo",
|
||||
"dialog.plugins.empty": "Plugins configurados en opencode.json",
|
||||
|
||||
"mcp.status.connected": "conectado",
|
||||
"mcp.status.failed": "fallido",
|
||||
"mcp.status.needs_auth": "necesita auth",
|
||||
"mcp.status.needs_auth": "requiere autenticación",
|
||||
"mcp.auth.clickToAuthenticate": "Haz clic para autenticar",
|
||||
"mcp.status.disabled": "deshabilitado",
|
||||
|
||||
@@ -327,13 +332,13 @@ export const dict = {
|
||||
"dialog.directory.action.selectFile": "Seleccionar archivo",
|
||||
"dialog.directory.action.selectFolder": "Seleccionar carpeta",
|
||||
"dialog.directory.root": "Raíz",
|
||||
"dialog.directory.parent": "Superior",
|
||||
"dialog.directory.parent": "Carpeta superior",
|
||||
"dialog.directory.readError": "No se puede leer esta carpeta",
|
||||
|
||||
"dialog.server.title": "Servidores",
|
||||
"dialog.server.description": "Cambiar a qué servidor de OpenCode se conecta esta app.",
|
||||
"dialog.server.description": "Cambiar el servidor de OpenCode al que se conecta esta aplicación.",
|
||||
"dialog.server.search.placeholder": "Buscar servidores",
|
||||
"dialog.server.empty": "No hay servidores aún",
|
||||
"dialog.server.empty": "Aún no hay servidores",
|
||||
"dialog.server.add.title": "Añadir un servidor",
|
||||
"dialog.server.add.url": "URL del servidor",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
@@ -347,7 +352,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Editar servidor",
|
||||
"dialog.server.default.title": "Servidor predeterminado",
|
||||
"dialog.server.default.description":
|
||||
"Conectar a este servidor al iniciar la app en lugar de iniciar un servidor local. Requiere reinicio.",
|
||||
"Conectar a este servidor al iniciar la aplicación en lugar de iniciar un servidor local. Requiere reiniciar.",
|
||||
"dialog.server.default.none": "Ningún servidor seleccionado",
|
||||
"dialog.server.default.set": "Establecer servidor actual como predeterminado",
|
||||
"dialog.server.default.clear": "Limpiar",
|
||||
@@ -355,7 +360,7 @@ export const dict = {
|
||||
|
||||
"dialog.server.menu.edit": "Editar",
|
||||
"dialog.server.menu.default": "Establecer como predeterminado",
|
||||
"dialog.server.menu.defaultRemove": "Quitar predeterminado",
|
||||
"dialog.server.menu.defaultRemove": "Quitar como predeterminado",
|
||||
"dialog.server.menu.delete": "Eliminar",
|
||||
"dialog.server.current": "Servidor actual",
|
||||
"dialog.server.status.default": "Predeterminado",
|
||||
@@ -442,7 +447,7 @@ export const dict = {
|
||||
"Se ejecuta después de crear un nuevo espacio de trabajo (árbol de trabajo).",
|
||||
"dialog.project.edit.worktree.startup.placeholder": "p. ej. bun install",
|
||||
|
||||
"context.breakdown.title": "Desglose de Contexto",
|
||||
"context.breakdown.title": "Desglose del contexto",
|
||||
"context.breakdown.note":
|
||||
'Desglose aproximado de tokens de entrada. "Otro" incluye definiciones de herramientas y sobrecarga.',
|
||||
"context.breakdown.system": "Sistema",
|
||||
@@ -451,25 +456,25 @@ export const dict = {
|
||||
"context.breakdown.tool": "Llamadas a herramientas",
|
||||
"context.breakdown.other": "Otro",
|
||||
|
||||
"context.systemPrompt.title": "Prompt del Sistema",
|
||||
"context.systemPrompt.title": "Prompt del sistema",
|
||||
"context.rawMessages.title": "Mensajes en bruto",
|
||||
|
||||
"context.stats.session": "Sesión",
|
||||
"context.stats.messages": "Mensajes",
|
||||
"context.stats.provider": "Proveedor",
|
||||
"context.stats.model": "Modelo",
|
||||
"context.stats.limit": "Límite de Contexto",
|
||||
"context.stats.totalTokens": "Tokens Totales",
|
||||
"context.stats.limit": "Límite de contexto",
|
||||
"context.stats.totalTokens": "Tokens totales",
|
||||
"context.stats.usage": "Uso",
|
||||
"context.stats.inputTokens": "Tokens de Entrada",
|
||||
"context.stats.outputTokens": "Tokens de Salida",
|
||||
"context.stats.reasoningTokens": "Tokens de Razonamiento",
|
||||
"context.stats.cacheTokens": "Tokens de Caché (lectura/escritura)",
|
||||
"context.stats.userMessages": "Mensajes de Usuario",
|
||||
"context.stats.assistantMessages": "Mensajes de Asistente",
|
||||
"context.stats.totalCost": "Costo Total",
|
||||
"context.stats.sessionCreated": "Sesión Creada",
|
||||
"context.stats.lastActivity": "Última Actividad",
|
||||
"context.stats.inputTokens": "Tokens de entrada",
|
||||
"context.stats.outputTokens": "Tokens de salida",
|
||||
"context.stats.reasoningTokens": "Tokens de razonamiento",
|
||||
"context.stats.cacheTokens": "Tokens de caché (lectura/escritura)",
|
||||
"context.stats.userMessages": "Mensajes de usuario",
|
||||
"context.stats.assistantMessages": "Mensajes de asistente",
|
||||
"context.stats.totalCost": "Costo total",
|
||||
"context.stats.sessionCreated": "Sesión creada",
|
||||
"context.stats.lastActivity": "Última actividad",
|
||||
|
||||
"context.usage.tokens": "Tokens",
|
||||
"context.usage.usage": "Uso",
|
||||
@@ -523,12 +528,12 @@ export const dict = {
|
||||
|
||||
"toast.session.share.copyFailed.title": "Fallo al copiar URL al portapapeles",
|
||||
"toast.session.share.success.title": "Sesión compartida",
|
||||
"toast.session.share.success.description": "¡URL compartida copiada al portapapeles!",
|
||||
"toast.session.share.success.description": "Enlace para compartir copiado al portapapeles.",
|
||||
"toast.session.share.failed.title": "Fallo al compartir sesión",
|
||||
"toast.session.share.failed.description": "Ocurrió un error al compartir la sesión",
|
||||
|
||||
"toast.session.unshare.success.title": "Sesión dejó de compartirse",
|
||||
"toast.session.unshare.success.description": "¡La sesión dejó de compartirse exitosamente!",
|
||||
"toast.session.unshare.success.title": "La sesión dejó de compartirse",
|
||||
"toast.session.unshare.success.description": "La sesión dejó de compartirse correctamente.",
|
||||
"toast.session.unshare.failed.title": "Fallo al dejar de compartir sesión",
|
||||
"toast.session.unshare.failed.description": "Ocurrió un error al dejar de compartir la sesión",
|
||||
|
||||
@@ -544,13 +549,13 @@ export const dict = {
|
||||
"error.page.description.localServerStartup": "Ocurrió un error al iniciar el servidor local.",
|
||||
"error.page.details.label": "Detalles del error",
|
||||
"error.page.action.restart": "Reiniciar",
|
||||
"error.page.action.report": "Informar error",
|
||||
"error.page.action.reported": "Error informado",
|
||||
"error.page.action.report": "Informar del error",
|
||||
"error.page.action.reported": "Error notificado",
|
||||
"error.page.action.exportLogs": "Exportar registros",
|
||||
"error.page.action.checking": "Comprobando...",
|
||||
"error.page.action.checkUpdates": "Buscar actualizaciones",
|
||||
"error.page.action.updateTo": "Actualizar a {{version}}",
|
||||
"error.page.report.prefix": "Por favor reporta este error al equipo de OpenCode",
|
||||
"error.page.report.prefix": "Informa de este error al equipo de OpenCode",
|
||||
"error.page.report.discord": "en Discord",
|
||||
"error.page.version": "Versión: {{version}}",
|
||||
|
||||
@@ -566,10 +571,10 @@ export const dict = {
|
||||
"error.chain.status": "Estado: {{status}}",
|
||||
"error.chain.retryable": "Reintentable: {{retryable}}",
|
||||
"error.chain.responseBody": "Cuerpo de la respuesta:\n{{body}}",
|
||||
"error.chain.didYouMean": "¿Quisiste decir: {{suggestions}}",
|
||||
"error.chain.didYouMean": "¿Quizá quisiste decir {{suggestions}}?",
|
||||
"error.chain.modelNotFound": "Modelo no encontrado: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Comprueba los nombres de proveedor/modelo en tu configuración (opencode.json)",
|
||||
"error.chain.mcpFailed": 'El servidor MCP "{{name}}" falló. Nota, OpenCode no soporta autenticación MCP todavía.',
|
||||
"error.chain.mcpFailed": 'El servidor MCP "{{name}}" falló. Nota: OpenCode aún no admite la autenticación MCP.',
|
||||
"error.chain.providerAuthFailed": "Autenticación de proveedor fallida ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'Fallo al inicializar proveedor "{{provider}}". Comprueba credenciales y configuración.',
|
||||
@@ -622,12 +627,12 @@ export const dict = {
|
||||
"session.error.notFound.description": "Esta pestaña apunta a una sesión que ya no existe en este servidor.",
|
||||
"session.error.notFound.closeTab": "Cerrar pestaña",
|
||||
"session.error.serverConnection": "No se puede conectar a este servidor",
|
||||
"session.review.filesChanged": "{{count}} Archivos Cambiados",
|
||||
"session.review.filesChanged": "Archivos modificados: {{count}}",
|
||||
"session.review.change.one": "Cambio",
|
||||
"session.review.change.other": "Cambios",
|
||||
"session.review.loadingChanges": "Cargando cambios...",
|
||||
"session.review.empty": "No hay cambios en esta sesión aún",
|
||||
"session.review.noVcs": "No se detectó Sistema de Control de Versiones Git, los cambios no se muestran",
|
||||
"session.review.empty": "Aún no hay cambios en esta sesión",
|
||||
"session.review.noVcs": "No se detectó ningún sistema de control de versiones Git; no se muestran los cambios",
|
||||
"session.review.noSnapshot":
|
||||
"El seguimiento de instantáneas está deshabilitado en la configuración, por lo que los cambios de sesión no están disponibles",
|
||||
"session.review.noChanges": "Sin cambios",
|
||||
@@ -637,13 +642,13 @@ export const dict = {
|
||||
"session.files.selectToOpen": "Selecciona un archivo para abrir",
|
||||
"session.files.all": "Todos los archivos",
|
||||
"session.files.empty": "Sin archivos",
|
||||
"session.files.binaryContent": "Archivo binario (el contenido no puede ser mostrado)",
|
||||
"session.files.binaryContent": "Archivo binario (no se puede mostrar el contenido)",
|
||||
|
||||
"session.messages.renderEarlier": "Renderizar mensajes anteriores",
|
||||
"session.messages.renderEarlier": "Mostrar mensajes anteriores",
|
||||
"session.messages.loadingEarlier": "Cargando mensajes anteriores...",
|
||||
"session.messages.loadEarlier": "Cargar mensajes anteriores",
|
||||
"session.messages.loading": "Cargando mensajes...",
|
||||
"session.messages.jumpToLatest": "Ir al último",
|
||||
"session.messages.jumpToLatest": "Ir al mensaje más reciente",
|
||||
|
||||
"session.context.addToContext": "Añadir {{selection}} al contexto",
|
||||
"session.todo.title": "Tareas",
|
||||
@@ -693,9 +698,9 @@ export const dict = {
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Plugins",
|
||||
"status.popover.action.manageServers": "Administrar servidores",
|
||||
"status.popover.action.manageServers": "Gestionar servidores",
|
||||
|
||||
"session.share.popover.title": "Publicar en web",
|
||||
"session.share.popover.title": "Publicar en la web",
|
||||
"session.share.popover.description.shared":
|
||||
"Esta sesión es pública en la web. Es accesible para cualquiera con el enlace.",
|
||||
"session.share.popover.description.unshared":
|
||||
@@ -725,7 +730,7 @@ export const dict = {
|
||||
"common.dismiss": "Descartar",
|
||||
"common.requestFailed": "Solicitud fallida",
|
||||
"common.moreOptions": "Más opciones",
|
||||
"common.learnMore": "Saber más",
|
||||
"common.learnMore": "Más información",
|
||||
"common.rename": "Renombrar",
|
||||
"common.reset": "Restablecer",
|
||||
"common.archive": "Archivar",
|
||||
@@ -735,15 +740,15 @@ export const dict = {
|
||||
"common.loadMore": "Cargar más",
|
||||
"common.key.esc": "ESC",
|
||||
|
||||
"sidebar.menu.toggle": "Alternar menú",
|
||||
"sidebar.menu.toggle": "Mostrar u ocultar menú",
|
||||
"sidebar.nav.projectsAndSessions": "Proyectos y sesiones",
|
||||
"sidebar.settings": "Ajustes",
|
||||
"sidebar.help": "Ayuda",
|
||||
"sidebar.workspaces.enable": "Habilitar espacios de trabajo",
|
||||
"sidebar.workspaces.disable": "Deshabilitar espacios de trabajo",
|
||||
"sidebar.gettingStarted.title": "Empezando",
|
||||
"sidebar.gettingStarted.title": "Primeros pasos",
|
||||
"sidebar.gettingStarted.line1": "OpenCode incluye modelos gratuitos para que puedas empezar inmediatamente.",
|
||||
"sidebar.gettingStarted.line2": "Conecta cualquier proveedor para usar modelos, inc. Claude, GPT, Gemini etc.",
|
||||
"sidebar.gettingStarted.line2": "Conecta cualquier proveedor para usar modelos como Claude, GPT o Gemini.",
|
||||
"sidebar.project.recentSessions": "Sesiones recientes",
|
||||
"sidebar.project.viewAllSessions": "Ver todas las sesiones",
|
||||
"sidebar.project.clearNotifications": "Borrar notificaciones",
|
||||
@@ -771,9 +776,8 @@ export const dict = {
|
||||
"settings.general.row.language.title": "Idioma",
|
||||
"settings.general.row.language.description": "Cambiar el idioma de visualización para OpenCode",
|
||||
"settings.general.row.shell.title": "Shell del terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Elige el shell que se usará en el terminal. Los shells compatibles también se usarán para las llamadas a herramientas del agente.",
|
||||
"settings.general.row.shell.autoDefault": "Automático (Predeterminado)",
|
||||
"settings.general.row.shell.description": "Shell que usan el terminal y las herramientas del agente",
|
||||
"settings.general.row.shell.autoDefault": "Automático (predeterminado)",
|
||||
"settings.general.row.shell.terminalOnly": "solo en el terminal",
|
||||
"settings.general.row.appearance.title": "Apariencia",
|
||||
"settings.general.row.appearance.description": "Personaliza cómo se ve OpenCode en tu dispositivo",
|
||||
@@ -807,8 +811,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navegación inferior",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Colocar la barra de título y las pestañas de sesión en la parte inferior de la pantalla en dispositivos móviles",
|
||||
"settings.general.row.showCustomAgents.title": "Agentes personalizados",
|
||||
"settings.general.row.showCustomAgents.description": "Mostrar el selector de agentes en el editor",
|
||||
"settings.general.row.showCustomAgents.title": "Mostrar agente",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Cambiar de agente en el editor. Si se oculta, se usa el agente Build de forma predeterminada.",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resúmenes de razonamiento",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Mostrar resúmenes del razonamiento del modelo en la línea de tiempo",
|
||||
@@ -827,9 +832,9 @@ export const dict = {
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Descartar",
|
||||
"settings.general.row.pinchZoom.title": "Pellizcar para ampliar",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Permitir ampliar con el gesto de pellizco del panel táctil y con Ctrl-scroll",
|
||||
"Permitir ampliar con el gesto de pellizco del panel táctil y con Ctrl + desplazamiento",
|
||||
"settings.general.row.wayland.title": "Usar Wayland nativo",
|
||||
"settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.",
|
||||
"settings.general.row.wayland.description": "Deshabilitar el uso alternativo de X11 en Wayland. Requiere reiniciar.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"En Linux con monitores de frecuencia de actualización mixta, Wayland nativo puede ser más estable.",
|
||||
|
||||
@@ -959,8 +964,7 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "Leer",
|
||||
"settings.permissions.tool.read.description": "Leer un archivo (coincide con la ruta del archivo)",
|
||||
"settings.permissions.tool.edit.title": "Editar",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modificar archivos, incluyendo ediciones, escrituras, parches y multi-ediciones",
|
||||
"settings.permissions.tool.edit.description": "Modificar archivos mediante ediciones, escrituras y parches",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Coincidir archivos usando patrones glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -970,20 +974,20 @@ export const dict = {
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Ejecutar comandos de shell",
|
||||
"settings.permissions.tool.task.title": "Tarea",
|
||||
"settings.permissions.tool.task.description": "Lanzar sub-agentes",
|
||||
"settings.permissions.tool.task.description": "Iniciar subagentes",
|
||||
"settings.permissions.tool.skill.title": "Habilidad",
|
||||
"settings.permissions.tool.skill.description": "Cargar una habilidad por nombre",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Ejecutar consultas de servidor de lenguaje",
|
||||
"settings.permissions.tool.todowrite.title": "Escribir Todo",
|
||||
"settings.permissions.tool.todowrite.title": "Escribir tareas",
|
||||
"settings.permissions.tool.todowrite.description": "Actualizar la lista de tareas",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.title": "Obtener contenido web",
|
||||
"settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
|
||||
"settings.permissions.tool.websearch.title": "Búsqueda Web",
|
||||
"settings.permissions.tool.websearch.title": "Búsqueda web",
|
||||
"settings.permissions.tool.websearch.description": "Buscar en la web",
|
||||
"settings.permissions.tool.external_directory.title": "Directorio Externo",
|
||||
"settings.permissions.tool.external_directory.title": "Directorio externo",
|
||||
"settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
|
||||
"settings.permissions.tool.doom_loop.title": "Bucle Infinito",
|
||||
"settings.permissions.tool.doom_loop.title": "Bucle infinito",
|
||||
"settings.permissions.tool.doom_loop.description": "Detectar llamadas a herramientas repetidas con entrada idéntica",
|
||||
|
||||
"session.delete.failed.title": "Fallo al eliminar sesión",
|
||||
@@ -997,13 +1001,13 @@ export const dict = {
|
||||
"workspace.create.failed.title": "Fallo al crear espacio de trabajo",
|
||||
"workspace.delete.failed.title": "Fallo al eliminar espacio de trabajo",
|
||||
"workspace.resetting.title": "Restableciendo espacio de trabajo",
|
||||
"workspace.resetting.description": "Esto puede tomar un minuto.",
|
||||
"workspace.resetting.description": "Esto puede tardar un minuto.",
|
||||
"workspace.reset.failed.title": "Fallo al restablecer espacio de trabajo",
|
||||
"workspace.reset.success.title": "Espacio de trabajo restablecido",
|
||||
"workspace.reset.success.description": "El espacio de trabajo ahora coincide con la rama predeterminada.",
|
||||
"workspace.error.stillPreparing": "El espacio de trabajo aún se está preparando",
|
||||
"workspace.status.checking": "Comprobando cambios no fusionados...",
|
||||
"workspace.status.error": "No se pudo verificar el estado de git.",
|
||||
"workspace.status.error": "No se pudo verificar el estado de Git.",
|
||||
"workspace.status.clean": "No se detectaron cambios no fusionados.",
|
||||
"workspace.status.dirty": "Cambios no fusionados detectados en este espacio de trabajo.",
|
||||
"workspace.delete.title": "Eliminar espacio de trabajo",
|
||||
@@ -1045,6 +1049,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} de {{total}} preguntas",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorador de archivos",
|
||||
"session.header.reveal.finder": "Mostrar en Finder",
|
||||
"session.header.reveal.fileExplorer": "Mostrar en el Explorador de archivos",
|
||||
"session.header.reveal.containingFolder": "Abrir carpeta contenedora",
|
||||
"session.header.open.fileManager": "Gestor de archivos",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -1063,13 +1070,13 @@ export const dict = {
|
||||
"debugBar.na": "n/d",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Última transición de ruta completada tocando una página de sesión, medida desde el inicio del router hasta el primer pintado después de asentarse.",
|
||||
"Última transición de ruta completada que afecta a una página de sesión, medida desde el inicio del enrutador hasta el primer renderizado tras estabilizarse.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Cuadros por segundo en los últimos 5 segundos.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Peor tiempo de cuadro en los últimos 5 segundos.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Cuadros superiores a 32ms en los últimos 5 segundos.",
|
||||
"debugBar.jank.tip": "Cuadros superiores a 32 ms en los últimos 5 segundos.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Tiempo bloqueado y recuento de tareas largas en los últimos 5 segundos. Tarea máx: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
@@ -1078,7 +1085,7 @@ export const dict = {
|
||||
"debugBar.inp.tip":
|
||||
"Duración aproximada de la interacción en los últimos 5 segundos. Esto es similar a INP, no el INP oficial de Web Vitals.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Cambio de diseño acumulativo para la vida útil actual de la aplicación.",
|
||||
"debugBar.cls.tip": "Cambio de diseño acumulativo durante la ejecución actual de la aplicación.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Heap JS usado vs límite de heap. Solo Chromium.",
|
||||
"debugBar.mem.tip": "Heap JS usado vs límite de heap. {{used}} de {{limit}}.",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+100
-88
@@ -29,8 +29,8 @@ export const dict = {
|
||||
"command.settings.open": "Ouvrir les paramètres",
|
||||
"command.session.previous": "Session précédente",
|
||||
"command.session.next": "Session suivante",
|
||||
"command.session.previous.unseen": "Session non lue précédente",
|
||||
"command.session.next.unseen": "Session non lue suivante",
|
||||
"command.session.previous.unseen": "Session précédente non lue",
|
||||
"command.session.next.unseen": "Session suivante non lue",
|
||||
"command.session.archive": "Archiver la session",
|
||||
"command.palette": "Palette de commandes",
|
||||
"command.theme.cycle": "Changer de thème",
|
||||
@@ -45,7 +45,7 @@ export const dict = {
|
||||
"command.tab.reopenClosed": "Rouvrir l'onglet fermé",
|
||||
"command.context.addSelection": "Ajouter la sélection au contexte",
|
||||
"command.context.addSelection.description": "Ajouter les lignes sélectionnées du fichier actuel",
|
||||
"command.input.focus": "Focus sur l'entrée",
|
||||
"command.input.focus": "Activer la zone de saisie",
|
||||
"command.terminal.toggle": "Basculer le terminal",
|
||||
"command.fileTree.toggle": "Basculer l'arborescence des fichiers",
|
||||
"command.review.toggle": "Basculer la revue",
|
||||
@@ -59,8 +59,8 @@ export const dict = {
|
||||
"command.message.next.description": "Aller au message utilisateur suivant",
|
||||
"command.model.choose": "Choisir le modèle",
|
||||
"command.model.choose.description": "Sélectionner un modèle différent",
|
||||
"command.mcp.toggle": "Basculer MCP",
|
||||
"command.mcp.toggle.description": "Basculer les MCPs",
|
||||
"command.mcp.toggle": "Activer ou désactiver les MCP",
|
||||
"command.mcp.toggle.description": "Activer ou désactiver les MCP",
|
||||
"command.agent.cycle": "Changer d'agent",
|
||||
"command.agent.cycle.description": "Passer à l'agent suivant",
|
||||
"command.agent.cycle.reverse": "Changer d'agent (inverse)",
|
||||
@@ -68,7 +68,7 @@ export const dict = {
|
||||
"command.model.variant.cycle": "Changer l'effort de réflexion",
|
||||
"command.model.variant.cycle.description": "Passer au niveau d'effort suivant",
|
||||
"command.prompt.mode.shell": "Shell",
|
||||
"command.prompt.mode.normal": "Prompt",
|
||||
"command.prompt.mode.normal": "Invite",
|
||||
"command.permissions.autoaccept.enable": "Accepter automatiquement les permissions",
|
||||
"command.permissions.autoaccept.disable": "Arrêter d'accepter automatiquement les permissions",
|
||||
"command.workspace.toggle": "Basculer les espaces de travail",
|
||||
@@ -92,8 +92,9 @@ export const dict = {
|
||||
"palette.group.files": "Fichiers",
|
||||
"dialog.provider.search.placeholder": "Rechercher des fournisseurs",
|
||||
"dialog.provider.empty": "Aucun fournisseur trouvé",
|
||||
"dialog.provider.group.popular": "Populaire",
|
||||
"dialog.provider.group.other": "Autre",
|
||||
"dialog.provider.group.popular": "Populaires",
|
||||
"dialog.provider.group.other": "Autres",
|
||||
"dialog.provider.custom.label": "Fournisseur personnalisé compatible avec OpenAI",
|
||||
"dialog.provider.tag.recommended": "Recommandé",
|
||||
"dialog.provider.opencode.note": "Modèles sélectionnés incluant Claude, GPT, Gemini et plus",
|
||||
"dialog.provider.opencode.tagline": "Modèles optimisés et fiables",
|
||||
@@ -109,9 +110,10 @@ export const dict = {
|
||||
"dialog.model.empty": "Aucun résultat de modèle",
|
||||
"dialog.model.manage": "Gérer les modèles",
|
||||
"dialog.model.manage.description": "Personnalisez les modèles qui apparaissent dans le sélecteur.",
|
||||
"dialog.model.manage.provider.toggle": "Basculer tous les modèles {{provider}}",
|
||||
"dialog.model.manage.provider.toggle": "Activer ou désactiver tous les modèles {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Modèles gratuits fournis par OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Ajouter plus de modèles de fournisseurs populaires",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Voir plus de 70 autres fournisseurs",
|
||||
"dialog.provider.viewAll": "Voir plus de fournisseurs",
|
||||
"provider.connect.title": "Connecter {{provider}}",
|
||||
"provider.connect.title.anthropicProMax": "Connexion avec Claude Pro/Max",
|
||||
@@ -148,19 +150,21 @@ export const dict = {
|
||||
"provider.connect.toast.connected.title": "{{provider}} connecté",
|
||||
"provider.connect.toast.connected.description": "Les modèles {{provider}} sont maintenant disponibles.",
|
||||
"provider.custom.title": "Fournisseur personnalisé",
|
||||
"provider.custom.description.prefix": "Configurer un fournisseur compatible OpenAI. Voir la ",
|
||||
"provider.custom.description.link": "doc de config fournisseur",
|
||||
"provider.custom.description.prefix": "Configurez un fournisseur compatible avec OpenAI. Consultez la ",
|
||||
"provider.custom.description.link": "documentation sur la configuration des fournisseurs",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.field.providerID.label": "ID du fournisseur",
|
||||
"provider.custom.field.providerID.placeholder": "monfournisseur",
|
||||
"provider.custom.field.providerID.description": "Lettres minuscules, chiffres, traits d'union ou tirets bas",
|
||||
"provider.custom.field.providerID.description":
|
||||
"Lettres minuscules, chiffres, traits d'union ou traits de soulignement",
|
||||
"provider.custom.field.name.label": "Nom d'affichage",
|
||||
"provider.custom.field.name.placeholder": "Mon fournisseur IA",
|
||||
"provider.custom.field.baseURL.label": "URL de base",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.monfournisseur.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Clé API",
|
||||
"provider.custom.field.apiKey.placeholder": "Clé API",
|
||||
"provider.custom.field.apiKey.description": "Optionnel. Laisser vide si vous gérez l'auth via les en-têtes.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Facultatif. Laissez ce champ vide si vous gérez l'authentification au moyen des en-têtes.",
|
||||
"provider.custom.models.label": "Modèles",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "id-modele",
|
||||
@@ -176,7 +180,8 @@ export const dict = {
|
||||
"provider.custom.headers.remove": "Supprimer l'en-tête",
|
||||
"provider.custom.headers.add": "Ajouter un en-tête",
|
||||
"provider.custom.error.providerID.required": "L'ID du fournisseur est requis",
|
||||
"provider.custom.error.providerID.format": "Utilisez des lettres minuscules, chiffres, traits d'union ou tirets bas",
|
||||
"provider.custom.error.providerID.format":
|
||||
"Utilisez des lettres minuscules, des chiffres, des traits d'union ou des traits de soulignement",
|
||||
"provider.custom.error.providerID.exists": "Cet ID de fournisseur existe déjà",
|
||||
"provider.custom.error.name.required": "Le nom d'affichage est requis",
|
||||
"provider.custom.error.baseURL.required": "L'URL de base est requise",
|
||||
@@ -186,7 +191,7 @@ export const dict = {
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} déconnecté",
|
||||
"provider.disconnect.toast.disconnected.description": "Les modèles {{provider}} ne sont plus disponibles.",
|
||||
"model.tag.free": "Gratuit",
|
||||
"model.tag.latest": "Dernier",
|
||||
"model.tag.latest": "Le plus récent",
|
||||
"model.provider.anthropic": "Anthropic",
|
||||
"model.provider.openai": "OpenAI",
|
||||
"model.provider.google": "Google",
|
||||
@@ -197,29 +202,29 @@ export const dict = {
|
||||
"model.input.audio": "audio",
|
||||
"model.input.video": "vidéo",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Autorise : {{inputs}}",
|
||||
"model.tooltip.allows": "Formats pris en charge : {{inputs}}",
|
||||
"model.tooltip.context.label": "Contexte",
|
||||
"model.tooltip.inputs": "Entrées",
|
||||
"model.tooltip.model": "Modèle",
|
||||
"model.tooltip.provider": "Fournisseur",
|
||||
"model.tooltip.reasoning": "Raisonnement",
|
||||
"model.tooltip.reasoning.allowed": "Autorise le raisonnement",
|
||||
"model.tooltip.reasoning.allowed": "Raisonnement pris en charge",
|
||||
"model.tooltip.reasoning.none": "Sans raisonnement",
|
||||
"model.tooltip.context": "Limite de contexte {{limit}}",
|
||||
"common.search.placeholder": "Rechercher",
|
||||
"common.goBack": "Retour",
|
||||
"common.goForward": "Avancer",
|
||||
"common.goForward": "Suivant",
|
||||
"common.loading": "Chargement",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Annuler",
|
||||
"common.clear": "Effacer",
|
||||
"common.connect": "Connecter",
|
||||
"common.disconnect": "Déconnecter",
|
||||
"common.continue": "Soumettre",
|
||||
"common.continue": "Continuer",
|
||||
"common.submit": "Soumettre",
|
||||
"common.save": "Enregistrer",
|
||||
"common.saving": "Enregistrement...",
|
||||
"common.default": "Défaut",
|
||||
"common.default": "Par défaut",
|
||||
"common.attachment": "pièce jointe",
|
||||
"prompt.placeholder.shell": "Entrez une commande shell... {{example}}",
|
||||
"prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"',
|
||||
@@ -227,8 +232,8 @@ export const dict = {
|
||||
"prompt.placeholder.summarizeComments": "Résumer les commentaires…",
|
||||
"prompt.placeholder.summarizeComment": "Résumer le commentaire…",
|
||||
"prompt.mode.shell": "Shell",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc pour quitter",
|
||||
"prompt.mode.normal": "Invite",
|
||||
"prompt.mode.shell.exit": "Échap pour quitter",
|
||||
"session.child.promptDisabled": "Les sessions de sous-agents ne peuvent pas recevoir de messages.",
|
||||
"session.child.backToParent": "Revenir à la session principale.",
|
||||
"prompt.example.1": "Corriger un TODO dans la base de code",
|
||||
@@ -237,7 +242,7 @@ export const dict = {
|
||||
"prompt.example.4": "Expliquer comment fonctionne l'authentification",
|
||||
"prompt.example.5": "Trouver et corriger les vulnérabilités de sécurité",
|
||||
"prompt.example.6": "Ajouter des tests unitaires pour le service utilisateur",
|
||||
"prompt.example.7": "Refactoriser cette fonction pour être plus lisible",
|
||||
"prompt.example.7": "Refactoriser cette fonction pour la rendre plus lisible",
|
||||
"prompt.example.8": "Que signifie cette erreur ?",
|
||||
"prompt.example.9": "Aidez-moi à déboguer ce problème",
|
||||
"prompt.example.10": "Générer la documentation de l'API",
|
||||
@@ -245,11 +250,11 @@ export const dict = {
|
||||
"prompt.example.12": "Ajouter une validation d'entrée",
|
||||
"prompt.example.13": "Créer un nouveau composant pour...",
|
||||
"prompt.example.14": "Comment déployer ce projet ?",
|
||||
"prompt.example.15": "Vérifier mon code pour les meilleures pratiques",
|
||||
"prompt.example.15": "Vérifier que mon code respecte les bonnes pratiques",
|
||||
"prompt.example.16": "Ajouter la gestion des erreurs à cette fonction",
|
||||
"prompt.example.17": "Expliquer ce modèle regex",
|
||||
"prompt.example.17": "Expliquer cette expression régulière",
|
||||
"prompt.example.18": "Convertir ceci en TypeScript",
|
||||
"prompt.example.19": "Ajouter des logs dans toute la base de code",
|
||||
"prompt.example.19": "Ajouter une journalisation dans toute la base de code",
|
||||
"prompt.example.20": "Quelles dépendances sont obsolètes ?",
|
||||
"prompt.example.21": "Aidez-moi à écrire un script de migration",
|
||||
"prompt.example.22": "Implémenter la mise en cache pour ce point de terminaison",
|
||||
@@ -261,7 +266,7 @@ export const dict = {
|
||||
"prompt.dropzone.label": "Déposez des images, des PDF ou des fichiers texte ici",
|
||||
"prompt.dropzone.file.label": "Déposez pour @mentionner le fichier",
|
||||
"prompt.slash.badge.custom": "personnalisé",
|
||||
"prompt.slash.badge.skill": "skill",
|
||||
"prompt.slash.badge.skill": "compétence",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "actif",
|
||||
"prompt.context.includeActiveFile": "Inclure le fichier actif",
|
||||
@@ -277,24 +282,25 @@ export const dict = {
|
||||
"prompt.action.send": "Envoyer",
|
||||
"prompt.action.stop": "Arrêter",
|
||||
"prompt.toast.pasteUnsupported.title": "Pièce jointe non prise en charge",
|
||||
"prompt.toast.attachmentDuplicate.title": "Ce fichier a déjà été téléversé",
|
||||
"prompt.toast.pasteUnsupported.description":
|
||||
"Seules les images, les PDF ou les fichiers texte peuvent être joints ici.",
|
||||
"prompt.toast.modelAgentRequired.title": "Sélectionnez un agent et un modèle",
|
||||
"prompt.toast.modelAgentRequired.description": "Choisissez un agent et un modèle avant d'envoyer un message.",
|
||||
"prompt.toast.modelAgentRequired.description": "Choisissez un agent et un modèle avant d'envoyer une invite.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Échec de la création de l'arbre de travail",
|
||||
"prompt.toast.sessionCreateFailed.title": "Échec de la création de la session",
|
||||
"prompt.toast.shellSendFailed.title": "Échec de l'envoi de la commande shell",
|
||||
"prompt.toast.commandSendFailed.title": "Échec de l'envoi de la commande",
|
||||
"prompt.toast.promptSendFailed.title": "Échec de l'envoi du message",
|
||||
"prompt.toast.promptSendFailed.title": "Échec de l'envoi de l'invite",
|
||||
"prompt.toast.promptSendFailed.description": "Impossible de récupérer la session",
|
||||
"dialog.mcp.title": "MCPs",
|
||||
"dialog.mcp.description": "{{enabled}} sur {{total}} activés",
|
||||
"dialog.mcp.title": "MCP",
|
||||
"dialog.mcp.description": "MCP actifs : {{enabled}} sur {{total}}",
|
||||
"dialog.mcp.empty": "Aucun MCP configuré",
|
||||
"dialog.lsp.empty": "LSPs détectés automatiquement par type de fichier",
|
||||
"dialog.plugins.empty": "Plugins configurés dans opencode.json",
|
||||
"dialog.lsp.empty": "Serveurs LSP détectés automatiquement selon le type de fichier",
|
||||
"dialog.plugins.empty": "Extensions configurées dans opencode.json",
|
||||
"mcp.status.connected": "connecté",
|
||||
"mcp.status.failed": "échoué",
|
||||
"mcp.status.needs_auth": "nécessite auth",
|
||||
"mcp.status.failed": "en échec",
|
||||
"mcp.status.needs_auth": "authentification requise",
|
||||
"mcp.auth.clickToAuthenticate": "Cliquez pour vous authentifier",
|
||||
"mcp.status.disabled": "désactivé",
|
||||
"dialog.fork.empty": "Aucun message à partir duquel bifurquer",
|
||||
@@ -329,7 +335,7 @@ export const dict = {
|
||||
"dialog.server.action.remove": "Supprimer le serveur",
|
||||
"dialog.server.menu.edit": "Modifier",
|
||||
"dialog.server.menu.default": "Définir par défaut",
|
||||
"dialog.server.menu.defaultRemove": "Supprimer par défaut",
|
||||
"dialog.server.menu.defaultRemove": "Ne plus utiliser par défaut",
|
||||
"dialog.server.menu.delete": "Supprimer",
|
||||
"dialog.server.current": "Serveur actuel",
|
||||
"dialog.server.status.default": "Défaut",
|
||||
@@ -399,8 +405,8 @@ export const dict = {
|
||||
"wsl.onboarding.notFound": "introuvable",
|
||||
"wsl.onboarding.version": "Version : {{version}}",
|
||||
"wsl.onboarding.unknown": "inconnue",
|
||||
"wsl.onboarding.desktopVersion": "desktop {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "La version installée ne correspond pas à celle de l'application desktop.",
|
||||
"wsl.onboarding.desktopVersion": "application de bureau {{version}}",
|
||||
"wsl.onboarding.versionMismatch": "La version installée ne correspond pas à celle de l'application de bureau.",
|
||||
"wsl.onboarding.adding": "Ajout...",
|
||||
"dialog.project.edit.title": "Modifier le projet",
|
||||
"dialog.project.edit.name": "Nom",
|
||||
@@ -416,13 +422,13 @@ export const dict = {
|
||||
"dialog.project.edit.worktree.startup.placeholder": "p. ex. bun install",
|
||||
"context.breakdown.title": "Répartition du contexte",
|
||||
"context.breakdown.note":
|
||||
"Répartition approximative des jetons d'entrée. \"Autre\" inclut les définitions d'outils et les frais généraux.",
|
||||
"Répartition approximative des jetons d'entrée. \"Autre\" inclut les définitions d'outils et les données annexes.",
|
||||
"context.breakdown.system": "Système",
|
||||
"context.breakdown.user": "Utilisateur",
|
||||
"context.breakdown.assistant": "Assistant",
|
||||
"context.breakdown.tool": "Appels d'outils",
|
||||
"context.breakdown.other": "Autre",
|
||||
"context.systemPrompt.title": "Prompt système",
|
||||
"context.systemPrompt.title": "Invite système",
|
||||
"context.rawMessages.title": "Messages bruts",
|
||||
"context.stats.session": "Session",
|
||||
"context.stats.messages": "Messages",
|
||||
@@ -436,7 +442,7 @@ export const dict = {
|
||||
"context.stats.reasoningTokens": "Jetons de raisonnement",
|
||||
"context.stats.cacheTokens": "Jetons de cache (lecture/écriture)",
|
||||
"context.stats.userMessages": "Messages utilisateur",
|
||||
"context.stats.assistantMessages": "Messages assistant",
|
||||
"context.stats.assistantMessages": "Messages de l'assistant",
|
||||
"context.stats.totalCost": "Coût total",
|
||||
"context.stats.sessionCreated": "Session créée",
|
||||
"context.stats.lastActivity": "Dernière activité",
|
||||
@@ -464,16 +470,16 @@ export const dict = {
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Langue",
|
||||
"toast.language.description": "Passé à {{language}}",
|
||||
"toast.language.description": "Langue définie sur {{language}}",
|
||||
"toast.theme.title": "Thème changé",
|
||||
"toast.scheme.title": "Schéma de couleurs",
|
||||
"toast.workspace.enabled.title": "Espaces de travail activés",
|
||||
"toast.workspace.enabled.description": "Plusieurs worktrees sont désormais affichés dans la barre latérale",
|
||||
"toast.workspace.enabled.description": "Plusieurs arbres de travail sont désormais affichés dans la barre latérale",
|
||||
"toast.workspace.disabled.title": "Espaces de travail désactivés",
|
||||
"toast.workspace.disabled.description": "Seul le worktree principal est affiché dans la barre latérale",
|
||||
"toast.workspace.disabled.description": "Seul l'arbre de travail principal est affiché dans la barre latérale",
|
||||
"toast.permissions.autoaccept.on.title": "Acceptation automatique des permissions",
|
||||
"toast.permissions.autoaccept.on.description": "Les demandes de permission seront approuvées automatiquement",
|
||||
"toast.permissions.autoaccept.off.title": "Acceptation automatique des permissions arrêtée",
|
||||
"toast.permissions.autoaccept.off.title": "Acceptation automatique des permissions désactivée",
|
||||
"toast.permissions.autoaccept.off.description": "Les demandes de permission nécessiteront une approbation",
|
||||
"toast.model.none.title": "Aucun modèle sélectionné",
|
||||
"toast.model.none.description": "Connectez un fournisseur pour résumer cette session",
|
||||
@@ -486,17 +492,18 @@ export const dict = {
|
||||
"toast.session.share.success.description": "URL de partage copiée dans le presse-papiers !",
|
||||
"toast.session.share.failed.title": "Échec du partage de la session",
|
||||
"toast.session.share.failed.description": "Une erreur s'est produite lors du partage de la session",
|
||||
"toast.session.unshare.success.title": "Session non partagée",
|
||||
"toast.session.unshare.success.description": "Session non partagée avec succès !",
|
||||
"toast.session.unshare.failed.title": "Échec de l'annulation du partage",
|
||||
"toast.session.unshare.failed.description": "Une erreur s'est produite lors de l'annulation du partage de la session",
|
||||
"toast.session.unshare.success.title": "Partage de la session désactivé",
|
||||
"toast.session.unshare.success.description": "Le partage de la session a bien été désactivé.",
|
||||
"toast.session.unshare.failed.title": "Échec de la désactivation du partage",
|
||||
"toast.session.unshare.failed.description":
|
||||
"Une erreur s'est produite lors de la désactivation du partage de la session",
|
||||
"toast.session.listFailed.title": "Échec du chargement des sessions pour {{project}}",
|
||||
"toast.update.title": "Mise à jour disponible",
|
||||
"toast.update.description":
|
||||
"Une nouvelle version d'OpenCode ({{version}}) est maintenant disponible pour installation.",
|
||||
"toast.update.action.installRestart": "Installer et redémarrer",
|
||||
"toast.update.action.notYet": "Pas encore",
|
||||
"error.page.title": "Quelque chose s'est mal passé",
|
||||
"toast.update.action.notYet": "Plus tard",
|
||||
"error.page.title": "Un problème est survenu",
|
||||
"error.page.description": "Une erreur s'est produite lors du chargement de l'application.",
|
||||
"error.page.description.localServerStartup": "Une erreur s'est produite lors du démarrage du serveur local.",
|
||||
"error.page.details.label": "Détails de l'erreur",
|
||||
@@ -519,13 +526,13 @@ export const dict = {
|
||||
"error.chain.causedBy": "Causé par :",
|
||||
"error.chain.apiError": "Erreur API",
|
||||
"error.chain.status": "Statut : {{status}}",
|
||||
"error.chain.retryable": "Réessayable : {{retryable}}",
|
||||
"error.chain.retryable": "Nouvelle tentative possible : {{retryable}}",
|
||||
"error.chain.responseBody": "Corps de la réponse :\n{{body}}",
|
||||
"error.chain.didYouMean": "Vouliez-vous dire : {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Modèle introuvable : {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Vérifiez votre configuration (opencode.json) pour les noms de fournisseur/modèle",
|
||||
"error.chain.checkConfig": "Vérifiez les noms de fournisseur et de modèle dans votre configuration (opencode.json)",
|
||||
"error.chain.mcpFailed":
|
||||
"Le serveur MCP \"{{name}}\" a échoué. Notez qu'OpenCode ne supporte pas encore l'authentification MCP.",
|
||||
"Le serveur MCP \"{{name}}\" a échoué. Notez qu'OpenCode ne prend pas encore en charge l'authentification MCP.",
|
||||
"error.chain.providerAuthFailed": "Échec de l'authentification du fournisseur ({{provider}}) : {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'Échec de l\'initialisation du fournisseur "{{provider}}". Vérifiez les identifiants et la configuration.',
|
||||
@@ -534,7 +541,7 @@ export const dict = {
|
||||
"Le fichier de configuration à {{path}} n'est pas un JSON(C) valide : {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Le répertoire "{{dir}}" dans {{path}} n\'est pas valide. Renommez le répertoire en "{{suggestion}}" ou supprimez-le. C\'est une faute de frappe courante.',
|
||||
"error.chain.configFrontmatterError": "Échec de l'analyse du frontmatter dans {{path}} :\n{{message}}",
|
||||
"error.chain.configFrontmatterError": "Échec de l'analyse de l'en-tête dans {{path}} :\n{{message}}",
|
||||
"error.chain.configInvalid": "Le fichier de configuration à {{path}} est invalide",
|
||||
"error.chain.configInvalidWithMessage": "Le fichier de configuration à {{path}} est invalide : {{message}}",
|
||||
"notification.permission.title": "Permission requise",
|
||||
@@ -582,7 +589,7 @@ export const dict = {
|
||||
"session.review.noChanges": "Aucune modification",
|
||||
"session.review.noUncommittedChanges": "Aucune modification non validée pour l'instant",
|
||||
"session.review.noBranchChanges": "Aucune modification de branche pour l'instant",
|
||||
"session.review.noVcs": "Aucun système de contrôle de version Git détecté, modifications non affichées",
|
||||
"session.review.noVcs": "Aucun système de gestion de versions Git détecté ; modifications non affichées",
|
||||
"session.review.noSnapshot":
|
||||
"Le suivi des instantanés est désactivé dans la configuration, les modifications de session sont donc indisponibles",
|
||||
"session.files.selectToOpen": "Sélectionnez un fichier à ouvrir",
|
||||
@@ -593,7 +600,7 @@ export const dict = {
|
||||
"session.messages.loadingEarlier": "Chargement des messages précédents...",
|
||||
"session.messages.loadEarlier": "Charger les messages précédents",
|
||||
"session.messages.loading": "Chargement des messages...",
|
||||
"session.messages.jumpToLatest": "Aller au dernier",
|
||||
"session.messages.jumpToLatest": "Aller au message le plus récent",
|
||||
"session.context.addToContext": "Ajouter {{selection}} au contexte",
|
||||
"session.todo.title": "Tâches",
|
||||
"session.todo.collapse": "Réduire",
|
||||
@@ -634,17 +641,17 @@ export const dict = {
|
||||
"session.header.open.menu": "Options d'ouverture",
|
||||
"session.header.open.copyPath": "Copier le chemin",
|
||||
"status.popover.trigger": "Statut",
|
||||
"status.popover.ariaLabel": "Configurations des serveurs",
|
||||
"status.popover.ariaLabel": "Configuration des serveurs",
|
||||
"status.popover.tab.servers": "Serveurs",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Plugins",
|
||||
"status.popover.action.manageServers": "Gérer les serveurs",
|
||||
"session.share.popover.title": "Publier sur le web",
|
||||
"session.share.popover.title": "Publier sur le Web",
|
||||
"session.share.popover.description.shared":
|
||||
"Cette session est publique sur le web. Elle est accessible à toute personne disposant du lien.",
|
||||
"Cette session est publique sur le Web. Elle est accessible à toute personne disposant du lien.",
|
||||
"session.share.popover.description.unshared":
|
||||
"Partager la session publiquement sur le web. Elle sera accessible à toute personne disposant du lien.",
|
||||
"Rendre la session publique sur le Web. Elle sera accessible à toute personne disposant du lien.",
|
||||
"session.share.action.share": "Partager",
|
||||
"session.share.action.publish": "Publier",
|
||||
"session.share.action.publishing": "Publication...",
|
||||
@@ -655,7 +662,7 @@ export const dict = {
|
||||
"session.share.copy.copyLink": "Copier le lien",
|
||||
"lsp.tooltip.none": "Aucun serveur LSP",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
"prompt.loading": "Chargement du prompt...",
|
||||
"prompt.loading": "Chargement de l'invite...",
|
||||
"terminal.loading": "Chargement du terminal...",
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
@@ -693,7 +700,7 @@ export const dict = {
|
||||
"sidebar.empty.title": "Aucun projet ouvert",
|
||||
"sidebar.empty.description": "Ouvrez un projet pour commencer",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Bureau",
|
||||
"settings.section.desktop": "Application de bureau",
|
||||
"settings.section.server": "Serveur",
|
||||
"settings.tab.general": "Général",
|
||||
"settings.tab.shortcuts": "Raccourcis",
|
||||
@@ -709,10 +716,10 @@ export const dict = {
|
||||
"settings.general.section.display": "Affichage",
|
||||
"settings.general.row.language.title": "Langue",
|
||||
"settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode",
|
||||
"settings.general.row.shell.title": "Shell du terminal",
|
||||
"settings.general.row.shell.title": "Interpréteur de commandes du terminal",
|
||||
"settings.general.row.shell.description":
|
||||
"Choisissez le shell utilisé par votre terminal. Les shells compatibles sont également utilisés pour les appels d'outils de l'agent.",
|
||||
"settings.general.row.shell.autoDefault": "Auto (par défaut)",
|
||||
"settings.general.row.shell.autoDefault": "Automatique (par défaut)",
|
||||
"settings.general.row.shell.terminalOnly": "terminal uniquement",
|
||||
"settings.general.row.appearance.title": "Apparence",
|
||||
"settings.general.row.appearance.description": "Personnaliser l'apparence d'OpenCode sur votre appareil",
|
||||
@@ -726,22 +733,22 @@ export const dict = {
|
||||
"settings.general.row.terminalFont.description": "Personnalisez la police utilisée dans le terminal",
|
||||
"settings.general.row.uiFont.title": "Police de l'interface",
|
||||
"settings.general.row.uiFont.description": "Personnaliser la police utilisée dans toute l'interface",
|
||||
"settings.general.row.followup.title": "Comportement de suivi",
|
||||
"settings.general.row.followup.title": "Comportement des messages de suivi",
|
||||
"settings.general.row.followup.description":
|
||||
"Choisissez si les messages de suivi dirigent immédiatement ou attendent dans une file d'attente",
|
||||
"Choisissez si les messages de suivi orientent immédiatement l'agent ou sont placés dans une file d'attente",
|
||||
"settings.general.row.followup.option.queue": "File d'attente",
|
||||
"settings.general.row.followup.option.steer": "Diriger",
|
||||
"settings.general.row.followup.option.steer": "Orienter",
|
||||
"settings.general.row.showFileTree.title": "Arborescence des fichiers",
|
||||
"settings.general.row.showFileTree.description": "Afficher le panneau d'arborescence des fichiers dans les sessions",
|
||||
"settings.general.row.showNavigation.title": "Commandes de navigation",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Afficher les boutons précédent et suivant dans la barre de titre de l'application desktop",
|
||||
"Afficher les boutons précédent et suivant dans la barre de titre de l'application de bureau",
|
||||
"settings.general.row.showSearch.title": "Palette de commandes",
|
||||
"settings.general.row.showSearch.description":
|
||||
"Afficher le bouton de recherche et de palette de commandes dans la barre de titre",
|
||||
"settings.general.row.showTerminal.title": "Terminal",
|
||||
"settings.general.row.showTerminal.description":
|
||||
"Afficher le bouton du terminal dans la barre de titre de l'application desktop",
|
||||
"Afficher le bouton du terminal dans la barre de titre de l'application de bureau",
|
||||
"settings.general.row.showStatus.title": "État du serveur",
|
||||
"settings.general.row.showStatus.description": "Afficher le bouton d'état du serveur dans la barre de titre",
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigation en bas",
|
||||
@@ -773,11 +780,13 @@ export const dict = {
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Sur Linux avec des moniteurs à taux de rafraîchissement mixte, Wayland natif peut être plus stable.",
|
||||
"settings.general.row.releaseNotes.title": "Notes de version",
|
||||
"settings.general.row.releaseNotes.description": 'Afficher des pop-ups "Quoi de neuf" après les mises à jour',
|
||||
"settings.general.row.releaseNotes.description":
|
||||
"Afficher les fenêtres contextuelles « Quoi de neuf » après les mises à jour",
|
||||
"settings.updates.row.startup.title": "Vérifier les mises à jour au démarrage",
|
||||
"settings.updates.row.startup.description": "Vérifier automatiquement les mises à jour au lancement d'OpenCode",
|
||||
"settings.updates.row.check.title": "Vérifier les mises à jour",
|
||||
"settings.updates.row.check.description": "Vérifier manuellement les mises à jour et installer si disponible",
|
||||
"settings.updates.row.check.description":
|
||||
"Rechercher manuellement les mises à jour et installer toute mise à jour disponible",
|
||||
"settings.updates.action.checkNow": "Vérifier maintenant",
|
||||
"settings.updates.action.checking": "Vérification...",
|
||||
"settings.updates.action.downloading": "Téléchargement...",
|
||||
@@ -845,12 +854,12 @@ export const dict = {
|
||||
"settings.general.sounds.errors.title": "Erreurs",
|
||||
"settings.general.sounds.errors.description": "Jouer un son lorsqu'une erreur se produit",
|
||||
"settings.shortcuts.title": "Raccourcis clavier",
|
||||
"settings.shortcuts.reset.button": "Rétablir les défauts",
|
||||
"settings.shortcuts.reset.button": "Rétablir les valeurs par défaut",
|
||||
"settings.shortcuts.reset.toast.title": "Raccourcis réinitialisés",
|
||||
"settings.shortcuts.reset.toast.description": "Les raccourcis clavier ont été réinitialisés aux valeurs par défaut.",
|
||||
"settings.shortcuts.conflict.title": "Raccourci déjà utilisé",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} est déjà assigné à {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Non assigné",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} est déjà attribué à {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Non attribué",
|
||||
"settings.shortcuts.pressKeys": "Appuyez sur les touches",
|
||||
"settings.shortcuts.search.placeholder": "Rechercher des raccourcis",
|
||||
"settings.shortcuts.search.empty": "Aucun raccourci trouvé",
|
||||
@@ -859,7 +868,7 @@ export const dict = {
|
||||
"settings.shortcuts.group.navigation": "Navigation",
|
||||
"settings.shortcuts.group.modelAndAgent": "Modèle et agent",
|
||||
"settings.shortcuts.group.terminal": "Terminal",
|
||||
"settings.shortcuts.group.prompt": "Prompt",
|
||||
"settings.shortcuts.group.prompt": "Invite",
|
||||
"settings.providers.title": "Fournisseurs",
|
||||
"settings.providers.description": "Les paramètres des fournisseurs seront configurables ici.",
|
||||
"settings.providers.section.connected": "Fournisseurs connectés",
|
||||
@@ -888,9 +897,9 @@ export const dict = {
|
||||
"settings.permissions.tool.read.description": "Lecture d'un fichier (correspond au chemin du fichier)",
|
||||
"settings.permissions.tool.edit.title": "Modifier",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modifier des fichiers, y compris les modifications, écritures, patchs et multi-modifications",
|
||||
"Modifier des fichiers, notamment au moyen des outils edit, write et patch",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Correspondre aux fichiers utilisant des modèles glob",
|
||||
"settings.permissions.tool.glob.description": "Rechercher des fichiers à l'aide de motifs glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description":
|
||||
"Rechercher dans le contenu des fichiers à l'aide d'expressions régulières",
|
||||
@@ -904,12 +913,12 @@ export const dict = {
|
||||
"settings.permissions.tool.skill.description": "Charger une compétence par son nom",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Exécuter des requêtes de serveur de langage",
|
||||
"settings.permissions.tool.todowrite.title": "Écrire Todo",
|
||||
"settings.permissions.tool.todowrite.title": "Écrire les tâches",
|
||||
"settings.permissions.tool.todowrite.description": "Mettre à jour la liste de tâches",
|
||||
"settings.permissions.tool.webfetch.title": "Récupération Web",
|
||||
"settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
|
||||
"settings.permissions.tool.websearch.title": "Recherche Web",
|
||||
"settings.permissions.tool.websearch.description": "Rechercher sur le web",
|
||||
"settings.permissions.tool.websearch.description": "Rechercher sur le Web",
|
||||
"settings.permissions.tool.external_directory.title": "Répertoire externe",
|
||||
"settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet",
|
||||
"settings.permissions.tool.doom_loop.title": "Boucle infernale",
|
||||
@@ -930,7 +939,7 @@ export const dict = {
|
||||
"workspace.reset.success.description": "L'espace de travail correspond maintenant à la branche par défaut.",
|
||||
"workspace.error.stillPreparing": "L'espace de travail est encore en cours de préparation",
|
||||
"workspace.status.checking": "Vérification des modifications non fusionnées...",
|
||||
"workspace.status.error": "Impossible de vérifier le statut git.",
|
||||
"workspace.status.error": "Impossible de vérifier l'état de Git.",
|
||||
"workspace.status.clean": "Aucune modification non fusionnée détectée.",
|
||||
"workspace.status.dirty": "Modifications non fusionnées détectées dans cet espace de travail.",
|
||||
"workspace.delete.title": "Supprimer l'espace de travail",
|
||||
@@ -952,9 +961,9 @@ export const dict = {
|
||||
"error.server.invalidConfiguration": "Configuration invalide",
|
||||
"common.moreCountSuffix": " (+{{count}} de plus)",
|
||||
"common.time.justNow": "À l'instant",
|
||||
"common.time.minutesAgo.short": "il y a {{count}}m",
|
||||
"common.time.hoursAgo.short": "il y a {{count}}h",
|
||||
"common.time.daysAgo.short": "il y a {{count}}j",
|
||||
"common.time.minutesAgo.short": "il y a {{count}} min",
|
||||
"common.time.hoursAgo.short": "il y a {{count}} h",
|
||||
"common.time.daysAgo.short": "il y a {{count}} j",
|
||||
"settings.providers.connected.environmentDescription": "Connecté à partir de vos variables d'environnement",
|
||||
"settings.providers.custom.description": "Ajouter un fournisseur compatible avec OpenAI via l'URL de base.",
|
||||
|
||||
@@ -972,6 +981,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} questions sur {{total}}",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Explorateur de fichiers",
|
||||
"session.header.reveal.finder": "Afficher dans le Finder",
|
||||
"session.header.reveal.fileExplorer": "Afficher dans l’Explorateur de fichiers",
|
||||
"session.header.reveal.containingFolder": "Ouvrir le dossier contenant",
|
||||
"session.header.open.fileManager": "Gestionnaire de fichiers",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -992,11 +1004,11 @@ export const dict = {
|
||||
"debugBar.nav.tip":
|
||||
"Dernière transition de route terminée touchant une page de session, mesurée du début du routeur jusqu'au premier affichage après stabilisation.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Images par seconde glissantes sur les 5 dernières secondes.",
|
||||
"debugBar.fps.tip": "Fréquence d'images calculée sur les 5 dernières secondes.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Pire temps d'image sur les 5 dernières secondes.",
|
||||
"debugBar.frame.tip": "Durée d'image maximale sur les 5 dernières secondes.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Images de plus de 32ms au cours des 5 dernières secondes.",
|
||||
"debugBar.jank.tip": "Images de plus de 32 ms au cours des 5 dernières secondes.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip":
|
||||
"Temps bloqué et nombre de tâches longues au cours des 5 dernières secondes. Tâche max : {{max}}.",
|
||||
@@ -1004,7 +1016,7 @@ export const dict = {
|
||||
"debugBar.delay.tip": "Pire délai d'entrée observé au cours des 5 dernières secondes.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Durée approximative d'interaction au cours des 5 dernières secondes. Ceci est similaire à INP, pas le INP officiel des Web Vitals.",
|
||||
"Durée approximative d'interaction au cours des 5 dernières secondes. Il s'agit d'une mesure similaire à l'INP, et non de l'INP officiel des Web Vitals.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Décalage cumulatif de la mise en page pour la durée de vie actuelle de l'application.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+25
-17
@@ -94,13 +94,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": "ProやMaxを含むClaudeモデルに直接アクセス",
|
||||
"dialog.provider.copilot.note": "GitHub Copilotを通じてコーディングを支援するAIモデル",
|
||||
"dialog.provider.openai.note": "高速で高性能な汎用AIタスク向けのGPTモデル",
|
||||
"dialog.provider.google.note": "高速で構造化された応答のためのGeminiモデル",
|
||||
"dialog.provider.openrouter.note": "1つのプロバイダーからすべてのサポートされているモデルにアクセス",
|
||||
"dialog.provider.vercel.note": "スマートルーターによるAIモデルへの統合アクセス",
|
||||
@@ -112,6 +113,7 @@ 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.anthropicProMax": "Claude Pro/Maxでログイン",
|
||||
@@ -195,7 +197,7 @@ 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": "入力",
|
||||
@@ -214,7 +216,7 @@ export const dict = {
|
||||
"common.clear": "クリア",
|
||||
"common.connect": "接続",
|
||||
"common.disconnect": "切断",
|
||||
"common.continue": "送信",
|
||||
"common.continue": "続行",
|
||||
"common.submit": "送信",
|
||||
"common.save": "保存",
|
||||
"common.saving": "保存中...",
|
||||
@@ -258,7 +260,7 @@ export const dict = {
|
||||
"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",
|
||||
@@ -276,6 +278,7 @@ export const dict = {
|
||||
"prompt.action.send": "送信",
|
||||
"prompt.action.stop": "停止",
|
||||
"prompt.toast.pasteUnsupported.title": "サポートされていない添付ファイル",
|
||||
"prompt.toast.attachmentDuplicate.title": "このファイルはすでにアップロードされています",
|
||||
"prompt.toast.pasteUnsupported.description": "画像、PDF、またはテキストファイルのみ添付できます。",
|
||||
"prompt.toast.modelAgentRequired.title": "エージェントとモデルを選択",
|
||||
"prompt.toast.modelAgentRequired.description": "プロンプトを送信する前にエージェントとモデルを選択してください。",
|
||||
@@ -566,7 +569,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": "変更を読み込み中...",
|
||||
@@ -656,10 +659,10 @@ export const dict = {
|
||||
"ターミナルの接続が中断されました。これはサーバーが再起動したときに発生することがあります。",
|
||||
"common.closeTab": "タブを閉じる",
|
||||
"common.dismiss": "閉じる",
|
||||
"common.requestFailed": "リクエスト失敗",
|
||||
"common.requestFailed": "リクエストに失敗しました",
|
||||
"common.moreOptions": "その他のオプション",
|
||||
"common.learnMore": "詳細",
|
||||
"common.rename": "名前変更",
|
||||
"common.rename": "名前を変更",
|
||||
"common.reset": "リセット",
|
||||
"common.archive": "アーカイブ",
|
||||
"common.delete": "削除",
|
||||
@@ -733,8 +736,9 @@ 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.showCustomAgents.title": "エージェントを表示",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"コンポーザーでエージェントを切り替えます。非表示の場合は、デフォルトでBuildエージェントが使用されます。",
|
||||
"settings.general.row.reasoningSummaries.title": "推論の要約を表示",
|
||||
"settings.general.row.reasoningSummaries.description": "タイムラインにモデルの推論の要約を表示します",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell ツールパーツを展開",
|
||||
@@ -845,7 +849,7 @@ export const dict = {
|
||||
"settings.shortcuts.group.terminal": "ターミナル",
|
||||
"settings.shortcuts.group.prompt": "プロンプト",
|
||||
"settings.providers.title": "プロバイダー",
|
||||
"settings.providers.description": "プロバイダー設定はここで構成できます。",
|
||||
"settings.providers.description": "プロバイダー設定は今後ここで構成できるようになります。",
|
||||
"settings.providers.section.connected": "接続済みプロバイダー",
|
||||
"settings.providers.connected.empty": "接続済みプロバイダーはありません",
|
||||
"settings.providers.section.popular": "人気のプロバイダー",
|
||||
@@ -854,13 +858,13 @@ export const dict = {
|
||||
"settings.providers.tag.custom": "カスタム",
|
||||
"settings.providers.tag.other": "その他",
|
||||
"settings.models.title": "モデル",
|
||||
"settings.models.description": "モデル設定はここで構成できます。",
|
||||
"settings.models.description": "モデル設定は今後ここで構成できるようになります。",
|
||||
"settings.agents.title": "エージェント",
|
||||
"settings.agents.description": "エージェント設定はここで構成できます。",
|
||||
"settings.agents.description": "エージェント設定は今後ここで構成できるようになります。",
|
||||
"settings.commands.title": "コマンド",
|
||||
"settings.commands.description": "コマンド設定はここで構成できます。",
|
||||
"settings.commands.description": "コマンド設定は今後ここで構成できるようになります。",
|
||||
"settings.mcp.title": "MCP",
|
||||
"settings.mcp.description": "MCP設定はここで構成できます。",
|
||||
"settings.mcp.description": "MCP設定は今後ここで構成できるようになります。",
|
||||
"settings.permissions.title": "権限",
|
||||
"settings.permissions.description": "サーバーがデフォルトで使用できるツールを制御します。",
|
||||
"settings.permissions.section.tools": "ツール",
|
||||
@@ -954,6 +958,9 @@ export const dict = {
|
||||
"session.question.progress": "{{total}} 問中 {{current}} 問",
|
||||
"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",
|
||||
@@ -971,7 +978,8 @@ export const dict = {
|
||||
"debugBar.ariaLabel": "開発パフォーマンス診断",
|
||||
"debugBar.na": "n/a",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip": "セッションページに触れる最後に完了したルート遷移。ルーター開始から安定後の最初の描画まで測定。",
|
||||
"debugBar.nav.tip":
|
||||
"セッションページに関係する最後に完了したルート遷移。ルーターの開始から処理完了後の最初の描画までを測定。",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "過去5秒間のローリングフレーム/秒。",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
|
||||
+30
-23
@@ -19,7 +19,7 @@ export const dict = {
|
||||
"theme.scheme.system": "시스템",
|
||||
"theme.scheme.light": "라이트",
|
||||
"theme.scheme.dark": "다크",
|
||||
"command.sidebar.toggle": "사이드바 토글",
|
||||
"command.sidebar.toggle": "사이드바 표시/숨기기",
|
||||
"command.project.open": "프로젝트 열기",
|
||||
"command.provider.connect": "공급자 연결",
|
||||
"command.server.switch": "서버 전환",
|
||||
@@ -42,12 +42,12 @@ export const dict = {
|
||||
"command.context.addSelection": "선택 영역을 컨텍스트에 추가",
|
||||
"command.context.addSelection.description": "현재 파일에서 선택한 줄을 추가",
|
||||
"command.input.focus": "입력창 포커스",
|
||||
"command.terminal.toggle": "터미널 토글",
|
||||
"command.fileTree.toggle": "파일 트리 토글",
|
||||
"command.review.toggle": "검토 토글",
|
||||
"command.terminal.toggle": "터미널 표시/숨기기",
|
||||
"command.fileTree.toggle": "파일 트리 표시/숨기기",
|
||||
"command.review.toggle": "검토 표시/숨기기",
|
||||
"command.terminal.new": "새 터미널",
|
||||
"command.terminal.new.description": "새 터미널 탭 생성",
|
||||
"command.steps.toggle": "단계 토글",
|
||||
"command.steps.toggle": "단계 표시/숨기기",
|
||||
"command.steps.toggle.description": "현재 메시지의 단계 표시/숨기기",
|
||||
"command.message.previous": "이전 메시지",
|
||||
"command.message.previous.description": "이전 사용자 메시지로 이동",
|
||||
@@ -55,8 +55,8 @@ export const dict = {
|
||||
"command.message.next.description": "다음 사용자 메시지로 이동",
|
||||
"command.model.choose": "모델 선택",
|
||||
"command.model.choose.description": "다른 모델 선택",
|
||||
"command.mcp.toggle": "MCP 토글",
|
||||
"command.mcp.toggle.description": "MCP 토글",
|
||||
"command.mcp.toggle": "MCP 전환",
|
||||
"command.mcp.toggle.description": "MCP 전환",
|
||||
"command.agent.cycle": "에이전트 순환",
|
||||
"command.agent.cycle.description": "다음 에이전트로 전환",
|
||||
"command.agent.cycle.reverse": "에이전트 역순환",
|
||||
@@ -90,13 +90,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": "Pro 및 Max를 포함한 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 모델 통합 액세스",
|
||||
@@ -108,6 +109,7 @@ 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.anthropicProMax": "Claude Pro/Max로 로그인",
|
||||
@@ -191,7 +193,7 @@ 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.reasoning.allowed": "추론 허용",
|
||||
"model.tooltip.reasoning.none": "추론 없음",
|
||||
@@ -204,7 +206,7 @@ export const dict = {
|
||||
"common.cancel": "취소",
|
||||
"common.connect": "연결",
|
||||
"common.disconnect": "연결 해제",
|
||||
"common.continue": "제출",
|
||||
"common.continue": "계속",
|
||||
"common.submit": "제출",
|
||||
"common.save": "저장",
|
||||
"common.saving": "저장 중...",
|
||||
@@ -254,7 +256,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": "명령어",
|
||||
@@ -264,6 +266,7 @@ export const dict = {
|
||||
"prompt.action.send": "전송",
|
||||
"prompt.action.stop": "중지",
|
||||
"prompt.toast.pasteUnsupported.title": "지원되지 않는 첨부 파일",
|
||||
"prompt.toast.attachmentDuplicate.title": "이 파일은 이미 업로드되었습니다",
|
||||
"prompt.toast.pasteUnsupported.description": "이미지, PDF 또는 텍스트 파일만 첨부할 수 있습니다.",
|
||||
"prompt.toast.modelAgentRequired.title": "에이전트 및 모델 선택",
|
||||
"prompt.toast.modelAgentRequired.description": "프롬프트를 보내기 전에 에이전트와 모델을 선택하세요.",
|
||||
@@ -588,15 +591,15 @@ export const dict = {
|
||||
"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.followup.option.steer": "즉시 반영",
|
||||
"settings.general.row.reasoningSummaries.title": "추론 요약 표시",
|
||||
"settings.general.row.reasoningSummaries.description": "타임라인에 모델 추론 요약 표시",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell 도구 파트 펼치기",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell 도구 항목 펼치기",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"타임라인에서 기본적으로 shell 도구 파트를 펼친 상태로 표시합니다",
|
||||
"settings.general.row.editToolPartsExpanded.title": "edit 도구 파트 펼치기",
|
||||
"타임라인에서 기본적으로 shell 도구 항목을 펼친 상태로 표시합니다",
|
||||
"settings.general.row.editToolPartsExpanded.title": "edit 도구 항목 펼치기",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"타임라인에서 기본적으로 edit, write, patch 도구 파트를 펼친 상태로 표시합니다",
|
||||
"타임라인에서 기본적으로 edit, write, patch 도구 항목을 펼친 상태로 표시합니다",
|
||||
"settings.general.row.wayland.title": "네이티브 Wayland 사용",
|
||||
"settings.general.row.wayland.description": "Wayland에서 X11 폴백을 비활성화합니다. 다시 시작해야 합니다.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
@@ -723,8 +726,8 @@ export const dict = {
|
||||
"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.skill.title": "스킬",
|
||||
"settings.permissions.tool.skill.description": "이름으로 스킬 불러오기",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "언어 서버 쿼리 실행",
|
||||
"settings.permissions.tool.todowrite.title": "할 일 쓰기",
|
||||
@@ -795,6 +798,9 @@ export const dict = {
|
||||
"session.question.progress": "{{total}}개의 질문 중 {{current}}개",
|
||||
"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",
|
||||
@@ -956,7 +962,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.unknown": "알 수 없는 세션",
|
||||
"session.error.notFound": "이 세션을 찾을 수 없습니다",
|
||||
@@ -999,8 +1005,9 @@ export const dict = {
|
||||
"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.showCustomAgents.title": "에이전트 표시",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"입력창에서 에이전트를 전환합니다. 숨기면 기본적으로 Build 에이전트를 사용합니다.",
|
||||
"settings.general.row.newInterface.title": "새 레이아웃",
|
||||
"settings.general.row.newInterface.badge": "신규",
|
||||
"settings.general.row.newInterface.description":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+65
-55
@@ -31,8 +31,8 @@ export const dict = {
|
||||
"command.settings.open": "Åpne innstillinger",
|
||||
"command.session.previous": "Forrige sesjon",
|
||||
"command.session.next": "Neste sesjon",
|
||||
"command.session.previous.unseen": "Forrige uleste økt",
|
||||
"command.session.next.unseen": "Neste uleste økt",
|
||||
"command.session.previous.unseen": "Forrige uleste sesjon",
|
||||
"command.session.next.unseen": "Neste uleste sesjon",
|
||||
"command.session.archive": "Arkiver sesjon",
|
||||
|
||||
"command.palette": "Kommandopalett",
|
||||
@@ -101,6 +101,7 @@ export const dict = {
|
||||
"dialog.provider.empty": "Ingen leverandører funnet",
|
||||
"dialog.provider.group.popular": "Populære",
|
||||
"dialog.provider.group.other": "Andre",
|
||||
"dialog.provider.custom.label": "Egendefinert OpenAI-kompatibel leverandør",
|
||||
"dialog.provider.tag.recommended": "Anbefalt",
|
||||
"dialog.provider.opencode.note": "Utvalgte modeller inkludert Claude, GPT, Gemini og mer",
|
||||
"dialog.provider.opencode.tagline": "Pålitelige, optimaliserte modeller",
|
||||
@@ -121,6 +122,7 @@ export const dict = {
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Gratis modeller levert av OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Legg til flere modeller fra populære leverandører",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Se over 70 flere leverandører",
|
||||
|
||||
"dialog.provider.viewAll": "Vis flere leverandører",
|
||||
|
||||
@@ -132,8 +134,8 @@ export const dict = {
|
||||
"provider.connect.status.waiting": "Venter på autorisering...",
|
||||
"provider.connect.status.failed": "Autorisering mislyktes: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Skriv inn din {{provider}} API-nøkkel for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API-nøkkel",
|
||||
"Skriv inn API-nøkkelen din for {{provider}} for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}}-API-nøkkel",
|
||||
"provider.connect.apiKey.placeholder": "API-nøkkel",
|
||||
"provider.connect.apiKey.required": "API-nøkkel er påkrevd",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
@@ -147,7 +149,7 @@ export const dict = {
|
||||
"provider.connect.oauth.code.visit.link": "denne lenken",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
" for å hente autorisasjonskoden din for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.",
|
||||
"provider.connect.oauth.code.label": "{{method}} autorisasjonskode",
|
||||
"provider.connect.oauth.code.label": "{{method}}-autorisasjonskode",
|
||||
"provider.connect.oauth.code.placeholder": "Autorisasjonskode",
|
||||
"provider.connect.oauth.code.required": "Autorisasjonskode er påkrevd",
|
||||
"provider.connect.oauth.code.invalid": "Ugyldig autorisasjonskode",
|
||||
@@ -168,11 +170,12 @@ export const dict = {
|
||||
"provider.custom.field.providerID.description": "Små bokstaver, tall, bindestreker eller understreker",
|
||||
"provider.custom.field.name.label": "Visningsnavn",
|
||||
"provider.custom.field.name.placeholder": "Min AI-leverandør",
|
||||
"provider.custom.field.baseURL.label": "Base-URL",
|
||||
"provider.custom.field.baseURL.label": "Basis-URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "API-nøkkel",
|
||||
"provider.custom.field.apiKey.placeholder": "API-nøkkel",
|
||||
"provider.custom.field.apiKey.description": "Valgfritt. La stå tomt hvis du administrerer autentisering via headers.",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Valgfritt. La stå tomt hvis du administrerer autentisering via HTTP-hoder.",
|
||||
"provider.custom.models.label": "Modeller",
|
||||
"provider.custom.models.id.label": "ID",
|
||||
"provider.custom.models.id.placeholder": "modell-id",
|
||||
@@ -180,18 +183,18 @@ export const dict = {
|
||||
"provider.custom.models.name.placeholder": "Visningsnavn",
|
||||
"provider.custom.models.remove": "Fjern modell",
|
||||
"provider.custom.models.add": "Legg til modell",
|
||||
"provider.custom.headers.label": "Headers (valgfritt)",
|
||||
"provider.custom.headers.key.label": "Header",
|
||||
"provider.custom.headers.key.placeholder": "Header-Navn",
|
||||
"provider.custom.headers.label": "HTTP-hoder (valgfritt)",
|
||||
"provider.custom.headers.key.label": "HTTP-hode",
|
||||
"provider.custom.headers.key.placeholder": "Navn på HTTP-hode",
|
||||
"provider.custom.headers.value.label": "Verdi",
|
||||
"provider.custom.headers.value.placeholder": "verdi",
|
||||
"provider.custom.headers.remove": "Fjern header",
|
||||
"provider.custom.headers.add": "Legg til header",
|
||||
"provider.custom.headers.remove": "Fjern HTTP-hode",
|
||||
"provider.custom.headers.add": "Legg til HTTP-hode",
|
||||
"provider.custom.error.providerID.required": "Leverandør-ID er påkrevd",
|
||||
"provider.custom.error.providerID.format": "Bruk små bokstaver, tall, bindestreker eller understreker",
|
||||
"provider.custom.error.providerID.exists": "Den leverandør-IDen finnes allerede",
|
||||
"provider.custom.error.providerID.exists": "Denne leverandør-ID-en finnes allerede",
|
||||
"provider.custom.error.name.required": "Visningsnavn er påkrevd",
|
||||
"provider.custom.error.baseURL.required": "Base-URL er påkrevd",
|
||||
"provider.custom.error.baseURL.required": "Basis-URL er påkrevd",
|
||||
"provider.custom.error.baseURL.format": "Må starte med http:// eller https://",
|
||||
"provider.custom.error.required": "Påkrevd",
|
||||
"provider.custom.error.duplicate": "Duplikat",
|
||||
@@ -224,7 +227,7 @@ export const dict = {
|
||||
"common.cancel": "Avbryt",
|
||||
"common.connect": "Koble til",
|
||||
"common.disconnect": "Koble fra",
|
||||
"common.continue": "Send inn",
|
||||
"common.continue": "Fortsett",
|
||||
"common.submit": "Send inn",
|
||||
"common.save": "Lagre",
|
||||
"common.saving": "Lagrer...",
|
||||
@@ -245,13 +248,13 @@ export const dict = {
|
||||
"prompt.example.3": "Fiks ødelagte tester",
|
||||
"prompt.example.4": "Forklar hvordan autentisering fungerer",
|
||||
"prompt.example.5": "Finn og fiks sikkerhetssårbarheter",
|
||||
"prompt.example.6": "Legg til enhetstester for brukerservicen",
|
||||
"prompt.example.6": "Legg til enhetstester for brukertjenesten",
|
||||
"prompt.example.7": "Refaktorer denne funksjonen for bedre lesbarhet",
|
||||
"prompt.example.8": "Hva betyr denne feilen?",
|
||||
"prompt.example.9": "Hjelp meg med å feilsøke dette problemet",
|
||||
"prompt.example.10": "Generer API-dokumentasjon",
|
||||
"prompt.example.11": "Optimaliser databasespørringer",
|
||||
"prompt.example.12": "Legg til inputvalidering",
|
||||
"prompt.example.12": "Legg til inndatavalidering",
|
||||
"prompt.example.13": "Lag en ny komponent for...",
|
||||
"prompt.example.14": "Hvordan deployer jeg dette prosjektet?",
|
||||
"prompt.example.15": "Gjennomgå koden min for beste praksis",
|
||||
@@ -261,7 +264,7 @@ export const dict = {
|
||||
"prompt.example.19": "Legg til logging i hele kodebasen",
|
||||
"prompt.example.20": "Hvilke avhengigheter er utdaterte?",
|
||||
"prompt.example.21": "Hjelp meg med å skrive et migreringsskript",
|
||||
"prompt.example.22": "Implementer caching for dette endepunktet",
|
||||
"prompt.example.22": "Implementer hurtigbufring for dette endepunktet",
|
||||
"prompt.example.23": "Legg til paginering i denne listen",
|
||||
"prompt.example.24": "Lag en CLI-kommando for...",
|
||||
"prompt.example.25": "Hvordan fungerer miljøvariabler her?",
|
||||
@@ -271,7 +274,7 @@ export const dict = {
|
||||
"prompt.dropzone.label": "Slipp bilder, PDF-er eller tekstfiler her",
|
||||
"prompt.dropzone.file.label": "Slipp for å @nevne fil",
|
||||
"prompt.slash.badge.custom": "egendefinert",
|
||||
"prompt.slash.badge.skill": "skill",
|
||||
"prompt.slash.badge.skill": "ferdighet",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "aktiv",
|
||||
"prompt.context.includeActiveFile": "Inkluder aktiv fil",
|
||||
@@ -288,6 +291,7 @@ export const dict = {
|
||||
"prompt.action.stop": "Stopp",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Ikke støttet vedlegg",
|
||||
"prompt.toast.attachmentDuplicate.title": "Denne filen er allerede lastet opp",
|
||||
"prompt.toast.pasteUnsupported.description": "Kun bilder, PDF-er eller tekstfiler kan legges ved her.",
|
||||
"prompt.toast.modelAgentRequired.title": "Velg en agent og modell",
|
||||
"prompt.toast.modelAgentRequired.description": "Velg en agent og modell før du sender en forespørsel.",
|
||||
@@ -296,14 +300,14 @@ export const dict = {
|
||||
"prompt.toast.shellSendFailed.title": "Kunne ikke sende shell-kommando",
|
||||
"prompt.toast.commandSendFailed.title": "Kunne ikke sende kommando",
|
||||
"prompt.toast.promptSendFailed.title": "Kunne ikke sende forespørsel",
|
||||
"prompt.toast.promptSendFailed.description": "Kunne ikke hente økt",
|
||||
"prompt.toast.promptSendFailed.description": "Kunne ikke hente sesjon",
|
||||
|
||||
"dialog.mcp.title": "MCP-er",
|
||||
"dialog.mcp.description": "{{enabled}} av {{total}} aktivert",
|
||||
"dialog.mcp.empty": "Ingen MCP-er konfigurert",
|
||||
|
||||
"dialog.lsp.empty": "LSP-er automatisk oppdaget fra filtyper",
|
||||
"dialog.plugins.empty": "Plugins konfigurert i opencode.json",
|
||||
"dialog.plugins.empty": "Programtillegg konfigurert i opencode.json",
|
||||
|
||||
"mcp.status.connected": "tilkoblet",
|
||||
"mcp.status.failed": "mislyktes",
|
||||
@@ -364,7 +368,8 @@ export const dict = {
|
||||
"dialog.project.edit.worktree.startup.placeholder": "f.eks. bun install",
|
||||
|
||||
"context.breakdown.title": "Kontekstfordeling",
|
||||
"context.breakdown.note": 'Omtrentlig fordeling av input-tokens. "Annet" inkluderer verktøydefinisjoner og overhead.',
|
||||
"context.breakdown.note":
|
||||
'Omtrentlig fordeling av inndatatokener. "Annet" inkluderer verktøydefinisjoner og overhead.',
|
||||
"context.breakdown.system": "System",
|
||||
"context.breakdown.user": "Bruker",
|
||||
"context.breakdown.assistant": "Assistent",
|
||||
@@ -379,19 +384,19 @@ export const dict = {
|
||||
"context.stats.provider": "Leverandør",
|
||||
"context.stats.model": "Modell",
|
||||
"context.stats.limit": "Kontekstgrense",
|
||||
"context.stats.totalTokens": "Totalt antall tokens",
|
||||
"context.stats.totalTokens": "Totalt antall tokener",
|
||||
"context.stats.usage": "Forbruk",
|
||||
"context.stats.inputTokens": "Input-tokens",
|
||||
"context.stats.outputTokens": "Output-tokens",
|
||||
"context.stats.reasoningTokens": "Resonnerings-tokens",
|
||||
"context.stats.cacheTokens": "Cache-tokens (les/skriv)",
|
||||
"context.stats.inputTokens": "Inndatatokener",
|
||||
"context.stats.outputTokens": "Utdatatokener",
|
||||
"context.stats.reasoningTokens": "Resonneringstokener",
|
||||
"context.stats.cacheTokens": "Hurtigbuffertokener (les/skriv)",
|
||||
"context.stats.userMessages": "Brukermeldinger",
|
||||
"context.stats.assistantMessages": "Assistentmeldinger",
|
||||
"context.stats.totalCost": "Total kostnad",
|
||||
"context.stats.sessionCreated": "Sesjon opprettet",
|
||||
"context.stats.lastActivity": "Siste aktivitet",
|
||||
|
||||
"context.usage.tokens": "Tokens",
|
||||
"context.usage.tokens": "Tokener",
|
||||
"context.usage.usage": "Forbruk",
|
||||
"context.usage.cost": "Kostnad",
|
||||
"context.usage.clickToView": "Klikk for å se kontekst",
|
||||
@@ -422,9 +427,9 @@ export const dict = {
|
||||
"toast.scheme.title": "Fargevalg",
|
||||
|
||||
"toast.workspace.enabled.title": "Arbeidsområder aktivert",
|
||||
"toast.workspace.enabled.description": "Flere worktrees vises nå i sidefeltet",
|
||||
"toast.workspace.enabled.description": "Flere arbeidsområder vises nå i sidefeltet",
|
||||
"toast.workspace.disabled.title": "Arbeidsområder deaktivert",
|
||||
"toast.workspace.disabled.description": "Kun hoved-worktree vises i sidefeltet",
|
||||
"toast.workspace.disabled.description": "Kun hovedarbeidsområdet vises i sidefeltet",
|
||||
|
||||
"toast.permissions.autoaccept.on.title": "Aksepterer tillatelser automatisk",
|
||||
"toast.permissions.autoaccept.on.description": "Forespørsler om tillatelse vil bli godkjent automatisk",
|
||||
@@ -472,7 +477,7 @@ export const dict = {
|
||||
"error.page.version": "Versjon: {{version}}",
|
||||
|
||||
"error.dev.rootNotFound":
|
||||
"Rotelement ikke funnet. Glemte du å legge det til i index.html? Eller kanskje id-attributten er feilstavet?",
|
||||
"Rotelement ikke funnet. Glemte du å legge det til i index.html? Eller kanskje id-attributtet er feilstavet?",
|
||||
|
||||
"error.serverSync.connectFailed": "Kunne ikke koble til server. Kjører det en server på `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Ugyldig mappe i URL-en.",
|
||||
@@ -572,7 +577,7 @@ export const dict = {
|
||||
"status.popover.tab.servers": "Servere",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
"status.popover.tab.plugins": "Plugins",
|
||||
"status.popover.tab.plugins": "Programtillegg",
|
||||
"status.popover.action.manageServers": "Administrer servere",
|
||||
|
||||
"session.share.popover.title": "Publiser på nett",
|
||||
@@ -612,7 +617,7 @@ export const dict = {
|
||||
"common.delete": "Slett",
|
||||
"common.close": "Lukk",
|
||||
"common.edit": "Rediger",
|
||||
"common.loadMore": "Last flere",
|
||||
"common.loadMore": "Last inn flere",
|
||||
"common.key.esc": "ESC",
|
||||
|
||||
"sidebar.menu.toggle": "Veksle meny",
|
||||
@@ -650,16 +655,17 @@ export const dict = {
|
||||
"settings.general.row.appearance.title": "Utseende",
|
||||
"settings.general.row.appearance.description": "Tilpass hvordan OpenCode ser ut på enheten din",
|
||||
"settings.general.row.colorScheme.title": "Fargevalg",
|
||||
"settings.general.row.colorScheme.description": "Velg om OpenCode skal følge systemets, lyst eller mørkt tema",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"Velg om OpenCode skal følge systeminnstillingen eller bruke lyst eller mørkt tema",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.",
|
||||
"settings.general.row.theme.description": "Tilpass temaet i OpenCode.",
|
||||
"settings.general.row.font.title": "Kodefont",
|
||||
"settings.general.row.font.description": "Tilpass skrifttypen som brukes i kodeblokker",
|
||||
"settings.general.row.terminalFont.title": "Terminalskrift",
|
||||
"settings.general.row.terminalFont.description": "Tilpass skrifttypen som brukes i terminalen",
|
||||
"settings.general.row.uiFont.title": "UI-skrift",
|
||||
"settings.general.row.uiFont.description": "Tilpass skrifttypen som brukes i hele grensesnittet",
|
||||
"settings.general.row.followup.title": "Oppfølgingsadferd",
|
||||
"settings.general.row.followup.title": "Oppfølgingsatferd",
|
||||
"settings.general.row.followup.description": "Velg om oppfølgingsspørsmål skal kjøres umiddelbart eller vente i kø",
|
||||
"settings.general.row.followup.option.queue": "Kø",
|
||||
"settings.general.row.followup.option.steer": "Styr",
|
||||
@@ -670,10 +676,10 @@ export const dict = {
|
||||
"settings.general.row.editToolPartsExpanded.title": "Utvid edit-verktøydeler",
|
||||
"settings.general.row.editToolPartsExpanded.description":
|
||||
"Vis edit-, write- og patch-verktøydeler utvidet som standard i tidslinjen",
|
||||
"settings.general.row.wayland.title": "Bruk innebygd Wayland",
|
||||
"settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Krever omstart.",
|
||||
"settings.general.row.wayland.title": "Bruk Wayland direkte",
|
||||
"settings.general.row.wayland.description": "Deaktiver X11-reserveløsningen på Wayland. Krever omstart.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"På Linux med skjermer med blandet oppdateringsfrekvens kan innebygd Wayland være mer stabilt.",
|
||||
"På Linux med skjermer med blandet oppdateringsfrekvens kan direkte Wayland-støtte være mer stabilt.",
|
||||
|
||||
"settings.general.row.releaseNotes.title": "Utgivelsesnotater",
|
||||
"settings.general.row.releaseNotes.description": 'Vis "Hva er nytt"-vinduer etter oppdateringer',
|
||||
@@ -684,7 +690,7 @@ export const dict = {
|
||||
"settings.updates.row.check.description": "Se etter oppdateringer manuelt og installer hvis tilgjengelig",
|
||||
"settings.updates.action.checkNow": "Sjekk nå",
|
||||
"settings.updates.action.checking": "Sjekker...",
|
||||
"settings.updates.toast.latest.title": "Du er oppdatert",
|
||||
"settings.updates.toast.latest.title": "OpenCode er oppdatert",
|
||||
"settings.updates.toast.latest.description": "Du bruker den nyeste versjonen av OpenCode.",
|
||||
"sound.option.none": "Ingen",
|
||||
"sound.option.alert01": "Varsel 01",
|
||||
@@ -753,7 +759,7 @@ export const dict = {
|
||||
"settings.shortcuts.reset.toast.title": "Snarveier tilbakestilt",
|
||||
"settings.shortcuts.reset.toast.description": "Tastatursnarveier er tilbakestilt til standard.",
|
||||
"settings.shortcuts.conflict.title": "Snarvei allerede i bruk",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} er allerede tilordnet til {{titles}}.",
|
||||
"settings.shortcuts.conflict.description": "{{keybind}} er allerede tilordnet {{titles}}.",
|
||||
"settings.shortcuts.unassigned": "Ikke tilordnet",
|
||||
"settings.shortcuts.pressKeys": "Trykk taster",
|
||||
"settings.shortcuts.search.placeholder": "Søk etter snarveier",
|
||||
@@ -803,7 +809,7 @@ export const dict = {
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Søk i filinnhold ved hjelp av regulære uttrykk",
|
||||
"settings.permissions.tool.list.title": "Liste",
|
||||
"settings.permissions.tool.list.description": "List filer i en mappe",
|
||||
"settings.permissions.tool.list.description": "List opp filer i en mappe",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Kjør shell-kommandoer",
|
||||
"settings.permissions.tool.task.title": "Oppgave",
|
||||
@@ -821,7 +827,7 @@ export const dict = {
|
||||
"settings.permissions.tool.external_directory.title": "Ekstern mappe",
|
||||
"settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "Oppdager gjentatte verktøykall med identisk input",
|
||||
"settings.permissions.tool.doom_loop.description": "Oppdager gjentatte verktøykall med identiske inndata",
|
||||
|
||||
"session.delete.failed.title": "Kunne ikke slette sesjon",
|
||||
"session.delete.title": "Slett sesjon",
|
||||
@@ -866,14 +872,14 @@ export const dict = {
|
||||
"common.time.hoursAgo.short": "{{count}} t siden",
|
||||
"common.time.daysAgo.short": "{{count}} d siden",
|
||||
"settings.providers.connected.environmentDescription": "Koblet til fra miljøvariablene dine",
|
||||
"settings.providers.custom.description": "Legg til en OpenAI-kompatibel leverandør via basis-URL.",
|
||||
"settings.providers.custom.description": "Legg til en OpenAI-kompatibel leverandør via en basis-URL.",
|
||||
|
||||
"app.server.unreachable": "Kunne ikke nå {{server}}",
|
||||
"app.server.retrying": "Prøver på nytt automatisk...",
|
||||
"app.server.otherServers": "Andre servere",
|
||||
"dialog.server.add.usernamePlaceholder": "brukernavn",
|
||||
"dialog.server.add.passwordPlaceholder": "passord",
|
||||
"server.row.noUsername": "inget brukernavn",
|
||||
"server.row.noUsername": "ikke noe brukernavn",
|
||||
"session.review.noVcs.createGit.title": "Opprett et Git-depot",
|
||||
"session.review.noVcs.createGit.description": "Spor, gjennomgå og angre endringer i dette prosjektet",
|
||||
"session.review.noVcs.createGit.actionLoading": "Oppretter Git-depot...",
|
||||
@@ -882,6 +888,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} av {{total}} spørsmål",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Filutforsker",
|
||||
"session.header.reveal.finder": "Vis i Finder",
|
||||
"session.header.reveal.fileExplorer": "Vis i Filutforsker",
|
||||
"session.header.reveal.containingFolder": "Åpne mappen som inneholder dette elementet",
|
||||
"session.header.open.fileManager": "Filbehandler",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -900,25 +909,25 @@ export const dict = {
|
||||
"debugBar.na": "i/t",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Siste fullførte ruteovergang som berører en sesjonsside, målt fra ruterstart til første opptegning etter at den har roet seg.",
|
||||
"Siste fullførte ruteovergang som berører en sesjonsside, målt fra ruterstart til første gjengivelse etter at den har roet seg.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Rullende bilder per sekund over de siste 5 sekundene.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Verste bildetid over de siste 5 sekundene.",
|
||||
"debugBar.frame.tip": "Lengste bildetid over de siste 5 sekundene.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Bilder over 32ms i de siste 5 sekundene.",
|
||||
"debugBar.jank.tip": "Bilder som tar over 32 ms i de siste 5 sekundene.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip": "Blokkert tid og antall lange oppgaver i de siste 5 sekundene. Maks oppgave: {{max}}.",
|
||||
"debugBar.long.tip": "Blokkert tid og antall lange oppgaver i de siste 5 sekundene. Lengste oppgave: {{max}}.",
|
||||
"debugBar.delay.label": "DELAY",
|
||||
"debugBar.delay.tip": "Verste observerte inndataforsinkelse i de siste 5 sekundene.",
|
||||
"debugBar.delay.tip": "Lengste observerte inndataforsinkelse i de siste 5 sekundene.",
|
||||
"debugBar.inp.label": "INP",
|
||||
"debugBar.inp.tip":
|
||||
"Omtrentlig interaksjonsvarighet over de siste 5 sekundene. Dette er INP-lignende, ikke den offisielle Web Vitals INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Kumulativ layoutforskyvning for gjeldende app-levetid.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Brukt JS-heap vs heap-grense. Kun Chromium.",
|
||||
"debugBar.mem.tip": "Brukt JS-heap vs heap-grense. {{used}} av {{limit}}.",
|
||||
"debugBar.mem.tipUnavailable": "Brukt JS-heap kontra heap-grense. Kun Chromium.",
|
||||
"debugBar.mem.tip": "Brukt JS-heap kontra heap-grense. {{used}} av {{limit}}.",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
@@ -1057,7 +1066,7 @@ export const dict = {
|
||||
"session.error.notFound.description": "Denne fanen peker til en sesjon som ikke lenger finnes på denne serveren.",
|
||||
"session.error.notFound.closeTab": "Lukk fane",
|
||||
"session.error.serverConnection": "Kan ikke koble til denne serveren",
|
||||
"session.review.noUncommittedChanges": "Ingen ucommittede endringer ennå",
|
||||
"session.review.noUncommittedChanges": "Ingen endringer som ikke er sjekket inn ennå",
|
||||
"session.review.noBranchChanges": "Ingen grenendringer ennå",
|
||||
"session.question.minimize": "Minimer spørsmål",
|
||||
"session.question.restore": "Gjenopprett spørsmål",
|
||||
@@ -1095,8 +1104,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Navigasjon nederst",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Plasser tittellinjen og sesjonsfanene nederst på mobilskjermen",
|
||||
"settings.general.row.showCustomAgents.title": "Egendefinerte agenter",
|
||||
"settings.general.row.showCustomAgents.description": "Vis agentvelgeren i skrivefeltet",
|
||||
"settings.general.row.showCustomAgents.title": "Vis agent",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Bytt mellom agenter i skrivefeltet. Når velgeren er skjult, brukes Build-agenten som standard.",
|
||||
"settings.general.row.newInterface.title": "Nytt oppsett",
|
||||
"settings.general.row.newInterface.badge": "Ny",
|
||||
"settings.general.row.newInterface.description":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,18 @@ const appLocales = [
|
||||
"tr",
|
||||
"zh",
|
||||
"zht",
|
||||
"hi",
|
||||
"nl",
|
||||
"id",
|
||||
"vi",
|
||||
"it",
|
||||
"ur",
|
||||
"pa",
|
||||
"az",
|
||||
"fi",
|
||||
"sv",
|
||||
] as const
|
||||
const desktopLocales = appLocales.filter((locale) => locale !== "th" && locale !== "tr")
|
||||
const desktopLocales = appLocales
|
||||
|
||||
const domains = [
|
||||
{
|
||||
|
||||
+69
-60
@@ -45,7 +45,7 @@ export const dict = {
|
||||
"command.tab.reopenClosed": "Otwórz ponownie zamkniętą kartę",
|
||||
"command.context.addSelection": "Dodaj zaznaczenie do kontekstu",
|
||||
"command.context.addSelection.description": "Dodaj zaznaczone linie z bieżącego pliku",
|
||||
"command.input.focus": "Fokus na pole wejściowe",
|
||||
"command.input.focus": "Przejdź do pola wprowadzania",
|
||||
"command.terminal.toggle": "Przełącz terminal",
|
||||
"command.fileTree.toggle": "Przełącz drzewo plików",
|
||||
"command.review.toggle": "Przełącz przegląd",
|
||||
@@ -82,7 +82,7 @@ export const dict = {
|
||||
"command.session.fork": "Rozwidlij od wiadomości",
|
||||
"command.session.fork.description": "Utwórz nową sesję od poprzedniej wiadomości",
|
||||
"command.session.share": "Udostępnij sesję",
|
||||
"command.session.share.description": "Udostępnij tę sesję i skopiuj URL do schowka",
|
||||
"command.session.share.description": "Udostępnij tę sesję i skopiuj adres URL do schowka",
|
||||
"command.session.unshare": "Przestań udostępniać sesję",
|
||||
"command.session.unshare.description": "Zatrzymaj udostępnianie tej sesji",
|
||||
"palette.search.placeholder": "Szukaj plików, poleceń i sesji",
|
||||
@@ -94,10 +94,11 @@ export const dict = {
|
||||
"dialog.provider.empty": "Nie znaleziono dostawców",
|
||||
"dialog.provider.group.popular": "Popularne",
|
||||
"dialog.provider.group.other": "Inne",
|
||||
"dialog.provider.custom.label": "Niestandardowy dostawca zgodny z OpenAI",
|
||||
"dialog.provider.tag.recommended": "Zalecane",
|
||||
"dialog.provider.opencode.note": "Wyselekcjonowane modele, w tym Claude, GPT, Gemini i inne",
|
||||
"dialog.provider.opencode.note": "Starannie dobrane modele, w tym Claude, GPT, Gemini i inne",
|
||||
"dialog.provider.opencode.tagline": "Niezawodne, zoptymalizowane modele",
|
||||
"dialog.provider.opencodeGo.tagline": "Tania subskrypcja dla każdego",
|
||||
"dialog.provider.opencodeGo.tagline": "Niedroga subskrypcja dla każdego",
|
||||
"dialog.provider.anthropic.note": "Bezpośredni dostęp do modeli Claude, w tym Pro i Max",
|
||||
"dialog.provider.copilot.note": "Modele AI do pomocy w kodowaniu przez GitHub Copilot",
|
||||
"dialog.provider.openai.note": "Modele GPT do szybkich i wszechstronnych zadań AI",
|
||||
@@ -106,15 +107,16 @@ export const dict = {
|
||||
"dialog.provider.vercel.note": "Ujednolicony dostęp do modeli AI z inteligentnym routingiem",
|
||||
"dialog.model.select.title": "Wybierz model",
|
||||
"dialog.model.search.placeholder": "Szukaj modeli",
|
||||
"dialog.model.empty": "Brak wyników modelu",
|
||||
"dialog.model.empty": "Nie znaleziono modeli",
|
||||
"dialog.model.manage": "Zarządzaj modelami",
|
||||
"dialog.model.manage.description": "Dostosuj, które modele pojawiają się w wyborze modelu.",
|
||||
"dialog.model.manage.provider.toggle": "Przełącz wszystkie modele {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Darmowe modele dostarczane przez OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Dodaj więcej modeli od popularnych dostawców",
|
||||
"dialog.model.unpaid.viewMoreProviders": "Zobacz ponad 70 innych dostawców",
|
||||
"dialog.provider.viewAll": "Zobacz więcej dostawców",
|
||||
"provider.connect.title": "Połącz {{provider}}",
|
||||
"provider.connect.title.anthropicProMax": "Zaloguj się z Claude Pro/Max",
|
||||
"provider.connect.title.anthropicProMax": "Zaloguj się za pomocą konta Claude Pro/Max",
|
||||
"provider.connect.selectMethod": "Wybierz metodę logowania dla {{provider}}.",
|
||||
"provider.connect.method.apiKey": "Klucz API",
|
||||
"provider.connect.status.inProgress": "Autoryzacja w toku...",
|
||||
@@ -126,9 +128,9 @@ export const dict = {
|
||||
"provider.connect.apiKey.placeholder": "Klucz API",
|
||||
"provider.connect.apiKey.required": "Klucz API jest wymagany",
|
||||
"provider.connect.opencodeZen.line1":
|
||||
"OpenCode Zen daje dostęp do wybranego zestawu niezawodnych, zoptymalizowanych modeli dla agentów kodujących.",
|
||||
"OpenCode Zen daje dostęp do wybranego zestawu niezawodnych, zoptymalizowanych modeli dla agentów programistycznych.",
|
||||
"provider.connect.opencodeZen.line2":
|
||||
"Z jednym kluczem API uzyskasz dostęp do modeli takich jak Claude, GPT, Gemini, GLM i więcej.",
|
||||
"Za pomocą jednego klucza API uzyskasz dostęp do modeli takich jak Claude, GPT, Gemini i GLM, a także wielu innych.",
|
||||
"provider.connect.opencodeZen.visit.prefix": "Odwiedź ",
|
||||
"provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
|
||||
"provider.connect.opencodeZen.visit.suffix": ", aby odebrać swój klucz API.",
|
||||
@@ -156,7 +158,7 @@ export const dict = {
|
||||
"provider.custom.field.providerID.description": "Małe litery, cyfry, łączniki lub podkreślenia",
|
||||
"provider.custom.field.name.label": "Nazwa wyświetlana",
|
||||
"provider.custom.field.name.placeholder": "Mój Dostawca AI",
|
||||
"provider.custom.field.baseURL.label": "Bazowy URL",
|
||||
"provider.custom.field.baseURL.label": "Bazowy adres URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.mojdostawca.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Klucz API",
|
||||
"provider.custom.field.apiKey.placeholder": "Klucz API",
|
||||
@@ -180,7 +182,7 @@ export const dict = {
|
||||
"provider.custom.error.providerID.format": "Używaj małych liter, cyfr, łączników lub podkreśleń",
|
||||
"provider.custom.error.providerID.exists": "To ID dostawcy już istnieje",
|
||||
"provider.custom.error.name.required": "Nazwa wyświetlana jest wymagana",
|
||||
"provider.custom.error.baseURL.required": "Bazowy URL jest wymagany",
|
||||
"provider.custom.error.baseURL.required": "Bazowy adres URL jest wymagany",
|
||||
"provider.custom.error.baseURL.format": "Musi zaczynać się od http:// lub https://",
|
||||
"provider.custom.error.required": "Wymagane",
|
||||
"provider.custom.error.duplicate": "Duplikat",
|
||||
@@ -216,7 +218,7 @@ export const dict = {
|
||||
"common.clear": "Wyczyść",
|
||||
"common.connect": "Połącz",
|
||||
"common.disconnect": "Rozłącz",
|
||||
"common.continue": "Prześlij",
|
||||
"common.continue": "Kontynuuj",
|
||||
"common.submit": "Prześlij",
|
||||
"common.save": "Zapisz",
|
||||
"common.saving": "Zapisywanie...",
|
||||
@@ -229,46 +231,46 @@ export const dict = {
|
||||
"prompt.placeholder.summarizeComment": "Podsumuj komentarz…",
|
||||
"prompt.mode.shell": "Terminal",
|
||||
"prompt.mode.normal": "Prompt",
|
||||
"prompt.mode.shell.exit": "esc aby wyjść",
|
||||
"prompt.mode.shell.exit": "Naciśnij Esc, aby wyjść",
|
||||
"session.child.promptDisabled": "Nie można wysyłać promptów w sesjach podagentów.",
|
||||
"session.child.backToParent": "Wróć do głównej sesji.",
|
||||
"prompt.example.1": "Napraw TODO w bazie kodu",
|
||||
"prompt.example.2": "Jaki jest stos technologiczny tego projektu?",
|
||||
"prompt.example.3": "Napraw zepsute testy",
|
||||
"prompt.example.4": "Wyjaśnij jak działa uwierzytelnianie",
|
||||
"prompt.example.4": "Wyjaśnij, jak działa uwierzytelnianie",
|
||||
"prompt.example.5": "Znajdź i napraw luki w zabezpieczeniach",
|
||||
"prompt.example.6": "Dodaj testy jednostkowe dla serwisu użytkownika",
|
||||
"prompt.example.7": "Zrefaktoryzuj tę funkcję, aby była bardziej czytelna",
|
||||
"prompt.example.8": "Co oznacza ten błąd?",
|
||||
"prompt.example.9": "Pomóż mi zdebugować ten problem",
|
||||
"prompt.example.9": "Pomóż mi zdiagnozować ten problem",
|
||||
"prompt.example.10": "Wygeneruj dokumentację API",
|
||||
"prompt.example.11": "Zoptymalizuj zapytania do bazy danych",
|
||||
"prompt.example.12": "Dodaj walidację danych wejściowych",
|
||||
"prompt.example.13": "Utwórz nowy komponent dla...",
|
||||
"prompt.example.14": "Jak wdrożyć ten projekt?",
|
||||
"prompt.example.15": "Sprawdź mój kod pod kątem najlepszych praktyk",
|
||||
"prompt.example.16": "Dodaj obsługę błędów do tej funkcję",
|
||||
"prompt.example.16": "Dodaj obsługę błędów do tej funkcji",
|
||||
"prompt.example.17": "Wyjaśnij ten wzorzec regex",
|
||||
"prompt.example.18": "Przekonwertuj to na TypeScript",
|
||||
"prompt.example.19": "Dodaj logowanie w całej bazie kodu",
|
||||
"prompt.example.20": "Które zależności są przestarzałe?",
|
||||
"prompt.example.21": "Pomóż mi napisać skrypt migracyjny",
|
||||
"prompt.example.22": "Zaimplementuj cachowanie dla tego punktu końcowego",
|
||||
"prompt.example.22": "Zaimplementuj buforowanie dla tego punktu końcowego",
|
||||
"prompt.example.23": "Dodaj stronicowanie do tej listy",
|
||||
"prompt.example.24": "Utwórz polecenie CLI dla...",
|
||||
"prompt.example.25": "Jak działają tutaj zmienne środowiskowe?",
|
||||
"prompt.popover.emptyResults": "Brak pasujących wyników",
|
||||
"prompt.popover.emptyCommands": "Brak pasujących poleceń",
|
||||
"prompt.dropzone.label": "Upuść tutaj obrazy, pliki PDF lub pliki tekstowe",
|
||||
"prompt.dropzone.file.label": "Upuść, aby @wspomnieć plik",
|
||||
"prompt.dropzone.file.label": "Upuść, aby oznaczyć plik za pomocą @",
|
||||
"prompt.slash.badge.custom": "własne",
|
||||
"prompt.slash.badge.skill": "skill",
|
||||
"prompt.slash.badge.skill": "umiejętność",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "aktywny",
|
||||
"prompt.context.includeActiveFile": "Dołącz aktywny plik",
|
||||
"prompt.context.removeActiveFile": "Usuń aktywny plik z kontekstu",
|
||||
"prompt.context.removeFile": "Usuń plik z kontekstu",
|
||||
"prompt.action.attachFile": "Załącz plik",
|
||||
"prompt.action.attachFile": "Dodaj pliki",
|
||||
"prompt.menu.addImagesAndFiles": "Dodaj pliki i inne elementy",
|
||||
"prompt.menu.imagesAndFiles": "Obrazy i pliki",
|
||||
"prompt.menu.commands": "Polecenia",
|
||||
@@ -278,6 +280,7 @@ export const dict = {
|
||||
"prompt.action.send": "Wyślij",
|
||||
"prompt.action.stop": "Zatrzymaj",
|
||||
"prompt.toast.pasteUnsupported.title": "Nieobsługiwany załącznik",
|
||||
"prompt.toast.attachmentDuplicate.title": "Ten plik został już przesłany",
|
||||
"prompt.toast.pasteUnsupported.description": "Można tutaj załączać tylko obrazy, pliki PDF lub pliki tekstowe.",
|
||||
"prompt.toast.modelAgentRequired.title": "Wybierz agenta i model",
|
||||
"prompt.toast.modelAgentRequired.description": "Wybierz agenta i model przed wysłaniem zapytania.",
|
||||
@@ -288,12 +291,12 @@ export const dict = {
|
||||
"prompt.toast.promptSendFailed.title": "Nie udało się wysłać zapytania",
|
||||
"prompt.toast.promptSendFailed.description": "Nie udało się pobrać sesji",
|
||||
"dialog.mcp.title": "MCP",
|
||||
"dialog.mcp.description": "{{enabled}} z {{total}} włączone",
|
||||
"dialog.mcp.description": "Włączone: {{enabled}} z {{total}}",
|
||||
"dialog.mcp.empty": "Brak skonfigurowanych MCP",
|
||||
"dialog.lsp.empty": "LSP wykryte automatycznie na podstawie typów plików",
|
||||
"dialog.plugins.empty": "Wtyczki skonfigurowane w opencode.json",
|
||||
"dialog.lsp.empty": "Serwery LSP są wykrywane automatycznie na podstawie typów plików",
|
||||
"dialog.plugins.empty": "Wtyczki są skonfigurowane w pliku opencode.json",
|
||||
"mcp.status.connected": "połączono",
|
||||
"mcp.status.failed": "niepowodzenie",
|
||||
"mcp.status.failed": "błąd",
|
||||
"mcp.status.needs_auth": "wymaga autoryzacji",
|
||||
"mcp.auth.clickToAuthenticate": "Kliknij, aby się uwierzytelnić",
|
||||
"mcp.status.disabled": "wyłączone",
|
||||
@@ -303,14 +306,14 @@ export const dict = {
|
||||
"dialog.directory.action.selectFile": "Wybierz plik",
|
||||
"dialog.directory.action.selectFolder": "Wybierz folder",
|
||||
"dialog.directory.root": "Katalog główny",
|
||||
"dialog.directory.parent": "Nadrzędny",
|
||||
"dialog.directory.parent": "Folder nadrzędny",
|
||||
"dialog.directory.readError": "Nie można odczytać tego folderu",
|
||||
"dialog.server.title": "Serwery",
|
||||
"dialog.server.description": "Przełącz serwer OpenCode, z którym łączy się ta aplikacja.",
|
||||
"dialog.server.search.placeholder": "Szukaj serwerów",
|
||||
"dialog.server.empty": "Brak serwerów",
|
||||
"dialog.server.add.title": "Dodaj serwer",
|
||||
"dialog.server.add.url": "URL serwera",
|
||||
"dialog.server.add.url": "Adres URL serwera",
|
||||
"dialog.server.add.placeholder": "http://localhost:4096",
|
||||
"dialog.server.add.error": "Nie można połączyć się z serwerem",
|
||||
"dialog.server.add.checking": "Sprawdzanie...",
|
||||
@@ -322,7 +325,7 @@ export const dict = {
|
||||
"dialog.server.edit.title": "Edytuj serwer",
|
||||
"dialog.server.default.title": "Domyślny serwer",
|
||||
"dialog.server.default.description":
|
||||
"Połącz z tym serwerem przy uruchomieniu aplikacji zamiast uruchamiać lokalny serwer. Wymaga restartu.",
|
||||
"Łącz się z tym serwerem przy uruchamianiu aplikacji zamiast uruchamiać serwer lokalny. Wymaga ponownego uruchomienia.",
|
||||
"dialog.server.default.none": "Nie wybrano serwera",
|
||||
"dialog.server.default.set": "Ustaw bieżący serwer jako domyślny",
|
||||
"dialog.server.default.clear": "Wyczyść",
|
||||
@@ -331,7 +334,7 @@ export const dict = {
|
||||
"dialog.server.menu.default": "Ustaw jako domyślny",
|
||||
"dialog.server.menu.defaultRemove": "Usuń domyślny",
|
||||
"dialog.server.menu.delete": "Usuń",
|
||||
"dialog.server.current": "Obecny serwer",
|
||||
"dialog.server.current": "Bieżący serwer",
|
||||
"dialog.server.status.default": "Domyślny",
|
||||
"wsl.server.add": "Dodaj serwer WSL",
|
||||
"wsl.server.addShort": "Dodaj WSL",
|
||||
@@ -372,7 +375,7 @@ export const dict = {
|
||||
"wsl.onboarding.distroStatus.ready": "Gotowa",
|
||||
"wsl.onboarding.distroStatus.checking": "Sprawdzanie...",
|
||||
"wsl.onboarding.distroStatus.opencodeMissing": "OpenCode nie jest zainstalowany",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Brak bash i curl",
|
||||
"wsl.onboarding.distroStatus.missingTools": "Brak narzędzi bash i curl",
|
||||
"wsl.onboarding.distroStatus.unsupported": "Nieobsługiwana · Użyj WSL 2",
|
||||
"wsl.onboarding.needAnotherDistro": "Potrzebujesz innej dystrybucji?",
|
||||
"wsl.onboarding.needAnotherDistroHint": "Zainstaluj dystrybucję systemu Linux z katalogu WSL",
|
||||
@@ -393,7 +396,7 @@ export const dict = {
|
||||
"wsl.onboarding.installDistro": "Zainstaluj dystrybucję",
|
||||
"wsl.onboarding.searchDistros": "Szukaj dystrybucji",
|
||||
"wsl.onboarding.wsl2Required": "Wymagany jest WSL 2.",
|
||||
"wsl.onboarding.toolsRequired": "Ta dystrybucja wymaga bash i curl.",
|
||||
"wsl.onboarding.toolsRequired": "Ta dystrybucja wymaga narzędzi bash i curl.",
|
||||
"wsl.onboarding.openTerminal": "Otwórz terminal",
|
||||
"wsl.onboarding.path": "Ścieżka: {{path}}",
|
||||
"wsl.onboarding.notFound": "nie znaleziono",
|
||||
@@ -406,8 +409,8 @@ export const dict = {
|
||||
"dialog.project.edit.name": "Nazwa",
|
||||
"dialog.project.edit.icon": "Ikona",
|
||||
"dialog.project.edit.icon.alt": "Ikona projektu",
|
||||
"dialog.project.edit.icon.hint": "Kliknij lub przeciągnij obraz",
|
||||
"dialog.project.edit.icon.recommended": "Zalecane: 128x128px",
|
||||
"dialog.project.edit.icon.hint": "Kliknij, aby wybrać obraz, lub przeciągnij go tutaj",
|
||||
"dialog.project.edit.icon.recommended": "Zalecane: 128 × 128 px",
|
||||
"dialog.project.edit.color": "Kolor",
|
||||
"dialog.project.edit.color.select": "Wybierz kolor {{color}}",
|
||||
"dialog.project.edit.worktree.startup": "Skrypt uruchamiania przestrzeni roboczej",
|
||||
@@ -467,9 +470,9 @@ export const dict = {
|
||||
"toast.theme.title": "Przełączono motyw",
|
||||
"toast.scheme.title": "Schemat kolorów",
|
||||
"toast.workspace.enabled.title": "Przestrzenie robocze włączone",
|
||||
"toast.workspace.enabled.description": "Kilka worktree jest teraz wyświetlanych na pasku bocznym",
|
||||
"toast.workspace.enabled.description": "Kilka drzew roboczych jest teraz wyświetlanych na pasku bocznym",
|
||||
"toast.workspace.disabled.title": "Przestrzenie robocze wyłączone",
|
||||
"toast.workspace.disabled.description": "Tylko główny worktree jest wyświetlany na pasku bocznym",
|
||||
"toast.workspace.disabled.description": "Tylko główne drzewo robocze jest wyświetlane na pasku bocznym",
|
||||
"toast.permissions.autoaccept.on.title": "Automatyczne akceptowanie uprawnień",
|
||||
"toast.permissions.autoaccept.on.description": "Żądania uprawnień będą automatycznie zatwierdzane",
|
||||
"toast.permissions.autoaccept.off.title": "Zatrzymano automatyczne akceptowanie uprawnień",
|
||||
@@ -480,9 +483,9 @@ export const dict = {
|
||||
"toast.file.listFailed.title": "Nie udało się wyświetlić listy plików",
|
||||
"toast.context.noLineSelection.title": "Brak zaznaczenia linii",
|
||||
"toast.context.noLineSelection.description": "Najpierw wybierz zakres linii w zakładce pliku.",
|
||||
"toast.session.share.copyFailed.title": "Nie udało się skopiować URL do schowka",
|
||||
"toast.session.share.copyFailed.title": "Nie udało się skopiować adresu URL do schowka",
|
||||
"toast.session.share.success.title": "Sesja udostępniona",
|
||||
"toast.session.share.success.description": "Link udostępniania skopiowany do schowka!",
|
||||
"toast.session.share.success.description": "Adres URL udostępnionej sesji skopiowano do schowka!",
|
||||
"toast.session.share.failed.title": "Nie udało się udostępnić sesji",
|
||||
"toast.session.share.failed.description": "Wystąpił błąd podczas udostępniania sesji",
|
||||
"toast.session.unshare.success.title": "Zatrzymano udostępnianie sesji",
|
||||
@@ -492,13 +495,13 @@ export const dict = {
|
||||
"toast.session.listFailed.title": "Nie udało się załadować sesji dla {{project}}",
|
||||
"toast.update.title": "Dostępna aktualizacja",
|
||||
"toast.update.description": "Nowa wersja OpenCode ({{version}}) jest teraz dostępna do instalacji.",
|
||||
"toast.update.action.installRestart": "Zainstaluj i zrestartuj",
|
||||
"toast.update.action.installRestart": "Zainstaluj i uruchom ponownie",
|
||||
"toast.update.action.notYet": "Jeszcze nie",
|
||||
"error.page.title": "Coś poszło nie tak",
|
||||
"error.page.description": "Wystąpił błąd podczas ładowania aplikacji.",
|
||||
"error.page.description.localServerStartup": "Wystąpił błąd podczas uruchamiania serwera lokalnego.",
|
||||
"error.page.details.label": "Szczegóły błędu",
|
||||
"error.page.action.restart": "Restartuj",
|
||||
"error.page.action.restart": "Uruchom ponownie",
|
||||
"error.page.action.report": "Zgłoś błąd",
|
||||
"error.page.action.reported": "Błąd zgłoszony",
|
||||
"error.page.action.exportLogs": "Eksportuj dzienniki",
|
||||
@@ -511,7 +514,7 @@ export const dict = {
|
||||
"error.dev.rootNotFound":
|
||||
"Nie znaleziono elementu głównego. Czy zapomniałeś dodać go do swojego index.html? A może atrybut id został błędnie wpisany?",
|
||||
"error.serverSync.connectFailed": "Nie można połączyć się z serwerem. Czy serwer działa pod adresem `{{url}}`?",
|
||||
"directory.error.invalidUrl": "Nieprawidłowy katalog w URL.",
|
||||
"directory.error.invalidUrl": "Nieprawidłowy katalog w adresie URL.",
|
||||
"error.chain.unknown": "Nieznany błąd",
|
||||
"error.chain.causedBy": "Spowodowany przez:",
|
||||
"error.chain.apiError": "Błąd API",
|
||||
@@ -520,7 +523,7 @@ export const dict = {
|
||||
"error.chain.responseBody": "Treść odpowiedzi:\n{{body}}",
|
||||
"error.chain.didYouMean": "Czy miałeś na myśli: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model nie znaleziony: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Sprawdź swoją konfigurację (opencode.json) nazwy dostawców/modeli",
|
||||
"error.chain.checkConfig": "Sprawdź nazwy dostawców i modeli w konfiguracji (opencode.json)",
|
||||
"error.chain.mcpFailed":
|
||||
"Serwer MCP „{{name}}” nie uruchomił się. Uwaga: OpenCode nie obsługuje jeszcze uwierzytelniania MCP.",
|
||||
"error.chain.providerAuthFailed": "Uwierzytelnianie dostawcy nie powiodło się ({{provider}}): {{message}}",
|
||||
@@ -575,7 +578,7 @@ export const dict = {
|
||||
"session.review.change.other": "Zmiany",
|
||||
"session.review.loadingChanges": "Ładowanie zmian...",
|
||||
"session.review.empty": "Brak zmian w tej sesji",
|
||||
"session.review.noVcs": "Nie wykryto systemu kontroli wersji Git, zmiany nie są wyświetlane",
|
||||
"session.review.noVcs": "Nie wykryto systemu kontroli wersji Git, dlatego zmiany nie są wyświetlane",
|
||||
"session.review.noSnapshot": "Śledzenie migawek jest wyłączone w konfiguracji, więc zmiany w sesji są niedostępne",
|
||||
"session.review.noChanges": "Brak zmian",
|
||||
"session.review.noUncommittedChanges": "Brak jeszcze niezatwierdzonych zmian",
|
||||
@@ -645,7 +648,7 @@ export const dict = {
|
||||
"session.share.action.publishing": "Publikowanie...",
|
||||
"session.share.action.unpublish": "Cofnij publikację",
|
||||
"session.share.action.unpublishing": "Cofanie publikacji...",
|
||||
"session.share.action.view": "Widok",
|
||||
"session.share.action.view": "Wyświetl",
|
||||
"session.share.copy.copied": "Skopiowano",
|
||||
"session.share.copy.copyLink": "Kopiuj link",
|
||||
"lsp.tooltip.none": "Brak serwerów LSP",
|
||||
@@ -657,7 +660,7 @@ export const dict = {
|
||||
"terminal.close": "Zamknij terminal",
|
||||
"terminal.connectionLost.title": "Utracono połączenie",
|
||||
"terminal.connectionLost.description":
|
||||
"Połączenie z terminalem zostało przerwane. Może się to zdarzyć przy restarcie serwera.",
|
||||
"Połączenie z terminalem zostało przerwane. Może się to zdarzyć podczas ponownego uruchamiania serwera.",
|
||||
"common.closeTab": "Zamknij kartę",
|
||||
"common.dismiss": "Odrzuć",
|
||||
"common.requestFailed": "Żądanie nie powiodło się",
|
||||
@@ -686,7 +689,7 @@ export const dict = {
|
||||
"sidebar.empty.title": "Brak otwartych projektów",
|
||||
"sidebar.empty.description": "Otwórz projekt, aby rozpocząć",
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
"settings.section.desktop": "Pulpit",
|
||||
"settings.section.desktop": "Aplikacja komputerowa",
|
||||
"settings.section.server": "Serwer",
|
||||
"settings.tab.general": "Ogólne",
|
||||
"settings.tab.shortcuts": "Skróty",
|
||||
@@ -738,8 +741,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Dolna nawigacja",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Umieść pasek tytułu i karty sesji u dołu ekranu na urządzeniach mobilnych",
|
||||
"settings.general.row.showCustomAgents.title": "Agenci niestandardowi",
|
||||
"settings.general.row.showCustomAgents.description": "Pokaż wybór agenta w edytorze wiadomości",
|
||||
"settings.general.row.showCustomAgents.title": "Wybór agenta",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Umożliwiaj przełączanie agentów w edytorze wiadomości. Po ukryciu domyślnie używany jest agent Build.",
|
||||
"settings.general.row.reasoningSummaries.title": "Pokaż podsumowania wnioskowania",
|
||||
"settings.general.row.reasoningSummaries.description": "Wyświetlaj podsumowania wnioskowania modelu na osi czasu",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Rozwijaj elementy narzędzia shell",
|
||||
@@ -757,13 +761,14 @@ export const dict = {
|
||||
"settings.general.row.newInterfaceNotice.dismiss": "Odrzuć",
|
||||
"settings.general.row.pinchZoom.title": "Powiększanie gestem szczypania",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Zezwalaj na powiększanie gestem szczypania na gładziku i przewijaniem z klawiszem Ctrl",
|
||||
"settings.general.row.wayland.title": "Użyj natywnego Wayland",
|
||||
"settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
|
||||
"Zezwalaj na powiększanie gestem szczypania na gładziku lub przez przewijanie z wciśniętym klawiszem Ctrl",
|
||||
"settings.general.row.wayland.title": "Użyj natywnego protokołu Wayland",
|
||||
"settings.general.row.wayland.description":
|
||||
"Wyłącz awaryjne korzystanie z X11 w środowisku Wayland. Wymaga ponownego uruchomienia.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Na Linuxie z monitorami o różnym odświeżaniu, natywny Wayland może być bardziej stabilny.",
|
||||
"W systemie Linux z monitorami o różnej częstotliwości odświeżania natywny protokół Wayland może być stabilniejszy.",
|
||||
"settings.general.row.releaseNotes.title": "Informacje o wydaniu",
|
||||
"settings.general.row.releaseNotes.description": 'Pokazuj wyskakujące okna "Co nowego" po aktualizacjach',
|
||||
"settings.general.row.releaseNotes.description": "Pokazuj wyskakujące okna „Co nowego” po aktualizacjach",
|
||||
"settings.updates.row.startup.title": "Sprawdzaj aktualizacje przy uruchomieniu",
|
||||
"settings.updates.row.startup.description": "Automatycznie sprawdzaj aktualizacje podczas uruchamiania OpenCode",
|
||||
"settings.updates.row.check.title": "Sprawdź aktualizacje",
|
||||
@@ -877,7 +882,8 @@ export const dict = {
|
||||
"settings.permissions.tool.read.title": "Odczyt",
|
||||
"settings.permissions.tool.read.description": "Odczyt pliku (pasuje do ścieżki pliku)",
|
||||
"settings.permissions.tool.edit.title": "Edycja",
|
||||
"settings.permissions.tool.edit.description": "Modyfikacja plików, w tym edycje, zapisy, łatki i multi-edycje",
|
||||
"settings.permissions.tool.edit.description":
|
||||
"Modyfikowanie plików, w tym edytowanie, zapisywanie, stosowanie łatek i edytowanie wielu plików",
|
||||
"settings.permissions.tool.glob.title": "Glob",
|
||||
"settings.permissions.tool.glob.description": "Dopasowywanie plików za pomocą wzorców glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
@@ -887,7 +893,7 @@ export const dict = {
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Uruchamianie poleceń powłoki",
|
||||
"settings.permissions.tool.task.title": "Zadanie",
|
||||
"settings.permissions.tool.task.description": "Uruchamianie pod-agentów",
|
||||
"settings.permissions.tool.task.description": "Uruchamianie podagentów",
|
||||
"settings.permissions.tool.skill.title": "Umiejętność",
|
||||
"settings.permissions.tool.skill.description": "Ładowanie umiejętności według nazwy",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
@@ -918,7 +924,7 @@ export const dict = {
|
||||
"workspace.reset.success.description": "Przestrzeń robocza odpowiada teraz domyślnej gałęzi.",
|
||||
"workspace.error.stillPreparing": "Przestrzeń robocza jest wciąż przygotowywana",
|
||||
"workspace.status.checking": "Sprawdzanie niezscalonych zmian...",
|
||||
"workspace.status.error": "Nie można zweryfikować statusu git.",
|
||||
"workspace.status.error": "Nie można zweryfikować statusu Git.",
|
||||
"workspace.status.clean": "Nie wykryto niezscalonych zmian.",
|
||||
"workspace.status.dirty": "Wykryto niezscalone zmiany w tej przestrzeni roboczej.",
|
||||
"workspace.delete.title": "Usuń przestrzeń roboczą",
|
||||
@@ -944,7 +950,7 @@ export const dict = {
|
||||
"common.time.hoursAgo.short": "{{count}} godz. temu",
|
||||
"common.time.daysAgo.short": "{{count}} dni temu",
|
||||
"settings.providers.connected.environmentDescription": "Połączono ze zmiennymi środowiskowymi",
|
||||
"settings.providers.custom.description": "Dodaj dostawcę zgodnego z OpenAI poprzez podstawowy URL.",
|
||||
"settings.providers.custom.description": "Dodaj dostawcę zgodnego z OpenAI za pomocą bazowego adresu URL.",
|
||||
|
||||
"app.server.unreachable": "Nie można połączyć z {{server}}",
|
||||
"app.server.retrying": "Ponawianie automatycznie...",
|
||||
@@ -960,6 +966,9 @@ export const dict = {
|
||||
"session.question.progress": "{{current}} z {{total}} pytań",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Eksplorator plików",
|
||||
"session.header.reveal.finder": "Pokaż w Finderze",
|
||||
"session.header.reveal.fileExplorer": "Pokaż w Eksploratorze plików",
|
||||
"session.header.reveal.containingFolder": "Otwórz folder zawierający",
|
||||
"session.header.open.fileManager": "Menedżer plików",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
@@ -974,17 +983,17 @@ export const dict = {
|
||||
"session.header.open.app.androidStudio": "Android Studio",
|
||||
"session.header.open.app.powershell": "PowerShell",
|
||||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
"debugBar.ariaLabel": "Diagnostyka wydajności deweloperskiej",
|
||||
"debugBar.ariaLabel": "Diagnostyka wydajności w środowisku deweloperskim",
|
||||
"debugBar.na": "n.d.",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Ostatnie zakończone przejście trasy dotykające strony sesji, mierzone od startu routera do pierwszego odrysowania po ustaleniu.",
|
||||
"Ostatnie zakończone przejście między trasami obejmujące stronę sesji, mierzone od uruchomienia routera do pierwszego odrysowania po ustabilizowaniu widoku.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Średnia liczba klatek na sekundę w ciągu ostatnich 5 sekund.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Najgorszy czas klatki w ciągu ostatnich 5 sekund.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
"debugBar.jank.tip": "Klatki powyżej 32ms w ciągu ostatnich 5 sekund.",
|
||||
"debugBar.jank.tip": "Klatki powyżej 32 ms w ciągu ostatnich 5 sekund.",
|
||||
"debugBar.long.label": "LONG",
|
||||
"debugBar.long.tip":
|
||||
"Zablokowany czas i liczba długich zadań w ciągu ostatnich 5 sekund. Maksymalne zadanie: {{max}}.",
|
||||
@@ -994,10 +1003,10 @@ export const dict = {
|
||||
"debugBar.inp.tip":
|
||||
"Przybliżony czas trwania interakcji w ciągu ostatnich 5 sekund. Jest to podobne do INP, a nie oficjalne Web Vitals INP.",
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Skumulowane przesunięcie układu dla bieżącego czasu życia aplikacji.",
|
||||
"debugBar.cls.tip": "Skumulowane przesunięcie układu od bieżącego uruchomienia aplikacji.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Użyta sterta JS vs limit sterty. Tylko Chromium.",
|
||||
"debugBar.mem.tip": "Użyta sterta JS vs limit sterty. {{used}} z {{limit}}.",
|
||||
"debugBar.mem.tipUnavailable": "Wykorzystana pamięć sterty JS względem jej limitu. Tylko Chromium.",
|
||||
"debugBar.mem.tip": "Wykorzystana pamięć sterty JS względem jej limitu: {{used}} z {{limit}}.",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
|
||||
+88
-81
@@ -37,12 +37,12 @@ export const dict = {
|
||||
|
||||
"command.palette": "Палитра команд",
|
||||
|
||||
"command.theme.cycle": "Цикл тем",
|
||||
"command.theme.cycle": "Переключить тему",
|
||||
"command.theme.set": "Использовать тему: {{theme}}",
|
||||
"command.theme.scheme.cycle": "Цикл цветовой схемы",
|
||||
"command.theme.scheme.cycle": "Переключить цветовую схему",
|
||||
"command.theme.scheme.set": "Использовать цветовую схему: {{scheme}}",
|
||||
|
||||
"command.language.cycle": "Цикл языков",
|
||||
"command.language.cycle": "Переключить язык",
|
||||
"command.language.set": "Использовать язык: {{language}}",
|
||||
|
||||
"command.session.new": "Новая сессия",
|
||||
@@ -51,7 +51,7 @@ export const dict = {
|
||||
"command.tab.reopenClosed": "Повторно открыть закрытую вкладку",
|
||||
"command.context.addSelection": "Добавить выделение в контекст",
|
||||
"command.context.addSelection.description": "Добавить выбранные строки из текущего файла",
|
||||
"command.input.focus": "Фокус на поле ввода",
|
||||
"command.input.focus": "Перейти к полю ввода",
|
||||
"command.terminal.toggle": "Переключить терминал",
|
||||
"command.fileTree.toggle": "Переключить дерево файлов",
|
||||
"command.review.toggle": "Переключить обзор",
|
||||
@@ -67,11 +67,11 @@ export const dict = {
|
||||
"command.model.choose.description": "Выбрать другую модель",
|
||||
"command.mcp.toggle": "Переключить MCP",
|
||||
"command.mcp.toggle.description": "Переключить MCP",
|
||||
"command.agent.cycle": "Цикл агентов",
|
||||
"command.agent.cycle": "Следующий агент",
|
||||
"command.agent.cycle.description": "Переключиться к следующему агенту",
|
||||
"command.agent.cycle.reverse": "Цикл агентов назад",
|
||||
"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": "Оболочка",
|
||||
"command.prompt.mode.normal": "Промпт",
|
||||
@@ -102,9 +102,10 @@ 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.opencode.tagline": "Надёжные оптимизированные модели",
|
||||
"dialog.provider.opencodeGo.tagline": "Доступная подписка для всех",
|
||||
"dialog.provider.anthropic.note": "Прямой доступ к моделям Claude, включая Pro и Max",
|
||||
"dialog.provider.copilot.note": "ИИ-модели для помощи в кодировании через GitHub Copilot",
|
||||
@@ -117,33 +118,34 @@ export const dict = {
|
||||
"dialog.model.search.placeholder": "Поиск моделей",
|
||||
"dialog.model.empty": "Модели не найдены",
|
||||
"dialog.model.manage": "Управление моделями",
|
||||
"dialog.model.manage.description": "Настройте какие модели появляются в выборе модели",
|
||||
"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.apiKey": "Ключ API",
|
||||
"provider.connect.status.inProgress": "Авторизация...",
|
||||
"provider.connect.status.waiting": "Ожидание авторизации...",
|
||||
"provider.connect.status.failed": "Ошибка авторизации: {{error}}",
|
||||
"provider.connect.apiKey.description":
|
||||
"Введите ваш API ключ {{provider}} для подключения аккаунта и использования моделей {{provider}} в OpenCode.",
|
||||
"provider.connect.apiKey.label": "{{provider}} API ключ",
|
||||
"provider.connect.apiKey.placeholder": "API ключ",
|
||||
"provider.connect.apiKey.required": "API ключ обязателен",
|
||||
"Введите ключ API {{provider}}, чтобы подключить аккаунт и использовать модели {{provider}} в OpenCode.",
|
||||
"provider.connect.apiKey.label": "Ключ API {{provider}}",
|
||||
"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 и другим.",
|
||||
"С одним ключом 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.opencodeZen.visit.suffix": ", чтобы получить ключ API.",
|
||||
"provider.connect.oauth.code.visit.prefix": "Посетите ",
|
||||
"provider.connect.oauth.code.visit.link": "эту ссылку",
|
||||
"provider.connect.oauth.code.visit.suffix":
|
||||
@@ -161,18 +163,18 @@ export const dict = {
|
||||
"provider.connect.toast.connected.description": "Модели {{provider}} теперь доступны.",
|
||||
|
||||
"provider.custom.title": "Пользовательский провайдер",
|
||||
"provider.custom.description.prefix": "Настройте OpenAI-совместимого провайдера. См. ",
|
||||
"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.name.placeholder": "Мой ИИ-провайдер",
|
||||
"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.label": "Ключ API",
|
||||
"provider.custom.field.apiKey.placeholder": "Ключ API",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"Необязательно. Оставьте пустым, если управляете авторизацией через заголовки.",
|
||||
"provider.custom.models.label": "Модели",
|
||||
@@ -211,15 +213,15 @@ export const dict = {
|
||||
"model.input.image": "изображение",
|
||||
"model.input.audio": "аудио",
|
||||
"model.input.video": "видео",
|
||||
"model.input.pdf": "pdf",
|
||||
"model.tooltip.allows": "Разрешено: {{inputs}}",
|
||||
"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.allowed": "Поддерживает рассуждения",
|
||||
"model.tooltip.reasoning.none": "Без рассуждений",
|
||||
"model.tooltip.context": "Лимит контекста {{limit}}",
|
||||
|
||||
"common.search.placeholder": "Поиск",
|
||||
@@ -231,7 +233,7 @@ export const dict = {
|
||||
"common.clear": "Очистить",
|
||||
"common.connect": "Подключить",
|
||||
"common.disconnect": "Отключить",
|
||||
"common.continue": "Отправить",
|
||||
"common.continue": "Продолжить",
|
||||
"common.submit": "Отправить",
|
||||
"common.save": "Сохранить",
|
||||
"common.saving": "Сохранение...",
|
||||
@@ -241,8 +243,8 @@ export const dict = {
|
||||
"prompt.placeholder.shell": "Введите команду оболочки... {{example}}",
|
||||
"prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"',
|
||||
"prompt.placeholder.simple": "Спросите что угодно...",
|
||||
"prompt.placeholder.summarizeComments": "Суммировать комментарии…",
|
||||
"prompt.placeholder.summarizeComment": "Суммировать комментарий…",
|
||||
"prompt.placeholder.summarizeComments": "Обобщить комментарии…",
|
||||
"prompt.placeholder.summarizeComment": "Обобщить комментарий…",
|
||||
"prompt.mode.shell": "Оболочка",
|
||||
"prompt.mode.normal": "Промпт",
|
||||
"prompt.mode.shell.exit": "esc для выхода",
|
||||
@@ -252,33 +254,33 @@ export const dict = {
|
||||
"prompt.example.1": "Исправить TODO в коде",
|
||||
"prompt.example.2": "Какой технологический стек этого проекта?",
|
||||
"prompt.example.3": "Исправить сломанные тесты",
|
||||
"prompt.example.4": "Объясни как работает аутентификация",
|
||||
"prompt.example.4": "Объясни, как работает аутентификация",
|
||||
"prompt.example.5": "Найти и исправить уязвимости безопасности",
|
||||
"prompt.example.6": "Добавить юнит-тесты для сервиса пользователя",
|
||||
"prompt.example.6": "Добавить модульные тесты для службы пользователей",
|
||||
"prompt.example.7": "Рефакторить эту функцию для лучшей читаемости",
|
||||
"prompt.example.8": "Что означает эта ошибка?",
|
||||
"prompt.example.9": "Помоги мне отладить эту проблему",
|
||||
"prompt.example.10": "Сгенерировать документацию API",
|
||||
"prompt.example.11": "Оптимизировать запросы к базе данных",
|
||||
"prompt.example.12": "Добавить валидацию ввода",
|
||||
"prompt.example.12": "Добавить проверку входных данных",
|
||||
"prompt.example.13": "Создать новый компонент для...",
|
||||
"prompt.example.14": "Как развернуть этот проект?",
|
||||
"prompt.example.15": "Проверь мой код на лучшие практики",
|
||||
"prompt.example.15": "Проверь, соответствует ли мой код рекомендациям",
|
||||
"prompt.example.16": "Добавить обработку ошибок в эту функцию",
|
||||
"prompt.example.17": "Объясни этот паттерн regex",
|
||||
"prompt.example.18": "Конвертировать это в TypeScript",
|
||||
"prompt.example.17": "Объясни это регулярное выражение",
|
||||
"prompt.example.18": "Преобразовать это в TypeScript",
|
||||
"prompt.example.19": "Добавить логирование по всему проекту",
|
||||
"prompt.example.20": "Какие зависимости устарели?",
|
||||
"prompt.example.21": "Помоги написать скрипт миграции",
|
||||
"prompt.example.22": "Реализовать кэширование для этой конечной точки",
|
||||
"prompt.example.22": "Реализовать кеширование для этой конечной точки",
|
||||
"prompt.example.23": "Добавить пагинацию в этот список",
|
||||
"prompt.example.24": "Создать CLI команду для...",
|
||||
"prompt.example.25": "Как работают переменные окружения здесь?",
|
||||
"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",
|
||||
@@ -297,6 +299,7 @@ export const dict = {
|
||||
"prompt.action.stop": "Остановить",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Неподдерживаемое вложение",
|
||||
"prompt.toast.attachmentDuplicate.title": "Этот файл уже загружен",
|
||||
"prompt.toast.pasteUnsupported.description": "Здесь можно прикрепить только изображения, PDF или текстовые файлы.",
|
||||
"prompt.toast.modelAgentRequired.title": "Выберите агента и модель",
|
||||
"prompt.toast.modelAgentRequired.description": "Выберите агента и модель перед отправкой запроса.",
|
||||
@@ -331,7 +334,7 @@ export const dict = {
|
||||
"dialog.directory.readError": "Не удалось прочитать эту папку",
|
||||
|
||||
"dialog.server.title": "Серверы",
|
||||
"dialog.server.description": "Переключите сервер OpenCode к которому подключается приложение.",
|
||||
"dialog.server.description": "Выберите сервер OpenCode, к которому подключается приложение.",
|
||||
"dialog.server.search.placeholder": "Поиск серверов",
|
||||
"dialog.server.empty": "Серверов пока нет",
|
||||
"dialog.server.add.title": "Добавить сервер",
|
||||
@@ -355,7 +358,7 @@ export const dict = {
|
||||
|
||||
"dialog.server.menu.edit": "Редактировать",
|
||||
"dialog.server.menu.default": "Сделать по умолчанию",
|
||||
"dialog.server.menu.defaultRemove": "Удалить по умолчанию",
|
||||
"dialog.server.menu.defaultRemove": "Отменить выбор по умолчанию",
|
||||
"dialog.server.menu.delete": "Удалить",
|
||||
"dialog.server.current": "Текущий сервер",
|
||||
"dialog.server.status.default": "По умолч.",
|
||||
@@ -404,7 +407,7 @@ export const dict = {
|
||||
"wsl.onboarding.needAnotherDistroHint": "Установите дистрибутив Linux из каталога WSL",
|
||||
"wsl.onboarding.wslNotInstalled.title": "WSL не установлена",
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"Для добавления сервера WSL в OpenCode требуется WSL (Windows Subsystem for Linux)",
|
||||
"Для добавления сервера WSL в OpenCode требуется WSL (Подсистема Windows для Linux)",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL недоступна",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode не смог проверить WSL на этом компьютере.",
|
||||
"wsl.onboarding.installWsl": "Установить WSL",
|
||||
@@ -431,10 +434,10 @@ export const dict = {
|
||||
|
||||
"dialog.project.edit.title": "Редактировать проект",
|
||||
"dialog.project.edit.name": "Название",
|
||||
"dialog.project.edit.icon": "Иконка",
|
||||
"dialog.project.edit.icon.alt": "Иконка проекта",
|
||||
"dialog.project.edit.icon": "Значок",
|
||||
"dialog.project.edit.icon.alt": "Значок проекта",
|
||||
"dialog.project.edit.icon.hint": "Нажмите или перетащите изображение",
|
||||
"dialog.project.edit.icon.recommended": "Рекомендуется: 128x128px",
|
||||
"dialog.project.edit.icon.recommended": "Рекомендуется: 128 x 128 пикселей",
|
||||
"dialog.project.edit.color": "Цвет",
|
||||
"dialog.project.edit.color.select": "Выбрать цвет {{color}}",
|
||||
|
||||
@@ -464,7 +467,7 @@ export const dict = {
|
||||
"context.stats.inputTokens": "Входные токены",
|
||||
"context.stats.outputTokens": "Выходные токены",
|
||||
"context.stats.reasoningTokens": "Токены рассуждения",
|
||||
"context.stats.cacheTokens": "Токены кэша (чтение/запись)",
|
||||
"context.stats.cacheTokens": "Токены кеша (чтение/запись)",
|
||||
"context.stats.userMessages": "Сообщения пользователя",
|
||||
"context.stats.assistantMessages": "Сообщения ассистента",
|
||||
"context.stats.totalCost": "Общая стоимость",
|
||||
@@ -563,27 +566,27 @@ export const dict = {
|
||||
"error.chain.causedBy": "Причина:",
|
||||
"error.chain.apiError": "Ошибка API",
|
||||
"error.chain.status": "Статус: {{status}}",
|
||||
"error.chain.retryable": "Повторная попытка: {{retryable}}",
|
||||
"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.checkConfig": "Проверьте названия провайдера и модели в конфигурации (opencode.json)",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP сервер "{{name}}" завершился с ошибкой. Обратите внимание, что OpenCode пока не поддерживает MCP авторизацию.',
|
||||
'MCP-сервер "{{name}}" завершился с ошибкой. Обратите внимание: OpenCode пока не поддерживает MCP-авторизацию.',
|
||||
"error.chain.providerAuthFailed": "Ошибка аутентификации провайдера ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'Не удалось инициализировать провайдера "{{provider}}". Проверьте учётные данные и конфигурацию.',
|
||||
"error.chain.configJsonInvalid": "Конфигурационный файл по адресу {{path}} не является валидным JSON(C)",
|
||||
"error.chain.configJsonInvalid": "Конфигурационный файл по адресу {{path}} содержит некорректный JSON(C)",
|
||||
"error.chain.configJsonInvalidWithMessage":
|
||||
"Конфигурационный файл по адресу {{path}} не является валидным JSON(C): {{message}}",
|
||||
"Конфигурационный файл по адресу {{path}} содержит некорректный JSON(C): {{message}}",
|
||||
"error.chain.configDirectoryTypo":
|
||||
'Папка "{{dir}}" в {{path}} невалидна. Переименуйте папку в "{{suggestion}}" или удалите её. Это распространённая опечатка.',
|
||||
'Папка "{{dir}}" в {{path}} недопустима. Переименуйте папку в "{{suggestion}}" или удалите её. Это распространённая опечатка.',
|
||||
"error.chain.configFrontmatterError": "Не удалось разобрать frontmatter в {{path}}:\n{{message}}",
|
||||
"error.chain.configInvalid": "Конфигурационный файл по адресу {{path}} невалиден",
|
||||
"error.chain.configInvalidWithMessage": "Конфигурационный файл по адресу {{path}} невалиден: {{message}}",
|
||||
"error.chain.configInvalid": "Недопустимый конфигурационный файл по адресу {{path}}",
|
||||
"error.chain.configInvalidWithMessage": "Недопустимый конфигурационный файл по адресу {{path}}: {{message}}",
|
||||
|
||||
"notification.permission.title": "Требуется разрешение",
|
||||
"notification.permission.description": "{{sessionTitle}} в {{projectName}} требуется разрешение",
|
||||
"notification.permission.description": "Для {{sessionTitle}} в {{projectName}} требуется разрешение",
|
||||
"notification.question.title": "Вопрос",
|
||||
"notification.question.description": "У {{sessionTitle}} в {{projectName}} есть вопрос",
|
||||
"notification.action.goToSession": "Перейти к сессии",
|
||||
@@ -622,7 +625,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": "Загрузка изменений...",
|
||||
@@ -651,13 +654,13 @@ export const dict = {
|
||||
"session.question.pending.one": "{{count}} вопрос без ответа",
|
||||
"session.question.pending.other": "Вопросы без ответа: {{count}}",
|
||||
"session.followupDock.summary.one": "{{count}} сообщение в очереди",
|
||||
"session.followupDock.summary.other": "{{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.summary.other": "Возвращено сообщений: {{count}}",
|
||||
"session.revertDock.collapse": "Свернуть возвращённые сообщения",
|
||||
"session.revertDock.expand": "Развернуть возвращённые сообщения",
|
||||
"session.revertDock.restore": "Восстановить сообщение",
|
||||
@@ -706,7 +709,7 @@ export const dict = {
|
||||
"session.share.copy.copied": "Скопировано",
|
||||
"session.share.copy.copyLink": "Копировать ссылку",
|
||||
|
||||
"lsp.tooltip.none": "Нет LSP серверов",
|
||||
"lsp.tooltip.none": "Нет LSP-серверов",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
|
||||
"prompt.loading": "Загрузка запроса...",
|
||||
@@ -763,7 +766,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",
|
||||
@@ -773,7 +776,7 @@ export const dict = {
|
||||
"settings.general.row.shell.autoDefault": "Авто (по умолчанию)",
|
||||
"settings.general.row.shell.terminalOnly": "только терминал",
|
||||
"settings.general.row.appearance.title": "Внешний вид",
|
||||
"settings.general.row.appearance.description": "Настройте как OpenCode выглядит на вашем устройстве",
|
||||
"settings.general.row.appearance.description": "Настройте внешний вид OpenCode на вашем устройстве",
|
||||
"settings.general.row.colorScheme.title": "Цветовая схема",
|
||||
"settings.general.row.colorScheme.description": "Выберите, следует ли OpenCode системной, светлой или тёмной теме",
|
||||
"settings.general.row.theme.title": "Тема",
|
||||
@@ -783,7 +786,7 @@ export const dict = {
|
||||
"settings.general.row.terminalFont.title": "Шрифт терминала",
|
||||
"settings.general.row.terminalFont.description": "Настройте шрифт, используемый в терминале",
|
||||
"settings.general.row.uiFont.title": "Шрифт интерфейса",
|
||||
"settings.general.row.uiFont.description": "Настройте шрифт, используемый во всем интерфейсе",
|
||||
"settings.general.row.uiFont.description": "Настройте шрифт, используемый во всём интерфейсе",
|
||||
"settings.general.row.followup.title": "Поведение уточняющих вопросов",
|
||||
"settings.general.row.followup.description":
|
||||
"Выберите, отправлять ли уточняющие вопросы сразу или помещать их в очередь",
|
||||
@@ -803,8 +806,9 @@ 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.showCustomAgents.title": "Выбор агента",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Показывать переключатель агентов в редакторе запросов. Если он скрыт, по умолчанию используется агент Build.",
|
||||
"settings.general.row.reasoningSummaries.title": "Показывать сводки рассуждений",
|
||||
"settings.general.row.reasoningSummaries.description": "Отображать сводки рассуждений модели в ленте",
|
||||
|
||||
@@ -821,11 +825,11 @@ 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.title": "Масштабирование сведением пальцев",
|
||||
"settings.general.row.pinchZoom.description":
|
||||
"Разрешить масштабирование жестом щипка на трекпаде и прокруткой с Ctrl",
|
||||
"Разрешить масштабирование сведением пальцев на трекпаде и прокруткой с нажатой клавишей Ctrl",
|
||||
"settings.general.row.wayland.title": "Использовать нативный Wayland",
|
||||
"settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.",
|
||||
"settings.general.row.wayland.description": "Отключить резервный режим X11 в Wayland. Требуется перезапуск.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"На Linux с мониторами разной частоты обновления нативный Wayland может быть стабильнее.",
|
||||
|
||||
@@ -891,22 +895,22 @@ export const dict = {
|
||||
|
||||
"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.notifications.errors.description": "Показывать системное уведомление, когда происходит ошибка",
|
||||
|
||||
"settings.general.sounds.agent.title": "Агент",
|
||||
"settings.general.sounds.agent.description": "Воспроизводить звук когда агент завершён или требует внимания",
|
||||
"settings.general.sounds.agent.description": "Воспроизводить звук, когда агент завершил работу или требует внимания",
|
||||
"settings.general.sounds.permissions.title": "Разрешения",
|
||||
"settings.general.sounds.permissions.description": "Воспроизводить звук когда требуется разрешение",
|
||||
"settings.general.sounds.permissions.description": "Воспроизводить звук, когда требуется разрешение",
|
||||
"settings.general.sounds.errors.title": "Ошибки",
|
||||
"settings.general.sounds.errors.description": "Воспроизводить звук когда происходит ошибка",
|
||||
"settings.general.sounds.errors.description": "Воспроизводить звук, когда происходит ошибка",
|
||||
|
||||
"settings.shortcuts.title": "Горячие клавиши",
|
||||
"settings.shortcuts.reset.button": "Сбросить к умолчаниям",
|
||||
"settings.shortcuts.reset.button": "Восстановить настройки по умолчанию",
|
||||
"settings.shortcuts.reset.toast.title": "Горячие клавиши сброшены",
|
||||
"settings.shortcuts.reset.toast.description": "Горячие клавиши были сброшены к значениям по умолчанию.",
|
||||
"settings.shortcuts.conflict.title": "Сочетание уже используется",
|
||||
@@ -942,7 +946,7 @@ export const dict = {
|
||||
"settings.mcp.description": "Настройки MCP будут доступны здесь.",
|
||||
|
||||
"settings.permissions.title": "Разрешения",
|
||||
"settings.permissions.description": "Контролируйте какие инструменты сервер может использовать по умолчанию.",
|
||||
"settings.permissions.description": "Укажите, какие инструменты сервер может использовать по умолчанию.",
|
||||
"settings.permissions.section.tools": "Инструменты",
|
||||
"settings.permissions.toast.updateFailed.title": "Не удалось обновить разрешения",
|
||||
|
||||
@@ -954,7 +958,7 @@ export const dict = {
|
||||
"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",
|
||||
@@ -964,7 +968,7 @@ export const dict = {
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Запуск команд оболочки",
|
||||
"settings.permissions.tool.task.title": "Task",
|
||||
"settings.permissions.tool.task.description": "Запуск подагентов",
|
||||
"settings.permissions.tool.task.description": "Запуск субагентов",
|
||||
"settings.permissions.tool.skill.title": "Skill",
|
||||
"settings.permissions.tool.skill.description": "Загрузка навыка по имени",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
@@ -998,7 +1002,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": "Удалить рабочее пространство",
|
||||
@@ -1009,13 +1013,13 @@ export const dict = {
|
||||
"workspace.reset.button": "Сбросить рабочее пространство",
|
||||
"workspace.reset.archived.none": "Активные сессии не будут архивированы.",
|
||||
"workspace.reset.archived.one": "1 сессия будет архивирована.",
|
||||
"workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
|
||||
"workspace.reset.archived.many": "Будут архивированы сессии: {{count}}.",
|
||||
"workspace.reset.note": "Это сбросит рабочее пространство до соответствия ветке по умолчанию.",
|
||||
"common.open": "Открыть",
|
||||
"dialog.releaseNotes.action.getStarted": "Начать",
|
||||
"dialog.releaseNotes.action.next": "Далее",
|
||||
"dialog.releaseNotes.action.hideFuture": "Больше не показывать",
|
||||
"dialog.releaseNotes.media.alt": "Превью релиза",
|
||||
"dialog.releaseNotes.media.alt": "Предварительный просмотр выпуска",
|
||||
"toast.project.reloadFailed.title": "Не удалось перезагрузить {{project}}",
|
||||
"error.server.invalidConfiguration": "Недопустимая конфигурация",
|
||||
"common.moreCountSuffix": " (ещё {{count}})",
|
||||
@@ -1040,6 +1044,9 @@ export const dict = {
|
||||
"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",
|
||||
@@ -1058,7 +1065,7 @@ export const dict = {
|
||||
"debugBar.na": "н/д",
|
||||
"debugBar.nav.label": "NAV",
|
||||
"debugBar.nav.tip":
|
||||
"Последний завершенный переход маршрута, затрагивающий страницу сеанса, измеренный от запуска маршрутизатора до первой отрисовки после стабилизации.",
|
||||
"Последний завершённый переход по маршруту, затрагивающий страницу сессии: от запуска маршрутизатора до первой отрисовки после стабилизации.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Скользящая частота кадров в секунду за последние 5 секунд.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
@@ -1073,7 +1080,7 @@ export const dict = {
|
||||
"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}}.",
|
||||
@@ -1095,7 +1102,7 @@ export const dict = {
|
||||
"error.page.circular": "[Циклично]",
|
||||
"error.serverSDK.noServerAvailable": "Нет доступного сервера",
|
||||
"error.serverSDK.serverNotAvailable": "Сервер недоступен",
|
||||
"error.childStore.persistedCacheCreateFailed": "Не удалось создать постоянный кэш",
|
||||
"error.childStore.persistedCacheCreateFailed": "Не удалось создать постоянный кеш",
|
||||
"error.childStore.persistedProjectMetadataCreateFailed": "Не удалось создать постоянные метаданные проекта",
|
||||
"error.childStore.persistedProjectIconCreateFailed": "Не удалось создать постоянный значок проекта",
|
||||
"error.childStore.storeCreateFailed": "Не удалось создать хранилище",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+72
-65
@@ -53,7 +53,7 @@ export const dict = {
|
||||
"command.context.addSelection.description": "เพิ่มบรรทัดที่เลือกจากไฟล์ปัจจุบัน",
|
||||
"command.input.focus": "โฟกัสช่องป้อนข้อมูล",
|
||||
"command.terminal.toggle": "สลับเทอร์มินัล",
|
||||
"command.fileTree.toggle": "สลับต้นไม้ไฟล์",
|
||||
"command.fileTree.toggle": "สลับแผนผังไฟล์",
|
||||
"command.review.toggle": "สลับการตรวจสอบ",
|
||||
"command.terminal.new": "เทอร์มินัลใหม่",
|
||||
"command.terminal.new.description": "สร้างแท็บเทอร์มินัลใหม่",
|
||||
@@ -79,8 +79,8 @@ export const dict = {
|
||||
"command.permissions.autoaccept.disable": "หยุดยอมรับสิทธิ์โดยอัตโนมัติ",
|
||||
"command.workspace.toggle": "สลับพื้นที่ทำงาน",
|
||||
"command.workspace.toggle.description": "เปิดหรือปิดใช้งานพื้นที่ทำงานหลายรายการในแถบด้านข้าง",
|
||||
"command.session.undo": "ยกเลิก",
|
||||
"command.session.undo.description": "ยกเลิกข้อความล่าสุด",
|
||||
"command.session.undo": "เลิกทำ",
|
||||
"command.session.undo.description": "เลิกทำข้อความล่าสุด",
|
||||
"command.session.redo": "ทำซ้ำ",
|
||||
"command.session.redo.description": "ทำซ้ำข้อความที่ถูกยกเลิกล่าสุด",
|
||||
"command.session.compact": "บีบอัดเซสชัน",
|
||||
@@ -102,6 +102,7 @@ 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": "โมเดลที่เชื่อถือได้และปรับให้เหมาะสม",
|
||||
@@ -122,6 +123,7 @@ export const dict = {
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "โมเดลฟรีที่จัดหาให้โดย OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "เพิ่มโมเดลเพิ่มเติมจากผู้ให้บริการยอดนิยม",
|
||||
"dialog.model.unpaid.viewMoreProviders": "ดูผู้ให้บริการเพิ่มเติมกว่า 70 ราย",
|
||||
|
||||
"dialog.provider.viewAll": "แสดงผู้ให้บริการเพิ่มเติม",
|
||||
|
||||
@@ -143,11 +145,11 @@ export const dict = {
|
||||
"ด้วยคีย์ 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.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}} ใน OpenCode",
|
||||
"provider.connect.oauth.code.label": "รหัสการอนุญาต {{method}}",
|
||||
"provider.connect.oauth.code.placeholder": "รหัสการอนุญาต",
|
||||
"provider.connect.oauth.code.required": "ต้องใช้รหัสการอนุญาต",
|
||||
@@ -157,13 +159,13 @@ export const dict = {
|
||||
"provider.connect.oauth.auto.visit.suffix":
|
||||
" และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน OpenCode",
|
||||
"provider.connect.oauth.auto.confirmationCode": "รหัสยืนยัน",
|
||||
"provider.connect.toast.connected.title": "{{provider}} ที่เชื่อมต่อแล้ว",
|
||||
"provider.connect.toast.connected.title": "เชื่อมต่อ {{provider}} แล้ว",
|
||||
"provider.connect.toast.connected.description": "โมเดล {{provider}} พร้อมใช้งานแล้ว",
|
||||
|
||||
"provider.custom.title": "ผู้ให้บริการที่กำหนดเอง",
|
||||
"provider.custom.description.prefix": "กำหนดค่าผู้ให้บริการที่เข้ากันได้กับ OpenAI ดู ",
|
||||
"provider.custom.description.link": "เอกสารการกำหนดค่าผู้ให้บริการ",
|
||||
"provider.custom.description.suffix": ".",
|
||||
"provider.custom.description.suffix": "",
|
||||
"provider.custom.field.providerID.label": "รหัสผู้ให้บริการ",
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "ตัวอักษรพิมพ์เล็ก ตัวเลข ยัติภังค์ หรือขีดล่าง",
|
||||
@@ -197,7 +199,7 @@ export const dict = {
|
||||
"provider.custom.error.required": "จำเป็น",
|
||||
"provider.custom.error.duplicate": "ซ้ำ",
|
||||
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} ที่ยกเลิกการเชื่อมต่อแล้ว",
|
||||
"provider.disconnect.toast.disconnected.title": "ยกเลิกการเชื่อมต่อ {{provider}} แล้ว",
|
||||
"provider.disconnect.toast.disconnected.description": "โมเดล {{provider}} ไม่พร้อมใช้งานอีกต่อไป",
|
||||
|
||||
"model.tag.free": "ฟรี",
|
||||
@@ -212,7 +214,7 @@ export const dict = {
|
||||
"model.input.audio": "เสียง",
|
||||
"model.input.video": "วิดีโอ",
|
||||
"model.input.pdf": "PDF",
|
||||
"model.tooltip.allows": "อนุญาต: {{inputs}}",
|
||||
"model.tooltip.allows": "รองรับ: {{inputs}}",
|
||||
"model.tooltip.context.label": "บริบท",
|
||||
"model.tooltip.inputs": "ข้อมูลนำเข้า",
|
||||
"model.tooltip.model": "โมเดล",
|
||||
@@ -224,14 +226,14 @@ export const dict = {
|
||||
|
||||
"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": "กำลังบันทึก...",
|
||||
@@ -246,8 +248,8 @@ export const dict = {
|
||||
"prompt.mode.shell": "เชลล์",
|
||||
"prompt.mode.normal": "พรอมต์",
|
||||
"prompt.mode.shell.exit": "กด esc เพื่อออก",
|
||||
"session.child.promptDisabled": "ไม่สามารถส่งพรอมต์ในเซสชันเอเจนต์ย่อยได้.",
|
||||
"session.child.backToParent": "กลับไปยังเซสชันหลัก.",
|
||||
"session.child.promptDisabled": "ไม่สามารถส่งพรอมต์ในเซสชันเอเจนต์ย่อยได้",
|
||||
"session.child.backToParent": "กลับไปยังเซสชันหลัก",
|
||||
|
||||
"prompt.example.1": "แก้ไข TODO ในโค้ดเบส",
|
||||
"prompt.example.2": "เทคโนโลยีของโปรเจกต์นี้คืออะไร?",
|
||||
@@ -262,7 +264,7 @@ export const dict = {
|
||||
"prompt.example.11": "ปรับปรุงการสืบค้นฐานข้อมูล",
|
||||
"prompt.example.12": "เพิ่มการตรวจสอบข้อมูลนำเข้า",
|
||||
"prompt.example.13": "สร้างคอมโพเนนต์ใหม่สำหรับ...",
|
||||
"prompt.example.14": "ฉันจะทำให้โปรเจกต์นี้ทำงานได้อย่างไร?",
|
||||
"prompt.example.14": "ฉันจะนำโปรเจกต์นี้ไปใช้งานได้อย่างไร?",
|
||||
"prompt.example.15": "ตรวจสอบโค้ดของฉันเพื่อแนวทางปฏิบัติที่ดีที่สุด",
|
||||
"prompt.example.16": "เพิ่มการจัดการข้อผิดพลาดในฟังก์ชันนี้",
|
||||
"prompt.example.17": "อธิบายรูปแบบ regex นี้",
|
||||
@@ -270,15 +272,15 @@ export const dict = {
|
||||
"prompt.example.19": "เพิ่มการบันทึกทั่วทั้งโค้ดเบส",
|
||||
"prompt.example.20": "มีการพึ่งพาอะไรที่ล้าสมัยอยู่?",
|
||||
"prompt.example.21": "ช่วยฉันเขียนสคริปต์การย้ายข้อมูล",
|
||||
"prompt.example.22": "ใช้งานแคชสำหรับจุดสิ้นสุดนี้",
|
||||
"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.label": "ลากรูปภาพ PDF หรือไฟล์ข้อความมาวางที่นี่",
|
||||
"prompt.dropzone.file.label": "วางเพื่ออ้างถึงไฟล์ด้วย @",
|
||||
"prompt.slash.badge.custom": "กำหนดเอง",
|
||||
"prompt.slash.badge.skill": "ทักษะ",
|
||||
"prompt.slash.badge.mcp": "MCP",
|
||||
@@ -297,14 +299,15 @@ export const dict = {
|
||||
"prompt.action.stop": "หยุด",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "ไฟล์แนบที่ไม่รองรับ",
|
||||
"prompt.toast.pasteUnsupported.description": "แนบได้เฉพาะรูปภาพ, PDF หรือไฟล์ข้อความเท่านั้น",
|
||||
"prompt.toast.attachmentDuplicate.title": "ไฟล์นี้ถูกอัปโหลดแล้ว",
|
||||
"prompt.toast.pasteUnsupported.description": "แนบได้เฉพาะรูปภาพ PDF หรือไฟล์ข้อความเท่านั้น",
|
||||
"prompt.toast.modelAgentRequired.title": "เลือกเอเจนต์และโมเดล",
|
||||
"prompt.toast.modelAgentRequired.description": "เลือกเอเจนต์และโมเดลก่อนส่งพร้อมท์",
|
||||
"prompt.toast.modelAgentRequired.description": "เลือกเอเจนต์และโมเดลก่อนส่งพรอมต์",
|
||||
"prompt.toast.worktreeCreateFailed.title": "ไม่สามารถสร้าง worktree",
|
||||
"prompt.toast.sessionCreateFailed.title": "ไม่สามารถสร้างเซสชัน",
|
||||
"prompt.toast.shellSendFailed.title": "ไม่สามารถส่งคำสั่งเชลล์",
|
||||
"prompt.toast.commandSendFailed.title": "ไม่สามารถส่งคำสั่ง",
|
||||
"prompt.toast.promptSendFailed.title": "ไม่สามารถส่งพร้อมท์",
|
||||
"prompt.toast.promptSendFailed.title": "ไม่สามารถส่งพรอมต์",
|
||||
"prompt.toast.promptSendFailed.description": "ไม่สามารถดึงเซสชันได้",
|
||||
|
||||
"dialog.mcp.title": "MCPs",
|
||||
@@ -316,7 +319,7 @@ export const dict = {
|
||||
|
||||
"mcp.status.connected": "เชื่อมต่อแล้ว",
|
||||
"mcp.status.failed": "ล้มเหลว",
|
||||
"mcp.status.needs_auth": "ต้องการการตรวจสอบสิทธิ์",
|
||||
"mcp.status.needs_auth": "ต้องยืนยันตัวตน",
|
||||
"mcp.auth.clickToAuthenticate": "คลิกเพื่อยืนยันตัวตน",
|
||||
"mcp.status.disabled": "ปิดใช้งาน",
|
||||
|
||||
@@ -326,7 +329,7 @@ export const dict = {
|
||||
"dialog.directory.empty": "ไม่พบโฟลเดอร์",
|
||||
"dialog.directory.action.selectFile": "เลือกไฟล์",
|
||||
"dialog.directory.action.selectFolder": "เลือกโฟลเดอร์",
|
||||
"dialog.directory.root": "ราก",
|
||||
"dialog.directory.root": "โฟลเดอร์ราก",
|
||||
"dialog.directory.parent": "โฟลเดอร์หลัก",
|
||||
"dialog.directory.readError": "ไม่สามารถอ่านโฟลเดอร์นี้ได้",
|
||||
|
||||
@@ -368,30 +371,30 @@ export const dict = {
|
||||
"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.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.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.updateOpencodeIn": "อัปเดต OpenCode ใน {{distro}}",
|
||||
"wsl.onboarding.updateOpencode": "อัปเดต OpenCode",
|
||||
"wsl.onboarding.opencodeReadyIn": "OpenCode พร้อมใช้งานใน {{distro}} แล้ว.",
|
||||
"wsl.onboarding.opencodeReady": "OpenCode พร้อมใช้งานแล้ว.",
|
||||
"wsl.onboarding.installOpencodeIn": "ติดตั้ง OpenCode ใน {{distro}}.",
|
||||
"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.chooseDistroFirst": "เลือกดิสโทรก่อน",
|
||||
"wsl.onboarding.loadFailed": "ไม่สามารถโหลดสถานะ WSL ได้",
|
||||
"wsl.onboarding.loading": "กำลังโหลด...",
|
||||
"wsl.onboarding.installedDistros": "ดิสโทรที่ติดตั้งแล้ว",
|
||||
"wsl.onboarding.checkAgain": "ตรวจสอบอีกครั้ง",
|
||||
@@ -406,27 +409,27 @@ export const dict = {
|
||||
"wsl.onboarding.wslNotInstalled.description":
|
||||
"ต้องมี WSL (Windows Subsystem for Linux) ก่อนที่ OpenCode จะเพิ่มเซิร์ฟเวอร์ WSL ได้",
|
||||
"wsl.onboarding.wslUnavailable.title": "WSL ไม่พร้อมใช้งาน",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode ไม่สามารถตรวจสอบ WSL บนเครื่องนี้ได้.",
|
||||
"wsl.onboarding.wslUnavailable.description": "OpenCode ไม่สามารถตรวจสอบ WSL บนเครื่องนี้ได้",
|
||||
"wsl.onboarding.installWsl": "ติดตั้ง WSL",
|
||||
"wsl.onboarding.windowsRestartRequired":
|
||||
"รีสตาร์ท Windows เพื่อให้การติดตั้ง WSL เสร็จสมบูรณ์ แล้วเปิด OpenCode อีกครั้ง.",
|
||||
"รีสตาร์ท Windows เพื่อให้การติดตั้ง WSL เสร็จสมบูรณ์ แล้วเปิด OpenCode อีกครั้ง",
|
||||
"wsl.onboarding.next": "ถัดไป",
|
||||
"wsl.onboarding.refresh": "รีเฟรช",
|
||||
"wsl.onboarding.allDistrosAdded": "เพิ่มดิสโทรที่ติดตั้งไว้ทั้งหมดแล้ว.",
|
||||
"wsl.onboarding.noDistros": "ยังไม่พบดิสโทร.",
|
||||
"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.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.versionMismatch": "เวอร์ชันที่ติดตั้งไม่ตรงกับเวอร์ชันของแอปเดสก์ท็อป",
|
||||
"wsl.onboarding.adding": "กำลังเพิ่ม...",
|
||||
|
||||
"dialog.project.edit.title": "แก้ไขโปรเจกต์",
|
||||
@@ -449,7 +452,7 @@ export const dict = {
|
||||
"context.breakdown.tool": "การเรียกเครื่องมือ",
|
||||
"context.breakdown.other": "อื่น ๆ",
|
||||
|
||||
"context.systemPrompt.title": "พร้อมท์ระบบ",
|
||||
"context.systemPrompt.title": "พรอมต์ระบบ",
|
||||
"context.rawMessages.title": "ข้อความดิบ",
|
||||
|
||||
"context.stats.session": "เซสชัน",
|
||||
@@ -539,7 +542,7 @@ export const dict = {
|
||||
|
||||
"error.page.title": "เกิดข้อผิดพลาด",
|
||||
"error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน",
|
||||
"error.page.description.localServerStartup": "เกิดข้อผิดพลาดขณะเริ่มเซิร์ฟเวอร์ในเครื่อง.",
|
||||
"error.page.description.localServerStartup": "เกิดข้อผิดพลาดขณะเริ่มเซิร์ฟเวอร์ในเครื่อง",
|
||||
"error.page.details.label": "รายละเอียดข้อผิดพลาด",
|
||||
"error.page.action.restart": "รีสตาร์ท",
|
||||
"error.page.action.report": "รายงานข้อผิดพลาด",
|
||||
@@ -583,7 +586,7 @@ export const dict = {
|
||||
"notification.question.description": "{{sessionTitle}} ใน {{projectName}} มีคำถาม",
|
||||
"notification.action.goToSession": "ไปที่เซสชัน",
|
||||
|
||||
"notification.session.responseReady.title": "การตอบสนองพร้อม",
|
||||
"notification.session.responseReady.title": "คำตอบพร้อมแล้ว",
|
||||
"notification.session.error.title": "ข้อผิดพลาดเซสชัน",
|
||||
"notification.session.error.fallbackDescription": "เกิดข้อผิดพลาด",
|
||||
|
||||
@@ -605,7 +608,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": "ตรวจสอบ",
|
||||
@@ -613,7 +616,7 @@ export const dict = {
|
||||
"session.tab.unknown": "เซสชันที่ไม่รู้จัก",
|
||||
"session.panel.reviewAndFiles": "ตรวจสอบและไฟล์",
|
||||
"session.error.notFound": "ไม่พบเซสชันนี้",
|
||||
"session.error.notFound.description": "แท็บนี้อ้างถึงเซสชันที่ไม่มีอยู่บนเซิร์ฟเวอร์นี้แล้ว.",
|
||||
"session.error.notFound.description": "แท็บนี้อ้างถึงเซสชันที่ไม่มีอยู่บนเซิร์ฟเวอร์นี้แล้ว",
|
||||
"session.error.notFound.closeTab": "ปิดแท็บ",
|
||||
"session.error.serverConnection": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์นี้",
|
||||
"session.review.filesChanged": "{{count}} ไฟล์ที่เปลี่ยนแปลง",
|
||||
@@ -703,7 +706,7 @@ export const dict = {
|
||||
"lsp.tooltip.none": "ไม่มีเซิร์ฟเวอร์ LSP",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
|
||||
"prompt.loading": "กำลังโหลดพร้อมท์...",
|
||||
"prompt.loading": "กำลังโหลดพรอมต์...",
|
||||
"terminal.loading": "กำลังโหลดเทอร์มินัล...",
|
||||
"terminal.title": "เทอร์มินัล",
|
||||
"terminal.title.numbered": "เทอร์มินัล {{number}}",
|
||||
@@ -766,23 +769,23 @@ export const dict = {
|
||||
"settings.general.row.shell.autoDefault": "อัตโนมัติ (ค่าเริ่มต้น)",
|
||||
"settings.general.row.shell.terminalOnly": "เทอร์มินัลเท่านั้น",
|
||||
"settings.general.row.appearance.title": "รูปลักษณ์",
|
||||
"settings.general.row.appearance.description": "ปรับแต่งวิธีการที่ OpenCode มีลักษณะบนอุปกรณ์ของคุณ",
|
||||
"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.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.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.showFileTree.title": "แผนผังไฟล์",
|
||||
"settings.general.row.showFileTree.description": "แสดงแผงแผนผังไฟล์ในเซสชัน",
|
||||
"settings.general.row.showNavigation.title": "ตัวควบคุมการนำทาง",
|
||||
"settings.general.row.showNavigation.description": "แสดงปุ่มย้อนกลับและไปข้างหน้าในแถบชื่อเรื่องเดสก์ท็อป",
|
||||
"settings.general.row.showSearch.title": "ชุดคำสั่ง",
|
||||
@@ -794,8 +797,9 @@ 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.showCustomAgents.title": "แสดงเอเจนต์",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"สลับระหว่างเอเจนต์ในช่องเขียนข้อความ เมื่อซ่อน ระบบจะใช้เอเจนต์ Build เป็นค่าเริ่มต้น",
|
||||
"settings.general.row.reasoningSummaries.title": "แสดงสรุปการใช้เหตุผล",
|
||||
"settings.general.row.reasoningSummaries.description": "แสดงสรุปการใช้เหตุผลของโมเดลในไทม์ไลน์",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "ขยายส่วนเครื่องมือ shell",
|
||||
@@ -907,7 +911,7 @@ export const dict = {
|
||||
"settings.shortcuts.group.navigation": "การนำทาง",
|
||||
"settings.shortcuts.group.modelAndAgent": "โมเดลและเอเจนต์",
|
||||
"settings.shortcuts.group.terminal": "เทอร์มินัล",
|
||||
"settings.shortcuts.group.prompt": "พร้อมท์",
|
||||
"settings.shortcuts.group.prompt": "พรอมต์",
|
||||
|
||||
"settings.providers.title": "ผู้ให้บริการ",
|
||||
"settings.providers.description": "การตั้งค่าผู้ให้บริการจะสามารถกำหนดค่าได้ที่นี่",
|
||||
@@ -944,7 +948,7 @@ export const dict = {
|
||||
"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": "เรียกใช้คำสั่งเชลล์",
|
||||
@@ -1017,13 +1021,16 @@ export const dict = {
|
||||
"dialog.server.add.passwordPlaceholder": "รหัสผ่าน",
|
||||
"server.row.noUsername": "ไม่มีชื่อผู้ใช้",
|
||||
"session.review.noVcs.createGit.title": "สร้าง Git รีโพซิทอรี",
|
||||
"session.review.noVcs.createGit.description": "ติดตาม ตรวจสอบ และเลิกทำสิ่งเปลี่ยนแปลงในโปรเจกต์นี้",
|
||||
"session.review.noVcs.createGit.description": "ติดตาม ตรวจสอบ และเลิกทำการเปลี่ยนแปลงในโปรเจกต์นี้",
|
||||
"session.review.noVcs.createGit.actionLoading": "กำลังสร้าง Git รีโพซิทอรี...",
|
||||
"session.review.noVcs.createGit.action": "สร้าง Git รีโพซิทอรี",
|
||||
"session.todo.progress": "เสร็จสิ้น {{done}} จาก {{total}} รายการ",
|
||||
"session.question.progress": "{{current}} จาก {{total}} คำถาม",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "File Explorer",
|
||||
"session.header.reveal.finder": "แสดงใน Finder",
|
||||
"session.header.reveal.fileExplorer": "แสดงใน File Explorer",
|
||||
"session.header.reveal.containingFolder": "เปิดโฟลเดอร์ที่มีรายการนี้",
|
||||
"session.header.open.fileManager": "File Manager",
|
||||
"session.header.open.app.vscode": "VS Code",
|
||||
"session.header.open.app.cursor": "Cursor",
|
||||
|
||||
+68
-61
@@ -78,9 +78,9 @@ export const dict = {
|
||||
"command.model.variant.cycle": "Düşünme eforu değiştir",
|
||||
"command.model.variant.cycle.description": "Sonraki efor seviyesine geç",
|
||||
"command.prompt.mode.shell": "Kabuk",
|
||||
"command.prompt.mode.normal": "Komut",
|
||||
"command.permissions.autoaccept.enable": "Düzenlemeleri otomatik kabul et",
|
||||
"command.permissions.autoaccept.disable": "Otomatik kabulü durdur",
|
||||
"command.prompt.mode.normal": "İstem",
|
||||
"command.permissions.autoaccept.enable": "İzinleri otomatik kabul et",
|
||||
"command.permissions.autoaccept.disable": "İzinleri otomatik kabul etmeyi durdur",
|
||||
"command.workspace.toggle": "Çalışma alanlarını aç/kapat",
|
||||
"command.workspace.toggle.description": "Kenar çubuğunda birden fazla çalışma alanını göster veya gizle",
|
||||
"command.session.undo": "Geri al",
|
||||
@@ -106,9 +106,10 @@ export const dict = {
|
||||
"dialog.provider.empty": "Sağlayıcı bulunamadı",
|
||||
"dialog.provider.group.popular": "Popüler",
|
||||
"dialog.provider.group.other": "Diğer",
|
||||
"dialog.provider.custom.label": "Özel OpenAI uyumlu sağlayıcı",
|
||||
"dialog.provider.tag.recommended": "Önerilen",
|
||||
"dialog.provider.opencode.note": "Claude, GPT, Gemini ve daha fazlasını içeren seçilmiş modeller",
|
||||
"dialog.provider.opencode.tagline": "Güvenilir optimize edilmiş modeller",
|
||||
"dialog.provider.opencode.tagline": "Güvenilir, optimize edilmiş modeller",
|
||||
"dialog.provider.opencodeGo.tagline": "Herkes için düşük maliyetli abonelik",
|
||||
"dialog.provider.anthropic.note": "Pro ve Max dahil Claude modellerine doğrudan erişim",
|
||||
"dialog.provider.copilot.note": "GitHub Copilot üzerinden kodlama yardımı için yapay zekâ modelleri",
|
||||
@@ -126,6 +127,7 @@ export const dict = {
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCode tarafından sunulan ücretsiz modeller",
|
||||
"dialog.model.unpaid.addMore.title": "Popüler sağlayıcılardan daha fazla model ekleyin",
|
||||
"dialog.model.unpaid.viewMoreProviders": "70'ten fazla sağlayıcı daha görüntüle",
|
||||
|
||||
"dialog.provider.viewAll": "Daha fazla sağlayıcı göster",
|
||||
|
||||
@@ -166,9 +168,9 @@ export const dict = {
|
||||
|
||||
"provider.custom.title": "Özel sağlayıcı",
|
||||
"provider.custom.description.prefix": "OpenAI uyumlu bir sağlayıcı yapılandırın. ",
|
||||
"provider.custom.description.link": "Sağlayıcı yapılandırma dökümanları",
|
||||
"provider.custom.description.link": "sağlayıcı yapılandırma dokümanları",
|
||||
"provider.custom.description.suffix": " sayfasına bakın.",
|
||||
"provider.custom.field.providerID.label": "Sağlayıcı kimlik",
|
||||
"provider.custom.field.providerID.label": "Sağlayıcı kimliği",
|
||||
"provider.custom.field.providerID.placeholder": "saglayicim",
|
||||
"provider.custom.field.providerID.description": "Küçük harfler, rakamlar, tire veya alt çizgi",
|
||||
"provider.custom.field.name.label": "Görünen ad",
|
||||
@@ -178,7 +180,7 @@ export const dict = {
|
||||
"provider.custom.field.apiKey.label": "API anahtarı",
|
||||
"provider.custom.field.apiKey.placeholder": "API anahtarı",
|
||||
"provider.custom.field.apiKey.description":
|
||||
"İsteğe bağlı. Kimlik doğrulamayı başlıklar ile yönetiyorsanız boş bırakın.",
|
||||
"İsteğe bağlı. Kimlik doğrulamayı başlıklarla yönetiyorsanız boş bırakın.",
|
||||
"provider.custom.models.label": "Modeller",
|
||||
"provider.custom.models.id.label": "Kimlik",
|
||||
"provider.custom.models.id.placeholder": "model-kimlik",
|
||||
@@ -193,9 +195,9 @@ export const dict = {
|
||||
"provider.custom.headers.value.placeholder": "değer",
|
||||
"provider.custom.headers.remove": "Başlığı kaldır",
|
||||
"provider.custom.headers.add": "Başlık ekle",
|
||||
"provider.custom.error.providerID.required": "Sağlayıcı kimlik gerekli",
|
||||
"provider.custom.error.providerID.required": "Sağlayıcı kimliği gerekli",
|
||||
"provider.custom.error.providerID.format": "Küçük harf, rakam, tire veya alt çizgi kullanın",
|
||||
"provider.custom.error.providerID.exists": "Bu sağlayıcı kimlik zaten mevcut",
|
||||
"provider.custom.error.providerID.exists": "Bu sağlayıcı kimliği zaten mevcut",
|
||||
"provider.custom.error.name.required": "Görünen ad gerekli",
|
||||
"provider.custom.error.baseURL.required": "Temel URL gerekli",
|
||||
"provider.custom.error.baseURL.format": "http:// veya https:// ile başlamalı",
|
||||
@@ -203,7 +205,7 @@ export const dict = {
|
||||
"provider.custom.error.duplicate": "Tekrar",
|
||||
|
||||
"provider.disconnect.toast.disconnected.title": "{{provider}} bağlantısı kesildi",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} modelleri artık kullanılabilir değil.",
|
||||
"provider.disconnect.toast.disconnected.description": "{{provider}} modelleri artık kullanılamıyor.",
|
||||
|
||||
"model.tag.free": "Ücretsiz",
|
||||
"model.tag.latest": "En yeni",
|
||||
@@ -223,9 +225,9 @@ export const dict = {
|
||||
"model.tooltip.model": "Model",
|
||||
"model.tooltip.provider": "Sağlayıcı",
|
||||
"model.tooltip.reasoning": "Akıl yürütme",
|
||||
"model.tooltip.reasoning.allowed": "Akıl yürütme destekler",
|
||||
"model.tooltip.reasoning.allowed": "Akıl yürütmeyi destekler",
|
||||
"model.tooltip.reasoning.none": "Akıl yürütme yok",
|
||||
"model.tooltip.context": "Bağlam limiti {{limit}}",
|
||||
"model.tooltip.context": "Bağlam sınırı {{limit}}",
|
||||
|
||||
"common.search.placeholder": "Ara",
|
||||
"common.goBack": "Geri git",
|
||||
@@ -235,8 +237,8 @@ export const dict = {
|
||||
"common.cancel": "İptal",
|
||||
"common.clear": "Temizle",
|
||||
"common.connect": "Bağlan",
|
||||
"common.disconnect": "Bağlantı Kes",
|
||||
"common.continue": "Gönder",
|
||||
"common.disconnect": "Bağlantıyı kes",
|
||||
"common.continue": "Devam et",
|
||||
"common.submit": "Gönder",
|
||||
"common.save": "Kaydet",
|
||||
"common.saving": "Kaydediliyor...",
|
||||
@@ -249,7 +251,7 @@ export const dict = {
|
||||
"prompt.placeholder.summarizeComments": "Yorumları özetle…",
|
||||
"prompt.placeholder.summarizeComment": "Yorumu özetle…",
|
||||
"prompt.mode.shell": "Kabuk",
|
||||
"prompt.mode.normal": "Komut",
|
||||
"prompt.mode.normal": "İstem",
|
||||
"prompt.mode.shell.exit": "çıkmak için esc",
|
||||
"session.child.promptDisabled": "Alt ajan oturumlarına istem gönderilemez.",
|
||||
"session.child.backToParent": "Ana oturuma dön.",
|
||||
@@ -266,30 +268,30 @@ export const dict = {
|
||||
"prompt.example.10": "API dokümantasyonu oluştur",
|
||||
"prompt.example.11": "Veritabanı sorgularını optimize et",
|
||||
"prompt.example.12": "Girdi doğrulama ekle",
|
||||
"prompt.example.13": "İçin yeni bir bileşen oluştur...",
|
||||
"prompt.example.13": "... için yeni bir bileşen oluştur",
|
||||
"prompt.example.14": "Bu projeyi nasıl dağıtabilirim?",
|
||||
"prompt.example.15": "Kodumu en iyi uygulamalar için incele",
|
||||
"prompt.example.16": "Bu fonksiyona hata yönetimi ekle",
|
||||
"prompt.example.17": "Bu regex kalıbını açıkla",
|
||||
"prompt.example.18": "Bunu TypeScript'e dönüştür",
|
||||
"prompt.example.19": "Kod tabanı boyunca loglama ekle",
|
||||
"prompt.example.19": "Kod tabanının tamamına günlük kaydı ekle",
|
||||
"prompt.example.20": "Hangi bağımlılıklar güncellenmemiş?",
|
||||
"prompt.example.21": "Bir göç betiği yazmama yardım et",
|
||||
"prompt.example.21": "Bir geçiş betiği yazmama yardım et",
|
||||
"prompt.example.22": "Bu uç nokta için önbellekleme uygula",
|
||||
"prompt.example.23": "Bu listeye sayfalama ekle",
|
||||
"prompt.example.24": "İçin bir CLI komutu oluştur...",
|
||||
"prompt.example.24": "... için bir CLI komutu oluştur",
|
||||
"prompt.example.25": "Ortam değişkenleri burada nasıl çalışıyor?",
|
||||
|
||||
"prompt.popover.emptyResults": "Eşleşen sonuç yok",
|
||||
"prompt.popover.emptyCommands": "Eşleşen komut yok",
|
||||
"prompt.dropzone.label": "Resimleri, PDF'leri veya metin dosyalarını buraya bırakın",
|
||||
"prompt.dropzone.file.label": "@bahsetmek için dosyayı bırakın",
|
||||
"prompt.dropzone.file.label": "Dosyayı @ ile belirtmek için buraya bırakın",
|
||||
"prompt.slash.badge.custom": "özel",
|
||||
"prompt.slash.badge.skill": "beceri",
|
||||
"prompt.slash.badge.mcp": "mcp",
|
||||
"prompt.context.active": "aktif",
|
||||
"prompt.context.includeActiveFile": "Aktif dosyayı dahil et",
|
||||
"prompt.context.removeActiveFile": "Aktif dosyayı bağlamdan çıkar",
|
||||
"prompt.context.active": "etkin",
|
||||
"prompt.context.includeActiveFile": "Etkin dosyayı dahil et",
|
||||
"prompt.context.removeActiveFile": "Etkin dosyayı bağlamdan çıkar",
|
||||
"prompt.context.removeFile": "Dosyayı bağlamdan çıkar",
|
||||
"prompt.action.attachFile": "Dosya ekle",
|
||||
"prompt.menu.addImagesAndFiles": "Dosya ve daha fazlasını ekle",
|
||||
@@ -302,18 +304,19 @@ export const dict = {
|
||||
"prompt.action.stop": "Durdur",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Desteklenmeyen ek",
|
||||
"prompt.toast.attachmentDuplicate.title": "Bu dosya zaten yüklendi",
|
||||
"prompt.toast.pasteUnsupported.description": "Buraya yalnızca resimler, PDF'ler veya metin dosyaları eklenebilir.",
|
||||
"prompt.toast.modelAgentRequired.title": "Bir ajan ve model seçin",
|
||||
"prompt.toast.modelAgentRequired.description": "Komut göndermeden önce bir ajan ve model seçin.",
|
||||
"prompt.toast.modelAgentRequired.description": "İstem göndermeden önce bir ajan ve model seçin.",
|
||||
"prompt.toast.worktreeCreateFailed.title": "Çalışma ağacı oluşturulamadı",
|
||||
"prompt.toast.sessionCreateFailed.title": "Oturum oluşturulamadı",
|
||||
"prompt.toast.shellSendFailed.title": "Kabuk komutu gönderilemedi",
|
||||
"prompt.toast.commandSendFailed.title": "Komut gönderilemedi",
|
||||
"prompt.toast.promptSendFailed.title": "Komut gönderilemedi",
|
||||
"prompt.toast.promptSendFailed.title": "İstem gönderilemedi",
|
||||
"prompt.toast.promptSendFailed.description": "Oturum alınamadı",
|
||||
|
||||
"dialog.mcp.title": "MCP'ler",
|
||||
"dialog.mcp.description": "{{total}} içerisinden {{enabled}} etkin",
|
||||
"dialog.mcp.description": "Toplam {{total}} öğeden {{enabled}} tanesi etkin",
|
||||
"dialog.mcp.empty": "Yapılandırılmış MCP yok",
|
||||
|
||||
"dialog.lsp.empty": "LSP'ler dosya türlerinden otomatik algılanır",
|
||||
@@ -348,7 +351,7 @@ export const dict = {
|
||||
"dialog.server.add.name": "Sunucu adı (isteğe bağlı)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Kullanıcı adı (isteğe bağlı)",
|
||||
"dialog.server.add.password": "Şifre (isteğe bağlı)",
|
||||
"dialog.server.add.password": "Parola (isteğe bağlı)",
|
||||
"dialog.server.edit.title": "Sunucuyu düzenle",
|
||||
"dialog.server.default.title": "Varsayılan sunucu",
|
||||
"dialog.server.default.description":
|
||||
@@ -454,14 +457,14 @@ export const dict = {
|
||||
"context.breakdown.tool": "Araç Çağrıları",
|
||||
"context.breakdown.other": "Diğer",
|
||||
|
||||
"context.systemPrompt.title": "Sistem Komutu",
|
||||
"context.systemPrompt.title": "Sistem İstemi",
|
||||
"context.rawMessages.title": "Ham mesajlar",
|
||||
|
||||
"context.stats.session": "Oturum",
|
||||
"context.stats.messages": "Mesajlar",
|
||||
"context.stats.provider": "Sağlayıcı",
|
||||
"context.stats.model": "Model",
|
||||
"context.stats.limit": "Bağlam Limiti",
|
||||
"context.stats.limit": "Bağlam Sınırı",
|
||||
"context.stats.totalTokens": "Toplam Token",
|
||||
"context.stats.usage": "Kullanım",
|
||||
"context.stats.inputTokens": "Girdi Tokenleri",
|
||||
@@ -471,7 +474,7 @@ export const dict = {
|
||||
"context.stats.userMessages": "Kullanıcı Mesajları",
|
||||
"context.stats.assistantMessages": "Asistan Mesajları",
|
||||
"context.stats.totalCost": "Toplam Maliyet",
|
||||
"context.stats.sessionCreated": "Oturum Oluşturulma",
|
||||
"context.stats.sessionCreated": "Oturum Oluşturma",
|
||||
"context.stats.lastActivity": "Son Etkinlik",
|
||||
|
||||
"context.usage.tokens": "Tokenler",
|
||||
@@ -510,10 +513,10 @@ export const dict = {
|
||||
"toast.workspace.disabled.title": "Çalışma alanları devre dışı bırakıldı",
|
||||
"toast.workspace.disabled.description": "Kenar çubuğunda yalnızca ana çalışma ağacı gösterilecek",
|
||||
|
||||
"toast.permissions.autoaccept.on.title": "Düzenlemeler otomatik kabul ediliyor",
|
||||
"toast.permissions.autoaccept.on.description": "Düzenleme ve yazma izinleri otomatik olarak onaylanacak",
|
||||
"toast.permissions.autoaccept.off.title": "Otomatik kabul durduruldu",
|
||||
"toast.permissions.autoaccept.off.description": "Düzenleme ve yazma izinleri onay gerektirecek",
|
||||
"toast.permissions.autoaccept.on.title": "İzinler otomatik kabul ediliyor",
|
||||
"toast.permissions.autoaccept.on.description": "İzin istekleri otomatik olarak onaylanacak",
|
||||
"toast.permissions.autoaccept.off.title": "İzinleri otomatik kabul etme durduruldu",
|
||||
"toast.permissions.autoaccept.off.description": "İzin istekleri onay gerektirecek",
|
||||
|
||||
"toast.model.none.title": "Model seçilmedi",
|
||||
"toast.model.none.description": "Bu oturumu özetlemek için bir sağlayıcı bağlayın",
|
||||
@@ -553,8 +556,8 @@ export const dict = {
|
||||
"error.page.action.checking": "Kontrol ediliyor...",
|
||||
"error.page.action.checkUpdates": "Güncellemeleri kontrol et",
|
||||
"error.page.action.updateTo": "{{version}} sürümüne güncelle",
|
||||
"error.page.report.prefix": "Lütfen bu hatayı OpenCode ekibine bildirin",
|
||||
"error.page.report.discord": "Discord üzerinden",
|
||||
"error.page.report.prefix": "Lütfen bu hatayı OpenCode ekibine",
|
||||
"error.page.report.discord": "Discord üzerinden bildirin",
|
||||
"error.page.version": "Sürüm: {{version}}",
|
||||
|
||||
"error.dev.rootNotFound":
|
||||
@@ -571,9 +574,9 @@ export const dict = {
|
||||
"error.chain.responseBody": "Yanıt gövdesi:\n{{body}}",
|
||||
"error.chain.didYouMean": "Bunu mu demek istediniz: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Model bulunamadı: {{provider}}/{{model}}",
|
||||
"error.chain.checkConfig": "Yapılandırma dosyanızı (opencode.json) sağlayıcı/model adlarını kontrol edin",
|
||||
"error.chain.checkConfig": "Yapılandırma dosyanızdaki (opencode.json) sağlayıcı/model adlarını kontrol edin",
|
||||
"error.chain.mcpFailed":
|
||||
'MCP sunucusu "{{name}}" başarısız oldu. Not: OpenCode henüz MCP kimlik doğrulamasını desteklemiyor.',
|
||||
'MCP sunucusu "{{name}}" çalıştırılamadı. Not: OpenCode henüz MCP kimlik doğrulamasını desteklemiyor.',
|
||||
"error.chain.providerAuthFailed": "Sağlayıcı kimlik doğrulaması başarısız ({{provider}}): {{message}}",
|
||||
"error.chain.providerInitFailed":
|
||||
'"{{provider}}" sağlayıcısı başlatılamadı. Kimlik bilgilerini ve yapılandırmayı kontrol edin.',
|
||||
@@ -623,9 +626,9 @@ export const dict = {
|
||||
"session.panel.reviewAndFiles": "İnceleme ve dosyalar",
|
||||
"session.error.notFound": "Bu oturum bulunamıyor",
|
||||
"session.error.notFound.description": "Bu sekme, artık bu sunucuda bulunmayan bir oturuma yönlendiriyor.",
|
||||
"session.error.notFound.closeTab": "Sekmeyi Kapat",
|
||||
"session.error.notFound.closeTab": "Sekmeyi kapat",
|
||||
"session.error.serverConnection": "Bu sunucuya bağlanılamıyor",
|
||||
"session.review.filesChanged": "{{count}} Dosya Değişti",
|
||||
"session.review.filesChanged": "{{count}} dosya değişti",
|
||||
"session.review.change.one": "Değişiklik",
|
||||
"session.review.change.other": "Değişiklik",
|
||||
"session.review.loadingChanges": "Değişiklikler yükleniyor...",
|
||||
@@ -634,7 +637,7 @@ export const dict = {
|
||||
"session.review.noSnapshot":
|
||||
"Yapılandırmada anlık görüntü takibi devre dışı bırakıldı, bu nedenle oturum değişiklikleri kullanılamıyor",
|
||||
"session.review.noChanges": "Değişiklik yok",
|
||||
"session.review.noUncommittedChanges": "Henüz commit edilmemiş değişiklik yok",
|
||||
"session.review.noUncommittedChanges": "Henüz işlenmemiş değişiklik yok",
|
||||
"session.review.noBranchChanges": "Henüz dal değişikliği yok",
|
||||
|
||||
"session.files.selectToOpen": "Açmak için bir dosya seçin",
|
||||
@@ -668,7 +671,7 @@ export const dict = {
|
||||
"session.revertDock.expand": "Geri alınan mesajları genişlet",
|
||||
"session.revertDock.restore": "Mesajı geri yükle",
|
||||
|
||||
"session.new.title": "İstediğini yap",
|
||||
"session.new.title": "Her şeyi geliştirin",
|
||||
"session.new.project.new": "Yeni proje",
|
||||
"session.new.project.search": "Proje ara",
|
||||
"session.new.project.add": "Proje ekle",
|
||||
@@ -689,6 +692,9 @@ export const dict = {
|
||||
"session.header.open.ariaLabel": "{{app}} ile aç",
|
||||
"session.header.open.menu": "Açma seçenekleri",
|
||||
"session.header.open.copyPath": "Yolu kopyala",
|
||||
"session.header.reveal.finder": "Finder'da göster",
|
||||
"session.header.reveal.fileExplorer": "Dosya Gezgini'nde göster",
|
||||
"session.header.reveal.containingFolder": "İçeren klasörü aç",
|
||||
|
||||
"status.popover.trigger": "Durum",
|
||||
"status.popover.ariaLabel": "Sunucu yapılandırmaları",
|
||||
@@ -709,17 +715,17 @@ export const dict = {
|
||||
"session.share.action.unpublishing": "Yayından kaldırılıyor...",
|
||||
"session.share.action.view": "Görüntüle",
|
||||
"session.share.copy.copied": "Kopyalandı",
|
||||
"session.share.copy.copyLink": "Bağlantı kopyala",
|
||||
"session.share.copy.copyLink": "Bağlantıyı kopyala",
|
||||
|
||||
"lsp.tooltip.none": "LSP sunucusu yok",
|
||||
"lsp.label.connected": "{{count}} LSP",
|
||||
|
||||
"prompt.loading": "Komut yükleniyor...",
|
||||
"prompt.loading": "İstem yükleniyor...",
|
||||
"terminal.loading": "Terminal yükleniyor...",
|
||||
"terminal.title": "Terminal",
|
||||
"terminal.title.numbered": "Terminal {{number}}",
|
||||
"terminal.close": "Terminali kapat",
|
||||
"terminal.connectionLost.title": "Bağlantı Kesildi",
|
||||
"terminal.connectionLost.title": "Bağlantı kesildi",
|
||||
"terminal.connectionLost.description":
|
||||
"Terminal bağlantısı kesildi. Bu durum sunucu yeniden başladığında oluşabilir.",
|
||||
|
||||
@@ -784,15 +790,15 @@ export const dict = {
|
||||
"OpenCode'un sistem, açık veya koyu temayı takip etip etmeyeceğini seçin",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.",
|
||||
"settings.general.row.font.title": "Kod Yazı Tipi",
|
||||
"settings.general.row.font.title": "Kod yazı tipi",
|
||||
"settings.general.row.font.description": "Kod bloklarında kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.terminalFont.title": "Terminal yazı tipi",
|
||||
"settings.general.row.terminalFont.description": "Terminalde kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.uiFont.title": "Arayüz Yazı Tipi",
|
||||
"settings.general.row.uiFont.title": "Arayüz yazı tipi",
|
||||
"settings.general.row.uiFont.description": "Arayüz genelinde kullanılan yazı tipini özelleştirin",
|
||||
"settings.general.row.followup.title": "Takip davranışı",
|
||||
"settings.general.row.followup.description":
|
||||
"Takip komutlarının hemen yönlendirilmesini mi yoksa sırada beklemesini mi istediğinizi seçin",
|
||||
"Takip istemlerinin hemen yönlendirilmesini mi yoksa sırada beklemesini mi istediğinizi seçin",
|
||||
"settings.general.row.followup.option.queue": "Sıra",
|
||||
"settings.general.row.followup.option.steer": "Yönlendir",
|
||||
"settings.general.row.showFileTree.title": "Dosya ağacı",
|
||||
@@ -808,8 +814,9 @@ export const dict = {
|
||||
"settings.general.row.mobileTitlebarBottom.title": "Alt gezinme",
|
||||
"settings.general.row.mobileTitlebarBottom.description":
|
||||
"Mobil cihazlarda başlık çubuğunu ve oturum sekmelerini ekranın altına yerleştir",
|
||||
"settings.general.row.showCustomAgents.title": "Özel ajanlar",
|
||||
"settings.general.row.showCustomAgents.description": "Yazma alanında ajan seçiciyi göster",
|
||||
"settings.general.row.showCustomAgents.title": "Ajanı göster",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Düzenleyicide ajanlar arasında geçiş yapın. Gizlendiğinde varsayılan olarak Build ajanı kullanılır.",
|
||||
"settings.general.row.reasoningSummaries.title": "Akıl yürütme özetlerini göster",
|
||||
"settings.general.row.reasoningSummaries.description": "Zaman çizelgesinde model akıl yürütme özetlerini görüntüle",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Kabuk araç bileşenlerini genişlet",
|
||||
@@ -833,7 +840,7 @@ export const dict = {
|
||||
"settings.general.row.wayland.description":
|
||||
"Wayland'da X11 geri dönüşünü devre dışı bırak. Yeniden başlatma gerektirir.",
|
||||
"settings.general.row.wayland.tooltip":
|
||||
"Karışık yenileme hızlı monitörlere sahip Linux'ta yerel Wayland daha kararlı olabilir.",
|
||||
"Farklı yenileme hızlarına sahip monitörlerin kullanıldığı Linux sistemlerinde yerel Wayland daha kararlı olabilir.",
|
||||
|
||||
"settings.general.row.releaseNotes.title": "Sürüm notları",
|
||||
"settings.general.row.releaseNotes.description": "Güncellemelerden sonra Yenilikler bildirimlerini göster",
|
||||
@@ -927,7 +934,7 @@ export const dict = {
|
||||
"settings.shortcuts.group.navigation": "Gezinme",
|
||||
"settings.shortcuts.group.modelAndAgent": "Model ve ajan",
|
||||
"settings.shortcuts.group.terminal": "Terminal",
|
||||
"settings.shortcuts.group.prompt": "Komut",
|
||||
"settings.shortcuts.group.prompt": "İstem",
|
||||
|
||||
"settings.providers.title": "Sağlayıcılar",
|
||||
"settings.providers.description": "Sağlayıcı ayarları burada yapılandırılabilecek.",
|
||||
@@ -952,7 +959,7 @@ export const dict = {
|
||||
"settings.permissions.section.tools": "Araçlar",
|
||||
"settings.permissions.toast.updateFailed.title": "İzinler güncellenemedi",
|
||||
|
||||
"settings.permissions.action.allow": "İzin Ver",
|
||||
"settings.permissions.action.allow": "İzin ver",
|
||||
"settings.permissions.action.ask": "Sor",
|
||||
"settings.permissions.action.deny": "Reddet",
|
||||
|
||||
@@ -992,7 +999,7 @@ export const dict = {
|
||||
|
||||
"workspace.new": "Yeni çalışma alanı",
|
||||
"workspace.type.local": "yerel",
|
||||
"workspace.type.sandbox": "sandbox",
|
||||
"workspace.type.sandbox": "korumalı alan",
|
||||
"workspace.create.failed.title": "Çalışma alanı oluşturulamadı",
|
||||
"workspace.delete.failed.title": "Çalışma alanı silinemedi",
|
||||
"workspace.resetting.title": "Çalışma alanı sıfırlanıyor",
|
||||
@@ -1024,9 +1031,9 @@ export const dict = {
|
||||
"error.server.invalidConfiguration": "Geçersiz yapılandırma",
|
||||
"common.moreCountSuffix": " (+{{count}} daha)",
|
||||
"common.time.justNow": "Şimdi",
|
||||
"common.time.minutesAgo.short": "{{count}}dk önce",
|
||||
"common.time.hoursAgo.short": "{{count}}sa önce",
|
||||
"common.time.daysAgo.short": "{{count}}g önce",
|
||||
"common.time.minutesAgo.short": "{{count}} dk önce",
|
||||
"common.time.hoursAgo.short": "{{count}} sa önce",
|
||||
"common.time.daysAgo.short": "{{count}} gün önce",
|
||||
"settings.providers.connected.environmentDescription": "Ortam değişkenlerinizden bağlandı",
|
||||
"settings.providers.custom.description": "Temel URL üzerinden OpenAI uyumlu bir sağlayıcı ekleyin.",
|
||||
|
||||
@@ -1064,7 +1071,7 @@ export const dict = {
|
||||
"debugBar.nav.tip":
|
||||
"Yönlendirici başlangıcından yerleşme sonrası ilk boyamaya kadar ölçülen, bir oturum sayfasına dokunan son tamamlanmış rota geçişi.",
|
||||
"debugBar.fps.label": "FPS",
|
||||
"debugBar.fps.tip": "Son 5 saniyedeki kayan saniye başına kare sayısı.",
|
||||
"debugBar.fps.tip": "Son 5 saniyedeki hareketli saniye başına kare ortalaması.",
|
||||
"debugBar.frame.label": "FRAME",
|
||||
"debugBar.frame.tip": "Son 5 saniyedeki en kötü kare süresi.",
|
||||
"debugBar.jank.label": "JANK",
|
||||
@@ -1078,14 +1085,14 @@ export const dict = {
|
||||
"debugBar.cls.label": "CLS",
|
||||
"debugBar.cls.tip": "Mevcut uygulama ömrü için kümülatif düzen kayması.",
|
||||
"debugBar.mem.label": "MEM",
|
||||
"debugBar.mem.tipUnavailable": "Kullanılan JS yığını vs yığın sınırı. Yalnızca Chromium.",
|
||||
"debugBar.mem.tip": "Kullanılan JS yığını vs yığın sınırı. {{limit}} içinde {{used}}.",
|
||||
"debugBar.mem.tipUnavailable": "Kullanılan JS yığınının yığın sınırına oranı. Yalnızca Chromium.",
|
||||
"debugBar.mem.tip": "Kullanılan JS yığınının yığın sınırına oranı: {{used}} / {{limit}}.",
|
||||
"common.key.ctrl": "Ctrl",
|
||||
"common.key.alt": "Alt",
|
||||
"common.key.shift": "Shift",
|
||||
"common.key.meta": "Meta",
|
||||
"common.key.space": "Boşluk",
|
||||
"common.key.backspace": "Geri",
|
||||
"common.key.backspace": "Geri Al",
|
||||
"common.key.enter": "Enter",
|
||||
"common.key.tab": "Tab",
|
||||
"common.key.delete": "Delete",
|
||||
|
||||
+43
-34
@@ -102,26 +102,28 @@ 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",
|
||||
"dialog.provider.copilot.note": "Моделі AI для допомоги в кодуванні через GitHub Copilot",
|
||||
"dialog.provider.openai.note": "Моделі GPT для швидких і універсальних завдань AI",
|
||||
"dialog.provider.copilot.note": "Моделі ШІ для допомоги в кодуванні через GitHub Copilot",
|
||||
"dialog.provider.openai.note": "Моделі GPT для швидких і універсальних завдань ШІ",
|
||||
"dialog.provider.google.note": "Моделі Gemini для швидких структурованих відповідей",
|
||||
"dialog.provider.openrouter.note": "Доступ до всіх підтримуваних моделей від одного провайдера",
|
||||
"dialog.provider.vercel.note": "Уніфікований доступ до моделей AI з інтелектуальною маршрутизацією",
|
||||
"dialog.provider.vercel.note": "Уніфікований доступ до моделей ШІ з інтелектуальною маршрутизацією",
|
||||
|
||||
"dialog.model.select.title": "Вибрати модель",
|
||||
"dialog.model.search.placeholder": "Пошук моделей",
|
||||
"dialog.model.empty": "Немає результатів моделей",
|
||||
"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": "Показати більше провайдерів",
|
||||
|
||||
@@ -168,7 +170,7 @@ export const dict = {
|
||||
"provider.custom.field.providerID.placeholder": "myprovider",
|
||||
"provider.custom.field.providerID.description": "Малі літери, цифри, дефіси або підкреслення",
|
||||
"provider.custom.field.name.label": "Відображувана назва",
|
||||
"provider.custom.field.name.placeholder": "Мій AI Провайдер",
|
||||
"provider.custom.field.name.placeholder": "Мій провайдер ШІ",
|
||||
"provider.custom.field.baseURL.label": "Базовий URL",
|
||||
"provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1",
|
||||
"provider.custom.field.apiKey.label": "Ключ API",
|
||||
@@ -189,7 +191,7 @@ export const dict = {
|
||||
"provider.custom.headers.value.placeholder": "значення",
|
||||
"provider.custom.headers.remove": "Видалити заголовок",
|
||||
"provider.custom.headers.add": "Додати заголовок",
|
||||
"provider.custom.error.providerID.required": "ID провайдера обов'язкове",
|
||||
"provider.custom.error.providerID.required": "ID провайдера обов'язковий",
|
||||
"provider.custom.error.providerID.format": "Використовуйте малі літери, цифри, дефіси або підкреслення",
|
||||
"provider.custom.error.providerID.exists": "ID провайдера вже існує",
|
||||
"provider.custom.error.name.required": "Відображувана назва обов'язкова",
|
||||
@@ -299,6 +301,7 @@ export const dict = {
|
||||
"prompt.action.stop": "Зупинити",
|
||||
|
||||
"prompt.toast.pasteUnsupported.title": "Непідтримуване вкладення",
|
||||
"prompt.toast.attachmentDuplicate.title": "Цей файл уже завантажено",
|
||||
"prompt.toast.pasteUnsupported.description": "Сюди можна прикріплювати лише зображення, PDF або текстові файли.",
|
||||
"prompt.toast.modelAgentRequired.title": "Виберіть агента та модель",
|
||||
"prompt.toast.modelAgentRequired.description": "Виберіть агента та модель перед надсиланням запиту.",
|
||||
@@ -313,7 +316,7 @@ export const dict = {
|
||||
"dialog.mcp.description": "{{enabled}} з {{total}} увімкнено",
|
||||
"dialog.mcp.empty": "MCP не налаштовано",
|
||||
|
||||
"dialog.lsp.empty": "LSP автоматично виявлені за типами файлів",
|
||||
"dialog.lsp.empty": "Сервери LSP автоматично виявляються за типами файлів",
|
||||
"dialog.plugins.empty": "Плагіни налаштовані в opencode.json",
|
||||
|
||||
"mcp.status.connected": "підключено",
|
||||
@@ -332,8 +335,8 @@ export const dict = {
|
||||
"dialog.directory.parent": "Батьківська папка",
|
||||
"dialog.directory.readError": "Не вдалося прочитати цю папку",
|
||||
|
||||
"app.server.unreachable": "Не вдалося досягти {{server}}",
|
||||
"app.server.retrying": "Автоматичне повторення...",
|
||||
"app.server.unreachable": "Не вдалося зв'язатися із {{server}}",
|
||||
"app.server.retrying": "Автоматична повторна спроба...",
|
||||
"app.server.otherServers": "Інші сервери",
|
||||
|
||||
"dialog.server.title": "Сервери",
|
||||
@@ -363,7 +366,7 @@ export const dict = {
|
||||
|
||||
"dialog.server.menu.edit": "Редагувати",
|
||||
"dialog.server.menu.default": "Встановити за замовчуванням",
|
||||
"dialog.server.menu.defaultRemove": "Видалити за замовчуванням",
|
||||
"dialog.server.menu.defaultRemove": "Скасувати використання за замовчуванням",
|
||||
"dialog.server.menu.delete": "Видалити",
|
||||
"dialog.server.current": "Поточний сервер",
|
||||
"dialog.server.status.default": "За замовчуванням",
|
||||
@@ -465,7 +468,7 @@ export const dict = {
|
||||
"context.breakdown.other": "Інше",
|
||||
|
||||
"context.systemPrompt.title": "Системний запит",
|
||||
"context.rawMessages.title": "Сировинні повідомлення",
|
||||
"context.rawMessages.title": "Необроблені повідомлення",
|
||||
|
||||
"context.stats.session": "Сесія",
|
||||
"context.stats.messages": "Повідомлення",
|
||||
@@ -559,14 +562,14 @@ export const dict = {
|
||||
"error.page.details.label": "Деталі помилки",
|
||||
"error.page.action.restart": "Перезапустити",
|
||||
"error.page.action.report": "Повідомити про помилку",
|
||||
"error.page.action.reported": "Помилку повідомлено",
|
||||
"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.report.discord": "у Discord",
|
||||
"error.page.version": "Версія: {{version}}",
|
||||
|
||||
"error.dev.rootNotFound":
|
||||
@@ -586,7 +589,7 @@ export const dict = {
|
||||
"error.chain.causedBy": "Причина:",
|
||||
"error.chain.apiError": "Помилка API",
|
||||
"error.chain.status": "Статус: {{status}}",
|
||||
"error.chain.retryable": "Повторювано: {{retryable}}",
|
||||
"error.chain.retryable": "Можна повторити: {{retryable}}",
|
||||
"error.chain.responseBody": "Тіло відповіді:\n{{body}}",
|
||||
"error.chain.didYouMean": "Можливо, ви мали на увазі: {{suggestions}}",
|
||||
"error.chain.modelNotFound": "Модель не знайдено: {{provider}}/{{model}}",
|
||||
@@ -662,7 +665,7 @@ export const dict = {
|
||||
"session.files.selectToOpen": "Виберіть файл для відкриття",
|
||||
"session.files.all": "Усі файли",
|
||||
"session.files.empty": "Немає файлів",
|
||||
"session.files.binaryContent": "Бінарний файл (вміст не може бути відображено)",
|
||||
"session.files.binaryContent": "Двійковий файл (вміст неможливо відобразити)",
|
||||
|
||||
"session.messages.renderEarlier": "Відобразити раніші повідомлення",
|
||||
"session.messages.loadingEarlier": "Завантаження раніших повідомлень...",
|
||||
@@ -714,7 +717,10 @@ export const dict = {
|
||||
"session.header.open.menu": "Параметри відкриття",
|
||||
"session.header.open.copyPath": "Копіювати шлях",
|
||||
"session.header.open.finder": "Finder",
|
||||
"session.header.open.fileExplorer": "Провідник файлів",
|
||||
"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",
|
||||
@@ -769,7 +775,7 @@ export const dict = {
|
||||
"common.dismiss": "Відхилити",
|
||||
"common.moreCountSuffix": " (ще {{count}})",
|
||||
"common.requestFailed": "Запит не виконано",
|
||||
"common.moreOptions": "Більше опцій",
|
||||
"common.moreOptions": "Більше параметрів",
|
||||
"common.learnMore": "Дізнатися більше",
|
||||
"common.rename": "Перейменувати",
|
||||
"common.reset": "Скинути",
|
||||
@@ -842,10 +848,10 @@ export const dict = {
|
||||
|
||||
"app.name.desktop": "OpenCode Desktop",
|
||||
|
||||
"settings.section.desktop": "Робочий стіл",
|
||||
"settings.section.desktop": "Десктопний застосунок",
|
||||
"settings.section.server": "Сервер",
|
||||
"settings.tab.general": "Загальні",
|
||||
"settings.tab.shortcuts": "Скорочення",
|
||||
"settings.tab.shortcuts": "Клавіатурні скорочення",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "Інтеграція WSL",
|
||||
"settings.desktop.wsl.description": "Запускати сервер OpenCode всередині WSL на Windows.",
|
||||
@@ -863,7 +869,7 @@ export const dict = {
|
||||
"settings.general.row.shell.title": "Командна оболонка термінала",
|
||||
"settings.general.row.shell.description":
|
||||
"Виберіть оболонку для термінала. Сумісні оболонки також використовуються для викликів інструментів агента.",
|
||||
"settings.general.row.shell.autoDefault": "Авто (за замовчуванням)",
|
||||
"settings.general.row.shell.autoDefault": "Автоматично (за замовчуванням)",
|
||||
"settings.general.row.shell.terminalOnly": "тільки термінал",
|
||||
"settings.general.row.appearance.title": "Зовнішній вигляд",
|
||||
"settings.general.row.appearance.description": "Налаштуйте вигляд OpenCode на вашому пристрої",
|
||||
@@ -884,18 +890,21 @@ export const dict = {
|
||||
"settings.general.row.showFileTree.title": "Дерево файлів",
|
||||
"settings.general.row.showFileTree.description": "Показувати панель дерева файлів у сесіях",
|
||||
"settings.general.row.showNavigation.title": "Елементи навігації",
|
||||
"settings.general.row.showNavigation.description": "Показувати кнопки назад і вперед у заголовку робочого столу",
|
||||
"settings.general.row.showNavigation.description":
|
||||
"Показувати кнопки назад і вперед на панелі заголовка десктопного застосунку",
|
||||
"settings.general.row.showSearch.title": "Палітра команд",
|
||||
"settings.general.row.showSearch.description": "Показувати кнопку пошуку та палітри команд у заголовку",
|
||||
"settings.general.row.showSearch.description": "Показувати кнопку пошуку та палітри команд на панелі заголовка",
|
||||
"settings.general.row.showTerminal.title": "Термінал",
|
||||
"settings.general.row.showTerminal.description": "Показувати кнопку термінала в заголовку робочого столу",
|
||||
"settings.general.row.showTerminal.description":
|
||||
"Показувати кнопку термінала на панелі заголовка десктопного застосунку",
|
||||
"settings.general.row.showStatus.title": "Статус сервера",
|
||||
"settings.general.row.showStatus.description": "Показувати кнопку статусу сервера в заголовку",
|
||||
"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.showCustomAgents.title": "Показувати агента",
|
||||
"settings.general.row.showCustomAgents.description":
|
||||
"Перемикатися між агентами в редакторі запиту. Якщо приховано, типовим є агент Build.",
|
||||
"settings.general.row.reasoningSummaries.title": "Показувати підсумки мислення",
|
||||
"settings.general.row.reasoningSummaries.description": "Відображати підсумки мислення моделі на часовій шкалі",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Розгортати частини інструменту оболонки",
|
||||
@@ -995,16 +1004,16 @@ export const dict = {
|
||||
"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.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.search.placeholder": "Пошук клавіатурних скорочень",
|
||||
"settings.shortcuts.search.empty": "Клавіатурних скорочень не знайдено",
|
||||
|
||||
"settings.shortcuts.group.general": "Загальні",
|
||||
"settings.shortcuts.group.session": "Сесія",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user