From ad214ccec61d4f355e858b389f8bccf7def735e8 Mon Sep 17 00:00:00 2001 From: Serhii Vecherenko Date: Fri, 21 Aug 2026 15:04:26 -0700 Subject: [PATCH] feat(agent-profiles): add multi-profile agent settings support - Add shared profile contracts, IPC, persistence, and environment handling - Refactor settings UI for reusable agent and Cursor profile management - Support profile-aware Cursor runtime detection and session startup - Preserve provider model identity and localized UI coverage - Add regression tests and adapter documentation --- .agents/docs/agent-adapters.md | 42 ++ src/main/ipc/localHandlers.ts | 53 +- src/main/sharedSettingsFile.test.ts | 163 ++++- src/main/sharedSettingsFile.ts | 96 ++- .../ProviderModelMenu.test.tsx | 20 + .../ProviderModelMenu/ProviderModelMenu.tsx | 7 +- .../ProviderModelMenu/parts/buildItems.ts | 4 + .../parts/modelShortcutLabel.test.ts | 34 + .../parts/modelShortcutLabel.ts | 5 +- .../parts/providerIdentity.ts | 7 +- .../common/ProviderModelMenu/parts/types.ts | 1 + .../providers/ProviderIcon.test.tsx | 15 + .../components/providers/ProviderIcon.tsx | 29 +- .../thread/buildModelPickerControls.test.ts | 37 + .../thread/buildModelPickerControls.tsx | 6 +- .../thread/threadDraftViewHelpers.test.ts | 25 + .../thread/threadDraftViewHelpers.ts | 3 +- src/renderer/locales/de/messages.po | 203 +++++- src/renderer/locales/en/messages.po | 203 +++++- src/renderer/locales/es/messages.po | 203 +++++- src/renderer/locales/fr/messages.po | 203 +++++- src/renderer/locales/ja/messages.po | 203 +++++- src/renderer/locales/ko/messages.po | 203 +++++- src/renderer/locales/pl/messages.po | 203 +++++- src/renderer/locales/pt-BR/messages.po | 203 +++++- src/renderer/locales/ru/messages.po | 203 +++++- src/renderer/locales/tr/messages.po | 203 +++++- src/renderer/locales/uk/messages.po | 203 +++++- src/renderer/locales/vi/messages.po | 203 +++++- src/renderer/locales/zh-CN/messages.po | 203 +++++- .../state/sharedSettingsStore.test.ts | 17 + src/renderer/state/sharedSettingsStore.ts | 5 +- .../parts/AgentProfileList.tsx | 361 ++++++++++ .../parts/ClaudeProfileSettings.test.tsx | 131 ++-- .../parts/ClaudeProfileSettings.tsx | 280 ++------ .../parts/ClaudeProfileSettingsModel.ts | 24 +- .../parts/CursorProfileIdentity.tsx | 125 ++++ .../parts/CursorProfileSettings.tsx | 61 ++ .../parts/CursorProviderSettings.test.tsx | 641 +++++++++++++++++- .../parts/CursorProviderSettings.tsx | 66 +- .../parts/CursorSdkRuntimeSetup.tsx | 95 ++- .../SettingsOverlay/parts/SettingsSidebar.tsx | 13 +- .../SingleAgentSettings.test.tsx | 2 + .../SingleAgentSettings.tsx | 39 +- .../parts/agentRegistryNative.ts | 72 +- .../views/SettingsOverlay/parts/profileIds.ts | 39 ++ src/shared/contracts.ts | 1 + src/shared/contracts/agentInstance.test.ts | 14 + src/shared/contracts/agentInstance.ts | 53 +- src/shared/contracts/agentProfiles.test.ts | 82 +++ src/shared/contracts/agentProfiles.ts | 111 +++ src/shared/ipc/procedureMap.ts | 3 +- src/shared/ipc/procedures/settings.ts | 21 +- .../agents/acp/sessionFactory.test.ts | 38 ++ src/supervisor/agents/acp/sessionFactory.ts | 6 +- src/supervisor/agents/cursor/cursor.test.ts | 39 ++ .../agents/cursor/detection.probeEnv.test.ts | 141 ++++ src/supervisor/agents/cursor/detection.ts | 28 +- src/supervisor/agents/cursor/index.ts | 100 ++- .../agents/cursor/sdkAdapterDetection.test.ts | 44 +- .../agents/cursor/sdkDetection.test.ts | 18 + src/supervisor/agents/cursor/sdkDetection.ts | 4 +- src/supervisor/agents/registry.test.ts | 20 +- src/supervisor/agents/registry.ts | 21 +- 64 files changed, 5170 insertions(+), 731 deletions(-) create mode 100644 src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx create mode 100644 src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx create mode 100644 src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx create mode 100644 src/renderer/views/SettingsOverlay/parts/profileIds.ts create mode 100644 src/shared/contracts/agentProfiles.test.ts create mode 100644 src/shared/contracts/agentProfiles.ts create mode 100644 src/supervisor/agents/cursor/detection.probeEnv.test.ts diff --git a/.agents/docs/agent-adapters.md b/.agents/docs/agent-adapters.md index 2ff0197c5..c9353e249 100644 --- a/.agents/docs/agent-adapters.md +++ b/.agents/docs/agent-adapters.md @@ -179,6 +179,48 @@ most often forgotten. > (single managed model) or `commandcode/` (multi-model + npm install/update + a > synthesized terminal Login method). +## Giving a Provider Multiple Profiles + +A **profile** is a second account or configuration of a provider that already +exists as a built-in agent (a second Claude account, a Cursor key for another +org). Each profile is an `AgentInstanceConfig` whose `driver` is the provider +id; it surfaces as the instance-scoped agent kind `:` and +gets its own adapter, sidebar entry, model-picker group, and settings page. + +Everything generic already handles profiles — the sealing settings writer, the +`createProfile` / `setProfileEnvironment` IPC, the sidebar nesting, the instance +badge on the provider icon, the profile list UI, and `removeAgentInstance`'s +cleanup of profile-scoped settings. Adding profiles to a provider is four +declarations, none of which is a new branch in shared code: + +- [ ] **Register the driver** — add `{ driver: "" }` (plus + `credentialEnvVar` when the provider authenticates with a single secret) + to `AGENT_PROFILE_DRIVERS` in `src/shared/contracts/agentProfiles.ts`. + This is what makes shared code treat `:` kinds as profiles. +- [ ] **Supervisor adapter factory** — export `createProfileAdapter(instance)` + from the provider's module and add it to `profileAdapterFactories` in + `src/supervisor/agents/registry.ts`. Build it from your normal adapter + factory with an overridden `kind`/`label` and the profile's credential in + `baseSpawnEnv`, so detection probes and every launch lane use that + credential (see `cursor/index.ts`). Throw when the profile is unusable: + the registry skips it with a warning instead of failing. +- [ ] **Profile descriptor** — set `profiles: ProfileSupport` on the + provider's `NATIVE_AGENT_REGISTRY_ENTRIES` entry. The descriptor supplies + only what differs: the one extra add-form field, the row subtitle + component, the removal-consequence copy, and `createPayload`. Optional + `onCreated` pins provider settings that must exist before the first + detection pass (Cursor pins its GUI runtime there). +- [ ] **Profile page** — the provider's `settingsPanel` already receives + instance-scoped kinds; branch on your own + `extractProfileInstanceId(agentKind)` to render the per-profile + editor instead of the base page. + +Do NOT add per-provider profile branches to `mergeManagedSharedSettings`, +`ProviderIcon`, `SettingsSidebar`, `SingleAgentSettings`, or the IPC surface — +they are all driven by the registry above. Reference implementations: +`cursor` (single sealed credential) and `claude` (free-form environment plus an +opaque per-profile `config`). + ## Plugin Architecture The codebase is provider-agnostic by design (targeting 5-10 providers). Each provider is a fully self-contained plugin: diff --git a/src/main/ipc/localHandlers.ts b/src/main/ipc/localHandlers.ts index cb2f346dd..58b5146a3 100644 --- a/src/main/ipc/localHandlers.ts +++ b/src/main/ipc/localHandlers.ts @@ -50,7 +50,8 @@ import { } from "../profile"; import { applyAgentSecretSetting, - applyClaudeProfileEnvironment, + applyCreateProfile, + applyProfileEnvironment, mergeManagedSharedSettings, readSharedSettingsFile, writeSharedSettingsFile, @@ -202,6 +203,17 @@ export function createLocalIpcHandlers( ...(viewedThreadIds.length > 0 ? { viewedThreadIds: [...new Set(viewedThreadIds)] } : {}), }); }; + // Shared plumbing for the main-local secret/profile handlers: read the + // settings file, apply one encrypting transform, persist, and notify. + const applyToSharedSettingsFile = ( + apply: (settings: SharedSettings, baseDir: string) => { settings: SharedSettings; result: T }, + ): T => { + const settingsPath = options.requirePoracodePaths().settingsPath; + const applied = apply(readSharedSettingsFile(settingsPath), dirname(settingsPath)); + writeSharedSettingsFile(settingsPath, applied.settings); + options.onSharedSettingsChanged?.(applied.settings); + return applied.result; + }; return defineMainLocalIpcHandlers({ pickFolder: async (defaultPath) => { const result = await dialog.showOpenDialog(options.getMainWindow()!, { @@ -383,7 +395,7 @@ export function createLocalIpcHandlers( getSharedSettings: () => readSharedSettingsFile(options.requirePoracodePaths().settingsPath), setSharedSettings: (settings) => { const settingsPath = options.requirePoracodePaths().settingsPath; - // Preserve supervisor-managed fields and encrypted Claude-profile + // Preserve supervisor-managed fields and encrypted provider-profile // environments so the renderer's persist cycle doesn't clobber writes // made out-of-band by the supervisor. (Shared with the app-controls MCP // `update_settings` tool via `mergeManagedSharedSettings`.) @@ -392,17 +404,11 @@ export function createLocalIpcHandlers( options.updatePowerSaveBlocker(); options.onSharedSettingsChanged?.(merged); }, - setAgentSecretSetting: (payload) => { - const settingsPath = options.requirePoracodePaths().settingsPath; - const { settings, storedValue } = applyAgentSecretSetting( - readSharedSettingsFile(settingsPath), - payload, - dirname(settingsPath), - ); - writeSharedSettingsFile(settingsPath, settings); - options.onSharedSettingsChanged?.(settings); - return { storedValue }; - }, + setAgentSecretSetting: (payload) => + applyToSharedSettingsFile((settings, baseDir) => { + const { settings: next, storedValue } = applyAgentSecretSetting(settings, payload, baseDir); + return { settings: next, result: { storedValue } }; + }), removeCrossagentRoutingOverride: ({ tags }) => { const settingsPath = options.requirePoracodePaths().settingsPath; const current = readSharedSettingsFile(settingsPath); @@ -434,17 +440,16 @@ export function createLocalIpcHandlers( options.onSharedSettingsChanged?.(settings); return usage; }, - setClaudeProfileEnvironment: (payload) => { - const settingsPath = options.requirePoracodePaths().settingsPath; - const { settings, instance } = applyClaudeProfileEnvironment( - readSharedSettingsFile(settingsPath), - payload, - dirname(settingsPath), - ); - writeSharedSettingsFile(settingsPath, settings); - options.onSharedSettingsChanged?.(settings); - return instance; - }, + setProfileEnvironment: (payload) => + applyToSharedSettingsFile((settings, baseDir) => { + const { settings: next, instance } = applyProfileEnvironment(settings, payload, baseDir); + return { settings: next, result: instance }; + }), + createProfile: (payload) => + applyToSharedSettingsFile((settings, baseDir) => { + const { settings: next, instance } = applyCreateProfile(settings, payload, baseDir); + return { settings: next, result: instance }; + }), setWindowChrome: async (payload: WindowChromePayload): Promise => { const nativeCapable = supportsNativeWindowMaterial(); const mainWindow = options.getMainWindow(); diff --git a/src/main/sharedSettingsFile.test.ts b/src/main/sharedSettingsFile.test.ts index 7e53f9fb6..e6920a9dc 100644 --- a/src/main/sharedSettingsFile.test.ts +++ b/src/main/sharedSettingsFile.test.ts @@ -13,7 +13,8 @@ import { } from "@/shared/settings"; import { applyAgentSecretSetting, - applyClaudeProfileEnvironment, + applyCreateProfile, + applyProfileEnvironment, mergeManagedSharedSettings, patchSharedSettingsFile, readSharedSettingsFile, @@ -624,7 +625,149 @@ describe("applyAgentSecretSetting", () => { }); }); -describe("applyClaudeProfileEnvironment", () => { +describe("applyProfileEnvironment (single-credential provider)", () => { + function cursorProfileSettings(): SharedSettings { + return { + ...defaultSharedSettings, + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + }, + }; + } + + it("seals a Cursor profile key and pins it during renderer writes", () => { + const dir = makeTempDir(); + const saved = applyProfileEnvironment( + cursorProfileSettings(), + { + instanceId: "work", + environment: { CURSOR_API_KEY: { value: "profile-secret", sensitive: false } }, + }, + dir, + ); + const stored = saved.instance.environment?.CURSOR_API_KEY?.value ?? ""; + + expect(isEncryptedSecret(stored)).toBe(true); + expect(decryptSecret(dir, stored)).toBe("profile-secret"); + expect(JSON.stringify(saved.settings)).not.toContain("profile-secret"); + + const merged = mergeManagedSharedSettings(saved.settings, { + ...saved.settings, + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Renamed" }, + }, + }); + expect(merged.agentInstances.work?.displayName).toBe("Renamed"); + expect(merged.agentInstances.work?.environment?.CURSOR_API_KEY?.value).toBe(stored); + }); + + it("rejects environment variables outside a single-credential profile's declaration", () => { + expect(() => + applyProfileEnvironment( + cursorProfileSettings(), + { instanceId: "work", environment: { NODE_OPTIONS: { value: "--inspect" } } }, + makeTempDir(), + ), + ).toThrow("only support CURSOR_API_KEY"); + }); + + it("rejects instances whose driver does not support profiles", () => { + expect(() => + applyProfileEnvironment( + { + ...defaultSharedSettings, + agentInstances: { + gadget: { id: "gadget", driver: "acp-generic", displayName: "Gadget" }, + }, + }, + { instanceId: "gadget", environment: { CURSOR_API_KEY: { value: "secret" } } }, + makeTempDir(), + ), + ).toThrow("Agent profile not found"); + }); + + it("rejects a missing instance", () => { + expect(() => + applyProfileEnvironment( + { ...defaultSharedSettings, agentInstances: {} }, + { instanceId: "missing", environment: {} }, + makeTempDir(), + ), + ).toThrow("Agent profile not found"); + }); + + it("drops the stored key when the payload key is empty", () => { + const settings = cursorProfileSettings(); + settings.agentInstances.work!.environment = { + CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true }, + }; + + const cleared = applyProfileEnvironment( + settings, + { instanceId: "work", environment: { CURSOR_API_KEY: { value: "", sensitive: true } } }, + ".", + ); + + expect(cleared.instance.environment).toBeUndefined(); + expect(cleared.settings.agentInstances.work?.environment).toBeUndefined(); + expect(cleared.settings.agentInstances.work?.displayName).toBe("Work"); + }); +}); + +describe("applyCreateProfile", () => { + it("creates the profile and seals its key in one settings result", () => { + const dir = makeTempDir(); + const created = applyCreateProfile( + defaultSharedSettings, + { + driver: "cursor", + id: "work", + displayName: " Work ", + environment: { CURSOR_API_KEY: { value: " profile-secret " } }, + }, + dir, + ); + const stored = created.instance.environment?.CURSOR_API_KEY?.value ?? ""; + + expect(created.settings.agentInstances.work).toBe(created.instance); + expect(created.instance.displayName).toBe("Work"); + expect(decryptSecret(dir, stored)).toBe(" profile-secret "); + expect(JSON.stringify(created.settings)).not.toContain(" profile-secret "); + }); + + it("creates a config-only profile for a provider with no credential", () => { + const created = applyCreateProfile( + defaultSharedSettings, + { driver: "claude", id: "work", displayName: "Work", config: { configDir: "~/x" } }, + makeTempDir(), + ); + + expect(created.instance.config).toEqual({ configDir: "~/x" }); + expect(created.instance.environment).toBeUndefined(); + }); + + it("does not mutate settings when creation validation fails", () => { + const settings = { ...defaultSharedSettings, agentInstances: {} }; + + expect(() => + applyCreateProfile( + settings, + { driver: "cursor", id: "work", displayName: " " }, + makeTempDir(), + ), + ).toThrow("require a name"); + expect(() => + applyCreateProfile( + settings, + { driver: "acp-generic", id: "gadget", displayName: "Gadget" }, + makeTempDir(), + ), + ).toThrow("does not support profiles"); + expect(settings.agentInstances).toEqual({}); + }); +}); + +describe("applyProfileEnvironment (free-form environment provider)", () => { function claudeProfileSettings(environment?: AgentInstanceConfig["environment"]): SharedSettings { const instance: AgentInstanceConfig = { id: "glm", @@ -637,7 +780,7 @@ describe("applyClaudeProfileEnvironment", () => { } it("seals sensitive values and stores non-sensitive ones as plaintext", () => { - const { settings, instance } = applyClaudeProfileEnvironment( + const { settings, instance } = applyProfileEnvironment( claudeProfileSettings(), { instanceId: "glm", @@ -662,14 +805,14 @@ describe("applyClaudeProfileEnvironment", () => { it("round-trips an already-sealed secret without re-sealing it", () => { const dir = makeTempDir(); - const first = applyClaudeProfileEnvironment( + const first = applyProfileEnvironment( claudeProfileSettings(), { instanceId: "glm", environment: { TOKEN: { value: "plain", sensitive: true } } }, dir, ); const sealed = first.instance.environment?.TOKEN?.value ?? ""; - const second = applyClaudeProfileEnvironment( + const second = applyProfileEnvironment( claudeProfileSettings(), { instanceId: "glm", environment: { TOKEN: { value: sealed, sensitive: true } } }, dir, @@ -678,7 +821,7 @@ describe("applyClaudeProfileEnvironment", () => { }); it("drops empty values and removes the environment field when all are empty", () => { - const { instance } = applyClaudeProfileEnvironment( + const { instance } = applyProfileEnvironment( claudeProfileSettings({ OLD: { value: "x" } }), { instanceId: "glm", environment: { OLD: { value: "" }, "": { value: "ignored" } } }, makeTempDir(), @@ -688,7 +831,7 @@ describe("applyClaudeProfileEnvironment", () => { it("throws for a missing instance or a non-Claude driver", () => { expect(() => - applyClaudeProfileEnvironment( + applyProfileEnvironment( claudeProfileSettings(), { instanceId: "nope", environment: {} }, makeTempDir(), @@ -702,11 +845,7 @@ describe("applyClaudeProfileEnvironment", () => { }, }; expect(() => - applyClaudeProfileEnvironment( - acpSettings, - { instanceId: "droid", environment: {} }, - makeTempDir(), - ), + applyProfileEnvironment(acpSettings, { instanceId: "droid", environment: {} }, makeTempDir()), ).toThrow(/not found/i); }); }); diff --git a/src/main/sharedSettingsFile.ts b/src/main/sharedSettingsFile.ts index 3917ecb10..7179e9fe4 100644 --- a/src/main/sharedSettingsFile.ts +++ b/src/main/sharedSettingsFile.ts @@ -3,8 +3,10 @@ import { writeFileAtomic } from "@/shared/atomicFile"; import type { AgentInstanceConfig, AgentInstanceEnvVar, - SetClaudeProfileEnvironmentPayload, + CreateProfilePayload, + SetProfileEnvironmentPayload, } from "@/shared/contracts"; +import { agentProfileDriver, isAgentProfileDriver } from "@/shared/contracts"; import { isSensitiveAgentSetting, sensitiveAgentSettingKeys } from "@/shared/agentSecrets"; import { encryptSecret } from "@/shared/secretStorage"; import { @@ -53,14 +55,14 @@ export function patchSharedSettingsFile( } /** - * Re-merge supervisor-managed fields and encrypted Claude-profile environments + * Re-merge supervisor-managed fields and encrypted provider-profile environments * from the on-disk settings into an `incoming` (renderer- or tool-originated) * settings object, so a plaintext-capable write can never clobber a secret or a * field the supervisor owns. Preserves: * - `acpRegistryInstalledAgents` and `agentHookSupport` (supervisor-managed), * - `acp-generic` agent instances (supervisor-managed), and - * - each Claude profile's `environment` (owned by the encrypting - * `setClaudeProfileEnvironment` path). + * - each Claude/Cursor profile's `environment` (owned by an encrypting + * main-local write path). * Extracted verbatim from the IPC `setSharedSettings` path so every write — * including the app-controls MCP `update_settings` tool — applies one guard. * `incoming` is assumed already normalized. @@ -89,10 +91,11 @@ export function mergeManagedSharedSettings( Object.entries(incoming.agentInstances) .filter(([, instance]) => instance.driver !== "acp-generic") .map(([id, instance]): [string, AgentInstanceConfig] => { - // A Claude profile's `environment` is owned by the encrypting - // `setClaudeProfileEnvironment` path; pin it to disk so a plaintext- - // capable write can never leak or clear a saved secret. - if (instance.driver !== "claude") return [id, instance]; + // Profile environments are owned by the encrypting main-local write + // path; pin them to disk so a plaintext-capable renderer write can + // never leak or clear a saved secret. Driven by the profile-driver + // registry, so a new multi-profile provider is covered on registration. + if (!isAgentProfileDriver(instance.driver)) return [id, instance]; const onDiskEnv = onDisk.agentInstances[id]?.environment; const next: AgentInstanceConfig = { ...instance }; if (onDiskEnv) next.environment = onDiskEnv; @@ -152,30 +155,45 @@ export function applyAgentSecretSetting( } /** - * Apply a Claude profile's environment edit, sealing any `sensitive` value that - * is not already sealed. `baseDir` is the settings directory (passed through to - * the cipher). Empty values are dropped (delete semantics); an empty result - * removes the `environment` field entirely. Throws if the instance is missing - * or is not a Claude profile. Returns the next settings plus the updated - * instance (with sealed env) for the renderer store to adopt without re-reading. + * Seal a profile's environment edit and write it onto its instance. + * + * Any `sensitive` value that is not already sealed is encrypted with `baseDir` + * (the settings directory) as the cipher scope. Empty values are dropped + * (delete semantics); an empty result removes the `environment` field entirely. + * Throws when the instance is missing or its driver does not support profiles, + * so a caller can never seal a secret onto an unrelated instance. Returns the + * next settings plus the updated instance so the renderer store can adopt the + * sealed result without re-reading the file. + * + * Shared by every multi-profile provider: Claude sends a free-form environment, + * Cursor sends its single credential var. Neither needs its own write path. */ -export function applyClaudeProfileEnvironment( +export function applyProfileEnvironment( settings: SharedSettings, - payload: SetClaudeProfileEnvironmentPayload, + payload: SetProfileEnvironmentPayload, baseDir: string, ): { settings: SharedSettings; instance: AgentInstanceConfig } { const instance = settings.agentInstances[payload.instanceId]; - if (!instance || instance.driver !== "claude") { - throw new Error(`Claude profile not found: ${payload.instanceId}`); + const profileDriver = instance ? agentProfileDriver(instance.driver) : undefined; + if (!instance || !profileDriver) { + throw new Error(`Agent profile not found: ${payload.instanceId}`); + } + + if ( + profileDriver.credentialEnvVar && + Object.keys(payload.environment).some((name) => name.trim() !== profileDriver.credentialEnvVar) + ) { + throw new Error(`${instance.driver} profiles only support ${profileDriver.credentialEnvVar}.`); } const nextEnv: Record = {}; for (const [name, variable] of Object.entries(payload.environment)) { const key = name.trim(); if (key.length === 0 || variable.value.length === 0) continue; - nextEnv[key] = variable.sensitive - ? { value: encryptSecret(baseDir, variable.value), sensitive: true } - : { value: variable.value }; + nextEnv[key] = + variable.sensitive || key === profileDriver.credentialEnvVar + ? { value: encryptSecret(baseDir, variable.value), sensitive: true } + : { value: variable.value }; } const nextInstance: AgentInstanceConfig = { ...instance }; @@ -193,3 +211,39 @@ export function applyClaudeProfileEnvironment( instance: nextInstance, }; } + +/** + * Create a profile instance and seal its initial environment in one write, so a + * credential supplied at creation time never travels through the renderer's + * plaintext settings flush. + */ +export function applyCreateProfile( + settings: SharedSettings, + payload: CreateProfilePayload, + baseDir: string, +): { settings: SharedSettings; instance: AgentInstanceConfig } { + if (settings.agentInstances[payload.id]) { + throw new Error(`Agent profile already exists: ${payload.id}`); + } + if (!isAgentProfileDriver(payload.driver)) { + throw new Error(`Driver does not support profiles: ${payload.driver}`); + } + const displayName = payload.displayName.trim(); + if (!displayName) { + throw new Error("Agent profiles require a name."); + } + const created: AgentInstanceConfig = { + id: payload.id, + driver: payload.driver, + displayName, + ...(payload.config === undefined ? {} : { config: payload.config }), + }; + return applyProfileEnvironment( + { + ...settings, + agentInstances: { ...settings.agentInstances, [payload.id]: created }, + }, + { instanceId: payload.id, environment: payload.environment ?? {} }, + baseDir, + ); +} diff --git a/src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.test.tsx b/src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.test.tsx index 75ac64842..a52be01fd 100644 --- a/src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.test.tsx +++ b/src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.test.tsx @@ -2,6 +2,7 @@ import { fireEvent, screen, waitFor, within } from "@testing-library/react"; import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; import { beforeEach, describe, expect, it, vi } from "vitest"; import "@/renderer/components/providers/opencode"; +import "@/renderer/components/providers/cursor"; import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { ProviderModelMenu, type ProviderModelMenuProvider } from "./ProviderModelMenu"; @@ -127,6 +128,25 @@ describe("ProviderModelMenu", () => { }); }); + it("uses a renamed Cursor profile label for the trigger badge", () => { + const provider = makeCursorProvider(); + provider.kind = "cursor:work"; + provider.label = "Cursor Day job"; + render( + void>()} + />, + ); + + const trigger = screen.getByRole("button", { name: "Select model" }); + expect(trigger).toHaveTextContent("D"); + expect(trigger).not.toHaveTextContent("W"); + expect(trigger).toHaveTextContent("Codex 5.1 Max"); + }); + it("hides the list scrollbar for long model lists", async () => { render( model.id === modelId)) { return modelId; } - if (provider.kind !== "cursor") { + if (baseAgentKind(provider.kind) !== "cursor") { return modelId; } const normalized = migrateCursorBaseId(parseCursorModelId(modelId).baseId); @@ -419,6 +419,7 @@ export function ProviderModelMenu(props: ProviderModelMenuProps) { @@ -878,6 +879,7 @@ function WindowedProviderModelList(props: { @@ -978,6 +980,7 @@ function HeaderProvider(props: { diff --git a/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts b/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts index 0bba6c37a..f4d4a43df 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts @@ -470,6 +470,7 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr providerKind: m.ref.agentKind, providerKey: visibleProvider?.key ?? m.ref.agentKind, hiddenModelsKey: visibleProvider?.visibilityKey ?? m.ref.agentKind, + providerLabel: m.providerLabel, modelId: m.ref.modelId, label: m.label, ...(m.ref.presentationMode ? { presentationMode: m.ref.presentationMode } : {}), @@ -541,6 +542,7 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr providerKind: provider.kind, providerKey: key, hiddenModelsKey: visibilityKey, + providerLabel: provider.label, modelId: m.id, label: m.label, ...(provider.presentationMode ? { presentationMode: provider.presentationMode } : {}), @@ -578,6 +580,7 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr providerKind: provider.kind, providerKey: key, hiddenModelsKey: visibilityKey, + providerLabel: provider.label, modelId: m.id, label: m.label, ...(provider.presentationMode ? { presentationMode: provider.presentationMode } : {}), @@ -610,6 +613,7 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr providerKind: provider.kind, providerKey: key, hiddenModelsKey: visibilityKey, + providerLabel: provider.label, modelId: m.id, label: m.label, ...(provider.presentationMode ? { presentationMode: provider.presentationMode } : {}), diff --git a/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.test.ts b/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.test.ts index f76ab609c..14eb60dcb 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.test.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.test.ts @@ -24,6 +24,15 @@ describe("formatShortcutModelLabel", () => { ).toBe("Composer 2.5 · Fast"); }); + it("formats Cursor profile shortcut ids with the same rules as the base provider", () => { + expect(formatShortcutFallbackLabel("cursor:work", "composer-2.5[fast=true]")).toBe( + "Composer 2.5 · Fast", + ); + expect(formatShortcutModelLabel("cursor:work", "composer-2.5[fast=true]", "Composer 2.5")).toBe( + "Composer 2.5 · Fast", + ); + }); + it("appends ACP param hints when a grouped Cursor row is reused", () => { expect(formatShortcutModelLabel("cursor", "composer-2.5[fast=true]", "Composer 2.5")).toBe( "Composer 2.5 · Fast", @@ -288,4 +297,29 @@ describe("buildProviderModelItems shortcut labels", () => { "model:cursor:gui:gpt-5.5[context=272k,reasoning=medium,fast=false]", ]); }); + + it("keeps Cursor profile identity and legacy runtime labels scoped to the profile", () => { + const items = buildProviderModelItems({ + providers: [ + codexProvider, + { + ...cursorProvider, + kind: "cursor:work", + label: "Cursor Day job", + presentationMode: "terminal", + }, + ], + search: "", + }); + const header = items.find( + (item) => item.type === "header-provider" && item.providerKind === "cursor:work", + ); + const row = items.find((item) => item.type === "model" && item.providerKind === "cursor:work"); + + expect(header?.type === "header-provider" ? header.label : undefined).toBe( + "Cursor Day job CLI", + ); + expect(row?.type === "model" ? row.providerLabel : undefined).toBe("Cursor Day job"); + expect(row?.type === "model" ? row.hiddenModelsKey : undefined).toBe("cursor:work"); + }); }); diff --git a/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.ts b/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.ts index 3375a778c..1c9b67acc 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/modelShortcutLabel.ts @@ -1,4 +1,5 @@ import { migrateCursorBaseId } from "@/shared/cursorModelId"; +import { baseAgentKind } from "@/shared/contracts"; import { capitalizeSegment, formatBracketParamHints, @@ -29,7 +30,7 @@ function formatCodexShortcutLabel(modelId: string, label: string): string { export function formatShortcutFallbackLabel(agentKind: string, modelId: string): string { const baseId = stripBracketParams(modelId); const baseLabel = - agentKind === "cursor" + baseAgentKind(agentKind) === "cursor" ? formatCursorBaseModelLabel(migrateCursorBaseId(baseId)) : baseId .split(/[-_/]/g) @@ -53,7 +54,7 @@ export function formatShortcutModelLabel( let next = agentKind === "codex" ? formatCodexShortcutLabel(modelId, label) : label; - if (agentKind === "cursor" && modelId.includes("[")) { + if (baseAgentKind(agentKind) === "cursor" && modelId.includes("[")) { const hints = formatBracketParamHints(modelId); if (hints && !next.includes(hints)) { next = `${next} · ${hints}`; diff --git a/src/renderer/components/common/ProviderModelMenu/parts/providerIdentity.ts b/src/renderer/components/common/ProviderModelMenu/parts/providerIdentity.ts index d24c90f17..f0bc7b76c 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/providerIdentity.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/providerIdentity.ts @@ -1,4 +1,4 @@ -import type { ThreadPresentationMode } from "@/shared/contracts"; +import { baseAgentKind, type ThreadPresentationMode } from "@/shared/contracts"; type ProviderIdentityInput = { kind: string; @@ -55,7 +55,8 @@ function resolveRuntimeVariantId( ): string | undefined { if (presentationMode !== "gui") return undefined; return ( - runtimeVariant?.toLowerCase() ?? legacySurfaceIdentity[agentKind]?.defaultGuiRuntimeVariant + runtimeVariant?.toLowerCase() ?? + legacySurfaceIdentity[baseAgentKind(agentKind)]?.defaultGuiRuntimeVariant ); } @@ -73,7 +74,7 @@ export function providerLabelForPresentation(provider: ProviderIdentityInput): s const label = provider.label ?? provider.kind; const runtimeLabel = provider.presentationMode === "terminal" - ? legacySurfaceIdentity[provider.kind]?.terminalRuntimeLabel + ? legacySurfaceIdentity[baseAgentKind(provider.kind)]?.terminalRuntimeLabel : provider.capabilities?.runtimeLabel; if (!runtimeLabel) return label; // Callers may pass a provider whose label was already qualified by this diff --git a/src/renderer/components/common/ProviderModelMenu/parts/types.ts b/src/renderer/components/common/ProviderModelMenu/parts/types.ts index 5544e90c6..843ec69aa 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/types.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/types.ts @@ -34,6 +34,7 @@ export interface ProviderModelRow { providerKey: string; hiddenModelsKey: string; providerIcon?: string; + providerLabel: string; presentationMode?: ThreadPresentationMode; modelId: string; label: string; diff --git a/src/renderer/components/providers/ProviderIcon.test.tsx b/src/renderer/components/providers/ProviderIcon.test.tsx index bae9327ca..eb366608b 100644 --- a/src/renderer/components/providers/ProviderIcon.test.tsx +++ b/src/renderer/components/providers/ProviderIcon.test.tsx @@ -2,6 +2,7 @@ import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import { ProviderIcon } from "./ProviderIcon"; import "./claude"; +import "./cursor"; describe("ProviderIcon", () => { it("uses the ACP instance id for generic fallback initials", () => { @@ -23,4 +24,18 @@ describe("ProviderIcon", () => { expect(screen.getByText("P")).toBeInTheDocument(); expect(screen.queryByText("C")).not.toBeInTheDocument(); }); + + it("badges a Cursor profile with its own initial", () => { + render(); + + expect(screen.getByText("W")).toBeInTheDocument(); + }); + + it("falls back to the first character of a non-latin profile name", () => { + render(); + + // A latin-only match would leave every non-latin profile sharing "?". + expect(screen.getByText("Р")).toBeInTheDocument(); + expect(screen.queryByText("?")).not.toBeInTheDocument(); + }); }); diff --git a/src/renderer/components/providers/ProviderIcon.tsx b/src/renderer/components/providers/ProviderIcon.tsx index 57a473ce9..3988105c2 100644 --- a/src/renderer/components/providers/ProviderIcon.tsx +++ b/src/renderer/components/providers/ProviderIcon.tsx @@ -1,5 +1,5 @@ import type { CSSProperties, ReactNode } from "react"; -import { ACP_GENERIC_KIND_PREFIX, CLAUDE_PROFILE_KIND_PREFIX } from "@/shared/contracts"; +import { ACP_GENERIC_KIND_PREFIX, baseAgentKind, isAgentProfileKind } from "@/shared/contracts"; import type { StatusTone } from "./statusTone"; import { syncMaskScanPhase } from "./syncMaskScanPhase"; import { lookupProviderRegistration } from "./providerRegistry"; @@ -67,16 +67,27 @@ function fallbackInitial(label: string | undefined): string { const raw = label?.startsWith(ACP_GENERIC_KIND_PREFIX) ? label.slice(ACP_GENERIC_KIND_PREFIX.length).trim() : (label?.trim() ?? ""); - return (raw.match(/[A-Za-z0-9]/)?.[0] ?? "?").toUpperCase(); + // Prefer a latin/digit initial, but fall back to the first grapheme of any + // script so a profile named "Работа" or "工作" gets its own initial instead + // of a shared "?" badge. Segmenting keeps combining marks and emoji whole. + const latin = raw.match(/[A-Za-z0-9]/)?.[0]; + if (latin) return latin.toUpperCase(); + const [firstGrapheme] = new Intl.Segmenter().segment(raw); + return (firstGrapheme?.segment ?? "?").toUpperCase(); } -function claudeProfileBadgeLabel(kind: string, fallbackLabel: string | undefined): string { - const profileId = kind.slice(CLAUDE_PROFILE_KIND_PREFIX.length); +function profileBadgeLabel(kind: string, fallbackLabel: string | undefined): string { + const baseKind = baseAgentKind(kind); + const profileId = kind.slice(baseKind.length + 1); const label = fallbackLabel?.trim(); if (!label) return profileId; - if (label === kind || label.toLowerCase().startsWith(CLAUDE_PROFILE_KIND_PREFIX)) + // An unlabelled status can arrive carrying the raw kind; the instance id + // reads better as a badge than the provider's first letter. + if (label === kind || label.toLowerCase().startsWith(`${baseKind.toLowerCase()}:`)) return profileId; - const profileLabel = label.replace(/^claude\s+/i, "").trim(); + const profileLabel = label.toLowerCase().startsWith(`${baseKind.toLowerCase()} `) + ? label.slice(baseKind.length).trim() + : label; return profileLabel || profileId; } @@ -135,12 +146,14 @@ export function ProviderIcon(props: { const rendered = ( ); - if (props.kind.startsWith(CLAUDE_PROFILE_KIND_PREFIX)) { + // Every multi-profile provider gets the same instance badge, so the icon + // needs no per-provider knowledge. + if (isAgentProfileKind(props.kind)) { return ( {rendered} - {fallbackInitial(claudeProfileBadgeLabel(props.kind, props.fallbackLabel))} + {fallbackInitial(profileBadgeLabel(props.kind, props.fallbackLabel))} ); diff --git a/src/renderer/components/thread/buildModelPickerControls.test.ts b/src/renderer/components/thread/buildModelPickerControls.test.ts index 941333614..6e6fd18f0 100644 --- a/src/renderer/components/thread/buildModelPickerControls.test.ts +++ b/src/renderer/components/thread/buildModelPickerControls.test.ts @@ -167,6 +167,43 @@ describe("buildControls model preferences", () => { ); expect(onPreferenceChange).toHaveBeenCalledWith("b", { effort: "high", fast: true }); }); + + it("normalizes a Cursor profile's bracket model before building controls", () => { + const agent = { + kind: "cursor:work", + label: "Cursor Work", + installed: true, + authState: "authenticated", + capabilities: { + ...capabilities, + models: [{ id: "gpt-5.1-codex-max", label: "Codex 5.1 Max" }], + }, + } as AgentStatus; + const thread = { + id: "thread-1", + projectId: "project-1", + title: "Thread", + agentKind: "cursor:work", + config: { model: "gpt-5.1-codex-high-thinking-fast" }, + status: "idle", + attention: "none", + canResumeWithConfig: true, + archived: false, + done: false, + starred: false, + presentationMode: "gui", + createdAt: "2026-08-20T00:00:00.000Z", + updatedAt: "2026-08-20T00:00:00.000Z", + } as Thread; + + const modelControl = buildControls(thread, agent, undefined, vi.fn()).find( + (control) => control.kind === "provider-model", + ); + + expect(modelControl?.kind === "provider-model" ? modelControl.currentModel : undefined).toBe( + "gpt-5.1-codex-max", + ); + }); }); describe("buildModelPickerControls fast toggle", () => { diff --git a/src/renderer/components/thread/buildModelPickerControls.tsx b/src/renderer/components/thread/buildModelPickerControls.tsx index 77598be1e..3bf84742e 100644 --- a/src/renderer/components/thread/buildModelPickerControls.tsx +++ b/src/renderer/components/thread/buildModelPickerControls.tsx @@ -7,6 +7,7 @@ import type { ThreadConfig, ThreadPresentationMode, } from "@/shared/contracts"; +import { baseAgentKind } from "@/shared/contracts"; import { migrateCursorBaseId, parseCursorModelId } from "@/shared/cursorModelId"; import { statusToMenuProvider, @@ -324,7 +325,10 @@ function normalizeCursorComposerConfig( capabilities: AgentStatus["capabilities"], ): ThreadConfig { if (!config) return { model: capabilities.models[0]?.id ?? "auto" }; - if (agentKind !== "cursor" || capabilities.models.some((model) => model.id === config.model)) { + if ( + baseAgentKind(agentKind) !== "cursor" || + capabilities.models.some((model) => model.id === config.model) + ) { return config; } diff --git a/src/renderer/components/thread/threadDraftViewHelpers.test.ts b/src/renderer/components/thread/threadDraftViewHelpers.test.ts index 21cd6108a..80cbebddf 100644 --- a/src/renderer/components/thread/threadDraftViewHelpers.test.ts +++ b/src/renderer/components/thread/threadDraftViewHelpers.test.ts @@ -57,6 +57,31 @@ describe("resolveProviderDraftConfig fast mode", () => { const gated = agentWith({ fastDisabledReason: "Fast requests are disabled for this account." }); expect(resolveProviderDraftConfig(gated, { model: "fast-capable" }).fast).toBeUndefined(); }); + + it("normalizes Cursor profile bracket models before resolving draft controls", () => { + expect( + resolveProviderDraftConfig( + { + ...agentWith(), + kind: "cursor:work", + label: "Cursor Work", + capabilities: { + ...capabilities, + models: [{ id: "gpt-5.1-codex-max", label: "Codex 5.1 Max" }], + modelEfforts: { "gpt-5.1-codex-max": ["high"] }, + fastModels: ["gpt-5.1-codex-max"], + thinkingModels: ["gpt-5.1-codex-max"], + }, + }, + { model: "gpt-5.1-codex-high-thinking-fast" }, + ), + ).toMatchObject({ + model: "gpt-5.1-codex-max", + effort: "high", + fast: true, + thinking: true, + }); + }); }); describe("resolveFastValue", () => { diff --git a/src/renderer/components/thread/threadDraftViewHelpers.ts b/src/renderer/components/thread/threadDraftViewHelpers.ts index 4757a6d1f..82f6ae22f 100644 --- a/src/renderer/components/thread/threadDraftViewHelpers.ts +++ b/src/renderer/components/thread/threadDraftViewHelpers.ts @@ -6,6 +6,7 @@ import type { ProviderDraftConfig, ThreadPresentationMode, } from "@/shared/contracts"; +import { baseAgentKind } from "@/shared/contracts"; import { migrateCursorBaseId, parseCursorModelId } from "@/shared/cursorModelId"; import { agentStatusForPresentation, @@ -188,7 +189,7 @@ function normalizeCursorPreferredDraft( agent: AgentStatus, preferred?: Partial, ): Partial | undefined { - if (agent.kind !== "cursor" || !preferred?.model) { + if (baseAgentKind(agent.kind) !== "cursor" || !preferred?.model) { return preferred; } if (agent.capabilities.models.some((model) => model.id === preferred.model)) { diff --git a/src/renderer/locales/de/messages.po b/src/renderer/locales/de/messages.po index 6d2733116..4909a1460 100644 --- a/src/renderer/locales/de/messages.po +++ b/src/renderer/locales/de/messages.po @@ -710,10 +710,18 @@ msgstr "7 T" msgid "A candidate is no longer on its experiment branch." msgstr "Ein Kandidat befindet sich nicht mehr auf seinem Experiment-Branch." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Ein Cursor-Profil mit diesem Namen existiert bereits." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Eine eingefrorene Worktree-Quelle erfordert einen vollständigen Commit-Hash" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Ein Profil mit diesem Namen existiert bereits." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Ein Server konnte nicht gestartet werden und wurde übersprungen." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Kandidaten hinzufügen" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Claude-Profil hinzufügen" +#~ msgid "Add Claude profile" +#~ msgstr "Claude-Profil hinzufügen" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Kontextfenster hinzufügen" msgid "Add credentials before this thread can run." msgstr "Fügen Sie Anmeldeinformationen hinzu, bevor dieser Thread ausgeführt werden kann." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Cursor-Profil hinzufügen" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Ordner hinzufügen" @@ -967,7 +979,7 @@ msgstr "Fügen Sie einen manuell hinzu oder importieren Sie ihn aus einem andere msgid "Add pattern" msgstr "Muster hinzufügen" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Profil hinzufügen" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Abbrechen" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Neues Claude-Profil abbrechen" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Neues Claude-Profil abbrechen" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Neues Cursor-Profil abbrechen" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Neues Profil abbrechen" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Claude-{0}-Profil gespeichert." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Claude-{displayName}-Profil hinzugefügt." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Claude-{displayName}-Profil hinzugefügt." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Claude-Profilname" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude-Profil entfernt." +#~ msgid "Claude profile removed." +#~ msgstr "Claude-Profil entfernt." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "„PR erstellen“-Aktion" msgid "Create PR…" msgstr "PR erstellen…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Profil erstellen" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Projekt erstellen" @@ -3493,6 +3517,10 @@ msgstr "Zeitplan erstellen" msgid "Create scheduled task" msgstr "Geplante Aufgabe erstellen" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Erstellen Sie separate Cursor-Profile mit eigenen API-Schlüsseln." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Aufgabe erstellen" @@ -3638,6 +3666,15 @@ msgstr "Aktuelle Serie" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Cursor-Profil {displayName} hinzugefügt." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Von Cursor ACP abgemeldet." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor-GUI-Laufzeit aktualisiert." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "API-Schlüssel des Cursor-Profils" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "API-Schlüssel des Cursor-Profils gespeichert." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Name des Cursor-Profils" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Cursor-Profil entfernt, aber der Status konnte nicht aktualisiert werden." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Cursor-Profil entfernt." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Cursor-Profil umbenannt." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "z. B. 512k oder 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "z. B. Codex GPT-5.5 fast für schnelle Recherchen, OpenCode GLM für umfangreiche Refactorings, Claude Opus für alles Kniffelige." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "z.B. Arbeit" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Name" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Neues Claude-Profilkonfigurationsverzeichnis" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Neuer Claude-Profilname" +#~ msgid "New Claude profile name" +#~ msgstr "Neuer Claude-Profilname" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Neuer Code" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "API-Schlüssel für neues Cursor-Profil" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Name des neuen Cursor-Profils" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Neues Experiment" @@ -7275,6 +7347,10 @@ msgstr "neues Modell" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Neue Telefone, Tablets und Browser erscheinen nach dem Pairing hier." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Name des neuen Profils" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Neues Projekt" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Noch keine Aktivität." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Keine zusätzlichen Claude-Profile." +#~ msgid "No additional Claude profiles." +#~ msgstr "Keine zusätzlichen Claude-Profile." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Keine zusätzlichen Cursor-Profile." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Keine weiteren Profile." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Offen" msgid "Open {0}" msgstr "{0} öffnen" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "{label} öffnen" @@ -8477,6 +8561,7 @@ msgstr "{label} API-Schlüssel einfügen" msgid "Paste <0>{0} here. Click to copy." msgstr "Fügen Sie hier <0>{0} ein. Zum Kopieren klicken." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Cursor-API-Schlüssel einfügen" @@ -8582,6 +8667,10 @@ msgstr "Symbol auswählen" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Wähle die Laufzeit für neue Cursor-GUI-Chats. Jede Laufzeit wird eigenständig installiert und authentifiziert; offene Chats behalten die Laufzeit, mit der sie gestartet wurden." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Wählen Sie die Laufzeitumgebung für neue Cursor-GUI-Chats. Der Profil-API-Schlüssel gilt für beide Laufzeitumgebungen." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Auswahl fehlgeschlagen" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Produktivität" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Profil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Profil {displayName} hinzugefügt." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Profil-API-Schlüssel" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Profil entfernt, die Status konnten aber nicht aktualisiert werden." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Profil entfernt." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Statistikzeitraum des Profils" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Profile" @@ -9353,7 +9459,7 @@ msgstr "Benachrichtigungsinhalt verbergen" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "Remote-URL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Entfernen" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Kandidat {0} entfernen" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Claude-Profil entfernen" +#~ msgid "Remove Claude profile" +#~ msgstr "Claude-Profil entfernen" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Verbindung entfernen" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Cursor-Profil entfernen" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Cursor-Profil {label} entfernen" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Cursor-Profil entfernen?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Cursor-SDK-API-Schlüssel entfernen" @@ -9597,6 +9716,14 @@ msgstr "Modell entfernen" msgid "Remove pinned route for {tagLabel}" msgstr "Angeheftete Route für {tagLabel} entfernen" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Profil {label} entfernen" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Profil entfernen?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Projekt entfernen" @@ -9626,6 +9753,19 @@ msgstr "Entfernen Sie die Experimente des Projekts, bevor Sie das Projekt entfer msgid "Remove worktree?" msgstr "Worktree entfernen?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Beim Entfernen von {0} wird der gespeicherte API-Schlüssel gelöscht. Threads, die dieses Profil verwenden, finden ihren Agenten nicht mehr." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Beim Entfernen von {profileName} wird der gespeicherte API-Schlüssel gelöscht. Threads, die dieses Profil verwenden, finden ihren Agenten nicht mehr." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Beim Entfernen von {profileName} gehen seine Poracode-Einstellungen verloren — Umgebungsvariablen, Modelle und Effort. Sein Konfigurationsverzeichnis und die darin enthaltenen Claude-Anmeldedaten bleiben auf der Festplatte." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Claude-Profil speichern" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Cursor-GUI-Laufzeit speichern" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "API-Schlüssel des Cursor-Profils speichern" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Name des Cursor-Profils speichern" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Cursor-SDK-API-Schlüssel speichern" @@ -10611,6 +10759,11 @@ msgstr "Nachricht senden" msgid "Sent selection to terminal." msgstr "Auswahl an Terminal gesendet." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Separater API-Schlüssel" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Claude Code-Konten nach Konfigurationsverzeichnis trennen oder ein Profil auf einen externen Anbieter (z.ai, …) ausrichten. Öffnen Sie ein Profil, um dessen Umgebungsvariablen, Modelle und Reasoning-Stufe zu konfigurieren." @@ -10852,6 +11005,10 @@ msgstr "Es werden {0, plural, one {# Skill} other {# Skills}} angezeigt" msgid "Showing the first 4,000 entries." msgstr "Es werden die ersten 4.000 Einträge angezeigt." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Wird in der Seitenleiste und in der Modellauswahl angezeigt." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Wird neben dem Projektnamen angezeigt." @@ -11797,6 +11954,10 @@ msgstr "der Agent" msgid "The agent" msgstr "Der Agent" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Der API-Schlüssel unten wird von Cursor CLI, ACP und SDK für dieses Profil verwendet." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "die App" @@ -12050,6 +12211,10 @@ msgstr "Dieser Ordner ist leer." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Diese HTTPS-Seite konnte den Desktop über einfaches HTTP nicht erreichen. Erlaube dieser Website den Zugriff auf das lokale Netzwerk und kopple erneut, oder fahre auf der Adresse des Desktops fort." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Dieser Schlüssel wird von Cursor CLI, ACP und SDK für dieses Profil verwendet." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Dieser Link öffnet die App, die Poracode in deinem Netzwerk bereitstellt. Die gehostete App wird über HTTPS ausgeliefert, was Browser daran hindert, einen Desktop über einfaches HTTP zu erreichen." diff --git a/src/renderer/locales/en/messages.po b/src/renderer/locales/en/messages.po index 777ca8e96..8542faf8e 100644 --- a/src/renderer/locales/en/messages.po +++ b/src/renderer/locales/en/messages.po @@ -710,10 +710,18 @@ msgstr "7d" msgid "A candidate is no longer on its experiment branch." msgstr "A candidate is no longer on its experiment branch." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "A Cursor profile with this name already exists." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "A frozen worktree source requires a full commit hash" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "A profile with this name already exists." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "A server could not be started and was skipped." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Add candidate" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Add Claude profile" +#~ msgid "Add Claude profile" +#~ msgstr "Add Claude profile" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Add context window" msgid "Add credentials before this thread can run." msgstr "Add credentials before this thread can run." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Add Cursor profile" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Add folder" @@ -967,7 +979,7 @@ msgstr "Add one manually or import from another agent." msgid "Add pattern" msgstr "Add pattern" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Add profile" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Cancel" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Cancel new Claude profile" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Cancel new Claude profile" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Cancel new Cursor profile" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Cancel new profile" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Claude {0} profile saved." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Claude {displayName} profile added." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Claude {displayName} profile added." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Claude profile name" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude profile removed." +#~ msgid "Claude profile removed." +#~ msgstr "Claude profile removed." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Create PR action" msgid "Create PR…" msgstr "Create PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Create profile" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Create project" @@ -3493,6 +3517,10 @@ msgstr "Create schedule" msgid "Create scheduled task" msgstr "Create scheduled task" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Create separate Cursor profiles with their own API keys." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Create task" @@ -3638,6 +3666,15 @@ msgstr "Current streak" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Cursor {displayName} profile added." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Cursor ACP logged out." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor GUI runtime updated." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Cursor profile API key" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Cursor profile API key saved." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Cursor profile name" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Cursor profile removed, but statuses could not be refreshed." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Cursor profile removed." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Cursor profile renamed." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "e.g. 512k or 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "e.g. Work" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Name" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "New Claude profile config directory" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "New Claude profile name" +#~ msgid "New Claude profile name" +#~ msgstr "New Claude profile name" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "New code" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "New Cursor profile API key" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "New Cursor profile name" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "New experiment" @@ -7275,6 +7347,10 @@ msgstr "new model" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "New phones, tablets, and browsers appear here after pairing." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "New profile name" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "New project" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "No activity yet." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "No additional Claude profiles." +#~ msgid "No additional Claude profiles." +#~ msgstr "No additional Claude profiles." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "No additional Cursor profiles." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "No additional profiles." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Open" msgid "Open {0}" msgstr "Open {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Open {label}" @@ -8477,6 +8561,7 @@ msgstr "Paste {label} API key" msgid "Paste <0>{0} here. Click to copy." msgstr "Paste <0>{0} here. Click to copy." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Paste Cursor API key" @@ -8582,6 +8667,10 @@ msgstr "Pick icon" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Picker failed" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Productivity" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Profile" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Profile {displayName} added." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Profile API key" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Profile removed, but statuses could not be refreshed." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Profile removed." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Profile stats range" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Profiles" @@ -9353,7 +9459,7 @@ msgstr "Redact notification content" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "Remote URL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Remove" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Remove candidate {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Remove Claude profile" +#~ msgid "Remove Claude profile" +#~ msgstr "Remove Claude profile" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Remove connection" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Remove Cursor profile" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Remove Cursor profile {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Remove Cursor profile?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Remove Cursor SDK API key" @@ -9597,6 +9716,14 @@ msgstr "Remove model" msgid "Remove pinned route for {tagLabel}" msgstr "Remove pinned route for {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Remove profile {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Remove profile?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Remove project" @@ -9626,6 +9753,19 @@ msgstr "Remove the project's experiments before removing the project." msgid "Remove worktree?" msgstr "Remove worktree?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Save Claude profile" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Save Cursor GUI runtime" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Save Cursor profile API key" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Save Cursor profile name" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Save Cursor SDK API key" @@ -10611,6 +10759,11 @@ msgstr "Send message" msgid "Sent selection to terminal." msgstr "Sent selection to terminal." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Separate API key" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." @@ -10852,6 +11005,10 @@ msgstr "Showing {0, plural, one {# skill} other {# skills}}" msgid "Showing the first 4,000 entries." msgstr "Showing the first 4,000 entries." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Shown in the sidebar and the model picker." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Shown next to the project name." @@ -11797,6 +11954,10 @@ msgstr "the agent" msgid "The agent" msgstr "The agent" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "The API key below is used by Cursor CLI, ACP, and SDK for this profile." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "the app" @@ -12050,6 +12211,10 @@ msgstr "This folder is empty." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "This key is used by Cursor CLI, ACP, and SDK for this profile." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." diff --git a/src/renderer/locales/es/messages.po b/src/renderer/locales/es/messages.po index 933a394b3..b23b8f9c9 100644 --- a/src/renderer/locales/es/messages.po +++ b/src/renderer/locales/es/messages.po @@ -710,10 +710,18 @@ msgstr "7d" msgid "A candidate is no longer on its experiment branch." msgstr "Un candidato ya no está en la rama de su experimento." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Ya existe un perfil de Cursor con este nombre." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Una fuente de worktree inmovilizada requiere un hash de commit completo" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Ya existe un perfil con este nombre." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "No se pudo iniciar un servidor y se omitió." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Añadir candidato" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Agregar perfil de Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Agregar perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Añadir ventana de contexto" msgid "Add credentials before this thread can run." msgstr "Agrega credenciales antes de que este hilo pueda ejecutarse." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Añadir perfil de Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Añadir carpeta" @@ -967,7 +979,7 @@ msgstr "Añade uno manualmente o impórtalo desde otro agente." msgid "Add pattern" msgstr "Añadir patrón" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Agregar perfil" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Cancelar" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Cancelar nuevo perfil de Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Cancelar nuevo perfil de Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Cancelar nuevo perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Cancelar el nuevo perfil" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Perfil de Claude {0} guardado." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Perfil de Claude {displayName} añadido." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Perfil de Claude {displayName} añadido." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Nombre del perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Perfil de Claude eliminado." +#~ msgid "Claude profile removed." +#~ msgstr "Perfil de Claude eliminado." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Acción de crear PR" msgid "Create PR…" msgstr "Crear PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Crear perfil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Crear proyecto" @@ -3493,6 +3517,10 @@ msgstr "Crear programación" msgid "Create scheduled task" msgstr "Crear tarea programada" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Crea perfiles de Cursor separados con sus propias claves de API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Crear tarea" @@ -3638,6 +3666,15 @@ msgstr "Racha actual" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Se añadió el perfil de Cursor {displayName}." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Se cerró la sesión de Cursor ACP." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Entorno de ejecución de la GUI de Cursor actualizado." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Clave de API del perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Clave de API del perfil de Cursor guardada." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Nombre del perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "El perfil de Cursor se eliminó, pero no se pudieron actualizar los estados." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Perfil de Cursor eliminado." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Perfil de Cursor renombrado." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "p. ej. 512k o 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "p. ej. Codex GPT-5.5 fast para búsquedas rápidas, OpenCode GLM para refactorizaciones masivas, Claude Opus para cualquier cosa delicada." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "p. ej. Trabajo" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Nombre" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Nuevo directorio de configuración del perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Nuevo nombre de perfil de Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Nuevo nombre de perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Nuevo código" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Clave de API del nuevo perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Nombre del nuevo perfil de Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Nuevo experimento" @@ -7275,6 +7347,10 @@ msgstr "modelo nuevo" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Los nuevos teléfonos, tabletas y navegadores aparecerán aquí después de emparejarlos." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Nombre del nuevo perfil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Nuevo proyecto" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Aún no hay actividad." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "No hay perfiles de Claude adicionales." +#~ msgid "No additional Claude profiles." +#~ msgstr "No hay perfiles de Claude adicionales." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "No hay perfiles de Cursor adicionales." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "No hay perfiles adicionales." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Abrir" msgid "Open {0}" msgstr "Abrir {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Abrir {label}" @@ -8477,6 +8561,7 @@ msgstr "Pega la clave API de {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Pega <0>{0} aquí. Haz clic para copiar." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Pega la clave de API de Cursor" @@ -8582,6 +8667,10 @@ msgstr "Elegir icono" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Elige el runtime que respalda los nuevos chats de la GUI de Cursor. Cada runtime se instala y se autentica por separado; los chats abiertos conservan el runtime con el que se iniciaron." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Elige el entorno que respalda los nuevos chats de la interfaz de Cursor. La clave de API del perfil se aplica a ambos entornos." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "El selector falló" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Productividad" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Perfil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Perfil {displayName} añadido." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Clave de API del perfil" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Perfil eliminado, pero no se pudieron actualizar los estados." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Perfil eliminado." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Rango de estadísticas del perfil" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Perfiles" @@ -9353,7 +9459,7 @@ msgstr "Ocultar el contenido de las notificaciones" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "URL del remoto" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Eliminar" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Eliminar candidato {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Eliminar perfil de Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Eliminar perfil de Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Eliminar conexión" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Eliminar perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Eliminar perfil de Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "¿Eliminar el perfil de Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Eliminar la clave de API del SDK de Cursor" @@ -9597,6 +9716,14 @@ msgstr "Eliminar modelo" msgid "Remove pinned route for {tagLabel}" msgstr "Eliminar la ruta fijada para {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Eliminar el perfil {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "¿Eliminar el perfil?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Eliminar proyecto" @@ -9626,6 +9753,19 @@ msgstr "Elimina los experimentos del proyecto antes de eliminarlo." msgid "Remove worktree?" msgstr "¿Eliminar worktree?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Al eliminar {0} se borra su clave de API guardada. Los hilos que usan este perfil ya no encontrarán su agente." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Al eliminar {profileName} se borra su clave de API guardada. Los hilos que usan este perfil ya no encontrarán su agente." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Al eliminar {profileName} se descartan sus ajustes de Poracode: variables de entorno, modelos y esfuerzo. Su directorio de configuración y las credenciales de Claude que contiene permanecen en el disco." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Guardar perfil de Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Guardar el entorno de ejecución de la GUI de Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Guardar clave de API del perfil de Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Guardar el nombre del perfil de Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Guardar la clave de API del SDK de Cursor" @@ -10611,6 +10759,11 @@ msgstr "Enviar mensaje" msgid "Sent selection to terminal." msgstr "Selección enviada al terminal." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Clave de API independiente" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Separa las cuentas de Claude Code por directorio de configuración, o apunta un perfil a un proveedor externo (z.ai, …). Abre un perfil para configurar sus variables de entorno, modelos y esfuerzo." @@ -10852,6 +11005,10 @@ msgstr "Mostrando {0, plural, one {# skill} other {# skills}}" msgid "Showing the first 4,000 entries." msgstr "Mostrando las primeras 4000 entradas." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Se muestra en la barra lateral y en el selector de modelos." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Se muestra junto al nombre del proyecto." @@ -11797,6 +11954,10 @@ msgstr "el agente" msgid "The agent" msgstr "El agente" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "La clave de API de abajo se usa en Cursor CLI, ACP y SDK para este perfil." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "la app" @@ -12050,6 +12211,10 @@ msgstr "Esta carpeta está vacía." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Esta página HTTPS no pudo conectar con el escritorio en HTTP simple. Permite el acceso a la red local para este sitio y vuelve a emparejar, o continúa en la dirección propia del escritorio." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Cursor CLI, ACP y SDK usan esta clave para este perfil." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Este enlace abre la app que Poracode sirve en tu red. La app alojada se sirve por HTTPS, lo que impide que los navegadores lleguen a un escritorio en HTTP simple." diff --git a/src/renderer/locales/fr/messages.po b/src/renderer/locales/fr/messages.po index 2fc021135..16db2a807 100644 --- a/src/renderer/locales/fr/messages.po +++ b/src/renderer/locales/fr/messages.po @@ -710,10 +710,18 @@ msgstr "7 j" msgid "A candidate is no longer on its experiment branch." msgstr "Un candidat n’est plus sur la branche de son expérience." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Un profil Cursor portant ce nom existe déjà." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Une source d'arbre de travail figée nécessite un hash de commit complet" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Un profil portant ce nom existe déjà." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Un serveur n'a pas pu démarrer et a été ignoré." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Ajouter un candidat" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Ajouter un profil Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Ajouter un profil Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Ajouter une fenêtre de contexte" msgid "Add credentials before this thread can run." msgstr "Ajoutez des informations d'identification avant que ce fil puisse s'exécuter." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Ajouter un profil Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Ajouter un dossier" @@ -967,7 +979,7 @@ msgstr "Ajoutez-en un manuellement ou importez-le depuis un autre agent." msgid "Add pattern" msgstr "Ajouter un motif" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Ajouter un profil" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Annuler" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Annuler le nouveau profil Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Annuler le nouveau profil Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Annuler le nouveau profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Annuler le nouveau profil" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Profil de Claude {0} enregistré." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Profil de Claude {displayName} ajouté." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Profil de Claude {displayName} ajouté." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Nom du profil Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Profil de Claude supprimé." +#~ msgid "Claude profile removed." +#~ msgstr "Profil de Claude supprimé." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Action Créer une PR" msgid "Create PR…" msgstr "Créer une PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Créer le profil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Créer un projet" @@ -3493,6 +3517,10 @@ msgstr "Créer une planification" msgid "Create scheduled task" msgstr "Créer une tâche planifiée" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Créez des profils Cursor distincts avec leurs propres clés API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Créer une tâche" @@ -3638,6 +3666,15 @@ msgstr "Série actuelle" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Profil Cursor {displayName} ajouté." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Déconnexion de Cursor ACP effectuée." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Environnement d’exécution de l’interface Cursor mis à jour." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Clé API du profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Clé API du profil Cursor enregistrée." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Nom du profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Le profil Cursor a été supprimé, mais les statuts n’ont pas pu être actualisés." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Profil Cursor supprimé." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Profil Cursor renommé." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "p. ex. 512k ou 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "par ex. Codex GPT-5.5 fast pour les recherches rapides, OpenCode GLM pour les refactorisations en masse, Claude Opus pour tout ce qui est subtil." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "par ex. Travail" @@ -7139,6 +7202,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Nom" @@ -7221,13 +7285,21 @@ msgid "New Claude profile config directory" msgstr "Nouveau répertoire de configuration du profil Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Nouveau nom de profil Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Nouveau nom de profil Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Nouveau code" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Clé API du nouveau profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Nom du nouveau profil Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Nouvelle expérience" @@ -7274,6 +7346,10 @@ msgstr "nouveau modèle" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Les nouveaux téléphones, tablettes et navigateurs apparaîtront ici après l’appairage." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Nom du nouveau profil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Nouveau projet" @@ -7406,8 +7482,16 @@ msgid "No activity yet." msgstr "Aucune activité pour l'instant." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Aucun profil Claude supplémentaire." +#~ msgid "No additional Claude profiles." +#~ msgstr "Aucun profil Claude supplémentaire." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Aucun profil Cursor supplémentaire." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Aucun profil supplémentaire." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7991,7 +8075,7 @@ msgstr "Ouvrir" msgid "Open {0}" msgstr "Ouvrir {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Ouvrir {label}" @@ -8476,6 +8560,7 @@ msgstr "Collez la clé API {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Collez <0>{0} ici. Cliquez pour copier." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Collez la clé API Cursor" @@ -8581,6 +8666,10 @@ msgstr "Choisir une icône" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Choisissez la durée d'exécution qui alimente les nouveaux chats GUI Cursor. Chaque durée d'exécution s'installe et s'authentifie séparément ; les chats ouverts conservent celle avec laquelle ils ont démarré." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Choisissez l’environnement d’exécution des nouveaux chats Cursor. La clé API du profil s’applique aux deux environnements." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Échec du sélecteur" @@ -8911,15 +9000,32 @@ msgid "Productivity" msgstr "Productivité" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Profil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Profil {displayName} ajouté." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Clé API du profil" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Profil supprimé, mais les statuts n'ont pas pu être actualisés." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Profil supprimé." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Plage des statistiques du profil" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Profils" @@ -9352,7 +9458,7 @@ msgstr "Masquer le contenu des notifications" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9511,6 +9617,7 @@ msgid "Remote URL" msgstr "URL distante" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Supprimer" @@ -9556,13 +9663,25 @@ msgid "Remove candidate {0}" msgstr "Supprimer le candidat {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Supprimer le profil de Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Supprimer le profil de Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Supprimer la connexion" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Supprimer le profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Supprimer le profil Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Supprimer le profil Cursor ?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Supprimer la clé API du SDK Cursor" @@ -9596,6 +9715,14 @@ msgstr "Supprimer le modèle" msgid "Remove pinned route for {tagLabel}" msgstr "Supprimer la route épinglée pour {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Supprimer le profil {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Supprimer le profil ?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Supprimer le projet" @@ -9625,6 +9752,19 @@ msgstr "Supprimez les expériences du projet avant de supprimer le projet." msgid "Remove worktree?" msgstr "Supprimer le worktree ?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Supprimer {0} efface sa clé API enregistrée. Les fils de discussion qui utilisent ce profil ne trouveront plus leur agent." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Supprimer {profileName} efface sa clé API enregistrée. Les fils de discussion qui utilisent ce profil ne trouveront plus leur agent." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Supprimer {profileName} abandonne ses réglages Poracode — variables d'environnement, modèles et effort. Son répertoire de configuration et les identifiants Claude qu'il contient restent sur le disque." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10200,6 +10340,14 @@ msgstr "Enregistrer le profil de Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Enregistrer l’environnement d’exécution de l’interface Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Enregistrer la clé API du profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Enregistrer le nom du profil Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Enregistrer la clé API du SDK Cursor" @@ -10610,6 +10758,11 @@ msgstr "Envoyer un message" msgid "Sent selection to terminal." msgstr "Sélection envoyée au terminal." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Clé API distincte" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Séparez les comptes Claude Code par dossier de configuration, ou dirigez un profil vers un fournisseur externe (z.ai, …). Ouvrez un profil pour configurer ses variables d'environnement, ses modèles et son effort." @@ -10851,6 +11004,10 @@ msgstr "Affichage de {0, plural, one {# compétence} other {# compétences}}" msgid "Showing the first 4,000 entries." msgstr "Affichage des 4 000 premières entrées." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Affiché dans la barre latérale et dans le sélecteur de modèles." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Affichée à côté du nom du projet." @@ -11796,6 +11953,10 @@ msgstr "l'agent" msgid "The agent" msgstr "L'agent" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "La clé API ci-dessous est utilisée par Cursor CLI, ACP et le SDK pour ce profil." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "l'application" @@ -12049,6 +12210,10 @@ msgstr "Ce dossier est vide." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Cette page HTTPS n'a pas pu joindre l'ordinateur en HTTP simple. Autorisez l'accès au réseau local pour ce site puis réessayez l'association, ou continuez sur l'adresse de l'ordinateur." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Cette clé est utilisée par Cursor CLI, ACP et SDK pour ce profil." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Ce lien ouvre l'application que Poracode sert sur votre réseau. L'application hébergée est servie en HTTPS, ce qui empêche les navigateurs de joindre un ordinateur en HTTP simple." diff --git a/src/renderer/locales/ja/messages.po b/src/renderer/locales/ja/messages.po index 8b3a3a598..af9de70b8 100644 --- a/src/renderer/locales/ja/messages.po +++ b/src/renderer/locales/ja/messages.po @@ -709,10 +709,18 @@ msgstr "7日" msgid "A candidate is no longer on its experiment branch." msgstr "候補が実験ブランチ上にありません。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "この名前のCursorプロファイルはすでに存在します。" + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "固定されたワークツリーのソースには完全なコミットハッシュが必要です" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "この名前のプロファイルはすでに存在します。" + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "サーバーを起動できなかったためスキップしました。" @@ -931,8 +939,8 @@ msgid "Add candidate" msgstr "候補を追加" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Claude プロファイルを追加" +#~ msgid "Add Claude profile" +#~ msgstr "Claude プロファイルを追加" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -942,6 +950,10 @@ msgstr "コンテキストウィンドウを追加" msgid "Add credentials before this thread can run." msgstr "このスレッドを実行する前に認証情報を追加してください。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Cursorプロファイルを追加" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "フォルダーを追加" @@ -966,7 +978,7 @@ msgstr "手動で追加するか、別のエージェントからインポート msgid "Add pattern" msgstr "パターンを追加する" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "プロフィールを追加" @@ -2049,8 +2061,16 @@ msgid "Cancel" msgstr "キャンセル" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "新しい Claude プロファイルをキャンセル" +#~ msgid "Cancel new Claude profile" +#~ msgstr "新しい Claude プロファイルをキャンセル" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "新しいCursorプロファイルをキャンセル" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "新しいプロファイルをキャンセル" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2390,8 +2410,8 @@ msgid "Claude {0} profile saved." msgstr "Claude {0} プロファイルが保存されました。" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Claude {displayName} プロファイルを追加しました。" +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Claude {displayName} プロファイルを追加しました。" #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2406,8 +2426,8 @@ msgid "Claude profile name" msgstr "Claude プロファイル名" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude プロファイルが削除されました。" +#~ msgid "Claude profile removed." +#~ msgstr "Claude プロファイルが削除されました。" #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3475,6 +3495,10 @@ msgstr "PR アクションの作成" msgid "Create PR…" msgstr "PR を作成…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "プロファイルを作成" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "プロジェクトの作成" @@ -3492,6 +3516,10 @@ msgstr "スケジュールを作成" msgid "Create scheduled task" msgstr "スケジュールタスクを作成" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "それぞれ独自のAPIキーを持つCursorプロファイルを作成します。" + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "タスクの作成" @@ -3637,6 +3665,15 @@ msgstr "現在の連続日数" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Cursorプロファイル「{displayName}」を追加しました。" + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3649,6 +3686,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Cursor ACP からログアウトしました。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3657,6 +3695,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor GUI ランタイムを更新しました。" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "CursorプロファイルのAPIキー" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "CursorプロファイルのAPIキーを保存しました。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Cursorプロファイル名" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Cursorプロファイルを削除しましたが、ステータスを更新できませんでした。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Cursorプロファイルを削除しました。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Cursorプロファイルの名前を変更しました。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4325,7 +4388,7 @@ msgstr "例: 512k または 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "例:クイックな調査には Codex GPT-5.5 fast、大量のリファクタリングには OpenCode GLM、繊細な作業には Claude Opus。" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "例: 仕事" @@ -7138,6 +7201,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "名前" @@ -7220,13 +7284,21 @@ msgid "New Claude profile config directory" msgstr "新しい Claude プロファイル設定ディレクトリ" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "新しい Claude プロファイル名" +#~ msgid "New Claude profile name" +#~ msgstr "新しい Claude プロファイル名" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "新しいコード" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "新しいCursorプロファイルのAPIキー" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "新しいCursorプロファイル名" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "新しい実験" @@ -7273,6 +7345,10 @@ msgstr "新しいモデル" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "新しい電話、タブレット、ブラウザーはペアリング後にここに表示されます。" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "新しいプロファイル名" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "新しいプロジェクト" @@ -7405,8 +7481,16 @@ msgid "No activity yet." msgstr "まだアクティビティがありません。" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "追加の Claude プロファイルはありません。" +#~ msgid "No additional Claude profiles." +#~ msgstr "追加の Claude プロファイルはありません。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "追加のCursorプロファイルはありません。" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "追加のプロファイルはありません。" #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7990,7 +8074,7 @@ msgstr "開く" msgid "Open {0}" msgstr "{0} を開く" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "{label}を開く" @@ -8475,6 +8559,7 @@ msgstr "{label} APIキーを貼り付け" msgid "Paste <0>{0} here. Click to copy." msgstr "<0>{0} をここに貼り付けます。クリックしてコピーします。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Cursor API キーを貼り付け" @@ -8580,6 +8665,10 @@ msgstr "アイコンを選択します" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "新しい Cursor GUI チャットで使用するランタイムを選択します。各ランタイムは個別にインストールおよび認証されます。開いているチャットは開始時のランタイムを維持します。" +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "新しいCursor GUIチャットで使用するランタイムを選択します。プロファイルのAPIキーは両方のランタイムに適用されます。" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "ピッカーが失敗しました" @@ -8910,15 +8999,32 @@ msgid "Productivity" msgstr "生産性" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "プロフィール" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "プロファイル {displayName} を追加しました。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "プロファイルのAPIキー" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "プロファイルを削除しましたが、ステータスを更新できませんでした。" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "プロファイルを削除しました。" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "プロフィール統計の期間" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "プロファイル" @@ -9351,7 +9457,7 @@ msgstr "通知内容を伏せる" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9510,6 +9616,7 @@ msgid "Remote URL" msgstr "リモートURL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "削除する" @@ -9555,13 +9662,25 @@ msgid "Remove candidate {0}" msgstr "候補 {0} を削除" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Claude プロファイルを削除" +#~ msgid "Remove Claude profile" +#~ msgstr "Claude プロファイルを削除" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "接続を削除" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Cursorプロファイルを削除" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Cursorプロファイル「{label}」を削除" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Cursorプロファイルを削除しますか?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Cursor SDK API キーを削除" @@ -9595,6 +9714,14 @@ msgstr "モデルを削除" msgid "Remove pinned route for {tagLabel}" msgstr "{tagLabel} の固定ルートを削除" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "プロファイル {label} を削除" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "プロファイルを削除しますか?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "プロジェクトを削除" @@ -9624,6 +9751,19 @@ msgstr "プロジェクトを削除する前に、そのプロジェクトの実 msgid "Remove worktree?" msgstr "ワークツリーを削除しますか?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "{0} を削除すると、保存されたAPIキーも削除されます。このプロファイルを使用しているスレッドはエージェントを見つけられなくなります。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "{profileName} を削除すると、保存されたAPIキーも削除されます。このプロファイルを使用しているスレッドはエージェントを見つけられなくなります。" + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "{profileName} を削除すると、Poracodeの設定(環境変数、モデル、Effort)が失われます。設定ディレクトリとその中のClaude認証情報はディスクに残ります。" + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10199,6 +10339,14 @@ msgstr "Claude プロフィールを保存" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Cursor GUI ランタイムを保存" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "CursorプロファイルのAPIキーを保存" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Cursorプロファイル名を保存" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Cursor SDK API キーを保存" @@ -10609,6 +10757,11 @@ msgstr "メッセージを送信する" msgid "Sent selection to terminal." msgstr "選択内容を端末に送信しました。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "個別のAPIキー" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Claude Codeのアカウントを設定ディレクトリごとに分離するか、プロファイルを外部プロバイダー(z.aiなど)に向けます。プロファイルを開くと、その環境変数・モデル・推論レベルを設定できます。" @@ -10850,6 +11003,10 @@ msgstr "{0, plural, other {# 件のスキル}}を表示中" msgid "Showing the first 4,000 entries." msgstr "最初の4,000件を表示しています。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "サイドバーとモデルセレクターに表示されます。" + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "プロジェクト名の横に表示されます。" @@ -11795,6 +11952,10 @@ msgstr "エージェント" msgid "The agent" msgstr "エージェント" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "以下のAPIキーは、このプロファイルのCursor CLI、ACP、SDKで使用されます。" + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "アプリ" @@ -12048,6 +12209,10 @@ msgstr "このフォルダーは空です。" msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "この HTTPS ページは平文 HTTP のデスクトップに接続できませんでした。このサイトにローカルネットワークへのアクセスを許可して再度ペアリングするか、デスクトップ自身のアドレスで続けてください。" +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "このキーは、このプロファイルのCursor CLI、ACP、SDKで使用されます。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "このリンクは Poracode がネットワーク上で提供しているアプリを開きます。ホスト版のアプリは HTTPS で配信されるため、ブラウザは平文 HTTP のデスクトップに接続できません。" diff --git a/src/renderer/locales/ko/messages.po b/src/renderer/locales/ko/messages.po index fddab9cab..13afc2e6b 100644 --- a/src/renderer/locales/ko/messages.po +++ b/src/renderer/locales/ko/messages.po @@ -710,10 +710,18 @@ msgstr "7일" msgid "A candidate is no longer on its experiment branch." msgstr "후보가 더 이상 실험 브랜치에 있지 않습니다." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "같은 이름의 Cursor 프로필이 이미 있습니다." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "고정된 작업 트리 소스에는 전체 커밋 해시가 필요합니다" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "같은 이름의 프로필이 이미 있습니다." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "서버를 시작할 수 없어 건너뛰었습니다." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "후보 추가" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Claude 프로필 추가" +#~ msgid "Add Claude profile" +#~ msgstr "Claude 프로필 추가" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "컨텍스트 창 추가" msgid "Add credentials before this thread can run." msgstr "이 스레드를 실행하려면 먼저 자격 증명을 추가하세요." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Cursor 프로필 추가" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "폴더 추가" @@ -967,7 +979,7 @@ msgstr "직접 추가하거나 다른 에이전트에서 가져오세요." msgid "Add pattern" msgstr "패턴 추가" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "프로필 추가" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "취소" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "새 Claude 프로필 취소" +#~ msgid "Cancel new Claude profile" +#~ msgstr "새 Claude 프로필 취소" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "새 Cursor 프로필 취소" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "새 프로필 취소" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Claude {0} 프로필이 저장되었습니다." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Claude {displayName} 프로필이 추가되었습니다." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Claude {displayName} 프로필이 추가되었습니다." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Claude 프로필 이름" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude 프로필이 삭제되었습니다." +#~ msgid "Claude profile removed." +#~ msgstr "Claude 프로필이 삭제되었습니다." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "PR 액션 만들기" msgid "Create PR…" msgstr "PR 만들기…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "프로필 만들기" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "프로젝트 생성" @@ -3493,6 +3517,10 @@ msgstr "일정 만들기" msgid "Create scheduled task" msgstr "예약 작업 만들기" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "각각 고유한 API 키를 사용하는 별도의 Cursor 프로필을 만드세요." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "작업 만들기" @@ -3638,6 +3666,15 @@ msgstr "현재 연속 기록" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Cursor {displayName} 프로필을 추가했습니다." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Cursor ACP에서 로그아웃했습니다." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor GUI 런타임을 업데이트했습니다." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Cursor 프로필 API 키" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Cursor 프로필 API 키를 저장했습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Cursor 프로필 이름" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Cursor 프로필을 제거했지만 상태를 새로 고칠 수 없습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Cursor 프로필을 제거했습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Cursor 프로필 이름을 변경했습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "예: 512k 또는 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "예: 빠른 조회에는 Codex GPT-5.5 fast, 대규모 리팩터링에는 OpenCode GLM, 미묘한 작업에는 Claude Opus." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "예: 업무" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "이름" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "새로운 Claude 프로필 구성 디렉터리" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "새로운 Claude 프로필 이름" +#~ msgid "New Claude profile name" +#~ msgstr "새로운 Claude 프로필 이름" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "새 코드" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "새 Cursor 프로필 API 키" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "새 Cursor 프로필 이름" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "새 실험" @@ -7275,6 +7347,10 @@ msgstr "새 모델" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "새 휴대폰, 태블릿, 브라우저는 페어링 후 여기에 표시됩니다." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "새 프로필 이름" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "새 프로젝트" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "아직 활동이 없습니다." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "추가 Claude 프로필이 없습니다." +#~ msgid "No additional Claude profiles." +#~ msgstr "추가 Claude 프로필이 없습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "추가 Cursor 프로필이 없습니다." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "추가 프로필이 없습니다." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "열기" msgid "Open {0}" msgstr "{0} 열기" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "{label} 열기" @@ -8477,6 +8561,7 @@ msgstr "{label} API 키 붙여넣기" msgid "Paste <0>{0} here. Click to copy." msgstr "여기에 <0>{0}을 붙여넣으세요. 복사하려면 클릭하세요." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Cursor API 키 붙여넣기" @@ -8582,6 +8667,10 @@ msgstr "아이콘 선택" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "새 Cursor GUI 채팅에 사용할 런타임을 선택하세요. 각 런타임은 개별적으로 설치되고 인증됩니다. 열려 있는 채팅은 시작할 때의 런타임을 유지합니다." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "새 Cursor GUI 채팅을 지원할 런타임을 선택하세요. 프로필 API 키는 두 런타임 모두에 적용됩니다." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "선택기 실패" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "생산성" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "프로필" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "프로필 {displayName}을(를) 추가했습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "프로필 API 키" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "프로필을 제거했지만 상태를 새로 고칠 수 없었습니다." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "프로필을 제거했습니다." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "프로필 통계 기간" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "프로필" @@ -9353,7 +9459,7 @@ msgstr "알림 내용 가리기" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "원격 URL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "제거" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "후보 {0} 제거" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Claude 프로필 삭제" +#~ msgid "Remove Claude profile" +#~ msgstr "Claude 프로필 삭제" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "연결 제거" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Cursor 프로필 제거" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Cursor {label} 프로필 제거" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Cursor 프로필을 제거할까요?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Cursor SDK API 키 제거" @@ -9597,6 +9716,14 @@ msgstr "모델 제거" msgid "Remove pinned route for {tagLabel}" msgstr "{tagLabel}의 고정 경로 제거" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "프로필 {label} 제거" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "프로필을 제거할까요?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "프로젝트 제거" @@ -9626,6 +9753,19 @@ msgstr "프로젝트를 제거하기 전에 프로젝트의 실험을 제거하 msgid "Remove worktree?" msgstr "작업 트리를 삭제하시겠습니까?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "{0}을(를) 제거하면 저장된 API 키도 삭제됩니다. 이 프로필을 사용하는 스레드는 더 이상 에이전트를 찾을 수 없습니다." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "{profileName}을(를) 제거하면 저장된 API 키도 삭제됩니다. 이 프로필을 사용하는 스레드는 더 이상 에이전트를 찾을 수 없습니다." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "{profileName}을(를) 제거하면 환경 변수, 모델, Effort 등 Poracode 설정이 사라집니다. 구성 디렉터리와 그 안의 Claude 자격 증명은 디스크에 남습니다." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Claude 프로필 저장" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Cursor GUI 런타임 저장" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Cursor 프로필 API 키 저장" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Cursor 프로필 이름 저장" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Cursor SDK API 키 저장" @@ -10611,6 +10759,11 @@ msgstr "메시지 보내기" msgid "Sent selection to terminal." msgstr "선택 항목을 터미널로 보냈습니다." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "별도의 API 키" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Claude Code 계정을 설정 디렉터리별로 분리하거나, 프로필을 외부 제공자(z.ai 등)로 지정합니다. 프로필을 열어 환경 변수, 모델, 추론 강도를 구성하세요." @@ -10852,6 +11005,10 @@ msgstr "{0, plural, other {#개 스킬 표시 중}}" msgid "Showing the first 4,000 entries." msgstr "처음 4,000개 항목을 표시합니다." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "사이드바와 모델 선택기에 표시됩니다." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "프로젝트 이름 옆에 표시됩니다." @@ -11797,6 +11954,10 @@ msgstr "에이전트" msgid "The agent" msgstr "에이전트" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "아래 API 키는 이 프로필의 Cursor CLI, ACP, SDK에서 사용됩니다." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "앱" @@ -12050,6 +12211,10 @@ msgstr "이 폴더는 비어 있습니다." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "이 HTTPS 페이지는 일반 HTTP 데스크톱에 연결할 수 없었습니다. 이 사이트에 로컬 네트워크 접근을 허용한 뒤 다시 페어링하거나, 데스크톱 자체 주소에서 계속하세요." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "이 키는 이 프로필의 Cursor CLI, ACP 및 SDK에서 사용됩니다." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "이 링크는 Poracode가 네트워크에서 제공하는 앱을 엽니다. 호스팅된 앱은 HTTPS로 제공되므로 브라우저가 일반 HTTP 데스크톱에 연결하는 것을 차단합니다." diff --git a/src/renderer/locales/pl/messages.po b/src/renderer/locales/pl/messages.po index 71665cebc..56895aee8 100644 --- a/src/renderer/locales/pl/messages.po +++ b/src/renderer/locales/pl/messages.po @@ -710,10 +710,18 @@ msgstr "7d" msgid "A candidate is no longer on its experiment branch." msgstr "Kandydat nie znajduje się już na swojej gałęzi eksperymentu." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Profil Cursor o tej nazwie już istnieje." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Zamrożone źródło drzewa roboczego wymaga pełnego skrótu commita" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Profil o tej nazwie już istnieje." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Nie udało się uruchomić serwera, więc został pominięty." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Dodaj kandydata" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Dodaj profil Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Dodaj profil Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Dodaj okno kontekstu" msgid "Add credentials before this thread can run." msgstr "Dodaj poświadczenia, zanim będzie można uruchomić ten wątek." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Dodaj profil Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Dodaj folder" @@ -967,7 +979,7 @@ msgstr "Dodaj ręcznie lub zaimportuj z innego agenta." msgid "Add pattern" msgstr "Dodaj wzór" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Dodaj profil" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Anuluj" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Anuluj nowy profil Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Anuluj nowy profil Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Anuluj nowy profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Anuluj nowy profil" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Profil Claude'a {0} został zapisany." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Dodano profil Claude {displayName}." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Dodano profil Claude {displayName}." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Nazwa profilu Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Usunięto profil Claude'a." +#~ msgid "Claude profile removed." +#~ msgstr "Usunięto profil Claude'a." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Stwórz akcję PR" msgid "Create PR…" msgstr "Stwórz PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Utwórz profil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Utwórz projekt" @@ -3493,6 +3517,10 @@ msgstr "Utwórz harmonogram" msgid "Create scheduled task" msgstr "Utwórz zaplanowane zadanie" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Utwórz osobne profile Cursor z własnymi kluczami API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Utwórz zadanie" @@ -3638,6 +3666,15 @@ msgstr "Obecna seria" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Dodano profil Cursor {displayName}." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Wylogowano z Cursor ACP." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Zaktualizowano środowisko GUI Cursor." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Klucz API profilu Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Zapisano klucz API profilu Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Nazwa profilu Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Usunięto profil Cursor, ale nie udało się odświeżyć stanów." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Usunięto profil Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Zmieniono nazwę profilu Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "np. 512k lub 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "np. Codex GPT-5.5 fast do szybkich wyszukiwań, OpenCode GLM do masowych refaktoryzacji, Claude Opus do wszystkiego, co subtelne." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "np. Praca" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Nazwa" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Nowy katalog konfiguracyjny profilu Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Nowa nazwa profilu Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Nowa nazwa profilu Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Nowy kod" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Klucz API nowego profilu Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Nazwa nowego profilu Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Nowy eksperyment" @@ -7275,6 +7347,10 @@ msgstr "nowy model" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Nowe telefony, tablety i przeglądarki pojawią się tutaj po sparowaniu." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Nazwa nowego profilu" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Nowy projekt" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Brak aktywności." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Żadnych dodatkowych profili Claude." +#~ msgid "No additional Claude profiles." +#~ msgstr "Żadnych dodatkowych profili Claude." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Brak dodatkowych profili Cursor." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Brak dodatkowych profili." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Otwórz" msgid "Open {0}" msgstr "Otwórz {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Otwórz {label}" @@ -8477,6 +8561,7 @@ msgstr "Wklej klucz API {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Wklej tutaj <0>{0}. Kliknij, aby skopiować." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Wklej klucz API Cursor" @@ -8582,6 +8667,10 @@ msgstr "Wybierz ikonę" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Wybierz czas wykonania dla nowych czatów GUI Cursor. Każdy instaluje się i uwierzytelnia oddzielnie; otwarte czaty zachowują ten, z którym zostały uruchomione." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Wybierz środowisko dla nowych czatów GUI Cursor. Klucz API profilu dotyczy obu środowisk." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Selektor nie powiódł się" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Produktywność" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Profil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Dodano profil {displayName}." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Klucz API profilu" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Usunięto profil, ale nie udało się odświeżyć statusów." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Usunięto profil." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Zakres statystyk profilu" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Profile" @@ -9353,7 +9459,7 @@ msgstr "Ukryj treść powiadomień" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "Zdalny adres URL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Usuń" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Usuń kandydata {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Usuń profil Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Usuń profil Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Usuń połączenie" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Usuń profil Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Usuń profil Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Usunąć profil Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Usuń klucz API SDK Cursor" @@ -9597,6 +9716,14 @@ msgstr "Usuń model" msgid "Remove pinned route for {tagLabel}" msgstr "Usuń przypiętą trasę dla {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Usuń profil {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Usunąć profil?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Usuń projekt" @@ -9626,6 +9753,19 @@ msgstr "Usuń eksperymenty projektu przed usunięciem projektu." msgid "Remove worktree?" msgstr "Usunąć drzewo robocze?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Usunięcie {0} spowoduje skasowanie zapisanego klucza API. Wątki korzystające z tego profilu nie znajdą już swojego agenta." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Usunięcie {profileName} spowoduje skasowanie zapisanego klucza API. Wątki korzystające z tego profilu nie znajdą już swojego agenta." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Usunięcie {profileName} odrzuca jego ustawienia Poracode — zmienne środowiskowe, modele i effort. Katalog konfiguracji i zapisane w nim dane uwierzytelniające Claude pozostaną na dysku." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Zapisz profil Claude'a" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Zapisz środowisko GUI Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Zapisz klucz API profilu Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Zapisz nazwę profilu Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Zapisz klucz API SDK Cursor" @@ -10611,6 +10759,11 @@ msgstr "Wyślij wiadomość" msgid "Sent selection to terminal." msgstr "Wysłano zaznaczenie do terminala." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Osobny klucz API" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Oddziel konta Claude Code według katalogu konfiguracji lub skieruj profil na zewnętrznego dostawcę (z.ai, …). Otwórz profil, aby skonfigurować jego zmienne środowiskowe, modele i poziom rozumowania." @@ -10852,6 +11005,10 @@ msgstr "Wyświetlanie {0, plural, one {# umiejętności} few {# umiejętności} msgid "Showing the first 4,000 entries." msgstr "Wyświetlanie pierwszych 4000 wpisów." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Widoczna na pasku bocznym i w selektorze modeli." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Wyświetlana obok nazwy projektu." @@ -11797,6 +11954,10 @@ msgstr "agent" msgid "The agent" msgstr "Agent" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Poniższy klucz API jest używany przez Cursor CLI, ACP i SDK dla tego profilu." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "aplikacja" @@ -12050,6 +12211,10 @@ msgstr "Ten folder jest pusty." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Ta strona HTTPS nie mogła połączyć się z desktopem po zwykłym HTTP. Zezwól tej witrynie na dostęp do sieci lokalnej i sparuj ponownie albo kontynuuj na własnym adresie desktopa." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Ten klucz jest używany przez Cursor CLI, ACP i SDK dla tego profilu." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Ten link otwiera aplikację, którą Poracode udostępnia w twojej sieci. Hostowana aplikacja jest serwowana przez HTTPS, co blokuje przeglądarkom dostęp do desktopa po zwykłym HTTP." diff --git a/src/renderer/locales/pt-BR/messages.po b/src/renderer/locales/pt-BR/messages.po index 6e22ed178..bb79f2f3d 100644 --- a/src/renderer/locales/pt-BR/messages.po +++ b/src/renderer/locales/pt-BR/messages.po @@ -710,10 +710,18 @@ msgstr "7d" msgid "A candidate is no longer on its experiment branch." msgstr "Um candidato não está mais na ramificação do experimento." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Já existe um perfil do Cursor com este nome." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Uma origem congelada da árvore de trabalho requer um hash completo do commit" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Já existe um perfil com este nome." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Não foi possível iniciar um servidor, que foi ignorado." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Adicionar candidato" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Adicionar perfil do Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Adicionar perfil do Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Adicionar janela de contexto" msgid "Add credentials before this thread can run." msgstr "Adicione credenciais antes que este tópico possa ser executado." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Adicionar perfil do Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Adicionar pasta" @@ -967,7 +979,7 @@ msgstr "Adicione um manualmente ou importe de outro agente." msgid "Add pattern" msgstr "Adicionar padrão" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Adicionar perfil" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Cancelar" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Cancelar novo perfil de Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Cancelar novo perfil de Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Cancelar novo perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Cancelar o novo perfil" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Perfil do Claude {0} salvo." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Perfil do Claude {displayName} adicionado." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Perfil do Claude {displayName} adicionado." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Nome do perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Perfil de Claude removido." +#~ msgid "Claude profile removed." +#~ msgstr "Perfil de Claude removido." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Ação de criar PR" msgid "Create PR…" msgstr "Criar PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Criar perfil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Criar projeto" @@ -3493,6 +3517,10 @@ msgstr "Criar agendamento" msgid "Create scheduled task" msgstr "Criar tarefa agendada" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Crie perfis separados do Cursor com suas próprias chaves de API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Criar tarefa" @@ -3638,6 +3666,15 @@ msgstr "Sequência atual" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Perfil {displayName} do Cursor adicionado." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Sessão do Cursor ACP encerrada." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Runtime da interface do Cursor atualizado." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Chave de API do perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Chave de API do perfil do Cursor salva." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Nome do perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "O perfil do Cursor foi removido, mas não foi possível atualizar os status." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Perfil do Cursor removido." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Perfil do Cursor renomeado." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "ex.: 512k ou 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "ex.: Codex GPT-5.5 fast para consultas rápidas, OpenCode GLM para refatorações em massa, Claude Opus para qualquer coisa sutil." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "por exemplo, Trabalho" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Nome" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Novo diretório de configuração do perfil Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Novo nome de perfil de Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Novo nome de perfil de Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Novo código" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Chave de API do novo perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Nome do novo perfil do Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Novo experimento" @@ -7275,6 +7347,10 @@ msgstr "novo modelo" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Novos celulares, tablets e navegadores aparecerão aqui após o pareamento." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Nome do novo perfil" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Novo projeto" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Nenhuma atividade ainda." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Nenhum perfil adicional de Claude." +#~ msgid "No additional Claude profiles." +#~ msgstr "Nenhum perfil adicional de Claude." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Nenhum perfil adicional do Cursor." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Nenhum perfil adicional." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Abrir" msgid "Open {0}" msgstr "Abrir {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Abrir {label}" @@ -8477,6 +8561,7 @@ msgstr "Cole a chave de API do {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Cole <0>{0} aqui. Clique para copiar." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Cole a chave de API do Cursor" @@ -8582,6 +8667,10 @@ msgstr "Escolha o ícone" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Escolha o tempo de execução usado pelos novos chats de GUI do Cursor. Cada um é instalado e autenticado separadamente; chats abertos mantêm aquele com que foram iniciados." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Escolha o runtime para novos chats da interface do Cursor. A chave de API do perfil se aplica aos dois runtimes." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Falha no seletor" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Produtividade" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Perfil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Perfil {displayName} adicionado." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Chave de API do perfil" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Perfil removido, mas não foi possível atualizar os status." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Perfil removido." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Período das estatísticas do perfil" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Perfis" @@ -9353,7 +9459,7 @@ msgstr "Ocultar o conteúdo das notificações" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "URL remota" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Remover" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Remover candidato {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Remover perfil do Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Remover perfil do Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Remover conexão" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Remover perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Remover perfil {label} do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Remover o perfil do Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Remover chave de API do SDK do Cursor" @@ -9597,6 +9716,14 @@ msgstr "Remover modelo" msgid "Remove pinned route for {tagLabel}" msgstr "Remover a rota fixada para {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Remover o perfil {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Remover o perfil?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Remover projeto" @@ -9626,6 +9753,19 @@ msgstr "Remova os experimentos do projeto antes de removê-lo." msgid "Remove worktree?" msgstr "Remover árvore de trabalho?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Remover {0} exclui a chave de API salva. Os tópicos que usam este perfil não encontrarão mais seu agente." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Remover {profileName} exclui a chave de API salva. Os tópicos que usam este perfil não encontrarão mais seu agente." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Remover {profileName} descarta suas configurações do Poracode — variáveis de ambiente, modelos e esforço. O diretório de configuração e as credenciais do Claude nele permanecem no disco." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Salvar perfil do Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Salvar runtime da interface do Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Salvar chave de API do perfil do Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Salvar o nome do perfil do Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Salvar chave de API do SDK do Cursor" @@ -10611,6 +10759,11 @@ msgstr "Enviar mensagem" msgid "Sent selection to terminal." msgstr "Seleção enviada para o terminal." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Chave de API separada" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Separe contas do Claude Code por diretório de configuração ou aponte um perfil para um provedor externo (z.ai, …). Abra um perfil para configurar suas variáveis de ambiente, modelos e esforço." @@ -10852,6 +11005,10 @@ msgstr "Exibindo {0, plural, one {# skill} other {# skills}}" msgid "Showing the first 4,000 entries." msgstr "Exibindo as primeiras 4.000 entradas." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Exibido na barra lateral e no seletor de modelos." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Mostrado ao lado do nome do projeto." @@ -11797,6 +11954,10 @@ msgstr "o agente" msgid "The agent" msgstr "o agente" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "A chave de API abaixo é usada pelo Cursor CLI, ACP e SDK para este perfil." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "o aplicativo" @@ -12050,6 +12211,10 @@ msgstr "Esta pasta está vazia." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Esta página HTTPS não conseguiu alcançar o desktop em HTTP simples. Permita o acesso à rede local para este site e pareie novamente, ou continue no endereço do próprio desktop." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Esta chave é usada pelo Cursor CLI, ACP e SDK para este perfil." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Este link abre o app que o Poracode serve na sua rede. O app hospedado é servido por HTTPS, o que impede os navegadores de alcançar um desktop em HTTP simples." diff --git a/src/renderer/locales/ru/messages.po b/src/renderer/locales/ru/messages.po index 72b6e226b..fd6a94e07 100644 --- a/src/renderer/locales/ru/messages.po +++ b/src/renderer/locales/ru/messages.po @@ -710,10 +710,18 @@ msgstr "7 дн" msgid "A candidate is no longer on its experiment branch." msgstr "Один из кандидатов больше не находится в ветке эксперимента." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Профиль Cursor с таким именем уже существует." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Для замороженного источника worktree требуется полный хеш коммита" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Профиль с таким именем уже существует." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Не удалось запустить сервер, он пропущен." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Добавить кандидата" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Добавить профиль Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Добавить профиль Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Добавить контекстное окно" msgid "Add credentials before this thread can run." msgstr "Добавьте учётные данные, прежде чем этот тред сможет запуститься." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Добавить профиль Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Добавить папку" @@ -967,7 +979,7 @@ msgstr "Добавьте сервер вручную или импортируй msgid "Add pattern" msgstr "Добавить шаблон" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Добавить профиль" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Отмена" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Отменить новый профиль Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Отменить новый профиль Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Отменить создание профиля Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Отменить новый профиль" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Профиль Claude {0} сохранён." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Профиль Claude {displayName} добавлен." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Профиль Claude {displayName} добавлен." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Имя профиля Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Профиль Claude удалён." +#~ msgid "Claude profile removed." +#~ msgstr "Профиль Claude удалён." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Действие создания PR" msgid "Create PR…" msgstr "Создать PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Создать профиль" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Создать проект" @@ -3493,6 +3517,10 @@ msgstr "Создать расписание" msgid "Create scheduled task" msgstr "Создать запланированную задачу" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Создавайте отдельные профили Cursor со своими ключами API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Создать задачу" @@ -3638,6 +3666,15 @@ msgstr "Текущая серия" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Профиль Cursor {displayName} добавлен." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Выполнен выход из Cursor ACP." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Среда выполнения GUI Cursor обновлена." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Ключ API профиля Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Ключ API профиля Cursor сохранён." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Имя профиля Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Профиль Cursor удалён, но не удалось обновить статусы." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Профиль Cursor удалён." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Профиль Cursor переименован." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "напр. 512k или 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "напр., Codex GPT-5.5 fast для быстрых запросов, OpenCode GLM для массовых рефакторингов, Claude Opus для всего тонкого." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "напр. Работа" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Имя" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Новый каталог конфигурации профиля Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Новое имя профиля Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Новое имя профиля Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Новый код" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Ключ API нового профиля Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Имя нового профиля Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Новый эксперимент" @@ -7275,6 +7347,10 @@ msgstr "новая модель" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Новые телефоны, планшеты и браузеры появятся здесь после сопряжения." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Имя нового профиля" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Новый проект" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Активности пока нет." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Дополнительных профилей Claude нет." +#~ msgid "No additional Claude profiles." +#~ msgstr "Дополнительных профилей Claude нет." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Дополнительных профилей Cursor нет." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Дополнительных профилей нет." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Открыть" msgid "Open {0}" msgstr "Открыть {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Открыть {label}" @@ -8477,6 +8561,7 @@ msgstr "Вставьте API-ключ {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Вставьте <0>{0} сюда. Нажмите, чтобы скопировать." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Вставьте API-ключ Cursor" @@ -8582,6 +8667,10 @@ msgstr "Выбрать значок" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Выберите среду выполнения для новых GUI-чатов Cursor. Каждая среда выполнения устанавливается и аутентифицируется отдельно; открытые чаты сохраняют ту, с которой были запущены." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Выберите среду для новых GUI-чатов Cursor. Ключ API профиля применяется к обеим средам." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Сбой выбора" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Продуктивность" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Профиль" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Профиль {displayName} добавлен." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Ключ API профиля" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Профиль удалён, но обновить статусы не удалось." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Профиль удалён." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Диапазон статистики профиля" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Профили" @@ -9353,7 +9459,7 @@ msgstr "Скрывать содержимое уведомлений" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "URL удалённого репозитория" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Удалить" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Удалить кандидата {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Удалить профиль Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Удалить профиль Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Удалить подключение" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Удалить профиль Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Удалить профиль Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Удалить профиль Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Удалить API-ключ Cursor SDK" @@ -9597,6 +9716,14 @@ msgstr "Удалить модель" msgid "Remove pinned route for {tagLabel}" msgstr "Удалить закреплённый маршрут для {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Удалить профиль {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Удалить профиль?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Удалить проект" @@ -9626,6 +9753,19 @@ msgstr "Удалите эксперименты проекта перед уда msgid "Remove worktree?" msgstr "Удалить worktree?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "При удалении {0} сохранённый API-ключ будет удалён. Треды, использующие этот профиль, больше не найдут своего агента." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "При удалении {profileName} сохранённый API-ключ будет удалён. Треды, использующие этот профиль, больше не найдут своего агента." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "При удалении {profileName} пропадут его настройки Poracode — переменные окружения, модели и effort. Каталог конфигурации и учётные данные Claude в нём останутся на диске." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Сохранить профиль Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Сохранить среду выполнения GUI Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Сохранить ключ API профиля Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Сохранить имя профиля Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Сохранить API-ключ Cursor SDK" @@ -10611,6 +10759,11 @@ msgstr "Отправить сообщение" msgid "Sent selection to terminal." msgstr "Выбор отправлен в терминал." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Отдельный ключ API" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Разделяйте аккаунты Claude Code по каталогу конфигурации или направляйте профиль на внешнего провайдера (z.ai, …). Откройте профиль, чтобы настроить его переменные окружения, модели и уровни рассуждений." @@ -10852,6 +11005,10 @@ msgstr "Показано {0, plural, one {# навык} few {# навыка} man msgid "Showing the first 4,000 entries." msgstr "Показаны первые 4 000 записей." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Отображается на боковой панели и в селекторе моделей." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Отображается рядом с названием проекта." @@ -11797,6 +11954,10 @@ msgstr "агент" msgid "The agent" msgstr "Агент" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Указанный ниже API-ключ используется в Cursor CLI, ACP и SDK для этого профиля." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "приложение" @@ -12050,6 +12211,10 @@ msgstr "Эта папка пуста." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Эта HTTPS-страница не смогла обратиться к десктопу по обычному HTTP. Разрешите этому сайту доступ к локальной сети и повторите привязку либо продолжите по собственному адресу десктопа." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Этот ключ используется Cursor CLI, ACP и SDK для данного профиля." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Эта ссылка открывает приложение, которое Poracode раздаёт в вашей сети. Размещённое приложение работает по HTTPS, поэтому браузеры блокируют обращение к десктопу по обычному HTTP." diff --git a/src/renderer/locales/tr/messages.po b/src/renderer/locales/tr/messages.po index c0fd5f8a2..022ed62f4 100644 --- a/src/renderer/locales/tr/messages.po +++ b/src/renderer/locales/tr/messages.po @@ -710,10 +710,18 @@ msgstr "7g" msgid "A candidate is no longer on its experiment branch." msgstr "Bir aday artık deney şubesinde değil." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Bu ada sahip bir Cursor profili zaten var." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Dondurulmuş bir çalışma ağacı kaynağı tam commit karması gerektirir" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Bu ada sahip bir profil zaten var." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Bir sunucu başlatılamadı ve atlandı." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Aday ekle" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Claude profili ekle" +#~ msgid "Add Claude profile" +#~ msgstr "Claude profili ekle" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Bağlam penceresi ekle" msgid "Add credentials before this thread can run." msgstr "Bu ileti dizisi çalışabilmesi için kimlik bilgilerini ekleyin." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Cursor profili ekle" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Klasör ekle" @@ -967,7 +979,7 @@ msgstr "Birini elle ekleyin veya başka bir ajandan içe aktarın." msgid "Add pattern" msgstr "Desen ekle" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Profil ekle" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "İptal" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Yeni Claude profilini iptal et" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Yeni Claude profilini iptal et" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Yeni Cursor profilini iptal et" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Yeni profili iptal et" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Claude {0} profili kaydedildi." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Claude {displayName} profili eklendi." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Claude {displayName} profili eklendi." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Claude profil adı" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude profili kaldırıldı." +#~ msgid "Claude profile removed." +#~ msgstr "Claude profili kaldırıldı." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "PR oluşturma eylemi" msgid "Create PR…" msgstr "PR oluştur…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Profil oluştur" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Proje oluştur" @@ -3493,6 +3517,10 @@ msgstr "Zamanlama oluştur" msgid "Create scheduled task" msgstr "Zamanlanmış görev oluştur" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Kendi API anahtarlarına sahip ayrı Cursor profilleri oluşturun." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Görev oluştur" @@ -3638,6 +3666,15 @@ msgstr "Güncel seri" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Cursor {displayName} profili eklendi." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Cursor ACP oturumu kapatıldı." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor GUI çalışma zamanı güncellendi." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Cursor profili API anahtarı" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Cursor profili API anahtarı kaydedildi." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Cursor profili adı" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Cursor profili kaldırıldı ancak durumlar yenilenemedi." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Cursor profili kaldırıldı." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Cursor profili yeniden adlandırıldı." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "örn. 512k veya 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "örn. hızlı aramalar için Codex GPT-5.5 fast, toplu yeniden düzenlemeler için OpenCode GLM, ince işler için Claude Opus." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "örneğin İş" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Ad" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Yeni Claude profili yapılandırma dizini" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Yeni Claude profil adı" +#~ msgid "New Claude profile name" +#~ msgstr "Yeni Claude profil adı" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Yeni kod" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Yeni Cursor profili API anahtarı" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Yeni Cursor profili adı" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Yeni deney" @@ -7275,6 +7347,10 @@ msgstr "yeni model" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Yeni telefonlar, tabletler ve tarayıcılar eşleştirmeden sonra burada görünür." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Yeni profil adı" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Yeni proje" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Henüz etkinlik yok." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Ek Claude profili yok." +#~ msgid "No additional Claude profiles." +#~ msgstr "Ek Claude profili yok." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Ek Cursor profili yok." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Ek profil yok." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Açık" msgid "Open {0}" msgstr "{0} aç" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "{label} öğesini aç" @@ -8477,6 +8561,7 @@ msgstr "{label} API anahtarını yapıştır" msgid "Paste <0>{0} here. Click to copy." msgstr "<0>{0} öğesini buraya yapıştırın. Kopyalamak için tıklayın." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Cursor API anahtarını yapıştırın" @@ -8582,6 +8667,10 @@ msgstr "Simge seç" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Yeni Cursor GUI sohbetlerinde kullanılacak çalışma zamanını seçin. Her çalışma zamanı ayrı ayrı yüklenir ve kimlik doğrulaması yapar; açık sohbetler başladıkları çalışma zamanını korur." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Yeni Cursor GUI sohbetlerini destekleyen çalışma zamanını seçin. Profil API anahtarı her iki çalışma zamanı için de geçerlidir." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Seçici başarısız oldu" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Üretkenlik" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Profil" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "{displayName} profili eklendi." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Profil API anahtarı" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Profil kaldırıldı, ancak durumlar yenilenemedi." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Profil kaldırıldı." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Profil istatistikleri aralığı" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Profiller" @@ -9353,7 +9459,7 @@ msgstr "Bildirim içeriğini gizle" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "Uzak URL" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Kaldır" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "{0}. adayı kaldır" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Claude profilini kaldır" +#~ msgid "Remove Claude profile" +#~ msgstr "Claude profilini kaldır" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Bağlantıyı kaldır" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Cursor profilini kaldır" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Cursor {label} profilini kaldır" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Cursor profili kaldırılsın mı?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Cursor SDK API anahtarını kaldır" @@ -9597,6 +9716,14 @@ msgstr "Modeli kaldır" msgid "Remove pinned route for {tagLabel}" msgstr "{tagLabel} için sabitlenmiş rotayı kaldır" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "{label} profilini kaldır" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Profil kaldırılsın mı?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Projeyi kaldır" @@ -9626,6 +9753,19 @@ msgstr "Projeyi kaldırmadan önce projenin deneylerini kaldırın." msgid "Remove worktree?" msgstr "Çalışma ağacı kaldırılsın mı?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "{0} kaldırıldığında kayıtlı API anahtarı da silinir. Bu profili kullanan konular artık aracılarını bulamaz." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "{profileName} kaldırıldığında kayıtlı API anahtarı da silinir. Bu profili kullanan konular artık aracılarını bulamaz." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "{profileName} kaldırıldığında Poracode ayarları — ortam değişkenleri, modeller ve effort — silinir. Yapılandırma dizini ve içindeki Claude kimlik bilgileri diskte kalır." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Claude profilini kaydet" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Cursor GUI çalışma zamanını kaydet" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Cursor profili API anahtarını kaydet" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Cursor profili adını kaydet" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Cursor SDK API anahtarını kaydet" @@ -10611,6 +10759,11 @@ msgstr "Mesaj gönder" msgid "Sent selection to terminal." msgstr "Seçim terminale gönderildi." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Ayrı API anahtarı" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Claude Code hesaplarını yapılandırma dizinine göre ayırın ya da bir profili harici bir sağlayıcıya (z.ai, …) yönlendirin. Ortam değişkenlerini, modellerini ve efor düzeyini yapılandırmak için bir profili açın." @@ -10852,6 +11005,10 @@ msgstr "{0, plural, one {# beceri} other {# beceri}} gösteriliyor" msgid "Showing the first 4,000 entries." msgstr "İlk 4.000 giriş gösteriliyor." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Kenar çubuğunda ve model seçicisinde gösterilir." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Proje adının yanında gösterilir." @@ -11797,6 +11954,10 @@ msgstr "aracı" msgid "The agent" msgstr "Temsilci" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Aşağıdaki API anahtarı bu profil için Cursor CLI, ACP ve SDK tarafından kullanılır." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "uygulama" @@ -12050,6 +12211,10 @@ msgstr "Bu klasör boş." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Bu HTTPS sayfası düz HTTP kullanan masaüstüne ulaşamadı. Bu siteye yerel ağ erişimi izni verip yeniden eşleştirin veya masaüstünün kendi adresinde devam edin." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Bu anahtar, bu profil için Cursor CLI, ACP ve SDK tarafından kullanılır." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Bu bağlantı, Poracode'un ağınızda sunduğu uygulamayı açar. Barındırılan uygulama HTTPS üzerinden sunulduğu için tarayıcılar düz HTTP kullanan bir masaüstüne erişimi engeller." diff --git a/src/renderer/locales/uk/messages.po b/src/renderer/locales/uk/messages.po index 0710672e9..7d2ca0823 100644 --- a/src/renderer/locales/uk/messages.po +++ b/src/renderer/locales/uk/messages.po @@ -710,10 +710,18 @@ msgstr "7 дн" msgid "A candidate is no longer on its experiment branch." msgstr "Один із кандидатів більше не перебуває в гілці експерименту." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Профіль Cursor із таким іменем уже існує." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Для замороженого джерела worktree потрібен повний хеш коміту" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Профіль із таким іменем уже існує." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Не вдалося запустити сервер, його пропущено." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Додати кандидата" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Додати профіль Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Додати профіль Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Додати контекстне вікно" msgid "Add credentials before this thread can run." msgstr "Додайте облікові дані, перш ніж цей тред зможе запуститися." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Додати профіль Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Додати теку" @@ -967,7 +979,7 @@ msgstr "Додайте сервер вручну або імпортуйте й msgid "Add pattern" msgstr "Додати шаблон" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Додати профіль" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Скасувати" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Скасувати новий профіль Claude" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Скасувати новий профіль Claude" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Скасувати створення профілю Cursor" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Скасувати новий профіль" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Профіль Claude {0} збережено." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Профіль Claude {displayName} додано." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Профіль Claude {displayName} додано." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Ім'я профілю Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Профіль Claude видалено." +#~ msgid "Claude profile removed." +#~ msgstr "Профіль Claude видалено." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Дія створення PR" msgid "Create PR…" msgstr "Створити PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Створити профіль" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Створити проект" @@ -3493,6 +3517,10 @@ msgstr "Створити розклад" msgid "Create scheduled task" msgstr "Створити заплановане завдання" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Створюйте окремі профілі Cursor із власними ключами API." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Створити завдання" @@ -3638,6 +3666,15 @@ msgstr "Поточна серія" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Профіль Cursor {displayName} додано." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Виконано вихід із Cursor ACP." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Середовище виконання GUI Cursor оновлено." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Ключ API профілю Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Ключ API профілю Cursor збережено." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Ім'я профілю Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Профіль Cursor видалено, але не вдалося оновити статуси." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Профіль Cursor видалено." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Профіль Cursor перейменовано." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "напр. 512k або 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "напр., Codex GPT-5.5 fast для швидких запитів, OpenCode GLM для масових рефакторингів, Claude Opus для всього тонкого." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "напр. Робота" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Ім'я" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Новий каталог конфігурації профілю Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Нова назва профілю Claude" +#~ msgid "New Claude profile name" +#~ msgstr "Нова назва профілю Claude" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Новий код" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Ключ API нового профілю Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Назва нового профілю Cursor" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Новий експеримент" @@ -7275,6 +7347,10 @@ msgstr "нова модель" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Нові телефони, планшети й браузери з’являться тут після сполучення." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Ім'я нового профілю" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Новий проєкт" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Активності ще немає." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Додаткових профілів Claude немає." +#~ msgid "No additional Claude profiles." +#~ msgstr "Додаткових профілів Claude немає." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Додаткових профілів Cursor немає." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Додаткових профілів немає." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Відкрити" msgid "Open {0}" msgstr "Відкрити {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Відкрити {label}" @@ -8477,6 +8561,7 @@ msgstr "Вставте API-ключ {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Вставте <0>{0} сюди. Натисніть, щоб скопіювати." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Вставте API-ключ Cursor" @@ -8582,6 +8667,10 @@ msgstr "Вибрати значок" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Виберіть середовище виконання для нових GUI-чатів Cursor. Кожне середовище встановлюється та автентифікується окремо; відкриті чати зберігають те, з яким були запущені." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Виберіть середовище для нових GUI-чатів Cursor. Ключ API профілю застосовується до обох середовищ." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Помилка вибору" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Продуктивність" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Профіль" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Профіль {displayName} додано." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Ключ API профілю" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Профіль видалено, але оновити статуси не вдалося." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Профіль видалено." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Діапазон статистики профілю" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Профілі" @@ -9353,7 +9459,7 @@ msgstr "Приховувати вміст сповіщень" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "URL віддаленого репозиторію" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Видалити" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Видалити кандидата {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Видалити профіль Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Видалити профіль Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Видалити підключення" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Видалити профіль Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Видалити профіль Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Видалити профіль Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Видалити API-ключ Cursor SDK" @@ -9597,6 +9716,14 @@ msgstr "Видалити модель" msgid "Remove pinned route for {tagLabel}" msgstr "Видалити закріплений маршрут для {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Видалити профіль {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Видалити профіль?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Видалити проєкт" @@ -9626,6 +9753,19 @@ msgstr "Видаліть експерименти проєкту, перш ні msgid "Remove worktree?" msgstr "Видалити worktree?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Під час видалення {0} збережений API-ключ буде стерто. Треди, що використовують цей профіль, більше не знайдуть свого агента." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Під час видалення {profileName} збережений API-ключ буде стерто. Треди, що використовують цей профіль, більше не знайдуть свого агента." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Видалення {profileName} скидає його налаштування Poracode — змінні середовища, моделі та effort. Каталог конфігурації та дані Claude у ньому залишаться на диску." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Зберегти профіль Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Зберегти середовище виконання GUI Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Зберегти ключ API профілю Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Зберегти ім'я профілю Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Зберегти API-ключ Cursor SDK" @@ -10611,6 +10759,11 @@ msgstr "Надіслати повідомлення" msgid "Sent selection to terminal." msgstr "Вибір надіслано в термінал." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Окремий ключ API" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Розділяйте облікові записи Claude Code за каталогом конфігурації або спрямовуйте профіль на зовнішнього провайдера (z.ai, …). Відкрийте профіль, щоб налаштувати його змінні середовища, моделі та рівень зусиль." @@ -10852,6 +11005,10 @@ msgstr "Показано {0, plural, one {# навичку} few {# навичк msgid "Showing the first 4,000 entries." msgstr "Показано перші 4 000 записів." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Показується на бічній панелі та в селекторі моделей." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Показується поруч із назвою проєкту." @@ -11797,6 +11954,10 @@ msgstr "агент" msgid "The agent" msgstr "Агент" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Наведений нижче API-ключ використовується в Cursor CLI, ACP і SDK для цього профілю." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "програму" @@ -12050,6 +12211,10 @@ msgstr "Ця папка порожня." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Ця HTTPS-сторінка не змогла звернутися до десктопа через звичайний HTTP. Дозвольте цьому сайту доступ до локальної мережі та повторіть підключення або продовжте за власною адресою десктопа." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Цей ключ використовується Cursor CLI, ACP і SDK для цього профілю." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Це посилання відкриває застосунок, який Poracode роздає у вашій мережі. Розміщений застосунок працює через HTTPS, тому браузери блокують звернення до десктопа через звичайний HTTP." diff --git a/src/renderer/locales/vi/messages.po b/src/renderer/locales/vi/messages.po index da593f91b..2cb8ee65c 100644 --- a/src/renderer/locales/vi/messages.po +++ b/src/renderer/locales/vi/messages.po @@ -710,10 +710,18 @@ msgstr "7 ngày" msgid "A candidate is no longer on its experiment branch." msgstr "Một ứng viên không còn ở nhánh thử nghiệm của mình." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "Đã có hồ sơ Cursor trùng tên này." + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "Nguồn cây làm việc đã đóng băng yêu cầu hàm băm đầy đủ của commit" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "Đã có hồ sơ trùng tên này." + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "Không thể khởi động một máy chủ nên đã bỏ qua." @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "Thêm ứng viên" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "Thêm hồ sơ Claude" +#~ msgid "Add Claude profile" +#~ msgstr "Thêm hồ sơ Claude" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "Thêm cửa sổ ngữ cảnh" msgid "Add credentials before this thread can run." msgstr "Thêm thông tin xác thực trước khi chuỗi này có thể chạy." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "Thêm hồ sơ Cursor" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "Thêm thư mục" @@ -967,7 +979,7 @@ msgstr "Thêm thủ công hoặc nhập từ một tác nhân khác." msgid "Add pattern" msgstr "Thêm mẫu" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "Thêm hồ sơ" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "Hủy bỏ" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "Hủy tạo hồ sơ Claude mới" +#~ msgid "Cancel new Claude profile" +#~ msgstr "Hủy tạo hồ sơ Claude mới" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "Hủy hồ sơ Cursor mới" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "Hủy hồ sơ mới" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Đã lưu hồ sơ Claude {0}." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "Đã thêm hồ sơ Claude {displayName}." +#~ msgid "Claude {displayName} profile added." +#~ msgstr "Đã thêm hồ sơ Claude {displayName}." #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Tên hồ sơ Claude" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Hồ sơ Claude đã bị xóa." +#~ msgid "Claude profile removed." +#~ msgstr "Hồ sơ Claude đã bị xóa." #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "Hành động Tạo PR" msgid "Create PR…" msgstr "Tạo PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "Tạo hồ sơ" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "Tạo dự án" @@ -3493,6 +3517,10 @@ msgstr "Tạo lịch" msgid "Create scheduled task" msgstr "Tạo tác vụ theo lịch" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "Tạo các hồ sơ Cursor riêng với khóa API riêng." + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "Tạo nhiệm vụ" @@ -3638,6 +3666,15 @@ msgstr "Chuỗi ngày hiện tại" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "Đã thêm hồ sơ Cursor {displayName}." + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "Đã đăng xuất khỏi Cursor ACP." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Đã cập nhật môi trường chạy GUI Cursor." +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Khóa API của hồ sơ Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "Đã lưu khóa API của hồ sơ Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Tên hồ sơ Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "Đã xóa hồ sơ Cursor nhưng không thể làm mới trạng thái." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "Đã xóa hồ sơ Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "Đã đổi tên hồ sơ Cursor." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "vd. 512k hoặc 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "ví dụ: Codex GPT-5.5 fast cho các tra cứu nhanh, OpenCode GLM cho tái cấu trúc hàng loạt, Claude Opus cho những việc tinh tế." -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "ví dụ: Work" @@ -7140,6 +7203,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "Tên" @@ -7222,13 +7286,21 @@ msgid "New Claude profile config directory" msgstr "Thư mục cấu hình hồ sơ Claude mới" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "Tên hồ sơ Claude mới" +#~ msgid "New Claude profile name" +#~ msgstr "Tên hồ sơ Claude mới" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "Mã mới" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "Khóa API của hồ sơ Cursor mới" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "Tên hồ sơ Cursor mới" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "Thử nghiệm mới" @@ -7275,6 +7347,10 @@ msgstr "mô hình mới" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "Điện thoại, máy tính bảng và trình duyệt mới sẽ xuất hiện ở đây sau khi ghép đôi." +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "Tên hồ sơ mới" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "Dự án mới" @@ -7407,8 +7483,16 @@ msgid "No activity yet." msgstr "Chưa có hoạt động nào." #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "Không có hồ sơ Claude bổ sung." +#~ msgid "No additional Claude profiles." +#~ msgstr "Không có hồ sơ Claude bổ sung." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "Không có hồ sơ Cursor bổ sung." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "Không có hồ sơ bổ sung." #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7992,7 +8076,7 @@ msgstr "Mở" msgid "Open {0}" msgstr "Mở {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "Mở {label}" @@ -8477,6 +8561,7 @@ msgstr "Dán khóa API {label}" msgid "Paste <0>{0} here. Click to copy." msgstr "Dán <0>{0} vào đây. Nhấn vào đây để sao chép." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "Dán khóa API Cursor" @@ -8582,6 +8667,10 @@ msgstr "Chọn biểu tượng" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "Chọn thời gian chạy cho các cuộc trò chuyện GUI Cursor mới. Mỗi thời gian chạy được cài đặt và xác thực riêng; các cuộc trò chuyện đang mở giữ nguyên thời gian chạy khi bắt đầu." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "Chọn runtime hỗ trợ các cuộc trò chuyện GUI Cursor mới. Khóa API của hồ sơ áp dụng cho cả hai runtime." + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "Bộ chọn không thành công" @@ -8912,15 +9001,32 @@ msgid "Productivity" msgstr "Năng suất" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "Hồ sơ" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "Đã thêm hồ sơ {displayName}." + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "Khóa API của hồ sơ" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "Đã xóa hồ sơ, nhưng không thể làm mới trạng thái." + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "Đã xóa hồ sơ." + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "Phạm vi thống kê hồ sơ" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "Hồ sơ" @@ -9353,7 +9459,7 @@ msgstr "Ẩn nội dung thông báo" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9512,6 +9618,7 @@ msgid "Remote URL" msgstr "URL từ xa" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "Xóa" @@ -9557,13 +9664,25 @@ msgid "Remove candidate {0}" msgstr "Xóa ứng viên {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "Xóa hồ sơ Claude" +#~ msgid "Remove Claude profile" +#~ msgstr "Xóa hồ sơ Claude" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "Xóa kết nối" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "Xóa hồ sơ Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "Xóa hồ sơ Cursor {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "Xóa hồ sơ Cursor?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "Xóa khóa API Cursor SDK" @@ -9597,6 +9716,14 @@ msgstr "Xóa mô hình" msgid "Remove pinned route for {tagLabel}" msgstr "Xóa tuyến đã ghim cho {tagLabel}" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "Xóa hồ sơ {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "Xóa hồ sơ?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "Xóa dự án" @@ -9626,6 +9753,19 @@ msgstr "Hãy xóa các thử nghiệm của dự án trước khi xóa dự án. msgid "Remove worktree?" msgstr "Xóa worktree?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "Xóa {0} sẽ xóa luôn khóa API đã lưu. Các luồng dùng hồ sơ này sẽ không còn tìm thấy tác nhân của chúng." + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "Xóa {profileName} sẽ xóa luôn khóa API đã lưu. Các luồng dùng hồ sơ này sẽ không còn tìm thấy tác nhân của chúng." + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "Xóa {profileName} sẽ bỏ các thiết lập Poracode của nó — biến môi trường, mô hình và effort. Thư mục cấu hình và thông tin đăng nhập Claude bên trong vẫn còn trên đĩa." + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10201,6 +10341,14 @@ msgstr "Lưu hồ sơ Claude" #~ msgid "Save Cursor GUI runtime" #~ msgstr "Lưu môi trường chạy GUI Cursor" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "Lưu khóa API của hồ sơ Cursor" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "Lưu tên hồ sơ Cursor" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "Lưu khóa API Cursor SDK" @@ -10611,6 +10759,11 @@ msgstr "Gửi tin nhắn" msgid "Sent selection to terminal." msgstr "Đã gửi lựa chọn đến thiết bị đầu cuối." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "Khóa API riêng" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "Tách các tài khoản Claude Code theo thư mục cấu hình, hoặc trỏ một hồ sơ tới nhà cung cấp bên ngoài (z.ai, …). Mở một hồ sơ để cấu hình biến môi trường, mô hình và mức nỗ lực của nó." @@ -10852,6 +11005,10 @@ msgstr "Đang hiển thị {0, plural, other {# skill}}" msgid "Showing the first 4,000 entries." msgstr "Đang hiển thị 4.000 mục đầu tiên." +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "Hiển thị ở thanh bên và bộ chọn mô hình." + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "Hiển thị cạnh tên dự án." @@ -11797,6 +11954,10 @@ msgstr "tác nhân" msgid "The agent" msgstr "Tác nhân" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "Khóa API bên dưới được Cursor CLI, ACP và SDK dùng cho hồ sơ này." + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "ứng dụng" @@ -12050,6 +12211,10 @@ msgstr "Thư mục này trống." msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "Trang HTTPS này không thể kết nối tới desktop dùng HTTP thuần. Hãy cho phép trang này truy cập mạng cục bộ rồi ghép đôi lại, hoặc tiếp tục trên địa chỉ riêng của desktop." +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "Khóa này được Cursor CLI, ACP và SDK sử dụng cho hồ sơ này." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "Liên kết này mở ứng dụng mà Poracode phục vụ trong mạng của bạn. Ứng dụng được host chạy qua HTTPS, nên trình duyệt chặn việc kết nối tới desktop dùng HTTP thuần." diff --git a/src/renderer/locales/zh-CN/messages.po b/src/renderer/locales/zh-CN/messages.po index 50f1abca5..3ce8bf5d6 100644 --- a/src/renderer/locales/zh-CN/messages.po +++ b/src/renderer/locales/zh-CN/messages.po @@ -710,10 +710,18 @@ msgstr "7天" msgid "A candidate is no longer on its experiment branch." msgstr "候选项已不在其实验分支上。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "A Cursor profile with this name already exists." +msgstr "已存在同名的 Cursor 配置文件。" + #: src/renderer/i18n/sharedMessages.ts msgid "A frozen worktree source requires a full commit hash" msgstr "冻结的工作树源需要完整的提交哈希" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "A profile with this name already exists." +msgstr "已存在同名的配置文件。" + #: src/renderer/components/plugins/pluginCopy.ts msgid "A server could not be started and was skipped." msgstr "无法启动某个服务器,已跳过。" @@ -932,8 +940,8 @@ msgid "Add candidate" msgstr "添加候选项" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Add Claude profile" -msgstr "添加 Claude 配置文件" +#~ msgid "Add Claude profile" +#~ msgstr "添加 Claude 配置文件" #: src/renderer/views/SettingsOverlay/parts/CodexProviderSettings.tsx msgid "Add context window" @@ -943,6 +951,10 @@ msgstr "添加上下文窗口" msgid "Add credentials before this thread can run." msgstr "在此线程运行之前添加凭据。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Add Cursor profile" +#~ msgstr "添加 Cursor 配置文件" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Add folder" msgstr "添加文件夹" @@ -967,7 +979,7 @@ msgstr "手动添加一个,或从其他代理导入。" msgid "Add pattern" msgstr "添加模式" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Add profile" msgstr "添加配置文件" @@ -2050,8 +2062,16 @@ msgid "Cancel" msgstr "取消" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Cancel new Claude profile" -msgstr "取消新的 Claude 配置文件" +#~ msgid "Cancel new Claude profile" +#~ msgstr "取消新的 Claude 配置文件" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cancel new Cursor profile" +#~ msgstr "取消新建 Cursor 配置文件" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Cancel new profile" +msgstr "取消新建配置文件" #: src/renderer/components/thread/ThreadPendingSteerStrip.tsx msgid "Cancel pending steer" @@ -2391,8 +2411,8 @@ msgid "Claude {0} profile saved." msgstr "Claude {0} 配置文件已保存。" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude {displayName} profile added." -msgstr "已添加 Claude {displayName} 配置文件。" +#~ msgid "Claude {displayName} profile added." +#~ msgstr "已添加 Claude {displayName} 配置文件。" #: src/renderer/components/providers/claude/manifest.ts msgid "Claude Code" @@ -2407,8 +2427,8 @@ msgid "Claude profile name" msgstr "Claude 配置文件名称" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Claude profile removed." -msgstr "Claude 配置文件已删除。" +#~ msgid "Claude profile removed." +#~ msgstr "Claude 配置文件已删除。" #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx @@ -3476,6 +3496,10 @@ msgstr "创建 PR 操作" msgid "Create PR…" msgstr "创建 PR…" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Create profile" +msgstr "创建配置文件" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Create project" msgstr "创建项目" @@ -3493,6 +3517,10 @@ msgstr "创建计划" msgid "Create scheduled task" msgstr "创建计划任务" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Create separate Cursor profiles with their own API keys." +msgstr "创建使用各自 API 密钥的独立 Cursor 配置文件。" + #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts msgid "Create task" msgstr "创建任务" @@ -3638,6 +3666,15 @@ msgstr "当前连续天数" msgid "Cursor" msgstr "Cursor" +#. placeholder {0}: cursorProfileInstance.displayName ?? cursorProfileInstance.id +#: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/SingleAgentSettings.tsx +#~ msgid "Cursor {0}" +#~ msgstr "Cursor {0}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor {displayName} profile added." +#~ msgstr "已添加 Cursor 配置文件 {displayName}。" + #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx #~ msgid "Cursor ACP" #~ msgstr "Cursor ACP" @@ -3650,6 +3687,7 @@ msgstr "Cursor" #~ msgid "Cursor ACP logged out." #~ msgstr "已退出 Cursor ACP。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor CLI (ACP)" msgstr "Cursor CLI (ACP)" @@ -3658,6 +3696,31 @@ msgstr "Cursor CLI (ACP)" #~ msgid "Cursor GUI runtime updated." #~ msgstr "Cursor GUI 运行时已更新。" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key" +msgstr "Cursor 配置文件 API 密钥" + +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Cursor profile API key saved." +msgstr "已保存 Cursor 配置文件 API 密钥。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile name" +msgstr "Cursor 配置文件名称" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed, but statuses could not be refreshed." +#~ msgstr "已移除 Cursor 配置文件,但无法刷新状态。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Cursor profile removed." +#~ msgstr "已移除 Cursor 配置文件。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Cursor profile renamed." +msgstr "已重命名 Cursor 配置文件。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx msgid "Cursor SDK" msgstr "Cursor SDK" @@ -4326,7 +4389,7 @@ msgstr "例如 512k 或 1m" msgid "e.g. Codex GPT-5.5 fast for quick lookups, OpenCode GLM for bulk refactors, Claude Opus for anything subtle." msgstr "例如:Codex GPT-5.5 fast 用于快速查询,OpenCode GLM 用于批量重构,Claude Opus 用于任何微妙的任务。" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "e.g. Work" msgstr "例如工作" @@ -7139,6 +7202,7 @@ msgstr "my-mcp-server" #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx msgid "Name" msgstr "名称" @@ -7221,13 +7285,21 @@ msgid "New Claude profile config directory" msgstr "新的 Claude 配置文件目录" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "New Claude profile name" -msgstr "新 Claude 配置文件名称" +#~ msgid "New Claude profile name" +#~ msgstr "新 Claude 配置文件名称" #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "New code" msgstr "新代码" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "New Cursor profile API key" +msgstr "新 Cursor 配置文件 API 密钥" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "New Cursor profile name" +#~ msgstr "新 Cursor 配置文件名称" + #: src/renderer/components/experiment/NewExperimentModal.tsx #~ msgid "New experiment" #~ msgstr "新建实验" @@ -7274,6 +7346,10 @@ msgstr "新模型" msgid "New phones, tablets, and browsers appear here after pairing." msgstr "新的手机、平板电脑和浏览器会在配对后显示在这里。" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "New profile name" +msgstr "新配置文件名称" + #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "New project" msgstr "新项目" @@ -7406,8 +7482,16 @@ msgid "No activity yet." msgstr "暂无活动。" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "No additional Claude profiles." -msgstr "没有其他 Claude 配置文件。" +#~ msgid "No additional Claude profiles." +#~ msgstr "没有其他 Claude 配置文件。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "No additional Cursor profiles." +#~ msgstr "没有其他 Cursor 配置文件。" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "No additional profiles." +msgstr "没有其他配置文件。" #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/useGitReviewActions.ts msgid "No agent available to generate PR summary" @@ -7991,7 +8075,7 @@ msgstr "打开" msgid "Open {0}" msgstr "打开 {0}" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Open {label}" msgstr "打开{label}" @@ -8476,6 +8560,7 @@ msgstr "粘贴 {label} API 密钥" msgid "Paste <0>{0} here. Click to copy." msgstr "将 <0>{0} 粘贴到此处。点击复制。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Paste Cursor API key" msgstr "粘贴 Cursor API 密钥" @@ -8581,6 +8666,10 @@ msgstr "选择图标" msgid "Pick the runtime that backs new Cursor GUI chats. Each runtime installs and authenticates on its own; open chats keep the runtime they started with." msgstr "选择用于新建 Cursor GUI 聊天的运行时。每个运行时单独安装并单独进行身份验证;已打开的聊天会保留启动时使用的运行时。" +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +msgid "Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both runtimes." +msgstr "选择用于新 Cursor GUI 聊天的运行时。配置文件 API 密钥适用于两个运行时。" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useElementPicker.ts msgid "Picker failed" msgstr "选取器失败" @@ -8911,15 +9000,32 @@ msgid "Productivity" msgstr "效率" #: src/mobile/settingsSections.ts +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx #: src/renderer/views/SettingsOverlay/parts/SettingsSidebar.tsx msgid "Profile" msgstr "个人资料" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile {displayName} added." +msgstr "已添加配置文件 {displayName}。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "Profile API key" +#~ msgstr "配置文件 API 密钥" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed, but statuses could not be refreshed." +msgstr "已移除配置文件,但无法刷新状态。" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Profile removed." +msgstr "已移除配置文件。" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "Profile stats range" msgstr "个人资料统计范围" -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Profiles" msgstr "配置文件" @@ -9352,7 +9458,7 @@ msgstr "隐藏通知内容" #: src/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions.tsx #: src/renderer/views/PrReviewOverlay/PrReviewOverlay.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx -#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Refresh" @@ -9511,6 +9617,7 @@ msgid "Remote URL" msgstr "远程网址" #: src/renderer/components/common/BranchSelector/BranchSelector.tsx +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx msgid "Remove" msgstr "删除" @@ -9556,13 +9663,25 @@ msgid "Remove candidate {0}" msgstr "移除候选项 {0}" #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx -msgid "Remove Claude profile" -msgstr "删除 Claude 配置文件" +#~ msgid "Remove Claude profile" +#~ msgstr "删除 Claude 配置文件" #: src/mobile/views/DesktopsView.tsx msgid "Remove connection" msgstr "删除连接" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile" +#~ msgstr "移除 Cursor 配置文件" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile {label}" +#~ msgstr "移除 Cursor 配置文件 {label}" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Remove Cursor profile?" +#~ msgstr "移除 Cursor 配置文件?" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Remove Cursor SDK API key" msgstr "移除 Cursor SDK API 密钥" @@ -9596,6 +9715,14 @@ msgstr "移除模型" msgid "Remove pinned route for {tagLabel}" msgstr "移除 {tagLabel} 的固定路由" +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile {label}" +msgstr "移除配置文件 {label}" + +#: src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx +msgid "Remove profile?" +msgstr "移除配置文件?" + #: src/mobile/views/ManageProjectsView.tsx msgid "Remove project" msgstr "移除项目" @@ -9625,6 +9752,19 @@ msgstr "删除项目前,请先删除该项目的实验。" msgid "Remove worktree?" msgstr "删除工作树?" +#. placeholder {0}: confirmRemoval?.displayName ?? confirmRemoval?.id +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#~ msgid "Removing {0} deletes its saved API key. Threads that use this profile will no longer find their agent." +#~ msgstr "移除 {0} 会删除其保存的 API 密钥。使用该配置文件的线程将再也找不到其代理。" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +msgid "Removing {profileName} deletes its saved API key. Threads that use this profile will no longer find their agent." +msgstr "移除 {profileName} 会删除其保存的 API 密钥。使用该配置文件的线程将再也找不到其代理。" + +#: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +msgid "Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config directory and the Claude credentials inside it stay on disk." +msgstr "移除 {profileName} 会丢弃其 Poracode 设置——环境变量、模型和 effort。其配置目录以及目录中的 Claude 凭据仍保留在磁盘上。" + #: src/mobile/ThreadTitleRow.tsx #: src/mobile/TodoActionsSheet.tsx #: src/mobile/views/DesktopsView.tsx @@ -10200,6 +10340,14 @@ msgstr "保存 Claude 配置文件" #~ msgid "Save Cursor GUI runtime" #~ msgstr "保存 Cursor GUI 运行时" +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +msgid "Save Cursor profile API key" +msgstr "保存 Cursor 配置文件 API 密钥" + +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Save Cursor profile name" +msgstr "保存 Cursor 配置文件名称" + #: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx msgid "Save Cursor SDK API key" msgstr "保存 Cursor SDK API 密钥" @@ -10610,6 +10758,11 @@ msgstr "发送消息" msgid "Sent selection to terminal." msgstr "将选择发送到终端。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx +#: src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +#~ msgid "Separate API key" +#~ msgstr "独立 API 密钥" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Separate Claude Code accounts by config directory, or point a profile at an external provider (z.ai, …). Open a profile to configure its env vars, models, and effort." msgstr "按配置目录区分Claude Code账户,或将配置指向外部服务商(z.ai,…)。打开某个配置以设置其环境变量、模型和思考级别。" @@ -10851,6 +11004,10 @@ msgstr "正在显示 {0, plural, other {# 个技能}}" msgid "Showing the first 4,000 entries." msgstr "仅显示前 4,000 个条目。" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "Shown in the sidebar and the model picker." +msgstr "显示在侧边栏和模型选择器中。" + #: src/renderer/views/ProjectSettingsOverlay/parts/GeneralSection.tsx msgid "Shown next to the project name." msgstr "显示在项目名称旁。" @@ -11796,6 +11953,10 @@ msgstr "代理" msgid "The agent" msgstr "代理" +#: src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx +msgid "The API key below is used by Cursor CLI, ACP, and SDK for this profile." +msgstr "下面的 API 密钥用于该配置文件的 Cursor CLI、ACP 和 SDK。" + #: src/renderer/components/providers/usageFormat.ts msgid "the app" msgstr "该应用程序" @@ -12049,6 +12210,10 @@ msgstr "此文件夹为空。" msgid "This HTTPS page couldn't reach the desktop on plain HTTP. Allow local network access for this site and pair again, or continue on the desktop's own address." msgstr "此 HTTPS 页面无法连接使用纯 HTTP 的桌面。请允许该站点访问本地网络后重新配对,或在桌面自身的地址上继续。" +#: src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +#~ msgid "This key is used by Cursor CLI, ACP, and SDK for this profile." +#~ msgstr "此密钥供该配置文件的 Cursor CLI、ACP 和 SDK 使用。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx #~ msgid "This link opens the app Poracode serves on your network. The hosted app is served over HTTPS, which browsers block from reaching a plain-HTTP desktop." #~ msgstr "此链接会打开 Poracode 在你网络中提供的应用。托管版应用通过 HTTPS 提供,浏览器会因此阻止它连接使用纯 HTTP 的桌面。" diff --git a/src/renderer/state/sharedSettingsStore.test.ts b/src/renderer/state/sharedSettingsStore.test.ts index 33f320ca0..0dc88ced8 100644 --- a/src/renderer/state/sharedSettingsStore.test.ts +++ b/src/renderer/state/sharedSettingsStore.test.ts @@ -334,4 +334,21 @@ describe("sharedSettingsStore", () => { expect(state.recentModels).toEqual([]); expect(state.providerOrder).toEqual(["claude"]); }); + + it("removes Cursor profile-scoped settings with the profile instance", () => { + useSharedSettings.getState().setAgentInstance({ + id: "work", + driver: "cursor", + displayName: "Work", + }); + useSharedSettings.setState({ + agentSettings: { "cursor:work": { structuredRuntime: "sdk" } }, + providerOrder: ["cursor", "cursor:work"], + }); + + useSharedSettings.getState().removeAgentInstance("work"); + + expect(useSharedSettings.getState().agentSettings["cursor:work"]).toBeUndefined(); + expect(useSharedSettings.getState().providerOrder).toEqual(["cursor"]); + }); }); diff --git a/src/renderer/state/sharedSettingsStore.ts b/src/renderer/state/sharedSettingsStore.ts index e43ee4866..877c18157 100644 --- a/src/renderer/state/sharedSettingsStore.ts +++ b/src/renderer/state/sharedSettingsStore.ts @@ -825,9 +825,10 @@ export const useSharedSettings = create()((set, get) => ({ }, removeAgentInstance: (instanceId) => { const current = get().agentInstances; - if (!current[instanceId]) return; + const instance = current[instanceId]; + if (!instance) return; const { [instanceId]: _removed, ...agentInstances } = current; - const prefix = `claude:${instanceId}`; + const prefix = `${instance.driver}:${instanceId}`; const removeProfileKey = (values: Record) => Object.fromEntries(Object.entries(values).filter(([key]) => key !== prefix)); set({ diff --git a/src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx b/src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx new file mode 100644 index 000000000..a3c7f41be --- /dev/null +++ b/src/renderer/views/SettingsOverlay/parts/AgentProfileList.tsx @@ -0,0 +1,361 @@ +import { useEffect, useRef, useState, type ReactNode } from "react"; +import { Button, toast } from "@heroui/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { Check, ChevronRight, Plus, RefreshCw, Trash2, X } from "lucide-react"; +import { agentProfileKind, type AgentInstanceConfig, type AgentStatus } from "@/shared/contracts"; +import { friendlyError } from "@/shared/messages"; +import { readBridge } from "@/renderer/bridge"; +import { ConfirmDialog, Input, PixelLoader } from "@/renderer/components/common"; +import { useAgentStatusesStore } from "@/renderer/state/agentStatusesStore"; +import { flushSharedSettings, useSharedSettings } from "@/renderer/state/sharedSettingsStore"; +import { currentWslDistros } from "@/renderer/utils/acpRegistryAuth"; +import type { NativeAgentProfileSupport } from "./agentRegistryNative"; +import { isDuplicateProfileName, uniqueProfileId } from "./profileIds"; + +/** + * The profile list every multi-profile provider renders on its base settings + * page: add, open, rename-free listing, and confirmed removal. + * + * Everything a provider can differ on arrives through its + * `NativeAgentProfileSupport` descriptor — the second add-form field, the row + * subtitle, the removal consequence, and how a create turns into a payload. The + * flow itself (id derivation, duplicate-name guard, Enter-to-submit, sealed + * create through `createProfile`, pending state, rollback on a failed flush, + * status refresh) is shared, so adding profiles to a new provider is a + * descriptor, not another copy of this file. + */ +export function AgentProfileList(props: { + profiles: NativeAgentProfileSupport; + /** The base provider's detected statuses, forwarded to `onCreated`. */ + statuses?: readonly AgentStatus[] | undefined; + onOpenProfile?: ((profileKind: string) => void) | undefined; +}) { + const { profiles: support } = props; + const { t } = useLingui(); + const agentInstances = useSharedSettings((state) => state.agentInstances); + const setAgentInstance = useSharedSettings((state) => state.setAgentInstance); + const removeAgentInstance = useSharedSettings((state) => state.removeAgentInstance); + const removeAgentStatus = useAgentStatusesStore((state) => state.removeAgentStatus); + const [isAdding, setIsAdding] = useState(false); + const [isSaving, setIsSaving] = useState(false); + const [pendingKind, setPendingKind] = useState(); + const [removingKind, setRemovingKind] = useState(); + const [confirmRemoval, setConfirmRemoval] = useState(); + const [newName, setNewName] = useState(""); + const [newField, setNewField] = useState(""); + const nameInputRef = useRef(null); + const addButtonRef = useRef(null); + + const driverInstances = Object.values(agentInstances).filter( + (instance) => instance.driver === support.driver, + ); + const listed = driverInstances + .filter((instance) => instance.enabled !== false) + .sort((left, right) => + (left.displayName ?? left.id).localeCompare(right.displayName ?? right.id), + ); + + const trimmedName = newName.trim(); + const isDuplicate = isDuplicateProfileName(trimmedName, driverInstances); + const fieldPlaceholder = support.field.placeholderFor + ? support.field.placeholderFor(newName) + : support.field.placeholder + ? t(support.field.placeholder) + : ""; + const canSubmit = + trimmedName.length > 0 && + !isDuplicate && + (support.field.required !== true || newField.trim().length > 0); + + const closeAddForm = () => { + setIsAdding(false); + setNewName(""); + setNewField(""); + // The form inputs unmount on close; hand keyboard focus back to the toggle. + requestAnimationFrame(() => addButtonRef.current?.focus()); + }; + + useEffect(() => { + if (isAdding) nameInputRef.current?.focus(); + }, [isAdding]); + + const refreshProfiles = (kind?: string) => + readBridge().refreshAgentStatuses( + currentWslDistros(), + kind ? { agentKinds: [kind] } : undefined, + ); + + const addProfile = async () => { + if (!canSubmit || isSaving) return; + const displayName = trimmedName; + const id = uniqueProfileId(displayName, agentInstances); + const kind = agentProfileKind(support.driver, id); + setIsSaving(true); + try { + // `createProfile` seals the payload in the main process, so a credential + // entered here never rides the renderer's plaintext settings flush. + const instance = await readBridge().createProfile( + support.createPayload({ + id, + displayName, + field: support.field.placeholderFor + ? newField.trim() || fieldPlaceholder + : newField.trim(), + }), + ); + setPendingKind(kind); + setAgentInstance(instance); + support.onCreated?.({ profileKind: kind, instance, statuses: props.statuses ?? [] }); + closeAddForm(); + toast.success(t`Profile ${displayName} added.`); + try { + // Flush before refreshing so the supervisor's registry rebuild reads + // any settings the provider pinned in `onCreated`. + await flushSharedSettings(); + await refreshProfiles(kind); + props.onOpenProfile?.(kind); + } catch (error) { + toast.danger(friendlyError(error)); + } finally { + setPendingKind(undefined); + } + } catch (error) { + toast.danger(friendlyError(error)); + } finally { + setIsSaving(false); + } + }; + + const removeProfile = async (instance: AgentInstanceConfig) => { + const kind = agentProfileKind(support.driver, instance.id); + if (removingKind) return; + setRemovingKind(kind); + try { + // `removeAgentInstance` strips the instance plus every profile-scoped + // slice (`agentSettings`, `providerOrder`, favorites, …) in one store + // update; snapshot them so a failed flush can restore the exact + // pre-removal state instead of a half-deleted profile. + const snapshot = useSharedSettings.getState(); + removeAgentInstance(instance.id); + try { + await flushSharedSettings(); + } catch (error) { + useSharedSettings.setState({ + agentInstances: snapshot.agentInstances, + providerConfigs: snapshot.providerConfigs, + providerModelPreferences: snapshot.providerModelPreferences, + hiddenModels: snapshot.hiddenModels, + agentSettings: snapshot.agentSettings, + lastPresentationModeByAgent: snapshot.lastPresentationModeByAgent, + disabledAgents: snapshot.disabledAgents, + favoriteModels: snapshot.favoriteModels, + recentModels: snapshot.recentModels, + providerOrder: snapshot.providerOrder, + }); + toast.danger(friendlyError(error)); + return; + } + removeAgentStatus(kind); + try { + await refreshProfiles(); + } catch { + toast.danger(t`Profile removed, but statuses could not be refreshed.`); + return; + } + toast.success(t`Profile removed.`); + } finally { + setRemovingKind(undefined); + } + }; + + const submitOnEnter = (event: { key: string; preventDefault: () => void }) => { + // Pasting a value and then hunting for a 28px icon-only button is the whole + // friction of this form. + if (event.key !== "Enter") return; + event.preventDefault(); + void addProfile(); + }; + + return ( +
+
+
+

+ Profiles +

+

{support.description}

+
+ {/* Profiles only appear once detection has seen them, so re-running it + is the recovery when one is missing from the sidebar. */} + +
+ + {listed.length === 0 && !isAdding ? ( +

+ No additional profiles. +

+ ) : null} + +
+ {listed.map((instance) => { + const label = instance.displayName ?? instance.id; + const kind = agentProfileKind(support.driver, instance.id); + const isPending = pendingKind === kind || removingKind === kind; + return ( +
+ +
+ +
+
+ ); + })} + + {isAdding ? ( +
+
+ setNewName(event.target.value)} + onKeyDown={submitOnEnter} + /> + setNewField(event.target.value)} + onKeyDown={submitOnEnter} + {...(support.field.secret + ? { + type: "password" as const, + spellCheck: false, + autoCapitalize: "off" as const, + autoCorrect: "off" as const, + } + : {})} + /> + {/* Icon-only actions matching the saved rows' action pair, so the + action column keeps the same width when the draft opens. */} +
+ + +
+
+ {isDuplicate ? ( +

+ A profile with this name already exists. +

+ ) : null} +
+ ) : null} +
+ + {!isAdding ? ( + + ) : null} + + { + const instance = confirmRemoval; + setConfirmRemoval(undefined); + if (instance) void removeProfile(instance); + }} + onClose={() => setConfirmRemoval(undefined)} + /> +
+ ); +} + +/** Convenience wrapper for a provider panel that only renders its list. */ +export function agentProfileListFor( + support: NativeAgentProfileSupport, +): (props: { onOpenProfile?: ((profileKind: string) => void) | undefined }) => ReactNode { + return (props) => ; +} diff --git a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.test.tsx b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.test.tsx index f8e54fec3..4af72d0fe 100644 --- a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.test.tsx +++ b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.test.tsx @@ -53,6 +53,7 @@ vi.mock("@/renderer/components/common", () => ({ onChange?: (event: { target: { value: string } }) => void; onFocus?: () => void; onBlur?: (event: unknown) => void; + onKeyDown?: (event: { key: string; preventDefault: () => void }) => void; }) => ( ({ onChange={props.onChange} onFocus={props.onFocus} onBlur={props.onBlur} + onKeyDown={props.onKeyDown} /> ), + PixelLoader: () => , + // The shared profile list confirms removals before touching settings. + ConfirmDialog: (props: { + isOpen: boolean; + title: string; + body: unknown; + confirmLabel: string; + onConfirm: () => void; + onClose: () => void; + }) => + props.isOpen ? ( +
+ +
+ ) : null, })); const refreshAgentStatusesMock = vi.hoisted(() => vi.fn<() => Promise>()); -const setClaudeProfileEnvironmentMock = vi.hoisted(() => +const setProfileEnvironmentMock = vi.hoisted(() => vi.fn<(payload: unknown) => Promise>(), ); +const createProfileMock = vi.hoisted(() => + vi.fn<(payload: { id: string; displayName: string }) => Promise>(), +); +const flushSharedSettingsMock = vi.hoisted(() => vi.fn<() => Promise>()); vi.mock("@/renderer/bridge", () => ({ readBridge: () => ({ refreshAgentStatuses: refreshAgentStatusesMock, - setClaudeProfileEnvironment: setClaudeProfileEnvironmentMock, + setProfileEnvironment: setProfileEnvironmentMock, + createProfile: createProfileMock, }), })); @@ -94,10 +118,21 @@ const statusState = { removeAgentStatus: vi.fn<(kind: string) => void>(), }; -vi.mock("@/renderer/state/sharedSettingsStore", () => ({ - useSharedSettings: (selector: (state: typeof settingsState) => unknown) => - selector(settingsState), -})); +vi.mock("@/renderer/state/sharedSettingsStore", () => { + // The shared profile list also reads the store imperatively (snapshot for the + // removal rollback) and flushes it, so the stub carries those entry points. + const useSharedSettings = ((selector: (state: typeof settingsState) => unknown) => + selector(settingsState)) as unknown as { + (selector: (state: typeof settingsState) => unknown): unknown; + getState: () => typeof settingsState; + setState: (patch: Partial) => void; + }; + useSharedSettings.getState = () => settingsState; + useSharedSettings.setState = (patch) => { + Object.assign(settingsState, patch); + }; + return { useSharedSettings, flushSharedSettings: flushSharedSettingsMock }; +}); vi.mock("@/renderer/state/agentStatusesStore", () => ({ useAgentStatusesStore: (selector: (state: typeof statusState) => unknown) => @@ -157,22 +192,23 @@ describe("ClaudeProfileSettings", () => { statusState.wslAgentStatuses = []; statusState.removeAgentStatus.mockReset(); refreshAgentStatusesMock.mockReset().mockResolvedValue(); - setClaudeProfileEnvironmentMock.mockReset().mockImplementation(async () => claudeProfile()); + setProfileEnvironmentMock.mockReset().mockImplementation(async () => claudeProfile()); + createProfileMock + .mockReset() + .mockImplementation(async ({ id, displayName }) => ({ id, driver: "claude", displayName })); + flushSharedSettingsMock.mockReset().mockResolvedValue(); toastMock.success.mockReset(); toastMock.danger.mockReset(); }); it("hides the add form until the add button is pressed", () => { render(); - expect(screen.queryByLabelText("New Claude profile name")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("New profile name")).not.toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - expect(screen.getByLabelText("New Claude profile name")).toHaveValue(""); - expect(screen.getByLabelText("New Claude profile name")).toHaveAttribute( - "placeholder", - "e.g. Work", - ); + expect(screen.getByLabelText("New profile name")).toHaveValue(""); + expect(screen.getByLabelText("New profile name")).toHaveAttribute("placeholder", "e.g. Work"); expect(screen.getByLabelText("New Claude profile config directory")).toHaveAttribute( "placeholder", "~/.poracode/claude-profiles/profile", @@ -183,10 +219,10 @@ describe("ClaudeProfileSettings", () => { render(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - const addButton = screen.getByRole("button", { name: "Add Claude profile" }); + const addButton = screen.getByRole("button", { name: "Create profile" }); expect(addButton).toBeDisabled(); - fireEvent.change(screen.getByLabelText("New Claude profile name"), { + fireEvent.change(screen.getByLabelText("New profile name"), { target: { value: "Work" }, }); @@ -197,38 +233,44 @@ describe("ClaudeProfileSettings", () => { ); }); - it("adds a profile with the derived config dir when the dir is left empty", () => { + it("adds a profile with the derived config dir when the dir is left empty", async () => { render(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - fireEvent.change(screen.getByLabelText("New Claude profile name"), { + fireEvent.change(screen.getByLabelText("New profile name"), { target: { value: "Work" }, }); - fireEvent.click(screen.getByRole("button", { name: "Add Claude profile" })); - - expect(settingsState.setAgentInstance).toHaveBeenCalledWith({ - id: "work", - driver: "claude", - displayName: "Work", - config: { configDir: "~/.poracode/claude-profiles/work" }, - }); + fireEvent.click(screen.getByRole("button", { name: "Create profile" })); + + // Every provider's profile is created through the one sealing main-local + // write, so a credential-carrying provider needs no separate path. + await vi.waitFor(() => + expect(createProfileMock).toHaveBeenCalledWith({ + driver: "claude", + id: "work", + displayName: "Work", + config: { configDir: "~/.poracode/claude-profiles/work" }, + }), + ); // The form collapses back to the add button after a successful add. - expect(screen.queryByLabelText("New Claude profile name")).not.toBeInTheDocument(); - expect(toastMock.success).toHaveBeenCalledWith("Claude Work profile added."); + await vi.waitFor(() => + expect(screen.queryByLabelText("New profile name")).not.toBeInTheDocument(), + ); + expect(toastMock.success).toHaveBeenCalledWith("Profile Work added."); }); it("discards the draft on cancel", () => { render(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - fireEvent.change(screen.getByLabelText("New Claude profile name"), { + fireEvent.change(screen.getByLabelText("New profile name"), { target: { value: "Work" }, }); - fireEvent.click(screen.getByRole("button", { name: "Cancel new Claude profile" })); + fireEvent.click(screen.getByRole("button", { name: "Cancel new profile" })); - expect(screen.queryByLabelText("New Claude profile name")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("New profile name")).not.toBeInTheDocument(); expect(settingsState.setAgentInstance).not.toHaveBeenCalled(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - expect(screen.getByLabelText("New Claude profile name")).toHaveValue(""); + expect(screen.getByLabelText("New profile name")).toHaveValue(""); }); it("opens a profile's own page from its row", () => { @@ -242,26 +284,33 @@ describe("ClaudeProfileSettings", () => { expect(onOpenProfile).toHaveBeenCalledWith("claude:glm"); }); - it("opens the new profile's page after adding", () => { + it("opens the new profile's page after adding", async () => { const onOpenProfile = vi.fn<(kind: string) => void>(); render(); fireEvent.click(screen.getByRole("button", { name: /add profile/i })); - fireEvent.change(screen.getByLabelText("New Claude profile name"), { + fireEvent.change(screen.getByLabelText("New profile name"), { target: { value: "Work" }, }); - fireEvent.click(screen.getByRole("button", { name: "Add Claude profile" })); + fireEvent.click(screen.getByRole("button", { name: "Create profile" })); - expect(onOpenProfile).toHaveBeenCalledWith("claude:work"); + await vi.waitFor(() => expect(onOpenProfile).toHaveBeenCalledWith("claude:work")); }); - it("removes a profile from settings and agent statuses", () => { + it("removes a profile only after the shared confirmation", async () => { settingsState.agentInstances = { glm: claudeProfile() }; render(); - fireEvent.click(screen.getByRole("button", { name: "Remove Claude profile" })); + fireEvent.click(screen.getByRole("button", { name: "Remove profile GLM" })); + // Claude profiles used to delete on the first click; the shared list makes + // every provider confirm first. + expect(settingsState.removeAgentInstance).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByRole("button", { name: "Remove" })); expect(settingsState.removeAgentInstance).toHaveBeenCalledWith("glm"); - expect(statusState.removeAgentStatus).toHaveBeenCalledWith("claude:glm"); + await vi.waitFor(() => + expect(statusState.removeAgentStatus).toHaveBeenCalledWith("claude:glm"), + ); }); }); @@ -270,7 +319,7 @@ describe("ClaudeProfileProviderSettings", () => { settingsState.agentInstances = { glm: claudeProfile() }; settingsState.setAgentInstance.mockReset(); refreshAgentStatusesMock.mockReset().mockResolvedValue(); - setClaudeProfileEnvironmentMock.mockReset().mockImplementation(async () => claudeProfile()); + setProfileEnvironmentMock.mockReset().mockImplementation(async () => claudeProfile()); toastMock.success.mockReset(); toastMock.danger.mockReset(); }); @@ -293,7 +342,7 @@ describe("ClaudeProfileProviderSettings", () => { fireEvent.click(screen.getByRole("button", { name: "Save Claude profile" })); - expect(setClaudeProfileEnvironmentMock).toHaveBeenCalledWith({ + expect(setProfileEnvironmentMock).toHaveBeenCalledWith({ instanceId: "glm", environment: { ANTHROPIC_BASE_URL: { value: "https://api.z.ai/api/anthropic" } }, }); @@ -466,7 +515,7 @@ describe("ClaudeProfileProviderSettings", () => { fireEvent.click(screen.getByRole("button", { name: "Save Claude profile" })); }); - expect(setClaudeProfileEnvironmentMock).toHaveBeenCalledWith({ + expect(setProfileEnvironmentMock).toHaveBeenCalledWith({ instanceId: "glm", environment: { ANTHROPIC_AUTH_TOKEN: { value: "lc-safe:v1:sealed", sensitive: true }, diff --git a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx index 543ac98d7..cb00f7d44 100644 --- a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx +++ b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx @@ -1,17 +1,6 @@ import { useRef, useState } from "react"; import { Button, Popover, toast } from "@heroui/react"; -import { - Check, - ChevronDown, - ChevronRight, - Lock, - LockOpen, - Plus, - RefreshCw, - Trash2, - Wand2, - X, -} from "lucide-react"; +import { Check, ChevronDown, Lock, LockOpen, Plus, Wand2, X } from "lucide-react"; import { msg } from "@lingui/core/macro"; import { Trans, useLingui } from "@lingui/react/macro"; import { @@ -29,6 +18,8 @@ import { formatEffortLabel } from "@/renderer/components/thread/threadDraftViewH import { useAgentStatusesStore } from "@/renderer/state/agentStatusesStore"; import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { currentWslDistros } from "@/renderer/utils/acpRegistryAuth"; +import { AgentProfileList } from "./AgentProfileList"; +import type { NativeAgentProfileSupport } from "./agentRegistryNative"; import { applyPresetEnvRows, cleanModels, @@ -44,7 +35,6 @@ import { SAVED_SECRET_MASK, selectedEffortsFromConfig, shouldTreatEnvKeyAsSensitive, - uniqueProfileId, type EnvRow, type ModelRow, type ProfilePreset, @@ -347,7 +337,7 @@ function ClaudeProfileEditor(props: { // Seal sensitive env in main first (returns the instance with sealed env), // then persist the non-secret config through the store. void readBridge() - .setClaudeProfileEnvironment({ instanceId: props.instance.id, environment }) + .setProfileEnvironment({ instanceId: props.instance.id, environment }) .then((updated) => { setAgentInstance({ ...updated, displayName: trimmedName, config }); setEnvRows(rowsFromEnvironment(updated.environment, nextRowId)); @@ -603,228 +593,64 @@ function ClaudeProfileEditor(props: { ); } -// ── Simple profile list (rendered on the base "Claude Code" page) ──────────── +// ── Profile list descriptor (rendered on the base "Claude Code" page) ──────── -function ClaudeProfileRow(props: { - instance: AgentInstanceConfig; - config: ClaudeProfileInstanceConfig; - onOpen: () => void; - onRemove: (id: string) => void; -}) { - const { t } = useLingui(); - const label = props.instance.displayName ?? props.instance.id; +/** + * Claude profiles are distinguished by their config directory, and optionally + * point at an external provider through their env vars. + */ +function ClaudeProfileConfigDir(props: { instance: AgentInstanceConfig }) { + let config: ClaudeProfileInstanceConfig | undefined; + try { + config = parseClaudeProfileInstanceConfig(props.instance.config); + } catch { + // Malformed records are skipped by the supervisor too; show the id only. + config = undefined; + } return ( -
- -
- - -
-
+ ) : null} + {config?.configDir ?? props.instance.id} +
); } +export const claudeProfileSupport: NativeAgentProfileSupport = { + driver: "claude", + description: ( + + Separate Claude Code accounts by config directory, or point a profile at an external provider + (z.ai, …). Open a profile to configure its env vars, models, and effort. + + ), + field: { + ariaLabel: msg`New Claude profile config directory`, + // Live default shown as the placeholder and used verbatim when left empty. + placeholderFor: (name) => defaultConfigDir(name), + }, + RowSubtitle: ClaudeProfileConfigDir, + removalBody: (profileName) => ( + + Removing {profileName} drops its Poracode settings — env vars, models, and effort. Its config + directory and the Claude credentials inside it stay on disk. + + ), + createPayload: ({ id, displayName, field }) => ({ + driver: "claude", + id, + displayName, + config: { configDir: field }, + }), +}; + export function ClaudeProfileSettings(props: { onOpenProfile?: ((profileKind: string) => void) | undefined; }) { - const { t } = useLingui(); - const agentInstances = useSharedSettings((s) => s.agentInstances ?? {}); - const setAgentInstance = useSharedSettings((s) => s.setAgentInstance); - const removeAgentInstance = useSharedSettings((s) => s.removeAgentInstance); - const removeAgentStatus = useAgentStatusesStore((s) => s.removeAgentStatus); - const [isAdding, setIsAdding] = useState(false); - const [newName, setNewName] = useState(""); - const [newConfigDir, setNewConfigDir] = useState(""); - - const profiles: Array<{ instance: AgentInstanceConfig; config: ClaudeProfileInstanceConfig }> = - []; - for (const instance of Object.values(agentInstances)) { - if (instance.driver !== "claude") continue; - try { - const config = parseClaudeProfileInstanceConfig(instance.config); - profiles.push({ instance, config }); - } catch { - // Ignore malformed records here; the supervisor skips them too. - } - } - profiles.sort((a, b) => - (a.instance.displayName ?? a.instance.id).localeCompare( - b.instance.displayName ?? b.instance.id, - ), - ); - - const canAdd = newName.trim().length > 0; - // Live default shown as the dir placeholder; used verbatim when left empty. - const suggestedConfigDir = defaultConfigDir(newName); - - function closeAddForm(): void { - setIsAdding(false); - setNewName(""); - setNewConfigDir(""); - } - - function addProfile(): void { - const displayName = newName.trim(); - const configDir = newConfigDir.trim() || suggestedConfigDir; - if (!displayName) return; - const id = uniqueProfileId(displayName, agentInstances); - const instance: AgentInstanceConfig = { - id, - driver: "claude", - displayName, - config: { configDir }, - }; - setAgentInstance(instance); - refreshClaudeProfile(claudeProfileKind(id)); - closeAddForm(); - toast.success(t`Claude ${displayName} profile added.`); - // Open the new profile's page so it can be pointed at an external provider. - props.onOpenProfile?.(claudeProfileKind(id)); - } - - return ( -
-
-
-

- Profiles -

-

- - Separate Claude Code accounts by config directory, or point a profile at an external - provider (z.ai, …). Open a profile to configure its env vars, models, and effort. - -

-
- -
- - {profiles.length === 0 && !isAdding ? ( -

- No additional Claude profiles. -

- ) : null} - -
- {profiles.map(({ instance, config }) => ( - props.onOpenProfile?.(claudeProfileKind(instance.id))} - onRemove={(id) => { - const kind = claudeProfileKind(id); - removeAgentInstance(id); - removeAgentStatus(kind); - refreshClaudeProfile(); - toast.success(t`Claude profile removed.`); - }} - /> - ))} - - {isAdding ? ( -
- node?.focus()} - aria-label={t`New Claude profile name`} - className="min-w-0" - placeholder={t`e.g. Work`} - value={newName} - onChange={(event) => setNewName(event.target.value)} - /> - setNewConfigDir(event.target.value)} - /> - {/* Icon-only actions matching the saved rows' action pair, so the - action column keeps the same width when the draft opens. */} -
- - -
-
- ) : null} -
- - {!isAdding ? ( - - ) : null} -
- ); + return ; } /** diff --git a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettingsModel.ts b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettingsModel.ts index 7d7448e16..7878bcd25 100644 --- a/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettingsModel.ts +++ b/src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettingsModel.ts @@ -8,6 +8,9 @@ import type { ClaudeProfileModel, } from "@/shared/contracts"; import { isEncryptedSecret } from "@/shared/secretFormat"; +import { slugifyProfileName } from "./profileIds"; + +export { slugifyProfileName, uniqueProfileId } from "./profileIds"; export const SAVED_SECRET_MASK = "••••••••"; @@ -228,31 +231,10 @@ export interface ModelRow { efforts?: Set; } -export function slugifyProfileName(value: string): string { - return ( - value - .trim() - .toLowerCase() - .replace(/[^a-z0-9]+/gu, "-") - .replace(/^-+|-+$/gu, "") || "profile" - ); -} - export function defaultConfigDir(name: string): string { return `~/.poracode/claude-profiles/${slugifyProfileName(name)}`; } -export function uniqueProfileId(name: string, existing: Readonly>): string { - const base = slugifyProfileName(name); - let candidate = base; - let index = 2; - while (existing[candidate]) { - candidate = `${base}-${index}`; - index += 1; - } - return candidate; -} - export function shouldTreatEnvKeyAsSensitive(key: string): boolean { return SENSITIVE_KEY_RE.test(key); } diff --git a/src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx b/src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx new file mode 100644 index 000000000..10f8c75e8 --- /dev/null +++ b/src/renderer/views/SettingsOverlay/parts/CursorProfileIdentity.tsx @@ -0,0 +1,125 @@ +import { useEffect, useState } from "react"; +import { Button, toast } from "@heroui/react"; +import { Check } from "lucide-react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { friendlyError } from "@/shared/messages"; +import { Input, PixelLoader } from "@/renderer/components/common"; +import { flushSharedSettings, useSharedSettings } from "@/renderer/state/sharedSettingsStore"; +import { CursorRuntimeCardRow } from "./CursorRuntimeCard"; +import { CursorProfileApiKeySetup } from "./CursorSdkRuntimeSetup"; +import { isDuplicateProfileName } from "./profileIds"; + +/** + * A Cursor profile's own identity: the name every surface labels it with, and + * the API key each runtime spawns under. Lives outside + * `CursorProviderSettings` so the runtime cards stay that file's single + * concern. The instance id (and therefore the agent kind) is deliberately + * immutable — threads reference it — so a rename only moves `displayName`. + */ +export function CursorProfileIdentity(props: { + agentKind: string; + profileInstanceId: string; + /** Detected auth state for this profile's key, rendered on the key row. */ + authDescription: string; + refreshStatus: () => Promise; +}) { + const { t } = useLingui(); + const instance = useSharedSettings((state) => state.agentInstances[props.profileInstanceId]); + const agentInstances = useSharedSettings((state) => state.agentInstances); + const setAgentInstance = useSharedSettings((state) => state.setAgentInstance); + const savedName = instance?.displayName ?? props.profileInstanceId; + const [name, setName] = useState(savedName); + const [pending, setPending] = useState(false); + + // Re-seed when the settings page switches to a different profile, or when the + // saved name changes out-of-band (another window, the MCP tool). + useEffect(() => { + setName(savedName); + }, [savedName]); + + const trimmed = name.trim(); + const cursorInstances = Object.values(agentInstances).filter( + (candidate) => candidate.driver === "cursor", + ); + const isDuplicate = isDuplicateProfileName(trimmed, cursorInstances, props.profileInstanceId); + const canSave = trimmed.length > 0 && trimmed !== savedName && !isDuplicate && !pending; + + const saveName = async () => { + if (!canSave || !instance) return; + setPending(true); + const previous = instance; + try { + setAgentInstance({ ...instance, displayName: trimmed }); + // The adapter builds its label from `displayName`, so the sidebar and + // model picker only pick the new name up after a detection refresh. + await flushSharedSettings(); + await props.refreshStatus(); + toast.success(t`Cursor profile renamed.`); + } catch (error) { + setAgentInstance(previous); + setName(previous.displayName ?? props.profileInstanceId); + toast.danger(friendlyError(error)); + } finally { + setPending(false); + } + }; + + if (!instance) return null; + + return ( +
+

+ Profile +

+

+ The API key below is used by Cursor CLI, ACP, and SDK for this profile. +

+
+ + A Cursor profile with this name already exists. + + ) : ( + Shown in the sidebar and the model picker. + ) + } + stacked + > +
+ setName(event.currentTarget.value)} + onKeyDown={(event) => { + if (event.key !== "Enter") return; + event.preventDefault(); + void saveName(); + }} + /> + +
+
+ +
+
+ ); +} diff --git a/src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx b/src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx new file mode 100644 index 000000000..f0a274de3 --- /dev/null +++ b/src/renderer/views/SettingsOverlay/parts/CursorProfileSettings.tsx @@ -0,0 +1,61 @@ +import { msg } from "@lingui/core/macro"; +import { Trans, useLingui } from "@lingui/react/macro"; +import type { AgentInstanceConfig, AgentStatus } from "@/shared/contracts"; +import { cursorProfileKind } from "@/shared/contracts"; +import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; +import type { NativeAgentProfileSupport } from "./agentRegistryNative"; +import { cursorRuntimeInstallState } from "./cursorRuntimeInstall"; + +/** + * Every Cursor profile has its own key, so naming that in the list would say + * nothing; the runtime it launches on is what differs. A profile with no saved + * choice runs on the SDK — the supervisor's default. + */ +function CursorProfileRuntimeLabel(props: { instance: AgentInstanceConfig }) { + const { t } = useLingui(); + const saved = useSharedSettings( + (state) => state.agentSettings[cursorProfileKind(props.instance.id)], + ); + return saved?.structuredRuntime === "acp" ? t`Cursor CLI (ACP)` : t`Cursor SDK`; +} + +/** + * Cursor's profile descriptor: one API key per profile, carried into the CLI, + * ACP and SDK runtimes. Everything structural lives in `AgentProfileList`. + */ +export const cursorProfileSupport: NativeAgentProfileSupport = { + driver: "cursor", + description: Create separate Cursor profiles with their own API keys., + field: { + ariaLabel: msg`New Cursor profile API key`, + placeholder: msg`Paste Cursor API key`, + secret: true, + required: true, + }, + RowSubtitle: CursorProfileRuntimeLabel, + removalBody: (profileName) => ( + + Removing {profileName} deletes its saved API key. Threads that use this profile will no longer + find their agent. + + ), + createPayload: ({ id, displayName, field }) => ({ + driver: "cursor", + id, + displayName, + environment: { CURSOR_API_KEY: { value: field, sensitive: true } }, + }), + onCreated: ({ profileKind, statuses }) => { + // Pin the runtime rather than leaning on the implicit SDK default: on a + // machine without `@cursor/sdk` that default leaves the profile pointing at + // a runtime it cannot start, with nothing in the UI explaining why. The + // package is shared by every Cursor kind, so the base provider's detection + // answers whether the SDK is available. + const sdkInstalled = statuses.some( + (status: AgentStatus) => cursorRuntimeInstallState(status).sdkInstalled, + ); + useSharedSettings + .getState() + .setAgentSetting(profileKind, "structuredRuntime", sdkInstalled ? "sdk" : "acp"); + }, +}; diff --git a/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.test.tsx b/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.test.tsx index ba7571d35..adc19b283 100644 --- a/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.test.tsx +++ b/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.test.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, screen } from "@testing-library/react"; -import type { ChangeEvent, ReactNode } from "react"; +import type { ChangeEvent, KeyboardEvent, ReactNode } from "react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; @@ -83,33 +83,76 @@ vi.mock("@heroui/react", async () => { }; }); -vi.mock("@/renderer/components/common", () => ({ - Input: (props: { - "aria-label"?: string; - value: string; - disabled?: boolean; - placeholder?: string; - onChange: (event: ChangeEvent) => void; - onFocus?: () => void; - onBlur?: () => void; - }) => ( - - ), - PixelLoader: () => , -})); +vi.mock("@/renderer/components/common", async () => { + const React = await import("react"); + return { + Input: React.forwardRef< + HTMLInputElement, + { + "aria-label"?: string; + value: string; + disabled?: boolean; + placeholder?: string; + onChange: (event: ChangeEvent) => void; + onFocus?: () => void; + onBlur?: () => void; + onKeyDown?: (event: KeyboardEvent) => void; + } + >((props, ref) => ( + + )), + PixelLoader: () => , + // Destructive profile removal routes through the shared confirm dialog, so + // the stub renders its real actions instead of swallowing them. + ConfirmDialog: (props: { + isOpen: boolean; + title: string; + body: ReactNode; + confirmLabel: string; + onConfirm: () => void; + onClose: () => void; + }) => + props.isOpen ? ( +
+
{props.body}
+ + +
+ ) : null, + }; +}); const bridgeMock = vi.hoisted(() => ({ refreshAgentStatuses: vi.fn<(...args: unknown[]) => Promise>(), getLatestAgentVersion: vi.fn<(payload: unknown) => Promise<{ version?: string; source?: string }>>(), + setProfileEnvironment: + vi.fn< + (payload: { instanceId: string; environment: unknown }) => Promise + >(), + createProfile: + vi.fn< + (payload: { + driver: string; + id: string; + displayName: string; + environment?: unknown; + }) => Promise + >(), })); const flushSharedSettingsMock = vi.hoisted(() => vi.fn<() => Promise>()); const setAgentSettingMock = vi.hoisted(() => @@ -118,6 +161,8 @@ const setAgentSettingMock = vi.hoisted(() => const setAgentSecretSettingMock = vi.hoisted(() => vi.fn<(agentKind: string, key: string, value: string) => Promise>(), ); +const setAgentInstanceMock = vi.hoisted(() => vi.fn<(instance: AgentInstanceConfig) => void>()); +const removeAgentInstanceMock = vi.hoisted(() => vi.fn<(instanceId: string) => void>()); const runAgentInstallCommandMock = vi.hoisted(() => vi.fn< (input: { @@ -148,7 +193,7 @@ vi.mock("@/renderer/state/sharedSettingsStore", async (importOriginal) => { import { useSharedSettings } from "@/renderer/state/sharedSettingsStore"; import { CursorProviderSettings } from "./CursorProviderSettings"; import { NATIVE_AGENT_REGISTRY_ENTRIES } from "./agentRegistryNative"; -import type { AgentCapability, AgentStatus } from "@/shared/contracts"; +import type { AgentCapability, AgentInstanceConfig, AgentStatus } from "@/shared/contracts"; const runtimeCapabilities: AgentCapability = { models: [ @@ -207,16 +252,33 @@ beforeEach(() => { bridgeMock.getLatestAgentVersion .mockReset() .mockResolvedValue({ version: "1.0.31", source: "npm" }); + bridgeMock.setProfileEnvironment.mockReset().mockImplementation(async ({ instanceId }) => ({ + id: instanceId, + driver: "cursor", + displayName: "Work", + environment: { CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true } }, + })); + bridgeMock.createProfile.mockReset().mockImplementation(async ({ id, displayName }) => ({ + id, + driver: "cursor", + displayName, + environment: { CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true } }, + })); flushSharedSettingsMock.mockReset().mockResolvedValue(undefined); setAgentSettingMock.mockReset(); setAgentSecretSettingMock.mockReset().mockResolvedValue(true); + setAgentInstanceMock.mockReset(); + removeAgentInstanceMock.mockReset(); runAgentInstallCommandMock.mockReset().mockReturnValue(true); toastMock.danger.mockReset(); toastMock.success.mockReset(); useSharedSettings.setState({ agentSettings: {}, + agentInstances: {}, setAgentSetting: setAgentSettingMock, setAgentSecretSetting: setAgentSecretSettingMock, + setAgentInstance: setAgentInstanceMock, + removeAgentInstance: removeAgentInstanceMock, }); }); @@ -285,6 +347,314 @@ describe("CursorProviderSettings", () => { expect(screen.queryByText(/package path/i)).not.toBeInTheDocument(); }); + it("lists Cursor profiles under the base provider and opens their settings", () => { + const onOpenProfile = vi.fn<(kind: string) => void>(); + useSharedSettings.setState({ + agentInstances: { + work: { + id: "work", + driver: "cursor", + displayName: "Work", + environment: { + CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true }, + }, + }, + }, + }); + + render( + , + ); + + const openButton = screen.getByRole("button", { name: "Open Work" }); + const chevron = openButton.querySelector("svg"); + expect(chevron).not.toBeNull(); + fireEvent.click(chevron!); + expect(onOpenProfile).toHaveBeenCalledWith("cursor:work"); + expect(screen.getAllByRole("button", { name: "Open Work" })).toHaveLength(1); + }); + + it("adds a Cursor profile with a required encrypted API key", async () => { + const onOpenProfile = vi.fn<(kind: string) => void>(); + setAgentInstanceMock.mockImplementation((instance) => { + useSharedSettings.setState((state) => ({ + agentInstances: { ...state.agentInstances, [instance.id]: instance }, + })); + }); + let resolveRefresh!: () => void; + bridgeMock.refreshAgentStatuses.mockReturnValueOnce( + new Promise((resolve) => { + resolveRefresh = resolve; + }), + ); + useSharedSettings.setState({ + agentInstances: {}, + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + fireEvent.change(screen.getByLabelText("New profile name"), { + target: { value: "Work" }, + }); + fireEvent.change(screen.getByLabelText("New Cursor profile API key"), { + target: { value: " profile-key " }, + }); + fireEvent.click(screen.getByRole("button", { name: "Create profile" })); + + await vi.waitFor(() => { + expect(bridgeMock.createProfile).toHaveBeenCalledWith({ + driver: "cursor", + id: "work", + displayName: "Work", + environment: { CURSOR_API_KEY: { value: "profile-key", sensitive: true } }, + }); + }); + const openProfile = screen.getByRole("button", { name: "Open Work" }); + expect(openProfile).toBeDisabled(); + expect(screen.getByRole("button", { name: "Remove profile Work" })).toBeDisabled(); + fireEvent.click(openProfile); + expect(onOpenProfile).not.toHaveBeenCalled(); + resolveRefresh(); + await vi.waitFor(() => expect(onOpenProfile).toHaveBeenCalledWith("cursor:work")); + // The key itself never rides the plaintext renderer flush; the pinned + // runtime does, so a new profile never inherits an unusable default. + expect(bridgeMock.createProfile).toHaveBeenCalledTimes(1); + expect(setAgentSettingMock).toHaveBeenCalledWith("cursor:work", "structuredRuntime", "sdk"); + expect(flushSharedSettingsMock).toHaveBeenCalled(); + expect(setAgentInstanceMock).toHaveBeenCalledWith( + expect.objectContaining({ id: "work", driver: "cursor", displayName: "Work" }), + ); + }); + + it("does not offer a fake cancellation after profile creation starts", async () => { + let resolveCreate!: (instance: AgentInstanceConfig) => void; + bridgeMock.createProfile.mockReturnValueOnce( + new Promise((resolve) => { + resolveCreate = resolve; + }), + ); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + fireEvent.change(screen.getByLabelText("New profile name"), { target: { value: "Work" } }); + fireEvent.change(screen.getByLabelText("New Cursor profile API key"), { + target: { value: "profile-key" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Create profile" })); + + await vi.waitFor(() => expect(bridgeMock.createProfile).toHaveBeenCalledOnce()); + expect(screen.getByRole("button", { name: "Cancel new profile" })).toBeDisabled(); + expect(screen.getByLabelText("New profile name")).toBeDisabled(); + + resolveCreate({ id: "work", driver: "cursor", displayName: "Work" }); + await vi.waitFor(() => expect(screen.queryByLabelText("New profile name")).toBeNull()); + }); + + it("gives each Cursor profile remove button a distinct accessible name", () => { + useSharedSettings.setState({ + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + personal: { id: "personal", driver: "cursor", displayName: "Personal" }, + }, + }); + + render( + , + ); + + expect(screen.getByRole("button", { name: "Remove profile Work" })).toBeTruthy(); + expect(screen.getByRole("button", { name: "Remove profile Personal" })).toBeTruthy(); + }); + + it("persists profile removal before refreshing the supervisor registry", async () => { + let resolveFlush!: () => void; + flushSharedSettingsMock.mockReturnValueOnce( + new Promise((resolve) => { + resolveFlush = resolve; + }), + ); + useSharedSettings.setState({ + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + }, + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Remove profile Work" })); + fireEvent.click(screen.getByRole("button", { name: "Remove" })); + expect(removeAgentInstanceMock).toHaveBeenCalledWith("work"); + expect(bridgeMock.refreshAgentStatuses).not.toHaveBeenCalled(); + + resolveFlush(); + await vi.waitFor(() => expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalled()); + }); + + it("restores profile-scoped settings when removal persistence fails", async () => { + flushSharedSettingsMock.mockRejectedValueOnce(new Error("write failed")); + // Mirror the store action's synchronous slice removal so the rollback has + // something real to restore. + removeAgentInstanceMock.mockImplementation((instanceId: string) => { + useSharedSettings.setState((state) => { + const { [instanceId]: _removed, ...agentInstances } = state.agentInstances; + const prefix = `cursor:${instanceId}`; + const dropProfileKey = >(values: T): T => + Object.fromEntries(Object.entries(values).filter(([key]) => key !== prefix)) as T; + return { + agentInstances, + agentSettings: dropProfileKey(state.agentSettings), + providerOrder: state.providerOrder.filter((kind) => kind !== prefix), + }; + }); + }); + useSharedSettings.setState({ + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + }, + agentSettings: { "cursor:work": { structuredRuntime: "sdk" } }, + providerOrder: ["cursor", "cursor:work"], + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Remove profile Work" })); + fireEvent.click(screen.getByRole("button", { name: "Remove" })); + + await vi.waitFor(() => expect(toastMock.danger).toHaveBeenCalledWith("write failed")); + const state = useSharedSettings.getState(); + expect(state.agentInstances.work?.displayName).toBe("Work"); + expect(state.agentSettings["cursor:work"]).toEqual({ structuredRuntime: "sdk" }); + expect(state.providerOrder).toEqual(["cursor", "cursor:work"]); + }); + + it("reports a removal refresh failure without claiming ordinary success", async () => { + bridgeMock.refreshAgentStatuses.mockRejectedValueOnce(new Error("refresh failed")); + useSharedSettings.setState({ + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + }, + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Remove profile Work" })); + fireEvent.click(screen.getByRole("button", { name: "Remove" })); + + await vi.waitFor(() => + expect(toastMock.danger).toHaveBeenCalledWith( + "Profile removed, but statuses could not be refreshed.", + ), + ); + expect(toastMock.success).not.toHaveBeenCalledWith("Profile removed."); + }); + + it("keeps API-key focus while typing in the add form", () => { + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + const apiKey = screen.getByLabelText("New Cursor profile API key"); + apiKey.focus(); + fireEvent.change(apiKey, { target: { value: "profile-key" } }); + + expect(apiKey).toHaveFocus(); + expect(apiKey).toHaveValue("profile-key"); + }); + + it("replaces a Cursor profile key through the profile secret path", async () => { + useSharedSettings.setState({ + agentInstances: { + work: { + id: "work", + driver: "cursor", + displayName: "Work", + environment: { + CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true }, + }, + }, + }, + }); + const profileStatus = { ...runtimeStatus, kind: "cursor:work", label: "Cursor Work" }; + + render( + , + ); + + const field = screen.getByLabelText("Cursor profile API key"); + expect(field).toHaveValue("***********"); + expect( + screen.getByText("The API key below is used by Cursor CLI, ACP, and SDK for this profile."), + ).toBeTruthy(); + expect(screen.getByRole("radiogroup", { name: "Structured runtime" }).contains(field)).toBe( + false, + ); + fireEvent.focus(field); + fireEvent.change(field, { target: { value: " replacement-key " } }); + fireEvent.click(screen.getByRole("button", { name: "Save Cursor profile API key" })); + + await vi.waitFor(() => { + expect(bridgeMock.setProfileEnvironment).toHaveBeenCalledWith({ + instanceId: "work", + environment: { CURSOR_API_KEY: { value: "replacement-key", sensitive: true } }, + }); + expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalledWith([], { + agentKinds: ["cursor:work"], + }); + }); + expect(screen.queryByRole("button", { name: "Remove Cursor SDK API key" })).toBeNull(); + }); + + it("confirms a saved profile key even when status refresh fails", async () => { + bridgeMock.refreshAgentStatuses.mockRejectedValueOnce(new Error("refresh failed")); + useSharedSettings.setState({ + agentInstances: { + work: { + id: "work", + driver: "cursor", + displayName: "Work", + environment: { + CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true }, + }, + }, + }, + }); + render( + , + ); + + const field = screen.getByLabelText("Cursor profile API key"); + fireEvent.focus(field); + fireEvent.change(field, { target: { value: "replacement-key" } }); + fireEvent.click(screen.getByRole("button", { name: "Save Cursor profile API key" })); + + await vi.waitFor(() => { + expect(toastMock.success).toHaveBeenCalledWith("Cursor profile API key saved."); + expect(toastMock.danger).toHaveBeenCalledWith("refresh failed"); + }); + }); + it("replaces the masked key on focus and restores the mask when left empty", () => { useSharedSettings.setState({ agentSettings: { cursor: { structuredRuntime: "sdk", sdkApiKey: "lc-safe:encrypted" } }, @@ -357,6 +727,18 @@ describe("CursorProviderSettings", () => { }); it("shows the separately detected SDK version and runs its package update", async () => { + useSharedSettings.setState({ + agentInstances: { + work: { + id: "work", + driver: "cursor", + displayName: "Work", + environment: { + CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true }, + }, + }, + }, + }); render( , ); @@ -377,7 +759,11 @@ describe("CursorProviderSettings", () => { ); const input = runAgentInstallCommandMock.mock.calls[0]![0]; await act(async () => input.onCommandComplete?.(0)); - await vi.waitFor(() => expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalled()); + await vi.waitFor(() => + expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalledWith([], { + agentKinds: ["cursor", "cursor:work"], + }), + ); }); it("hides the SDK update button when the newest supported release is installed", async () => { @@ -471,6 +857,47 @@ describe("CursorProviderSettings", () => { expect(setAgentSettingMock).toHaveBeenLastCalledWith("cursor", "structuredRuntime", "acp"); }); + it("persists a profile runtime choice under the profile kind", async () => { + render( + , + ); + + fireEvent.click(screen.getByRole("radio", { name: /ACP/ })); + + await vi.waitFor(() => { + expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalledWith([], { + agentKinds: ["cursor:work"], + }); + }); + expect(setAgentSettingMock).toHaveBeenCalledWith("cursor:work", "structuredRuntime", "acp"); + expect(flushSharedSettingsMock.mock.invocationCallOrder[0]).toBeLessThan( + bridgeMock.refreshAgentStatuses.mock.invocationCallOrder[0]!, + ); + }); + + it("restores a profile runtime choice when the settings flush fails", async () => { + flushSharedSettingsMock.mockRejectedValueOnce(new Error("write failed")); + render( + , + ); + + fireEvent.click(screen.getByRole("radio", { name: /ACP/ })); + + await act(async () => { + await vi.waitFor(() => expect(toastMock.danger).toHaveBeenCalled()); + }); + expect(bridgeMock.refreshAgentStatuses).not.toHaveBeenCalled(); + expect(setAgentSettingMock).toHaveBeenLastCalledWith("cursor:work", "structuredRuntime", "sdk"); + }); + it("saves an SDK API key through the encrypted setting path and refreshes detection", async () => { const missingSdkAuth: AgentStatus = { ...runtimeStatus, @@ -503,4 +930,168 @@ describe("CursorProviderSettings", () => { expect(screen.getByRole("radio", { name: /SDK/ })).toBeDisabled(); expect(toastMock.success).toHaveBeenCalledWith("Cursor SDK API key saved."); }); + it("submits the add form from Enter in either field", async () => { + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + fireEvent.change(screen.getByLabelText("New profile name"), { + target: { value: "Work" }, + }); + const apiKey = screen.getByLabelText("New Cursor profile API key"); + fireEvent.change(apiKey, { target: { value: "profile-key" } }); + fireEvent.keyDown(apiKey, { key: "Enter" }); + + await vi.waitFor(() => + expect(bridgeMock.createProfile).toHaveBeenCalledWith({ + driver: "cursor", + id: "work", + displayName: "Work", + environment: { CURSOR_API_KEY: { value: "profile-key", sensitive: true } }, + }), + ); + }); + + it("blocks a duplicate profile name instead of creating a second identical row", () => { + useSharedSettings.setState({ + agentInstances: { work: { id: "work", driver: "cursor", displayName: "Work" } }, + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + fireEvent.change(screen.getByLabelText("New profile name"), { + target: { value: " work " }, + }); + fireEvent.change(screen.getByLabelText("New Cursor profile API key"), { + target: { value: "profile-key" }, + }); + + expect(screen.getByText("A profile with this name already exists.")).toBeTruthy(); + expect(screen.getByRole("button", { name: "Create profile" })).toBeDisabled(); + fireEvent.keyDown(screen.getByLabelText("New Cursor profile API key"), { key: "Enter" }); + expect(bridgeMock.createProfile).not.toHaveBeenCalled(); + }); + + it("pins the ACP runtime for a new profile when @cursor/sdk is missing", async () => { + const withoutSdk: AgentStatus = { + ...runtimeStatus, + runtimeVariants: { + ...runtimeStatus.runtimeVariants, + sdk: { ...runtimeStatus.runtimeVariants!.sdk!, installed: false, authState: "missing" }, + }, + }; + render(); + + fireEvent.click(screen.getByRole("button", { name: "Add profile" })); + fireEvent.change(screen.getByLabelText("New profile name"), { + target: { value: "Work" }, + }); + fireEvent.change(screen.getByLabelText("New Cursor profile API key"), { + target: { value: "profile-key" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Create profile" })); + + await vi.waitFor(() => + expect(setAgentSettingMock).toHaveBeenCalledWith("cursor:work", "structuredRuntime", "acp"), + ); + }); + + it("keeps a profile until its removal is confirmed", () => { + useSharedSettings.setState({ + agentInstances: { work: { id: "work", driver: "cursor", displayName: "Work" } }, + }); + render( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "Remove profile Work" })); + expect(removeAgentInstanceMock).not.toHaveBeenCalled(); + expect(screen.getByRole("alertdialog", { name: "Remove profile?" })).toBeTruthy(); + + fireEvent.click(screen.getByRole("button", { name: "Cancel" })); + expect(removeAgentInstanceMock).not.toHaveBeenCalled(); + expect(screen.getByRole("button", { name: "Remove profile Work" })).toBeTruthy(); + }); + + it("labels each profile row with the runtime it launches on", () => { + useSharedSettings.setState({ + agentInstances: { + work: { id: "work", driver: "cursor", displayName: "Work" }, + personal: { id: "personal", driver: "cursor", displayName: "Personal" }, + }, + agentSettings: { "cursor:personal": { structuredRuntime: "acp" } }, + }); + render( + , + ); + + // "Personal" pinned ACP; "Work" has no saved choice and follows the + // supervisor's SDK default. + const rowText = screen.getAllByRole("button").map((button) => button.textContent); + expect(rowText).toContain("PersonalCursor CLI (ACP)"); + expect(rowText).toContain("WorkCursor SDK"); + }); + + it("renames a profile and refreshes so every surface relabels", async () => { + useSharedSettings.setState({ + agentInstances: { work: { id: "work", driver: "cursor", displayName: "Work" } }, + }); + const profileStatus = { ...runtimeStatus, kind: "cursor:work", label: "Cursor Work" }; + render( + , + ); + + const nameField = screen.getByLabelText("Cursor profile name"); + expect(nameField).toHaveValue("Work"); + fireEvent.change(nameField, { target: { value: " Day job " } }); + fireEvent.keyDown(nameField, { key: "Enter" }); + + await vi.waitFor(() => + expect(toastMock.success).toHaveBeenCalledWith("Cursor profile renamed."), + ); + expect(setAgentInstanceMock).toHaveBeenCalledWith( + expect.objectContaining({ id: "work", displayName: "Day job" }), + ); + expect(bridgeMock.refreshAgentStatuses).toHaveBeenCalledWith([], { + agentKinds: ["cursor:work"], + }); + }); + + it("reports the profile key's detected auth state instead of restating the section", () => { + useSharedSettings.setState({ + agentInstances: { + work: { + id: "work", + driver: "cursor", + displayName: "Work", + environment: { CURSOR_API_KEY: { value: "lc-safe:encrypted", sensitive: true } }, + }, + }, + }); + // The SDK variant is the one that authenticates with the key itself; ACP + // stays "authenticated" here precisely because it can fall back to the + // machine login, which must not make a bad profile key look fine. + const unauthenticated: AgentStatus = { + ...runtimeStatus, + kind: "cursor:work", + label: "Cursor Work", + runtimeVariants: { + ...runtimeStatus.runtimeVariants, + sdk: { ...runtimeStatus.runtimeVariants!.sdk!, authState: "missing" }, + }, + }; + render( + , + ); + + expect(screen.queryByText("Separate API key")).toBeNull(); + expect(screen.getAllByText("API key required").length).toBeGreaterThan(0); + }); }); diff --git a/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx b/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx index ed17bc1c8..b6999fa0e 100644 --- a/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx +++ b/src/renderer/views/SettingsOverlay/parts/CursorProviderSettings.tsx @@ -3,9 +3,18 @@ import { RadioGroup, toast } from "@heroui/react"; import { Trans, useLingui } from "@lingui/react/macro"; import { readBridge } from "@/renderer/bridge"; import { flushSharedSettings, useSharedSettings } from "@/renderer/state/sharedSettingsStore"; -import type { AgentCapability, AgentStatus, AuthState } from "@/shared/contracts"; +import { + cursorProfileKind, + extractCursorProfileInstanceId, + type AgentCapability, + type AgentStatus, + type AuthState, +} from "@/shared/contracts"; import { friendlyError } from "@/shared/messages"; import { CursorRuntimeCard } from "./CursorRuntimeCard"; +import { AgentProfileList } from "./AgentProfileList"; +import { CursorProfileIdentity } from "./CursorProfileIdentity"; +import { cursorProfileSupport } from "./CursorProfileSettings"; import { CursorSdkRuntimeSetup } from "./CursorSdkRuntimeSetup"; import { cursorRuntimeInstallState } from "./cursorRuntimeInstall"; @@ -13,8 +22,11 @@ type CursorStructuredRuntime = "acp" | "sdk"; function readStructuredRuntime( settings: Record | undefined, + fallback: CursorStructuredRuntime = "acp", ): CursorStructuredRuntime { - return settings?.structuredRuntime === "sdk" ? "sdk" : "acp"; + if (settings?.structuredRuntime === "sdk") return "sdk"; + if (settings?.structuredRuntime === "acp") return "acp"; + return fallback; } /** @@ -31,11 +43,20 @@ export function CursorProviderSettings(props: { wslDistros: string[]; /** Per-environment Cursor CLI rows, handed over by `SingleAgentSettings`. */ installRows?: ReactNode; + onOpenProfile?: ((profileKind: string) => void) | undefined; }) { const { t } = useLingui(); const { agentKind, statuses, wslDistros } = props; + const profileInstanceId = extractCursorProfileInstanceId(agentKind); const savedAgentSettings = useSharedSettings((state) => state.agentSettings[agentKind]); - const selectedRuntime = readStructuredRuntime(savedAgentSettings); + const agentInstances = useSharedSettings((state) => state.agentInstances); + const cursorProfileKinds = Object.values(agentInstances) + .filter((instance) => instance.driver === "cursor" && instance.enabled !== false) + .map((instance) => cursorProfileKind(instance.id)); + const selectedRuntime = readStructuredRuntime( + savedAgentSettings, + profileInstanceId ? "sdk" : "acp", + ); const setAgentSetting = useSharedSettings((state) => state.setAgentSetting); const firstStatus = statuses?.[0]; @@ -73,6 +94,10 @@ export function CursorProviderSettings(props: { const refreshStatus = () => readBridge().refreshAgentStatuses(wslDistros, { agentKinds: [agentKind] }); + const refreshPackageStatus = () => + readBridge().refreshAgentStatuses(wslDistros, { + agentKinds: ["cursor", ...cursorProfileKinds], + }); /** * Persists the default runtime right away; there is nothing else to batch. @@ -98,15 +123,33 @@ export function CursorProviderSettings(props: { return (
+ {profileInstanceId ? ( + + ) : null}

GUI runtime

- - Pick the runtime that backs new Cursor GUI chats. Each runtime installs and - authenticates on its own; open chats keep the runtime they started with. - + {profileInstanceId ? ( + + Pick the runtime that backs new Cursor GUI chats. The profile API key applies to both + runtimes. + + ) : ( + + Pick the runtime that backs new Cursor GUI chats. Each runtime installs and + authenticates on its own; open chats keep the runtime they started with. + + )}

@@ -140,13 +183,22 @@ export function CursorProviderSettings(props: { > + {profileInstanceId === undefined ? ( + + ) : null}
); } diff --git a/src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx b/src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx index 5f46825bf..3f2d97309 100644 --- a/src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx +++ b/src/renderer/views/SettingsOverlay/parts/CursorSdkRuntimeSetup.tsx @@ -32,9 +32,11 @@ import { SAVED_CREDENTIAL_MASK } from "./secretMask"; */ export function CursorSdkRuntimeSetup(props: { agentKind: string; + profileInstanceId?: string; status: AgentStatus | undefined; authDescription: string; refreshStatus: () => Promise; + refreshPackageStatus: () => Promise; /** Runs after the key is dropped, so the selected runtime can fall back. */ onApiKeyCleared: () => Promise | void; }) { @@ -43,19 +45,39 @@ export function CursorSdkRuntimeSetup(props: { - + {props.profileInstanceId ? null : ( + + )} ); } +export function CursorProfileApiKeySetup(props: { + agentKind: string; + profileInstanceId: string; + authDescription: string; + refreshStatus: () => Promise; +}) { + return ( + undefined} + /> + ); +} + function SdkPackageRow(props: { agentKind: string; status: AgentStatus | undefined; @@ -177,6 +199,7 @@ function SdkPackageRow(props: { function SdkApiKeyRow(props: { agentKind: string; + profileInstanceId?: string; isInstalled: boolean; authDescription: string; refreshStatus: () => Promise; @@ -184,14 +207,21 @@ function SdkApiKeyRow(props: { }) { const { t } = useLingui(); const savedAgentSettings = useSharedSettings((state) => state.agentSettings[props.agentKind]); + const profileInstance = useSharedSettings((state) => + props.profileInstanceId ? state.agentInstances[props.profileInstanceId] : undefined, + ); const setAgentSecretSetting = useSharedSettings((state) => state.setAgentSecretSetting); + const setAgentInstance = useSharedSettings((state) => state.setAgentInstance); const [apiKey, setApiKey] = useState(""); // A stored key is shown as a mask, matching the ACP credential fields. Typing // starts a replacement; the mask returns when the field is left empty. const [isEditing, setIsEditing] = useState(false); const [pending, setPending] = useState(false); - const hasSavedApiKey = - typeof savedAgentSettings?.sdkApiKey === "string" && savedAgentSettings.sdkApiKey.length > 0; + const hasSavedApiKey = props.profileInstanceId + ? typeof profileInstance?.environment?.CURSOR_API_KEY?.value === "string" && + profileInstance.environment.CURSOR_API_KEY.value.length > 0 + : typeof savedAgentSettings?.sdkApiKey === "string" && savedAgentSettings.sdkApiKey.length > 0; + const canEditApiKey = props.profileInstanceId !== undefined || props.isInstalled; const isMasked = hasSavedApiKey && !isEditing; const saveApiKey = async () => { @@ -199,11 +229,28 @@ function SdkApiKeyRow(props: { if (!value || pending) return; setPending(true); try { - await setAgentSecretSetting(props.agentKind, "sdkApiKey", value); + if (props.profileInstanceId) { + // Sealed in the main process; the same path Claude profiles use. + const instance = await readBridge().setProfileEnvironment({ + instanceId: props.profileInstanceId, + environment: { CURSOR_API_KEY: { value, sensitive: true } }, + }); + setAgentInstance(instance); + } else { + await setAgentSecretSetting(props.agentKind, "sdkApiKey", value); + } setApiKey(""); setIsEditing(false); + toast.success( + props.profileInstanceId ? t`Cursor profile API key saved.` : t`Cursor SDK API key saved.`, + ); + } catch (error) { + toast.danger(friendlyError(error)); + setPending(false); + return; + } + try { await props.refreshStatus(); - toast.success(t`Cursor SDK API key saved.`); } catch (error) { toast.danger(friendlyError(error)); } finally { @@ -232,18 +279,22 @@ function SdkApiKeyRow(props: {
setApiKey(event.currentTarget.value)} + onKeyDown={(event) => { + // Enter saves, matching the other credential fields in Settings. + if (event.key !== "Enter") return; + event.preventDefault(); + void saveApiKey(); + }} /> - {hasSavedApiKey ? ( + {hasSavedApiKey && !props.profileInstanceId ? (