diff --git a/.agents/skills/interactive-testing/scripts/poracode-browser-smoke.mjs b/.agents/skills/interactive-testing/scripts/poracode-browser-smoke.mjs index 142f51d3b..a9999cb87 100644 --- a/.agents/skills/interactive-testing/scripts/poracode-browser-smoke.mjs +++ b/.agents/skills/interactive-testing/scripts/poracode-browser-smoke.mjs @@ -30,6 +30,9 @@ await mkdir(outDir, { recursive: true }); const appTarget = await waitForAppTarget(); const app = await connectTarget(appTarget); +let externalTabId = null; +let deviceEmulationEnabled = false; +let findActive = false; let browserUi = app; try { @@ -77,6 +80,7 @@ try { assert(state.activeTabId !== null, "Active tab", "browser state has an active tab"); assert(activeTab(state)?.url === firstUrl, "Navigate", "active tab reached first smoke URL"); const tabId = state.activeTabId; + externalTabId = tabId; await screenshot(browserUi, "smoke-browser-02-tab.png"); await wait(500); @@ -137,6 +141,136 @@ try { ); await screenshot(browserUi, "smoke-browser-04-navigation.png"); + step("checking downloads internal page creation and reuse"); + await callBridge("browserOpenInternalPage", { page: "downloads" }); + state = await waitForBrowserState( + (s) => activeTab(s)?.internalPage === "downloads", + "downloads internal tab", + ); + const downloadsTabId = state.activeTabId; + const downloadsTabCount = state.tabs.filter((tab) => tab.internalPage === "downloads").length; + const downloadsPage = await waitForInternalPage(downloadsTabId, "Download history"); + assert(downloadsPage.headingVisible, "Downloads heading", "Download history heading is visible"); + assert( + downloadsPage.activeTabWebview === false, + "Downloads guest omission", + "active downloads tab has no webview", + ); + await screenshot(app, "smoke-browser-05-downloads.png"); + + step("checking passwords internal page creation and reuse"); + await callBridge("browserOpenInternalPage", { page: "passwords" }); + state = await waitForBrowserState( + (s) => activeTab(s)?.internalPage === "passwords", + "passwords internal tab", + ); + const passwordsTabId = state.activeTabId; + const passwordsTabCount = state.tabs.filter((tab) => tab.internalPage === "passwords").length; + const passwordsPage = await waitForInternalPage(passwordsTabId, "Manage passwords"); + assert(passwordsPage.headingVisible, "Passwords heading", "Manage passwords heading is visible"); + assert( + passwordsPage.activeTabWebview === false, + "Passwords guest omission", + "active passwords tab has no webview", + ); + await screenshot(app, "smoke-browser-06-passwords.png"); + + await callBridge("browserOpenInternalPage", { page: "downloads" }); + state = await waitForBrowserState( + (s) => s.activeTabId === downloadsTabId, + "reused downloads internal tab", + ); + assert( + state.tabs.filter((tab) => tab.internalPage === "downloads").length === downloadsTabCount, + "Reuse downloads tab", + "opening downloads again activates the existing internal tab", + ); + + await callBridge("browserOpenInternalPage", { page: "passwords" }); + state = await waitForBrowserState( + (s) => s.activeTabId === passwordsTabId, + "reused passwords internal tab", + ); + assert( + state.tabs.filter((tab) => tab.internalPage === "passwords").length === passwordsTabCount, + "Reuse passwords tab", + "opening passwords again activates the existing internal tab", + ); + + step("checking device emulation state and toolbar"); + await callBridge("browserActivateTab", { tabId }); + await waitForBrowserState((s) => s.activeTabId === tabId, "reactivated external smoke tab"); + const emulation = { + width: 833, + height: 970, + deviceScaleFactor: 1, + scale: 1, + mobile: false, + touch: false, + preset: "Responsive", + }; + await callBridge("browserSetDeviceEmulation", { tabId, emulation }); + deviceEmulationEnabled = true; + state = await waitForBrowserState((s) => { + const active = activeTab(s); + return ( + active?.tabId === tabId && + active.deviceEmulation?.width === emulation.width && + active.deviceEmulation?.height === emulation.height && + active.deviceEmulation?.scale === emulation.scale && + active.deviceEmulation?.preset === emulation.preset + ); + }, "device emulation state"); + assert( + activeTab(state)?.deviceEmulation?.preset === "Responsive", + "Device emulation state", + "active external tab exposes Responsive emulation state", + ); + const deviceToolbar = await waitForDeviceToolbar(tabId, emulation.width, emulation.height); + assert( + deviceToolbar.visible, + "Device toolbar", + "device controls are visible with the expected dimensions", + ); + assert( + deviceToolbar.activeTabWebview, + "Emulated guest preserved", + "active external tab keeps its webview while emulated", + ); + await screenshot(app, "smoke-browser-07-device-toolbar.png"); + + await callBridge("browserSetDeviceEmulation", { tabId, emulation: null }); + deviceEmulationEnabled = false; + state = await waitForBrowserState( + (s) => s.activeTabId === tabId && activeTab(s)?.deviceEmulation === undefined, + "cleared device emulation state", + ); + assert( + activeTab(state)?.deviceEmulation === undefined, + "Clear device emulation", + "active external tab returns to its normal viewport", + ); + const toolbarCleared = await evaluate( + `document.querySelector('[data-poracode-browser] [aria-label="Close device toolbar"]') === null`, + ); + assert(toolbarCleared, "Hide device toolbar", "device controls disappear after clearing state"); + + step("checking Find in page against the generated data page"); + await openFindBar(); + findActive = true; + await setFindQuery("second page"); + const findResult = await waitForFindResult("1 of 1"); + assert(findResult.visible, "Find in page", "find bar reports one match in the data page"); + await screenshot(app, "smoke-browser-08-find.png"); + await clickButton("Close find"); + findActive = false; + await waitFor(async () => { + const closed = await evaluate( + `document.querySelector('input[placeholder="Find in page"]') === null`, + ); + return closed ? true : null; + }, "closed browser find bar"); + step("checking Browser settings"); let settingsOpened = await settingsOverlayVisible(); if (!settingsOpened.ok) { @@ -155,7 +289,7 @@ try { }); if (settingsText.ok) { assert(true, "Browser settings", "Browser settings page is reachable"); - await screenshot(app, "smoke-browser-05-settings.png"); + await screenshot(app, "smoke-browser-09-settings.png"); } else { findings.push("Settings opened, but Browser settings page was not reachable by text click."); } @@ -179,6 +313,27 @@ try { printReport(errors); process.exitCode = results.some((result) => result.status === "FAIL") ? 1 : 0; } finally { + if (findActive && externalTabId) { + try { + await clickButton("Close find", { optional: true }); + await callBridge("browserStopFindInPage", { + tabId: externalTabId, + action: "clearSelection", + }); + } catch (error) { + findings.push(`Find cleanup failed: ${error.message}`); + } + } + if (deviceEmulationEnabled && externalTabId) { + try { + await callBridge("browserSetDeviceEmulation", { + tabId: externalTabId, + emulation: null, + }); + } catch (error) { + findings.push(`Device emulation cleanup failed: ${error.message}`); + } + } if (browserUi !== app) browserUi.close(); app.close(); } @@ -466,6 +621,106 @@ function activeTab(state) { return state.tabs.find((tab) => tab.tabId === state.activeTabId); } +async function waitForInternalPage(tabId, heading) { + return waitFor(async () => { + const snapshot = await evaluate( + `(() => { + const root = document.querySelector('[data-poracode-browser]'); + const tabId = ${JSON.stringify(tabId)}; + const headingText = ${JSON.stringify(heading)}; + const heading = [...(root?.querySelectorAll('h1') ?? [])].find( + (candidate) => candidate.textContent?.trim() === headingText, + ); + return { + headingVisible: Boolean(heading && heading.getClientRects().length > 0), + activeTabWebview: Boolean(root?.querySelector('webview[data-tab-id="' + CSS.escape(tabId) + '"]')), + }; + })()`, + ); + return snapshot.headingVisible ? snapshot : null; + }, `${heading} internal page`); +} + +async function waitForDeviceToolbar(tabId, width, height) { + return waitFor(async () => { + const snapshot = await evaluate( + `(() => { + const root = document.querySelector('[data-poracode-browser]'); + const widthInput = root?.querySelector('[aria-label="Viewport width"]'); + const heightInput = root?.querySelector('[aria-label="Viewport height"]'); + const close = root?.querySelector('[aria-label="Close device toolbar"]'); + const tabId = ${JSON.stringify(tabId)}; + return { + visible: Boolean(close && close.getClientRects().length > 0) && + widthInput?.value === ${JSON.stringify(String(width))} && + heightInput?.value === ${JSON.stringify(String(height))}, + activeTabWebview: Boolean(root?.querySelector('webview[data-tab-id="' + CSS.escape(tabId) + '"]')), + }; + })()`, + ); + return snapshot.visible ? snapshot : null; + }, "device toolbar dimensions"); +} + +async function openFindBar() { + await clickButton("Browser menu"); + await waitFor(async () => { + const clicked = await evaluate( + `(() => { + const item = [...document.querySelectorAll('[role="menuitem"]')].find((candidate) => + candidate.querySelector('label')?.textContent?.trim() === "Find in page" + ); + if (!item || item.getAttribute('aria-disabled') === 'true') return false; + item.click(); + return true; + })()`, + ); + return clicked ? true : null; + }, "Find in page menu item"); + await waitFor(async () => { + const visible = await evaluate( + `Boolean(document.querySelector('input[placeholder="Find in page"]'))`, + ); + return visible ? true : null; + }, "browser find bar"); +} + +async function setFindQuery(query) { + const focused = await evaluate( + `(() => { + const input = document.querySelector('input[placeholder="Find in page"]'); + if (!input) return false; + input.focus(); + input.select(); + return true; + })()`, + ); + if (!focused) throw new Error("Browser find input was not available"); + await send(app, "Input.insertText", { text: query }); + await waitFor(async () => { + const value = await evaluate( + `document.querySelector('input[placeholder="Find in page"]')?.value ?? null`, + ); + return value === query ? true : null; + }, "browser find query input"); +} + +async function waitForFindResult(counter) { + return waitFor(async () => { + const snapshot = await evaluate( + `(() => { + const input = document.querySelector('input[placeholder="Find in page"]'); + const search = input?.closest('[role="search"]'); + return { + visible: Boolean(search && search.getClientRects().length > 0), + text: search?.textContent ?? '', + }; + })()`, + ); + return snapshot.visible && snapshot.text.includes(counter) ? snapshot : null; + }, `browser find result ${counter}`); +} + async function callBridge(method, payload) { const result = await evaluate( `(() => { diff --git a/.agents/skills/interactive-testing/scripts/smoke-scenarios.mjs b/.agents/skills/interactive-testing/scripts/smoke-scenarios.mjs index 2e02708ad..a8ca5201a 100644 --- a/.agents/skills/interactive-testing/scripts/smoke-scenarios.mjs +++ b/.agents/skills/interactive-testing/scripts/smoke-scenarios.mjs @@ -75,7 +75,8 @@ export const functionalAreas = [ }, { id: "browser", - title: "In-app Browser, browser MCP, picker, bookmarks, and Chrome extension", + title: + "In-app Browser, internal pages, downloads, passwords, device emulation, find, browser MCP, picker, bookmarks, and Chrome extension", patterns: [/browser/i, /chrome-extension/i, /ChromeMcp/i, /picker/i], automated: ["baseline", "browser"], manual: [], diff --git a/src/main/browser/BrowserCredentialStore.test.ts b/src/main/browser/BrowserCredentialStore.test.ts new file mode 100644 index 000000000..db46f19bd --- /dev/null +++ b/src/main/browser/BrowserCredentialStore.test.ts @@ -0,0 +1,181 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const state = new Map(); +const writes: string[] = []; +vi.mock("../db", () => ({ + dbGetState: (key: string) => state.get(key) ?? null, + dbSetState: (key: string, value: string) => { + state.set(key, value); + writes.push(value); + }, +})); + +import { BrowserCredentialStore, normalizeBrowserCredentialOrigin } from "./BrowserCredentialStore"; + +describe("BrowserCredentialStore", () => { + beforeEach(() => { + state.clear(); + writes.length = 0; + }); + + it("normalizes HTTP(S) origins and rejects other protocols", () => { + expect(normalizeBrowserCredentialOrigin("HTTPS://Example.com:443/login?q=1")).toBe( + "https://example.com", + ); + expect(normalizeBrowserCredentialOrigin("http://example.com:8080/path")).toBe( + "http://example.com:8080", + ); + expect(() => normalizeBrowserCredentialOrigin("file:///tmp/login")).toThrow("HTTP(S) origin"); + }); + + it("persists only sealed passwords and returns metadata-only lists", () => { + const store = new BrowserCredentialStore(); + const metadata = store.upsert({ + origin: "https://example.com/login", + username: "ada@example.com", + password: "correct horse battery staple", + }); + + expect(metadata).not.toHaveProperty("password"); + expect(metadata).not.toHaveProperty("sealedPassword"); + expect(store.list()).toEqual([metadata]); + const raw = [...state.values()][0]; + expect(raw).toBeDefined(); + expect(raw).not.toContain("correct horse battery staple"); + expect(raw).toContain("lc-safe:v1:"); + }); + + it("decrypts only through explicit get and survives a new store instance", () => { + const metadata = new BrowserCredentialStore().upsert({ + origin: "https://example.com", + username: "ada", + password: "secret", + }); + + expect(new BrowserCredentialStore().get(metadata.id)).toEqual({ + ...metadata, + password: "secret", + }); + }); + + it("encrypts passwords that resemble the shared sealed-value format", () => { + const password = "lc-safe:v1:not-really-encrypted"; + const metadata = new BrowserCredentialStore().upsert({ + origin: "https://example.com", + username: "ada", + password, + }); + + expect([...state.values()][0]).not.toContain(password); + expect(new BrowserCredentialStore().get(metadata.id)?.password).toBe(password); + }); + + it("upserts by exact normalized origin and username", () => { + const store = new BrowserCredentialStore(); + const original = store.upsert({ + origin: "https://EXAMPLE.com/login", + username: "ada", + password: "old", + }); + const updated = store.upsert({ + origin: "https://example.com:443/account", + username: "ada", + password: "new", + }); + + expect(updated.id).toBe(original.id); + expect(store.list()).toHaveLength(1); + expect(store.get(original.id)?.password).toBe("new"); + }); + + it("persists successful batch entries once and reports invalid rows", () => { + const store = new BrowserCredentialStore(); + const result = store.upsertMany([ + { origin: "https://example.com/login", username: "ada", password: "one" }, + { origin: "file:///invalid", username: "skip", password: "two" }, + { origin: "https://example.org/login", username: "grace", password: "three" }, + ]); + + expect(result).toMatchObject({ failed: 1 }); + expect(result.saved).toHaveLength(2); + expect( + store + .list() + .map((credential) => credential.username) + .sort(), + ).toEqual(["ada", "grace"]); + expect(writes).toHaveLength(1); + }); + + it("updates metadata by id while preserving an existing import source", () => { + const store = new BrowserCredentialStore(); + const original = store.upsert({ + origin: "https://example.com", + username: "ada", + password: "old", + source: "chrome:Default", + }); + const updated = store.upsert({ + id: original.id, + origin: "https://example.org/account", + username: "grace", + password: "new", + }); + + expect(updated).toMatchObject({ + id: original.id, + origin: "https://example.org", + username: "grace", + source: "chrome:Default", + }); + expect(store.get(original.id)?.password).toBe("new"); + }); + + it("filters metadata by exact origin instead of parent or subdomains", () => { + const store = new BrowserCredentialStore(); + store.upsert({ origin: "https://example.com", username: "root", password: "one" }); + store.upsert({ origin: "https://sub.example.com", username: "sub", password: "two" }); + + expect(store.list("https://example.com/path").map((entry) => entry.username)).toEqual(["root"]); + expect(() => store.list("")).toThrow("Invalid URL"); + }); + + it("deletes credentials by id", () => { + const store = new BrowserCredentialStore(); + const metadata = store.upsert({ + origin: "https://example.com", + username: "ada", + password: "secret", + }); + + expect(store.delete(metadata.id)).toBe(true); + expect(store.delete(metadata.id)).toBe(false); + expect(store.get(metadata.id)).toBeUndefined(); + }); + + it("ignores persisted plaintext and malformed origins", () => { + state.set( + "browser-credentials-v1", + JSON.stringify([ + { + id: "plaintext", + origin: "https://example.com", + username: "ada", + sealedPassword: "secret", + createdAt: 1, + updatedAt: 1, + }, + { + id: "file-origin", + origin: "file://example.com", + username: "ada", + sealedPassword: "lc-safe:v1:invalid", + createdAt: 1, + updatedAt: 1, + }, + ]), + ); + + expect(new BrowserCredentialStore().list()).toEqual([]); + }); +}); diff --git a/src/main/browser/BrowserCredentialStore.ts b/src/main/browser/BrowserCredentialStore.ts new file mode 100644 index 000000000..bc91e9fa9 --- /dev/null +++ b/src/main/browser/BrowserCredentialStore.ts @@ -0,0 +1,209 @@ +import { randomUUID } from "node:crypto"; +import type { BrowserCredentialInfo } from "@/shared/ipc"; +import { decryptSecret, encryptSecret, isEncryptedSecret } from "@/shared/secretStorage"; +import { dbGetState, dbSetState } from "../db"; + +const CREDENTIALS_KEY = "browser-credentials-v1"; +const NO_BASE_DIR = ""; +const PASSWORD_PAYLOAD_PREFIX = "browser-password:v1:"; + +export interface BrowserCredential extends BrowserCredentialInfo { + password: string; +} + +export interface BrowserCredentialInput { + id?: string; + origin: string; + username: string; + password: string; + source?: string; +} + +export interface BrowserCredentialBatchResult { + saved: BrowserCredentialInfo[]; + failed: number; +} + +interface PersistedBrowserCredential extends BrowserCredentialInfo { + sealedPassword: string; +} + +/** Normalize a page URL to the exact HTTP(S) origin used for credential matching. */ +export function normalizeBrowserCredentialOrigin(value: string): string { + const url = new URL(value); + if (url.protocol !== "http:" && url.protocol !== "https:") { + throw new Error("Browser credentials require an HTTP(S) origin."); + } + return url.origin; +} + +/** + * Encrypted browser credentials. List operations return metadata only; callers + * must explicitly request a credential by id to obtain its decrypted password. + */ +export class BrowserCredentialStore { + private credentials: PersistedBrowserCredential[] = []; + private loaded = false; + + list(origin?: string): BrowserCredentialInfo[] { + this.load(); + const normalizedOrigin = + origin === undefined ? undefined : normalizeBrowserCredentialOrigin(origin); + return this.credentials + .filter( + (credential) => normalizedOrigin === undefined || credential.origin === normalizedOrigin, + ) + .map(toMetadata) + .sort((a, b) => b.updatedAt - a.updatedAt); + } + + get(id: string): BrowserCredential | undefined { + this.load(); + const credential = this.credentials.find((entry) => entry.id === id); + if (!credential) return undefined; + try { + return { + ...toMetadata(credential), + password: unsealPassword(credential.sealedPassword), + }; + } catch { + return undefined; + } + } + + upsert(input: BrowserCredentialInput): BrowserCredentialInfo { + this.load(); + const credentials = this.credentials.map((credential) => ({ ...credential })); + const metadata = upsertCredential(credentials, input); + this.persist(credentials); + this.credentials = credentials; + return metadata; + } + + upsertMany(inputs: readonly BrowserCredentialInput[]): BrowserCredentialBatchResult { + this.load(); + const credentials = this.credentials.map((credential) => ({ ...credential })); + const saved: BrowserCredentialInfo[] = []; + let failed = 0; + for (const input of inputs) { + try { + saved.push(upsertCredential(credentials, input)); + } catch { + failed += 1; + } + } + if (saved.length > 0) { + this.persist(credentials); + this.credentials = credentials; + } + return { saved, failed }; + } + + delete(id: string): boolean { + this.load(); + const next = this.credentials.filter((credential) => credential.id !== id); + if (next.length === this.credentials.length) return false; + this.credentials = next; + this.persist(this.credentials); + return true; + } + + private load(): void { + if (this.loaded) return; + this.loaded = true; + try { + const raw = dbGetState(CREDENTIALS_KEY); + if (!raw) return; + const parsed = JSON.parse(raw) as unknown; + if (!Array.isArray(parsed)) return; + this.credentials = parsed.filter(isPersistedBrowserCredential); + } catch {} + } + + private persist(credentials: PersistedBrowserCredential[]): void { + dbSetState(CREDENTIALS_KEY, JSON.stringify(credentials)); + } +} + +function upsertCredential( + credentials: PersistedBrowserCredential[], + input: BrowserCredentialInput, +): BrowserCredentialInfo { + const origin = normalizeBrowserCredentialOrigin(input.origin); + const now = Date.now(); + const existing = input.id + ? credentials.find((credential) => credential.id === input.id) + : credentials.find( + (credential) => credential.origin === origin && credential.username === input.username, + ); + if (existing) { + existing.origin = origin; + existing.username = input.username; + existing.sealedPassword = sealPassword(input.password); + existing.updatedAt = now; + if (input.source !== undefined) existing.source = input.source; + return toMetadata(existing); + } + + const credential: PersistedBrowserCredential = { + id: `credential-${randomUUID()}`, + origin, + username: input.username, + sealedPassword: sealPassword(input.password), + createdAt: now, + updatedAt: now, + ...(input.source !== undefined ? { source: input.source } : {}), + }; + credentials.push(credential); + return toMetadata(credential); +} + +function sealPassword(password: string): string { + // `encryptSecret` deliberately accepts already-sealed values unchanged. Add + // an application payload prefix so a real password beginning with the shared + // secret marker is still encrypted rather than mistaken for ciphertext. + return encryptSecret(NO_BASE_DIR, `${PASSWORD_PAYLOAD_PREFIX}${password}`); +} + +function unsealPassword(sealedPassword: string): string { + const payload = decryptSecret(NO_BASE_DIR, sealedPassword); + if (!payload.startsWith(PASSWORD_PAYLOAD_PREFIX)) { + throw new Error("Invalid browser credential payload."); + } + return payload.slice(PASSWORD_PAYLOAD_PREFIX.length); +} + +function toMetadata(credential: PersistedBrowserCredential): BrowserCredentialInfo { + return { + id: credential.id, + origin: credential.origin, + username: credential.username, + createdAt: credential.createdAt, + updatedAt: credential.updatedAt, + ...(credential.source !== undefined ? { source: credential.source } : {}), + }; +} + +function isPersistedBrowserCredential(value: unknown): value is PersistedBrowserCredential { + if (!value || typeof value !== "object") return false; + const credential = value as Partial; + if ( + typeof credential.id !== "string" || + typeof credential.origin !== "string" || + typeof credential.username !== "string" || + typeof credential.sealedPassword !== "string" || + !isEncryptedSecret(credential.sealedPassword) || + typeof credential.createdAt !== "number" || + !Number.isFinite(credential.createdAt) || + typeof credential.updatedAt !== "number" || + !Number.isFinite(credential.updatedAt) || + (credential.source !== undefined && typeof credential.source !== "string") + ) { + return false; + } + try { + return normalizeBrowserCredentialOrigin(credential.origin) === credential.origin; + } catch { + return false; + } +} diff --git a/src/main/browser/BrowserDownloadManager.test.ts b/src/main/browser/BrowserDownloadManager.test.ts new file mode 100644 index 000000000..8951d6ee0 --- /dev/null +++ b/src/main/browser/BrowserDownloadManager.test.ts @@ -0,0 +1,224 @@ +import { EventEmitter } from "node:events"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const dbGetState = vi.hoisted(() => vi.fn<(key: string) => string | null>()); +const dbSetState = vi.hoisted(() => vi.fn<(key: string, value: string) => void>()); +const openPath = vi.hoisted(() => vi.fn<(path: string) => Promise>()); +const showItemInFolder = vi.hoisted(() => vi.fn<(path: string) => void>()); + +vi.mock("../db", () => ({ dbGetState, dbSetState })); +vi.mock("electron", () => ({ shell: { openPath, showItemInFolder } })); + +class FakeSession extends EventEmitter { + emitDownload(item: FakeDownloadItem, webContentsId = 91): void { + this.emit("will-download", {}, item, { id: webContentsId }); + } +} + +class FakeDownloadItem extends EventEmitter { + state: "progressing" | "completed" | "cancelled" | "interrupted" = "progressing"; + receivedBytes = 10; + totalBytes = 100; + paused = false; + resumable = true; + endTime = 0; + savePath = "C:\\Users\\demo\\Downloads\\report.pdf"; + readonly pause = vi.fn<() => void>(() => { + this.paused = true; + }); + readonly resume = vi.fn<() => void>(() => { + this.paused = false; + this.state = "progressing"; + }); + readonly cancel = vi.fn<() => void>(() => { + this.state = "cancelled"; + }); + + getFilename(): string { + return "report.pdf"; + } + getURL(): string { + return "https://example.test/report.pdf"; + } + getMimeType(): string { + return "application/pdf"; + } + getState(): typeof this.state { + return this.state; + } + getReceivedBytes(): number { + return this.receivedBytes; + } + getTotalBytes(): number { + return this.totalBytes; + } + getStartTime(): number { + return 1_700_000_000; + } + getEndTime(): number { + return this.endTime; + } + isPaused(): boolean { + return this.paused; + } + canResume(): boolean { + return this.resumable; + } + getSavePath(): string { + return this.savePath; + } +} + +describe("BrowserDownloadManager", () => { + beforeEach(() => { + vi.clearAllMocks(); + dbGetState.mockReturnValue(null); + openPath.mockResolvedValue(""); + }); + + it("subscribes once, emits renderer-safe metadata, and disposes all listeners", async () => { + const { BrowserDownloadManager } = await import("./BrowserDownloadManager"); + const session = new FakeSession(); + const item = new FakeDownloadItem(); + const onEvent = vi.fn<(event: unknown) => void>(); + const manager = new BrowserDownloadManager(session as never, { onEvent }); + + expect(session.listenerCount("will-download")).toBe(1); + session.emitDownload(item, 17); + + const download = manager.list()[0]!; + expect(download).toMatchObject({ + filename: "report.pdf", + url: "https://example.test/report.pdf", + state: "progressing", + }); + expect(download).not.toHaveProperty("savePath"); + expect(onEvent).toHaveBeenCalledWith({ + type: "created", + download, + webContentsId: 17, + }); + expect(item.listenerCount("updated")).toBe(1); + expect(item.listenerCount("done")).toBe(1); + + manager.dispose(); + + expect(session.listenerCount("will-download")).toBe(0); + expect(item.listenerCount("updated")).toBe(0); + expect(item.listenerCount("done")).toBe(0); + }); + + it("controls a live DownloadItem by opaque ID and tracks progress", async () => { + const { BrowserDownloadManager } = await import("./BrowserDownloadManager"); + const session = new FakeSession(); + const item = new FakeDownloadItem(); + const manager = new BrowserDownloadManager(session as never); + session.emitDownload(item); + const id = manager.list()[0]!.id; + + expect(manager.pause(id)).toBe(true); + expect(item.pause).toHaveBeenCalledOnce(); + expect(manager.list()[0]).toMatchObject({ state: "paused" }); + + expect(manager.resume(id)).toBe(true); + expect(item.resume).toHaveBeenCalledOnce(); + expect(manager.list()[0]).toMatchObject({ state: "progressing" }); + + item.receivedBytes = 60; + item.emit("updated", {}, "progressing"); + expect(manager.list()[0]).toMatchObject({ receivedBytes: 60 }); + + expect(manager.cancel(id)).toBe(true); + expect(item.cancel).toHaveBeenCalledOnce(); + expect(manager.list()[0]).toMatchObject({ state: "cancelled" }); + manager.dispose(); + }); + + it("opens and reveals only completed downloads using the stored main-process path", async () => { + const { BrowserDownloadManager } = await import("./BrowserDownloadManager"); + const session = new FakeSession(); + const item = new FakeDownloadItem(); + const manager = new BrowserDownloadManager(session as never); + session.emitDownload(item); + const id = manager.list()[0]!.id; + + expect(await manager.open(id)).toBe(false); + expect(manager.reveal(id)).toBe(false); + item.state = "completed"; + item.receivedBytes = 100; + item.endTime = 1_700_000_010; + item.resumable = false; + item.emit("done", {}, "completed"); + + expect(await manager.open(id)).toBe(true); + expect(openPath).toHaveBeenCalledWith(item.savePath); + expect(manager.reveal(id)).toBe(true); + expect(showItemInFolder).toHaveBeenCalledWith(item.savePath); + expect(await manager.open("missing-id")).toBe(false); + expect(manager.reveal("missing-id")).toBe(false); + manager.dispose(); + }); + + it("persists history, marks stale in-flight entries interrupted, and never restores handles", async () => { + dbGetState.mockReturnValue( + JSON.stringify({ + downloads: [ + { + id: "persisted-id", + filename: "archive.zip", + url: "https://example.test/archive.zip", + mimeType: "application/zip", + state: "progressing", + receivedBytes: 50, + totalBytes: 100, + bytesPerSecond: 20, + percentComplete: 50, + startTime: 1_700_000_000_000, + paused: false, + canResume: true, + savePath: "C:\\Users\\demo\\Downloads\\archive.zip", + urlChain: ["https://example.test/archive.zip"], + eTag: '"etag"', + lastModified: "Tue, 01 Jan 2026 00:00:00 GMT", + }, + ], + }), + ); + const { BrowserDownloadManager } = await import("./BrowserDownloadManager"); + const manager = new BrowserDownloadManager(new FakeSession() as never); + + expect(manager.list()).toEqual([ + expect.objectContaining({ + id: "persisted-id", + state: "interrupted", + canResume: false, + }), + ]); + expect(manager.pause("persisted-id")).toBe(false); + expect(manager.resume("persisted-id")).toBe(false); + manager.dispose(); + }); + + it("removes terminal history without deleting the file", async () => { + const { BrowserDownloadManager } = await import("./BrowserDownloadManager"); + const session = new FakeSession(); + const item = new FakeDownloadItem(); + const onEvent = vi.fn<(event: unknown) => void>(); + const manager = new BrowserDownloadManager(session as never, { onEvent }); + session.emitDownload(item); + const id = manager.list()[0]!.id; + + expect(manager.remove(id)).toBe(false); + item.state = "completed"; + item.resumable = false; + item.emit("done", {}, "completed"); + expect(manager.remove(id)).toBe(true); + expect(manager.list()).toEqual([]); + expect(onEvent).toHaveBeenLastCalledWith({ type: "removed", downloadId: id }); + expect(dbSetState).toHaveBeenLastCalledWith( + "browser-downloads-v1", + JSON.stringify({ downloads: [] }), + ); + manager.dispose(); + }); +}); diff --git a/src/main/browser/BrowserDownloadManager.ts b/src/main/browser/BrowserDownloadManager.ts new file mode 100644 index 000000000..f4e28d5e3 --- /dev/null +++ b/src/main/browser/BrowserDownloadManager.ts @@ -0,0 +1,322 @@ +import { randomUUID } from "node:crypto"; +import { shell, type DownloadItem, type Session, type WebContents } from "electron"; +import { + browserDownloadStateSchema, + type BrowserDownloadInfo, + type BrowserDownloadState, +} from "@/shared/ipc"; +import { dbGetState, dbSetState } from "../db"; + +const PERSIST_KEY = "browser-downloads-v1"; +const PERSIST_DEBOUNCE_MS = 250; +const MAX_DOWNLOADS = 500; + +export type BrowserDownloadManagerEvent = + | { type: "created"; download: BrowserDownloadInfo; webContentsId: number } + | { type: "updated"; download: BrowserDownloadInfo } + | { type: "removed"; downloadId: string }; + +export interface BrowserDownloadManagerOptions { + onEvent?(event: BrowserDownloadManagerEvent): void; +} + +interface BrowserDownloadRecord extends BrowserDownloadInfo { + savePath: string; +} + +interface LiveDownload { + item: DownloadItem; + cleanup(): void; +} + +interface PersistedBrowserDownloads { + downloads: BrowserDownloadRecord[]; +} + +/** + * Owns downloads initiated by the embedded browser session. Callers operate on + * opaque IDs; paths and Electron DownloadItem instances never leave main. + */ +export class BrowserDownloadManager { + private readonly records = new Map(); + private readonly liveItems = new Map(); + private persistTimer: ReturnType | null = null; + private disposed = false; + + private readonly onWillDownload = ( + _event: Electron.Event, + item: DownloadItem, + webContents: WebContents, + ): void => { + if (this.disposed) return; + const id = randomUUID(); + const record = recordFromItem(id, item); + this.records.set(id, record); + this.bindItem(id, item); + this.prune(); + this.persistNow(); + this.emit({ type: "created", download: toInfo(record), webContentsId: webContents.id }); + }; + + constructor( + private readonly session: Session, + private readonly options: BrowserDownloadManagerOptions = {}, + ) { + this.load(); + this.session.on("will-download", this.onWillDownload); + } + + list(): BrowserDownloadInfo[] { + return this.sortedRecords().map(toInfo); + } + + pause(downloadId: string): boolean { + const item = this.liveItems.get(downloadId)?.item; + const record = this.records.get(downloadId); + if (!item || !record || record.state !== "progressing" || item.isPaused()) return false; + item.pause(); + this.updateRecord(downloadId, item, "paused"); + return true; + } + + resume(downloadId: string): boolean { + const item = this.liveItems.get(downloadId)?.item; + const record = this.records.get(downloadId); + if (!item || !record || !item.canResume()) return false; + if ( + record.state !== "paused" && + record.state !== "interrupted" && + record.state !== "cancelled" + ) { + return false; + } + item.resume(); + this.updateRecord(downloadId, item, "progressing"); + return true; + } + + cancel(downloadId: string): boolean { + const item = this.liveItems.get(downloadId)?.item; + const record = this.records.get(downloadId); + if (!item || !record || isTerminal(record.state)) return false; + item.cancel(); + this.updateRecord(downloadId, item, "cancelled", Date.now()); + return true; + } + + /** Removes history only; the downloaded file is not deleted. */ + remove(downloadId: string): boolean { + const record = this.records.get(downloadId); + if (!record || !isTerminal(record.state)) return false; + this.cleanupItem(downloadId); + this.records.delete(downloadId); + this.persistNow(); + this.emit({ type: "removed", downloadId }); + return true; + } + + async open(downloadId: string): Promise { + const record = this.records.get(downloadId); + if (!record || record.state !== "completed" || !record.savePath) return false; + try { + return (await shell.openPath(record.savePath)) === ""; + } catch { + return false; + } + } + + reveal(downloadId: string): boolean { + const record = this.records.get(downloadId); + if (!record || record.state !== "completed" || !record.savePath) return false; + try { + shell.showItemInFolder(record.savePath); + return true; + } catch { + return false; + } + } + + dispose(): void { + if (this.disposed) return; + this.disposed = true; + this.session.removeListener("will-download", this.onWillDownload); + for (const live of this.liveItems.values()) live.cleanup(); + this.liveItems.clear(); + if (this.persistTimer) { + clearTimeout(this.persistTimer); + this.persistTimer = null; + this.persistNow(); + } + } + + private bindItem(id: string, item: DownloadItem): void { + const onUpdated = (_event: Electron.Event, state: "progressing" | "interrupted"): void => { + this.updateRecord(id, item, state === "progressing" && item.isPaused() ? "paused" : state); + }; + const onDone = ( + _event: Electron.Event, + state: "completed" | "cancelled" | "interrupted", + ): void => { + const endTime = item.getEndTime(); + this.updateRecord(id, item, state, endTime > 0 ? secondsToMilliseconds(endTime) : Date.now()); + if (state === "completed" || !item.canResume()) this.cleanupItem(id); + this.persistNow(); + }; + item.on("updated", onUpdated); + item.on("done", onDone); + this.liveItems.set(id, { + item, + cleanup: () => { + item.removeListener("updated", onUpdated); + item.removeListener("done", onDone); + }, + }); + } + + private cleanupItem(id: string): void { + this.liveItems.get(id)?.cleanup(); + this.liveItems.delete(id); + } + + private updateRecord( + id: string, + item: DownloadItem, + state: BrowserDownloadState, + endTime?: number, + ): void { + const previous = this.records.get(id); + if (!previous) return; + const next = recordFromItem(id, item, state, previous.startTime, endTime); + this.records.set(id, next); + this.schedulePersist(); + this.emit({ type: "updated", download: toInfo(next) }); + } + + private load(): void { + try { + const raw = dbGetState(PERSIST_KEY); + if (!raw) return; + const parsed = JSON.parse(raw) as PersistedBrowserDownloads; + if (!parsed || !Array.isArray(parsed.downloads)) return; + for (const candidate of parsed.downloads) { + if (!isBrowserDownloadRecord(candidate)) continue; + // DownloadItem handles are session-local. In-flight records remain as + // history after restart but cannot claim resumability without a handle. + const staleInFlight = candidate.state === "progressing" || candidate.state === "paused"; + this.records.set(candidate.id, { + ...toInfo(candidate), + ...(staleInFlight ? { state: "interrupted" as const } : {}), + canResume: false, + savePath: candidate.savePath, + }); + } + this.prune(); + } catch {} + } + + private prune(): void { + if (this.records.size <= MAX_DOWNLOADS) return; + for (const record of [...this.records.values()].sort((a, b) => a.startTime - b.startTime)) { + if (this.records.size <= MAX_DOWNLOADS) break; + if (this.liveItems.has(record.id)) continue; + this.records.delete(record.id); + } + } + + private sortedRecords(): BrowserDownloadRecord[] { + return [...this.records.values()].sort((a, b) => b.startTime - a.startTime); + } + + private schedulePersist(): void { + if (this.persistTimer) clearTimeout(this.persistTimer); + this.persistTimer = setTimeout(() => { + this.persistTimer = null; + this.persistNow(); + }, PERSIST_DEBOUNCE_MS); + } + + private persistNow(): void { + if (this.persistTimer) { + clearTimeout(this.persistTimer); + this.persistTimer = null; + } + try { + dbSetState(PERSIST_KEY, JSON.stringify({ downloads: this.sortedRecords() })); + } catch {} + } + + private emit(event: BrowserDownloadManagerEvent): void { + try { + this.options.onEvent?.(event); + } catch {} + } +} + +function recordFromItem( + id: string, + item: DownloadItem, + state: BrowserDownloadState = normalizeItemState(item), + startTime: number = secondsToMilliseconds(item.getStartTime()), + endTime?: number, +): BrowserDownloadRecord { + const totalBytes = Math.max(0, item.getTotalBytes()); + const receivedBytes = Math.max(0, item.getReceivedBytes()); + return { + id, + filename: item.getFilename(), + url: item.getURL(), + mimeType: item.getMimeType(), + state, + receivedBytes, + totalBytes, + startTime: startTime > 0 ? startTime : Date.now(), + ...(endTime !== undefined ? { endTime } : {}), + canResume: item.canResume(), + savePath: item.getSavePath(), + }; +} + +function normalizeItemState(item: DownloadItem): BrowserDownloadState { + const state = item.getState(); + return state === "progressing" && item.isPaused() ? "paused" : state; +} + +function secondsToMilliseconds(value: number): number { + return Math.round(value * 1000); +} + +function isTerminal(state: BrowserDownloadState): boolean { + return state === "completed" || state === "cancelled" || state === "interrupted"; +} + +function toInfo(record: BrowserDownloadRecord): BrowserDownloadInfo { + return { + id: record.id, + filename: record.filename, + url: record.url, + mimeType: record.mimeType, + state: record.state, + receivedBytes: record.receivedBytes, + totalBytes: record.totalBytes, + startTime: record.startTime, + ...(record.endTime !== undefined ? { endTime: record.endTime } : {}), + canResume: record.canResume, + }; +} + +function isBrowserDownloadRecord(value: unknown): value is BrowserDownloadRecord { + if (!value || typeof value !== "object") return false; + const record = value as Partial; + return ( + typeof record.id === "string" && + typeof record.filename === "string" && + typeof record.url === "string" && + typeof record.mimeType === "string" && + browserDownloadStateSchema.safeParse(record.state).success && + typeof record.receivedBytes === "number" && + typeof record.totalBytes === "number" && + typeof record.startTime === "number" && + typeof record.canResume === "boolean" && + typeof record.savePath === "string" + ); +} diff --git a/src/main/browser/BrowserPanelManager.test.ts b/src/main/browser/BrowserPanelManager.test.ts index 710414859..31ef0c1da 100644 --- a/src/main/browser/BrowserPanelManager.test.ts +++ b/src/main/browser/BrowserPanelManager.test.ts @@ -1,7 +1,14 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { BrowserTabGroupInfo } from "@/shared/ipc"; +import type { BrowserDownloadInfo, BrowserImportResult, BrowserTabGroupInfo } from "@/shared/ipc"; +import type { BrowserCredentialStore } from "./BrowserCredentialStore"; +import type { BrowserDownloadManager, BrowserDownloadManagerEvent } from "./BrowserDownloadManager"; +import type { WindowsChromiumImportService } from "./WindowsChromiumImportService"; const resolveWebContentsById = vi.hoisted(() => vi.fn<(id: number) => unknown>()); +const browserTabHarness = vi.hoisted(() => ({ + attach: vi.fn<(webContents: unknown) => void>(), + whenAttached: vi.fn<() => Promise>(() => Promise.resolve()), +})); vi.mock("electron", () => ({ BrowserWindow: class BrowserWindow {}, @@ -41,7 +48,59 @@ vi.mock("./picker/pickerScript", () => ({ })); vi.mock("./BrowserTab", () => ({ - BrowserTab: class BrowserTab {}, + BrowserTab: class BrowserTab { + readonly tabId: string; + private url: string; + private title = ""; + + constructor( + private readonly options: { + tabId: string; + initialUrl?: string; + onUpdate(snapshot: { + tabId: string; + url: string; + title: string; + loading: boolean; + canGoBack: boolean; + canGoForward: boolean; + devToolsOpen: boolean; + zoomFactor: number; + }): void; + }, + ) { + this.tabId = options.tabId; + this.url = options.initialUrl ?? "about:blank"; + } + + snapshot() { + return { + tabId: this.tabId, + url: this.url, + title: this.title, + loading: false, + canGoBack: false, + canGoForward: false, + devToolsOpen: false, + zoomFactor: 1, + }; + } + + isAttached() { + return false; + } + + attach = browserTabHarness.attach; + + whenAttached = browserTabHarness.whenAttached; + + async loadURL(url: string) { + this.url = url; + this.options.onUpdate(this.snapshot()); + } + + async destroy() {} + }, resolveWebContentsById, })); @@ -98,6 +157,47 @@ function seedGroupState( } } +function createBrowserSession() { + return { + on: vi.fn<(event: string, listener: (...args: unknown[]) => void) => void>(), + removeListener: vi.fn<(event: string, listener: (...args: unknown[]) => void) => void>(), + clearStorageData: vi.fn<() => Promise>(() => Promise.resolve()), + clearCache: vi.fn<() => Promise>(() => Promise.resolve()), + }; +} + +function browserDataServices(manager: unknown): { + credentials: BrowserCredentialStore; + downloads: BrowserDownloadManager | null; + importer: WindowsChromiumImportService | null; +} { + return manager as { + credentials: BrowserCredentialStore; + downloads: BrowserDownloadManager | null; + importer: WindowsChromiumImportService | null; + }; +} + +function emptyImportResult(): BrowserImportResult { + return { + passwordsImported: 0, + cookiesImported: 0, + passwordsSkipped: 0, + cookiesSkipped: 0, + protectedItemsSkipped: 0, + errors: [], + }; +} + +function isCredentialsUpdatedEvent(value: unknown): boolean { + return ( + typeof value === "object" && + value !== null && + "type" in value && + value.type === "credentials-updated" + ); +} + describe("BrowserPanelManager", () => { beforeEach(() => { vi.clearAllMocks(); @@ -137,6 +237,188 @@ describe("BrowserPanelManager", () => { expect(tab.attach).toHaveBeenCalledWith(guestWebContents); }); + it("opens app-owned download history once and reuses the existing tab", async () => { + const { BrowserPanelManager } = await import("./BrowserPanelManager"); + const manager = new BrowserPanelManager( + { settingsPath: "settings.json" } as never, + "Mozilla/5.0 Chrome/141.0.0.0 Safari/537.36", + ); + const first = await manager.openInternalPage("downloads"); + const second = await manager.openInternalPage("downloads"); + + expect(first).toMatchObject({ + url: "chrome://downloads/", + title: "Download history", + internalPage: "downloads", + }); + expect(second.tabId).toBe(first.tabId); + expect(manager.snapshot().tabs).toHaveLength(1); + expect(manager.getTab(first.tabId)).toBeNull(); + expect(manager.getActiveTab()).toBeNull(); + + manager.attachWebContents(first.tabId, 99); + expect(resolveWebContentsById).not.toHaveBeenCalled(); + expect(browserTabHarness.attach).not.toHaveBeenCalled(); + }); + + it("replaces an internal page when the tab navigates to the web", async () => { + const { BrowserPanelManager } = await import("./BrowserPanelManager"); + const manager = new BrowserPanelManager( + { settingsPath: "settings.json" } as never, + "Mozilla/5.0 Chrome/141.0.0.0 Safari/537.36", + ); + const internal = await manager.openInternalPage("passwords"); + + await manager.navigate(internal.tabId, "https://example.com/"); + + expect(manager.snapshot().tabs[0]?.url).toBe("https://example.com/"); + expect(manager.snapshot().tabs[0]?.internalPage).toBeUndefined(); + + const guestWebContents = { id: 99 }; + resolveWebContentsById.mockReturnValue(guestWebContents); + manager.attachWebContents(internal.tabId, 99); + expect(resolveWebContentsById).toHaveBeenCalledWith(99); + expect(browserTabHarness.attach).toHaveBeenCalledWith(guestWebContents); + }); + + it("wires the shared browser session into browser data services", async () => { + const { BrowserPanelManager } = await import("./BrowserPanelManager"); + const session = createBrowserSession(); + const manager = new BrowserPanelManager( + { settingsPath: "settings.json" } as never, + "Mozilla/5.0 Chrome/141.0.0.0 Safari/537.36", + { browserSession: session as never }, + ); + const services = browserDataServices(manager); + const downloadWiring = services.downloads as unknown as { session: unknown }; + const importWiring = services.importer as unknown as { + session: unknown; + credentialStore: unknown; + }; + + expect(downloadWiring.session).toBe(session); + expect(importWiring.session).toBe(session); + expect(importWiring.credentialStore).toBe(services.credentials); + expect(session.on).toHaveBeenCalledWith("will-download", expect.any(Function)); + + const internal = await manager.openInternalPage("downloads"); + await manager.clearCookies(internal.tabId); + await manager.clearCache(internal.tabId); + + expect(session.clearStorageData).toHaveBeenCalledWith({ storages: ["cookies"] }); + expect(session.clearCache).toHaveBeenCalledOnce(); + + manager.dispose(); + expect(session.removeListener).toHaveBeenCalledWith("will-download", expect.any(Function)); + }); + + it("forwards download updates and opaque-id actions through the download manager", async () => { + const { BrowserPanelManager } = await import("./BrowserPanelManager"); + const manager = new BrowserPanelManager( + { settingsPath: "settings.json" } as never, + "Mozilla/5.0 Chrome/141.0.0.0 Safari/537.36", + { browserSession: createBrowserSession() as never }, + ); + const downloads = browserDataServices(manager).downloads; + if (!downloads) throw new Error("Expected the download manager"); + const download = { + id: "download-1", + filename: "report.pdf", + url: "https://example.test/report.pdf", + mimeType: "application/pdf", + state: "progressing", + receivedBytes: 10, + totalBytes: 100, + startTime: 1, + canResume: true, + } satisfies BrowserDownloadInfo; + const list = vi.spyOn(downloads, "list").mockReturnValue([download]); + const pause = vi.spyOn(downloads, "pause"); + const resume = vi.spyOn(downloads, "resume"); + const cancel = vi.spyOn(downloads, "cancel"); + const remove = vi.spyOn(downloads, "remove"); + const open = vi.spyOn(downloads, "open").mockResolvedValue(true); + const reveal = vi.spyOn(downloads, "reveal").mockReturnValue(true); + const events: unknown[] = []; + manager.addEventListener((event) => events.push(event)); + const { onEvent } = ( + downloads as unknown as { + options: { onEvent?(event: BrowserDownloadManagerEvent): void }; + } + ).options; + + onEvent?.({ type: "updated", download }); + onEvent?.({ type: "removed", downloadId: download.id }); + + expect(events).toContainEqual({ type: "download-updated", download }); + expect(events).toContainEqual({ type: "download-removed", downloadId: download.id }); + expect(list).not.toHaveBeenCalled(); + expect(manager.getDownloads()).toEqual([download]); + + await manager.downloadAction(download.id, "pause"); + await manager.downloadAction(download.id, "resume"); + await manager.downloadAction(download.id, "cancel"); + await manager.downloadAction(download.id, "remove"); + await manager.downloadAction(download.id, "open"); + await manager.downloadAction(download.id, "show-in-folder"); + + expect(pause).toHaveBeenCalledWith(download.id); + expect(resume).toHaveBeenCalledWith(download.id); + expect(cancel).toHaveBeenCalledWith(download.id); + expect(remove).toHaveBeenCalledWith(download.id); + expect(open).toHaveBeenCalledWith(download.id); + expect(reveal).toHaveBeenCalledWith(download.id); + + open.mockResolvedValueOnce(false); + await expect(manager.downloadAction(download.id, "open")).rejects.toThrow( + "Unable to open download", + ); + reveal.mockReturnValueOnce(false); + await expect(manager.downloadAction(download.id, "show-in-folder")).rejects.toThrow( + "Unable to show download in folder", + ); + }); + + it("notifies credential consumers only after credential data changes", async () => { + const { BrowserPanelManager } = await import("./BrowserPanelManager"); + const manager = new BrowserPanelManager( + { settingsPath: "settings.json" } as never, + "Mozilla/5.0 Chrome/141.0.0.0 Safari/537.36", + { browserSession: createBrowserSession() as never }, + ); + const importer = browserDataServices(manager).importer; + if (!importer) throw new Error("Expected the browser importer"); + const events: unknown[] = []; + manager.addEventListener((event) => events.push(event)); + + const credential = manager.upsertCredential({ + origin: "https://example.test", + username: "alice", + password: "secret", + }); + manager.deleteCredential("missing"); + manager.deleteCredential(credential.id); + const importData = vi + .spyOn(importer, "importData") + .mockResolvedValueOnce(emptyImportResult()) + .mockResolvedValueOnce({ ...emptyImportResult(), passwordsImported: 1 }); + await manager.importBrowserData({ + sourceId: "source-1", + passwords: true, + cookies: false, + acknowledgeProtectedData: true, + }); + await manager.importBrowserData({ + sourceId: "source-1", + passwords: true, + cookies: false, + acknowledgeProtectedData: true, + }); + + expect(events.filter((event) => isCredentialsUpdatedEvent(event))).toHaveLength(3); + expect(importData).toHaveBeenCalledTimes(2); + }); + it("keeps automation active until every explicit session ends", async () => { const { BrowserPanelManager } = await import("./BrowserPanelManager"); const manager = new BrowserPanelManager( diff --git a/src/main/browser/BrowserPanelManager.ts b/src/main/browser/BrowserPanelManager.ts index a3575ee1e..f816999fc 100644 --- a/src/main/browser/BrowserPanelManager.ts +++ b/src/main/browser/BrowserPanelManager.ts @@ -1,8 +1,15 @@ import { randomUUID } from "node:crypto"; -import { BrowserWindow, shell } from "electron"; +import { BrowserWindow, shell, type Session } from "electron"; import { IPC_EVENT_CHANNELS, type BrowserEvent, + type BrowserCredentialInfo, + type BrowserDeviceEmulation, + type BrowserDownloadInfo, + type BrowserInternalPage, + type BrowserImportResult, + type BrowserImportSourceInfo, + type BrowserPrintResult, type BrowserState, type BrowserStartPickerResult, type BrowserTabGroupInfo, @@ -15,7 +22,10 @@ import { dbGetState, dbSetState } from "../db"; import { readSharedSettingsFile } from "../sharedSettingsFile"; import { saveClipboardImageFile } from "../attachments/localFiles"; import { BrowserLoginCaptureCoordinator } from "./BrowserLoginCaptureCoordinator"; -import { BrowserTab, resolveWebContentsById } from "./BrowserTab"; +import { BrowserCredentialStore } from "./BrowserCredentialStore"; +import { BrowserDownloadManager } from "./BrowserDownloadManager"; +import { BrowserTab, resolveWebContentsById, type BrowserTabSnapshot } from "./BrowserTab"; +import { WindowsChromiumImportService } from "./WindowsChromiumImportService"; import { BrowserTabGroups } from "./BrowserTabGroups"; import { BrowserHistoryStore, fetchSearchSuggestions } from "./browserHistory"; import { BrowserBookmarkStore, type BrowserBookmark } from "./browserBookmarks"; @@ -30,6 +40,10 @@ const ATTACH_TIMEOUT_MS = 8000; const AUTOMATION_GRACE_MS = 45_000; const INTERNAL_BROWSER_PROTOCOLS = new Set(["http:", "https:"]); const SYSTEM_BROWSER_PROTOCOLS = new Set(["http:", "https:", "mailto:"]); +const INTERNAL_PAGES: Record = { + downloads: { url: "chrome://downloads/", title: "Download history" }, + passwords: { url: "chrome://password-manager/passwords", title: "Password Manager" }, +}; interface PersistedTabsState { tabs: Array<{ url: string; title: string; groupId?: string }>; @@ -57,6 +71,7 @@ type PickerPayload = interface BrowserPanelManagerOptions { isExtracted?: () => boolean; focusExtractedWindow?: () => void; + browserSession?: Session; } export class BrowserPanelManager { @@ -72,6 +87,10 @@ export class BrowserPanelManager { private persistTimer: ReturnType | null = null; private readonly history = new BrowserHistoryStore(); private readonly bookmarks = new BrowserBookmarkStore(); + private readonly credentials = new BrowserCredentialStore(); + private readonly downloads: BrowserDownloadManager | null; + private readonly importer: WindowsChromiumImportService | null; + private readonly internalPages = new Map(); private restored = false; private automationActive = false; private readonly automationSessions = new Set(); @@ -90,6 +109,20 @@ export class BrowserPanelManager { private readonly browserUserAgent: string, private readonly options: BrowserPanelManagerOptions = {}, ) { + this.downloads = options.browserSession + ? new BrowserDownloadManager(options.browserSession, { + onEvent: (event) => { + if (event.type === "removed") { + this.emit({ type: "download-removed", downloadId: event.downloadId }); + } else { + this.emit({ type: "download-updated", download: event.download }); + } + }, + }) + : null; + this.importer = options.browserSession + ? new WindowsChromiumImportService(options.browserSession, this.credentials) + : null; this.unsubscribePicker = onPickerCommit((commit) => { void this.onPickerCommit(commit); }); @@ -103,7 +136,7 @@ export class BrowserPanelManager { const groups = this.tabGroups.serialize(); const state: PersistedTabsState = { tabs: this.tabs.map((t) => { - const s = t.snapshot(); + const s = this.toInfo(t); const groupId = this.tabGroups.groupIdForTab(t.tabId); return { url: s.url, title: s.title, ...(groupId ? { groupId } : {}) }; }), @@ -182,10 +215,12 @@ export class BrowserPanelManager { this.automationActive = false; this.clearPickerShortcut(); this.loginCoordinator.cancelLoginConfirmations(); + this.downloads?.dispose(); for (const t of this.tabs) { void t.destroy(); } this.tabs = []; + this.internalPages.clear(); this.activeTabId = null; this.hosts.clear(); } @@ -312,7 +347,7 @@ export class BrowserPanelManager { async ensureTabReady(tabId: string): Promise { this.markAutomationActivity(); const tab = this.findTab(tabId); - if (!tab || tab.isAttached()) return; + if (!tab || this.internalPages.has(tabId) || tab.isAttached()) return; await this.awaitAttach(tab); } @@ -375,18 +410,22 @@ export class BrowserPanelManager { return true; } - private toInfo(t: BrowserTab): BrowserTabInfo { - const s = t.snapshot(); + private toInfo(t: BrowserTab, s: BrowserTabSnapshot = t.snapshot()): BrowserTabInfo { const groupId = this.tabGroups.groupIdForTab(s.tabId); + const internalPage = this.internalPages.get(s.tabId); + const internal = internalPage ? INTERNAL_PAGES[internalPage] : null; return { tabId: s.tabId, - url: s.url, - title: s.title, - loading: s.loading, - canGoBack: s.canGoBack, - canGoForward: s.canGoForward, + url: internal?.url ?? s.url, + title: internal?.title ?? s.title, + loading: internal ? false : s.loading, + canGoBack: internal ? false : s.canGoBack, + canGoForward: internal ? false : s.canGoForward, devToolsOpen: s.devToolsOpen, - ...(s.faviconUrl ? { faviconUrl: s.faviconUrl } : {}), + zoomFactor: s.zoomFactor, + ...(internalPage ? { internalPage } : {}), + ...(!internal && s.deviceEmulation ? { deviceEmulation: s.deviceEmulation } : {}), + ...(!internal && s.faviconUrl ? { faviconUrl: s.faviconUrl } : {}), ...(groupId ? { groupId } : {}), }; } @@ -467,7 +506,7 @@ export class BrowserPanelManager { attachWebContents(tabId: string, webContentsId: number): void { const tab = this.findTab(tabId); - if (!tab) return; + if (!tab || this.internalPages.has(tabId)) return; // Reject a host window's own WebContents by id first, before resolving it. for (const host of this.hosts) { if (host.webContents.id === webContentsId) return; @@ -500,13 +539,18 @@ export class BrowserPanelManager { this.revealForUserOpen(); } const tabId = `tab-${randomUUID()}`; + const internalPage = payload.url ? internalPageForUrl(payload.url) : undefined; + if (internalPage) this.internalPages.set(tabId, internalPage); const tab = new BrowserTab({ tabId, - ...(payload.url ? { initialUrl: payload.url } : {}), + ...(payload.url && !internalPage ? { initialUrl: payload.url } : {}), userAgent: this.browserUserAgent, - onUpdate: (snap) => { - this.emit({ type: "tab-updated", tab: { ...snap } }); - if (!snap.loading) this.history.record(snap.url, snap.title, Date.now()); + onUpdate: (snapshot) => { + const info = this.toInfo(tab, snapshot); + this.emit({ type: "tab-updated", tab: info }); + if (!info.loading && !info.internalPage) { + this.history.record(info.url, info.title, Date.now()); + } this.schedulePersist(); }, onAttention: (id) => { @@ -515,6 +559,12 @@ export class BrowserPanelManager { onPopup: (_sourceTabId, popupUrl) => { void this.openLink(popupUrl).catch(() => {}); }, + onFindRequested: (id) => { + this.emit({ type: "find-open-requested", tabId: id }); + }, + onFindResult: (id, result) => { + this.emit({ type: "find-result", result: { tabId: id, ...result } }); + }, }); this.tabs.push(tab); // Agent-created tabs auto-join a group (parity with the external extension) @@ -532,7 +582,7 @@ export class BrowserPanelManager { this.schedulePersist(); // Wait for the renderer to mount the and attach its webContents // so callers (e.g. MCP) can immediately use cdp / dialogs / network. - if (opts.awaitAttach !== false) { + if (opts.awaitAttach !== false && !internalPage) { await this.awaitAttach(tab); } return this.toInfo(tab); @@ -570,6 +620,7 @@ export class BrowserPanelManager { if (idx < 0) return; const [tab] = this.tabs.splice(idx, 1); if (!tab) return; + this.internalPages.delete(tabId); await tab.destroy(); this.tabGroups.removeTab(tabId); if (this.activeTabId === tabId) { @@ -583,12 +634,32 @@ export class BrowserPanelManager { async navigate(tabId: string, url: string): Promise { const t = this.findTab(tabId); if (!t) throw new Error(`No browser tab: ${tabId}`); + const internalPage = internalPageForUrl(url); + if (internalPage) { + this.internalPages.set(tabId, internalPage); + this.emitState(); + this.schedulePersist(); + return; + } + this.internalPages.delete(tabId); await t.loadURL(url); } + async openInternalPage(page: BrowserInternalPage): Promise { + const existing = this.tabs.find((tab) => this.internalPages.get(tab.tabId) === page); + if (existing) { + this.setActiveTab(existing.tabId); + return this.toInfo(existing); + } + return await this.createTab( + { url: INTERNAL_PAGES[page].url, activate: true }, + { awaitAttach: false }, + ); + } + async back(tabId: string): Promise { const t = this.findTab(tabId); - if (!t || !t.isAttached()) return; + if (!t || this.internalPages.has(tabId) || !t.isAttached()) return; const wc = t.webContents; if (wc.navigationHistory.canGoBack()) { wc.navigationHistory.goBack(); @@ -597,7 +668,7 @@ export class BrowserPanelManager { async forward(tabId: string): Promise { const t = this.findTab(tabId); - if (!t || !t.isAttached()) return; + if (!t || this.internalPages.has(tabId) || !t.isAttached()) return; const wc = t.webContents; if (wc.navigationHistory.canGoForward()) { wc.navigationHistory.goForward(); @@ -606,22 +677,123 @@ export class BrowserPanelManager { async reload(tabId: string): Promise { const t = this.findTab(tabId); - if (!t || !t.isAttached()) return; + if (!t || this.internalPages.has(tabId) || !t.isAttached()) return; t.webContents.reload(); } async hardReload(tabId: string): Promise { const t = this.findTab(tabId); - if (!t) return; + if (!t || this.internalPages.has(tabId)) return; t.hardReload(); } async toggleDevTools(tabId: string): Promise { const t = this.findTab(tabId); - if (!t) return; + if (!t || this.internalPages.has(tabId)) return; t.toggleDevTools(); } + findInPage( + tabId: string, + text: string, + options: { forward: boolean; findNext: boolean; matchCase: boolean }, + ): void { + if (this.internalPages.has(tabId)) return; + this.findTab(tabId)?.findInPage(text, options); + } + + stopFindInPage( + tabId: string, + action: "clearSelection" | "keepSelection" | "activateSelection", + ): void { + if (this.internalPages.has(tabId)) return; + this.findTab(tabId)?.stopFindInPage(action); + } + + async print(tabId: string): Promise { + const tab = this.findTab(tabId); + if (!tab || this.internalPages.has(tabId)) return { ok: false }; + return await tab.print(); + } + + setZoomFactor(tabId: string, zoomFactor: number): void { + if (this.internalPages.has(tabId)) return; + this.findTab(tabId)?.setZoomFactor(zoomFactor); + } + + setDeviceEmulation(tabId: string, emulation: BrowserDeviceEmulation | null): void { + if (this.internalPages.has(tabId)) return; + this.findTab(tabId)?.setDeviceEmulation(emulation); + } + + getDownloads(): BrowserDownloadInfo[] { + return this.downloads?.list() ?? []; + } + + async downloadAction( + downloadId: string, + action: "pause" | "resume" | "cancel" | "remove" | "open" | "show-in-folder", + ): Promise { + if (!this.downloads) return; + if (action === "pause") this.downloads.pause(downloadId); + else if (action === "resume") this.downloads.resume(downloadId); + else if (action === "cancel") this.downloads.cancel(downloadId); + else if (action === "remove") this.downloads.remove(downloadId); + else if (action === "open") { + if (!(await this.downloads.open(downloadId))) throw new Error("Unable to open download"); + } else if (!this.downloads.reveal(downloadId)) { + throw new Error("Unable to show download in folder"); + } + } + + listCredentials(): BrowserCredentialInfo[] { + return this.credentials.list(); + } + + getCredentialPassword(id: string): string | null { + return this.credentials.get(id)?.password ?? null; + } + + upsertCredential(input: { + id?: string; + origin: string; + username: string; + password: string; + }): BrowserCredentialInfo { + const credential = this.credentials.upsert(input); + this.emit({ type: "credentials-updated" }); + return credential; + } + + deleteCredential(id: string): void { + if (this.credentials.delete(id)) this.emit({ type: "credentials-updated" }); + } + + async listImportSources(): Promise { + return (await this.importer?.listSources()) ?? []; + } + + async importBrowserData(input: { + sourceId: string; + passwords: boolean; + cookies: boolean; + acknowledgeProtectedData: boolean; + }): Promise { + if (!this.importer) { + return { + passwordsImported: 0, + cookiesImported: 0, + passwordsSkipped: 0, + cookiesSkipped: 0, + protectedItemsSkipped: 0, + errors: ["browser-import-unavailable"], + }; + } + const result = await this.importer.importData(input); + if (result.passwordsImported > 0) this.emit({ type: "credentials-updated" }); + return result; + } + async clearHistory(tabId: string): Promise { this.history.clear(); const t = this.findTab(tabId); @@ -643,12 +815,20 @@ export class BrowserPanelManager { } async clearCookies(tabId: string): Promise { + if (this.options.browserSession) { + await this.options.browserSession.clearStorageData({ storages: ["cookies"] }); + return; + } const t = this.findTab(tabId); if (!t) return; await t.clearCookies(); } async clearCache(tabId: string): Promise { + if (this.options.browserSession) { + await this.options.browserSession.clearCache(); + return; + } const t = this.findTab(tabId); if (!t) return; await t.clearCache(); @@ -656,7 +836,7 @@ export class BrowserPanelManager { async capturePng(tabId: string): Promise { const t = this.findTab(tabId); - if (!t || !t.isAttached()) return null; + if (!t || this.internalPages.has(tabId) || !t.isAttached()) return null; return await t.capturePng(); } @@ -716,10 +896,12 @@ export class BrowserPanelManager { } getActiveTab(): BrowserTab | null { - return this.activeTabId ? (this.findTab(this.activeTabId) ?? null) : null; + if (!this.activeTabId || this.internalPages.has(this.activeTabId)) return null; + return this.findTab(this.activeTabId) ?? null; } getTab(tabId: string): BrowserTab | null { + if (this.internalPages.has(tabId)) return null; return this.findTab(tabId) ?? null; } @@ -728,7 +910,7 @@ export class BrowserPanelManager { tabId: string; }): Promise { const tab = this.findTab(payload.tabId); - if (!tab) { + if (!tab || this.internalPages.has(payload.tabId)) { return { ok: false, error: `No browser tab: ${payload.tabId}` }; } if (!tab.isAttached()) { @@ -855,6 +1037,12 @@ function isEscapeKeyDown(input: Electron.Input): boolean { return input.key === "Escape" || input.key === "Esc" || input.code === "Escape"; } +function internalPageForUrl(url: string): BrowserInternalPage | undefined { + return ( + Object.entries(INTERNAL_PAGES) as Array<[BrowserInternalPage, { url: string; title: string }]> + ).find(([, page]) => page.url === url)?.[0]; +} + function isPickerPayload(value: unknown): value is PickerPayload { if (!value || typeof value !== "object") return false; const kind = (value as { kind?: unknown }).kind; diff --git a/src/main/browser/BrowserTab.test.ts b/src/main/browser/BrowserTab.test.ts index 2c28eb542..b70c46602 100644 --- a/src/main/browser/BrowserTab.test.ts +++ b/src/main/browser/BrowserTab.test.ts @@ -5,6 +5,11 @@ const installNavigationGuards = vi.hoisted(() => vi.fn<() => () => void>(() => vi.fn<() => void>()), ); const dialogEnable = vi.hoisted(() => vi.fn<() => Promise>().mockResolvedValue(undefined)); +const cdpSend = vi.hoisted(() => + vi.fn<(method: string, params?: Record) => Promise>(() => + Promise.resolve(), + ), +); vi.mock("electron", () => ({ webContents: { fromId: vi.fn<() => null>(() => null) }, @@ -14,6 +19,7 @@ vi.mock("./cdp/cdpClient", () => ({ CdpClient: class CdpClient { attach = vi.fn<() => Promise>().mockResolvedValue(undefined); detach = vi.fn<() => void>(); + send = cdpSend; }, })); @@ -42,6 +48,18 @@ function createWebContents(initialUrl = "https://example.com/") { return { session: {}, setUserAgent: vi.fn<(userAgent: string) => void>(), + setZoomFactor: vi.fn<(zoomFactor: number) => void>(), + enableDeviceEmulation: vi.fn<(parameters: Electron.Parameters) => void>(), + disableDeviceEmulation: vi.fn<() => void>(), + findInPage: vi.fn<(text: string, options: Electron.FindInPageOptions) => number>(() => 17), + stopFindInPage: + vi.fn<(action: "clearSelection" | "keepSelection" | "activateSelection") => void>(), + print: vi.fn< + ( + options: Electron.WebContentsPrintOptions, + callback: (success: boolean, failureReason: string) => void, + ) => void + >((_options, callback) => callback(true, "")), getURL: vi.fn<() => string>(() => currentUrl), getTitle: vi.fn<() => string>(() => "Example"), isDestroyed: vi.fn<() => boolean>(() => false), @@ -60,6 +78,7 @@ function createWebContents(initialUrl = "https://example.com/") { clear: vi.fn<() => void>(), }, reload: vi.fn<() => void>(), + reloadIgnoringCache: vi.fn<() => void>(), loadURL: vi.fn<(url: string) => Promise>((url) => { currentUrl = url; return Promise.resolve(); @@ -102,6 +121,8 @@ describe("BrowserTab", () => { onUpdate: vi.fn<() => void>(), onAttention: vi.fn<() => void>(), onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), }); const webContents = createWebContents(); @@ -112,6 +133,237 @@ describe("BrowserTab", () => { expect(installNavigationGuards).toHaveBeenCalled(); }); + it("requests find from the guest shortcut and forwards find results", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const onFindRequested = vi.fn<(tabId: string) => void>(); + const onFindResult = vi.fn< + ( + tabId: string, + result: { + requestId: number; + activeMatchOrdinal: number; + matches: number; + finalUpdate: boolean; + }, + ) => void + >(); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate: vi.fn<() => void>(), + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested, + onFindResult, + }); + const webContents = createWebContents(); + tab.attach(webContents as never); + + const handler = captureBeforeInput(webContents); + const shortcut = { preventDefault: vi.fn<() => void>() }; + handler(shortcut, { type: "keyDown", control: true, key: "f" }); + + expect(shortcut.preventDefault).toHaveBeenCalledOnce(); + expect(onFindRequested).toHaveBeenCalledWith("tab-1"); + + expect(tab.findInPage("needle", { forward: true, findNext: true, matchCase: true })).toBe(17); + expect(webContents.findInPage).toHaveBeenCalledWith("needle", { + forward: true, + findNext: true, + matchCase: true, + }); + + webContents.emit( + "found-in-page", + {}, + { + requestId: 16, + activeMatchOrdinal: 1, + matches: 1, + finalUpdate: true, + }, + ); + expect(onFindResult).not.toHaveBeenCalled(); + + webContents.emit( + "found-in-page", + {}, + { + requestId: 17, + activeMatchOrdinal: 2, + matches: 4, + finalUpdate: true, + }, + ); + expect(onFindResult).toHaveBeenCalledWith("tab-1", { + requestId: 17, + activeMatchOrdinal: 2, + matches: 4, + finalUpdate: true, + }); + + tab.stopFindInPage("clearSelection"); + expect(webContents.stopFindInPage).toHaveBeenCalledWith("clearSelection"); + }); + + it("prints natively from the method and guest shortcut", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate: vi.fn<() => void>(), + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), + }); + const webContents = createWebContents(); + tab.attach(webContents as never); + + await expect(tab.print()).resolves.toEqual({ ok: true }); + expect(webContents.print).toHaveBeenCalledWith({ silent: false }, expect.any(Function)); + + const handler = captureBeforeInput(webContents); + const shortcut = { preventDefault: vi.fn<() => void>() }; + handler(shortcut, { type: "keyDown", meta: true, key: "p" }); + + expect(shortcut.preventDefault).toHaveBeenCalledOnce(); + expect(webContents.print).toHaveBeenCalledTimes(2); + + webContents.print.mockImplementationOnce((_options, callback) => { + callback(false, "Print job canceled"); + }); + await expect(tab.print()).resolves.toEqual({ ok: false, error: "Print job canceled" }); + }); + + it("applies zoom and device emulation and exposes them in the snapshot", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const onUpdate = vi.fn<() => void>(); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate, + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), + }); + const webContents = createWebContents(); + tab.attach(webContents as never); + onUpdate.mockClear(); + + tab.setZoomFactor(1.25); + expect(webContents.setZoomFactor).toHaveBeenLastCalledWith(1.25); + expect(tab.snapshot().zoomFactor).toBe(1.25); + + const emulation = { + width: 430, + height: 932, + deviceScaleFactor: 3, + scale: 0.75, + mobile: true, + touch: true, + preset: "iPhone 15 Pro Max", + }; + tab.setDeviceEmulation(emulation); + + expect(webContents.enableDeviceEmulation).toHaveBeenCalledWith({ + screenPosition: "mobile", + screenSize: { width: 430, height: 932 }, + viewPosition: { x: 0, y: 0 }, + deviceScaleFactor: 3, + viewSize: { width: 430, height: 932 }, + scale: 0.75, + }); + expect(cdpSend).toHaveBeenCalledWith("Emulation.setTouchEmulationEnabled", { + enabled: true, + maxTouchPoints: 5, + }); + expect(tab.snapshot()).toMatchObject({ zoomFactor: 1.25, deviceEmulation: emulation }); + + webContents.enableDeviceEmulation.mockClear(); + cdpSend.mockClear(); + tab.setDeviceEmulation(emulation); + expect(webContents.enableDeviceEmulation).not.toHaveBeenCalled(); + expect(cdpSend).not.toHaveBeenCalled(); + + tab.setDeviceEmulation(null); + expect(webContents.disableDeviceEmulation).toHaveBeenCalledOnce(); + expect(cdpSend).toHaveBeenLastCalledWith("Emulation.setTouchEmulationEnabled", { + enabled: false, + }); + expect(tab.snapshot().deviceEmulation).toBeUndefined(); + expect(onUpdate).toHaveBeenCalled(); + }); + + it("reapplies zoom and device emulation when webContents reattaches", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate: vi.fn<() => void>(), + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), + }); + const emulation = { + width: 1024, + height: 768, + deviceScaleFactor: 1, + scale: 1, + mobile: false, + touch: false, + }; + tab.setZoomFactor(1.5); + tab.setDeviceEmulation(emulation); + + const first = createWebContents(); + tab.attach(first as never); + expect(first.setZoomFactor).toHaveBeenCalledWith(1.5); + expect(first.enableDeviceEmulation).toHaveBeenCalledOnce(); + + first.emit("destroyed"); + const second = createWebContents(); + tab.attach(second as never); + + expect(second.setZoomFactor).toHaveBeenCalledWith(1.5); + expect(second.enableDeviceEmulation).toHaveBeenCalledWith({ + screenPosition: "desktop", + screenSize: { width: 1024, height: 768 }, + viewPosition: { x: 0, y: 0 }, + deviceScaleFactor: 1, + viewSize: { width: 1024, height: 768 }, + scale: 1, + }); + }); + + it("preserves reload and hard-reload guest shortcuts", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate: vi.fn<() => void>(), + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), + }); + const webContents = createWebContents(); + tab.attach(webContents as never); + + const handler = captureBeforeInput(webContents); + const reload = { preventDefault: vi.fn<() => void>() }; + handler(reload, { type: "keyDown", control: true, key: "r" }); + expect(reload.preventDefault).toHaveBeenCalledOnce(); + expect(webContents.reload).toHaveBeenCalledOnce(); + + const hardReload = { preventDefault: vi.fn<() => void>() }; + handler(hardReload, { type: "keyDown", meta: true, shift: true, key: "r" }); + expect(hardReload.preventDefault).toHaveBeenCalledOnce(); + expect(webContents.reloadIgnoringCache).toHaveBeenCalledOnce(); + }); + it("does not clear newly navigated history after initial page cleanup", async () => { vi.useFakeTimers(); try { @@ -123,6 +375,8 @@ describe("BrowserTab", () => { onUpdate: vi.fn<() => void>(), onAttention: vi.fn<() => void>(), onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), }); const webContents = createWebContents("data:text/html,first"); tab.attach(webContents as never); @@ -142,6 +396,25 @@ describe("BrowserTab", () => { } }); + it("clears synthetic history when an unmounted tab receives its first navigation", async () => { + const { BrowserTab } = await import("./BrowserTab"); + const tab = new BrowserTab({ + tabId: "tab-1", + userAgent: "ua", + onUpdate: vi.fn<() => void>(), + onAttention: vi.fn<() => void>(), + onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), + }); + + await tab.loadURL("data:text/html,first"); + const webContents = createWebContents("data:text/html,first"); + tab.attach(webContents as never); + + expect(webContents.navigationHistory.clear).toHaveBeenCalledOnce(); + }); + it("navigates back/forward on Ctrl+[ and Ctrl+] keydown", async () => { const { BrowserTab } = await import("./BrowserTab"); const tab = new BrowserTab({ @@ -150,6 +423,8 @@ describe("BrowserTab", () => { onUpdate: vi.fn<() => void>(), onAttention: vi.fn<() => void>(), onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), }); const webContents = createWebContents(); tab.attach(webContents as never); @@ -175,6 +450,8 @@ describe("BrowserTab", () => { onUpdate: vi.fn<() => void>(), onAttention: vi.fn<() => void>(), onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), }); const webContents = createWebContents(); tab.attach(webContents as never); @@ -194,6 +471,8 @@ describe("BrowserTab", () => { onUpdate: vi.fn<() => void>(), onAttention: vi.fn<() => void>(), onPopup: vi.fn<() => void>(), + onFindRequested: vi.fn<() => void>(), + onFindResult: vi.fn<() => void>(), }); const webContents = createWebContents(); tab.attach(webContents as never); diff --git a/src/main/browser/BrowserTab.ts b/src/main/browser/BrowserTab.ts index 5fa591399..473637081 100644 --- a/src/main/browser/BrowserTab.ts +++ b/src/main/browser/BrowserTab.ts @@ -1,4 +1,5 @@ import { webContents as webContentsModule, type WebContents } from "electron"; +import type { BrowserDeviceEmulation, BrowserFindResult, BrowserPrintResult } from "@/shared/ipc"; import { CdpClient } from "./cdp/cdpClient"; import { DialogController } from "./cdp/dialogController"; import { NetworkCapture } from "./cdp/networkCapture"; @@ -17,9 +18,21 @@ export interface BrowserTabSnapshot { canGoBack: boolean; canGoForward: boolean; devToolsOpen: boolean; + zoomFactor: number; + deviceEmulation?: BrowserDeviceEmulation; faviconUrl?: string; } +export interface BrowserFindInPageOptions { + forward: boolean; + findNext: boolean; + matchCase: boolean; +} + +export type BrowserStopFindInPageAction = "clearSelection" | "keepSelection" | "activateSelection"; + +export type BrowserTabFindResult = Omit; + export interface BrowserTabOptions { tabId: string; initialUrl?: string; @@ -27,6 +40,8 @@ export interface BrowserTabOptions { onUpdate(snapshot: BrowserTabSnapshot): void; onAttention(tabId: string): void; onPopup(tabId: string, url: string): void; + onFindRequested(tabId: string): void; + onFindResult(tabId: string, result: BrowserTabFindResult): void; } export interface ConsoleEntry { @@ -57,6 +72,9 @@ export class BrowserTab { private initialHistoryUrl: string | null = null; private clearInitialHistoryTimer: ReturnType | null = null; private devToolsVisible = false; + private findRequestId: number | null = null; + private zoomFactor = 1; + private deviceEmulation: BrowserDeviceEmulation | undefined; private wcCleanups: Array<() => void> = []; private attachedPromise: Promise; private resolveAttached: (() => void) | null = null; @@ -105,8 +123,14 @@ export class BrowserTab { this.teardownListeners(); } this._webContents = webContents; + this.findRequestId = null; webContents.setUserAgent(this.opts.userAgent); + webContents.setZoomFactor(this.zoomFactor); this._cdp = new CdpClient(webContents); + if (this.deviceEmulation) { + this.applyDeviceEmulation(webContents, this.deviceEmulation); + this.applyTouchEmulation(this.deviceEmulation.touch); + } installSessionPermissions(webContents.session); const removeNavGuards = installNavigationGuards(webContents, (popupUrl) => { this.opts.onPopup(this.tabId, popupUrl); @@ -186,7 +210,29 @@ export class BrowserTab { wc.on("will-prevent-unload", onWillPreventUnload); this.wcCleanups.push(() => wc.removeListener("will-prevent-unload", onWillPreventUnload)); + const onFoundInPage = (_event: Electron.Event, result: Electron.Result) => { + if (this.findRequestId !== result.requestId) return; + this.opts.onFindResult(this.tabId, { + requestId: result.requestId, + activeMatchOrdinal: result.activeMatchOrdinal, + matches: result.matches, + finalUpdate: result.finalUpdate, + }); + }; + wc.on("found-in-page", onFoundInPage); + this.wcCleanups.push(() => wc.removeListener("found-in-page", onFoundInPage)); + const onBeforeInputEvent = (event: Electron.Event, input: Electron.Input) => { + if (isBrowserFindKeyDown(input)) { + event.preventDefault(); + this.opts.onFindRequested(this.tabId); + return; + } + if (isBrowserPrintKeyDown(input)) { + event.preventDefault(); + void this.print(); + return; + } if (isBrowserReloadKeyDown(input)) { event.preventDefault(); if (isBrowserHardReloadKeyDown(input)) { @@ -262,6 +308,7 @@ export class BrowserTab { this.teardownListeners(); this._webContents = null; this._cdp = null; + this.findRequestId = null; // The tab can be unmounted when the headless browser goes idle, then // remounted on the next automation. Re-arm the attach promise so a later // whenAttached() waits for that fresh attach instead of resolving stale. @@ -369,6 +416,8 @@ export class BrowserTab { canGoBack: wc?.navigationHistory.canGoBack() ?? false, canGoForward: wc?.navigationHistory.canGoForward() ?? false, devToolsOpen: this.devToolsVisible, + zoomFactor: this.zoomFactor, + ...(this.deviceEmulation ? { deviceEmulation: this.deviceEmulation } : {}), ...(this.faviconUrl ? { faviconUrl: this.faviconUrl } : {}), }; } @@ -382,6 +431,7 @@ export class BrowserTab { // initial `` (or a later attach) reflects the request. this.currentUrl = url; this.clearInitialHistoryOnLoad = true; + this.initialHistoryUrl = url; this.emit(); return; } @@ -422,6 +472,83 @@ export class BrowserTab { } catch {} } + findInPage(text: string, options: BrowserFindInPageOptions): number | null { + if (this.destroyed || !this.isAttached()) return null; + this.findRequestId = this.webContents.findInPage(text, options); + return this.findRequestId; + } + + stopFindInPage(action: BrowserStopFindInPageAction): void { + if (this.destroyed || !this.isAttached()) return; + this.findRequestId = null; + this.webContents.stopFindInPage(action); + } + + async print(): Promise { + if (this.destroyed || !this.isAttached()) { + return { ok: false }; + } + return await new Promise((resolve) => { + try { + this.webContents.print({ silent: false }, (success, failureReason) => { + resolve( + success + ? { ok: true } + : { ok: false, ...(failureReason ? { error: failureReason } : {}) }, + ); + }); + } catch (error) { + resolve({ + ok: false, + error: error instanceof Error ? error.message : String(error), + }); + } + }); + } + + setZoomFactor(zoomFactor: number): void { + if (this.destroyed) return; + if (this.isAttached()) this.webContents.setZoomFactor(zoomFactor); + this.zoomFactor = zoomFactor; + this.emit(); + } + + setDeviceEmulation(emulation: BrowserDeviceEmulation | null): void { + if (this.destroyed) return; + const next = emulation ? { ...emulation } : undefined; + if (deviceEmulationEqual(this.deviceEmulation, next)) return; + const touchChanged = this.deviceEmulation?.touch !== next?.touch; + if (this.isAttached()) { + if (next) { + this.applyDeviceEmulation(this.webContents, next); + } else { + this.webContents.disableDeviceEmulation(); + } + if (touchChanged) this.applyTouchEmulation(next?.touch === true); + } + this.deviceEmulation = next; + this.emit(); + } + + private applyDeviceEmulation(wc: WebContents, emulation: BrowserDeviceEmulation): void { + const size = { width: emulation.width, height: emulation.height }; + wc.enableDeviceEmulation({ + screenPosition: emulation.mobile ? "mobile" : "desktop", + screenSize: size, + viewPosition: { x: 0, y: 0 }, + deviceScaleFactor: emulation.deviceScaleFactor, + viewSize: size, + scale: emulation.scale, + }); + } + + private applyTouchEmulation(enabled: boolean): void { + const params: Record = enabled + ? { enabled: true, maxTouchPoints: 5 } + : { enabled: false }; + void this.cdp.send("Emulation.setTouchEmulationEnabled", params).catch(() => {}); + } + clearHistory(): void { if (this.destroyed) return; this.clearNavigationHistory(); @@ -485,10 +612,42 @@ function snapshotsEqual(a: BrowserTabSnapshot, b: BrowserTabSnapshot): boolean { a.canGoBack === b.canGoBack && a.canGoForward === b.canGoForward && a.devToolsOpen === b.devToolsOpen && + a.zoomFactor === b.zoomFactor && + deviceEmulationEqual(a.deviceEmulation, b.deviceEmulation) && a.faviconUrl === b.faviconUrl ); } +function deviceEmulationEqual( + a: BrowserDeviceEmulation | undefined, + b: BrowserDeviceEmulation | undefined, +): boolean { + if (!a || !b) return a === b; + return ( + a.width === b.width && + a.height === b.height && + a.deviceScaleFactor === b.deviceScaleFactor && + a.scale === b.scale && + a.mobile === b.mobile && + a.touch === b.touch && + a.preset === b.preset + ); +} + +function isBrowserFindKeyDown(input: Electron.Input): boolean { + if (input.type !== "keyDown") return false; + return ( + (input.control || input.meta) && !input.shift && !input.alt && input.key.toLowerCase() === "f" + ); +} + +function isBrowserPrintKeyDown(input: Electron.Input): boolean { + if (input.type !== "keyDown") return false; + return ( + (input.control || input.meta) && !input.shift && !input.alt && input.key.toLowerCase() === "p" + ); +} + function isBrowserReloadKeyDown(input: Electron.Input): boolean { if (input.type !== "keyDown") return false; if (input.key === "F5" || input.code === "F5") return true; diff --git a/src/main/browser/WindowsChromiumImportService.test.ts b/src/main/browser/WindowsChromiumImportService.test.ts new file mode 100644 index 000000000..4c4505a53 --- /dev/null +++ b/src/main/browser/WindowsChromiumImportService.test.ts @@ -0,0 +1,403 @@ +import { createCipheriv, createHash, randomBytes } from "node:crypto"; +import { existsSync } from "node:fs"; +import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +import { tmpdir } from "node:os"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { BrowserCredentialInput } from "./BrowserCredentialStore"; +import { + BROWSER_IMPORT_ERROR_CODES, + WindowsChromiumImportService, + type BrowserImportCookieDetails, + type BrowserImportDatabase, + type BrowserImportDatabaseFactory, +} from "./WindowsChromiumImportService"; + +const browserRoots = { + chrome: join("Google", "Chrome", "User Data"), + edge: join("Microsoft", "Edge", "User Data"), + brave: join("BraveSoftware", "Brave-Browser", "User Data"), + chromium: join("Chromium", "User Data"), +} as const; + +interface FixtureProfileOptions { + browser: keyof typeof browserRoots; + profile?: string; + profileLabel?: string; + passwords?: boolean; + cookies?: boolean; + encryptedKey?: Buffer; + appBound?: boolean; + wal?: boolean; +} + +let fixtureRoot: string; + +beforeEach(async () => { + fixtureRoot = await mkdtemp(join(tmpdir(), "poracode-chromium-import-test-")); +}); + +afterEach(async () => { + await rm(fixtureRoot, { recursive: true, force: true }); +}); + +describe("WindowsChromiumImportService", () => { + it("discovers Chrome, Edge, Brave, and Chromium profiles without exposing paths", async () => { + await Promise.all( + (Object.keys(browserRoots) as Array).map((browser, index) => + createProfile({ + browser, + profile: index === 0 ? "Default" : `Profile ${index}`, + profileLabel: `${browser} profile`, + passwords: true, + cookies: true, + appBound: browser === "chrome", + }), + ), + ); + const { service } = createService({ databaseFactory: emptyDatabaseFactory() }); + + const sources = await service.listSources(); + + expect(sources.map((source) => source.browser).sort()).toEqual([ + "brave", + "chrome", + "chromium", + "edge", + ]); + expect(sources.find((source) => source.browser === "chrome")).toMatchObject({ + profileLabel: "chrome profile", + supportsPasswords: true, + supportsCookies: true, + hasAppBoundData: true, + }); + for (const source of sources) { + expect(source.id).toMatch(/^chromium-(brave|chrome|chromium|edge)-[a-f0-9]{20}$/u); + expect(source.id).not.toContain("Default"); + expect(source).not.toHaveProperty("profilePath"); + } + }); + + it("imports v10/v11 passwords and cookies from read-only WAL snapshots", async () => { + const key = randomBytes(32); + const wrappedKey = Buffer.from("wrapped-chromium-key"); + await createProfile({ + browser: "chrome", + passwords: true, + cookies: true, + encryptedKey: Buffer.concat([Buffer.from("DPAPI"), wrappedKey]), + wal: true, + }); + const loginRows = [ + { + origin_url: "https://example.test/login", + action_url: "https://example.test/session", + signon_realm: "https://example.test/", + username_value: "alice", + password_value: encryptChromiumValue("v10", key, Buffer.from("correct horse")), + blacklisted_by_user: 0, + }, + ]; + const cookieRows = [ + { + host_key: ".example.test", + name: "session", + value: "", + encrypted_value: encryptChromiumValue( + "v11", + key, + Buffer.concat([ + createHash("sha256").update(".example.test").digest(), + Buffer.from("cookie-value"), + ]), + ), + path: "/account", + expires_utc: 11_644_473_600_000_000 + 3_600_000_000, + is_secure: 1, + is_httponly: 1, + samesite: 1, + }, + ]; + const snapshotChecks: boolean[] = []; + const databaseFactory = vi.fn((path, options) => { + expect(options).toEqual({ readonly: true, fileMustExist: true }); + snapshotChecks.push(fileExistsSyncForTest(`${path}-wal`)); + return databaseFor( + basename(path) === "Login Data" ? loginRows : cookieRows, + basename(path) === "Cookies" ? 24 : undefined, + ); + }); + const dpapiDecrypt = vi.fn<(encrypted: Buffer) => Promise>(async (encrypted) => { + expect(encrypted).toEqual(wrappedKey); + return key; + }); + const { service, cookieSet, credentialUpsertMany } = createService({ + databaseFactory, + dpapiDecrypt, + }); + const [source] = await service.listSources(); + + const result = await service.importData({ + sourceId: source!.id, + passwords: true, + cookies: true, + }); + + expect(result).toEqual({ + passwordsImported: 1, + cookiesImported: 1, + passwordsSkipped: 0, + cookiesSkipped: 0, + protectedItemsSkipped: 0, + errors: [], + }); + expect(dpapiDecrypt).toHaveBeenCalledOnce(); + expect(credentialUpsertMany).toHaveBeenCalledWith([ + { + origin: "https://example.test/login", + username: "alice", + password: "correct horse", + source: "Google Chrome — Default", + }, + ]); + expect(cookieSet).toHaveBeenCalledWith({ + url: "https://example.test/account", + name: "session", + value: "cookie-value", + domain: ".example.test", + path: "/account", + secure: true, + httpOnly: true, + sameSite: "lax", + expirationDate: 3600, + }); + expect(snapshotChecks).toEqual([true, true]); + const snapshotParents = new Set(databaseFactory.mock.calls.map(([path]) => dirname(path))); + expect(snapshotParents.size).toBe(1); + for (const directory of snapshotParents) await expectPathMissing(directory); + }); + + it("preserves host-only Chromium cookies without broadening them to subdomains", async () => { + await createProfile({ browser: "chromium", cookies: true }); + const databaseFactory: BrowserImportDatabaseFactory = () => + databaseFor([ + { + host_key: "example.test", + name: "host-only", + value: "cookie-value", + encrypted_value: Buffer.alloc(0), + path: "/", + expires_utc: 0, + is_secure: 1, + is_httponly: 0, + samesite: 1, + }, + ]); + const { service, cookieSet } = createService({ databaseFactory }); + const [source] = await service.listSources(); + + const result = await service.importData({ + sourceId: source!.id, + passwords: false, + cookies: true, + }); + + expect(result.cookiesImported).toBe(1); + expect(cookieSet).toHaveBeenCalledWith({ + url: "https://example.test/", + name: "host-only", + value: "cookie-value", + path: "/", + secure: true, + httpOnly: false, + sameSite: "lax", + }); + }); + + it("skips partitioned Chromium cookies that Electron cannot preserve", async () => { + await createProfile({ browser: "chrome", cookies: true }); + const databaseFactory: BrowserImportDatabaseFactory = (_path, _options) => + databaseFor( + [ + { + host_key: ".example.test", + name: "partitioned", + value: "cookie-value", + encrypted_value: Buffer.alloc(0), + path: "/", + expires_utc: 0, + is_secure: 1, + is_httponly: 0, + samesite: 0, + top_frame_site_key: "https://top-level.test", + }, + ], + 24, + ); + const { service, cookieSet } = createService({ databaseFactory }); + const [source] = await service.listSources(); + + const result = await service.importData({ + sourceId: source!.id, + passwords: false, + cookies: true, + }); + + expect(result.cookiesImported).toBe(0); + expect(result.cookiesSkipped).toBe(1); + expect(result.errors).toEqual([BROWSER_IMPORT_ERROR_CODES.partitionedCookieSkipped]); + expect(cookieSet).not.toHaveBeenCalled(); + }); + + it("counts v20 App-Bound values as protected without invoking DPAPI", async () => { + await createProfile({ + browser: "edge", + passwords: true, + cookies: true, + appBound: true, + }); + const databaseFactory: BrowserImportDatabaseFactory = (path) => + databaseFor( + basename(path) === "Login Data" + ? [ + { + origin_url: "https://protected.test/", + username_value: "user", + password_value: Buffer.concat([Buffer.from("v20"), Buffer.alloc(32, 1)]), + blacklisted_by_user: 0, + }, + ] + : [ + { + host_key: ".protected.test", + name: "session", + value: "", + encrypted_value: Buffer.concat([Buffer.from("v20"), Buffer.alloc(32, 2)]), + path: "/", + expires_utc: 0, + is_secure: 1, + is_httponly: 1, + samesite: -1, + }, + ], + ); + const dpapiDecrypt = vi.fn<(encrypted: Buffer) => Promise>(); + const { service, cookieSet, credentialUpsertMany } = createService({ + databaseFactory, + dpapiDecrypt, + }); + const [source] = await service.listSources(); + + const result = await service.importData({ + sourceId: source!.id, + passwords: true, + cookies: true, + }); + + expect(result).toEqual({ + passwordsImported: 0, + cookiesImported: 0, + passwordsSkipped: 1, + cookiesSkipped: 1, + protectedItemsSkipped: 2, + errors: [BROWSER_IMPORT_ERROR_CODES.appBoundSkipped], + }); + expect(dpapiDecrypt).not.toHaveBeenCalled(); + expect(credentialUpsertMany).not.toHaveBeenCalled(); + expect(cookieSet).not.toHaveBeenCalled(); + }); + + it("rejects path-like source IDs instead of resolving caller-controlled paths", async () => { + await createProfile({ browser: "brave", passwords: true, cookies: true }); + const databaseFactory = vi.fn(); + const { service, cookieSet, credentialUpsertMany } = createService({ databaseFactory }); + + const result = await service.importData({ + sourceId: "../../Google/Chrome/User Data/Default", + passwords: true, + cookies: true, + }); + + expect(result.errors).toEqual([BROWSER_IMPORT_ERROR_CODES.sourceNotFound]); + expect(databaseFactory).not.toHaveBeenCalled(); + expect(cookieSet).not.toHaveBeenCalled(); + expect(credentialUpsertMany).not.toHaveBeenCalled(); + }); +}); + +async function createProfile(options: FixtureProfileOptions): Promise { + const profile = options.profile ?? "Default"; + const userDataPath = join(fixtureRoot, browserRoots[options.browser]); + const profilePath = join(userDataPath, profile); + await mkdir(join(profilePath, "Network"), { recursive: true }); + const localState = { + profile: { info_cache: { [profile]: { name: options.profileLabel ?? profile } } }, + os_crypt: { + ...(options.encryptedKey ? { encrypted_key: options.encryptedKey.toString("base64") } : {}), + ...(options.appBound + ? { app_bound_encrypted_key: Buffer.from("APPBprotected").toString("base64") } + : {}), + }, + }; + await writeFile(join(userDataPath, "Local State"), JSON.stringify(localState)); + if (options.passwords) { + const path = join(profilePath, "Login Data"); + await writeFile(path, "fixture"); + if (options.wal) await writeFile(`${path}-wal`, "wal"); + } + if (options.cookies) { + const path = join(profilePath, "Network", "Cookies"); + await writeFile(path, "fixture"); + if (options.wal) await writeFile(`${path}-wal`, "wal"); + } +} + +function createService(options: { + databaseFactory: BrowserImportDatabaseFactory; + dpapiDecrypt?: (encrypted: Buffer) => Promise; +}) { + const cookieSet = vi.fn<(details: BrowserImportCookieDetails) => Promise>(async () => {}); + const credentialUpsertMany = vi.fn<(inputs: readonly BrowserCredentialInput[]) => unknown>( + (inputs) => ({ saved: inputs.map(() => ({ id: "credential-id" })), failed: 0 }), + ); + const service = new WindowsChromiumImportService( + { cookies: { set: cookieSet } }, + { upsertMany: credentialUpsertMany } as never, + { + localAppData: fixtureRoot, + tempRoot: fixtureRoot, + platform: "win32", + databaseFactory: options.databaseFactory, + ...(options.dpapiDecrypt ? { dpapiDecrypt: options.dpapiDecrypt } : {}), + }, + ); + return { service, cookieSet, credentialUpsertMany }; +} + +function emptyDatabaseFactory(): BrowserImportDatabaseFactory { + return () => databaseFor([]); +} + +function databaseFor(rows: unknown[], version?: number): BrowserImportDatabase { + return { + prepare: (sql) => ({ + all: () => (sql.includes("FROM meta") && version !== undefined ? [{ value: version }] : rows), + }), + close: vi.fn<() => void>(), + }; +} + +function encryptChromiumValue(version: "v10" | "v11", key: Buffer, plaintext: Buffer): Buffer { + const nonce = Buffer.alloc(12, version === "v10" ? 10 : 11); + const cipher = createCipheriv("aes-256-gcm", key, nonce); + const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); + return Buffer.concat([Buffer.from(version), nonce, ciphertext, cipher.getAuthTag()]); +} + +function fileExistsSyncForTest(path: string): boolean { + return existsSync(path); +} + +async function expectPathMissing(path: string): Promise { + await expect(access(path)).rejects.toMatchObject({ code: "ENOENT" }); +} diff --git a/src/main/browser/WindowsChromiumImportService.ts b/src/main/browser/WindowsChromiumImportService.ts new file mode 100644 index 000000000..7637bef93 --- /dev/null +++ b/src/main/browser/WindowsChromiumImportService.ts @@ -0,0 +1,726 @@ +import { spawn } from "node:child_process"; +import { createDecipheriv, createHash } from "node:crypto"; +import type { Dirent } from "node:fs"; +import { + access, + copyFile, + lstat, + mkdtemp, + readFile, + readdir, + realpath, + rm, +} from "node:fs/promises"; +import { basename, dirname, join, relative, resolve, sep } from "node:path"; +import { tmpdir } from "node:os"; +import Database from "better-sqlite3"; +import type { BrowserImportResult, BrowserImportSourceInfo } from "@/shared/ipc"; +import type { BrowserCredentialInput, BrowserCredentialStore } from "./BrowserCredentialStore"; + +const TEMP_PREFIX = "poracode-browser-import-"; +const DPAPI_PREFIX = Buffer.from("DPAPI"); +const WINDOWS_EPOCH_OFFSET_MICROSECONDS = 11_644_473_600_000_000; +const DPAPI_TIMEOUT_MS = 15_000; +const MAX_DPAPI_OUTPUT_BYTES = 1024 * 1024; + +const BROWSERS = [ + { + id: "chrome", + label: "Google Chrome", + relativeUserDataPath: join("Google", "Chrome", "User Data"), + }, + { + id: "edge", + label: "Microsoft Edge", + relativeUserDataPath: join("Microsoft", "Edge", "User Data"), + }, + { + id: "brave", + label: "Brave", + relativeUserDataPath: join("BraveSoftware", "Brave-Browser", "User Data"), + }, + { + id: "chromium", + label: "Chromium", + relativeUserDataPath: join("Chromium", "User Data"), + }, +] as const; + +type ChromiumBrowserId = BrowserImportSourceInfo["browser"]; + +export interface BrowserImportRequest { + sourceId: string; + passwords: boolean; + cookies: boolean; +} + +export const BROWSER_IMPORT_ERROR_CODES = { + sourceNotFound: "source-not-found", + sourceUnavailable: "source-unavailable", + passwordsUnavailable: "passwords-unavailable", + cookiesUnavailable: "cookies-unavailable", + passwordDatabaseFailed: "password-database-failed", + cookieDatabaseFailed: "cookie-database-failed", + cookieIntegrityFailed: "cookie-integrity-check-failed", + partitionedCookieSkipped: "partitioned-cookie-skipped", + legacyKeyFailed: "legacy-key-decryption-failed", + legacyValueFailed: "legacy-value-decryption-failed", + appBoundSkipped: "app-bound-data-skipped", + passwordWriteFailed: "password-write-failed", + cookieWriteFailed: "cookie-write-failed", +} as const; + +export interface BrowserImportCookieDetails { + url: string; + name: string; + value: string; + domain?: string; + path: string; + secure: boolean; + httpOnly: boolean; + sameSite: "unspecified" | "no_restriction" | "lax" | "strict"; + expirationDate?: number; +} + +export interface BrowserImportCookieSession { + cookies: { + set(details: BrowserImportCookieDetails): Promise; + }; +} + +export interface BrowserImportDatabase { + prepare(sql: string): { all(): unknown[] }; + close(): void; +} + +export type BrowserImportDatabaseFactory = ( + path: string, + options: { readonly: true; fileMustExist: true }, +) => BrowserImportDatabase; + +export type DpapiDecryptor = (encrypted: Buffer) => Promise; + +export interface WindowsChromiumImportServiceOptions { + localAppData?: string; + tempRoot?: string; + platform?: NodeJS.Platform; + dpapiDecrypt?: DpapiDecryptor; + databaseFactory?: BrowserImportDatabaseFactory; +} + +interface DiscoveredSource { + info: BrowserImportSourceInfo; + userDataPath: string; + profilePath: string; + localStatePath: string; + loginDataPath: string; + cookiesPath: string; +} + +interface ChromiumLocalState { + profile?: { info_cache?: Record }; + os_crypt?: { encrypted_key?: string; app_bound_encrypted_key?: string }; +} + +interface ChromiumLoginRow { + origin_url?: unknown; + action_url?: unknown; + signon_realm?: unknown; + username_value?: unknown; + password_value?: unknown; + blacklisted_by_user?: unknown; +} + +interface ChromiumCookieRow { + host_key?: unknown; + name?: unknown; + value?: unknown; + encrypted_value?: unknown; + path?: unknown; + expires_utc?: unknown; + is_secure?: unknown; + is_httponly?: unknown; + samesite?: unknown; + top_frame_site_key?: unknown; +} + +interface ImportContext { + result: BrowserImportResult; + legacyKey: Promise | null; +} + +/** + * Imports legacy Windows Chromium browser data without accepting filesystem + * paths from IPC callers. App-Bound (v20) values are counted and skipped. + */ +export class WindowsChromiumImportService { + private readonly sources = new Map(); + private readonly localAppData: string | undefined; + private readonly tempRoot: string; + private readonly platform: NodeJS.Platform; + private readonly dpapiDecrypt: DpapiDecryptor; + private readonly databaseFactory: BrowserImportDatabaseFactory; + + constructor( + private readonly session: BrowserImportCookieSession, + private readonly credentialStore: Pick, + options: WindowsChromiumImportServiceOptions = {}, + ) { + this.localAppData = options.localAppData ?? process.env.LOCALAPPDATA; + this.tempRoot = resolve(options.tempRoot ?? tmpdir()); + this.platform = options.platform ?? process.platform; + this.dpapiDecrypt = options.dpapiDecrypt ?? decryptDpapiWithPowerShell; + this.databaseFactory = options.databaseFactory ?? defaultDatabaseFactory; + } + + async listSources(): Promise { + await this.discover(); + return [...this.sources.values()] + .map((source) => ({ ...source.info })) + .sort( + (a, b) => + a.browserLabel.localeCompare(b.browserLabel) || + a.profileLabel.localeCompare(b.profileLabel), + ); + } + + async importData(request: BrowserImportRequest): Promise { + const context: ImportContext = { + result: emptyResult(), + legacyKey: null, + }; + if (this.sources.size === 0) await this.discover(); + const source = this.sources.get(request.sourceId); + if (!source) { + addError(context, BROWSER_IMPORT_ERROR_CODES.sourceNotFound); + return context.result; + } + if (!(await this.isSourceStillValid(source))) { + this.sources.delete(request.sourceId); + addError(context, BROWSER_IMPORT_ERROR_CODES.sourceUnavailable); + return context.result; + } + + const tempDirectory = await mkdtemp(join(this.tempRoot, TEMP_PREFIX)); + try { + const localState = await readLocalState(source.localStatePath); + const getLegacyKey = (): Promise => { + context.legacyKey ??= this.readLegacyKey(localState, context); + return context.legacyKey; + }; + + if (request.passwords) { + if (!source.info.supportsPasswords) { + addError(context, BROWSER_IMPORT_ERROR_CODES.passwordsUnavailable); + } else { + await this.importPasswords(source, tempDirectory, getLegacyKey, context); + } + } + if (request.cookies) { + if (!source.info.supportsCookies) { + addError(context, BROWSER_IMPORT_ERROR_CODES.cookiesUnavailable); + } else { + await this.importCookies(source, tempDirectory, getLegacyKey, context); + } + } + return context.result; + } finally { + await removeImportTempDirectory(this.tempRoot, tempDirectory); + } + } + + private async discover(): Promise { + this.sources.clear(); + if (this.platform !== "win32" || !this.localAppData) return; + for (const browser of BROWSERS) { + const userDataPath = resolve(this.localAppData, browser.relativeUserDataPath); + const localStatePath = join(userDataPath, "Local State"); + const localState = await readLocalState(localStatePath); + let entries: Dirent[]; + try { + entries = await readdir(userDataPath, { withFileTypes: true, encoding: "utf8" }); + } catch { + continue; + } + let realUserDataPath: string; + try { + realUserDataPath = await realpath(userDataPath); + } catch { + continue; + } + for (const entry of entries) { + if (!entry.isDirectory() || !isChromiumProfileDirectory(entry.name)) continue; + const profilePath = join(userDataPath, entry.name); + if (!(await isSafeDiscoveredDirectory(realUserDataPath, profilePath))) continue; + const loginDataPath = join(profilePath, "Login Data"); + const networkCookiesPath = join(profilePath, "Network", "Cookies"); + const legacyCookiesPath = join(profilePath, "Cookies"); + const supportsPasswords = await fileExists(loginDataPath); + const cookiesPath = (await fileExists(networkCookiesPath)) + ? networkCookiesPath + : legacyCookiesPath; + const supportsCookies = await fileExists(cookiesPath); + if (!supportsPasswords && !supportsCookies) continue; + const id = sourceId(browser.id, profilePath); + const info: BrowserImportSourceInfo = { + id, + browser: browser.id, + browserLabel: browser.label, + profileLabel: localState.profile?.info_cache?.[entry.name]?.name || entry.name, + supportsPasswords, + supportsCookies, + hasAppBoundData: !!localState.os_crypt?.app_bound_encrypted_key, + }; + this.sources.set(id, { + info, + userDataPath, + profilePath, + localStatePath, + loginDataPath, + cookiesPath, + }); + } + } + } + + private async isSourceStillValid(source: DiscoveredSource): Promise { + try { + const realUserDataPath = await realpath(source.userDataPath); + return await isSafeDiscoveredDirectory(realUserDataPath, source.profilePath); + } catch { + return false; + } + } + + private async readLegacyKey( + localState: ChromiumLocalState, + context: ImportContext, + ): Promise { + const encoded = localState.os_crypt?.encrypted_key; + if (!encoded) { + addError(context, BROWSER_IMPORT_ERROR_CODES.legacyKeyFailed); + return null; + } + try { + const wrapped = Buffer.from(encoded, "base64"); + if (!wrapped.subarray(0, DPAPI_PREFIX.length).equals(DPAPI_PREFIX)) { + throw new Error("Unexpected Chromium encrypted-key format"); + } + return await this.dpapiDecrypt(wrapped.subarray(DPAPI_PREFIX.length)); + } catch { + addError(context, BROWSER_IMPORT_ERROR_CODES.legacyKeyFailed); + return null; + } + } + + private async importPasswords( + source: DiscoveredSource, + tempDirectory: string, + getLegacyKey: () => Promise, + context: ImportContext, + ): Promise { + const snapshotPath = join(tempDirectory, "Login Data"); + try { + await snapshotDatabase(source.loginDataPath, snapshotPath); + await withReadOnlyDatabase(this.databaseFactory, snapshotPath, async (database) => { + const rows = database + .prepare( + "SELECT origin_url, action_url, signon_realm, username_value, password_value, blacklisted_by_user FROM logins", + ) + .all() as ChromiumLoginRow[]; + const credentials: BrowserCredentialInput[] = []; + for (const row of rows) { + if (toBoolean(row.blacklisted_by_user)) continue; + const encrypted = toBuffer(row.password_value); + const decrypted = await this.decryptValue(encrypted, getLegacyKey, context); + if (decrypted.kind === "protected") { + context.result.passwordsSkipped += 1; + context.result.protectedItemsSkipped += 1; + continue; + } + if (decrypted.kind === "failed" || decrypted.value.length === 0) { + context.result.passwordsSkipped += 1; + continue; + } + const origin = firstString(row.origin_url, row.action_url, row.signon_realm); + if (!origin) { + context.result.passwordsSkipped += 1; + continue; + } + credentials.push({ + origin, + username: stringValue(row.username_value), + password: decrypted.value.toString("utf8"), + source: `${source.info.browserLabel} — ${source.info.profileLabel}`, + }); + } + if (credentials.length === 0) return; + try { + const batch = this.credentialStore.upsertMany(credentials); + context.result.passwordsImported += batch.saved.length; + context.result.passwordsSkipped += batch.failed; + if (batch.failed > 0) { + addError(context, BROWSER_IMPORT_ERROR_CODES.passwordWriteFailed); + } + } catch { + context.result.passwordsSkipped += credentials.length; + addError(context, BROWSER_IMPORT_ERROR_CODES.passwordWriteFailed); + } + }); + } catch { + addError(context, BROWSER_IMPORT_ERROR_CODES.passwordDatabaseFailed); + } + } + + private async importCookies( + source: DiscoveredSource, + tempDirectory: string, + getLegacyKey: () => Promise, + context: ImportContext, + ): Promise { + const snapshotPath = join(tempDirectory, "Cookies"); + try { + await snapshotDatabase(source.cookiesPath, snapshotPath); + await withReadOnlyDatabase(this.databaseFactory, snapshotPath, async (database) => { + const databaseVersion = readCookieDatabaseVersion(database); + const partitionKeyColumn = databaseVersion >= 15 ? ", top_frame_site_key" : ""; + const rows = database + .prepare( + `SELECT host_key, name, value, encrypted_value, path, expires_utc, is_secure, is_httponly, samesite${partitionKeyColumn} FROM cookies`, + ) + .all() as ChromiumCookieRow[]; + for (const row of rows) { + if (stringValue(row.top_frame_site_key)) { + context.result.cookiesSkipped += 1; + addError(context, BROWSER_IMPORT_ERROR_CODES.partitionedCookieSkipped); + continue; + } + const hostKey = stringValue(row.host_key); + const host = hostKey.replace(/^\./u, ""); + if (!host || !isValidCookieHost(host)) { + context.result.cookiesSkipped += 1; + continue; + } + const plaintext = stringValue(row.value); + let value = plaintext; + if (!plaintext) { + const decrypted = await this.decryptValue( + toBuffer(row.encrypted_value), + getLegacyKey, + context, + ); + if (decrypted.kind === "protected") { + context.result.cookiesSkipped += 1; + context.result.protectedItemsSkipped += 1; + continue; + } + if (decrypted.kind === "failed") { + context.result.cookiesSkipped += 1; + continue; + } + const cookieValue = decodeCookieValue(decrypted.value, hostKey, databaseVersion); + if (!cookieValue) { + context.result.cookiesSkipped += 1; + addError(context, BROWSER_IMPORT_ERROR_CODES.cookieIntegrityFailed); + continue; + } + value = cookieValue.toString("utf8"); + } + const secure = toBoolean(row.is_secure); + const path = normalizeCookiePath(stringValue(row.path)); + const expirationDate = chromiumTimeToUnixSeconds(row.expires_utc); + const details: BrowserImportCookieDetails = { + url: `${secure ? "https" : "http"}://${host}${path}`, + name: stringValue(row.name), + value, + ...(hostKey.startsWith(".") ? { domain: hostKey } : {}), + path, + secure, + httpOnly: toBoolean(row.is_httponly), + sameSite: chromiumSameSite(row.samesite), + ...(expirationDate !== undefined ? { expirationDate } : {}), + }; + try { + await this.session.cookies.set(details); + context.result.cookiesImported += 1; + } catch { + context.result.cookiesSkipped += 1; + addError(context, BROWSER_IMPORT_ERROR_CODES.cookieWriteFailed); + } + } + }); + } catch { + addError(context, BROWSER_IMPORT_ERROR_CODES.cookieDatabaseFailed); + } + } + + private async decryptValue( + encrypted: Buffer, + getLegacyKey: () => Promise, + context: ImportContext, + ): Promise<{ kind: "ok"; value: Buffer } | { kind: "protected" } | { kind: "failed" }> { + if (encrypted.subarray(0, 3).toString("ascii") === "v20") { + addError(context, BROWSER_IMPORT_ERROR_CODES.appBoundSkipped); + return { kind: "protected" }; + } + const version = encrypted.subarray(0, 3).toString("ascii"); + try { + if (version === "v10" || version === "v11") { + const key = await getLegacyKey(); + if (!key) return { kind: "failed" }; + return { kind: "ok", value: decryptAesGcm(encrypted.subarray(3), key) }; + } + if (encrypted.length === 0) return { kind: "ok", value: Buffer.alloc(0) }; + return { kind: "ok", value: await this.dpapiDecrypt(encrypted) }; + } catch { + addError(context, BROWSER_IMPORT_ERROR_CODES.legacyValueFailed); + return { kind: "failed" }; + } + } +} + +/** Current-user DPAPI only. It deliberately does not elevate or unwrap v20 data. */ +export function decryptDpapiWithPowerShell(encrypted: Buffer): Promise { + if (process.platform !== "win32") { + return Promise.reject(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)); + } + const script = [ + "$ErrorActionPreference = 'Stop'", + "Add-Type -AssemblyName System.Security", + "$encoded = [Console]::In.ReadToEnd().Trim()", + "$encrypted = [Convert]::FromBase64String($encoded)", + "$plain = [Security.Cryptography.ProtectedData]::Unprotect($encrypted, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)", + "[Console]::Out.Write([Convert]::ToBase64String($plain))", + ].join("; "); + return new Promise((resolvePromise, rejectPromise) => { + const child = spawn( + "powershell.exe", + ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], + { windowsHide: true, stdio: ["pipe", "pipe", "ignore"] }, + ); + const chunks: Buffer[] = []; + let outputBytes = 0; + let settled = false; + const finish = (error?: Error, value?: Buffer): void => { + if (settled) return; + settled = true; + clearTimeout(timer); + if (error) rejectPromise(error); + else resolvePromise(value ?? Buffer.alloc(0)); + }; + const timer = setTimeout(() => { + child.kill(); + finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)); + }, DPAPI_TIMEOUT_MS); + child.stdout.on("data", (chunk: Buffer) => { + outputBytes += chunk.length; + if (outputBytes > MAX_DPAPI_OUTPUT_BYTES) { + child.kill(); + finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)); + return; + } + chunks.push(chunk); + }); + child.once("error", () => finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed))); + child.once("close", (code) => { + if (code !== 0) { + finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)); + return; + } + const encoded = Buffer.concat(chunks).toString("ascii").trim(); + if (!/^[A-Za-z0-9+/]*={0,2}$/u.test(encoded)) { + finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)); + return; + } + finish(undefined, Buffer.from(encoded, "base64")); + }); + child.stdin.once("error", () => + finish(new Error(BROWSER_IMPORT_ERROR_CODES.legacyValueFailed)), + ); + child.stdin.end(encrypted.toString("base64")); + }); +} + +const defaultDatabaseFactory: BrowserImportDatabaseFactory = (path, options) => + new Database(path, options) as unknown as BrowserImportDatabase; + +async function withReadOnlyDatabase( + factory: BrowserImportDatabaseFactory, + path: string, + operation: (database: BrowserImportDatabase) => Promise, +): Promise { + const database = factory(path, { readonly: true, fileMustExist: true }); + try { + await operation(database); + } finally { + database.close(); + } +} + +async function snapshotDatabase(source: string, target: string): Promise { + await copyFile(source, target); + await copyIfPresent(`${source}-wal`, `${target}-wal`); + await copyIfPresent(`${source}-shm`, `${target}-shm`); +} + +async function copyIfPresent(source: string, target: string): Promise { + try { + await copyFile(source, target); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } +} + +async function removeImportTempDirectory(tempRoot: string, directory: string): Promise { + const resolvedRoot = resolve(tempRoot); + const resolvedDirectory = resolve(directory); + if ( + dirname(resolvedDirectory) !== resolvedRoot || + !basename(resolvedDirectory).startsWith(TEMP_PREFIX) + ) { + throw new Error("Refusing to remove an unexpected browser-import directory"); + } + await rm(resolvedDirectory, { recursive: true, force: true }); +} + +async function readLocalState(path: string): Promise { + try { + return JSON.parse(await readFile(path, "utf8")) as ChromiumLocalState; + } catch { + return {}; + } +} + +async function fileExists(path: string): Promise { + try { + await access(path); + return true; + } catch { + return false; + } +} + +async function isSafeDiscoveredDirectory( + realUserDataPath: string, + profilePath: string, +): Promise { + try { + const stats = await lstat(profilePath); + if (!stats.isDirectory() || stats.isSymbolicLink()) return false; + const realProfilePath = await realpath(profilePath); + return isPathInside(realUserDataPath, realProfilePath); + } catch { + return false; + } +} + +function isPathInside(parent: string, child: string): boolean { + const childRelative = relative(resolve(parent), resolve(child)); + return childRelative !== "" && childRelative !== ".." && !childRelative.startsWith(`..${sep}`); +} + +function isChromiumProfileDirectory(name: string): boolean { + return name === "Default" || /^Profile \d+$/u.test(name); +} + +function sourceId(browser: ChromiumBrowserId, profilePath: string): string { + const digest = createHash("sha256").update(resolve(profilePath).toLowerCase()).digest("hex"); + return `chromium-${browser}-${digest.slice(0, 20)}`; +} + +function decryptAesGcm(payload: Buffer, key: Buffer): Buffer { + if (payload.length < 12 + 16) throw new Error("Invalid Chromium AES-GCM payload"); + const nonce = payload.subarray(0, 12); + const ciphertextAndTag = payload.subarray(12); + const tag = ciphertextAndTag.subarray(ciphertextAndTag.length - 16); + const ciphertext = ciphertextAndTag.subarray(0, ciphertextAndTag.length - 16); + const decipher = createDecipheriv("aes-256-gcm", key, nonce); + decipher.setAuthTag(tag); + return Buffer.concat([decipher.update(ciphertext), decipher.final()]); +} + +function readCookieDatabaseVersion(database: BrowserImportDatabase): number { + try { + const [row] = database + .prepare("SELECT value FROM meta WHERE key = 'version' LIMIT 1") + .all() as Array<{ + value?: unknown; + }>; + const version = Number(row?.value); + return Number.isInteger(version) && version >= 0 ? version : 0; + } catch { + return 0; + } +} + +function decodeCookieValue(value: Buffer, hostKey: string, databaseVersion: number): Buffer | null { + const hostDigest = createHash("sha256").update(hostKey).digest(); + const hasHostDigest = + value.length >= hostDigest.length && value.subarray(0, hostDigest.length).equals(hostDigest); + if (hasHostDigest) return value.subarray(hostDigest.length); + return databaseVersion >= 24 ? null : value; +} + +function toBuffer(value: unknown): Buffer { + if (Buffer.isBuffer(value)) return value; + if (value instanceof Uint8Array) return Buffer.from(value); + return Buffer.alloc(0); +} + +function firstString(...values: unknown[]): string | undefined { + for (const value of values) { + if (typeof value === "string" && value.length > 0) return value; + } + return undefined; +} + +function stringValue(value: unknown): string { + return typeof value === "string" ? value : ""; +} + +function toBoolean(value: unknown): boolean { + return value === true || value === 1; +} + +function normalizeCookiePath(value: string): string { + return value.startsWith("/") ? value : "/"; +} + +function isValidCookieHost(value: string): boolean { + if (value.includes("/") || value.includes("\\") || value.includes("://")) return false; + try { + return new URL(`https://${value}/`).hostname === value; + } catch { + return false; + } +} + +function chromiumSameSite(value: unknown): BrowserImportCookieDetails["sameSite"] { + if (value === 0) return "no_restriction"; + if (value === 1) return "lax"; + if (value === 2) return "strict"; + return "unspecified"; +} + +function chromiumTimeToUnixSeconds(value: unknown): number | undefined { + const microseconds = typeof value === "number" ? value : Number(value); + if (!Number.isFinite(microseconds) || microseconds <= WINDOWS_EPOCH_OFFSET_MICROSECONDS) { + return undefined; + } + return (microseconds - WINDOWS_EPOCH_OFFSET_MICROSECONDS) / 1_000_000; +} + +function emptyResult(): BrowserImportResult { + return { + passwordsImported: 0, + cookiesImported: 0, + passwordsSkipped: 0, + cookiesSkipped: 0, + protectedItemsSkipped: 0, + errors: [], + }; +} + +function addError(context: ImportContext, code: string): void { + if (!context.result.errors.includes(code)) context.result.errors.push(code); +} diff --git a/src/main/ipc/localHandlers.ts b/src/main/ipc/localHandlers.ts index feedb98b2..e233c4537 100644 --- a/src/main/ipc/localHandlers.ts +++ b/src/main/ipc/localHandlers.ts @@ -542,6 +542,8 @@ export function createLocalIpcHandlers( ...(payload.activate !== undefined ? { activate: payload.activate } : {}), ...(payload.reveal !== undefined ? { reveal: payload.reveal } : {}), }), + browserOpenInternalPage: ({ page }) => + requireBrowserPanel(options.getBrowserPanelManager).openInternalPage(page), browserCloseTab: ({ tabId }) => requireBrowserPanel(options.getBrowserPanelManager).closeTab(tabId), browserActivateTab: ({ tabId }) => { @@ -577,6 +579,23 @@ export function createLocalIpcHandlers( requireBrowserPanel(options.getBrowserPanelManager).hardReload(tabId), browserToggleDevTools: ({ tabId }) => requireBrowserPanel(options.getBrowserPanelManager).toggleDevTools(tabId), + browserFindInPage: ({ tabId, text, forward, findNext, matchCase }) => { + requireBrowserPanel(options.getBrowserPanelManager).findInPage(tabId, text, { + forward, + findNext, + matchCase, + }); + }, + browserStopFindInPage: ({ tabId, action }) => { + requireBrowserPanel(options.getBrowserPanelManager).stopFindInPage(tabId, action); + }, + browserPrint: ({ tabId }) => requireBrowserPanel(options.getBrowserPanelManager).print(tabId), + browserSetZoomFactor: ({ tabId, zoomFactor }) => { + requireBrowserPanel(options.getBrowserPanelManager).setZoomFactor(tabId, zoomFactor); + }, + browserSetDeviceEmulation: ({ tabId, emulation }) => { + requireBrowserPanel(options.getBrowserPanelManager).setDeviceEmulation(tabId, emulation); + }, browserClearHistory: ({ tabId }) => requireBrowserPanel(options.getBrowserPanelManager).clearHistory(tabId), browserClearCookies: ({ tabId }) => @@ -620,6 +639,32 @@ export function createLocalIpcHandlers( }, browserRecentHistory: ({ limit }) => requireBrowserPanel(options.getBrowserPanelManager).recentHistory(limit), + browserGetDownloads: () => requireBrowserPanel(options.getBrowserPanelManager).getDownloads(), + browserDownloadAction: ({ id, action }) => + requireBrowserPanel(options.getBrowserPanelManager).downloadAction(id, action), + browserListCredentials: () => + requireBrowserPanel(options.getBrowserPanelManager).listCredentials(), + browserGetCredentialPassword: ({ id }) => { + const password = requireBrowserPanel(options.getBrowserPanelManager).getCredentialPassword( + id, + ); + if (password === null) throw new Error("Browser credential not found"); + return { password }; + }, + browserUpsertCredential: ({ id, origin, username, password }) => + requireBrowserPanel(options.getBrowserPanelManager).upsertCredential({ + ...(id !== undefined ? { id } : {}), + origin, + username, + password, + }), + browserDeleteCredential: ({ id }) => { + requireBrowserPanel(options.getBrowserPanelManager).deleteCredential(id); + }, + browserListImportSources: () => + requireBrowserPanel(options.getBrowserPanelManager).listImportSources(), + browserImportData: (payload) => + requireBrowserPanel(options.getBrowserPanelManager).importBrowserData(payload), browserExtractToWindow: () => { options.extractBrowserToWindow(); }, diff --git a/src/main/main.ts b/src/main/main.ts index 59a66cfbd..6a43ff310 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -996,6 +996,7 @@ if (!hasSingleInstanceLock) { browserPanelManager = new BrowserPanelManager(paths, browserUserAgent, { isExtracted: () => browserExtractWindow !== null && !browserExtractWindow.isDestroyed(), focusExtractedWindow: focusBrowserExtractWindow, + browserSession, }); browserMcpIngress = new BrowserMcpIngress(); browserMcpIngress.setManagerAccessor(() => browserPanelManager); diff --git a/src/renderer/components/find/findController.test.ts b/src/renderer/components/find/findController.test.ts index bf1287b4c..e14592c20 100644 --- a/src/renderer/components/find/findController.test.ts +++ b/src/renderer/components/find/findController.test.ts @@ -1,8 +1,10 @@ import { afterEach, describe, expect, it } from "vitest"; import { useCommandPaletteStore } from "@/renderer/commands/commandPaletteStore"; import { useAppStore } from "@/renderer/state/appStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import { usePanelStore } from "@/renderer/state/panelStore"; -import { resolveFindTarget } from "./findController"; +import { openFindForActiveSurface, resolveFindTarget } from "./findController"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; function resetFindRoutingState(): void { document.body.innerHTML = ""; @@ -17,6 +19,8 @@ function resetFindRoutingState(): void { cloneProjectModalOpen: false, }); useAppStore.setState({ view: { kind: "home" } }); + useBrowserPanelStore.setState({ tabs: [], activeTabId: null }); + useBrowserFindStore.setState({ tabId: null }); } describe("resolveFindTarget", () => { @@ -30,12 +34,35 @@ describe("resolveFindTarget", () => { expect(resolveFindTarget()).toBe("chat"); }); - it("does not route browser chrome focus to chat find", () => { + it("routes browser chrome focus to browser find", () => { useAppStore.setState({ view: { kind: "thread", panes: ["thread-1"] } }); document.body.innerHTML = `
`; document.getElementById("address")?.focus(); - expect(resolveFindTarget()).toBeNull(); + expect(resolveFindTarget()).toBe("browser"); + }); + + it("does not open browser find on an app-owned internal page", () => { + document.body.innerHTML = `
`; + document.getElementById("address")?.focus(); + useBrowserPanelStore.setState({ + activeTabId: "tab-1", + tabs: [ + { + tabId: "tab-1", + url: "chrome://downloads/", + title: "Download history", + loading: false, + canGoBack: false, + canGoForward: false, + internalPage: "downloads", + }, + ], + }); + + openFindForActiveSurface(); + + expect(useBrowserFindStore.getState().tabId).toBeNull(); }); it("lets the command palette keep Ctrl+F", () => { diff --git a/src/renderer/components/find/findController.ts b/src/renderer/components/find/findController.ts index b9b3ff259..3c982323b 100644 --- a/src/renderer/components/find/findController.ts +++ b/src/renderer/components/find/findController.ts @@ -1,4 +1,6 @@ import { useAppStore } from "@/renderer/state/appStore"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import { useChatFindStore } from "@/renderer/state/chatFindStore"; import { useFileEditorStore } from "@/renderer/state/fileEditorStore"; import { useFindFocusStore } from "@/renderer/state/findFocusStore"; @@ -9,7 +11,7 @@ import { isEditorFocusElement, isTerminalFocusElement } from "@/renderer/command import { openEditorFind } from "./editorFindBridge"; import { openTerminalFind } from "./terminalFindBridge"; -export type FindTarget = "editor" | "terminal" | "settings" | "git" | "tree" | "chat"; +export type FindTarget = "browser" | "editor" | "terminal" | "settings" | "git" | "tree" | "chat"; /** * Decide which surface a Find (Ctrl+F) press should target. Focus wins first @@ -28,7 +30,7 @@ export function resolveFindTarget(): FindTarget | null { if (scope === "git" || scope === "settings" || scope === "tree" || scope === "chat") { return scope; } - if (element?.closest("[data-poracode-browser]")) return null; + if (element?.closest("[data-poracode-browser]")) return "browser"; const panel = usePanelStore.getState(); // Blocking modals trap their own input — leave Ctrl+F to them. @@ -52,6 +54,12 @@ export function openFindForActiveSurface(): void { const target = resolveFindTarget(); if (!target) return; switch (target) { + case "browser": { + const browser = useBrowserPanelStore.getState(); + const tab = browser.tabs.find((candidate) => candidate.tabId === browser.activeTabId); + if (tab && !tab.internalPage) useBrowserFindStore.getState().open(tab.tabId); + return; + } case "editor": openEditorFind(); return; diff --git a/src/renderer/locales/de/messages.po b/src/renderer/locales/de/messages.po index 336982693..6ed5b52da 100644 --- a/src/renderer/locales/de/messages.po +++ b/src/renderer/locales/de/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30 T" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7 T" @@ -827,6 +831,10 @@ msgstr "Hinzufügen" msgid "Add a local project" msgstr "Lokales Projekt hinzufügen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Passwort hinzufügen oder eines aus einem anderen Browser importieren." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Modell hinzufügen" msgid "Add one manually or import from another agent." msgstr "Fügen Sie einen manuell hinzu oder importieren Sie ihn aus einem anderen Agenten." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Passwort hinzufügen" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Muster hinzufügen" @@ -988,6 +1000,10 @@ msgstr "Aufgabe hinzufügen" msgid "Add workspace" msgstr "Arbeitsbereich hinzufügen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Erweitert" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Eine benutzerdefinierte Basis-URL bekanntgeben, z. B. für einen Reverse-Proxy oder Tunnel. Für automatisch leer lassen." @@ -1077,6 +1093,7 @@ msgstr "Agents klassifizieren delegierte Arbeit mit Task-Tags. Manuelle Task-Rou #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Agenten klassifizieren delegierte Arbeit mit Aufgaben-Tags. Passende erlernte Routen werden zuerst eingestuft, gefolgt von globaler Crossagents-Nutzung, Favoriten, normaler Agentennutzung und der integrierten Reihenfolge." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "blockiert:" msgid "Bold" msgstr "Fett" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Diese Seite als Lesezeichen speichern" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Lesezeichen" @@ -1719,6 +1737,10 @@ msgstr "Zurück ins Panel holen" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Holen Sie ihn zurück in dieses Panel oder wechseln Sie zum Fenster, in dem er läuft." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Gespeicherte Passwörter und Cookies aus einem installierten Browser übernehmen." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Browser ist in einem separaten Fenster geöffnet" msgid "Browser MCP enabled for this thread" msgstr "Browser-MCP für diesen Thread aktiviert" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Browser-Menü" @@ -1893,6 +1915,8 @@ msgstr "Der Remote-Server ist nicht erreichbar. Prüfe, ob er online ist, und ve #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Der Remote-Server ist nicht erreichbar. Prüfe, ob er online ist, und ve msgid "Cancel" msgstr "Abbrechen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Download abbrechen" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Neues Claude-Profil abbrechen" @@ -1932,6 +1960,7 @@ msgstr "abgebrochen" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Abgebrochen" @@ -2128,6 +2157,10 @@ msgstr "Übergeordneten Ordner auswählen" msgid "Choose at least two candidates and enter a prompt." msgstr "Wählen Sie mindestens zwei Kandidaten aus und geben Sie einen Prompt ein." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Daten für die Übernahme in den integrierten Browser auswählen." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Sauberer Arbeitsbaum" msgid "Cleanup script" msgstr "Bereinigungsskript" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Browserdaten löschen" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Browserverlauf löschen" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Browserverlauf löschen" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Cache leeren" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Cache leeren" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Cookies löschen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Cookies löschen" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Ziel löschen" @@ -2361,6 +2406,10 @@ msgstr "Crossagents schließen" msgid "Close Crossagents panel" msgstr "Crossagents-Bereich schließen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Geräteleiste schließen" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Editor schließen" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "abgeschlossen" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Abgeschlossen" @@ -2967,6 +3017,10 @@ msgstr "Steuert den gekoppelten Desktop, während der Agent klickt oder tippt" msgid "Conversation" msgstr "Gespräch" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookies" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Alle Lightcode-Daten in Poracode kopieren. Poracode wird neu gestartet u msgid "Copy code" msgstr "Code kopieren" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Aktuelle URL kopieren" @@ -3045,6 +3099,10 @@ msgstr "Kopplungslink kopieren" msgid "Copy pairing token" msgstr "Kopplungstoken kopieren" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Passwort kopieren" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Pfad kopieren" @@ -3554,6 +3612,7 @@ msgstr "Delegieren Sie Aufgaben an andere KI-Agenten." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Dauerhaft löschen" msgid "Delete run" msgstr "Ausführung löschen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Gespeichertes Passwort löschen" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Gespeichertes Passwort löschen?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Zeitplan löschen" @@ -3775,6 +3842,10 @@ msgstr "Dev-Server, die auf dem localhost Ihres Desktops lauschen." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Überschreibungen nur für die Entwicklung. Nur im PORACODE DEV-Build sichtbar." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Gerätevoreinstellung" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Von der Composer-Spracheingabetaste verwendetes Gerät." msgid "Devices can pair while this desktop is reachable." msgstr "Geräte können gekoppelt werden, solange dieser Desktop erreichbar ist." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Abmessungen:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Direkte Verbindung erforderlich" @@ -3964,10 +4039,27 @@ msgstr "Fertig" msgid "Done · Waiting for your input" msgstr "Fertig · Warten auf Ihre Eingabe" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Download fehlgeschlagen" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Downloadverlauf" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Bild herunterladen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Downloadfortschritt" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Wird heruntergeladen" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Sprachmodell wird heruntergeladen..." msgid "Downloading… {0}%" msgstr "Wird heruntergeladen… {0} %" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Downloads" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Downloads aus dem integrierten Browser werden in diesem Verlauf gespeichert." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Ziel bearbeiten" msgid "Edit MCP server" msgstr "MCP-Server bearbeiten" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Passwort bearbeiten" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Profil bearbeiten" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Gespeichertes Passwort bearbeiten" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Dateien für {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Dateien, die mit diesen Globs übereinstimmen, werden bei der @file-Erwähnungssuche ausgeblendet." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Heruntergeladene Dateien werden hier angezeigt" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Fülle alle erforderlichen Workflow-Eingaben aus." @@ -4700,6 +4812,11 @@ msgstr "Im Chat suchen" msgid "Find in diff" msgstr "Im Diff suchen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Auf Seite suchen" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Im Terminal suchen" @@ -4923,6 +5040,10 @@ msgstr "Häufigkeit" msgid "Friday at 4:00 PM" msgstr "Freitag um 16:00 Uhr" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Von" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Die Seitenleiste mit dem Unschärfe-Material des Systems (Vibrancy) mattieren, angelehnt an aktuelle macOS-Versionen. Wo nicht unterstützt, wird auf eine durchscheinende Tönung zurückgegriffen." @@ -5185,7 +5306,7 @@ msgstr "handle" msgid "Handle" msgstr "Handle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Hart neu laden" @@ -5249,6 +5370,10 @@ msgstr "Blenden Sie Modelle, die Sie nicht verwenden, bei allen Anbietern in der msgid "Hide panel" msgstr "Panel ausblenden" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Passwort ausblenden" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Laufzeit-Debug-Panel ausblenden" @@ -5272,7 +5397,7 @@ msgstr "Skills von {0} ausblenden" msgid "Hide terminal" msgstr "Terminal ausblenden" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Verlauf" @@ -5317,10 +5442,18 @@ msgstr "Stündlich" msgid "HTTP (streamable)" msgstr "HTTP (streamfähig)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Ich verstehe, dass geschützte Browserdaten möglicherweise übersprungen werden." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Bild" msgid "Image preview" msgstr "Bildvorschau" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Importieren" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Erneut importieren" @@ -5362,10 +5499,23 @@ msgstr "Erneut importieren" msgid "Import and restart" msgstr "Importieren und neu starten" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Browserdaten importieren" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Importkonflikt" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Cookies importieren" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Cookies und Passwörter importieren" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Externe Agent-Skills importieren" msgid "Import external skills" msgstr "Externe Skills importieren" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Aus deinem Browser importieren" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Lightcode-Daten importieren" @@ -5399,10 +5553,18 @@ msgstr "MCP-Server importieren" msgid "Import method" msgstr "Importmethode" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Passwörter importieren" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Ausgewählte importieren" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Importquelle" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "In {destinationProjectName} importieren" @@ -5430,6 +5592,17 @@ msgstr "{0} Skills importiert." msgid "Imported 1 skill." msgstr "1 Skill importiert." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Importiert aus {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Importierte Passwörter: {0}. Importierte Cookies: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Verbessert" @@ -5662,6 +5835,7 @@ msgstr "Anweisungen, denen Agenten folgen, wenn sie auswählen, an welchen Agent #~ msgid "Interrupt" #~ msgstr "Unterbrechen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Unterbrochen" @@ -5670,6 +5844,26 @@ msgstr "Unterbrochen" msgid "Interrupted: agent session ended before completion." msgstr "Unterbrochen: Agentensitzung wurde vorzeitig beendet." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Kursiv" @@ -5769,6 +5963,14 @@ msgstr "Sprache für KI-generierte Commit-Nachrichten und PR-Zusammenfassungen. msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Sprache, die das Sprachmodell beim Transkribieren des Diktats im Eingabefeld erwarten sollte." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Letzte Bewertung:" @@ -5996,6 +6198,10 @@ msgstr "Lokal" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Lokale Änderungen müssen vor dem Pull von {branch} gestasht werden" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Lokaler Passwortschutz" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Längste Serie" msgid "Longest task" msgstr "Längste Aufgabe" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Browserprofile werden gesucht…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Suche nach Dev-Servern…" @@ -6082,6 +6292,10 @@ msgstr "Die Seitenleiste durchscheinend machen — das Unschärfe-Material des S #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Globale und projektbezogene Agent-Skills verwalten, einschließlich der von anderen Anbietern importierten Skills." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Passwörter verwalten" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Remote-Umgebungen verwalten" @@ -6840,8 +7054,8 @@ msgstr "Neuer Zeitplan" #~ msgstr "Neue geplante Aufgabe" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Neuer Tab" @@ -7084,6 +7298,10 @@ msgstr "Für {0} ist kein Unterschied verfügbar." msgid "No diff for {visiblePath}" msgstr "Kein Unterschied für {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Keine Downloads entsprechen der Suche" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "In diesem Geltungsbereich wurden keine externen Skills gefunden." @@ -7107,7 +7325,7 @@ msgstr "Keine GitHub-CLI-Konten gefunden. Melden Sie sich mit <0>gh auth login{0} from Poracode." +msgstr "Dadurch wird das gespeicherte Passwort für <0>{0} aus Poracode entfernt." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Dadurch werden der Zeitplan und sein letztes Ergebnis von diesem Gerät entfernt." @@ -11739,6 +12097,10 @@ msgstr "Kopplungstoken kann nicht kopiert werden." msgid "Unable to copy Tailscale URL." msgstr "Tailscale-URL konnte nicht kopiert werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Das Passwort konnte nicht kopiert werden." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Der Kandidaten-Worktree konnte nicht erstellt werden." @@ -11752,6 +12114,10 @@ msgstr "Pull Request konnte nicht erstellt werden: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Das WSL-Worktree-Verzeichnis „{path}“ kann nicht erstellt werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Das gespeicherte Passwort konnte nicht gelöscht werden." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Der Worktree kann nicht gelöscht werden." @@ -11760,6 +12126,18 @@ msgstr "Der Worktree kann nicht gelöscht werden." msgid "Unable to determine the experiment's source branch." msgstr "Der Quell-Branch des Experiments konnte nicht ermittelt werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Das gespeicherte Passwort konnte nicht bearbeitet werden." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Es konnten keine Browserdaten zum Importieren gefunden werden." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Browserdaten konnten nicht importiert werden." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "{0} konnte nicht installiert werden." msgid "Unable to load diff." msgstr "Diff konnte nicht geladen werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Downloads konnten nicht geladen werden." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Remotezugriff-Kopplung konnte nicht geladen werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Gespeicherte Passwörter konnten nicht geladen werden." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Thread konnte nicht geladen werden." @@ -11877,6 +12263,10 @@ msgstr "Commit des Experimentkandidaten konnte nicht aufgelöst werden" msgid "Unable to restore the SSH connection." msgstr "Die SSH-Verbindung konnte nicht wiederhergestellt werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Das Passwort konnte nicht eingeblendet werden." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Remote-Sitzung konnte nicht widerrufen werden." @@ -11890,6 +12280,10 @@ msgstr "{0}-Anmeldeinformationen können nicht gespeichert werden." msgid "Unable to save Claude {displayLabel} profile." msgstr "Claude-Profil {displayLabel} konnte nicht gespeichert werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Das Passwort konnte nicht gespeichert werden." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "{label} kann nicht gestartet werden." @@ -11961,6 +12355,10 @@ msgstr "Remotezugriff kann nicht aktualisiert werden." msgid "Unable to update Tailscale HTTPS." msgstr "Tailscale HTTPS konnte nicht aktualisiert werden." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Der Download konnte nicht aktualisiert werden." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Die öffentliche URL konnte nicht aktualisiert werden." @@ -12345,6 +12743,10 @@ msgstr "Von {conflict} verwendet" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Benutzername" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Die integrierte Claude-Modellliste wird verwendet." @@ -12418,6 +12820,18 @@ msgstr "Änderungsprotokoll ansehen" msgid "View-only — this connection can't manage projects." msgstr "Nur Lesezugriff – diese Verbindung kann keine Projekte verwalten." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Höhe des Ansichtsbereichs" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Skalierung des Ansichtsbereichs" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Breite des Ansichtsbereichs" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Webserver" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Website" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Website-Ursprung" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Wochentags" @@ -12928,10 +13347,16 @@ msgstr "Ihre im Stash gesicherten lokalen Änderungen wurden erneut angewendet." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Zoom" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Vergrößern" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Verkleinern" diff --git a/src/renderer/locales/en/messages.po b/src/renderer/locales/en/messages.po index b35d54236..f24325d3d 100644 --- a/src/renderer/locales/en/messages.po +++ b/src/renderer/locales/en/messages.po @@ -679,6 +679,10 @@ msgstr "22" msgid "30d" msgstr "30d" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7d" @@ -832,6 +836,10 @@ msgstr "Add" msgid "Add a local project" msgstr "Add a local project" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Add a password or import one from another browser." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -924,6 +932,10 @@ msgstr "Add model" msgid "Add one manually or import from another agent." msgstr "Add one manually or import from another agent." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Add password" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Add pattern" @@ -993,6 +1005,10 @@ msgstr "Add to-do" msgid "Add workspace" msgstr "Add workspace" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Advanced" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." @@ -1082,6 +1098,7 @@ msgstr "Agents classify delegated work with task tags. Manual task routes rank f #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1666,11 +1683,12 @@ msgstr "blocked:" msgid "Bold" msgstr "Bold" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Bookmark this page" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Bookmarks" @@ -1724,6 +1742,10 @@ msgstr "Bring back to panel" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Bring it back into this panel, or jump to the window it’s running in." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Bring saved passwords and cookies over from an installed browser." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1777,7 +1799,7 @@ msgstr "Browser is open in a separate window" msgid "Browser MCP enabled for this thread" msgstr "Browser MCP enabled for this thread" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Browser menu" @@ -1898,6 +1920,8 @@ msgstr "Can't reach the remote server. Check that it is online, then reconnect i #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1909,6 +1933,10 @@ msgstr "Can't reach the remote server. Check that it is online, then reconnect i msgid "Cancel" msgstr "Cancel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Cancel download" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Cancel new Claude profile" @@ -1937,6 +1965,7 @@ msgstr "cancelled" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Cancelled" @@ -2133,6 +2162,10 @@ msgstr "Choose a parent folder" msgid "Choose at least two candidates and enter a prompt." msgstr "Choose at least two candidates and enter a prompt." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Choose data to bring over to the built-in browser." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2240,21 +2273,33 @@ msgstr "Clean Worktree" msgid "Cleanup script" msgstr "Cleanup script" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Clear browsing data" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Clear browsing history" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Clear Browsing History" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" +msgstr "Clear cache" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" -msgstr "Clear Cache" +#~ msgid "Clear Cache" +#~ msgstr "Clear Cache" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" +msgstr "Clear cookies" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" -msgstr "Clear Cookies" +#~ msgid "Clear Cookies" +#~ msgstr "Clear Cookies" #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" @@ -2366,6 +2411,10 @@ msgstr "Close Crossagents" msgid "Close Crossagents panel" msgstr "Close Crossagents panel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Close device toolbar" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Close editor" @@ -2727,6 +2776,7 @@ msgid "completed" msgstr "completed" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Completed" @@ -2972,6 +3022,10 @@ msgstr "Controls the paired desktop while the agent clicks or types" msgid "Conversation" msgstr "Conversation" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookies" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3007,7 +3061,7 @@ msgstr "Copy all Lightcode data into Poracode. Poracode restarts and keeps a com msgid "Copy code" msgstr "Copy code" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Copy Current URL" @@ -3050,6 +3104,10 @@ msgstr "Copy pairing link" msgid "Copy pairing token" msgstr "Copy pairing token" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Copy password" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Copy Path" @@ -3559,6 +3617,7 @@ msgstr "Delegate work to other AI agents." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3615,6 +3674,14 @@ msgstr "Delete permanently" msgid "Delete run" msgstr "Delete run" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Delete saved password" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Delete saved password?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Delete schedule" @@ -3780,6 +3847,10 @@ msgstr "Dev servers listening on your desktop's localhost." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Development-only overrides. Only visible in the PORACODE DEV build." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Device preset" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3789,6 +3860,10 @@ msgstr "Device used by the composer voice input button." msgid "Devices can pair while this desktop is reachable." msgstr "Devices can pair while this desktop is reachable." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Dimensions:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Direct connection required" @@ -3969,10 +4044,27 @@ msgstr "Done" msgid "Done · Waiting for your input" msgstr "Done · Waiting for your input" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Download failed" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Download history" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Download image" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Download progress" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Downloading" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3998,6 +4090,14 @@ msgstr "Downloading voice model..." msgid "Downloading… {0}%" msgstr "Downloading… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Downloads" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Downloads from the built-in browser are saved in this history." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4060,10 +4160,18 @@ msgstr "Edit goal" msgid "Edit MCP server" msgstr "Edit MCP server" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Edit password" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Edit profile" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Edit saved password" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4667,6 +4775,10 @@ msgstr "Files for {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Files matching these globs are hidden from the @file mention search." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Files you download appear here" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Fill in all required workflow inputs." @@ -4705,6 +4817,11 @@ msgstr "Find in chat" msgid "Find in diff" msgstr "Find in diff" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Find in page" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Find in terminal" @@ -4928,6 +5045,10 @@ msgstr "Frequency" msgid "Friday at 4:00 PM" msgstr "Friday at 4:00 PM" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "From" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." @@ -5190,7 +5311,7 @@ msgstr "handle" msgid "Handle" msgstr "Handle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Hard Reload" @@ -5254,6 +5375,10 @@ msgstr "Hide models you don't use from the model picker across every provider." msgid "Hide panel" msgstr "Hide panel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Hide password" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Hide runtime debug panel" @@ -5277,7 +5402,7 @@ msgstr "Hide skills from {0}" msgid "Hide terminal" msgstr "Hide terminal" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "History" @@ -5322,10 +5447,18 @@ msgstr "Hourly" msgid "HTTP (streamable)" msgstr "HTTP (streamable)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "I understand that protected browser data may be skipped." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5359,6 +5492,10 @@ msgstr "Image" msgid "Image preview" msgstr "Image preview" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Import" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Import again" @@ -5367,10 +5504,23 @@ msgstr "Import again" msgid "Import and restart" msgstr "Import and restart" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Import browser data" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Import conflict" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Import cookies" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Import cookies and passwords" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5388,6 +5538,10 @@ msgstr "Import external agent skills" msgid "Import external skills" msgstr "Import external skills" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Import from your browser" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Import Lightcode data" @@ -5404,10 +5558,18 @@ msgstr "Import MCP servers" msgid "Import method" msgstr "Import method" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Import passwords" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Import selected" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Import source" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Import to {destinationProjectName}" @@ -5435,6 +5597,17 @@ msgstr "Imported {0} skills." msgid "Imported 1 skill." msgstr "Imported 1 skill." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Imported from {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Imported passwords: {0}. Imported cookies: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Improved" @@ -5667,6 +5840,7 @@ msgstr "Instructions agents follow when choosing which agent or model to delegat #~ msgid "Interrupt" #~ msgstr "Interrupt" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Interrupted" @@ -5675,6 +5849,26 @@ msgstr "Interrupted" msgid "Interrupted: agent session ended before completion." msgstr "Interrupted: agent session ended before completion." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Italic" @@ -5774,6 +5968,14 @@ msgstr "Language for AI-generated commit messages and pull request summaries. Th msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Language the speech model should expect when transcribing composer dictation." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Last evaluation:" @@ -6001,6 +6203,10 @@ msgstr "Local" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Local changes need to be stashed before pulling from {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Local password protection" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6071,6 +6277,10 @@ msgstr "Longest streak" msgid "Longest task" msgstr "Longest task" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Looking for browser profiles…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Looking for dev servers…" @@ -6087,6 +6297,10 @@ msgstr "Make the sidebar translucent — the system blur material on Windows 11, #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Manage global and project agent skills, including skills imported from other providers." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Manage passwords" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Manage remote environments" @@ -6845,8 +7059,8 @@ msgstr "New schedule" #~ msgstr "New scheduled task" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "New tab" @@ -7089,6 +7303,10 @@ msgstr "No diff available for {0}" msgid "No diff for {visiblePath}" msgstr "No diff for {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "No downloads match your search" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "No external skills were found in this scope." @@ -7112,7 +7330,7 @@ msgstr "No GitHub CLI accounts found. Sign in with <0>gh auth login, or <1>p #~ msgid "No goal for this thread." #~ msgstr "No goal for this thread." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "No history yet" @@ -7207,6 +7425,10 @@ msgstr "No other agents installed" msgid "No paired devices yet." msgstr "No paired devices yet." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "No passwords match your search" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "No patterns." @@ -7280,6 +7502,10 @@ msgstr "No runs yet." msgid "No runtime items yet for this thread." msgstr "No runtime items yet for this thread." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "No saved passwords" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "No servers connected yet." @@ -7321,6 +7547,10 @@ msgstr "No staged changes" msgid "No supported agents detected" msgstr "No supported agents detected" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "No supported browser profiles were found on this device." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "No tags — the entry stops influencing tag affinity." @@ -7606,6 +7836,10 @@ msgstr "Open Crossagent: {0}" msgid "Open dialog" msgstr "Open dialog" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Open downloaded file" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Open experiment" @@ -7986,9 +8220,36 @@ msgid "Passphrase (optional)" msgstr "Passphrase (optional)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Password" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Password copied." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Password manager" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Password manager section" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Passwords" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Passwords and autofill" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8030,11 +8291,16 @@ msgstr "Pause" #~ msgid "Pause {0}" #~ msgstr "Pause {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Pause download" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Pause goal" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Paused" @@ -8137,6 +8403,10 @@ msgstr "Pinned" msgid "Pinned task routes" msgstr "Pinned task routes" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8367,6 +8637,10 @@ msgstr "Previous runs" msgid "Previous tab" msgstr "Previous tab" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Print" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Privacy Policy" @@ -8481,6 +8755,10 @@ msgstr "Prompts" msgid "Proposed plan" msgstr "Proposed plan" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Protected browser data" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Provided and managed by Poracode for supported agents." @@ -8991,6 +9269,7 @@ msgid "Remove action" msgstr "Remove action" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Remove bookmark" @@ -9016,6 +9295,10 @@ msgstr "Remove Cursor SDK API key" msgid "Remove environment variable" msgstr "Remove environment variable" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Remove from download history" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Remove from favorites" @@ -9209,6 +9492,10 @@ msgstr "Reset sidebar frosting to default" msgid "Reset to default" msgstr "Reset to default" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Reset zoom" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Resize browser drawer" @@ -9251,11 +9538,27 @@ msgstr "Resize split" msgid "Resize terminal panel" msgstr "Resize terminal panel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Resize viewport" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Resize viewport height" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Resize viewport width" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Resolves merge conflicts during rebase or merge." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Responsive" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Restart Poracode" @@ -9293,6 +9596,10 @@ msgstr "Resume" #~ msgid "Resume {label}" #~ msgstr "Resume {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Resume download" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Resume goal" @@ -9345,6 +9652,10 @@ msgstr "Reveal in File Explorer" msgid "Reveal in file tree" msgstr "Reveal in file tree" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Reveal password" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9446,6 +9757,10 @@ msgstr "Right panel" msgid "Right-click to switch ring" msgstr "Right-click to switch ring" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Rotate viewport" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9594,11 +9909,16 @@ msgstr "Runtime debug" msgid "Runtime options" msgstr "Runtime options" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9781,6 +10101,10 @@ msgstr "Search agents..." msgid "Search branches..." msgstr "Search branches..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Search downloads" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9814,6 +10138,10 @@ msgstr "Search models..." msgid "Search or enter address" msgstr "Search or enter address" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Search passwords" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Search pull requests" @@ -10034,7 +10362,7 @@ msgstr "Set when this session started — start a new thread to change servers" #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10084,11 +10412,11 @@ msgstr "Show a projectless Home scope for OS-level agent sessions." msgid "Show all" msgstr "Show all" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Show bookmark bar" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Show Bookmark Bar" @@ -10110,6 +10438,10 @@ msgstr "Show composer" msgid "Show context usage details" msgstr "Show context usage details" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Show device toolbar" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10124,6 +10456,10 @@ msgstr "Show estimated cost" msgid "Show in file explorer" msgstr "Show in file explorer" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Show in folder" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10405,11 +10741,20 @@ msgstr "Solution {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Solution {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Some browser data was skipped. Protected items skipped: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Some checks are failing or pending." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10849,6 +11194,14 @@ msgstr "Support:" msgid "Supports Fast mode" msgstr "Supports Fast mode" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Swap notes and to-dos" @@ -10969,7 +11322,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "Tailscale URL" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Take Screenshot" @@ -11397,6 +11750,11 @@ msgstr "This project's remote server is offline. Reconnect it to start a thread. msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "This removes later messages and restores files when a checkpoint snapshot is available." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "This removes the saved password for <0>{0} from Poracode." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "This removes the schedule and its latest result from this device." @@ -11744,6 +12102,10 @@ msgstr "Unable to copy pairing token." msgid "Unable to copy Tailscale URL." msgstr "Unable to copy Tailscale URL." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Unable to copy the password." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Unable to create the candidate worktree." @@ -11757,6 +12119,10 @@ msgstr "Unable to create the pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Unable to create WSL worktree directory \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Unable to delete the saved password." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Unable to delete the worktree." @@ -11765,6 +12131,18 @@ msgstr "Unable to delete the worktree." msgid "Unable to determine the experiment's source branch." msgstr "Unable to determine the experiment's source branch." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Unable to edit the saved password." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Unable to find browser data to import." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Unable to import browser data." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11779,10 +12157,18 @@ msgstr "Unable to install {0}." msgid "Unable to load diff." msgstr "Unable to load diff." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Unable to load downloads." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Unable to load remote access pairing." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Unable to load saved passwords." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Unable to load thread." @@ -11882,6 +12268,10 @@ msgstr "Unable to resolve the experiment candidate commit" msgid "Unable to restore the SSH connection." msgstr "Unable to restore the SSH connection." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Unable to reveal the password." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Unable to revoke remote session." @@ -11895,6 +12285,10 @@ msgstr "Unable to save {0} credentials." msgid "Unable to save Claude {displayLabel} profile." msgstr "Unable to save Claude {displayLabel} profile." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Unable to save the password." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Unable to start {label}." @@ -11966,6 +12360,10 @@ msgstr "Unable to update remote access." msgid "Unable to update Tailscale HTTPS." msgstr "Unable to update Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Unable to update the download." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Unable to update the public URL." @@ -12350,6 +12748,10 @@ msgstr "Used by {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Username" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Using the built-in Claude model list." @@ -12423,6 +12825,18 @@ msgstr "View the changelog" msgid "View-only — this connection can't manage projects." msgstr "View-only — this connection can't manage projects." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Viewport height" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Viewport scale" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Viewport width" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12565,11 +12979,16 @@ msgid "Web server" msgstr "Web server" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Website" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Website origin" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Weekdays" @@ -12933,10 +13352,16 @@ msgstr "Your stashed local changes were re-applied." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Zoom" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Zoom in" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Zoom out" diff --git a/src/renderer/locales/es/messages.po b/src/renderer/locales/es/messages.po index 84d4334d3..fe296fd2f 100644 --- a/src/renderer/locales/es/messages.po +++ b/src/renderer/locales/es/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30d" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7d" @@ -827,6 +831,10 @@ msgstr "Añadir" msgid "Add a local project" msgstr "Agregar un proyecto local" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Agrega una contraseña o importa una desde otro navegador." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Añadir modelo" msgid "Add one manually or import from another agent." msgstr "Añade uno manualmente o impórtalo desde otro agente." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Agregar contraseña" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Añadir patrón" @@ -988,6 +1000,10 @@ msgstr "Añadir tarea" msgid "Add workspace" msgstr "Añadir espacio de trabajo" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Avanzado" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Anuncia una URL base personalizada, por ejemplo un proxy inverso o un túnel. Déjalo vacío para que sea automático." @@ -1077,6 +1093,7 @@ msgstr "Los agentes clasifican el trabajo delegado con etiquetas de tarea. Las r #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Los agentes clasifican el trabajo delegado con etiquetas de tarea. Las rutas aprendidas que coinciden se clasifican primero, seguidas del uso global de Crossagents, los favoritos, el uso normal de agentes y el orden integrado." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "bloqueado:" msgid "Bold" msgstr "Negrita" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Añadir esta página a marcadores" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Marcadores" @@ -1719,6 +1737,10 @@ msgstr "Devolver al panel" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Devuélvelo a este panel o ve a la ventana en la que se está ejecutando." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Importa las contraseñas y cookies guardadas desde un navegador instalado." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "El navegador está abierto en una ventana separada" msgid "Browser MCP enabled for this thread" msgstr "Browser MCP habilitado para este hilo" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Menú del navegador" @@ -1893,6 +1915,8 @@ msgstr "No se puede conectar con el servidor remoto. Comprueba que esté en lín #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "No se puede conectar con el servidor remoto. Comprueba que esté en lín msgid "Cancel" msgstr "Cancelar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Cancelar descarga" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Cancelar nuevo perfil de Claude" @@ -1932,6 +1960,7 @@ msgstr "cancelado" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Cancelado" @@ -2128,6 +2157,10 @@ msgstr "Elige una carpeta principal" msgid "Choose at least two candidates and enter a prompt." msgstr "Elige al menos dos candidatos e introduce un prompt." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Elige los datos que quieres transferir al navegador integrado." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Worktree limpio" msgid "Cleanup script" msgstr "Script de limpieza" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Borrar datos de navegación" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Borrar historial de navegación" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Borrar historial de navegación" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Borrar caché" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Borrar caché" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Borrar cookies" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Borrar cookies" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Borrar objetivo" @@ -2361,6 +2406,10 @@ msgstr "Cerrar Crossagents" msgid "Close Crossagents panel" msgstr "Cerrar el panel de Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Cerrar barra de herramientas de dispositivos" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Cerrar editor" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "completado" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Completado" @@ -2967,6 +3017,10 @@ msgstr "Controla el escritorio emparejado mientras el agente hace clic o escribe msgid "Conversation" msgstr "Conversación" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookies" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Copia todos los datos de Lightcode en Poracode. Poracode se reiniciará msgid "Copy code" msgstr "Copiar código" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Copiar la URL actual" @@ -3045,6 +3099,10 @@ msgstr "Copiar enlace de emparejamiento" msgid "Copy pairing token" msgstr "Copiar token de emparejamiento" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Copiar contraseña" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Copiar ruta" @@ -3554,6 +3612,7 @@ msgstr "Delega trabajo a otros agentes de IA." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Eliminar permanentemente" msgid "Delete run" msgstr "Eliminar ejecución" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Eliminar contraseña guardada" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "¿Eliminar contraseña guardada?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Eliminar programación" @@ -3775,6 +3842,10 @@ msgstr "Servidores de desarrollo escuchando en el localhost de tu escritorio." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Sustituciones solo para desarrollo. Solo visibles en la compilación PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Dispositivo predefinido" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Dispositivo que usa el botón de entrada de voz del redactor." msgid "Devices can pair while this desktop is reachable." msgstr "Los dispositivos pueden emparejarse mientras este escritorio esté accesible." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Dimensiones:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Se requiere una conexión directa" @@ -3964,10 +4039,27 @@ msgstr "Listo" msgid "Done · Waiting for your input" msgstr "Listo · Esperando tu entrada" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Error en la descarga" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Historial de descargas" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Descargar imagen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Progreso de la descarga" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Descargando" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Descargando modelo de voz..." msgid "Downloading… {0}%" msgstr "Descargando… {0} %" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Descargas" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Las descargas del navegador integrado se guardan en este historial." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Editar objetivo" msgid "Edit MCP server" msgstr "Editar servidor MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Editar contraseña" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Editar perfil" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Editar contraseña guardada" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Archivos de {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Los archivos que coinciden con estos globs se ocultan de la búsqueda de menciones @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Los archivos que descargues aparecerán aquí" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Completa todas las entradas obligatorias del workflow." @@ -4700,6 +4812,11 @@ msgstr "Buscar en el chat" msgid "Find in diff" msgstr "Buscar en el diff" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Buscar en la página" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Buscar en el terminal" @@ -4923,6 +5040,10 @@ msgstr "Frecuencia" msgid "Friday at 4:00 PM" msgstr "Viernes a las 4:00 p. m." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Desde" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Aplica al panel lateral el efecto esmerilado del desenfoque del sistema (vibrancy), al estilo de las versiones recientes de macOS. Si no es compatible, usa un tinte translúcido." @@ -5185,7 +5306,7 @@ msgstr "identificador" msgid "Handle" msgstr "Identificador" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Recarga forzada" @@ -5249,6 +5370,10 @@ msgstr "Oculta los modelos que no usas del selector de modelos en todos los prov msgid "Hide panel" msgstr "Ocultar panel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Ocultar contraseña" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Ocultar panel de depuración del runtime" @@ -5272,7 +5397,7 @@ msgstr "Ocultar skills de {0}" msgid "Hide terminal" msgstr "Ocultar terminal" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Historial" @@ -5317,10 +5442,18 @@ msgstr "Cada hora" msgid "HTTP (streamable)" msgstr "HTTP (transmisión continua)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Entiendo que algunos datos protegidos del navegador podrían omitirse." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Imagen" msgid "Image preview" msgstr "Vista previa de imagen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Importar" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Importar de nuevo" @@ -5362,10 +5499,23 @@ msgstr "Importar de nuevo" msgid "Import and restart" msgstr "Importar y reiniciar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Importar datos del navegador" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Conflicto de importación" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Importar cookies" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Importar cookies y contraseñas" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Importar skills de agentes externos" msgid "Import external skills" msgstr "Importar skills externos" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Importar desde tu navegador" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Importar datos de Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Importar servidores MCP" msgid "Import method" msgstr "Método de importación" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Importar contraseñas" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Importar seleccionados" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Origen de importación" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Importar a {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "Se importaron {0} skills." msgid "Imported 1 skill." msgstr "Se importó 1 skill." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Importado desde {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Contraseñas importadas: {0}. Cookies importadas: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Mejorado" @@ -5662,6 +5835,7 @@ msgstr "Instrucciones que siguen los agentes al elegir a qué agente o modelo de #~ msgid "Interrupt" #~ msgstr "Interrumpir" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Interrumpida" @@ -5670,6 +5844,26 @@ msgstr "Interrumpida" msgid "Interrupted: agent session ended before completion." msgstr "Interrumpido: la sesión del agente terminó antes de completarse." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Cursiva" @@ -5769,6 +5963,14 @@ msgstr "Idioma de los mensajes de commit y los resúmenes de pull request genera msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Idioma que debe esperar el modelo de voz al transcribir el dictado del redactor." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Última evaluación:" @@ -5996,6 +6198,10 @@ msgstr "Local" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Es necesario guardar los cambios locales en stash antes de hacer pull desde {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Protección local de contraseñas" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Racha más larga" msgid "Longest task" msgstr "Tarea más larga" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Buscando perfiles de navegador…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Buscando servidores de desarrollo…" @@ -6082,6 +6292,10 @@ msgstr "Hace translúcido el panel lateral: el efecto de desenfoque del sistema #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Gestiona los skills globales y de proyecto de los agentes, incluidos los importados de otros proveedores." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Administrar contraseñas" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Administrar entornos remotos" @@ -6840,8 +7054,8 @@ msgstr "Nueva programación" #~ msgstr "Nueva tarea programada" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Nueva pestaña" @@ -7084,6 +7298,10 @@ msgstr "No hay diff disponible para {0}" msgid "No diff for {visiblePath}" msgstr "No hay diff para {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Ninguna descarga coincide con tu búsqueda" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "No se encontraron skills externos en este ámbito." @@ -7107,7 +7325,7 @@ msgstr "No se encontraron cuentas de GitHub CLI. Inicia sesión con <0>gh auth l #~ msgid "No goal for this thread." #~ msgstr "No hay ningún objetivo para este hilo." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Aún no hay historial" @@ -7202,6 +7420,10 @@ msgstr "No hay otros agentes instalados" msgid "No paired devices yet." msgstr "Aún no hay dispositivos emparejados." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Ninguna contraseña coincide con tu búsqueda" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Sin patrones." @@ -7275,6 +7497,10 @@ msgstr "Aún no hay ejecuciones." msgid "No runtime items yet for this thread." msgstr "Aún no hay elementos de runtime para este hilo." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "No hay contraseñas guardadas" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Aún no hay servidores conectados." @@ -7316,6 +7542,10 @@ msgstr "No hay cambios preparados" msgid "No supported agents detected" msgstr "No se detectaron agentes compatibles" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "No se encontraron perfiles de navegador compatibles en este dispositivo." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Sin etiquetas: la entrada deja de influir en la afinidad de etiquetas." @@ -7601,6 +7831,10 @@ msgstr "Abrir Crossagent: {0}" msgid "Open dialog" msgstr "Abrir diálogo" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Abrir archivo descargado" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Abrir experimento" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Frase de contraseña (opcional)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Contraseña" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Contraseña copiada." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Gestor de contraseñas" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Sección del gestor de contraseñas" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Contraseñas" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Contraseñas y autocompletado" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Las contraseñas se cifran en el almacén local de credenciales de Poracode. Los valores de las contraseñas solo se solicitan cuando las muestras, copias o editas." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Pausar" #~ msgid "Pause {0}" #~ msgstr "Pausar {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Pausar descarga" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Pausar objetivo" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "En pausa" @@ -8132,6 +8398,10 @@ msgstr "Fijado" msgid "Pinned task routes" msgstr "Rutas de tarea fijadas" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Ejecuciones anteriores" msgid "Previous tab" msgstr "Pestaña anterior" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Imprimir" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Política de privacidad" @@ -8476,6 +8750,10 @@ msgstr "Prompts" msgid "Proposed plan" msgstr "Plan propuesto" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Datos protegidos del navegador" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Poracode lo proporciona y administra para los agentes compatibles." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Eliminar acción" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Eliminar marcador" @@ -9011,6 +9290,10 @@ msgstr "Eliminar la clave de API del SDK de Cursor" msgid "Remove environment variable" msgstr "Eliminar variable de entorno" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Eliminar del historial de descargas" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Quitar de favoritos" @@ -9204,6 +9487,10 @@ msgstr "Restablecer el esmerilado del panel lateral a sus valores predeterminado msgid "Reset to default" msgstr "Restablecer valores predeterminados" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Restablecer zoom" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Redimensionar cajón del navegador" @@ -9246,11 +9533,27 @@ msgstr "Redimensionar división" msgid "Resize terminal panel" msgstr "Redimensionar panel de terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Cambiar el tamaño del área de visualización" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Cambiar la altura del área de visualización" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Cambiar la anchura del área de visualización" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Resuelve conflictos de fusión durante el rebase o la fusión." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Adaptable" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Reiniciar Poracode" @@ -9288,6 +9591,10 @@ msgstr "Reanudar" #~ msgid "Resume {label}" #~ msgstr "Reanudar {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Reanudar descarga" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Reanudar objetivo" @@ -9340,6 +9647,10 @@ msgstr "Mostrar en el Explorador de archivos" msgid "Reveal in file tree" msgstr "Mostrar en el árbol de archivos" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Mostrar contraseña" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Panel derecho" msgid "Right-click to switch ring" msgstr "Haz clic derecho para cambiar de anillo" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Girar el área de visualización" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Depuración de runtime" msgid "Runtime options" msgstr "Opciones de runtime" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Buscar agentes..." msgid "Search branches..." msgstr "Buscar ramas..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Buscar descargas" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Buscar modelos..." msgid "Search or enter address" msgstr "Buscar o introducir dirección" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Buscar contraseñas" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Buscar pull requests" @@ -10029,7 +10357,7 @@ msgstr "Se fijó al iniciar esta sesión — inicia un hilo nuevo para cambiar l #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "Mostrar un ámbito de inicio sin proyecto para las sesiones de agente a msgid "Show all" msgstr "Mostrar todo" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Mostrar la barra de marcadores" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Mostrar barra de marcadores" @@ -10105,6 +10433,10 @@ msgstr "Mostrar Composer" msgid "Show context usage details" msgstr "Mostrar detalles del uso de contexto" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Mostrar barra de herramientas de dispositivos" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Mostrar coste estimado" msgid "Show in file explorer" msgstr "Mostrar en el explorador de archivos" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Mostrar en la carpeta" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Solución {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Solución {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Se omitieron algunos datos del navegador. Elementos protegidos omitidos: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Algunas comprobaciones están fallando o pendientes." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Algunos navegadores Chromium protegen las cookies y contraseñas con cifrado App-Bound. Poracode no elude esa protección. Los datos que el sistema operativo no ponga a disposición se omitirán." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Soporte:" msgid "Supports Fast mode" msgstr "Compatible con el modo rápido" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Intercambiar notas y tareas pendientes" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "URL de Tailscale" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Hacer captura de pantalla" @@ -11392,6 +11745,11 @@ msgstr "El servidor remoto de este proyecto está sin conexión. Vuelve a conect msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Esto elimina los mensajes posteriores y restaura los archivos cuando hay disponible una instantánea de punto de control." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Esto elimina de Poracode la contraseña guardada de <0>{0}." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Esto elimina la programación y su resultado más reciente de este dispositivo." @@ -11739,6 +12097,10 @@ msgstr "No se pudo copiar el token de emparejamiento." msgid "Unable to copy Tailscale URL." msgstr "No se pudo copiar la URL de Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "No se pudo copiar la contraseña." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "No se pudo crear el árbol de trabajo del candidato." @@ -11752,6 +12114,10 @@ msgstr "No se puede crear la pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "No se pudo crear el directorio de worktree de WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "No se pudo eliminar la contraseña guardada." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "No se pudo eliminar el worktree." @@ -11760,6 +12126,18 @@ msgstr "No se pudo eliminar el worktree." msgid "Unable to determine the experiment's source branch." msgstr "No se puede determinar la rama de origen del experimento." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "No se pudo editar la contraseña guardada." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "No se encontraron datos del navegador para importar." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "No se pudieron importar los datos del navegador." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "No se pudo instalar {0}." msgid "Unable to load diff." msgstr "No se pudo cargar el diff." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "No se pudieron cargar las descargas." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "No se pudo cargar el emparejamiento de acceso remoto." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "No se pudieron cargar las contraseñas guardadas." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "No se pudo cargar el hilo." @@ -11877,6 +12263,10 @@ msgstr "No se pudo resolver el commit del candidato del experimento" msgid "Unable to restore the SSH connection." msgstr "No se pudo restaurar la conexión SSH." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "No se pudo mostrar la contraseña." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "No se pudo revocar la sesión remota." @@ -11890,6 +12280,10 @@ msgstr "No se pueden guardar las credenciales de {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "No se pudo guardar el perfil {displayLabel} de Claude." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "No se pudo guardar la contraseña." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "No se pudo iniciar {label}." @@ -11961,6 +12355,10 @@ msgstr "No se pudo actualizar el acceso remoto." msgid "Unable to update Tailscale HTTPS." msgstr "No se pudo actualizar el HTTPS de Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "No se pudo actualizar la descarga." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "No se pudo actualizar la URL pública." @@ -12345,6 +12743,10 @@ msgstr "Lo usa {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Nombre de usuario" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Usando la lista de modelos integrada de Claude." @@ -12418,6 +12820,18 @@ msgstr "Ver el registro de cambios" msgid "View-only — this connection can't manage projects." msgstr "Solo lectura: esta conexión no puede gestionar proyectos." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Altura del área de visualización" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Escala del área de visualización" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Anchura del área de visualización" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Servidor web" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Sitio web" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Origen del sitio web" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Días laborables" @@ -12928,10 +13347,16 @@ msgstr "Tus cambios locales guardados en el stash se volvieron a aplicar." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Zoom" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Acercar" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Alejar" diff --git a/src/renderer/locales/fr/messages.po b/src/renderer/locales/fr/messages.po index f2de8ceb6..710784ab6 100644 --- a/src/renderer/locales/fr/messages.po +++ b/src/renderer/locales/fr/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30 j" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7 j" @@ -827,6 +831,10 @@ msgstr "Ajouter" msgid "Add a local project" msgstr "Ajouter un projet local" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Ajoutez un mot de passe ou importez-en un depuis un autre navigateur." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Ajouter un modèle" msgid "Add one manually or import from another agent." msgstr "Ajoutez-en un manuellement ou importez-le depuis un autre agent." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Ajouter un mot de passe" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Ajouter un motif" @@ -988,6 +1000,10 @@ msgstr "Ajouter une tâche" msgid "Add workspace" msgstr "Ajouter un espace de travail" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Avancé" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Annoncez une URL de base personnalisée, par ex. un proxy inverse ou un tunnel. Laissez vide pour un choix automatique." @@ -1077,6 +1093,7 @@ msgstr "Les agents classent le travail délégué avec des tags de tâche. Les r #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Les agents classent le travail délégué avec des étiquettes de tâche. Les routes apprises correspondantes sont classées en premier, suivies de l’utilisation globale de Crossagents, des favoris, de l’utilisation normale des agents et de l’ordre intégré." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "bloqué :" msgid "Bold" msgstr "Gras" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Ajouter cette page aux favoris" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Favoris" @@ -1719,6 +1737,10 @@ msgstr "Ramener dans le panneau" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Ramenez-le dans ce panneau ou accédez à la fenêtre dans laquelle il s’exécute." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Importez les mots de passe et les cookies enregistrés depuis un navigateur installé." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Le navigateur est ouvert dans une fenêtre séparée" msgid "Browser MCP enabled for this thread" msgstr "Navigateur MCP activé pour ce fil de discussion" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Menu du navigateur" @@ -1893,6 +1915,8 @@ msgstr "Serveur distant inaccessible. Vérifiez qu'il est en ligne, puis reconne #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Serveur distant inaccessible. Vérifiez qu'il est en ligne, puis reconne msgid "Cancel" msgstr "Annuler" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Annuler le téléchargement" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Annuler le nouveau profil Claude" @@ -1932,6 +1960,7 @@ msgstr "annulé" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Annulé" @@ -2128,6 +2157,10 @@ msgstr "Choisissez un dossier parent" msgid "Choose at least two candidates and enter a prompt." msgstr "Choisissez au moins deux candidats et saisissez une invite." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Choisissez les données à transférer vers le navigateur intégré." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Worktree propre" msgid "Cleanup script" msgstr "Script de nettoyage" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Effacer les données de navigation" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Effacer l’historique de navigation" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Effacer l'historique de navigation" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Vider le cache" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Vider le cache" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Effacer les cookies" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Effacer les cookies" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Effacer l'objectif" @@ -2361,6 +2406,10 @@ msgstr "Fermer Crossagents" msgid "Close Crossagents panel" msgstr "Fermer le panneau Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Fermer la barre d’outils des appareils" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Fermer l'éditeur" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "terminé" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Terminé" @@ -2967,6 +3017,10 @@ msgstr "Contrôle le bureau associé pendant que l'agent clique ou tape" msgid "Conversation" msgstr "Conversation" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookies" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Copiez toutes les données Lightcode dans Poracode. Poracode redémarrer msgid "Copy code" msgstr "Copier le code" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Copier l'URL actuelle" @@ -3045,6 +3099,10 @@ msgstr "Copier le lien d’association" msgid "Copy pairing token" msgstr "Copier le jeton d'association" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Copier le mot de passe" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Copier le chemin" @@ -3554,6 +3612,7 @@ msgstr "Déléguez le travail à d’autres agents IA." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Supprimer définitivement" msgid "Delete run" msgstr "Supprimer l’exécution" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Supprimer le mot de passe enregistré" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Supprimer le mot de passe enregistré ?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Supprimer la planification" @@ -3775,6 +3842,10 @@ msgstr "Serveurs de développement à l'écoute sur le localhost de votre bureau msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Remplacements réservés au développement. Visible uniquement dans la version PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Préréglage d’appareil" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Appareil utilisé par le bouton de saisie vocale du composeur." msgid "Devices can pair while this desktop is reachable." msgstr "Les appareils peuvent s’appairer lorsque ce bureau est joignable." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Dimensions :" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Connexion directe requise" @@ -3964,10 +4039,27 @@ msgstr "Terminé" msgid "Done · Waiting for your input" msgstr "Terminé · En attente de votre saisie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Échec du téléchargement" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Historique des téléchargements" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Télécharger l'image" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Progression du téléchargement" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Téléchargement en cours" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Téléchargement du modèle vocal..." msgid "Downloading… {0}%" msgstr "Téléchargement… {0} %" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Téléchargements" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Les téléchargements du navigateur intégré sont enregistrés dans cet historique." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Modifier l'objectif" msgid "Edit MCP server" msgstr "Modifier le serveur MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Modifier le mot de passe" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Modifier le profil" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Modifier le mot de passe enregistré" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Fichiers pour {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Les fichiers correspondant à ces globs sont masqués dans la recherche de mention @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Les fichiers que vous téléchargez apparaissent ici" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Renseignez toutes les entrées obligatoires du workflow." @@ -4700,6 +4812,11 @@ msgstr "Rechercher dans le chat" msgid "Find in diff" msgstr "Rechercher dans la différence" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Rechercher dans la page" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Rechercher dans le terminal" @@ -4923,6 +5040,10 @@ msgstr "Fréquence" msgid "Friday at 4:00 PM" msgstr "Vendredi à 16 h 00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Depuis" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Givre la barre latérale avec le flou système (vibrancy), à la manière des versions récentes de macOS. Bascule sur une teinte translucide là où ce n'est pas pris en charge." @@ -5185,7 +5306,7 @@ msgstr "identifiant" msgid "Handle" msgstr "Identifiant" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Rechargement forcé" @@ -5249,6 +5370,10 @@ msgstr "Masquez les modèles que vous n’utilisez pas dans le sélecteur de mod msgid "Hide panel" msgstr "Masquer le panneau" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Masquer le mot de passe" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Masquer le panneau de débogage d'exécution" @@ -5272,7 +5397,7 @@ msgstr "Masquer les compétences de {0}" msgid "Hide terminal" msgstr "Masquer le terminal" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Historique" @@ -5317,10 +5442,18 @@ msgstr "Toutes les heures" msgid "HTTP (streamable)" msgstr "HTTP (diffusion en continu)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Je comprends que certaines données protégées du navigateur peuvent être ignorées." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Image" msgid "Image preview" msgstr "Aperçu de l'image" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Importer" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Importer à nouveau" @@ -5362,10 +5499,23 @@ msgstr "Importer à nouveau" msgid "Import and restart" msgstr "Importer et redémarrer" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Importer les données du navigateur" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Conflit d’importation" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Importer les cookies" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Importer les cookies et les mots de passe" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Importer des compétences d’agents externes" msgid "Import external skills" msgstr "Importer des compétences externes" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Importer depuis votre navigateur" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Importer les données Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Importer des serveurs MCP" msgid "Import method" msgstr "Méthode d’importation" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Importer les mots de passe" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Importer la sélection" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Source de l’importation" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Importer dans {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "{0} compétences importées." msgid "Imported 1 skill." msgstr "1 compétence importée." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Importé depuis {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Mots de passe importés : {0}. Cookies importés : {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Amélioré" @@ -5662,6 +5835,7 @@ msgstr "Instructions que les agents suivent pour choisir à quel agent ou modèl #~ msgid "Interrupt" #~ msgstr "Interrompre" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Interrompue" @@ -5670,6 +5844,26 @@ msgstr "Interrompue" msgid "Interrupted: agent session ended before completion." msgstr "Interrompu : la session de l'agent s'est terminée avant la fin." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Italique" @@ -5769,6 +5963,14 @@ msgstr "Langue des messages de commit et des résumés de pull request généré msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Langue que le modèle vocal doit attendre lors de la transcription de la dictée dans le composeur." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Dernière évaluation :" @@ -5996,6 +6198,10 @@ msgstr "Local" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Les modifications locales doivent être mises de côté (stash) avant de récupérer depuis {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Protection locale des mots de passe" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Plus longue série" msgid "Longest task" msgstr "Tâche la plus longue" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Recherche des profils de navigateur…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Recherche de serveurs de développement…" @@ -6082,6 +6292,10 @@ msgstr "Rend la barre latérale translucide — le flou système sur Windows 11, #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Gérez les compétences globales et de projet des agents, y compris celles importées depuis d’autres fournisseurs." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Gérer les mots de passe" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Gérer les environnements distants" @@ -6839,8 +7053,8 @@ msgstr "Nouvelle planification" #~ msgstr "Nouvelle tâche planifiée" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Nouvel onglet" @@ -7083,6 +7297,10 @@ msgstr "Aucune différence disponible pour {0}" msgid "No diff for {visiblePath}" msgstr "Aucune différence pour {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Aucun téléchargement ne correspond à votre recherche" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "Aucune compétence externe n’a été trouvée dans cette portée." @@ -7106,7 +7324,7 @@ msgstr "Aucun compte GitHub CLI trouvé. Connectez-vous avec <0>gh auth login{0} from Poracode." +msgstr "Cette action supprime de Poracode le mot de passe enregistré pour <0>{0}." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Cela supprime la planification et son dernier résultat de cet appareil." @@ -11738,6 +12096,10 @@ msgstr "Impossible de copier le jeton d'association." msgid "Unable to copy Tailscale URL." msgstr "Impossible de copier l'URL Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Impossible de copier le mot de passe." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Impossible de créer l’arbre de travail du candidat." @@ -11751,6 +12113,10 @@ msgstr "Impossible de créer la pull request : {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Impossible de créer le répertoire d'arbre de travail WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Impossible de supprimer le mot de passe enregistré." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Impossible de supprimer l'arbre de travail." @@ -11759,6 +12125,18 @@ msgstr "Impossible de supprimer l'arbre de travail." msgid "Unable to determine the experiment's source branch." msgstr "Impossible de déterminer la branche source de l’expérience." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Impossible de modifier le mot de passe enregistré." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Impossible de trouver des données de navigateur à importer." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Impossible d’importer les données du navigateur." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11773,10 +12151,18 @@ msgstr "Impossible d'installer {0}." msgid "Unable to load diff." msgstr "Impossible de charger le diff." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Impossible de charger les téléchargements." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Impossible de charger l’association d’accès distant." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Impossible de charger les mots de passe enregistrés." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Impossible de charger le fil." @@ -11876,6 +12262,10 @@ msgstr "Impossible de résoudre le commit du candidat de l'expérience" msgid "Unable to restore the SSH connection." msgstr "Impossible de rétablir la connexion SSH." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Impossible d’afficher le mot de passe." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Impossible de révoquer la session distante." @@ -11889,6 +12279,10 @@ msgstr "Impossible d'enregistrer les informations d'identification {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Impossible d'enregistrer le profil Claude {displayLabel}." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Impossible d’enregistrer le mot de passe." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Impossible de démarrer {label}." @@ -11960,6 +12354,10 @@ msgstr "Impossible de mettre à jour l’accès distant." msgid "Unable to update Tailscale HTTPS." msgstr "Impossible de mettre à jour Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Impossible de mettre à jour le téléchargement." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Impossible de mettre à jour l'URL publique." @@ -12344,6 +12742,10 @@ msgstr "Utilisé par {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Nom d’utilisateur" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Utilisation de la liste de modèles Claude intégrée." @@ -12417,6 +12819,18 @@ msgstr "Voir le journal des modifications" msgid "View-only — this connection can't manage projects." msgstr "Lecture seule — cette connexion ne peut pas gérer les projets." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Hauteur de la zone d’affichage" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Échelle de la zone d’affichage" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Largeur de la zone d’affichage" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12559,11 +12973,16 @@ msgid "Web server" msgstr "Serveur web" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Site Web" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Origine du site web" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Jours de semaine" @@ -12927,10 +13346,16 @@ msgstr "Vos modifications locales mises en stash ont été réappliquées." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Zoom" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Zoom avant" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Zoom arrière" diff --git a/src/renderer/locales/ja/messages.po b/src/renderer/locales/ja/messages.po index 52ac50fd7..bd166c785 100644 --- a/src/renderer/locales/ja/messages.po +++ b/src/renderer/locales/ja/messages.po @@ -673,6 +673,10 @@ msgstr "22" msgid "30d" msgstr "30日" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7日" @@ -826,6 +830,10 @@ msgstr "追加" msgid "Add a local project" msgstr "ローカルプロジェクトを追加" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "パスワードを追加するか、別のブラウザからインポートしてください。" + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -918,6 +926,10 @@ msgstr "モデルを追加" msgid "Add one manually or import from another agent." msgstr "手動で追加するか、別のエージェントからインポートしてください。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "パスワードを追加" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "パターンを追加する" @@ -987,6 +999,10 @@ msgstr "タスクを追加" msgid "Add workspace" msgstr "ワークスペースを追加" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "詳細設定" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "カスタムのベースURLを通知します(例: リバースプロキシやトンネル)。空欄の場合は自動的に設定されます。" @@ -1076,6 +1092,7 @@ msgstr "エージェントはタスクタグで委任された作業を分類し #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "エージェントは委任された作業をタスクタグで分類します。一致する学習済みルートが最優先され、その後に Crossagents 全体の使用状況、お気に入り、通常のエージェント使用状況、組み込み順序が続きます。" +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1660,11 +1677,12 @@ msgstr "ブロックされました:" msgid "Bold" msgstr "太字" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "このページをブックマーク" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "ブックマーク" @@ -1718,6 +1736,10 @@ msgstr "パネルに戻す" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "このパネルに戻すか、実行中のウィンドウに移動します。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "インストール済みのブラウザから保存されたパスワードと Cookie を取り込みます。" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1771,7 +1793,7 @@ msgstr "ブラウザは別のウィンドウで開いています" msgid "Browser MCP enabled for this thread" msgstr "このスレッドに対してブラウザ MCP が有効になっています" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "ブラウザメニュー" @@ -1892,6 +1914,8 @@ msgstr "リモートサーバーに接続できません。オンラインであ #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1903,6 +1927,10 @@ msgstr "リモートサーバーに接続できません。オンラインであ msgid "Cancel" msgstr "キャンセル" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "ダウンロードをキャンセル" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "新しい Claude プロファイルをキャンセル" @@ -1931,6 +1959,7 @@ msgstr "キャンセルされました" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "キャンセル済み" @@ -2127,6 +2156,10 @@ msgstr "親フォルダーを選択" msgid "Choose at least two candidates and enter a prompt." msgstr "候補を 2 つ以上選択し、プロンプトを入力してください。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "内蔵ブラウザに取り込むデータを選択してください。" + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2234,22 +2267,34 @@ msgstr "クリーンなワークツリー" msgid "Cleanup script" msgstr "クリーンアップスクリプト" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "閲覧データを消去" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "閲覧履歴を消去" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "閲覧履歴を消去" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" -msgstr "キャッシュのクリア" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" +msgstr "キャッシュを消去" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "キャッシュのクリア" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Cookie を消去" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Cookie を消去" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "目標をクリア" @@ -2360,6 +2405,10 @@ msgstr "Crossagentsを閉じる" msgid "Close Crossagents panel" msgstr "Crossagentsパネルを閉じる" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "デバイス ツールバーを閉じる" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "エディタを閉じる" @@ -2721,6 +2770,7 @@ msgid "completed" msgstr "完了しました" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "完了" @@ -2966,6 +3016,10 @@ msgstr "エージェントがクリックや入力をしている間、ペアリ msgid "Conversation" msgstr "会話" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3001,7 +3055,7 @@ msgstr "すべての Lightcode データを Poracode にコピーします。Por msgid "Copy code" msgstr "コードをコピーする" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "現在の URL をコピー" @@ -3044,6 +3098,10 @@ msgstr "ペアリングリンクをコピー" msgid "Copy pairing token" msgstr "ペアリングトークンをコピー" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "パスワードをコピー" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "パスのコピー" @@ -3553,6 +3611,7 @@ msgstr "他の AI エージェントに作業を委任します。" #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3609,6 +3668,14 @@ msgstr "完全に削除" msgid "Delete run" msgstr "実行を削除" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "保存済みパスワードを削除" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "保存済みパスワードを削除しますか?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "スケジュールを削除" @@ -3774,6 +3841,10 @@ msgstr "デスクトップの localhost で待ち受けている開発サーバ msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "開発専用の上書き。 PORACODE DEV ビルドでのみ表示されます。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "デバイス プリセット" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3783,6 +3854,10 @@ msgstr "コンポーザーの音声入力ボタンで使用されるデバイス msgid "Devices can pair while this desktop is reachable." msgstr "このデスクトップに到達できる間、デバイスをペアリングできます。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "サイズ:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "直接接続が必要です" @@ -3963,10 +4038,27 @@ msgstr "完了" msgid "Done · Waiting for your input" msgstr "完了 · 入力を待っています" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "ダウンロードに失敗しました" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "ダウンロード履歴" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "画像をダウンロード" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "ダウンロードの進行状況" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "ダウンロード中" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3992,6 +4084,14 @@ msgstr "音声モデルをダウンロードしています..." msgid "Downloading… {0}%" msgstr "ダウンロード中… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "ダウンロード" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "内蔵ブラウザからのダウンロードはこの履歴に保存されます。" + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4054,10 +4154,18 @@ msgstr "目標を編集" msgid "Edit MCP server" msgstr "MCPサーバーを編集" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "パスワードを編集" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "プロフィールを編集" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "保存済みパスワードを編集" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4661,6 +4769,10 @@ msgstr "{0}のファイル" msgid "Files matching these globs are hidden from the @file mention search." msgstr "これらのグロブに一致するファイルは、@file メンション検索から非表示になります。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "ダウンロードしたファイルがここに表示されます" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "必須のワークフロー入力をすべて入力してください。" @@ -4699,6 +4811,11 @@ msgstr "チャット内を検索" msgid "Find in diff" msgstr "差分内を検索" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "ページ内検索" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "ターミナル内を検索" @@ -4922,6 +5039,10 @@ msgstr "頻度" msgid "Friday at 4:00 PM" msgstr "金曜日午後4:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "インポート元" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "システムのぼかし素材(vibrancy)でサイドバーをすりガラス調にし、最近のmacOSの質感を再現します。非対応の環境では半透明の色付けに切り替わります。" @@ -5184,7 +5305,7 @@ msgstr "ハンドル" msgid "Handle" msgstr "ハンドル" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "ハードリロード" @@ -5248,6 +5369,10 @@ msgstr "すべてのプロバイダーのモデル ピッカーから使用し msgid "Hide panel" msgstr "パネルを非表示にする" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "パスワードを隠す" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "ランタイムデバッグパネルを非表示にする" @@ -5271,7 +5396,7 @@ msgstr "{0} のスキルを非表示" msgid "Hide terminal" msgstr "ターミナルを隠す" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "履歴" @@ -5316,10 +5441,18 @@ msgstr "毎時" msgid "HTTP (streamable)" msgstr "HTTP(ストリーミング対応)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "保護されたブラウザ データがスキップされる場合があることを理解しました。" + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5353,6 +5486,10 @@ msgstr "画像" msgid "Image preview" msgstr "画像プレビュー" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "インポート" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "再度インポート" @@ -5361,10 +5498,23 @@ msgstr "再度インポート" msgid "Import and restart" msgstr "インポートして再起動" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "ブラウザ データをインポート" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "インポートの競合" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Cookie をインポート" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Cookie とパスワードをインポート" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5382,6 +5532,10 @@ msgstr "外部エージェントのスキルをインポート" msgid "Import external skills" msgstr "外部スキルをインポート" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "ブラウザからインポート" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Lightcode データをインポート" @@ -5398,10 +5552,18 @@ msgstr "MCPサーバーをインポート" msgid "Import method" msgstr "インポート方法" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "パスワードをインポート" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "選択項目をインポート" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "インポート元" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "{destinationProjectName} にインポート" @@ -5429,6 +5591,17 @@ msgstr "{0} 個のスキルをインポートしました。" msgid "Imported 1 skill." msgstr "1 個のスキルをインポートしました。" +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "{0} からインポート済み" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "インポートしたパスワード:{0}。インポートした Cookie:{1}。" + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "改善" @@ -5661,6 +5834,7 @@ msgstr "どのエージェントやモデルに委任するかを選ぶ際に、 #~ msgid "Interrupt" #~ msgstr "中断" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "中断" @@ -5669,6 +5843,26 @@ msgstr "中断" msgid "Interrupted: agent session ended before completion." msgstr "中断されました: エージェントセッションが完了前に終了しました。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "イタリック体" @@ -5768,6 +5962,14 @@ msgstr "AI が生成するコミットメッセージとプルリクエストの msgid "Language the speech model should expect when transcribing composer dictation." msgstr "コンポーザーでの音声入力を文字起こしする際に音声モデルが想定する言語。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "前回の評価:" @@ -5995,6 +6197,10 @@ msgstr "ローカル" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "ローカルの変更は{branch}からプルする前に隠しておく必要があります" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "ローカル パスワード保護" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6065,6 +6271,10 @@ msgstr "最長の連続日数" msgid "Longest task" msgstr "最長のタスク" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "ブラウザ プロファイルを検索しています…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "開発サーバーを検索しています…" @@ -6081,6 +6291,10 @@ msgstr "サイドバーを半透明にします。Windows 11ではシステム #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "他のプロバイダーからインポートしたスキルを含む、グローバルおよびプロジェクトのエージェントスキルを管理します。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "パスワードを管理" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "リモート環境を管理" @@ -6838,8 +7052,8 @@ msgstr "新しいスケジュール" #~ msgstr "新しいスケジュールタスク" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "新しいタブ" @@ -7082,6 +7296,10 @@ msgstr "{0}に利用できる差分はありません" msgid "No diff for {visiblePath}" msgstr "{visiblePath}の差分はありません" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "検索条件に一致するダウンロードはありません" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "このスコープには外部スキルが見つかりませんでした。" @@ -7105,7 +7323,7 @@ msgstr "GitHub CLI アカウントが見つかりません。 <0>gh auth login{0} from Poracode." +msgstr "Poracode から <0>{0} の保存済みパスワードを削除します。" + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "このデバイスからスケジュールと最新の結果を削除します。" @@ -11737,6 +12095,10 @@ msgstr "ペアリングトークンをコピーできません。" msgid "Unable to copy Tailscale URL." msgstr "Tailscale URL をコピーできませんでした。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "パスワードをコピーできませんでした。" + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "候補のワークツリーを作成できませんでした。" @@ -11750,6 +12112,10 @@ msgstr "プルリクエストを作成できません:{0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "WSL ワークツリー ディレクトリ \"{path}\" を作成できません" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "保存済みパスワードを削除できませんでした。" + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "worktreeを削除できませんでした。" @@ -11758,6 +12124,18 @@ msgstr "worktreeを削除できませんでした。" msgid "Unable to determine the experiment's source branch." msgstr "実験のソースブランチを特定できません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "保存済みパスワードを編集できませんでした。" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "インポートするブラウザ データが見つかりませんでした。" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "ブラウザ データをインポートできませんでした。" + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11772,10 +12150,18 @@ msgstr "{0}をインストールできません。" msgid "Unable to load diff." msgstr "差分を読み込めませんでした。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "ダウンロードを読み込めませんでした。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "リモートアクセスのペアリングを読み込めませんでした。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "保存済みパスワードを読み込めませんでした。" + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "スレッドを読み込めません。" @@ -11875,6 +12261,10 @@ msgstr "実験候補のコミットを解決できません" msgid "Unable to restore the SSH connection." msgstr "SSH 接続を復元できません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "パスワードを表示できませんでした。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "リモートセッションを取り消せませんでした。" @@ -11888,6 +12278,10 @@ msgstr "{0}資格情報を保存できません。" msgid "Unable to save Claude {displayLabel} profile." msgstr "Claudeの{displayLabel}プロファイルを保存できません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "パスワードを保存できませんでした。" + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "{label}を開始できません。" @@ -11959,6 +12353,10 @@ msgstr "リモートアクセスを更新できません。" msgid "Unable to update Tailscale HTTPS." msgstr "Tailscale HTTPS を更新できません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "ダウンロードを更新できませんでした。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "公開URLを更新できません。" @@ -12343,6 +12741,10 @@ msgstr "{conflict}で使用中" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "ユーザー名" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "組み込みのClaudeモデル一覧を使用しています。" @@ -12416,6 +12818,18 @@ msgstr "変更履歴を表示" msgid "View-only — this connection can't manage projects." msgstr "閲覧のみ — この接続ではプロジェクトを管理できません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "ビューポートの高さ" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "ビューポートの倍率" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "ビューポートの幅" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12558,11 +12972,16 @@ msgid "Web server" msgstr "Webサーバー" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "ウェブサイト" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "ウェブサイトのオリジン" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "平日" @@ -12926,10 +13345,16 @@ msgstr "スタッシュされていたローカルの変更が再適用されま msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "ズーム" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "ズームイン" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "ズームアウト" diff --git a/src/renderer/locales/ko/messages.po b/src/renderer/locales/ko/messages.po index e9b553832..220167df2 100644 --- a/src/renderer/locales/ko/messages.po +++ b/src/renderer/locales/ko/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30일" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7일" @@ -827,6 +831,10 @@ msgstr "추가" msgid "Add a local project" msgstr "로컬 프로젝트 추가" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "암호를 추가하거나 다른 브라우저에서 가져오세요." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "모델 추가" msgid "Add one manually or import from another agent." msgstr "직접 추가하거나 다른 에이전트에서 가져오세요." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "암호 추가" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "패턴 추가" @@ -988,6 +1000,10 @@ msgstr "할 일 추가" msgid "Add workspace" msgstr "작업 영역 추가" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "고급" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "사용자 정의 기본 URL을 게시합니다(예: 리버스 프록시 또는 터널). 자동으로 설정하려면 비워 두세요." @@ -1077,6 +1093,7 @@ msgstr "에이전트는 위임된 작업을 작업 태그로 분류합니다. #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "에이전트는 위임된 작업을 작업 태그로 분류합니다. 일치하는 학습된 경로가 먼저 순위에 오르고, 전체 Crossagents 사용량, 즐겨찾기, 일반 에이전트 사용량, 기본 순서가 이어집니다." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "차단됨:" msgid "Bold" msgstr "굵게" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "이 페이지 북마크" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "북마크" @@ -1719,6 +1737,10 @@ msgstr "패널로 되돌리기" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "이 패널로 되돌리거나 실행 중인 창으로 이동하세요." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "설치된 브라우저에서 저장된 암호와 쿠키를 가져옵니다." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "브라우저가 별도의 창에서 열려 있습니다" msgid "Browser MCP enabled for this thread" msgstr "이 스레드에 대해 브라우저 MCP가 활성화되었습니다." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "브라우저 메뉴" @@ -1893,6 +1915,8 @@ msgstr "원격 서버에 연결할 수 없습니다. 온라인 상태인지 확 #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "원격 서버에 연결할 수 없습니다. 온라인 상태인지 확 msgid "Cancel" msgstr "취소" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "다운로드 취소" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "새 Claude 프로필 취소" @@ -1932,6 +1960,7 @@ msgstr "취소됨" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "취소됨" @@ -2128,6 +2157,10 @@ msgstr "상위 폴더 선택" msgid "Choose at least two candidates and enter a prompt." msgstr "후보를 두 개 이상 선택하고 프롬프트를 입력하세요." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "내장 브라우저로 가져올 데이터를 선택하세요." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "클린 작업 트리" msgid "Cleanup script" msgstr "정리 스크립트" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "인터넷 사용 기록 삭제" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "방문 기록 지우기" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "검색 기록 지우기" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "캐시 지우기" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "캐시 지우기" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "쿠키 지우기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "쿠키 지우기" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "목표 지우기" @@ -2361,6 +2406,10 @@ msgstr "Crossagents 닫기" msgid "Close Crossagents panel" msgstr "Crossagents 패널 닫기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "기기 툴바 닫기" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "편집기 닫기" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "완료" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "완료됨" @@ -2967,6 +3017,10 @@ msgstr "에이전트가 클릭하거나 입력하는 동안 페어링된 데스 msgid "Conversation" msgstr "대화" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "쿠키" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "모든 Lightcode 데이터를 Poracode로 복사합니다. Poracode가 msgid "Copy code" msgstr "코드 복사" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "현재 URL 복사" @@ -3045,6 +3099,10 @@ msgstr "페어링 링크 복사" msgid "Copy pairing token" msgstr "페어링 토큰 복사" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "암호 복사" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "경로 복사" @@ -3554,6 +3612,7 @@ msgstr "다른 AI 에이전트에게 작업을 위임합니다." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "영구 삭제" msgid "Delete run" msgstr "실행 삭제" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "저장된 암호 삭제" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "저장된 암호를 삭제할까요?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "일정 삭제" @@ -3775,6 +3842,10 @@ msgstr "데스크톱의 localhost에서 대기 중인 개발 서버입니다." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "개발 전용 재정의. PORACODE DEV 빌드에서만 볼 수 있습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "기기 프리셋" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "컴포저 음성 입력 버튼에 사용되는 장치입니다." msgid "Devices can pair while this desktop is reachable." msgstr "이 데스크톱에 연결할 수 있는 동안 기기를 페어링할 수 있습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "크기:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "직접 연결이 필요합니다" @@ -3964,10 +4039,27 @@ msgstr "완료" msgid "Done · Waiting for your input" msgstr "완료 · 귀하의 의견을 기다리는 중" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "다운로드 실패" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "다운로드 기록" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "이미지 다운로드" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "다운로드 진행률" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "다운로드 중" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "음성 모델 다운로드 중..." msgid "Downloading… {0}%" msgstr "다운로드 중… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "다운로드" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "내장 브라우저에서 다운로드한 항목은 이 기록에 저장됩니다." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "목표 편집" msgid "Edit MCP server" msgstr "MCP 서버 편집" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "암호 편집" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "프로필 편집" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "저장된 암호 편집" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "{0}에 대한 파일" msgid "Files matching these globs are hidden from the @file mention search." msgstr "이러한 glob과 일치하는 파일은 @file 멘션 검색에서 숨겨집니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "다운로드한 파일이 여기에 표시됩니다" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "필수 워크플로 입력을 모두 작성하세요." @@ -4700,6 +4812,11 @@ msgstr "채팅에서 찾기" msgid "Find in diff" msgstr "diff에서 찾기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "페이지에서 찾기" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "터미널에서 찾기" @@ -4923,6 +5040,10 @@ msgstr "빈도" msgid "Friday at 4:00 PM" msgstr "금요일 오후 4:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "출처" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "최신 macOS와 유사하게 시스템 블러 효과(비브런시)로 사이드바에 프로스트 효과를 적용합니다. 지원되지 않는 환경에서는 반투명 색조로 대체됩니다." @@ -5185,7 +5306,7 @@ msgstr "핸들" msgid "Handle" msgstr "핸들" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "강제 새로고침" @@ -5249,6 +5370,10 @@ msgstr "모든 공급자의 모델 선택기에서 사용하지 않는 모델을 msgid "Hide panel" msgstr "패널 숨기기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "암호 숨기기" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "런타임 디버그 패널 숨기기" @@ -5272,7 +5397,7 @@ msgstr "{0}의 스킬 숨기기" msgid "Hide terminal" msgstr "터미널 숨기기" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "기록" @@ -5317,10 +5442,18 @@ msgstr "매시간" msgid "HTTP (streamable)" msgstr "HTTP(스트리밍 가능)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "보호된 브라우저 데이터가 건너뛰어질 수 있음을 이해합니다." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "이미지" msgid "Image preview" msgstr "이미지 미리보기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "가져오기" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "다시 가져오기" @@ -5362,10 +5499,23 @@ msgstr "다시 가져오기" msgid "Import and restart" msgstr "가져오고 다시 시작" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "브라우저 데이터 가져오기" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "가져오기 충돌" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "쿠키 가져오기" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "쿠키 및 암호 가져오기" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "외부 에이전트 스킬 가져오기" msgid "Import external skills" msgstr "외부 스킬 가져오기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "브라우저에서 가져오기" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Lightcode 데이터 가져오기" @@ -5399,10 +5553,18 @@ msgstr "MCP 서버 가져오기" msgid "Import method" msgstr "가져오기 방법" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "암호 가져오기" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "선택 항목 가져오기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "가져오기 원본" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "{destinationProjectName}(으)로 가져오기" @@ -5430,6 +5592,17 @@ msgstr "스킬 {0}개를 가져왔습니다." msgid "Imported 1 skill." msgstr "스킬 1개를 가져왔습니다." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "{0}에서 가져옴" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "가져온 암호: {0}. 가져온 쿠키: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "개선됨" @@ -5662,6 +5835,7 @@ msgstr "에이전트가 어떤 에이전트나 모델에 위임할지 선택할 #~ msgid "Interrupt" #~ msgstr "중단" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "중단됨" @@ -5670,6 +5844,26 @@ msgstr "중단됨" msgid "Interrupted: agent session ended before completion." msgstr "중단됨: 에이전트 세션이 완료되기 전에 종료되었습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "이탤릭체" @@ -5769,6 +5963,14 @@ msgstr "AI가 생성하는 커밋 메시지와 풀 요청 요약에 사용할 msgid "Language the speech model should expect when transcribing composer dictation." msgstr "작성기 받아쓰기를 기록할 때 음성 모델이 예상해야 하는 언어입니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "마지막 평가:" @@ -5996,6 +6198,10 @@ msgstr "로컬" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "{branch}에서 가져오기 전에 로컬 변경 사항을 숨겨야 합니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "로컬 암호 보호" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "최장 연속 기록" msgid "Longest task" msgstr "최장 작업" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "브라우저 프로필을 찾는 중…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "개발 서버를 찾는 중…" @@ -6082,6 +6292,10 @@ msgstr "사이드바를 반투명하게 만듭니다 — Windows 11에서는 시 #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "다른 제공자에서 가져온 스킬을 포함하여 전역 및 프로젝트 에이전트 스킬을 관리합니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "암호 관리" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "원격 환경 관리" @@ -6840,8 +7054,8 @@ msgstr "새 일정" #~ msgstr "새 예약 작업" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "새 탭" @@ -7084,6 +7298,10 @@ msgstr "{0}에 대한 차이점이 없습니다." msgid "No diff for {visiblePath}" msgstr "{visiblePath}에는 차이가 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "검색과 일치하는 다운로드가 없습니다" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "이 범위에서 외부 스킬을 찾지 못했습니다." @@ -7107,7 +7325,7 @@ msgstr "GitHub CLI 계정을 찾을 수 없습니다. <0>gh auth login으로 #~ msgid "No goal for this thread." #~ msgstr "이 스레드에는 목표가 없습니다." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "아직 기록이 없습니다" @@ -7202,6 +7420,10 @@ msgstr "다른 에이전트가 설치되어 있지 않습니다." msgid "No paired devices yet." msgstr "아직 페어링된 기기가 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "검색과 일치하는 암호가 없습니다" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "패턴이 없습니다." @@ -7275,6 +7497,10 @@ msgstr "아직 실행이 없습니다." msgid "No runtime items yet for this thread." msgstr "이 스레드에는 아직 런타임 항목이 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "저장된 암호가 없습니다" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "아직 연결된 서버가 없습니다." @@ -7316,6 +7542,10 @@ msgstr "단계적 변경 없음" msgid "No supported agents detected" msgstr "지원되는 에이전트가 감지되지 않았습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "이 기기에서 지원되는 브라우저 프로필을 찾지 못했습니다." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "태그 없음 — 이 항목은 더 이상 태그 친화도에 영향을 주지 않습니다." @@ -7601,6 +7831,10 @@ msgstr "Crossagent 열기: {0}" msgid "Open dialog" msgstr "대화상자 열기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "다운로드한 파일 열기" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "실험 열기" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "암호 문구(선택 사항)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "암호" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "암호를 복사했습니다." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "암호 관리자" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "암호 관리자 섹션" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "암호" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "암호 및 자동 완성" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "암호는 Poracode의 로컬 자격 증명 저장소에 암호화됩니다. 암호 값은 표시하거나 복사하거나 편집할 때만 요청됩니다." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "일시 중지" #~ msgid "Pause {0}" #~ msgstr "{0} 일시 중지" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "다운로드 일시 중지" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "목표 일시 중지" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "일시중지됨" @@ -8132,6 +8398,10 @@ msgstr "고정됨" msgid "Pinned task routes" msgstr "고정된 작업 경로" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "이전 실행" msgid "Previous tab" msgstr "이전 탭" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "인쇄" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "개인정보 처리방침" @@ -8476,6 +8750,10 @@ msgstr "프롬프트" msgid "Proposed plan" msgstr "제안된 계획" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "보호된 브라우저 데이터" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "지원되는 에이전트를 위해 Poracode에서 제공하고 관리합니다." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "작업 제거" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "북마크 삭제" @@ -9011,6 +9290,10 @@ msgstr "Cursor SDK API 키 제거" msgid "Remove environment variable" msgstr "환경 변수 제거" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "다운로드 기록에서 제거" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "즐겨찾기에서 제거" @@ -9204,6 +9487,10 @@ msgstr "사이드바 프로스트를 기본값으로 재설정" msgid "Reset to default" msgstr "기본값으로 재설정" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "확대/축소 재설정" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "브라우저 서랍 크기 조정" @@ -9246,11 +9533,27 @@ msgstr "분할 크기 조정" msgid "Resize terminal panel" msgstr "터미널 패널 크기 조정" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "뷰포트 크기 조절" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "뷰포트 높이 조절" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "뷰포트 너비 조절" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "리베이스 또는 병합 중 병합 충돌을 해결합니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "반응형" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Poracode 다시 시작" @@ -9288,6 +9591,10 @@ msgstr "재개" #~ msgid "Resume {label}" #~ msgstr "{label} 재개" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "다운로드 재개" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "목표 재개" @@ -9340,6 +9647,10 @@ msgstr "파일 탐색기에 표시" msgid "Reveal in file tree" msgstr "파일 트리에 표시" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "암호 표시" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "오른쪽 패널" msgid "Right-click to switch ring" msgstr "마우스 오른쪽 버튼을 클릭해 링 전환" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "뷰포트 회전" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "런타임 디버그" msgid "Runtime options" msgstr "런타임 옵션" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "에이전트 검색..." msgid "Search branches..." msgstr "브랜치 검색..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "다운로드 검색" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "모델 검색..." msgid "Search or enter address" msgstr "주소 검색 또는 입력" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "암호 검색" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "풀 리퀘스트 검색" @@ -10029,7 +10357,7 @@ msgstr "이 세션 시작 시 확정됨 — 서버를 변경하려면 새 스레 #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "OS 수준 에이전트 세션에 대해 프로젝트 없는 홈 범위 msgid "Show all" msgstr "모두 표시" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "북마크 막대 표시" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "북마크바 표시" @@ -10105,6 +10433,10 @@ msgstr "작성기 표시" msgid "Show context usage details" msgstr "컨텍스트 사용 세부정보 표시" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "기기 툴바 표시" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "예상 비용 표시" msgid "Show in file explorer" msgstr "파일 탐색기에 표시" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "폴더에서 보기" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "솔루션 {0}" #~ msgid "Solution {ordinal}" #~ msgstr "솔루션 {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "일부 브라우저 데이터를 건너뛰었습니다. 건너뛴 보호된 항목: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "일부 검사가 실패했거나 보류 중입니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "일부 Chromium 브라우저는 App-Bound Encryption으로 쿠키와 암호를 보호합니다. Poracode는 이 보호를 우회하지 않습니다. 운영 체제에서 제공하지 않는 데이터는 건너뜁니다." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "지원:" msgid "Supports Fast mode" msgstr "고속 모드 지원" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "메모와 할 일 바꾸기" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "Tailscale URL" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "스크린샷 찍기" @@ -11392,6 +11745,11 @@ msgstr "이 프로젝트의 원격 서버가 오프라인입니다. 스레드를 msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "이렇게 하면 체크포인트 스냅샷을 사용할 수 있을 때 이후 메시지가 제거되고 파일이 복원됩니다." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Poracode에서 <0>{0}의 저장된 암호를 삭제합니다." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "이 기기에서 일정과 최신 결과를 삭제합니다." @@ -11739,6 +12097,10 @@ msgstr "페어링 토큰을 복사할 수 없습니다." msgid "Unable to copy Tailscale URL." msgstr "Tailscale URL을 복사할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "암호를 복사할 수 없습니다." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "후보 워크트리를 만들 수 없습니다." @@ -11752,6 +12114,10 @@ msgstr "풀 리퀘스트를 생성할 수 없습니다: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "WSL 작업 트리 디렉터리 \"{path}\"을(를) 생성할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "저장된 암호를 삭제할 수 없습니다." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "worktree를 삭제할 수 없습니다." @@ -11760,6 +12126,18 @@ msgstr "worktree를 삭제할 수 없습니다." msgid "Unable to determine the experiment's source branch." msgstr "실험의 소스 브랜치를 확인할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "저장된 암호를 편집할 수 없습니다." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "가져올 브라우저 데이터를 찾을 수 없습니다." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "브라우저 데이터를 가져올 수 없습니다." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "{0}을(를) 설치할 수 없습니다." msgid "Unable to load diff." msgstr "diff를 불러올 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "다운로드를 불러올 수 없습니다." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "원격 액세스 페어링을 로드할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "저장된 암호를 불러올 수 없습니다." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "스레드를 불러올 수 없습니다." @@ -11877,6 +12263,10 @@ msgstr "실험 후보 커밋을 확인할 수 없습니다" msgid "Unable to restore the SSH connection." msgstr "SSH 연결을 복원할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "암호를 표시할 수 없습니다." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "원격 세션을 해지할 수 없습니다." @@ -11890,6 +12280,10 @@ msgstr "{0} 자격 증명을 저장할 수 없습니다." msgid "Unable to save Claude {displayLabel} profile." msgstr "Claude {displayLabel} 프로필을 저장할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "암호를 저장할 수 없습니다." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "{label}을(를) 시작할 수 없습니다." @@ -11961,6 +12355,10 @@ msgstr "원격 액세스를 업데이트할 수 없습니다." msgid "Unable to update Tailscale HTTPS." msgstr "Tailscale HTTPS를 업데이트할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "다운로드를 업데이트할 수 없습니다." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "공개 URL을 업데이트할 수 없습니다." @@ -12345,6 +12743,10 @@ msgstr "{conflict}에서 사용 중" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "사용자 이름" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "내장 Claude 모델 목록을 사용합니다." @@ -12418,6 +12820,18 @@ msgstr "변경 내역 보기" msgid "View-only — this connection can't manage projects." msgstr "읽기 전용 — 이 연결로는 프로젝트를 관리할 수 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "뷰포트 높이" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "뷰포트 배율" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "뷰포트 너비" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "웹 서버" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "웹사이트" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "웹사이트 출처" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "평일" @@ -12928,10 +13347,16 @@ msgstr "스태시에 저장된 로컬 변경 사항이 다시 적용되었습니 msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "확대/축소" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "확대" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "축소" diff --git a/src/renderer/locales/pl/messages.po b/src/renderer/locales/pl/messages.po index cde7d9f55..85369a26b 100644 --- a/src/renderer/locales/pl/messages.po +++ b/src/renderer/locales/pl/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30d" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7d" @@ -827,6 +831,10 @@ msgstr "Dodaj" msgid "Add a local project" msgstr "Dodaj projekt lokalny" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Dodaj hasło lub zaimportuj je z innej przeglądarki." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Dodaj model" msgid "Add one manually or import from another agent." msgstr "Dodaj ręcznie lub zaimportuj z innego agenta." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Dodaj hasło" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Dodaj wzór" @@ -988,6 +1000,10 @@ msgstr "Dodaj zadanie" msgid "Add workspace" msgstr "Dodaj obszar roboczy" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Zaawansowane" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Ustaw niestandardowy bazowy URL, np. reverse proxy lub tunel. Pozostaw puste, aby ustawić automatycznie." @@ -1077,6 +1093,7 @@ msgstr "Agenci klasyfikują delegowaną pracę za pomocą tagów zadań. Ręczne #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Agenci klasyfikują delegowaną pracę za pomocą tagów zadań. Pasujące wyuczone trasy są klasyfikowane jako pierwsze, a następnie globalne użycie Crossagents, ulubione, zwykłe użycie agentów i kolejność wbudowana." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "zablokowane:" msgid "Bold" msgstr "Pogrubienie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Dodaj tę stronę do zakładek" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Zakładki" @@ -1719,6 +1737,10 @@ msgstr "Przywróć do panelu" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Przywróć ją do tego panelu lub przejdź do okna, w którym działa." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Przenieś zapisane hasła i pliki cookie z zainstalowanej przeglądarki." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Przeglądarka jest otwarta w osobnym oknie" msgid "Browser MCP enabled for this thread" msgstr "W przeglądarce włączono MCP dla tego wątku" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Menu przeglądarki" @@ -1893,6 +1915,8 @@ msgstr "Nie można połączyć się z serwerem zdalnym. Sprawdź, czy jest onlin #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Nie można połączyć się z serwerem zdalnym. Sprawdź, czy jest onlin msgid "Cancel" msgstr "Anuluj" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Anuluj pobieranie" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Anuluj nowy profil Claude" @@ -1932,6 +1960,7 @@ msgstr "anulowane" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Anulowano" @@ -2128,6 +2157,10 @@ msgstr "Wybierz folder nadrzędny" msgid "Choose at least two candidates and enter a prompt." msgstr "Wybierz co najmniej dwóch kandydatów i wpisz prompt." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Wybierz dane do przeniesienia do wbudowanej przeglądarki." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Czyste drzewo robocze" msgid "Cleanup script" msgstr "Skrypt czyszczący" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Wyczyść dane przeglądania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Wyczyść historię przeglądania" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Wyczyść historię przeglądania" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Wyczyść pamięć podręczną" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Wyczyść pamięć podręczną" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Wyczyść pliki cookie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Wyczyść pliki cookie" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Wyczyść cel" @@ -2361,6 +2406,10 @@ msgstr "Zamknij Crossagents" msgid "Close Crossagents panel" msgstr "Zamknij panel Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Zamknij pasek urządzeń" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Zamknij edytor" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "ukończone" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Ukończono" @@ -2967,6 +3017,10 @@ msgstr "Steruje sparowanym pulpitem, gdy agent klika lub pisze" msgid "Conversation" msgstr "Rozmowa" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Pliki cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Skopiuj wszystkie dane Lightcode do Poracode. Poracode uruchomi się pon msgid "Copy code" msgstr "Skopiuj kod" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Skopiuj bieżący adres URL" @@ -3045,6 +3099,10 @@ msgstr "Skopiuj link parowania" msgid "Copy pairing token" msgstr "Kopiuj token parowania" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Kopiuj hasło" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Skopiuj ścieżkę" @@ -3554,6 +3612,7 @@ msgstr "Deleguj zadania innym agentom AI." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Usuń trwale" msgid "Delete run" msgstr "Usuń uruchomienie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Usuń zapisane hasło" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Usunąć zapisane hasło?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Usuń harmonogram" @@ -3775,6 +3842,10 @@ msgstr "Serwery deweloperskie nasłuchujące na localhost Twojego komputera." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Zastąpienia tylko w fazie rozwojowej. Widoczne tylko w kompilacji PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Profil urządzenia" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Urządzenie używane przez przycisk wprowadzania głosowego kompozytora. msgid "Devices can pair while this desktop is reachable." msgstr "Urządzenia mogą się parować, gdy ten komputer jest osiągalny." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Wymiary:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Wymagane jest bezpośrednie połączenie" @@ -3964,10 +4039,27 @@ msgstr "Gotowe" msgid "Done · Waiting for your input" msgstr "Gotowe · Oczekiwanie na Twoją odpowiedź" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Pobieranie nie powiodło się" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Historia pobierania" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Pobierz obraz" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Postęp pobierania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Pobieranie" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Pobieranie modelu głosu..." msgid "Downloading… {0}%" msgstr "Pobieranie… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Pobrane pliki" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Pliki pobrane we wbudowanej przeglądarce są zapisywane w tej historii." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Edytuj cel" msgid "Edit MCP server" msgstr "Edytuj serwer MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Edytuj hasło" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Edytuj profil" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Edytuj zapisane hasło" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Pliki dla {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Pliki pasujące do tych globów są ukryte przed wyszukiwaniem wzmianek @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Tutaj pojawią się pobrane pliki" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Uzupełnij wszystkie wymagane dane wejściowe workflow." @@ -4700,6 +4812,11 @@ msgstr "Znajdź w czacie" msgid "Find in diff" msgstr "Znajdź w różnicach" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Znajdź na stronie" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Znajdź w terminalu" @@ -4923,6 +5040,10 @@ msgstr "Częstotliwość" msgid "Friday at 4:00 PM" msgstr "Piątek o 16:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Z" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Zmatowienie paska bocznego systemowym efektem rozmycia (vibrancy), nawiązujące do nowszych wersji macOS. Tam, gdzie nie jest obsługiwane, używa półprzezroczystego odcienia." @@ -5185,7 +5306,7 @@ msgstr "identyfikator" msgid "Handle" msgstr "Identyfikator" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Twarde przeładowanie" @@ -5249,6 +5370,10 @@ msgstr "Ukryj modele, których nie używasz, w selektorze modeli u każdego dost msgid "Hide panel" msgstr "Ukryj panel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Ukryj hasło" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Ukryj panel debugowania środowiska wykonawczego" @@ -5272,7 +5397,7 @@ msgstr "Ukryj umiejętności z {0}" msgid "Hide terminal" msgstr "Ukryj terminal" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Historia" @@ -5317,10 +5442,18 @@ msgstr "Co godzinę" msgid "HTTP (streamable)" msgstr "HTTP (strumieniowy)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Rozumiem, że chronione dane przeglądarki mogą zostać pominięte." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Obraz" msgid "Image preview" msgstr "Podgląd obrazu" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Importuj" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Importuj ponownie" @@ -5362,10 +5499,23 @@ msgstr "Importuj ponownie" msgid "Import and restart" msgstr "Importuj i uruchom ponownie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Importuj dane przeglądarki" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Konflikt importu" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Importuj pliki cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Importuj pliki cookie i hasła" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Importuj umiejętności zewnętrznych agentów" msgid "Import external skills" msgstr "Importuj zewnętrzne umiejętności" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Importuj ze swojej przeglądarki" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Importuj dane Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Importuj serwery MCP" msgid "Import method" msgstr "Metoda importu" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Importuj hasła" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Importuj wybrane" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Źródło importu" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Importuj do {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "Zaimportowano umiejętności: {0}." msgid "Imported 1 skill." msgstr "Zaimportowano 1 umiejętność." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Zaimportowano z {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Liczba zaimportowanych haseł: {0}. Liczba zaimportowanych plików cookie: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Ulepszono" @@ -5662,6 +5835,7 @@ msgstr "Instrukcje, którymi kierują się agenci przy wyborze agenta lub modelu #~ msgid "Interrupt" #~ msgstr "Przerwij" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Przerwano" @@ -5670,6 +5844,26 @@ msgstr "Przerwano" msgid "Interrupted: agent session ended before completion." msgstr "Przerwano: sesja agenta zakończyła się przed jej ukończeniem." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Kursywa" @@ -5769,6 +5963,14 @@ msgstr "Język komunikatów zatwierdzeń i podsumowań żądań ściągnięcia g msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Język, jakiego powinien spodziewać się model mowy podczas transkrypcji dyktowania w polu wprowadzania." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Ostatnia ocena:" @@ -5996,6 +6198,10 @@ msgstr "Lokalny" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Lokalne zmiany muszą zostać schowane przed pobraniem z {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Lokalna ochrona haseł" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Najdłuższa seria" msgid "Longest task" msgstr "Najdłuższe zadanie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Wyszukiwanie profili przeglądarek…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Szukanie serwerów deweloperskich…" @@ -6082,6 +6292,10 @@ msgstr "Uczyń pasek boczny półprzezroczystym — systemowy efekt rozmycia w W #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Zarządzaj globalnymi umiejętnościami agentów i umiejętnościami projektu, w tym zaimportowanymi od innych dostawców." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Zarządzaj hasłami" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Zarządzaj środowiskami zdalnymi" @@ -6840,8 +7054,8 @@ msgstr "Nowy harmonogram" #~ msgstr "Nowe zaplanowane zadanie" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Nowa zakładka" @@ -7084,6 +7298,10 @@ msgstr "Brak dostępnych różnic dla {0}" msgid "No diff for {visiblePath}" msgstr "Brak różnicy dla {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Brak pobrań pasujących do wyszukiwania" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "Nie znaleziono zewnętrznych umiejętności w tym zakresie." @@ -7107,7 +7325,7 @@ msgstr "Nie znaleziono kont GitHub CLI. Zaloguj się za pomocą <0>gh auth login #~ msgid "No goal for this thread." #~ msgstr "Brak celu dla tego wątku." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Brak historii" @@ -7202,6 +7420,10 @@ msgstr "Nie zainstalowano żadnych innych agentów" msgid "No paired devices yet." msgstr "Brak sparowanych urządzeń." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Brak haseł pasujących do wyszukiwania" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Żadnych wzorów." @@ -7275,6 +7497,10 @@ msgstr "Brak uruchomień." msgid "No runtime items yet for this thread." msgstr "Brak jeszcze elementów wykonawczych dla tego wątku." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "Brak zapisanych haseł" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Brak połączonych serwerów." @@ -7316,6 +7542,10 @@ msgstr "Żadnych zmian etapowych" msgid "No supported agents detected" msgstr "Nie wykryto obsługiwanych agentów" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "Na tym urządzeniu nie znaleziono profili obsługiwanych przeglądarek." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Brak tagów — wpis przestaje wpływać na powinowactwo tagów." @@ -7601,6 +7831,10 @@ msgstr "Otwórz Crossagenta: {0}" msgid "Open dialog" msgstr "Otwórz okno dialogowe" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Otwórz pobrany plik" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Otwórz eksperyment" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Fraza hasła (opcjonalnie)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Hasło" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Skopiowano hasło." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Menedżer haseł" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Sekcja menedżera haseł" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Hasła" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Hasła i autouzupełnianie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Hasła są szyfrowane w lokalnym magazynie poświadczeń Poracode. Wartości haseł są pobierane tylko wtedy, gdy je wyświetlasz, kopiujesz lub edytujesz." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Wstrzymaj" #~ msgid "Pause {0}" #~ msgstr "Wstrzymaj {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Wstrzymaj pobieranie" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Wstrzymaj cel" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Wstrzymano" @@ -8132,6 +8398,10 @@ msgstr "Przypięty" msgid "Pinned task routes" msgstr "Przypięte trasy zadań" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Poprzednie uruchomienia" msgid "Previous tab" msgstr "Poprzednia karta" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Drukuj" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Polityka prywatności" @@ -8476,6 +8750,10 @@ msgstr "Prompty" msgid "Proposed plan" msgstr "Proponowany plan" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Chronione dane przeglądarki" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Udostępniany i zarządzany przez Poracode na potrzeby obsługiwanych agentów." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Usuń akcję" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Usuń zakładkę" @@ -9011,6 +9290,10 @@ msgstr "Usuń klucz API SDK Cursor" msgid "Remove environment variable" msgstr "Usuń zmienną środowiskową" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Usuń z historii pobierania" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Usuń z ulubionych" @@ -9204,6 +9487,10 @@ msgstr "Przywróć domyślne zmatowienie paska bocznego" msgid "Reset to default" msgstr "Przywróć domyślne" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Zresetuj powiększenie" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Zmień rozmiar szuflady przeglądarki" @@ -9246,11 +9533,27 @@ msgstr "Zmień rozmiar podziału" msgid "Resize terminal panel" msgstr "Zmień rozmiar panelu terminali" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Zmień rozmiar obszaru wyświetlania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Zmień wysokość obszaru wyświetlania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Zmień szerokość obszaru wyświetlania" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Rozwiązuje konflikty scalania podczas zmiany bazy lub scalania." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Responsywny" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Uruchom Poracode ponownie" @@ -9288,6 +9591,10 @@ msgstr "Wznów" #~ msgid "Resume {label}" #~ msgstr "Wznów {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Wznów pobieranie" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Wznów cel" @@ -9340,6 +9647,10 @@ msgstr "Pokaż w Eksploratorze plików" msgid "Reveal in file tree" msgstr "Pokaż w drzewie plików" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Pokaż hasło" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Prawy panel" msgid "Right-click to switch ring" msgstr "Kliknij prawym przyciskiem, aby przełączyć pierścień" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Obróć obszar wyświetlania" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Debugowanie w czasie wykonywania" msgid "Runtime options" msgstr "Opcje środowiska wykonawczego" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Wyszukaj agentów..." msgid "Search branches..." msgstr "Szukaj gałęzi..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Szukaj w pobranych plikach" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Wyszukaj modele..." msgid "Search or enter address" msgstr "Wyszukaj lub wpisz adres" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Szukaj haseł" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Szukaj żądań ściągnięcia" @@ -10029,7 +10357,7 @@ msgstr "Ustalone przy starcie tej sesji — aby zmienić serwery, rozpocznij now #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "Wyświetl bezprojektowy zakres główny dla sesji agenta na poziomie sys msgid "Show all" msgstr "Pokaż wszystko" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Pokaż pasek zakładek" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Pokaż pasek zakładek" @@ -10105,6 +10433,10 @@ msgstr "Pokaż pole tworzenia wiadomości" msgid "Show context usage details" msgstr "Pokaż szczegóły użycia kontekstu" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Pokaż pasek urządzeń" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Pokaż szacunkowy koszt" msgid "Show in file explorer" msgstr "Pokaż w eksploratorze plików" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Pokaż w folderze" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Rozwiązanie {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Rozwiązanie {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Pominięto część danych przeglądarki. Liczba pominiętych chronionych elementów: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Niektóre kontrole kończą się niepowodzeniem lub oczekują na realizację." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Niektóre przeglądarki Chromium chronią pliki cookie i hasła za pomocą mechanizmu App-Bound Encryption. Poracode nie omija tej ochrony. Dane, których system operacyjny nie udostępnia, zostaną pominięte." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Wsparcie:" msgid "Supports Fast mode" msgstr "Obsługuje tryb szybki" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Zamień notatki i zadania" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "URL Tailscale" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Zrób zrzut ekranu" @@ -11392,6 +11745,11 @@ msgstr "Serwer zdalny tego projektu jest offline. Połącz ponownie, aby rozpocz msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Spowoduje to usunięcie późniejszych wiadomości i przywrócenie plików, gdy dostępna będzie migawka punktu kontrolnego." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Spowoduje to usunięcie zapisanego hasła dla <0>{0} z Poracode." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Usuwa harmonogram i jego ostatni wynik z tego urządzenia." @@ -11739,6 +12097,10 @@ msgstr "Nie można skopiować tokenu parowania." msgid "Unable to copy Tailscale URL." msgstr "Nie można skopiować URL Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Nie udało się skopiować hasła." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Nie udało się utworzyć drzewa roboczego kandydata." @@ -11752,6 +12114,10 @@ msgstr "Nie udało się utworzyć pull requesta: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Nie można utworzyć katalogu drzewa roboczego WSL „{path}”" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Nie udało się usunąć zapisanego hasła." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Nie można usunąć worktree." @@ -11760,6 +12126,18 @@ msgstr "Nie można usunąć worktree." msgid "Unable to determine the experiment's source branch." msgstr "Nie udało się określić gałęzi źródłowej eksperymentu." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Nie udało się edytować zapisanego hasła." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Nie udało się znaleźć danych przeglądarki do zaimportowania." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Nie udało się zaimportować danych przeglądarki." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "Nie można zainstalować {0}." msgid "Unable to load diff." msgstr "Nie udało się wczytać różnic." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Nie udało się wczytać pobranych plików." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Nie można załadować parowania dostępu zdalnego." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Nie udało się wczytać zapisanych haseł." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Nie można załadować wątku." @@ -11877,6 +12263,10 @@ msgstr "Nie można ustalić commita kandydata eksperymentu" msgid "Unable to restore the SSH connection." msgstr "Nie można przywrócić połączenia SSH." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Nie udało się wyświetlić hasła." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Nie można unieważnić sesji zdalnej." @@ -11890,6 +12280,10 @@ msgstr "Nie można zapisać danych uwierzytelniających {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Nie można zapisać profilu Claude {displayLabel}." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Nie udało się zapisać hasła." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Nie można uruchomić {label}." @@ -11961,6 +12355,10 @@ msgstr "Nie można zaktualizować dostępu zdalnego." msgid "Unable to update Tailscale HTTPS." msgstr "Nie można zaktualizować Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Nie udało się zaktualizować pobierania." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Nie można zaktualizować publicznego adresu URL." @@ -12345,6 +12743,10 @@ msgstr "Używany przez {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Nazwa użytkownika" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Używana jest wbudowana lista modeli Claude." @@ -12418,6 +12820,18 @@ msgstr "Zobacz dziennik zmian" msgid "View-only — this connection can't manage projects." msgstr "Tylko do odczytu — to połączenie nie może zarządzać projektami." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Wysokość obszaru wyświetlania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Skala obszaru wyświetlania" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Szerokość obszaru wyświetlania" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Serwer WWW" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Strona internetowa" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Adres witryny" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Dni robocze" @@ -12928,10 +13347,16 @@ msgstr "Twoje lokalne zmiany ze skrytki zostały ponownie zastosowane." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Powiększenie" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Powiększ" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Pomniejsz" diff --git a/src/renderer/locales/pt-BR/messages.po b/src/renderer/locales/pt-BR/messages.po index 80f7bc616..1645d0d30 100644 --- a/src/renderer/locales/pt-BR/messages.po +++ b/src/renderer/locales/pt-BR/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30d" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7d" @@ -827,6 +831,10 @@ msgstr "Adicionar" msgid "Add a local project" msgstr "Adicione um projeto local" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Adicione uma senha ou importe uma de outro navegador." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Adicionar modelo" msgid "Add one manually or import from another agent." msgstr "Adicione um manualmente ou importe de outro agente." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Adicionar senha" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Adicionar padrão" @@ -988,6 +1000,10 @@ msgstr "Adicionar tarefa" msgid "Add workspace" msgstr "Adicionar espaço de trabalho" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Avançado" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Anuncie uma URL base personalizada, por exemplo, um proxy reverso ou túnel. Deixe em branco para automático." @@ -1077,6 +1093,7 @@ msgstr "Os agentes classificam o trabalho delegado com tags de tarefa. As rotas #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Os agentes classificam o trabalho delegado com tags de tarefa. As rotas aprendidas correspondentes ficam em primeiro lugar, seguidas pelo uso global do Crossagents, favoritos, uso normal de agentes e ordem integrada." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "bloqueado:" msgid "Bold" msgstr "Negrito" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Adicionar esta página aos favoritos" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Favoritos" @@ -1719,6 +1737,10 @@ msgstr "Trazer de volta ao painel" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Traga-o de volta para este painel ou vá para a janela em que ele está sendo executado." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Importe senhas e cookies salvos de um navegador instalado." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "O navegador está aberto em uma janela separada" msgid "Browser MCP enabled for this thread" msgstr "MCP do navegador ativado para este tópico" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Menu do navegador" @@ -1893,6 +1915,8 @@ msgstr "Não foi possível acessar o servidor remoto. Verifique se ele está onl #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Não foi possível acessar o servidor remoto. Verifique se ele está onl msgid "Cancel" msgstr "Cancelar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Cancelar download" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Cancelar novo perfil de Claude" @@ -1932,6 +1960,7 @@ msgstr "cancelado" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Cancelado" @@ -2128,6 +2157,10 @@ msgstr "Escolha uma pasta pai" msgid "Choose at least two candidates and enter a prompt." msgstr "Escolha pelo menos dois candidatos e insira um prompt." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Escolha os dados que deseja transferir para o navegador integrado." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Árvore de trabalho limpa" msgid "Cleanup script" msgstr "Script de limpeza" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Limpar dados de navegação" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Limpar histórico de navegação" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Limpar histórico de navegação" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" -msgstr "Limpar Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" +msgstr "Limpar cache" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Limpar Cache" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Limpar cookies" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Limpar cookies" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Limpar objetivo" @@ -2361,6 +2406,10 @@ msgstr "Fechar Crossagents" msgid "Close Crossagents panel" msgstr "Fechar o painel de Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Fechar barra de ferramentas do dispositivo" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Fechar editor" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "concluído" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Concluído" @@ -2967,6 +3017,10 @@ msgstr "Controla a área de trabalho emparelhada enquanto o agente clica ou digi msgid "Conversation" msgstr "Conversa" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookies" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Copie todos os dados do Lightcode para o Poracode. O Poracode será rein msgid "Copy code" msgstr "Copiar código" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Copiar URL atual" @@ -3045,6 +3099,10 @@ msgstr "Copiar link de pareamento" msgid "Copy pairing token" msgstr "Copiar token de pareamento" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Copiar senha" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Copiar caminho" @@ -3554,6 +3612,7 @@ msgstr "Delegue o trabalho a outros agentes de IA." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Excluir permanentemente" msgid "Delete run" msgstr "Excluir execução" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Excluir senha salva" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Excluir senha salva?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Excluir agendamento" @@ -3775,6 +3842,10 @@ msgstr "Servidores de desenvolvimento escutando no localhost do seu desktop." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Substituições apenas de desenvolvimento. Visível apenas na compilação PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Predefinição de dispositivo" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Dispositivo usado pelo botão de entrada de voz do compositor." msgid "Devices can pair while this desktop is reachable." msgstr "Dispositivos podem parear enquanto este desktop estiver acessível." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Dimensões:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Conexão direta necessária" @@ -3964,10 +4039,27 @@ msgstr "Concluído" msgid "Done · Waiting for your input" msgstr "Concluído · Aguardando sua resposta" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Falha no download" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Histórico de downloads" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Baixar imagem" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Progresso do download" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Baixando" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Baixando modelo de voz..." msgid "Downloading… {0}%" msgstr "Baixando… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Downloads" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Os downloads do navegador integrado são salvos neste histórico." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Editar objetivo" msgid "Edit MCP server" msgstr "Editar servidor MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Editar senha" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Editar perfil" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Editar senha salva" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Arquivos para {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Os arquivos que correspondem a esses globs ficam ocultos na pesquisa de menção @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Os arquivos que você baixar aparecerão aqui" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Preencha todas as entradas obrigatórias do workflow." @@ -4700,6 +4812,11 @@ msgstr "Localizar no chat" msgid "Find in diff" msgstr "Localizar na comparação" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Localizar na página" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Localizar no terminal" @@ -4923,6 +5040,10 @@ msgstr "Frequência" msgid "Friday at 4:00 PM" msgstr "Sexta-feira às 16:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "De" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Aplica o efeito fosco à barra lateral com o material de desfoque do sistema (vibrância), à semelhança do macOS recente. Recorre a um tom translúcido onde não houver suporte." @@ -5185,7 +5306,7 @@ msgstr "identificador" msgid "Handle" msgstr "Identificador" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Recarregar forçado" @@ -5249,6 +5370,10 @@ msgstr "Oculte os modelos que você não usa no seletor de modelos de todos os p msgid "Hide panel" msgstr "Ocultar painel" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Ocultar senha" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Ocultar painel de depuração de tempo de execução" @@ -5272,7 +5397,7 @@ msgstr "Ocultar skills de {0}" msgid "Hide terminal" msgstr "Ocultar terminal" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Histórico" @@ -5317,10 +5442,18 @@ msgstr "A cada hora" msgid "HTTP (streamable)" msgstr "HTTP (com streaming)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Entendo que dados protegidos do navegador podem ser ignorados." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Imagem" msgid "Image preview" msgstr "Visualização da imagem" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Importar" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Importar novamente" @@ -5362,10 +5499,23 @@ msgstr "Importar novamente" msgid "Import and restart" msgstr "Importar e reiniciar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Importar dados do navegador" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Conflito de importação" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Importar cookies" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Importar cookies e senhas" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Importar skills de agentes externos" msgid "Import external skills" msgstr "Importar skills externas" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Importar do seu navegador" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Importar dados do Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Importar servidores MCP" msgid "Import method" msgstr "Método de importação" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Importar senhas" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Importar selecionadas" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Origem da importação" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Importar para {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "{0} skills importadas." msgid "Imported 1 skill." msgstr "1 skill importada." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Importado de {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Senhas importadas: {0}. Cookies importados: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Melhorado" @@ -5662,6 +5835,7 @@ msgstr "Instruções que os agentes seguem ao escolher para qual agente ou model #~ msgid "Interrupt" #~ msgstr "Interromper" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Interrompida" @@ -5670,6 +5844,26 @@ msgstr "Interrompida" msgid "Interrupted: agent session ended before completion." msgstr "Interrompida: a sessão do agente terminou antes da conclusão." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Itálico" @@ -5769,6 +5963,14 @@ msgstr "Idioma das mensagens de commit e dos resumos de pull request gerados por msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Idioma que o modelo de fala deve esperar ao transcrever o ditado do compositor." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Última avaliação:" @@ -5996,6 +6198,10 @@ msgstr "Locais" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "As alterações locais precisam ser armazenadas (stash) antes de fazer pull de {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Proteção local de senhas" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Maior sequência" msgid "Longest task" msgstr "Tarefa mais longa" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Procurando perfis de navegador…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Procurando servidores de desenvolvimento…" @@ -6082,6 +6292,10 @@ msgstr "Torna a barra lateral translúcida — o material de desfoque do sistema #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Gerencie skills globais e de projeto dos agentes, inclusive skills importadas de outros provedores." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Gerenciar senhas" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Gerenciar ambientes remotos" @@ -6840,8 +7054,8 @@ msgstr "Novo agendamento" #~ msgstr "Nova tarefa agendada" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Nova guia" @@ -7084,6 +7298,10 @@ msgstr "Nenhuma diferença disponível para {0}" msgid "No diff for {visiblePath}" msgstr "Sem diferença para {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Nenhum download corresponde à sua pesquisa" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "Nenhuma skill externa foi encontrada neste escopo." @@ -7107,7 +7325,7 @@ msgstr "Nenhuma conta GitHub CLI encontrada. Faça login com <0>gh auth login{0} from Poracode." +msgstr "Isso remove do Poracode a senha salva para <0>{0}." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Isso remove o agendamento e seu resultado mais recente deste dispositivo." @@ -11739,6 +12097,10 @@ msgstr "Não foi possível copiar o token de pareamento." msgid "Unable to copy Tailscale URL." msgstr "Não foi possível copiar a URL do Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Não foi possível copiar a senha." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Não foi possível criar a árvore de trabalho do candidato." @@ -11752,6 +12114,10 @@ msgstr "Não foi possível criar a pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Não foi possível criar o diretório da árvore de trabalho WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Não foi possível excluir a senha salva." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Não foi possível excluir o worktree." @@ -11760,6 +12126,18 @@ msgstr "Não foi possível excluir o worktree." msgid "Unable to determine the experiment's source branch." msgstr "Não foi possível determinar o branch de origem do experimento." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Não foi possível editar a senha salva." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Não foi possível encontrar dados do navegador para importar." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Não foi possível importar os dados do navegador." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "Não foi possível instalar {0}." msgid "Unable to load diff." msgstr "Não foi possível carregar o diff." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Não foi possível carregar os downloads." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Não foi possível carregar o pareamento de acesso remoto." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Não foi possível carregar as senhas salvas." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Não foi possível carregar a thread." @@ -11877,6 +12263,10 @@ msgstr "Não foi possível resolver o commit do candidato do experimento" msgid "Unable to restore the SSH connection." msgstr "Não foi possível restaurar a conexão SSH." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Não foi possível revelar a senha." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Não foi possível revogar a sessão remota." @@ -11890,6 +12280,10 @@ msgstr "Não foi possível salvar as credenciais do {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Não foi possível salvar o perfil {displayLabel} do Claude." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Não foi possível salvar a senha." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Não foi possível iniciar {label}." @@ -11961,6 +12355,10 @@ msgstr "Não foi possível atualizar o acesso remoto." msgid "Unable to update Tailscale HTTPS." msgstr "Não foi possível atualizar o HTTPS do Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Não foi possível atualizar o download." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Não foi possível atualizar a URL pública." @@ -12345,6 +12743,10 @@ msgstr "Usado por {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Nome de usuário" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Usando a lista de modelos integrada do Claude." @@ -12418,6 +12820,18 @@ msgstr "Ver o registro de alterações" msgid "View-only — this connection can't manage projects." msgstr "Somente leitura — esta conexão não pode gerenciar projetos." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Altura da área de visualização" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Escala da área de visualização" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Largura da área de visualização" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Servidor web" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Site" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Origem do site" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Dias úteis" @@ -12928,10 +13347,16 @@ msgstr "Suas alterações locais guardadas no stash foram reaplicadas." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Zoom" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Aumentar zoom" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Diminuir zoom" diff --git a/src/renderer/locales/ru/messages.po b/src/renderer/locales/ru/messages.po index fc61ba820..bd7c219bf 100644 --- a/src/renderer/locales/ru/messages.po +++ b/src/renderer/locales/ru/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30 дн" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7 дн" @@ -827,6 +831,10 @@ msgstr "Добавить" msgid "Add a local project" msgstr "Добавить локальный проект" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Добавьте пароль или импортируйте его из другого браузера." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Добавить модель" msgid "Add one manually or import from another agent." msgstr "Добавьте сервер вручную или импортируйте его из другого агента." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Добавить пароль" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Добавить шаблон" @@ -988,6 +1000,10 @@ msgstr "Добавить задачу" msgid "Add workspace" msgstr "Добавить рабочую область" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Дополнительно" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Укажите собственный базовый URL, например обратный прокси или туннель. Оставьте пустым для автоматического режима." @@ -1077,6 +1093,7 @@ msgstr "Агенты классифицируют делегированную #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Агенты классифицируют делегированную работу с помощью тегов задач. Сначала ранжируются подходящие изученные маршруты, затем глобальная статистика Crossagents, избранное, обычная статистика агентов и встроенный порядок." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "заблокировано:" msgid "Bold" msgstr "Полужирный" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Добавить страницу в закладки" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Закладки" @@ -1719,6 +1737,10 @@ msgstr "Вернуть на панель" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Верните его на эту панель или перейдите к окну, в котором он запущен." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Перенесите сохранённые пароли и cookie из установленного браузера." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Браузер открыт в отдельном окне" msgid "Browser MCP enabled for this thread" msgstr "Browser MCP включён для этого треда" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Меню браузера" @@ -1893,6 +1915,8 @@ msgstr "Не удалось связаться с удалённым серве #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Не удалось связаться с удалённым серве msgid "Cancel" msgstr "Отмена" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Отменить загрузку" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Отменить новый профиль Claude" @@ -1932,6 +1960,7 @@ msgstr "отменено" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Отменено" @@ -2128,6 +2157,10 @@ msgstr "Выберите родительскую папку" msgid "Choose at least two candidates and enter a prompt." msgstr "Выберите как минимум двух кандидатов и введите промпт." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Выберите данные для переноса во встроенный браузер." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,21 +2268,33 @@ msgstr "Чистый worktree" msgid "Cleanup script" msgstr "Скрипт очистки" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Очистить данные браузера" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Очистить историю просмотров" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Очистить историю просмотров" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Очистить кэш" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" -msgstr "Очистить cookies" +#~ msgid "Clear Cache" +#~ msgstr "Очистить кэш" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" +msgstr "Очистить cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Очистить cookies" #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" @@ -2361,6 +2406,10 @@ msgstr "Закрыть Crossagents" msgid "Close Crossagents panel" msgstr "Закрыть панель Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Закрыть панель устройств" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Закрыть редактор" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "завершено" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Завершено" @@ -2967,6 +3017,10 @@ msgstr "Управляет сопряжённым рабочим столом, msgid "Conversation" msgstr "Разговор" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Скопируйте все данные Lightcode в Poracode. Poracode msgid "Copy code" msgstr "Копировать код" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Копировать текущий URL" @@ -3045,6 +3099,10 @@ msgstr "Скопировать ссылку для сопряжения" msgid "Copy pairing token" msgstr "Скопировать токен привязки" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Копировать пароль" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Копировать путь" @@ -3554,6 +3612,7 @@ msgstr "Делегируйте работу другим ИИ-агентам." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Удалить безвозвратно" msgid "Delete run" msgstr "Удалить запуск" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Удалить сохранённый пароль" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Удалить сохранённый пароль?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Удалить расписание" @@ -3775,6 +3842,10 @@ msgstr "Серверы разработки, слушающие localhost ваш msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Переопределения только для разработки. Видны только в сборке PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Профиль устройства" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Устройство, используемое кнопкой голос msgid "Devices can pair while this desktop is reachable." msgstr "Устройства могут подключаться, пока этот рабочий стол доступен." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Размеры:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Требуется прямое подключение" @@ -3964,10 +4039,27 @@ msgstr "Готово" msgid "Done · Waiting for your input" msgstr "Готово · Ожидается ваш ввод" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Ошибка загрузки" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "История загрузок" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Скачать изображение" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Ход загрузки" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Загрузка" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Загрузка голосовой модели..." msgid "Downloading… {0}%" msgstr "Загрузка… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Загрузки" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Загрузки из встроенного браузера сохраняются в этой истории." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Изменить цель" msgid "Edit MCP server" msgstr "Изменить сервер MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Изменить пароль" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Редактировать профиль" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Изменить сохранённый пароль" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Файлы для {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Файлы, соответствующие этим glob-шаблонам, скрыты из поиска упоминаний @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Здесь появятся скачанные файлы" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Заполните все обязательные параметры workflow." @@ -4700,6 +4812,11 @@ msgstr "Найти в чате" msgid "Find in diff" msgstr "Найти в diff" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Найти на странице" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Найти в терминале" @@ -4923,6 +5040,10 @@ msgstr "Частота" msgid "Friday at 4:00 PM" msgstr "В пятницу в 16:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Из" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Сделать боковую панель матовой за счёт системного размытия (vibrancy) в духе свежих версий macOS. При отсутствии поддержки используется полупрозрачный оттенок." @@ -5185,7 +5306,7 @@ msgstr "никнейм" msgid "Handle" msgstr "Никнейм" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Жёсткая перезагрузка" @@ -5249,6 +5370,10 @@ msgstr "Скрывает модели, которые вы не использу msgid "Hide panel" msgstr "Скрыть панель" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Скрыть пароль" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Скрыть панель отладки среды выполнения" @@ -5272,7 +5397,7 @@ msgstr "Скрыть навыки из {0}" msgid "Hide terminal" msgstr "Скрыть терминал" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "История" @@ -5317,10 +5442,18 @@ msgstr "Ежечасно" msgid "HTTP (streamable)" msgstr "HTTP (потоковый)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Я понимаю, что защищённые данные браузера могут быть пропущены." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Изображение" msgid "Image preview" msgstr "Предпросмотр изображения" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Импортировать" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Импортировать снова" @@ -5362,10 +5499,23 @@ msgstr "Импортировать снова" msgid "Import and restart" msgstr "Импортировать и перезапустить" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Импортировать данные браузера" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Конфликт импорта" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Импортировать cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Импортировать cookie и пароли" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Импортировать навыки внешних агентов" msgid "Import external skills" msgstr "Импортировать внешние навыки" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Импортировать из вашего браузера" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Импортировать данные Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Импортировать серверы MCP" msgid "Import method" msgstr "Способ импорта" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Импортировать пароли" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Импортировать выбранные" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Источник импорта" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Импортировать в {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "Импортировано навыков: {0}." msgid "Imported 1 skill." msgstr "Импортирован 1 навык." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Импортировано из {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Импортировано паролей: {0}. Импортировано cookie: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Улучшено" @@ -5662,6 +5835,7 @@ msgstr "Инструкции, которым следуют агенты при #~ msgid "Interrupt" #~ msgstr "Прервать" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Прервано" @@ -5670,6 +5844,26 @@ msgstr "Прервано" msgid "Interrupted: agent session ended before completion." msgstr "Прервано: сессия агента завершилась до окончания." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Курсив" @@ -5769,6 +5963,14 @@ msgstr "Язык сообщений коммитов и сводок pull reques msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Язык, который должна ожидать модель распознавания речи при транскрибировании диктовки в поле ввода." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Ноутбук" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Ноутбук L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Последняя оценка:" @@ -5996,6 +6198,10 @@ msgstr "Локальный" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Перед выполнением pull из {branch} локальные изменения нужно поместить в stash" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Локальная защита паролей" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Самая длинная серия" msgid "Longest task" msgstr "Самая длинная задача" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Поиск профилей браузера…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Поиск серверов разработки…" @@ -6082,6 +6292,10 @@ msgstr "Сделать боковую панель полупрозрачной #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Управляйте глобальными навыками агентов и навыками проекта, включая импортированные из других провайдеров." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Управление паролями" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Управление удалёнными средами" @@ -6840,8 +7054,8 @@ msgstr "Новое расписание" #~ msgstr "Новая запланированная задача" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Новая вкладка" @@ -7084,6 +7298,10 @@ msgstr "Нет доступного diff для {0}" msgid "No diff for {visiblePath}" msgstr "Нет diff для {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Нет загрузок, соответствующих поиску" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "В этой области не найдено внешних навыков." @@ -7107,7 +7325,7 @@ msgstr "Учётные записи GitHub CLI не найдены. Войдит #~ msgid "No goal for this thread." #~ msgstr "Для этого треда нет цели." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Истории пока нет" @@ -7202,6 +7420,10 @@ msgstr "Других установленных агентов нет" msgid "No paired devices yet." msgstr "Пока нет сопряженных устройств." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Нет паролей, соответствующих поиску" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Нет шаблонов." @@ -7275,6 +7497,10 @@ msgstr "Запусков ещё нет." msgid "No runtime items yet for this thread." msgstr "Для этого треда пока нет runtime-элементов." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "Нет сохранённых паролей" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Пока нет подключённых серверов." @@ -7316,6 +7542,10 @@ msgstr "Нет подготовленных изменений" msgid "No supported agents detected" msgstr "Поддерживаемые агенты не обнаружены" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "На этом устройстве не найдены профили поддерживаемых браузеров." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Нет тегов — запись перестаёт влиять на сродство тегов." @@ -7601,6 +7831,10 @@ msgstr "Открыть Crossagent: {0}" msgid "Open dialog" msgstr "Открывать диалог" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Открыть скачанный файл" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Открыть эксперимент" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Кодовая фраза (необязательно)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Пароль" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Пароль скопирован." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Менеджер паролей" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Раздел менеджера паролей" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Пароли" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Пароли и автозаполнение" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Пароли шифруются в локальном хранилище учётных данных Poracode. Значения паролей запрашиваются только при их отображении, копировании или изменении." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Приостановить" #~ msgid "Pause {0}" #~ msgstr "Приостановить {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Приостановить загрузку" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Приостановить цель" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Приостановлено" @@ -8132,6 +8398,10 @@ msgstr "Закреплено" msgid "Pinned task routes" msgstr "Закреплённые маршруты задач" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Предыдущие запуски" msgid "Previous tab" msgstr "Предыдущая вкладка" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Печать" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Политика конфиденциальности" @@ -8476,6 +8750,10 @@ msgstr "Запросы" msgid "Proposed plan" msgstr "Предложенный план" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Защищённые данные браузера" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Предоставляется и управляется Poracode для поддерживаемых агентов." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Удалить действие" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Удалить закладку" @@ -9011,6 +9290,10 @@ msgstr "Удалить API-ключ Cursor SDK" msgid "Remove environment variable" msgstr "Удалить переменную окружения" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Удалить из истории загрузок" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Удалить из избранного" @@ -9204,6 +9487,10 @@ msgstr "Сбросить матовость боковой панели к зн msgid "Reset to default" msgstr "Сбросить к значению по умолчанию" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Сбросить масштаб" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Изменить размер панели браузера" @@ -9246,11 +9533,27 @@ msgstr "Изменить размер разделения" msgid "Resize terminal panel" msgstr "Изменить размер панели терминала" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Изменить размер области просмотра" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Изменить высоту области просмотра" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Изменить ширину области просмотра" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Разрешает конфликты слияния во время rebase или слияния." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Адаптивный" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Перезапустить Poracode" @@ -9288,6 +9591,10 @@ msgstr "Возобновить" #~ msgid "Resume {label}" #~ msgstr "Возобновить {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Возобновить загрузку" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Возобновить цель" @@ -9340,6 +9647,10 @@ msgstr "Показать в Проводнике" msgid "Reveal in file tree" msgstr "Показать в дереве файлов" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Показать пароль" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Панель справа" msgid "Right-click to switch ring" msgstr "Щелкните правой кнопкой, чтобы переключить кольцо" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Повернуть область просмотра" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Отладка среды выполнения" msgid "Runtime options" msgstr "Параметры среды выполнения" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Поиск агентов..." msgid "Search branches..." msgstr "Поиск веток..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Поиск загрузок" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Поиск моделей..." msgid "Search or enter address" msgstr "Поиск или ввод адреса" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Поиск паролей" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Искать pull request" @@ -10029,7 +10357,7 @@ msgstr "Зафиксировано при запуске этой сессии #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "Показывать домашнюю область без проект msgid "Show all" msgstr "Показать всё" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Показать панель закладок" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Показать панель закладок" @@ -10105,6 +10433,10 @@ msgstr "Показать Composer" msgid "Show context usage details" msgstr "Показать детали использования контекста" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Показать панель устройств" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Показывать оценочную стоимость" msgid "Show in file explorer" msgstr "Показать в проводнике" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Показать в папке" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Решение {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Решение {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Часть данных браузера была пропущена. Пропущено защищённых элементов: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Некоторые проверки не пройдены или ожидают выполнения." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Некоторые браузеры Chromium защищают cookie и пароли с помощью App-Bound Encryption. Poracode не обходит эту защиту. Данные, к которым операционная система не предоставляет доступ, будут пропущены." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Поддержка:" msgid "Supports Fast mode" msgstr "Поддерживает быстрый режим" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Поменять местами заметки и задачи" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "URL Tailscale" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Сделать снимок экрана" @@ -11392,6 +11745,11 @@ msgstr "Удалённый сервер этого проекта офлайн. msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Это удаляет последующие сообщения и восстанавливает файлы, если доступен снимок контрольной точки." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Сохранённый пароль для <0>{0} будет удалён из Poracode." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Удаляет расписание и его последний результат с этого устройства." @@ -11739,6 +12097,10 @@ msgstr "Не удалось скопировать токен привязки." msgid "Unable to copy Tailscale URL." msgstr "Не удалось скопировать URL Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Не удалось скопировать пароль." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Не удалось создать рабочее дерево кандидата." @@ -11752,6 +12114,10 @@ msgstr "Не удалось создать pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Не удалось создать каталог worktree WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Не удалось удалить сохранённый пароль." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Не удалось удалить worktree." @@ -11760,6 +12126,18 @@ msgstr "Не удалось удалить worktree." msgid "Unable to determine the experiment's source branch." msgstr "Не удалось определить исходную ветку эксперимента." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Не удалось изменить сохранённый пароль." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Не удалось найти данные браузера для импорта." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Не удалось импортировать данные браузера." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "Не удалось установить {0}." msgid "Unable to load diff." msgstr "Не удалось загрузить различия." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Не удалось загрузить список загрузок." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Не удалось загрузить сопряжение удаленного доступа." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Не удалось загрузить сохранённые пароли." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Не удалось загрузить поток." @@ -11877,6 +12263,10 @@ msgstr "Не удалось определить коммит кандидата msgid "Unable to restore the SSH connection." msgstr "Не удалось восстановить SSH-подключение." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Не удалось показать пароль." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Не удалось отозвать удаленную сессию." @@ -11890,6 +12280,10 @@ msgstr "Не удалось сохранить учётные данные {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Не удалось сохранить профиль Claude {displayLabel}." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Не удалось сохранить пароль." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Не удалось запустить {label}." @@ -11961,6 +12355,10 @@ msgstr "Не удалось обновить удаленный доступ." msgid "Unable to update Tailscale HTTPS." msgstr "Не удалось обновить Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Не удалось обновить загрузку." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Не удалось обновить публичный URL." @@ -12345,6 +12743,10 @@ msgstr "Используется командой {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Имя пользователя" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Используется встроенный список моделей Claude." @@ -12418,6 +12820,18 @@ msgstr "Открыть список изменений" msgid "View-only — this connection can't manage projects." msgstr "Только просмотр — это подключение не может управлять проектами." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Высота области просмотра" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Масштаб области просмотра" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Ширина области просмотра" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Веб-сервер" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Веб-сайт" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Адрес сайта" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "По будням" @@ -12928,10 +13347,16 @@ msgstr "Ваши локальные изменения из stash были пр msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Масштаб" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Увеличить" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Уменьшить" diff --git a/src/renderer/locales/tr/messages.po b/src/renderer/locales/tr/messages.po index 800792d17..788b78cb8 100644 --- a/src/renderer/locales/tr/messages.po +++ b/src/renderer/locales/tr/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30g" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7g" @@ -827,6 +831,10 @@ msgstr "Ekle" msgid "Add a local project" msgstr "Yerel bir proje ekle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Bir parola ekleyin veya başka bir tarayıcıdan içe aktarın." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Model ekle" msgid "Add one manually or import from another agent." msgstr "Birini elle ekleyin veya başka bir ajandan içe aktarın." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Parola ekle" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Desen ekle" @@ -988,6 +1000,10 @@ msgstr "Yapılacak ekle" msgid "Add workspace" msgstr "Çalışma alanı ekle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Gelişmiş" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Özel bir temel URL duyurun, örn. bir ters proxy veya tünel. Otomatik için boş bırakın." @@ -1077,6 +1093,7 @@ msgstr "Agentlar devredilen işi görev etiketleriyle sınıflandırır. Manuel #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Aracılar devredilen işleri görev etiketleriyle sınıflandırır. Eşleşen öğrenilmiş rotalar ilk sırada yer alır; ardından genel Crossagents kullanımı, favoriler, normal aracı kullanımı ve yerleşik sıra gelir." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "engellendi:" msgid "Bold" msgstr "Kalın" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Bu sayfayı yer imlerine ekle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Yer imleri" @@ -1719,6 +1737,10 @@ msgstr "Panele geri getir" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Bu panele geri getirin veya çalıştığı pencereye geçin." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Kayıtlı parolaları ve çerezleri yüklü bir tarayıcıdan aktarın." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Tarayıcı ayrı bir pencerede açık" msgid "Browser MCP enabled for this thread" msgstr "Bu konu için Tarayıcı MCP'si etkinleştirildi" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Tarayıcı menüsü" @@ -1893,6 +1915,8 @@ msgstr "Uzak sunucuya ulaşılamıyor. Çevrimiçi olduğunu doğrulayıp yenide #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Uzak sunucuya ulaşılamıyor. Çevrimiçi olduğunu doğrulayıp yenide msgid "Cancel" msgstr "İptal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "İndirmeyi iptal et" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Yeni Claude profilini iptal et" @@ -1932,6 +1960,7 @@ msgstr "iptal edildi" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "İptal edildi" @@ -2128,6 +2157,10 @@ msgstr "Bir ana klasör seçin" msgid "Choose at least two candidates and enter a prompt." msgstr "En az iki aday seçin ve bir istem girin." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Yerleşik tarayıcıya aktarılacak verileri seçin." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,21 +2268,33 @@ msgstr "Temiz çalışma ağacı" msgid "Cleanup script" msgstr "Temizleme komut dosyası" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Tarama verilerini temizle" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Tarama geçmişini temizle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Tarama Geçmişini Temizle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" +msgstr "Önbelleği temizle" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" -msgstr "Önbelleği Temizle" +#~ msgid "Clear Cache" +#~ msgstr "Önbelleği Temizle" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" +msgstr "Çerezleri temizle" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" -msgstr "Çerezleri Temizle" +#~ msgid "Clear Cookies" +#~ msgstr "Çerezleri Temizle" #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" @@ -2361,6 +2406,10 @@ msgstr "Crossagents'i kapat" msgid "Close Crossagents panel" msgstr "Crossagents panelini kapat" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Cihaz araç çubuğunu kapat" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Düzenleyiciyi kapat" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "tamamlandı" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Tamamlandı" @@ -2967,6 +3017,10 @@ msgstr "Ajan tıklarken veya yazarken eşleştirilmiş masaüstünü kontrol ede msgid "Conversation" msgstr "Konuşma" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Çerezler" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Tüm Lightcode verilerini Poracode'a kopyalayın. Poracode yeniden başl msgid "Copy code" msgstr "Kodu kopyala" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Mevcut URL'yi Kopyala" @@ -3045,6 +3099,10 @@ msgstr "Eşleştirme bağlantısını kopyala" msgid "Copy pairing token" msgstr "Eşleştirme token'ını kopyala" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Parolayı kopyala" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Yolu Kopyala" @@ -3554,6 +3612,7 @@ msgstr "İşi diğer yapay zeka aracılarına devredin." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Kalıcı olarak sil" msgid "Delete run" msgstr "Çalıştırmayı sil" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Kayıtlı parolayı sil" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Kayıtlı parola silinsin mi?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Zamanlamayı sil" @@ -3775,6 +3842,10 @@ msgstr "Masaüstünüzün localhost'unda dinleyen geliştirme sunucuları." msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Yalnızca geliştirme amaçlı geçersiz kılmalar. Yalnızca PORACODE DEV yapısında görünür." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Cihaz ön ayarı" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Oluşturucu ses giriş düğmesi tarafından kullanılan cihaz." msgid "Devices can pair while this desktop is reachable." msgstr "Cihazlar bu masaüstüne erişilebildiği sürece eşleşebilir." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Boyutlar:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Doğrudan bağlantı gerekiyor" @@ -3964,10 +4039,27 @@ msgstr "Bitti" msgid "Done · Waiting for your input" msgstr "Bitti · Girişinizi bekliyorum" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "İndirme başarısız oldu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "İndirme geçmişi" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Görseli indir" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "İndirme ilerlemesi" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "İndiriliyor" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Ses modeli indiriliyor..." msgid "Downloading… {0}%" msgstr "İndiriliyor… %{0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "İndirilenler" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Yerleşik tarayıcıdan indirilenler bu geçmişe kaydedilir." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Hedefi düzenle" msgid "Edit MCP server" msgstr "MCP sunucusunu düzenle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Parolayı düzenle" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Profili düzenle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Kayıtlı parolayı düzenle" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "{0} için dosyalar" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Bu glob'larla eşleşen dosyalar, @file bahsetme aramasında gizlenir." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "İndirdiğiniz dosyalar burada görünür" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Tüm gerekli workflow girdilerini doldurun." @@ -4700,6 +4812,11 @@ msgstr "Sohbette bul" msgid "Find in diff" msgstr "Farkta bul" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Sayfada bul" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Terminalde bul" @@ -4923,6 +5040,10 @@ msgstr "Sıklık" msgid "Friday at 4:00 PM" msgstr "Cuma 16:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Kaynak" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Kenar çubuğunu, son macOS sürümlerini andıran sistem bulanıklık materyaliyle (vibrancy) buzlu cama dönüştürün. Desteklenmediğinde yarı saydam bir tona geri döner." @@ -5185,7 +5306,7 @@ msgstr "kullanıcı adı" msgid "Handle" msgstr "Kullanıcı adı" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Sert Yeniden Yükleme" @@ -5249,6 +5370,10 @@ msgstr "Kullanmadığınız modelleri her sağlayıcıdaki model seçiciden gizl msgid "Hide panel" msgstr "Paneli gizle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Parolayı gizle" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Çalışma zamanı hata ayıklama panelini gizle" @@ -5272,7 +5397,7 @@ msgstr "{0} becerilerini gizle" msgid "Hide terminal" msgstr "Terminali gizle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Geçmiş" @@ -5317,10 +5442,18 @@ msgstr "Saatlik" msgid "HTTP (streamable)" msgstr "HTTP (akış destekli)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Korunan tarayıcı verilerinin atlanabileceğini anlıyorum." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Resim" msgid "Image preview" msgstr "Resim önizlemesi" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "İçe aktar" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Tekrar içe aktar" @@ -5362,10 +5499,23 @@ msgstr "Tekrar içe aktar" msgid "Import and restart" msgstr "İçe aktar ve yeniden başlat" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Tarayıcı verilerini içe aktar" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "İçe aktarma çakışması" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Çerezleri içe aktar" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Çerezleri ve parolaları içe aktar" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Harici aracı becerilerini içe aktar" msgid "Import external skills" msgstr "Harici becerileri içe aktar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Tarayıcınızdan içe aktar" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Lightcode verilerini içe aktar" @@ -5399,10 +5553,18 @@ msgstr "MCP sunucularını içe aktar" msgid "Import method" msgstr "İçe aktarma yöntemi" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Parolaları içe aktar" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Seçilenleri içe aktar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "İçe aktarma kaynağı" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "{destinationProjectName} projesine içe aktar" @@ -5430,6 +5592,17 @@ msgstr "{0} beceri içe aktarıldı." msgid "Imported 1 skill." msgstr "1 beceri içe aktarıldı." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "{0} kaynağından içe aktarıldı" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "İçe aktarılan parolalar: {0}. İçe aktarılan çerezler: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "İyileştirildi" @@ -5662,6 +5835,7 @@ msgstr "Aracıların hangi aracıya veya modele devredeceğini seçerken izledi #~ msgid "Interrupt" #~ msgstr "Kes" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Kesintiye uğradı" @@ -5670,6 +5844,26 @@ msgstr "Kesintiye uğradı" msgid "Interrupted: agent session ended before completion." msgstr "Kesintiye uğradı: Ajan oturumu tamamlanmadan sona erdi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "italik" @@ -5769,6 +5963,14 @@ msgstr "Yapay zeka tarafından oluşturulan commit mesajları ve çekme isteği msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Yazma alanı diktesini yazıya dökerken konuşma modelinin beklemesi gereken dil." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Dizüstü bilgisayar" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Dizüstü bilgisayar L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Son değerlendirme:" @@ -5996,6 +6198,10 @@ msgstr "Yerel" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "{branch}'dan çekilmeden önce yerel değişikliklerin saklanması gerekiyor" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Yerel parola koruması" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "En uzun seri" msgid "Longest task" msgstr "En uzun görev" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Tarayıcı profilleri aranıyor…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Geliştirme sunucuları aranıyor…" @@ -6082,6 +6292,10 @@ msgstr "Kenar çubuğunu yarı saydam yapın — Windows 11'de sistem bulanıkl #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Diğer sağlayıcılardan içe aktarılanlar dahil olmak üzere genel ve proje aracı becerilerini yönetin." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Parolaları yönet" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Uzak ortamları yönet" @@ -6840,8 +7054,8 @@ msgstr "Yeni zamanlama" #~ msgstr "Yeni zamanlanmış görev" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Yeni sekme" @@ -7084,6 +7298,10 @@ msgstr "{0} için fark mevcut değil" msgid "No diff for {visiblePath}" msgstr "{visiblePath} için fark yok" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Aramanızla eşleşen indirme yok" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "Bu kapsamda harici beceri bulunamadı." @@ -7107,7 +7325,7 @@ msgstr "GitHub CLI hesabı bulunamadı. <0>gh auth login ile oturum açın v #~ msgid "No goal for this thread." #~ msgstr "Bu konu için hedef yok." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Henüz geçmiş yok" @@ -7202,6 +7420,10 @@ msgstr "Başka aracı yüklü değil" msgid "No paired devices yet." msgstr "Henüz eşleştirilmiş cihaz yok." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Aramanızla eşleşen parola yok" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Desen yok." @@ -7275,6 +7497,10 @@ msgstr "Henüz çalıştırma yok." msgid "No runtime items yet for this thread." msgstr "Bu konu için henüz çalışma zamanı öğesi yok." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "Kayıtlı parola yok" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Henüz bağlı sunucu yok." @@ -7316,6 +7542,10 @@ msgstr "Aşamalı değişiklik yok" msgid "No supported agents detected" msgstr "Desteklenen aracı algılanmadı" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "Bu cihazda desteklenen bir tarayıcı profili bulunamadı." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Etiket yok — girdi artık etiket yakınlığını etkilemiyor." @@ -7601,6 +7831,10 @@ msgstr "Crossagent'ı aç: {0}" msgid "Open dialog" msgstr "İletişim kutusunu aç" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "İndirilen dosyayı aç" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Deneyi aç" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Parola ifadesi (isteğe bağlı)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Parola" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Parola kopyalandı." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Parola yöneticisi" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Parola yöneticisi bölümü" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Parolalar" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Parolalar ve otomatik doldurma" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Parolalar, Poracode'un yerel kimlik bilgisi deposunda şifrelenir. Parola değerleri yalnızca onları gösterdiğinizde, kopyaladığınızda veya düzenlediğinizde istenir." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Duraklat" #~ msgid "Pause {0}" #~ msgstr "{0} öğesini duraklat" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "İndirmeyi duraklat" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Hedefi duraklat" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Duraklatıldı" @@ -8132,6 +8398,10 @@ msgstr "Sabitlendi" msgid "Pinned task routes" msgstr "Sabitlenmiş görev rotaları" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Önceki çalıştırmalar" msgid "Previous tab" msgstr "Önceki sekme" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Yazdır" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Gizlilik Politikası" @@ -8476,6 +8750,10 @@ msgstr "İstemler" msgid "Proposed plan" msgstr "Önerilen plan" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Korunan tarayıcı verileri" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Desteklenen aracılar için Poracode tarafından sağlanır ve yönetilir." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Eylemi kaldır" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Yer imini kaldır" @@ -9011,6 +9290,10 @@ msgstr "Cursor SDK API anahtarını kaldır" msgid "Remove environment variable" msgstr "Ortam değişkenini kaldır" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "İndirme geçmişinden kaldır" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Favorilerden kaldır" @@ -9204,6 +9487,10 @@ msgstr "Kenar çubuğu buzlu cam efektini varsayılana sıfırla" msgid "Reset to default" msgstr "Varsayılana sıfırla" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Yakınlaştırmayı sıfırla" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Tarayıcı çekmecesini yeniden boyutlandır" @@ -9246,11 +9533,27 @@ msgstr "Bölmeyi yeniden boyutlandır" msgid "Resize terminal panel" msgstr "Terminal panelini yeniden boyutlandır" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Görüntü alanını yeniden boyutlandır" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Görüntü alanının yüksekliğini değiştir" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Görüntü alanının genişliğini değiştir" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Yeniden temel alma veya birleştirme sırasındaki birleştirme çakışmalarını çözer." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Duyarlı" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Poracode'u yeniden başlat" @@ -9288,6 +9591,10 @@ msgstr "Sürdür" #~ msgid "Resume {label}" #~ msgstr "{label} öğesini sürdür" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "İndirmeyi sürdür" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Hedefi sürdür" @@ -9340,6 +9647,10 @@ msgstr "Dosya Gezgini'nde Göster" msgid "Reveal in file tree" msgstr "Dosya ağacında göster" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Parolayı göster" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Sağ panel" msgid "Right-click to switch ring" msgstr "Halkayı değiştirmek için sağ tıklayın" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Görüntü alanını döndür" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Çalışma zamanı hata ayıklaması" msgid "Runtime options" msgstr "Çalışma zamanı seçenekleri" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Aracıları ara..." msgid "Search branches..." msgstr "Şube ara..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "İndirilenlerde ara" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Modelleri ara..." msgid "Search or enter address" msgstr "Adres arayın veya girin" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Parolalarda ara" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Çekme isteklerinde ara" @@ -10029,7 +10357,7 @@ msgstr "Bu oturum başlatıldığında sabitlendi — sunucuları değiştirmek #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "İşletim sistemi düzeyindeki aracı oturumları için projesiz bir Ana msgid "Show all" msgstr "Tümünü göster" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Yer imi çubuğunu göster" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Yer İşareti Çubuğunu Göster" @@ -10105,6 +10433,10 @@ msgstr "Composer'ı göster" msgid "Show context usage details" msgstr "Bağlam kullanım ayrıntılarını göster" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Cihaz araç çubuğunu göster" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Tahmini maliyeti göster" msgid "Show in file explorer" msgstr "Dosya gezgininde göster" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Klasörde göster" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Çözüm {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Çözüm {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Bazı tarayıcı verileri atlandı. Atlanan korunan öğe sayısı: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Bazı kontroller başarısız oluyor veya beklemede." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Bazı Chromium tarayıcıları çerezleri ve parolaları App-Bound Encryption ile korur. Poracode bu korumayı aşmaz. İşletim sisteminin erişime açmadığı veriler atlanır." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Destek:" msgid "Supports Fast mode" msgstr "Hızlı modu destekler" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Notları ve yapılacak işleri değiştir" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "Tailscale URL'si" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Ekran Görüntüsü Al" @@ -11392,6 +11745,11 @@ msgstr "Bu projenin uzak sunucusu çevrimdışı. Bir iş parçacığı başlatm msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Bu, daha sonraki mesajları kaldırır ve bir denetim noktası anlık görüntüsü mevcut olduğunda dosyaları geri yükler." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Bu işlem, <0>{0} için kaydedilen parolayı Poracode'dan kaldırır." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Zamanlamayı ve en son sonucunu bu cihazdan kaldırır." @@ -11739,6 +12097,10 @@ msgstr "Eşleştirme token'ı kopyalanamadı." msgid "Unable to copy Tailscale URL." msgstr "Tailscale URL'si kopyalanamadı." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Parola kopyalanamadı." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Aday çalışma ağacı oluşturulamadı." @@ -11752,6 +12114,10 @@ msgstr "Pull request oluşturulamadı: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "\"{path}\" WSL çalışma ağacı dizini oluşturulamıyor" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Kayıtlı parola silinemedi." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Worktree silinemedi." @@ -11760,6 +12126,18 @@ msgstr "Worktree silinemedi." msgid "Unable to determine the experiment's source branch." msgstr "Deneyin kaynak şubesi belirlenemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Kayıtlı parola düzenlenemedi." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "İçe aktarılacak tarayıcı verileri bulunamadı." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Tarayıcı verileri içe aktarılamadı." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "{0} yüklenemiyor." msgid "Unable to load diff." msgstr "Fark yüklenemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "İndirilenler yüklenemedi." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Uzaktan erişim eşleştirmesi yüklenemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Kayıtlı parolalar yüklenemedi." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "İş parçacığı yüklenemiyor." @@ -11877,6 +12263,10 @@ msgstr "Deney adayının commit'i çözümlenemedi" msgid "Unable to restore the SSH connection." msgstr "SSH bağlantısı geri yüklenemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Parola gösterilemedi." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Uzak oturum iptal edilemedi." @@ -11890,6 +12280,10 @@ msgstr "{0} kimlik bilgileri kaydedilemiyor." msgid "Unable to save Claude {displayLabel} profile." msgstr "Claude {displayLabel} profili kaydedilemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Parola kaydedilemedi." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "{label} başlatılamıyor." @@ -11961,6 +12355,10 @@ msgstr "Uzaktan erişim güncellenemedi." msgid "Unable to update Tailscale HTTPS." msgstr "Tailscale HTTPS güncellenemedi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "İndirme güncellenemedi." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Genel URL güncellenemedi." @@ -12345,6 +12743,10 @@ msgstr "{conflict} tarafından kullanılıyor" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Kullanıcı adı" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Yerleşik Claude model listesi kullanılıyor." @@ -12418,6 +12820,18 @@ msgstr "Değişiklik günlüğünü görüntüle" msgid "View-only — this connection can't manage projects." msgstr "Yalnızca görüntüleme — bu bağlantı projeleri yönetemez." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Görüntü alanı yüksekliği" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Görüntü alanı ölçeği" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Görüntü alanı genişliği" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Web sunucusu" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Web sitesi" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Web sitesi adresi" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Hafta içi" @@ -12928,10 +13347,16 @@ msgstr "Zuladaki (stash) yerel değişiklikleriniz yeniden uygulandı." msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Yakınlaştırma" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Yakınlaştır" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Uzaklaştır" diff --git a/src/renderer/locales/uk/messages.po b/src/renderer/locales/uk/messages.po index 98905b6d7..3defe9592 100644 --- a/src/renderer/locales/uk/messages.po +++ b/src/renderer/locales/uk/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30 дн" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7 дн" @@ -827,6 +831,10 @@ msgstr "Додати" msgid "Add a local project" msgstr "Додати локальний проєкт" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Додайте пароль або імпортуйте його з іншого браузера." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Додати модель" msgid "Add one manually or import from another agent." msgstr "Додайте сервер вручну або імпортуйте його з іншого агента." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Додати пароль" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Додати шаблон" @@ -988,6 +1000,10 @@ msgstr "Додати завдання" msgid "Add workspace" msgstr "Додати робочу область" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Додатково" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Оголосіть власний базовий URL, наприклад зворотний проксі або тунель. Залиште порожнім для автоматичного режиму." @@ -1077,6 +1093,7 @@ msgstr "Агенти класифікують делеговану роботу #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Агенти класифікують делеговану роботу за допомогою тегів завдань. Спочатку ранжуються відповідні вивчені маршрути, потім глобальна статистика Crossagents, вибране, звичайна статистика агентів і вбудований порядок." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "заблоковано:" msgid "Bold" msgstr "Жирний" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Додати сторінку в закладки" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Закладки" @@ -1719,6 +1737,10 @@ msgstr "Повернути на панель" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Поверніть його на цю панель або перейдіть до вікна, де він запущений." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Перенесіть збережені паролі та файли cookie зі встановленого браузера." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Браузер відкритий в окремому вікні" msgid "Browser MCP enabled for this thread" msgstr "Browser MCP увімкнено для цього треду" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Меню браузера" @@ -1893,6 +1915,8 @@ msgstr "Не вдалося зв’язатися з віддаленим сер #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Не вдалося зв’язатися з віддаленим сер msgid "Cancel" msgstr "Скасувати" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Скасувати завантаження" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Скасувати новий профіль Claude" @@ -1932,6 +1960,7 @@ msgstr "скасовано" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Скасовано" @@ -2128,6 +2157,10 @@ msgstr "Виберіть батьківську папку" msgid "Choose at least two candidates and enter a prompt." msgstr "Виберіть щонайменше двох кандидатів і введіть промпт." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Виберіть дані для перенесення до вбудованого браузера." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,21 +2268,33 @@ msgstr "Чистий worktree" msgid "Cleanup script" msgstr "Скрипт очищення" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Очистити дані браузера" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Очистити історію перегляду" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Очистити історію переглядів" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Очистити кеш" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" -msgstr "Очистити cookies" +#~ msgid "Clear Cache" +#~ msgstr "Очистити кеш" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" +msgstr "Очистити файли cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Очистити cookies" #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" @@ -2361,6 +2406,10 @@ msgstr "Закрити Crossagents" msgid "Close Crossagents panel" msgstr "Закрити панель Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Закрити панель пристроїв" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Закрити редактор" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "завершено" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Завершено" @@ -2967,6 +3017,10 @@ msgstr "Керує спареним робочим столом, поки аге msgid "Conversation" msgstr "Розмова" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Файли cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Скопіюйте всі дані Lightcode до Poracode. Poracode п msgid "Copy code" msgstr "Копіювати код" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Копіювати поточний URL" @@ -3045,6 +3099,10 @@ msgstr "Скопіювати посилання для спарення" msgid "Copy pairing token" msgstr "Скопіювати токен підключення" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Копіювати пароль" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Копіювати шлях" @@ -3554,6 +3612,7 @@ msgstr "Делегуйте роботу іншим ШІ-агентам." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Видалити назавжди" msgid "Delete run" msgstr "Видалити запуск" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Видалити збережений пароль" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Видалити збережений пароль?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Видалити розклад" @@ -3775,6 +3842,10 @@ msgstr "Сервери розробки, що слухають localhost ваш msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Перевизначення лише для розробки. Видимі лише у збірці PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Профіль пристрою" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Пристрій, який використовує кнопка гол msgid "Devices can pair while this desktop is reachable." msgstr "Пристрої можуть сполучатися, доки цей робочий стіл доступний." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Розміри:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Потрібне пряме підключення" @@ -3964,10 +4039,27 @@ msgstr "Завершено" msgid "Done · Waiting for your input" msgstr "Готово · Очікується ваш ввід" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Помилка завантаження" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Історія завантажень" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Завантажити зображення" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Перебіг завантаження" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Триває завантаження" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Завантаження голосової моделі..." msgid "Downloading… {0}%" msgstr "Завантаження… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Завантаження" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Завантаження з вбудованого браузера зберігаються в цій історії." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Редагувати мету" msgid "Edit MCP server" msgstr "Редагувати сервер MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Змінити пароль" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Редагувати профіль" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Змінити збережений пароль" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Файли для {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Файли, що відповідають цим glob-шаблонам, приховані з пошуку згадок @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Тут з’являться завантажені вами файли" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Заповніть усі обов’язкові параметри workflow." @@ -4700,6 +4812,11 @@ msgstr "Пошук у чаті" msgid "Find in diff" msgstr "Пошук у diff" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Знайти на сторінці" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Пошук у терміналі" @@ -4923,6 +5040,10 @@ msgstr "Частота" msgid "Friday at 4:00 PM" msgstr "У п’ятницю о 16:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Звідки" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Зробіть бічну панель матовою за допомогою системного розмиття (vibrancy) у стилі сучасної macOS. Там, де це не підтримується, застосовується напівпрозоре тонування." @@ -5185,7 +5306,7 @@ msgstr "нік" msgid "Handle" msgstr "Нік" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Жорстке перезавантаження" @@ -5249,6 +5370,10 @@ msgstr "Приховує моделі, які ви не використовує msgid "Hide panel" msgstr "Сховати панель" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Приховати пароль" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Сховати панель налагодження середовища виконання" @@ -5272,7 +5397,7 @@ msgstr "Приховати навички з {0}" msgid "Hide terminal" msgstr "Сховати термінал" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Історія" @@ -5317,10 +5442,18 @@ msgstr "Щогодини" msgid "HTTP (streamable)" msgstr "HTTP (потоковий)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Я розумію, що захищені дані браузера можуть бути пропущені." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Зображення" msgid "Image preview" msgstr "Попередній перегляд зображення" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Імпортувати" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Імпортувати знову" @@ -5362,10 +5499,23 @@ msgstr "Імпортувати знову" msgid "Import and restart" msgstr "Імпортувати й перезапустити" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Імпортувати дані браузера" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Конфлікт імпорту" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Імпортувати файли cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Імпортувати файли cookie та паролі" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Імпортувати навички зовнішніх агентів" msgid "Import external skills" msgstr "Імпортувати зовнішні навички" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Імпортувати з вашого браузера" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Імпортувати дані Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Імпортувати сервери MCP" msgid "Import method" msgstr "Спосіб імпорту" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Імпортувати паролі" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Імпортувати вибрані" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Джерело імпорту" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Імпортувати до {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "Імпортовано навичок: {0}." msgid "Imported 1 skill." msgstr "Імпортовано 1 навичку." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Імпортовано з {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Імпортовано паролів: {0}. Імпортовано файлів cookie: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Покращено" @@ -5662,6 +5835,7 @@ msgstr "Інструкції, яких дотримуються агенти, о #~ msgid "Interrupt" #~ msgstr "Перервати" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Перервано" @@ -5670,6 +5844,26 @@ msgstr "Перервано" msgid "Interrupted: agent session ended before completion." msgstr "Перервано: сесія агента завершилася до завершення." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Курсив" @@ -5769,6 +5963,14 @@ msgstr "Мова для згенерованих ШІ повідомлень к msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Мова, яку модель розпізнавання мовлення має очікувати під час транскрибування диктування в полі вводу." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Ноутбук" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Ноутбук L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Остання оцінка:" @@ -5996,6 +6198,10 @@ msgstr "Локальний" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Перед виконанням pull з {branch} локальні зміни потрібно помістити у stash" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Локальний захист паролів" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Найдовша серія" msgid "Longest task" msgstr "Найдовше завдання" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Пошук профілів браузера…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Пошук серверів розробки…" @@ -6082,6 +6292,10 @@ msgstr "Зробіть бічну панель напівпрозорою — с #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Керуйте глобальними навичками агентів і навичками проєкту, зокрема імпортованими від інших провайдерів." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Керування паролями" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Керування віддаленими середовищами" @@ -6840,8 +7054,8 @@ msgstr "Новий розклад" #~ msgstr "Нове заплановане завдання" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Нова вкладка" @@ -7084,6 +7298,10 @@ msgstr "Немає доступного diff для {0}" msgid "No diff for {visiblePath}" msgstr "Немає diff для {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Немає завантажень, що відповідають пошуку" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "У цій області не знайдено зовнішніх навичок." @@ -7107,7 +7325,7 @@ msgstr "Облікові записи GitHub CLI не знайдено. Увій #~ msgid "No goal for this thread." #~ msgstr "Для цього треду немає цілі." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Історії ще немає" @@ -7202,6 +7420,10 @@ msgstr "Інших встановлених агентів немає" msgid "No paired devices yet." msgstr "Ще немає спарених пристроїв." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Немає паролів, що відповідають пошуку" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Немає шаблонів." @@ -7275,6 +7497,10 @@ msgstr "Запусків ще немає." msgid "No runtime items yet for this thread." msgstr "Для цього треду ще немає runtime-елементів." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "Немає збережених паролів" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Ще немає підключених серверів." @@ -7316,6 +7542,10 @@ msgstr "Немає підготовлених змін" msgid "No supported agents detected" msgstr "Підтримуваних агентів не виявлено" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "На цьому пристрої не знайдено профілів підтримуваних браузерів." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Немає тегів — запис перестає впливати на спорідненість тегів." @@ -7601,6 +7831,10 @@ msgstr "Відкрити Crossagent: {0}" msgid "Open dialog" msgstr "Відкрити діалог" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Відкрити завантажений файл" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Відкрити експеримент" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Парольна фраза (необов’язково)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Пароль" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Пароль скопійовано." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Менеджер паролів" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Розділ менеджера паролів" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Паролі" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Паролі й автозаповнення" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Паролі зашифровано в локальному сховищі облікових даних Poracode. Значення паролів запитуються лише тоді, коли ви їх показуєте, копіюєте або редагуєте." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Призупинити" #~ msgid "Pause {0}" #~ msgstr "Призупинити {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Призупинити завантаження" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Призупинити мету" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Призупинено" @@ -8132,6 +8398,10 @@ msgstr "Закріплено" msgid "Pinned task routes" msgstr "Закріплені маршрути завдань" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Попередні запуски" msgid "Previous tab" msgstr "Попередня вкладка" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "Друк" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Політика конфіденційності" @@ -8476,6 +8750,10 @@ msgstr "Запити" msgid "Proposed plan" msgstr "Запропонований план" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Захищені дані браузера" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Надається та керується Poracode для підтримуваних агентів." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Видалити дію" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Видалити закладку" @@ -9011,6 +9290,10 @@ msgstr "Видалити API-ключ Cursor SDK" msgid "Remove environment variable" msgstr "Видалити змінну середовища" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Видалити з історії завантажень" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Видалити з обраного" @@ -9204,6 +9487,10 @@ msgstr "Скинути матовість бічної панелі до тип msgid "Reset to default" msgstr "Скинути до типового" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Скинути масштаб" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Змінити розмір панелі браузера" @@ -9246,11 +9533,27 @@ msgstr "Змінити розмір розділення" msgid "Resize terminal panel" msgstr "Змінити розмір панелі термінала" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Змінити розмір області перегляду" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Змінити висоту області перегляду" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Змінити ширину області перегляду" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Розв'язує конфлікти злиття під час rebase або злиття." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Адаптивний" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Перезапустити Poracode" @@ -9288,6 +9591,10 @@ msgstr "Продовжити" #~ msgid "Resume {label}" #~ msgstr "Відновити {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Продовжити завантаження" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Відновити мету" @@ -9340,6 +9647,10 @@ msgstr "Показати в Провіднику" msgid "Reveal in file tree" msgstr "Показати в дереві файлів" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Показати пароль" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Права панель" msgid "Right-click to switch ring" msgstr "Клацніть правою кнопкою, щоб перемкнути кільце" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Повернути область перегляду" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Налагодження середовища виконання" msgid "Runtime options" msgstr "Параметри середовища виконання" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Пошук агентів..." msgid "Search branches..." msgstr "Пошук гілок..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Пошук завантажень" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Пошук моделей..." msgid "Search or enter address" msgstr "Пошук або введення адреси" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Пошук паролів" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Шукати pull request" @@ -10029,7 +10357,7 @@ msgstr "Зафіксовано під час запуску цієї сесії #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "Показувати домашню область без проєкту msgid "Show all" msgstr "Показати все" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Показати панель закладок" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Показати панель закладок" @@ -10105,6 +10433,10 @@ msgstr "Показати Composer" msgid "Show context usage details" msgstr "Показати деталі використання контексту" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Показати панель пристроїв" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Показувати орієнтовну вартість" msgid "Show in file explorer" msgstr "Показати в провіднику файлів" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Показати в папці" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Рішення {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Рішення {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Деякі дані браузера було пропущено. Пропущено захищених елементів: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Деякі перевірки не пройдені або очікують виконання." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Деякі браузери Chromium захищають файли cookie та паролі за допомогою App-Bound Encryption. Poracode не обходить цей захист. Дані, до яких операційна система не надає доступу, буде пропущено." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Підтримка:" msgid "Supports Fast mode" msgstr "Підтримує швидкий режим" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Поміняти місцями нотатки та завдання" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "URL Tailscale" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Зробити знімок екрана" @@ -11392,6 +11745,11 @@ msgstr "Віддалений сервер цього проєкту офлайн msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Це видаляє наступні повідомлення та відновлює файли, якщо доступний знімок контрольної точки." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Збережений пароль для <0>{0} буде видалено з Poracode." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Видаляє розклад і його останній результат із цього пристрою." @@ -11739,6 +12097,10 @@ msgstr "Не вдалося скопіювати токен підключенн msgid "Unable to copy Tailscale URL." msgstr "Не вдалося скопіювати URL Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Не вдалося скопіювати пароль." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Не вдалося створити робоче дерево кандидата." @@ -11752,6 +12114,10 @@ msgstr "Не вдалося створити pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Не вдалося створити каталог worktree WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Не вдалося видалити збережений пароль." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Не вдалося видалити worktree." @@ -11760,6 +12126,18 @@ msgstr "Не вдалося видалити worktree." msgid "Unable to determine the experiment's source branch." msgstr "Не вдалося визначити вихідну гілку експерименту." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Не вдалося змінити збережений пароль." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Не вдалося знайти дані браузера для імпорту." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Не вдалося імпортувати дані браузера." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "Не вдалося встановити {0}." msgid "Unable to load diff." msgstr "Не вдалося завантажити відмінності." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Не вдалося завантажити список завантажень." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Не вдалося завантажити спарення віддаленого доступу." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Не вдалося завантажити збережені паролі." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Не удалось загрузить поток." @@ -11877,6 +12263,10 @@ msgstr "Не вдалося визначити коміт кандидата е msgid "Unable to restore the SSH connection." msgstr "Не вдалося відновити SSH-підключення." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Не вдалося показати пароль." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Не вдалося відкликати віддалену сесію." @@ -11890,6 +12280,10 @@ msgstr "Не вдалося зберегти облікові дані {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Не вдалося зберегти профіль Claude {displayLabel}." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Не вдалося зберегти пароль." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Не вдалося запустити {label}." @@ -11961,6 +12355,10 @@ msgstr "Не вдалося оновити віддалений доступ." msgid "Unable to update Tailscale HTTPS." msgstr "Не вдалося оновити Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Не вдалося оновити завантаження." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Не вдалося оновити публічний URL." @@ -12345,6 +12743,10 @@ msgstr "Використовується для {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Ім’я користувача" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Використовується вбудований список моделей Claude." @@ -12418,6 +12820,18 @@ msgstr "Відкрити список змін" msgid "View-only — this connection can't manage projects." msgstr "Лише перегляд — це підключення не може керувати проєктами." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Висота області перегляду" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Масштаб області перегляду" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Ширина області перегляду" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Веб-сервер" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Вебсайт" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Адреса вебсайту" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "У будні" @@ -12928,10 +13347,16 @@ msgstr "Ваші локальні зміни зі stash було повторн msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Масштаб" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Збільшити" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Зменшити" diff --git a/src/renderer/locales/vi/messages.po b/src/renderer/locales/vi/messages.po index 6293c13df..c366f3068 100644 --- a/src/renderer/locales/vi/messages.po +++ b/src/renderer/locales/vi/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30 ngày" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7 ngày" @@ -827,6 +831,10 @@ msgstr "Thêm vào" msgid "Add a local project" msgstr "Thêm một dự án cục bộ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "Thêm mật khẩu hoặc nhập từ một trình duyệt khác." + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "Thêm mô hình" msgid "Add one manually or import from another agent." msgstr "Thêm thủ công hoặc nhập từ một tác nhân khác." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "Thêm mật khẩu" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "Thêm mẫu" @@ -988,6 +1000,10 @@ msgstr "Thêm việc cần làm" msgid "Add workspace" msgstr "Thêm không gian làm việc" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "Nâng cao" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "Công bố một URL cơ sở tùy chỉnh, ví dụ một reverse proxy hoặc tunnel. Để trống để dùng tự động." @@ -1077,6 +1093,7 @@ msgstr "Các agent phân loại công việc được ủy quyền bằng thẻ #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "Các tác nhân phân loại công việc được ủy quyền bằng thẻ tác vụ. Các tuyến đã học phù hợp được xếp hạng đầu tiên, tiếp theo là mức sử dụng Crossagents toàn cục, mục yêu thích, mức sử dụng tác nhân thông thường và thứ tự tích hợp." +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "bị chặn:" msgid "Bold" msgstr "Đậm" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "Đánh dấu trang này" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "Dấu trang" @@ -1719,6 +1737,10 @@ msgstr "Đưa về bảng điều khiển" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "Đưa trở lại bảng điều khiển này, hoặc chuyển đến cửa sổ đang chạy." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "Chuyển mật khẩu và cookie đã lưu từ một trình duyệt đã cài đặt." + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "Trình duyệt đang mở trong một cửa sổ riêng" msgid "Browser MCP enabled for this thread" msgstr "Đã bật MCP trình duyệt cho chủ đề này" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "Trình đơn trình duyệt" @@ -1893,6 +1915,8 @@ msgstr "Không thể kết nối tới máy chủ từ xa. Hãy kiểm tra máy #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "Không thể kết nối tới máy chủ từ xa. Hãy kiểm tra máy msgid "Cancel" msgstr "Hủy bỏ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "Hủy tải xuống" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "Hủy tạo hồ sơ Claude mới" @@ -1932,6 +1960,7 @@ msgstr "đã hủy" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "Đã hủy" @@ -2128,6 +2157,10 @@ msgstr "Chọn một thư mục cha" msgid "Choose at least two candidates and enter a prompt." msgstr "Chọn ít nhất hai ứng viên và nhập lời nhắc." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "Chọn dữ liệu cần chuyển sang trình duyệt tích hợp." + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "Cây làm việc sạch" msgid "Cleanup script" msgstr "Tập lệnh dọn dẹp" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "Xóa dữ liệu duyệt web" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "Xóa lịch sử duyệt web" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "Xóa lịch sử duyệt web" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "Xóa bộ nhớ đệm" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "Xóa bộ nhớ đệm" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "Xóa cookie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "Xóa cookie" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "Xóa mục tiêu" @@ -2361,6 +2406,10 @@ msgstr "Đóng Crossagents" msgid "Close Crossagents panel" msgstr "Đóng bảng Crossagents" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "Đóng thanh công cụ thiết bị" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "Đóng trình chỉnh sửa" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "hoàn thành" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "Đã hoàn tất" @@ -2967,6 +3017,10 @@ msgstr "Điều khiển máy tính đã ghép nối khi agent nhấp hoặc gõ" msgid "Conversation" msgstr "Cuộc trò chuyện" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "Sao chép toàn bộ dữ liệu Lightcode vào Poracode. Poracode sẽ msgid "Copy code" msgstr "Sao chép mã" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "Sao chép URL hiện tại" @@ -3045,6 +3099,10 @@ msgstr "Sao chép liên kết ghép đôi" msgid "Copy pairing token" msgstr "Sao chép token ghép đôi" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "Sao chép mật khẩu" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "Sao chép đường dẫn" @@ -3554,6 +3612,7 @@ msgstr "Giao việc cho các tác nhân AI khác." #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "Xóa vĩnh viễn" msgid "Delete run" msgstr "Xóa lượt chạy" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "Xóa mật khẩu đã lưu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "Xóa mật khẩu đã lưu?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "Xóa lịch" @@ -3775,6 +3842,10 @@ msgstr "Máy chủ phát triển đang lắng nghe trên localhost của máy t msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "Ghi đè chỉ dành cho phát triển. Chỉ hiển thị trong bản dựng PORACODE DEV." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "Cấu hình thiết bị" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "Thiết bị được dùng cho nút nhập giọng nói của khung so msgid "Devices can pair while this desktop is reachable." msgstr "Thiết bị có thể ghép nối khi máy tính này có thể truy cập được." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "Kích thước:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "Cần kết nối trực tiếp" @@ -3964,10 +4039,27 @@ msgstr "Xong" msgid "Done · Waiting for your input" msgstr "Xong · Đang chờ bạn nhập" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "Tải xuống không thành công" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "Lịch sử tải xuống" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "Tải hình ảnh" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "Tiến trình tải xuống" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "Đang tải xuống" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "Đang tải xuống mẫu giọng nói..." msgid "Downloading… {0}%" msgstr "Đang tải xuống… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "Nội dung tải xuống" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "Các tệp tải xuống từ trình duyệt tích hợp được lưu trong lịch sử này." + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "Chỉnh sửa mục tiêu" msgid "Edit MCP server" msgstr "Chỉnh sửa máy chủ MCP" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "Chỉnh sửa mật khẩu" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "Chỉnh sửa hồ sơ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "Chỉnh sửa mật khẩu đã lưu" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "Tập tin cho {0}" msgid "Files matching these globs are hidden from the @file mention search." msgstr "Các tệp khớp với các mẫu glob này sẽ bị ẩn khỏi tìm kiếm @file." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "Các tệp bạn tải xuống sẽ xuất hiện tại đây" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "Điền tất cả đầu vào workflow bắt buộc." @@ -4700,6 +4812,11 @@ msgstr "Tìm trong cuộc trò chuyện" msgid "Find in diff" msgstr "Tìm trong diff" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "Tìm trong trang" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "Tìm trong terminal" @@ -4923,6 +5040,10 @@ msgstr "Tần suất" msgid "Friday at 4:00 PM" msgstr "Thứ Sáu lúc 4:00 CH" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "Nguồn" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "Làm mờ thanh bên bằng hiệu ứng làm mờ của hệ thống (vibrancy), gợi nhớ macOS gần đây. Sẽ dùng lớp màu mờ trong suốt nếu không được hỗ trợ." @@ -5185,7 +5306,7 @@ msgstr "handle" msgid "Handle" msgstr "Handle" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "Tải lại cứng" @@ -5249,6 +5370,10 @@ msgstr "Ẩn các mô hình bạn không sử dụng khỏi bộ chọn mô hìn msgid "Hide panel" msgstr "Ẩn bảng điều khiển" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "Ẩn mật khẩu" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "Ẩn bảng gỡ lỗi thời gian chạy" @@ -5272,7 +5397,7 @@ msgstr "Ẩn skill từ {0}" msgid "Hide terminal" msgstr "Ẩn thiết bị đầu cuối" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "Lịch sử" @@ -5317,10 +5442,18 @@ msgstr "Hằng giờ" msgid "HTTP (streamable)" msgstr "HTTP (hỗ trợ truyền phát)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "Tôi hiểu rằng dữ liệu trình duyệt được bảo vệ có thể bị bỏ qua." + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "Hình ảnh" msgid "Image preview" msgstr "Xem trước hình ảnh" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "Nhập" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "Nhập lại" @@ -5362,10 +5499,23 @@ msgstr "Nhập lại" msgid "Import and restart" msgstr "Nhập và khởi động lại" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "Nhập dữ liệu trình duyệt" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "Xung đột khi nhập" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "Nhập cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "Nhập cookie và mật khẩu" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "Nhập skill của tác tử bên ngoài" msgid "Import external skills" msgstr "Nhập skill bên ngoài" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "Nhập từ trình duyệt của bạn" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "Nhập dữ liệu Lightcode" @@ -5399,10 +5553,18 @@ msgstr "Nhập máy chủ MCP" msgid "Import method" msgstr "Phương thức nhập" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "Nhập mật khẩu" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "Nhập mục đã chọn" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "Nguồn nhập" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "Nhập vào {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "Đã nhập {0} skill." msgid "Imported 1 skill." msgstr "Đã nhập 1 skill." +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "Đã nhập từ {0}" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "Mật khẩu đã nhập: {0}. Cookie đã nhập: {1}." + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "Cải thiện" @@ -5662,6 +5835,7 @@ msgstr "Hướng dẫn mà các tác nhân tuân theo khi chọn tác nhân ho #~ msgid "Interrupt" #~ msgstr "Ngắt" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "Bị gián đoạn" @@ -5670,6 +5844,26 @@ msgstr "Bị gián đoạn" msgid "Interrupted: agent session ended before completion." msgstr "Bị gián đoạn: phiên tác nhân đã kết thúc trước khi hoàn thành." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "Nghiêng" @@ -5769,6 +5963,14 @@ msgstr "Ngôn ngữ cho thông điệp commit và tóm tắt pull request do AI msgid "Language the speech model should expect when transcribing composer dictation." msgstr "Ngôn ngữ mà mô hình giọng nói cần dùng khi phiên âm lời đọc trong trình soạn thảo." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "Lần đánh giá gần nhất:" @@ -5996,6 +6198,10 @@ msgstr "Cục bộ" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "Các thay đổi cục bộ cần được lưu tạm trước khi kéo từ {branch}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "Bảo vệ mật khẩu cục bộ" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "Chuỗi ngày dài nhất" msgid "Longest task" msgstr "Tác vụ dài nhất" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "Đang tìm hồ sơ trình duyệt…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "Đang tìm máy chủ phát triển…" @@ -6082,6 +6292,10 @@ msgstr "Làm thanh bên trong suốt — hiệu ứng làm mờ của hệ thố #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "Quản lý skill tác tử toàn cục và theo dự án, bao gồm cả skill được nhập từ nhà cung cấp khác." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "Quản lý mật khẩu" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "Quản lý môi trường từ xa" @@ -6840,8 +7054,8 @@ msgstr "Lịch mới" #~ msgstr "Tác vụ đã lên lịch mới" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "Tab mới" @@ -7084,6 +7298,10 @@ msgstr "Không có sự khác biệt nào cho {0}" msgid "No diff for {visiblePath}" msgstr "Không có sự khác biệt đối với {visiblePath}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "Không có bản tải xuống nào khớp với tìm kiếm của bạn" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "Không tìm thấy skill bên ngoài trong phạm vi này." @@ -7107,7 +7325,7 @@ msgstr "Không tìm thấy tài khoản GitHub CLI nào. Đăng nhập bằng <0 #~ msgid "No goal for this thread." #~ msgstr "Luồng này chưa có mục tiêu." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "Chưa có lịch sử" @@ -7202,6 +7420,10 @@ msgstr "Không có tác nhân nào khác được cài đặt" msgid "No paired devices yet." msgstr "Chưa có thiết bị nào được ghép đôi." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "Không có mật khẩu nào khớp với tìm kiếm của bạn" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "Không có mẫu." @@ -7275,6 +7497,10 @@ msgstr "Chưa có lần chạy nào." msgid "No runtime items yet for this thread." msgstr "Chưa có mục thời gian chạy nào cho luồng này." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "Không có mật khẩu đã lưu" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "Chưa có máy chủ nào được kết nối." @@ -7316,6 +7542,10 @@ msgstr "Không có thay đổi nào được đưa vào staging" msgid "No supported agents detected" msgstr "Không phát hiện thấy tác nhân được hỗ trợ nào" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "Không tìm thấy hồ sơ trình duyệt được hỗ trợ nào trên thiết bị này." + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "Không có thẻ — mục này sẽ không còn ảnh hưởng đến độ ưu tiên theo thẻ." @@ -7601,6 +7831,10 @@ msgstr "Mở Crossagent: {0}" msgid "Open dialog" msgstr "Mở hộp thoại" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "Mở tệp đã tải xuống" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "Mở thử nghiệm" @@ -7981,9 +8215,36 @@ msgid "Passphrase (optional)" msgstr "Cụm mật khẩu (không bắt buộc)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "Mật khẩu" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "Đã sao chép mật khẩu." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "Trình quản lý mật khẩu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "Phần trình quản lý mật khẩu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "Mật khẩu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "Mật khẩu và tự động điền" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "Mật khẩu được mã hóa trong kho thông tin xác thực cục bộ của Poracode. Giá trị mật khẩu chỉ được yêu cầu khi bạn hiển thị, sao chép hoặc chỉnh sửa chúng." + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8025,11 +8286,16 @@ msgstr "Tạm dừng" #~ msgid "Pause {0}" #~ msgstr "Tạm dừng {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "Tạm dừng tải xuống" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "Tạm dừng mục tiêu" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "Đã tạm dừng" @@ -8132,6 +8398,10 @@ msgstr "Đã ghim" msgid "Pinned task routes" msgstr "Tuyến tác vụ đã ghim" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8362,6 +8632,10 @@ msgstr "Các lần chạy trước" msgid "Previous tab" msgstr "Tab trước" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "In" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "Chính sách quyền riêng tư" @@ -8476,6 +8750,10 @@ msgstr "Prompt" msgid "Proposed plan" msgstr "Kế hoạch đề xuất" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "Dữ liệu trình duyệt được bảo vệ" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "Do Poracode cung cấp và quản lý cho các tác nhân được hỗ trợ." @@ -8986,6 +9264,7 @@ msgid "Remove action" msgstr "Xóa hành động" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "Xóa dấu trang" @@ -9011,6 +9290,10 @@ msgstr "Xóa khóa API Cursor SDK" msgid "Remove environment variable" msgstr "Xóa biến môi trường" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "Xóa khỏi lịch sử tải xuống" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "Xóa khỏi mục yêu thích" @@ -9204,6 +9487,10 @@ msgstr "Đặt lại độ mờ thanh bên về mặc định" msgid "Reset to default" msgstr "Đặt lại về mặc định" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "Đặt lại mức thu phóng" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Thay đổi kích thước ngăn trình duyệt" @@ -9246,11 +9533,27 @@ msgstr "Thay đổi kích thước phần chia" msgid "Resize terminal panel" msgstr "Thay đổi kích thước bảng điều khiển thiết bị đầu cuối" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "Thay đổi kích thước khung nhìn" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "Thay đổi chiều cao khung nhìn" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "Thay đổi chiều rộng khung nhìn" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "Giải quyết xung đột hợp nhất trong quá trình rebase hoặc hợp nhất." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "Đáp ứng" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "Khởi động lại Poracode" @@ -9288,6 +9591,10 @@ msgstr "Tiếp tục" #~ msgid "Resume {label}" #~ msgstr "Tiếp tục {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "Tiếp tục tải xuống" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "Tiếp tục mục tiêu" @@ -9340,6 +9647,10 @@ msgstr "Hiển thị trong File Explorer" msgid "Reveal in file tree" msgstr "Hiển thị trong cây tập tin" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "Hiển thị mật khẩu" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9441,6 +9752,10 @@ msgstr "Bảng bên phải" msgid "Right-click to switch ring" msgstr "Nhấp chuột phải để đổi vòng" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "Xoay khung nhìn" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9589,11 +9904,16 @@ msgstr "Gỡ lỗi thời gian chạy" msgid "Runtime options" msgstr "Tùy chọn thời gian chạy" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9776,6 +10096,10 @@ msgstr "Tìm kiếm tác nhân..." msgid "Search branches..." msgstr "Tìm kiếm nhánh..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "Tìm kiếm bản tải xuống" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9809,6 +10133,10 @@ msgstr "Tìm kiếm mô hình..." msgid "Search or enter address" msgstr "Tìm kiếm hoặc nhập địa chỉ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "Tìm kiếm mật khẩu" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "Tìm kiếm pull request" @@ -10029,7 +10357,7 @@ msgstr "Được cố định khi phiên này bắt đầu — hãy tạo luồn #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10079,11 +10407,11 @@ msgstr "Hiển thị phạm vi Trang chủ không có dự án cho các phiên t msgid "Show all" msgstr "Hiển thị tất cả" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "Hiển thị thanh dấu trang" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "Hiển thị thanh dấu trang" @@ -10105,6 +10433,10 @@ msgstr "Hiển thị trình soạn thảo" msgid "Show context usage details" msgstr "Hiển thị chi tiết sử dụng ngữ cảnh" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "Hiển thị thanh công cụ thiết bị" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10119,6 +10451,10 @@ msgstr "Hiển thị chi phí ước tính" msgid "Show in file explorer" msgstr "Hiển thị trong trình duyệt tệp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "Hiển thị trong thư mục" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10400,11 +10736,20 @@ msgstr "Giải pháp {0}" #~ msgid "Solution {ordinal}" #~ msgstr "Giải pháp {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "Một số dữ liệu trình duyệt đã bị bỏ qua. Số mục được bảo vệ bị bỏ qua: {0}." + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "Một số kiểm tra không thành công hoặc đang chờ xử lý." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "Một số trình duyệt Chromium bảo vệ cookie và mật khẩu bằng App-Bound Encryption. Poracode không vượt qua cơ chế bảo vệ đó. Dữ liệu mà hệ điều hành không cung cấp sẽ bị bỏ qua." + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10844,6 +11189,14 @@ msgstr "Hỗ trợ:" msgid "Supports Fast mode" msgstr "Hỗ trợ chế độ nhanh" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "Hoán đổi ghi chú và việc cần làm" @@ -10964,7 +11317,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "URL Tailscale" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "Chụp ảnh màn hình" @@ -11392,6 +11745,11 @@ msgstr "Máy chủ từ xa của dự án này đang ngoại tuyến. Hãy kết msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "Thao tác này sẽ xóa các tin nhắn sau này và khôi phục các tệp khi có ảnh chụp nhanh điểm kiểm tra." +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "Thao tác này sẽ xóa mật khẩu đã lưu cho <0>{0} khỏi Poracode." + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "Thao tác này xóa lịch biểu và kết quả mới nhất khỏi thiết bị này." @@ -11739,6 +12097,10 @@ msgstr "Không thể sao chép token ghép đôi." msgid "Unable to copy Tailscale URL." msgstr "Không thể sao chép URL Tailscale." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "Không thể sao chép mật khẩu." + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "Không thể tạo cây làm việc cho ứng viên." @@ -11752,6 +12114,10 @@ msgstr "Không thể tạo pull request: {0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "Không thể tạo thư mục cây làm việc WSL \"{path}\"" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "Không thể xóa mật khẩu đã lưu." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "Không thể xóa worktree." @@ -11760,6 +12126,18 @@ msgstr "Không thể xóa worktree." msgid "Unable to determine the experiment's source branch." msgstr "Không thể xác định nhánh nguồn của thử nghiệm." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "Không thể chỉnh sửa mật khẩu đã lưu." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "Không thể tìm thấy dữ liệu trình duyệt để nhập." + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "Không thể nhập dữ liệu trình duyệt." + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11774,10 +12152,18 @@ msgstr "Không thể cài đặt {0}." msgid "Unable to load diff." msgstr "Không thể tải phần khác biệt." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "Không thể tải nội dung tải xuống." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "Không thể tải ghép đôi truy cập từ xa." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "Không thể tải các mật khẩu đã lưu." + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "Không thể tải luồng." @@ -11877,6 +12263,10 @@ msgstr "Không thể xác định commit của ứng viên thử nghiệm" msgid "Unable to restore the SSH connection." msgstr "Không thể khôi phục kết nối SSH." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "Không thể hiển thị mật khẩu." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "Không thể thu hồi phiên từ xa." @@ -11890,6 +12280,10 @@ msgstr "Không thể lưu thông tin xác thực {0}." msgid "Unable to save Claude {displayLabel} profile." msgstr "Không thể lưu hồ sơ Claude {displayLabel}." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "Không thể lưu mật khẩu." + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "Không thể khởi động {label}." @@ -11961,6 +12355,10 @@ msgstr "Không thể cập nhật quyền truy cập từ xa." msgid "Unable to update Tailscale HTTPS." msgstr "Không thể cập nhật Tailscale HTTPS." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "Không thể cập nhật nội dung tải xuống." + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "Không thể cập nhật URL công khai." @@ -12345,6 +12743,10 @@ msgstr "Đã được dùng bởi {conflict}" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "Tên người dùng" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "Đang dùng danh sách mô hình Claude tích hợp sẵn." @@ -12418,6 +12820,18 @@ msgstr "Xem nhật ký thay đổi" msgid "View-only — this connection can't manage projects." msgstr "Chỉ xem — kết nối này không thể quản lý dự án." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "Chiều cao khung nhìn" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "Tỷ lệ khung nhìn" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "Chiều rộng khung nhìn" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12560,11 +12974,16 @@ msgid "Web server" msgstr "Máy chủ web" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "Trang web" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "Nguồn trang web" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "Ngày trong tuần" @@ -12928,10 +13347,16 @@ msgstr "Các thay đổi cục bộ trong stash của bạn đã được áp d msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "Thu phóng" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "Phóng to" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "Thu nhỏ" diff --git a/src/renderer/locales/zh-CN/messages.po b/src/renderer/locales/zh-CN/messages.po index b6c0fa73a..78818d925 100644 --- a/src/renderer/locales/zh-CN/messages.po +++ b/src/renderer/locales/zh-CN/messages.po @@ -674,6 +674,10 @@ msgstr "22" msgid "30d" msgstr "30天" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "4K" +msgstr "4K" + #: src/renderer/views/SettingsOverlay/parts/ProfileSettings.tsx msgid "7d" msgstr "7天" @@ -827,6 +831,10 @@ msgstr "添加" msgid "Add a local project" msgstr "添加本地项目" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add a password or import one from another browser." +msgstr "添加密码或从其他浏览器导入。" + #: src/mobile/setupEmptyState.tsx #: src/mobile/views/ManageProjectsView.tsx #: src/renderer/views/QuickComposerOverlay/QuickComposerOverlay.tsx @@ -919,6 +927,10 @@ msgstr "添加模型" msgid "Add one manually or import from another agent." msgstr "手动添加一个,或从其他代理导入。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Add password" +msgstr "添加密码" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "Add pattern" msgstr "添加模式" @@ -988,6 +1000,10 @@ msgstr "添加待办事项" msgid "Add workspace" msgstr "添加工作区" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Advanced" +msgstr "高级" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Advertise a custom base URL, e.g. a reverse proxy or tunnel. Leave empty for automatic." msgstr "公布自定义基础 URL,例如反向代理或隧道。留空则自动设置。" @@ -1077,6 +1093,7 @@ msgstr "代理使用任务标签对委派的工作进行分类。手动任务路 #~ msgid "Agents classify delegated work with task tags. Matching learned routes rank first, followed by global Crossagents usage, favorites, normal agent usage, and built-in order." #~ msgstr "智能体使用任务标签对委派的工作进行分类。匹配的已学习路由优先排序,之后依次是 Crossagents 全局使用情况、收藏项、普通智能体使用情况和内置顺序。" +#. Settings section: AI / assistant configuration #. Settings section: AI / assistant configuration #: src/mobile/settingsSections.ts #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx @@ -1661,11 +1678,12 @@ msgstr "已阻止:" msgid "Bold" msgstr "加粗" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Bookmark this page" msgstr "将此页加入书签" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Bookmarks" msgstr "书签" @@ -1719,6 +1737,10 @@ msgstr "移回面板" msgid "Bring it back into this panel, or jump to the window it’s running in." msgstr "将其移回此面板,或跳转到正在运行它的窗口。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Bring saved passwords and cookies over from an installed browser." +msgstr "从已安装的浏览器导入已保存的密码和 Cookie。" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/ThreadContextMenu.tsx @@ -1772,7 +1794,7 @@ msgstr "浏览器已在单独的窗口中打开" msgid "Browser MCP enabled for this thread" msgstr "为此线程启用浏览器 MCP" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Browser menu" msgstr "浏览器菜单" @@ -1893,6 +1915,8 @@ msgstr "无法连接远程服务器。请确认其处于在线状态,然后重 #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx #: src/renderer/views/MainView/parts/PullFromSourceDialog.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/DeleteWorktreeDialog.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/PrReviewOverlay/parts/SubmitReviewPopover.tsx @@ -1904,6 +1928,10 @@ msgstr "无法连接远程服务器。请确认其处于在线状态,然后重 msgid "Cancel" msgstr "取消" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Cancel download" +msgstr "取消下载" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Cancel new Claude profile" msgstr "取消新的 Claude 配置文件" @@ -1932,6 +1960,7 @@ msgstr "已取消" #: src/renderer/components/thread/ThreadGoalDock.tsx #: src/renderer/utils/prStatus.ts #: src/renderer/views/GitHubActionsView/GitHubActionsRunList.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Cancelled" msgstr "已取消" @@ -2128,6 +2157,10 @@ msgstr "选择父文件夹" msgid "Choose at least two candidates and enter a prompt." msgstr "请选择至少两个候选项并输入提示。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Choose data to bring over to the built-in browser." +msgstr "选择要导入内置浏览器的数据。" + #: src/renderer/views/SettingsOverlay/parts/GitSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Choose how Poracode performs manual merges and automatic PR merges." @@ -2235,22 +2268,34 @@ msgstr "干净的工作树" msgid "Cleanup script" msgstr "清理脚本" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear browsing data" +msgstr "清除浏览数据" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear browsing history" msgstr "清除浏览历史记录" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Clear Browsing History" msgstr "清除浏览历史记录" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cache" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cache" msgstr "清除缓存" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx -msgid "Clear Cookies" +#~ msgid "Clear Cache" +#~ msgstr "清除缓存" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Clear cookies" msgstr "清除 Cookie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#~ msgid "Clear Cookies" +#~ msgstr "清除 Cookie" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Clear goal" msgstr "清除目标" @@ -2361,6 +2406,10 @@ msgstr "关闭 Crossagents" msgid "Close Crossagents panel" msgstr "关闭 Crossagents 面板" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Close device toolbar" +msgstr "关闭设备工具栏" + #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx msgid "Close editor" msgstr "关闭编辑器" @@ -2722,6 +2771,7 @@ msgid "completed" msgstr "已完成" #: src/renderer/utils/prStatus.ts +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx msgid "Completed" msgstr "已完成" @@ -2967,6 +3017,10 @@ msgstr "代理点击或输入时控制已配对的桌面" msgid "Conversation" msgstr "对话" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Cookies" +msgstr "Cookie" + #: src/mobile/UserMessageActionsSheet.tsx #: src/mobile/views/PortsView.tsx #: src/renderer/components/thread/ChatPane/parts/items/CopyTextButton.tsx @@ -3002,7 +3056,7 @@ msgstr "将所有 Lightcode 数据复制到 Poracode。Poracode 将重新启动 msgid "Copy code" msgstr "复制代码" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Copy Current URL" msgstr "复制当前网址" @@ -3045,6 +3099,10 @@ msgstr "复制配对链接" msgid "Copy pairing token" msgstr "复制配对令牌" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Copy password" +msgstr "复制密码" + #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx msgid "Copy Path" msgstr "复制路径" @@ -3554,6 +3612,7 @@ msgstr "将工作委派给其他 AI 智能体。" #: src/renderer/components/thread/ChatPane/parts/items/toolDisplay.ts #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/parts/TreeEntryRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/TodoRow.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx @@ -3610,6 +3669,14 @@ msgstr "永久删除" msgid "Delete run" msgstr "删除运行" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password" +msgstr "删除已保存的密码" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Delete saved password?" +msgstr "删除已保存的密码?" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx msgid "Delete schedule" msgstr "删除计划" @@ -3775,6 +3842,10 @@ msgstr "监听桌面端 localhost 的开发服务器。" msgid "Development-only overrides. Only visible in the PORACODE DEV build." msgstr "仅开发覆盖。仅在 PORACODE DEV 版本中可见。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Device preset" +msgstr "设备预设" + #: src/renderer/views/SettingsOverlay/parts/AudioSettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Device used by the composer voice input button." @@ -3784,6 +3855,10 @@ msgstr "输入框语音输入按钮使用的设备。" msgid "Devices can pair while this desktop is reachable." msgstr "当此桌面可访问时,设备可以配对。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Dimensions:" +msgstr "尺寸:" + #: src/mobile/views/PortsView.tsx #~ msgid "Direct connection required" #~ msgstr "需要直接连接" @@ -3964,10 +4039,27 @@ msgstr "完成" msgid "Done · Waiting for your input" msgstr "完成 · 等待您的输入" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download failed" +msgstr "下载失败" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Download history" +msgstr "下载记录" + #: src/renderer/components/thread/ChatPane/parts/items/ImageCard.tsx msgid "Download image" msgstr "下载图片" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Download progress" +msgstr "下载进度" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloading" +msgstr "正在下载" + #. placeholder {0}: name || t`the chosen folder` #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Downloading into “{0}”. This can take a moment for large repositories." @@ -3993,6 +4085,14 @@ msgstr "正在下载语音模型..." msgid "Downloading… {0}%" msgstr "正在下载… {0}%" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Downloads" +msgstr "下载内容" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Downloads from the built-in browser are saved in this history." +msgstr "内置浏览器中的下载内容会保存在此记录中。" + #: src/renderer/views/PrReviewOverlay/parts/PrMetaRow.tsx #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Draft" @@ -4055,10 +4155,18 @@ msgstr "编辑目标" msgid "Edit MCP server" msgstr "编辑 MCP 服务器" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit password" +msgstr "编辑密码" + #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx msgid "Edit profile" msgstr "编辑资料" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Edit saved password" +msgstr "编辑已保存的密码" + #: src/renderer/views/SchedulesView/parts/ScheduleRow.tsx #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Edit schedule" @@ -4662,6 +4770,10 @@ msgstr "{0}的文件" msgid "Files matching these globs are hidden from the @file mention search." msgstr "与这些 glob 匹配的文件在 @file 提及搜索中隐藏。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Files you download appear here" +msgstr "您下载的文件会显示在这里" + #: src/renderer/views/GitHubActionsView/GitHubActionsDispatchPopover.tsx msgid "Fill in all required workflow inputs." msgstr "请填写所有必需的工作流输入。" @@ -4700,6 +4812,11 @@ msgstr "在聊天中查找" msgid "Find in diff" msgstr "在差异中查找" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Find in page" +msgstr "在页面中查找" + #: src/renderer/components/terminal/XTermSurface.tsx msgid "Find in terminal" msgstr "在终端中查找" @@ -4923,6 +5040,10 @@ msgstr "频率" msgid "Friday at 4:00 PM" msgstr "星期五下午 4:00" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "From" +msgstr "来源" + #: src/renderer/views/SettingsOverlay/parts/AppearanceSettings.tsx msgid "Frost the sidebar with the system blur material (vibrancy), echoing recent macOS. Falls back to a translucent tint where unsupported." msgstr "使用系统模糊材质(vibrancy)为侧边栏添加磨砂效果,呼应新版macOS。不支持时回退为半透明着色。" @@ -5185,7 +5306,7 @@ msgstr "用户名" msgid "Handle" msgstr "用户名" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Hard Reload" msgstr "硬重载" @@ -5249,6 +5370,10 @@ msgstr "从每个提供商的模型选择器中隐藏您不使用的模型。" msgid "Hide panel" msgstr "隐藏面板" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Hide password" +msgstr "隐藏密码" + #: src/renderer/components/thread/ThreadView.tsx msgid "Hide runtime debug panel" msgstr "隐藏运行时调试面板" @@ -5272,7 +5397,7 @@ msgstr "隐藏来自 {0} 的技能" msgid "Hide terminal" msgstr "隐藏终端" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "History" msgstr "历史记录" @@ -5317,10 +5442,18 @@ msgstr "每小时" msgid "HTTP (streamable)" msgstr "HTTP(流式)" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "https://example.com" +msgstr "https://example.com" + #: src/renderer/components/mcp/McpServerEditor.tsx msgid "https://example.com/mcp" msgstr "https://example.com/mcp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "I understand that protected browser data may be skipped." +msgstr "我了解受保护的浏览器数据可能会被跳过。" + #. placeholder {0}: agent.id #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "ID: {0}" @@ -5354,6 +5487,10 @@ msgstr "图片" msgid "Image preview" msgstr "图片预览" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import" +msgstr "导入" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import again" msgstr "再次导入" @@ -5362,10 +5499,23 @@ msgstr "再次导入" msgid "Import and restart" msgstr "导入并重新启动" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import browser data" +msgstr "导入浏览器数据" + #: src/renderer/components/skills/SkillsManager.tsx msgid "Import conflict" msgstr "导入冲突" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import cookies" +msgstr "导入 Cookie" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Import cookies and passwords" +msgstr "导入 Cookie 和密码" + #: src/renderer/components/mcp/McpExternalImportModal.tsx #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import destination" @@ -5383,6 +5533,10 @@ msgstr "导入外部智能体技能" msgid "Import external skills" msgstr "导入外部技能" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import from your browser" +msgstr "从浏览器导入" + #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx msgid "Import Lightcode data" msgstr "导入 Lightcode 数据" @@ -5399,10 +5553,18 @@ msgstr "导入 MCP 服务器" msgid "Import method" msgstr "导入方式" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import passwords" +msgstr "导入密码" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "Import selected" msgstr "导入所选项" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Import source" +msgstr "导入来源" + #: src/renderer/components/mcp/McpExternalImportModal.tsx msgid "Import to {destinationProjectName}" msgstr "导入到 {destinationProjectName}" @@ -5430,6 +5592,17 @@ msgstr "已导入 {0} 个技能。" msgid "Imported 1 skill." msgstr "已导入 1 个技能。" +#. placeholder {0}: credential.source +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Imported from {0}" +msgstr "已从 {0} 导入" + +#. placeholder {0}: result.passwordsImported +#. placeholder {1}: result.cookiesImported +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Imported passwords: {0}. Imported cookies: {1}." +msgstr "已导入密码:{0}。已导入 Cookie:{1}。" + #: src/renderer/components/changelog/ChangelogView.tsx msgid "Improved" msgstr "已改进" @@ -5662,6 +5835,7 @@ msgstr "代理在选择要委派给哪个代理或模型时遵循的说明。" #~ msgid "Interrupt" #~ msgstr "中断" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/parts/PreviousRunsModal.tsx msgid "Interrupted" msgstr "已中断" @@ -5670,6 +5844,26 @@ msgstr "已中断" msgid "Interrupted: agent session ended before completion." msgstr "已中断:代理会话在完成之前结束。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Air" +msgstr "iPad Air" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPad Mini" +msgstr "iPad Mini" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro" +msgstr "iPhone 15 Pro" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone 15 Pro Max" +msgstr "iPhone 15 Pro Max" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "iPhone SE" +msgstr "iPhone SE" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesEditor.tsx msgid "Italic" msgstr "斜体" @@ -5769,6 +5963,14 @@ msgstr "AI 生成的提交信息和拉取请求摘要所使用的语言。线程 msgid "Language the speech model should expect when transcribing composer dictation." msgstr "语音模型在转录编辑器口述输入时应预期的语言。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop" +msgstr "Laptop" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Laptop L" +msgstr "Laptop L" + #: src/renderer/components/thread/ThreadGoalDock.tsx msgid "Last evaluation:" msgstr "上次评估:" @@ -5996,6 +6198,10 @@ msgstr "本地" msgid "Local changes need to be stashed before pulling from {branch}" msgstr "从 {branch} 拉取之前需要暂存(stash)本地更改" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Local password protection" +msgstr "本地密码保护" + #. placeholder {0}: forward.targetPort #: src/mobile/views/PortsView.tsx msgid "localhost:{0} on desktop" @@ -6066,6 +6272,10 @@ msgstr "最长连续天数" msgid "Longest task" msgstr "最长任务" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Looking for browser profiles…" +msgstr "正在查找浏览器配置文件…" + #: src/mobile/views/PortsView.tsx msgid "Looking for dev servers…" msgstr "正在查找开发服务器…" @@ -6082,6 +6292,10 @@ msgstr "将侧边栏设为半透明——在Windows 11上使用系统模糊材 #~ msgid "Manage global and project agent skills, including skills imported from other providers." #~ msgstr "管理全局和项目智能体技能,包括从其他提供商导入的技能。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Manage passwords" +msgstr "管理密码" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarRemoteServers.tsx #~ msgid "Manage remote environments" #~ msgstr "管理远程环境" @@ -6839,8 +7053,8 @@ msgstr "新建计划" #~ msgstr "新建计划任务" #: src/mobile/views/BrowserView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "New tab" msgstr "新标签页" @@ -7083,6 +7297,10 @@ msgstr "{0}没有可用的差异" msgid "No diff for {visiblePath}" msgstr "{visiblePath}没有差异" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "No downloads match your search" +msgstr "没有与搜索匹配的下载内容" + #: src/renderer/components/skills/SkillImportModal.tsx msgid "No external skills were found in this scope." msgstr "在此范围内未找到外部技能。" @@ -7106,7 +7324,7 @@ msgstr "未找到 GitHub CLI 帐户。使用<0>gh auth login登录,或<1> #~ msgid "No goal for this thread." #~ msgstr "此线程没有目标。" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "No history yet" msgstr "暂无历史记录" @@ -7201,6 +7419,10 @@ msgstr "没有安装其他代理" msgid "No paired devices yet." msgstr "还没有已配对的设备。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No passwords match your search" +msgstr "没有与搜索匹配的密码" + #: src/renderer/views/SettingsOverlay/parts/SearchExcludeBody.tsx msgid "No patterns." msgstr "没有模式。" @@ -7274,6 +7496,10 @@ msgstr "尚无运行记录。" msgid "No runtime items yet for this thread." msgstr "该线程还没有运行时项目。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "No saved passwords" +msgstr "没有已保存的密码" + #: src/renderer/views/SettingsOverlay/parts/RemoteServersSettings.tsx #~ msgid "No servers connected yet." #~ msgstr "尚未连接任何服务器。" @@ -7315,6 +7541,10 @@ msgstr "没有暂存的更改" msgid "No supported agents detected" msgstr "未检测到支持的代理" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "No supported browser profiles were found on this device." +msgstr "在此设备上未找到受支持的浏览器配置文件。" + #: src/renderer/views/SettingsOverlay/parts/crossagent/CrossagentMemorySection.tsx msgid "No tags — the entry stops influencing tag affinity." msgstr "无标签 - 此条目不再影响标签亲和性。" @@ -7600,6 +7830,10 @@ msgstr "打开 Crossagent:{0}" msgid "Open dialog" msgstr "打开对话框" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Open downloaded file" +msgstr "打开已下载的文件" + #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx msgid "Open experiment" msgstr "打开实验" @@ -7980,9 +8214,36 @@ msgid "Passphrase (optional)" msgstr "密码短语(可选)" #: src/mobile/views/DesktopsView.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx msgid "Password" msgstr "密码" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password copied." +msgstr "密码已复制。" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +msgid "Password manager" +msgstr "密码管理器" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Password manager section" +msgstr "密码管理器部分" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords" +msgstr "密码" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Passwords and autofill" +msgstr "密码和自动填充" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Passwords are encrypted in Poracode's local credential store. Password values are requested only when you reveal, copy, or edit them." +msgstr "密码在 Poracode 的本地凭据存储中加密。只有在您显示、复制或编辑密码时,才会请求密码值。" + #: src/renderer/commands/shortcutCatalog.ts #: src/renderer/components/terminal/XTermSurface.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -8024,11 +8285,16 @@ msgstr "暂停" #~ msgid "Pause {0}" #~ msgstr "暂停 {0}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Pause download" +msgstr "暂停下载" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Pause goal" msgstr "暂停目标" #: src/renderer/components/thread/ThreadGoalDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "Paused" msgstr "已暂停" @@ -8131,6 +8397,10 @@ msgstr "已固定" msgid "Pinned task routes" msgstr "已固定的任务路由" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Pixel 8" +msgstr "Pixel 8" + #: src/mobile/ComposerInfoChips.tsx #: src/renderer/components/layout/UnifiedRightPanel.tsx #: src/renderer/components/providers/composerControlBuilders.tsx @@ -8361,6 +8631,10 @@ msgstr "历史运行" msgid "Previous tab" msgstr "上一个选项卡" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Print" +msgstr "打印" + #: src/mobile/views/MoreView.tsx msgid "Privacy Policy" msgstr "隐私政策" @@ -8475,6 +8749,10 @@ msgstr "提示" msgid "Proposed plan" msgstr "拟议计划" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Protected browser data" +msgstr "受保护的浏览器数据" + #: src/renderer/components/mcp/McpServersManager.tsx msgid "Provided and managed by Poracode for supported agents." msgstr "由 Poracode 为受支持的代理提供和管理。" @@ -8985,6 +9263,7 @@ msgid "Remove action" msgstr "删除动作" #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Remove bookmark" msgstr "移除书签" @@ -9010,6 +9289,10 @@ msgstr "移除 Cursor SDK API 密钥" msgid "Remove environment variable" msgstr "移除环境变量" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Remove from download history" +msgstr "从下载记录中移除" + #: src/renderer/components/common/ProviderModelMenu/ProviderModelMenu.tsx msgid "Remove from favorites" msgstr "从收藏夹中删除" @@ -9203,6 +9486,10 @@ msgstr "将侧边栏磨砂效果重置为默认" msgid "Reset to default" msgstr "重置为默认" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Reset zoom" +msgstr "重置缩放" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "调整浏览器抽屉的大小" @@ -9245,11 +9532,27 @@ msgstr "调整分割大小" msgid "Resize terminal panel" msgstr "调整终端面板大小" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport" +msgstr "调整视口大小" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport height" +msgstr "调整视口高度" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabWebview.tsx +msgid "Resize viewport width" +msgstr "调整视口宽度" + #: src/renderer/views/SettingsOverlay/parts/AISettings.tsx #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts msgid "Resolves merge conflicts during rebase or merge." msgstr "解决变基或合并期间的合并冲突。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Responsive" +msgstr "响应式" + #: src/renderer/components/startup/StartupRecoveryScreen.tsx msgid "Restart Poracode" msgstr "重新启动 Poracode" @@ -9287,6 +9590,10 @@ msgstr "继续" #~ msgid "Resume {label}" #~ msgstr "继续 {label}" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Resume download" +msgstr "继续下载" + #: src/renderer/components/thread/ThreadGoalControls.tsx msgid "Resume goal" msgstr "继续目标" @@ -9339,6 +9646,10 @@ msgstr "在文件资源管理器中显示" msgid "Reveal in file tree" msgstr "在文件树中显示" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Reveal password" +msgstr "显示密码" + #: src/renderer/components/thread/ChatPane/parts/CheckpointRevertControls.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/FileRow.tsx #: src/renderer/views/GitReviewOverlay/parts/GitStackedDiff.tsx @@ -9440,6 +9751,10 @@ msgstr "右面板" msgid "Right-click to switch ring" msgstr "右键点击以切换环" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Rotate viewport" +msgstr "旋转视口" + #: src/mobile/views/threadActionSurfaces.tsx #: src/mobile/views/threadContextMenus.tsx #: src/renderer/components/thread/ChatPane/parts/items/commandSummary.ts @@ -9588,11 +9903,16 @@ msgstr "运行时调试" msgid "Runtime options" msgstr "运行时选项" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Samsung Galaxy S24 Ultra" +msgstr "Samsung Galaxy S24 Ultra" + #: src/mobile/views/FilesView.tsx #: src/renderer/components/mcp/McpServerEditor.tsx #: src/renderer/components/thread/ThreadGoalControls.tsx #: src/renderer/components/workspace/WorkspaceNameDialog.tsx #: src/renderer/views/FileEditorOverlay/parts/FileEditorPane/parts/EditorToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/ProfileOverlay/parts/EditProfileDialog.tsx #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx #: src/renderer/views/SettingsOverlay/parts/OpenCodeProviderSettings.tsx @@ -9775,6 +10095,10 @@ msgstr "搜索代理..." msgid "Search branches..." msgstr "搜索分支..." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Search downloads" +msgstr "搜索下载内容" + #: src/mobile/views/FilesView.tsx #: src/renderer/views/FileEditorOverlay/parts/ProjectTreeView/ProjectTreeView.tsx msgid "Search files" @@ -9808,6 +10132,10 @@ msgstr "搜索模型..." msgid "Search or enter address" msgstr "搜索或输入地址" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Search passwords" +msgstr "搜索密码" + #: src/renderer/views/PullRequestsView/PullRequestsView.tsx msgid "Search pull requests" msgstr "搜索拉取请求" @@ -10028,7 +10356,7 @@ msgstr "在本会话启动时已固定 — 如需更改服务器,请开始新 #: src/mobile/NarrowShell.tsx #: src/mobile/WideShell.tsx #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx #: src/renderer/views/MainView/parts/Sidebar/parts/SidebarFooterNav.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx @@ -10078,11 +10406,11 @@ msgstr "为操作系统级别的代理会话显示一个无项目的主页范围 msgid "Show all" msgstr "显示全部" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show bookmark bar" msgstr "显示书签栏" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Show Bookmark Bar" msgstr "显示书签栏" @@ -10104,6 +10432,10 @@ msgstr "显示编辑器" msgid "Show context usage details" msgstr "显示上下文使用详细信息" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Show device toolbar" +msgstr "显示设备工具栏" + #: src/renderer/views/SettingsOverlay/parts/settingsSearchIndex.ts #: src/renderer/views/SettingsOverlay/parts/UsageSettings.tsx msgid "Show estimated cost" @@ -10118,6 +10450,10 @@ msgstr "显示预计费用" msgid "Show in file explorer" msgstr "在文件资源管理器中显示" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Show in folder" +msgstr "在文件夹中显示" + #: src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx #: src/renderer/components/thread/ChatPane/parts/items/UserMessage.tsx #: src/renderer/views/ExperimentView/parts/ExperimentPromptSection.tsx @@ -10399,11 +10735,20 @@ msgstr "方案 {0}" #~ msgid "Solution {ordinal}" #~ msgstr "方案 {ordinal}" +#. placeholder {0}: result.protectedItemsSkipped +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some browser data was skipped. Protected items skipped: {0}." +msgstr "某些浏览器数据已被跳过。跳过的受保护项目:{0}。" + #: src/mobile/views/pr/PrOverviewPage.tsx #: src/renderer/views/GitReviewOverlay/parts/GitReviewSidebar/parts/PrSection.tsx msgid "Some checks are failing or pending." msgstr "一些检查失败或待处理。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Some Chromium browsers protect cookies and passwords with App-Bound Encryption. Poracode does not bypass that protection. Data the operating system does not make available will be skipped." +msgstr "某些 Chromium 浏览器使用 App-Bound Encryption 保护 Cookie 和密码。Poracode 不会绕过此保护。操作系统不提供的数据将被跳过。" + #: src/renderer/actions/experimentActions.ts #: src/renderer/actions/experimentDecisionActions.ts msgid "Some experiment worktrees could not be removed." @@ -10843,6 +11188,14 @@ msgstr "支持:" msgid "Supports Fast mode" msgstr "支持快速模式" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Duo" +msgstr "Surface Duo" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Surface Pro 7" +msgstr "Surface Pro 7" + #: src/renderer/views/MainView/parts/RightPanel/parts/NotesPanel/NotesPanel.tsx msgid "Swap notes and to-dos" msgstr "切换笔记和待办事项" @@ -10963,7 +11316,7 @@ msgstr "Tailscale HTTPS" msgid "Tailscale URL" msgstr "Tailscale URL" -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Take Screenshot" msgstr "截图" @@ -11391,6 +11744,11 @@ msgstr "该项目的远程服务器处于离线状态。请重新连接后再开 msgid "This removes later messages and restores files when a checkpoint snapshot is available." msgstr "当检查点快照可用时,这会删除稍后的消息并恢复文件。" +#. placeholder {0}: pendingDelete?.origin +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "This removes the saved password for <0>{0} from Poracode." +msgstr "这会从 Poracode 中删除 <0>{0} 的已保存密码。" + #: src/renderer/views/SchedulesView/SchedulesView.tsx msgid "This removes the schedule and its latest result from this device." msgstr "这会从此设备中删除计划及其最新结果。" @@ -11738,6 +12096,10 @@ msgstr "无法复制配对令牌。" msgid "Unable to copy Tailscale URL." msgstr "无法复制 Tailscale URL。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to copy the password." +msgstr "无法复制密码。" + #: src/renderer/actions/experimentActions.ts msgid "Unable to create the candidate worktree." msgstr "无法创建候选项工作树。" @@ -11751,6 +12113,10 @@ msgstr "无法创建拉取请求:{0}" msgid "Unable to create WSL worktree directory \"{path}\"" msgstr "无法创建 WSL 工作树目录“{path}”" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to delete the saved password." +msgstr "无法删除已保存的密码。" + #: src/mobile/useRemoteDesktop.ts msgid "Unable to delete the worktree." msgstr "无法删除 worktree。" @@ -11759,6 +12125,18 @@ msgstr "无法删除 worktree。" msgid "Unable to determine the experiment's source branch." msgstr "无法确定实验的源分支。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to edit the saved password." +msgstr "无法编辑已保存的密码。" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to find browser data to import." +msgstr "无法找到要导入的浏览器数据。" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx +msgid "Unable to import browser data." +msgstr "无法导入浏览器数据。" + #. placeholder {0}: props.selectedAgent.label #: src/renderer/components/thread/ThreadDraftComposerArea.tsx msgid "Unable to install {0} hooks." @@ -11773,10 +12151,18 @@ msgstr "无法安装{0}。" msgid "Unable to load diff." msgstr "无法加载差异。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to load downloads." +msgstr "无法加载下载内容。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to load remote access pairing." msgstr "无法加载远程访问配对。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to load saved passwords." +msgstr "无法加载已保存的密码。" + #: src/mobile/useRemoteDesktop.ts msgid "Unable to load thread." msgstr "无法加载线程。" @@ -11876,6 +12262,10 @@ msgstr "无法解析实验候选项的提交" msgid "Unable to restore the SSH connection." msgstr "无法恢复 SSH 连接。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to reveal the password." +msgstr "无法显示密码。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to revoke remote session." msgstr "无法撤销远程会话。" @@ -11889,6 +12279,10 @@ msgstr "无法保存{0}凭据。" msgid "Unable to save Claude {displayLabel} profile." msgstr "无法保存Claude {displayLabel}配置。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Unable to save the password." +msgstr "无法保存密码。" + #: src/renderer/utils/shellUtils.ts msgid "Unable to start {label}." msgstr "无法启动{label}。" @@ -11960,6 +12354,10 @@ msgstr "无法更新远程访问。" msgid "Unable to update Tailscale HTTPS." msgstr "无法更新 Tailscale HTTPS。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDownloadsPage.tsx +msgid "Unable to update the download." +msgstr "无法更新下载内容。" + #: src/renderer/views/SettingsOverlay/parts/RemoteAccessSettings.tsx msgid "Unable to update the public URL." msgstr "无法更新公共 URL。" @@ -12344,6 +12742,10 @@ msgstr "已被{conflict}使用" msgid "user@example.com" msgstr "user@example.com" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Username" +msgstr "用户名" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Using the built-in Claude model list." msgstr "正在使用内置的Claude模型列表。" @@ -12417,6 +12819,18 @@ msgstr "查看变更日志" msgid "View-only — this connection can't manage projects." msgstr "仅查看 — 此连接无法管理项目。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport height" +msgstr "视口高度" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport scale" +msgstr "视口缩放" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx +msgid "Viewport width" +msgstr "视口宽度" + #. placeholder {0}: provider.label #: src/renderer/views/SettingsOverlay/parts/SingleAgentSettings/parts/ModelVisibilityDropdown.tsx msgid "Visible {0} models" @@ -12559,11 +12973,16 @@ msgid "Web server" msgstr "Web 服务器" #. External link to the product website +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx #: src/renderer/views/SettingsOverlay/parts/AboutSettings.tsx #: src/renderer/views/SettingsOverlay/parts/AcpRegistrySettings.tsx msgid "Website" msgstr "网站" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserPasswordsPage.tsx +msgid "Website origin" +msgstr "网站来源" + #: src/renderer/views/SchedulesView/ScheduleEditor.tsx msgid "Weekdays" msgstr "工作日" @@ -12927,10 +13346,16 @@ msgstr "您贮藏的本地更改已重新应用。" msgid "z.ai" msgstr "z.ai" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx +msgid "Zoom" +msgstr "缩放" + #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom in" msgstr "放大" #: src/renderer/components/composer/ImageLightbox.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserMenu.tsx msgid "Zoom out" msgstr "缩小" diff --git a/src/renderer/state/browserCredentialsStore.ts b/src/renderer/state/browserCredentialsStore.ts new file mode 100644 index 000000000..6643878be --- /dev/null +++ b/src/renderer/state/browserCredentialsStore.ts @@ -0,0 +1,16 @@ +import { create } from "zustand"; +import type { BrowserCredentialInfo } from "@/shared/ipc"; + +interface BrowserCredentialsState { + credentials: BrowserCredentialInfo[]; + revision: number; + setCredentials: (credentials: BrowserCredentialInfo[]) => void; + invalidate: () => void; +} + +export const useBrowserCredentialsStore = create((set) => ({ + credentials: [], + revision: 0, + setCredentials: (credentials) => set({ credentials }), + invalidate: () => set((state) => ({ revision: state.revision + 1 })), +})); diff --git a/src/renderer/state/browserDownloadsStore.test.ts b/src/renderer/state/browserDownloadsStore.test.ts new file mode 100644 index 000000000..5a66d41df --- /dev/null +++ b/src/renderer/state/browserDownloadsStore.test.ts @@ -0,0 +1,46 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import type { BrowserDownloadInfo } from "@/shared/ipc"; +import { useBrowserDownloadsStore } from "./browserDownloadsStore"; + +const olderDownload: BrowserDownloadInfo = { + id: "older", + filename: "older.zip", + url: "https://example.test/older.zip", + mimeType: "application/zip", + state: "progressing", + receivedBytes: 10, + totalBytes: 100, + startTime: 1, + canResume: true, +}; + +const newerDownload: BrowserDownloadInfo = { + ...olderDownload, + id: "newer", + filename: "newer.zip", + startTime: 2, +}; + +describe("browserDownloadsStore", () => { + beforeEach(() => useBrowserDownloadsStore.getState().setDownloads([])); + + it("inserts downloads in newest-first order and replaces updates in place", () => { + const store = useBrowserDownloadsStore.getState(); + store.upsertDownload(olderDownload); + store.upsertDownload(newerDownload); + store.upsertDownload({ ...olderDownload, state: "paused", receivedBytes: 50 }); + + expect(useBrowserDownloadsStore.getState().downloads).toEqual([ + newerDownload, + { ...olderDownload, state: "paused", receivedBytes: 50 }, + ]); + }); + + it("removes a download by id", () => { + const store = useBrowserDownloadsStore.getState(); + store.setDownloads([newerDownload, olderDownload]); + store.removeDownload(newerDownload.id); + + expect(useBrowserDownloadsStore.getState().downloads).toEqual([olderDownload]); + }); +}); diff --git a/src/renderer/state/browserDownloadsStore.ts b/src/renderer/state/browserDownloadsStore.ts new file mode 100644 index 000000000..6a61b454b --- /dev/null +++ b/src/renderer/state/browserDownloadsStore.ts @@ -0,0 +1,30 @@ +import { create } from "zustand"; +import type { BrowserDownloadInfo } from "@/shared/ipc"; + +interface BrowserDownloadsState { + downloads: BrowserDownloadInfo[]; + setDownloads: (downloads: BrowserDownloadInfo[]) => void; + upsertDownload: (download: BrowserDownloadInfo) => void; + removeDownload: (downloadId: string) => void; +} + +export const useBrowserDownloadsStore = create((set) => ({ + downloads: [], + setDownloads: (downloads) => set({ downloads }), + upsertDownload: (download) => + set((state) => { + const index = state.downloads.findIndex((candidate) => candidate.id === download.id); + if (index < 0) { + return { + downloads: [...state.downloads, download].sort((a, b) => b.startTime - a.startTime), + }; + } + const downloads = [...state.downloads]; + downloads[index] = download; + return { downloads }; + }), + removeDownload: (downloadId) => + set((state) => ({ + downloads: state.downloads.filter((download) => download.id !== downloadId), + })), +})); diff --git a/src/renderer/state/browserFindStore.ts b/src/renderer/state/browserFindStore.ts new file mode 100644 index 000000000..8eecad2fc --- /dev/null +++ b/src/renderer/state/browserFindStore.ts @@ -0,0 +1,52 @@ +import { create } from "zustand"; +import type { BrowserFindResult } from "@/shared/ipc"; + +interface BrowserFindState { + tabId: string | null; + query: string; + matchCase: boolean; + matches: number; + currentIndex: number; + openToken: number; + open: (tabId: string) => void; + close: () => void; + setQuery: (query: string) => void; + toggleMatchCase: () => void; + applyResult: (result: BrowserFindResult) => void; +} + +export const useBrowserFindStore = create((set) => ({ + tabId: null, + query: "", + matchCase: false, + matches: 0, + currentIndex: -1, + openToken: 0, + open: (tabId) => + set((state) => ({ + tabId, + query: state.tabId === tabId ? state.query : "", + matches: state.tabId === tabId ? state.matches : 0, + currentIndex: state.tabId === tabId ? state.currentIndex : -1, + openToken: state.openToken + 1, + })), + close: () => + set((state) => + state.tabId === null ? {} : { tabId: null, query: "", matches: 0, currentIndex: -1 }, + ), + setQuery: (query) => + set((state) => (state.query === query ? {} : { query, matches: 0, currentIndex: -1 })), + toggleMatchCase: () => + set((state) => ({ + matchCase: !state.matchCase, + matches: 0, + currentIndex: -1, + })), + applyResult: (result) => + set((state) => { + if (state.tabId !== result.tabId) return {}; + const currentIndex = result.activeMatchOrdinal > 0 ? result.activeMatchOrdinal - 1 : -1; + if (state.matches === result.matches && state.currentIndex === currentIndex) return {}; + return { matches: result.matches, currentIndex }; + }), +})); diff --git a/src/renderer/state/browserImportStore.ts b/src/renderer/state/browserImportStore.ts new file mode 100644 index 000000000..49ee6ffec --- /dev/null +++ b/src/renderer/state/browserImportStore.ts @@ -0,0 +1,11 @@ +import { create } from "zustand"; + +interface BrowserImportState { + open: boolean; + setOpen: (open: boolean) => void; +} + +export const useBrowserImportStore = create((set) => ({ + open: false, + setOpen: (open) => set((state) => (state.open === open ? {} : { open })), +})); diff --git a/src/renderer/state/browserPanelStore.ts b/src/renderer/state/browserPanelStore.ts index 02cfede28..a8348cc14 100644 --- a/src/renderer/state/browserPanelStore.ts +++ b/src/renderer/state/browserPanelStore.ts @@ -90,10 +90,9 @@ export const useBrowserPanelStore = create((set) => ({ if (idx < 0) { return { tabs: [...s.tabs, tab] }; } - const nextTab = { ...s.tabs[idx]!, ...tab }; - if (tabInfoEqual(s.tabs[idx]!, nextTab)) return {}; + if (tabInfoEqual(s.tabs[idx]!, tab)) return {}; const next = s.tabs.slice(); - next[idx] = nextTab; + next[idx] = tab; return { tabs: next }; }), setActive: (tabId) => set((state) => (state.activeTabId === tabId ? {} : { activeTabId: tabId })), @@ -157,6 +156,26 @@ function tabInfoEqual(a: BrowserTabInfo, b: BrowserTabInfo): boolean { a.canGoBack === b.canGoBack && a.canGoForward === b.canGoForward && a.devToolsOpen === b.devToolsOpen && + a.internalPage === b.internalPage && + a.zoomFactor === b.zoomFactor && + deviceEmulationEqual(a.deviceEmulation, b.deviceEmulation) && a.groupId === b.groupId ); } + +function deviceEmulationEqual( + a: BrowserTabInfo["deviceEmulation"], + b: BrowserTabInfo["deviceEmulation"], +): boolean { + if (a === b) return true; + if (!a || !b) return false; + return ( + a.width === b.width && + a.height === b.height && + a.deviceScaleFactor === b.deviceScaleFactor && + a.scale === b.scale && + a.mobile === b.mobile && + a.touch === b.touch && + a.preset === b.preset + ); +} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.test.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.test.tsx index eda3eea6e..cbdf41904 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.test.tsx +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.test.tsx @@ -1,6 +1,7 @@ -import { act, fireEvent } from "@testing-library/react"; +import { act, fireEvent, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import { usePanelStore } from "@/renderer/state/panelStore"; import { BrowserPanel } from "./BrowserPanel"; @@ -8,8 +9,12 @@ import { BrowserPanel } from "./BrowserPanel"; const bridge = vi.hoisted(() => ({ browserCreateTab: vi.fn<() => Promise>().mockResolvedValue(undefined), browserAttachWebContents: vi.fn<() => Promise>().mockResolvedValue(undefined), + browserSetDeviceEmulation: vi.fn<() => Promise>().mockResolvedValue(undefined), browserReload: vi.fn<() => Promise>().mockResolvedValue(undefined), browserHardReload: vi.fn<() => Promise>().mockResolvedValue(undefined), + browserFindInPage: vi.fn<() => Promise>().mockResolvedValue(undefined), + browserStopFindInPage: vi.fn<() => Promise>().mockResolvedValue(undefined), + browserPrint: vi.fn<() => Promise<{ ok: true }>>().mockResolvedValue({ ok: true }), browserExtractToWindow: vi.fn<() => Promise>().mockResolvedValue(undefined), browserInjectToMain: vi.fn<() => Promise>().mockResolvedValue(undefined), })); @@ -33,14 +38,31 @@ vi.mock("./parts/BrowserTabStrip", () => ({ BrowserTabStrip: () =>
, })); +vi.mock("./parts/BrowserDownloadsPage", () => ({ + BrowserDownloadsPage: () =>
, +})); + +vi.mock("./parts/BrowserPasswordsPage", () => ({ + BrowserPasswordsPage: () =>
, +})); + +vi.mock("./parts/BrowserImportModal", () => ({ + BrowserImportModal: () => null, +})); + vi.mock("@/renderer/bridge", () => ({ isMac: () => false, isWindows: () => false, + isRemoteSession: () => false, readBridge: () => ({ browserCreateTab: bridge.browserCreateTab, browserAttachWebContents: bridge.browserAttachWebContents, + browserSetDeviceEmulation: bridge.browserSetDeviceEmulation, browserReload: bridge.browserReload, browserHardReload: bridge.browserHardReload, + browserFindInPage: bridge.browserFindInPage, + browserStopFindInPage: bridge.browserStopFindInPage, + browserPrint: bridge.browserPrint, browserExtractToWindow: bridge.browserExtractToWindow, browserInjectToMain: bridge.browserInjectToMain, }), @@ -60,6 +82,14 @@ describe("BrowserPanel", () => { attentionTabId: null, automationActive: false, }); + useBrowserFindStore.setState({ + tabId: null, + query: "", + matchCase: false, + matches: 0, + currentIndex: -1, + openToken: 0, + }); usePanelStore.setState({ browserPanelOpen: false, browserOverlayOpen: false, @@ -99,8 +129,38 @@ describe("BrowserPanel", () => { expect(webviews).toHaveLength(2); expect(webviews[0]?.getAttribute("partition")).toBe("persist:lightcode-browser"); expect(webviews[0]?.getAttribute("allowpopups")).toBe("true"); - expect((webviews[0] as HTMLElement).style.display).toBe("flex"); - expect((webviews[1] as HTMLElement).style.display).toBe("none"); + expect((webviews[0]!.parentElement!.parentElement as HTMLElement).style.display).toBe("block"); + expect((webviews[1]!.parentElement!.parentElement as HTMLElement).style.display).toBe("none"); + }); + + it("does not mount webviews for internal browser pages", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }, + { + tabId: "downloads", + url: "poracode://downloads", + title: "Downloads", + loading: false, + canGoBack: false, + canGoForward: false, + internalPage: "downloads", + }, + ], + activeTabId: "downloads", + }); + + const { container } = render(); + const webviews = container.querySelectorAll("webview"); + expect(webviews).toHaveLength(1); + expect(webviews[0]?.getAttribute("data-tab-id")).toBe("tab-1"); }); it("updates tab group membership from browser state", () => { @@ -132,6 +192,38 @@ describe("BrowserPanel", () => { expect(useBrowserPanelStore.getState().tabs[0]?.groupId).toBe("group-1"); }); + it("clears optional tab state when a full tab update omits it", () => { + const tab = { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }; + useBrowserPanelStore.setState({ + tabs: [ + { + ...tab, + internalPage: "downloads", + deviceEmulation: { + width: 1280, + height: 720, + deviceScaleFactor: 1, + scale: 0.75, + mobile: false, + touch: false, + }, + }, + ], + activeTabId: "tab-1", + }); + + act(() => useBrowserPanelStore.getState().upsertTab(tab)); + + expect(useBrowserPanelStore.getState().tabs[0]).toEqual(tab); + }); + it("keeps the same webview mounted when browser panel goes fullscreen", () => { useBrowserPanelStore.setState({ tabs: [ @@ -164,6 +256,163 @@ describe("BrowserPanel", () => { expect(container.querySelector("webview")).toBe(webview); }); + it("focuses the browser when its overlay opens", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }, + ], + activeTabId: "tab-1", + }); + const { container } = render(); + const outside = document.createElement("input"); + document.body.append(outside); + outside.focus(); + + act(() => usePanelStore.setState({ browserOverlayOpen: true })); + + expect(document.activeElement).toBe(container.querySelector("[data-poracode-browser]")); + outside.remove(); + }); + + it("focuses the standalone browser window on mount", () => { + const outside = document.createElement("input"); + document.body.append(outside); + outside.focus(); + + const { container } = render(); + + expect(document.activeElement).toBe(container.querySelector("[data-poracode-browser]")); + outside.remove(); + }); + + it("keeps the same webview mounted when device emulation changes", () => { + const tab = { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }; + useBrowserPanelStore.setState({ tabs: [tab], activeTabId: "tab-1" }); + const { container } = render(); + const webview = container.querySelector("webview"); + + act(() => { + useBrowserPanelStore.getState().setState({ + tabs: [ + { + ...tab, + deviceEmulation: { + width: 833, + height: 970, + deviceScaleFactor: 1, + scale: 1, + mobile: false, + touch: false, + preset: "Responsive", + }, + }, + ], + activeTabId: "tab-1", + }); + }); + + expect(container.querySelector("webview")).toBe(webview); + expect((webview!.parentElement as HTMLElement).style.width).toBe("833px"); + expect((webview!.parentElement as HTMLElement).style.height).toBe("970px"); + }); + + it("resizes an emulated viewport without remounting its webview", async () => { + const emulation = { + width: 833, + height: 970, + deviceScaleFactor: 1, + scale: 0.5, + mobile: false, + touch: false, + preset: "Responsive", + }; + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + deviceEmulation: emulation, + }, + ], + activeTabId: "tab-1", + }); + const { container, getByRole } = render(); + const webview = container.querySelector("webview"); + const viewport = webview?.parentElement as HTMLElement; + + fireEvent.pointerDown(getByRole("button", { name: "Resize viewport" }), { + clientX: 100, + clientY: 100, + }); + fireEvent.pointerMove(window, { clientX: 150, clientY: 125 }); + + await waitFor(() => { + expect(viewport.style.width).toBe("466.5px"); + expect(viewport.style.height).toBe("510px"); + }); + expect(container.querySelector("webview")).toBe(webview); + + fireEvent.pointerUp(window); + expect(bridge.browserSetDeviceEmulation).toHaveBeenLastCalledWith({ + tabId: "tab-1", + emulation: { ...emulation, width: 933, height: 1020 }, + }); + }); + + it("stops an emulated viewport drag when the webview unmounts", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + deviceEmulation: { + width: 833, + height: 970, + deviceScaleFactor: 1, + scale: 1, + mobile: false, + touch: false, + }, + }, + ], + activeTabId: "tab-1", + }); + const { getByRole, unmount } = render(); + + fireEvent.pointerDown(getByRole("button", { name: "Resize viewport" }), { + clientX: 100, + clientY: 100, + }); + unmount(); + bridge.browserSetDeviceEmulation.mockClear(); + fireEvent.pointerMove(window, { clientX: 150, clientY: 150 }); + fireEvent.pointerUp(window); + + expect(bridge.browserSetDeviceEmulation).not.toHaveBeenCalled(); + }); + it("attaches the right-panel webview contents to the browser tab", () => { useBrowserPanelStore.setState({ tabs: [ @@ -251,6 +500,54 @@ describe("BrowserPanel", () => { expect(bridge.browserHardReload).toHaveBeenCalledTimes(2); }); + it("opens Find in page from the panel shortcut", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }, + ], + activeTabId: "tab-1", + }); + const { container, getByRole } = render(); + + fireEvent.keyDown(container.querySelector("[data-poracode-browser]")!, { + key: "f", + ctrlKey: true, + }); + + expect(getByRole("textbox", { name: "Find in page" })).toBeTruthy(); + }); + + it("does not treat Ctrl+Alt chords as Find or Print shortcuts", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }, + ], + activeTabId: "tab-1", + }); + const { container, queryByRole } = render(); + const panel = container.querySelector("[data-poracode-browser]")!; + + fireEvent.keyDown(panel, { key: "f", ctrlKey: true, altKey: true }); + fireEvent.keyDown(panel, { key: "p", ctrlKey: true, altKey: true }); + + expect(queryByRole("textbox", { name: "Find in page" })).toBeNull(); + expect(bridge.browserPrint).not.toHaveBeenCalled(); + }); + it("moves the overlay browser to a separate window", () => { usePanelStore.setState({ browserOverlayOpen: true, diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx index b7c3e77ec..8a009534f 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx @@ -10,9 +10,9 @@ import { PictureInPicture2, X, } from "lucide-react"; -import { BROWSER_SESSION_PARTITION } from "@/shared/browserPartition"; import { isMac, readBridge } from "@/renderer/bridge"; import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; import { usePanelStore } from "@/renderer/state/panelStore"; import { macosTrafficLightGutterClass, @@ -20,7 +20,13 @@ import { panelHeaderIconButtonClass, } from "@/renderer/components/layout/sidebarChrome"; import { BrowserBookmarkBar } from "./parts/BrowserBookmarkBar"; +import { BrowserDeviceToolbar } from "./parts/BrowserDeviceToolbar"; +import { BrowserDownloadsPage } from "./parts/BrowserDownloadsPage"; import { BrowserEmptyState } from "./parts/BrowserEmptyState"; +import { BrowserFindBar } from "./parts/BrowserFindBar"; +import { BrowserImportModal } from "./parts/BrowserImportModal"; +import { BrowserPasswordsPage } from "./parts/BrowserPasswordsPage"; +import { BrowserTabWebview } from "./parts/BrowserTabWebview"; import { BrowserTabStrip } from "./parts/BrowserTabStrip"; import { BrowserToolbar } from "./parts/BrowserToolbar"; import { extractBrowserToWindow, injectBrowserToMain } from "./browserWindowActions"; @@ -52,8 +58,13 @@ export function BrowserPanel(props: { visible: boolean; surface?: "main" | "wind } = useElementPicker(); const everHadTabsRef = useRef(false); const hasActiveTab = tabs.length > 0 && activeTabId !== null; + const activeTab = activeTabId ? tabs.find((tab) => tab.tabId === activeTabId) : undefined; const rootRef = useRef(null); + useEffect(() => { + if (browserOverlayOpen || isWindowSurface) rootRef.current?.focus({ preventScroll: true }); + }, [browserOverlayOpen, isWindowSurface]); + const createTab = useCallback(() => { void readBridge() .browserCreateTab({ url: DEFAULT_HOME, activate: true }) @@ -68,7 +79,24 @@ export function BrowserPanel(props: { visible: boolean; surface?: "main" | "wind const el = rootRef.current; if (!el) return; const handleKeyDown = (event: KeyboardEvent) => { - if (!activeTabId || !isBrowserReloadShortcut(event)) return; + if (!activeTabId) return; + if (isBrowserFindShortcut(event)) { + if (activeTab?.internalPage) return; + event.preventDefault(); + event.stopPropagation(); + useBrowserFindStore.getState().open(activeTabId); + return; + } + if (isBrowserPrintShortcut(event)) { + if (activeTab?.internalPage) return; + event.preventDefault(); + event.stopPropagation(); + readBridge() + .browserPrint({ tabId: activeTabId }) + .catch(() => {}); + return; + } + if (!isBrowserReloadShortcut(event)) return; event.preventDefault(); event.stopPropagation(); const bridge = readBridge(); @@ -80,7 +108,7 @@ export function BrowserPanel(props: { visible: boolean; surface?: "main" | "wind }; el.addEventListener("keydown", handleKeyDown); return () => el.removeEventListener("keydown", handleKeyDown); - }, [activeTabId]); + }, [activeTab?.internalPage, activeTabId]); const onPick = useCallback(() => { void startPicker(); @@ -131,6 +159,7 @@ export function BrowserPanel(props: { visible: boolean; surface?: "main" | "wind ref={rootRef} data-poracode-browser="" role="group" + tabIndex={-1} aria-label={t`Browser`} className="flex h-full w-full min-h-0 flex-col bg-[var(--content-background)]" > @@ -233,15 +262,29 @@ export function BrowserPanel(props: { visible: boolean; surface?: "main" | "wind /> {hasWindowHeader ? null : } +
- {tabs.map((tab) => ( - - ))} + {tabs + .filter((tab) => !tab.internalPage) + .map((tab) => ( + + ))} + {activeTab?.internalPage === "downloads" ? ( +
+ +
+ ) : activeTab?.internalPage === "passwords" ? ( +
+ +
+ ) : null} + {menuPreviewDataUrl ? ( ) : null}
+
); } @@ -334,64 +378,25 @@ function BrowserDeviceCodeButton() { ); } -function BrowserTabWebview(props: { tabId: string; initialSrc: string; visible: boolean }) { - const ref = useRef(null); - const initialSrcRef = useRef(props.initialSrc); - - useEffect(() => { - const el = ref.current; - if (!el) return; - let cancelled = false; - const onDomReady = () => { - if (cancelled) return; - let webContentsId: number; - try { - webContentsId = el.getWebContentsId(); - } catch { - return; - } - readBridge() - .browserAttachWebContents({ tabId: props.tabId, webContentsId }) - .catch(() => {}); - }; - el.addEventListener("dom-ready", onDomReady); - return () => { - cancelled = true; - el.removeEventListener("dom-ready", onDomReady); - }; - }, [props.tabId]); - - useEffect(() => { - if (!props.visible) return; - const el = ref.current; - if (!el) return; - let webContentsId: number; - try { - webContentsId = el.getWebContentsId(); - } catch { - return; - } - readBridge() - .browserAttachWebContents({ tabId: props.tabId, webContentsId }) - .catch(() => {}); - }, [props.tabId, props.visible]); +function isBrowserReloadShortcut(event: KeyboardEvent): boolean { + if (event.key === "F5") return true; + return (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "r"; +} +function isBrowserFindShortcut(event: KeyboardEvent): boolean { return ( - + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey && + event.key.toLowerCase() === "f" ); } -function isBrowserReloadShortcut(event: KeyboardEvent): boolean { - if (event.key === "F5") return true; - return (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "r"; +function isBrowserPrintShortcut(event: KeyboardEvent): boolean { + return ( + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey && + event.key.toLowerCase() === "p" + ); } diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useBrowserSync.ts b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useBrowserSync.ts index 2731fffef..ed1d21fbe 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useBrowserSync.ts +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useBrowserSync.ts @@ -1,5 +1,8 @@ import { useEffect } from "react"; import { readBridge } from "@/renderer/bridge"; +import { useBrowserCredentialsStore } from "@/renderer/state/browserCredentialsStore"; +import { useBrowserDownloadsStore } from "@/renderer/state/browserDownloadsStore"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import { selectAnyObstructingOverlayOpen, usePanelStore } from "@/renderer/state/panelStore"; @@ -13,6 +16,11 @@ export function useBrowserSync(): void { const clearUsageLoginConfirmation = useBrowserPanelStore((s) => s.clearUsageLoginConfirmation); const setUsageLoginDeviceCode = useBrowserPanelStore((s) => s.setUsageLoginDeviceCode); const clearUsageLoginDeviceCode = useBrowserPanelStore((s) => s.clearUsageLoginDeviceCode); + const applyFindResult = useBrowserFindStore((s) => s.applyResult); + const openFind = useBrowserFindStore((s) => s.open); + const upsertDownload = useBrowserDownloadsStore((s) => s.upsertDownload); + const removeDownload = useBrowserDownloadsStore((s) => s.removeDownload); + const invalidateCredentials = useBrowserCredentialsStore((s) => s.invalidate); useEffect(() => { let cancelled = false; @@ -40,6 +48,16 @@ export function useBrowserSync(): void { upsertTab(event.tab); } else if (event.type === "tab-attention") { setAttention(event.tabId); + } else if (event.type === "find-open-requested") { + openFind(event.tabId); + } else if (event.type === "find-result") { + applyFindResult(event.result); + } else if (event.type === "download-updated") { + upsertDownload(event.download); + } else if (event.type === "download-removed") { + removeDownload(event.downloadId); + } else if (event.type === "credentials-updated") { + invalidateCredentials(); } else if (event.type === "open-panel") { if (!reactsToPresentation()) return; const panel = usePanelStore.getState(); @@ -91,5 +109,10 @@ export function useBrowserSync(): void { clearUsageLoginConfirmation, setUsageLoginDeviceCode, clearUsageLoginDeviceCode, + applyFindResult, + openFind, + upsertDownload, + removeDownload, + invalidateCredentials, ]); } diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.test.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.test.tsx new file mode 100644 index 000000000..769cc51e1 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.test.tsx @@ -0,0 +1,115 @@ +import { act, fireEvent } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; +import { BrowserDeviceToolbar } from "./BrowserDeviceToolbar"; + +const bridge = vi.hoisted(() => ({ + browserSetDeviceEmulation: vi.fn<() => Promise>().mockResolvedValue(undefined), +})); + +vi.mock("@/renderer/bridge", () => ({ + readBridge: () => bridge, + isRemoteSession: () => false, +})); + +const emulation = { + width: 833, + height: 970, + deviceScaleFactor: 1, + scale: 1, + mobile: false, + touch: false, + preset: "Responsive", +}; + +describe("BrowserDeviceToolbar", () => { + beforeEach(() => { + vi.clearAllMocks(); + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + deviceEmulation: emulation, + }, + ], + activeTabId: "tab-1", + }); + }); + + it("updates dimensions, rotates the viewport, and closes emulation", () => { + const { getByRole } = render(); + const width = getByRole("spinbutton", { name: "Viewport width" }); + + fireEvent.change(width, { target: { value: "500" } }); + fireEvent.blur(width); + expect(bridge.browserSetDeviceEmulation).toHaveBeenLastCalledWith({ + tabId: "tab-1", + emulation: { ...emulation, width: 500, preset: "Responsive" }, + }); + + fireEvent.click(getByRole("button", { name: "Rotate viewport" })); + expect(bridge.browserSetDeviceEmulation).toHaveBeenLastCalledWith({ + tabId: "tab-1", + emulation: { ...emulation, width: 970, height: 833 }, + }); + + fireEvent.click(getByRole("button", { name: "Close device toolbar" })); + expect(bridge.browserSetDeviceEmulation).toHaveBeenLastCalledWith({ + tabId: "tab-1", + emulation: null, + }); + }); + + it("does not render for an internal browser page", () => { + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "poracode://downloads", + title: "Downloads", + loading: false, + canGoBack: false, + canGoForward: false, + internalPage: "downloads", + deviceEmulation: emulation, + }, + ], + }); + + const { queryByText } = render(); + expect(queryByText("Dimensions:")).toBeNull(); + }); + + it("discards an uncommitted dimension when the active tab changes", () => { + const { getByRole } = render(); + fireEvent.change(getByRole("spinbutton", { name: "Viewport width" }), { + target: { value: "500" }, + }); + + act(() => { + useBrowserPanelStore.setState({ + tabs: [ + ...useBrowserPanelStore.getState().tabs, + { + tabId: "tab-2", + url: "https://example.org/", + title: "Example 2", + loading: false, + canGoBack: false, + canGoForward: false, + deviceEmulation: emulation, + }, + ], + activeTabId: "tab-2", + }); + }); + + expect(getByRole("spinbutton", { name: "Viewport width" })).toHaveValue(833); + }); +}); diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx new file mode 100644 index 000000000..38a7f24a1 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserDeviceToolbar.tsx @@ -0,0 +1,267 @@ +import { useEffect, useState } from "react"; +import { Button, Input, TextField } from "@heroui/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { RotateCw, X } from "lucide-react"; +import { readBridge } from "@/renderer/bridge"; +import { Select } from "@/renderer/components/common"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; +import type { BrowserDeviceEmulation, BrowserTabInfo } from "@/shared/ipc"; + +interface DevicePreset extends Omit { + id: string; + label: string; +} + +const scaleValues = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2] as const; + +export function BrowserDeviceToolbar() { + const activeTab = useBrowserPanelStore((state) => + state.activeTabId ? state.tabs.find((tab) => tab.tabId === state.activeTabId) : undefined, + ); + if (!activeTab?.deviceEmulation || activeTab.internalPage) return null; + return ; +} + +function ActiveBrowserDeviceToolbar({ tab }: { tab: BrowserTabInfo }) { + const { t } = useLingui(); + const emulation = tab.deviceEmulation!; + const [widthDraft, setWidthDraft] = useState(String(emulation.width)); + const [heightDraft, setHeightDraft] = useState(String(emulation.height)); + + useEffect(() => setWidthDraft(String(emulation.width)), [emulation.width]); + useEffect(() => setHeightDraft(String(emulation.height)), [emulation.height]); + + const presets: DevicePreset[] = [ + { + id: "Responsive", + label: t`Responsive`, + width: emulation.width, + height: emulation.height, + deviceScaleFactor: 1, + mobile: false, + touch: false, + }, + { + id: "4K", + label: t`4K`, + width: 3840, + height: 2160, + deviceScaleFactor: 1, + mobile: false, + touch: false, + }, + { + id: "Laptop L", + label: t`Laptop L`, + width: 1440, + height: 900, + deviceScaleFactor: 1, + mobile: false, + touch: false, + }, + { + id: "Laptop", + label: t`Laptop`, + width: 1024, + height: 768, + deviceScaleFactor: 1, + mobile: false, + touch: false, + }, + { + id: "Surface Pro 7", + label: t`Surface Pro 7`, + width: 912, + height: 1368, + deviceScaleFactor: 2, + mobile: true, + touch: true, + }, + { + id: "iPad Air", + label: t`iPad Air`, + width: 820, + height: 1180, + deviceScaleFactor: 2, + mobile: true, + touch: true, + }, + { + id: "iPad Mini", + label: t`iPad Mini`, + width: 768, + height: 1024, + deviceScaleFactor: 2, + mobile: true, + touch: true, + }, + { + id: "Surface Duo", + label: t`Surface Duo`, + width: 540, + height: 720, + deviceScaleFactor: 2.5, + mobile: true, + touch: true, + }, + { + id: "iPhone 15 Pro Max", + label: t`iPhone 15 Pro Max`, + width: 430, + height: 932, + deviceScaleFactor: 3, + mobile: true, + touch: true, + }, + { + id: "Pixel 8", + label: t`Pixel 8`, + width: 412, + height: 915, + deviceScaleFactor: 2.625, + mobile: true, + touch: true, + }, + { + id: "iPhone 15 Pro", + label: t`iPhone 15 Pro`, + width: 393, + height: 852, + deviceScaleFactor: 3, + mobile: true, + touch: true, + }, + { + id: "Samsung Galaxy S24 Ultra", + label: t`Samsung Galaxy S24 Ultra`, + width: 412, + height: 915, + deviceScaleFactor: 3.5, + mobile: true, + touch: true, + }, + { + id: "iPhone SE", + label: t`iPhone SE`, + width: 375, + height: 667, + deviceScaleFactor: 2, + mobile: true, + touch: true, + }, + ]; + const scaleOptions = scaleValues.map((scale) => ({ + id: String(scale), + label: `${Math.round(scale * 100)}%`, + })); + + function update(nextEmulation: BrowserDeviceEmulation | null) { + readBridge() + .browserSetDeviceEmulation({ tabId: tab.tabId, emulation: nextEmulation }) + .catch(() => {}); + } + + function selectPreset(id: string) { + const preset = presets.find((candidate) => candidate.id === id); + if (!preset) return; + update({ + width: preset.width, + height: preset.height, + deviceScaleFactor: preset.deviceScaleFactor, + scale: emulation.scale, + mobile: preset.mobile, + touch: preset.touch, + preset: preset.id, + }); + } + + function commitDimension(dimension: "width" | "height", draft: string) { + const value = Number.parseInt(draft, 10); + if (!Number.isInteger(value) || value < 240 || value > 7680) { + if (dimension === "width") setWidthDraft(String(emulation.width)); + else setHeightDraft(String(emulation.height)); + return; + } + update({ ...emulation, [dimension]: value, preset: "Responsive" }); + } + + return ( +
+ + Dimensions: + + { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + + × + + commitDimension("height", heightDraft)} + > + { + if (event.key === "Enter") event.currentTarget.blur(); + }} + /> + + + setQuery(event.currentTarget.value)} + className="pl-8 text-xs" + /> +
+
+ + + {visibleDownloads.length === 0 ? ( +
+
+ + + +
+
+

+ {downloads.length === 0 ? ( + Files you download appear here + ) : ( + No downloads match your search + )} +

+ {downloads.length === 0 ? ( +

+ Downloads from the built-in browser are saved in this history. +

+ ) : null} +
+
+ ) : ( +
+
+ {visibleDownloads.map((download) => ( + void runAction(download, action)} + /> + ))} +
+
+ )} +
+ ); +} + +function DownloadRow(props: { + download: BrowserDownloadInfo; + busy: boolean; + onAction: (action: DownloadAction) => void; +}) { + const { t } = useLingui(); + const { download } = props; + const progress = + download.totalBytes > 0 + ? Math.min(100, Math.round((download.receivedBytes / download.totalBytes) * 100)) + : download.state === "completed" + ? 100 + : null; + const transferLabel = + download.totalBytes > 0 + ? `${formatBytes(download.receivedBytes)} / ${formatBytes(download.totalBytes)}` + : formatBytes(download.receivedBytes); + const indeterminate = progress === null && download.state === "progressing"; + + return ( +
+
+ + + +
+

+ {download.filename} +

+

+ {downloadSource(download.url)} +

+
+
+ {download.state === "progressing" || download.state === "paused" ? ( + + ) : null} + {download.state === "interrupted" && download.canResume ? ( + + ) : null} + {download.state === "progressing" || download.state === "paused" ? ( + + ) : null} + {download.state === "completed" ? ( + <> + + + + ) : null} + {download.state === "completed" || + download.state === "cancelled" || + download.state === "interrupted" ? ( + + ) : null} +
+
+ +
+
+
+
+
+ + {transferLabel} +
+
+
+ ); +} + +function DownloadStateLabel(props: { download: BrowserDownloadInfo }) { + switch (props.download.state) { + case "progressing": + return Downloading; + case "paused": + return Paused; + case "completed": + return Completed; + case "cancelled": + return Cancelled; + case "interrupted": + return props.download.canResume ? Interrupted : Download failed; + } +} + +function downloadSource(url: string): string { + try { + return new URL(url).hostname || url; + } catch { + return url; + } +} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.test.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.test.tsx new file mode 100644 index 000000000..7b47dff56 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.test.tsx @@ -0,0 +1,161 @@ +import { act, fireEvent } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; +import { BrowserFindBar } from "./BrowserFindBar"; + +const bridge = vi.hoisted(() => ({ + browserFindInPage: vi.fn<() => Promise>().mockResolvedValue(undefined), + browserStopFindInPage: vi.fn<() => Promise>().mockResolvedValue(undefined), +})); + +vi.mock("@/renderer/bridge", () => ({ readBridge: () => bridge })); + +describe("BrowserFindBar", () => { + beforeEach(() => { + vi.clearAllMocks(); + useBrowserFindStore.setState({ + tabId: null, + query: "", + matchCase: false, + matches: 0, + currentIndex: -1, + openToken: 0, + }); + useBrowserPanelStore.setState({ + tabs: [ + { + tabId: "tab-1", + url: "https://example.com/", + title: "Example", + loading: false, + canGoBack: false, + canGoForward: false, + }, + ], + activeTabId: "tab-1", + }); + }); + + it("starts a new search for edits and continues it for match navigation", () => { + act(() => useBrowserFindStore.getState().open("tab-1")); + const { getByRole } = render(); + const input = getByRole("textbox", { name: "Find in page" }); + + fireEvent.change(input, { target: { value: "needle" } }); + expect(bridge.browserFindInPage).toHaveBeenNthCalledWith(1, { + tabId: "tab-1", + text: "needle", + forward: true, + findNext: true, + matchCase: false, + }); + + fireEvent.keyDown(input, { key: "Enter" }); + expect(bridge.browserFindInPage).toHaveBeenNthCalledWith(2, { + tabId: "tab-1", + text: "needle", + forward: true, + findNext: false, + matchCase: false, + }); + + fireEvent.keyDown(input, { key: "Enter", shiftKey: true }); + expect(bridge.browserFindInPage).toHaveBeenNthCalledWith(3, { + tabId: "tab-1", + text: "needle", + forward: false, + findNext: false, + matchCase: false, + }); + + fireEvent.click(getByRole("button", { name: "Match case" })); + expect(bridge.browserFindInPage).toHaveBeenNthCalledWith(4, { + tabId: "tab-1", + text: "needle", + forward: true, + findNext: true, + matchCase: true, + }); + }); + + it("clears the native selection when the query is cleared or the bar closes", () => { + act(() => useBrowserFindStore.getState().open("tab-1")); + const { getByRole, queryByRole } = render(); + const input = getByRole("textbox", { name: "Find in page" }); + + fireEvent.change(input, { target: { value: "needle" } }); + fireEvent.change(input, { target: { value: "" } }); + expect(bridge.browserStopFindInPage).toHaveBeenLastCalledWith({ + tabId: "tab-1", + action: "clearSelection", + }); + + bridge.browserStopFindInPage.mockClear(); + fireEvent.click(getByRole("button", { name: "Close find" })); + expect(bridge.browserStopFindInPage).toHaveBeenCalledWith({ + tabId: "tab-1", + action: "clearSelection", + }); + expect(useBrowserFindStore.getState().tabId).toBeNull(); + expect(queryByRole("textbox", { name: "Find in page" })).toBeNull(); + }); + + it("focuses and selects the query again when find is reopened", () => { + useBrowserFindStore.setState({ tabId: "tab-1", query: "needle", openToken: 1 }); + const { getByRole } = render(); + const input = getByRole("textbox", { name: "Find in page" }) as HTMLInputElement; + expect(document.activeElement).toBe(input); + + input.setSelectionRange(0, 0); + act(() => useBrowserFindStore.getState().open("tab-1")); + + expect(document.activeElement).toBe(input); + expect(input.selectionStart).toBe(0); + expect(input.selectionEnd).toBe(6); + }); + + it("hides a find session that does not belong to the active page", () => { + useBrowserFindStore.setState({ tabId: "tab-1", query: "needle", openToken: 1 }); + useBrowserPanelStore.setState({ activeTabId: null }); + + const { queryByRole } = render(); + expect(queryByRole("textbox", { name: "Find in page" })).toBeNull(); + }); + + it("stops and closes the find session when the active page changes", () => { + useBrowserFindStore.setState({ tabId: "tab-1", query: "needle", openToken: 1 }); + render(); + + act(() => useBrowserPanelStore.setState({ activeTabId: null })); + + expect(bridge.browserStopFindInPage).toHaveBeenCalledWith({ + tabId: "tab-1", + action: "clearSelection", + }); + expect(useBrowserFindStore.getState().tabId).toBeNull(); + }); + + it("does not restore stale find results after switching away and back", () => { + useBrowserFindStore.setState({ + tabId: "tab-1", + query: "needle", + matches: 2, + currentIndex: 0, + openToken: 1, + }); + const { queryByRole } = render(); + + act(() => useBrowserPanelStore.setState({ activeTabId: null })); + act(() => useBrowserPanelStore.setState({ activeTabId: "tab-1" })); + + expect(queryByRole("textbox", { name: "Find in page" })).toBeNull(); + expect(useBrowserFindStore.getState()).toMatchObject({ + tabId: null, + query: "", + matches: 0, + currentIndex: -1, + }); + }); +}); diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx new file mode 100644 index 000000000..9b166b3b1 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserFindBar.tsx @@ -0,0 +1,88 @@ +import { useEffect, useRef } from "react"; +import { useLingui } from "@lingui/react/macro"; +import { readBridge } from "@/renderer/bridge"; +import { FindBar } from "@/renderer/components/find/FindBar"; +import { useFindBarChrome } from "@/renderer/components/find/useFindBarChrome"; +import { useBrowserFindStore } from "@/renderer/state/browserFindStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; + +export function BrowserFindBar() { + const tabId = useBrowserFindStore((state) => state.tabId); + const activeTab = useBrowserPanelStore((state) => + state.activeTabId ? state.tabs.find((tab) => tab.tabId === state.activeTabId) : undefined, + ); + if (!tabId || activeTab?.tabId !== tabId || activeTab.internalPage) return null; + return ; +} + +function ActiveBrowserFindBar({ tabId }: { tabId: string }) { + const { t } = useLingui(); + const inputRef = useRef(null); + const query = useBrowserFindStore((state) => state.query); + const matchCase = useBrowserFindStore((state) => state.matchCase); + const matches = useBrowserFindStore((state) => state.matches); + const currentIndex = useBrowserFindStore((state) => state.currentIndex); + const openToken = useBrowserFindStore((state) => state.openToken); + const setQuery = useBrowserFindStore((state) => state.setQuery); + const toggleMatchCase = useBrowserFindStore((state) => state.toggleMatchCase); + const close = useBrowserFindStore((state) => state.close); + + useEffect( + () => () => { + readBridge() + .browserStopFindInPage({ tabId, action: "clearSelection" }) + .catch(() => {}); + const findState = useBrowserFindStore.getState(); + if (findState.tabId === tabId) findState.close(); + }, + [tabId], + ); + + function find(text: string, forward: boolean, findNext: boolean, nextMatchCase = matchCase) { + if (!text) return; + readBridge() + .browserFindInPage({ tabId, text, forward, findNext, matchCase: nextMatchCase }) + .catch(() => {}); + } + + function onQueryChange(nextQuery: string) { + setQuery(nextQuery); + if (!nextQuery) { + readBridge() + .browserStopFindInPage({ tabId, action: "clearSelection" }) + .catch(() => {}); + return; + } + find(nextQuery, true, true); + } + + function onToggleMatchCase() { + const nextMatchCase = !matchCase; + toggleMatchCase(); + find(query, true, true, nextMatchCase); + } + + function onClose() { + close(); + } + + useFindBarChrome(inputRef, openToken, onClose); + + return ( +
+ find(query, true, false)} + onPrev={() => find(query, false, false)} + onClose={onClose} + placeholder={t`Find in page`} + /> +
+ ); +} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.test.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.test.tsx new file mode 100644 index 000000000..3fcdc6f2f --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.test.tsx @@ -0,0 +1,100 @@ +import { fireEvent, screen, waitFor } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useBrowserImportStore } from "@/renderer/state/browserImportStore"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; +import type { BrowserImportResult, BrowserImportSourceInfo } from "@/shared/ipc"; +import { BrowserImportModal } from "./BrowserImportModal"; + +type ImportDataPayload = { + sourceId: string; + passwords: boolean; + cookies: boolean; + acknowledgeProtectedData: boolean; +}; + +const bridge = vi.hoisted(() => ({ + browserListImportSources: vi.fn<() => Promise>(), + browserImportData: vi.fn<(payload: ImportDataPayload) => Promise>(), +})); + +vi.mock("@/renderer/bridge", () => ({ + readBridge: () => bridge, + isRemoteSession: () => false, +})); + +const source: BrowserImportSourceInfo = { + id: "chrome:default", + browser: "chrome", + browserLabel: "Google Chrome", + profileLabel: "Default", + supportsPasswords: true, + supportsCookies: true, + hasAppBoundData: true, +}; + +describe("BrowserImportModal", () => { + beforeEach(() => { + bridge.browserListImportSources.mockReset().mockResolvedValue([source]); + bridge.browserImportData.mockReset().mockResolvedValue({ + passwordsImported: 1, + cookiesImported: 2, + passwordsSkipped: 0, + cookiesSkipped: 0, + protectedItemsSkipped: 0, + errors: [], + }); + useBrowserImportStore.setState({ open: true }); + }); + + it("requires acknowledgement before importing App-Bound protected data", async () => { + render(); + + expect(await screen.findByText("Protected browser data")).toBeInTheDocument(); + expect(screen.getByText(/Poracode does not bypass that protection/)).toBeInTheDocument(); + + const importButton = screen.getByRole("button", { name: "Import" }); + expect(importButton).toBeDisabled(); + + fireEvent.click( + screen.getByRole("checkbox", { + name: "I understand that protected browser data may be skipped.", + }), + ); + expect(importButton).toBeEnabled(); + + fireEvent.click(importButton); + + await waitFor(() => + expect(bridge.browserImportData).toHaveBeenCalledWith({ + sourceId: "chrome:default", + passwords: true, + cookies: true, + acknowledgeProtectedData: true, + }), + ); + expect(useBrowserImportStore.getState().open).toBe(false); + }); + + it("keeps the dialog open when no browser data could be imported", async () => { + bridge.browserImportData.mockResolvedValue({ + passwordsImported: 0, + cookiesImported: 0, + passwordsSkipped: 1, + cookiesSkipped: 1, + protectedItemsSkipped: 2, + errors: ["app-bound-data-skipped"], + }); + render(); + + await screen.findByText("Protected browser data"); + fireEvent.click( + screen.getByRole("checkbox", { + name: "I understand that protected browser data may be skipped.", + }), + ); + fireEvent.click(screen.getByRole("button", { name: "Import" })); + + await waitFor(() => expect(bridge.browserImportData).toHaveBeenCalledOnce()); + expect(useBrowserImportStore.getState().open).toBe(true); + }); +}); diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx new file mode 100644 index 000000000..974faa8ba --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserImportModal.tsx @@ -0,0 +1,222 @@ +import { useEffect, useState } from "react"; +import { Button, Checkbox, Modal, toast } from "@heroui/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { Cookie, KeyRound, Loader2, ShieldAlert } from "lucide-react"; +import { readBridge } from "@/renderer/bridge"; +import { Select, ToggleSwitch } from "@/renderer/components/common"; +import { useBrowserImportStore } from "@/renderer/state/browserImportStore"; +import type { BrowserImportSourceInfo } from "@/shared/ipc"; + +export function BrowserImportModal() { + const { t } = useLingui(); + const open = useBrowserImportStore((state) => state.open); + const setOpen = useBrowserImportStore((state) => state.setOpen); + const [sources, setSources] = useState([]); + const [sourceId, setSourceId] = useState(""); + const [passwords, setPasswords] = useState(true); + const [cookies, setCookies] = useState(true); + const [acknowledgeProtectedData, setAcknowledgeProtectedData] = useState(false); + const [loading, setLoading] = useState(false); + const [importing, setImporting] = useState(false); + + useEffect(() => { + if (!open) return; + let cancelled = false; + setSources([]); + setSourceId(""); + setLoading(true); + setAcknowledgeProtectedData(false); + readBridge() + .browserListImportSources() + .then((items) => { + if (cancelled) return; + setSources(items); + const first = items[0]; + setSourceId(first?.id ?? ""); + setPasswords(first?.supportsPasswords === true); + setCookies(first?.supportsCookies === true); + }) + .catch(() => { + if (!cancelled) toast.danger(t`Unable to find browser data to import.`); + }) + .finally(() => { + if (!cancelled) setLoading(false); + }); + return () => { + cancelled = true; + }; + }, [open, t]); + + const selectedSource = sources.find((source) => source.id === sourceId); + const needsProtectedDataAcknowledgement = + selectedSource?.hasAppBoundData === true && (passwords || cookies); + const canImport = + !!selectedSource && + ((passwords && selectedSource.supportsPasswords) || + (cookies && selectedSource.supportsCookies)) && + (!needsProtectedDataAcknowledgement || acknowledgeProtectedData); + const sourceOptions = sources.map((source) => ({ + id: source.id, + label: `${source.browserLabel} — ${source.profileLabel}`, + })); + + function selectSource(id: string) { + const source = sources.find((item) => item.id === id); + setSourceId(id); + setPasswords(source?.supportsPasswords === true); + setCookies(source?.supportsCookies === true); + setAcknowledgeProtectedData(false); + } + + async function importData() { + if (!selectedSource || !canImport) return; + setImporting(true); + try { + const result = await readBridge().browserImportData({ + sourceId: selectedSource.id, + passwords: passwords && selectedSource.supportsPasswords, + cookies: cookies && selectedSource.supportsCookies, + acknowledgeProtectedData, + }); + if (result.passwordsImported + result.cookiesImported === 0) { + toast.danger(t`Unable to import browser data.`); + return; + } + toast.success( + t`Imported passwords: ${result.passwordsImported}. Imported cookies: ${result.cookiesImported}.`, + ); + const skipped = + result.passwordsSkipped + result.cookiesSkipped + result.protectedItemsSkipped; + if (skipped > 0 || result.errors.length > 0) { + toast.warning( + t`Some browser data was skipped. Protected items skipped: ${result.protectedItemsSkipped}.`, + ); + } + setOpen(false); + } catch { + toast.danger(t`Unable to import browser data.`); + } finally { + setImporting(false); + } + } + + return ( + !nextOpen && !importing && setOpen(false)} + > + + + + + + Import from your browser + +

+ Choose data to bring over to the built-in browser. +

+
+ + {loading ? ( +
+ + Looking for browser profiles… +
+ ) : sources.length === 0 ? ( +
+ No supported browser profiles were found on this device. +
+ ) : ( + <> + setQuery(event.currentTarget.value)} + className="pl-8 text-xs" + /> + + + + + {visibleCredentials.length === 0 ? ( +
+ + + +
+

+ {credentials.length === 0 ? ( + No saved passwords + ) : ( + No passwords match your search + )} +

+ {credentials.length === 0 ? ( +

+ Add a password or import one from another browser. +

+ ) : null} +
+
+ ) : ( +
+ {visibleCredentials.map((credential, index) => { + const revealedPassword = revealedPasswords[credential.id]; + const busy = busyCredentialId === credential.id; + return ( +
0 ? "border-t border-border" : ""}`} + > + + + +
+

+ {credential.origin} +

+

{credential.username}

+ {credential.source ? ( +

+ Imported from {credential.source} +

+ ) : null} +
+ + {revealedPassword ?? "••••••••"} + +
+ + + + +
+
+ ); + })} +
+ )} + + ) : ( +
+
+ + + +

+ Import browser data +

+

+ Bring saved passwords and cookies over from an installed browser. +

+ +
+
+ + + +

+ Local password protection +

+

+ + Passwords are encrypted in Poracode's local credential store. Password values + are requested only when you reveal, copy, or edit them. + +

+
+
+ )} + + + + {editor ? ( + setEditor(null)} + onSaved={() => { + setEditor(null); + }} + /> + ) : null} + + This removes the saved password for {pendingDelete?.origin} from + Poracode. + + } + confirmLabel={t`Delete`} + onConfirm={() => void deleteCredential()} + onClose={() => setPendingDelete(null)} + /> + + ); +} + +function CredentialEditorModal(props: { + initial: CredentialDraft; + onClose: () => void; + onSaved: () => void; +}) { + const { t } = useLingui(); + const [origin, setOrigin] = useState(props.initial.origin); + const [username, setUsername] = useState(props.initial.username); + const [password, setPassword] = useState(props.initial.password); + const [saving, setSaving] = useState(false); + + async function save() { + setSaving(true); + try { + await readBridge().browserUpsertCredential({ + ...(props.initial.id ? { id: props.initial.id } : {}), + origin: origin.trim(), + username, + password, + }); + props.onSaved(); + } catch { + toast.danger(t`Unable to save the password.`); + } finally { + setSaving(false); + } + } + + return ( + !open && !saving && props.onClose()} + > + + + + + + {props.initial.id ? Edit password : Add password} + + + + + + + + + + + + + + + ); +} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx index 3e92809d7..ce8b11ca4 100644 --- a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserTabStrip.tsx @@ -1,4 +1,4 @@ -import { Globe, Plus, X } from "lucide-react"; +import { Download, Globe, KeyRound, Plus, X } from "lucide-react"; import { useState, type MouseEvent as ReactMouseEvent, type ReactNode } from "react"; import { useLingui } from "@lingui/react/macro"; import { useShallow } from "zustand/shallow"; @@ -9,7 +9,11 @@ import type { BrowserTabGroupInfo, BrowserTabInfo } from "@/shared/ipc"; import { BrowserTabGroupMenu } from "./BrowserTabGroupMenu"; import { groupColor } from "./groupColors"; -function TabFavicon(props: { faviconUrl?: string; loading: boolean }) { +function TabFavicon(props: { + faviconUrl?: string; + loading: boolean; + internalPage?: BrowserTabInfo["internalPage"]; +}) { if (props.loading) { return ( @@ -17,6 +21,12 @@ function TabFavicon(props: { faviconUrl?: string; loading: boolean }) { ); } + if (props.internalPage === "downloads") { + return ; + } + if (props.internalPage === "passwords") { + return ; + } if (props.faviconUrl) { return ( void; variant?: "row ) => { const active = tab.tabId === activeTabId; const attention = !active && tab.tabId === attentionTabId; + const displayTitle = + tab.internalPage === "downloads" + ? t`Download history` + : tab.internalPage === "passwords" + ? t`Password manager` + : tab.title || tab.url || t`New tab`; const activate = () => { if (!active) { readBridge() @@ -206,8 +222,9 @@ export function BrowserTabStrip(props: { onCreateTab: () => void; variant?: "row - {tab.title || tab.url || t`New tab`} + {displayTitle} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {bookmarks.length > 0 ? : null} - {bookmarks.slice(0, 20).map((b) => ( - - {b.faviconUrl ? ( - { - (e.currentTarget as HTMLImageElement).style.display = "none"; - }} - /> - ) : ( - - - - )} - - - ))} - - - - - - - - - - - - - - {recentHistory.length === 0 ? ( - - - - ) : ( - recentHistory.map((h) => ( - - - - - - - )) - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ); } diff --git a/src/shared/ipc/events.ts b/src/shared/ipc/events.ts index 713e02861..ac3b65335 100644 --- a/src/shared/ipc/events.ts +++ b/src/shared/ipc/events.ts @@ -16,7 +16,12 @@ import type { UsageLoginDeviceCode, UsageSnapshot, } from "../contracts"; -import type { BrowserState, BrowserTabInfo } from "./procedures/browser"; +import type { + BrowserDownloadInfo, + BrowserFindResult, + BrowserState, + BrowserTabInfo, +} from "./procedures/browser"; import type { BrowserLinkPresentationMode, CrossagentRoutingOverride } from "../settings"; import type { IpcProcedurePayload, SupervisorProcedureName } from "./procedureMap"; import type { MessageKey } from "../messages"; @@ -154,6 +159,11 @@ export type BrowserEvent = | { type: "state"; state: BrowserState } | { type: "tab-updated"; tab: BrowserTabInfo } | { type: "tab-attention"; tabId: string } + | { type: "find-open-requested"; tabId: string } + | { type: "find-result"; result: BrowserFindResult } + | { type: "download-updated"; download: BrowserDownloadInfo } + | { type: "download-removed"; downloadId: string } + | { type: "credentials-updated" } | { type: "open-panel"; mode?: BrowserLinkPresentationMode } | { type: "usage-login-confirmation"; request: UsageLoginConfirmationRequest } | { type: "usage-login-confirmation-closed"; requestId: string } diff --git a/src/shared/ipc/index.ts b/src/shared/ipc/index.ts index 12739d03e..2b61512c7 100644 --- a/src/shared/ipc/index.ts +++ b/src/shared/ipc/index.ts @@ -46,13 +46,23 @@ export { type UpdateStatus, } from "./events"; export { + browserDownloadStateSchema, browserPickResultSchema, browserStateSchema, browserTabGroupSchema, browserTabSchema, type BrowserBookmarkInfo, + type BrowserCredentialInfo, + type BrowserDeviceEmulation, + type BrowserDownloadInfo, + type BrowserDownloadState, + type BrowserFindResult, type BrowserHistoryEntryInfo, + type BrowserImportResult, + type BrowserImportSourceInfo, + type BrowserInternalPage, type BrowserPickResult, + type BrowserPrintResult, type BrowserRect, type BrowserState, type BrowserStartPickerResult, diff --git a/src/shared/ipc/procedureMap.ts b/src/shared/ipc/procedureMap.ts index 602a2a444..14dd1214d 100644 --- a/src/shared/ipc/procedureMap.ts +++ b/src/shared/ipc/procedureMap.ts @@ -137,6 +137,7 @@ export const MAIN_LOCAL_PROCEDURE_NAMES = [ "installUpdate", "browserGetState", "browserCreateTab", + "browserOpenInternalPage", "browserCloseTab", "browserActivateTab", "browserMoveTab", @@ -152,6 +153,11 @@ export const MAIN_LOCAL_PROCEDURE_NAMES = [ "browserReload", "browserHardReload", "browserToggleDevTools", + "browserFindInPage", + "browserStopFindInPage", + "browserPrint", + "browserSetZoomFactor", + "browserSetDeviceEmulation", "browserClearHistory", "browserClearCookies", "browserClearCache", @@ -165,6 +171,14 @@ export const MAIN_LOCAL_PROCEDURE_NAMES = [ "browserRemoveBookmark", "browserSetBookmarkBarVisible", "browserRecentHistory", + "browserGetDownloads", + "browserDownloadAction", + "browserListCredentials", + "browserGetCredentialPassword", + "browserUpsertCredential", + "browserDeleteCredential", + "browserListImportSources", + "browserImportData", "browserExtractToWindow", "browserInjectToMain", "startUsageLogin", diff --git a/src/shared/ipc/procedures/browser.ts b/src/shared/ipc/procedures/browser.ts index 5b5831b32..22c40178f 100644 --- a/src/shared/ipc/procedures/browser.ts +++ b/src/shared/ipc/procedures/browser.ts @@ -32,6 +32,20 @@ export const browserTabGroupSchema = z.object({ }); export type BrowserTabGroupInfo = z.infer; +export const browserInternalPageSchema = z.enum(["downloads", "passwords"]); +export type BrowserInternalPage = z.infer; + +export const browserDeviceEmulationSchema = z.object({ + width: z.number().int().min(240).max(7680), + height: z.number().int().min(240).max(7680), + deviceScaleFactor: z.number().min(0.5).max(4), + scale: z.number().min(0.25).max(2), + mobile: z.boolean(), + touch: z.boolean(), + preset: z.string().optional(), +}); +export type BrowserDeviceEmulation = z.infer; + export const browserTabSchema = z.object({ tabId: z.string(), url: z.string(), @@ -41,6 +55,9 @@ export const browserTabSchema = z.object({ canGoBack: z.boolean(), canGoForward: z.boolean(), devToolsOpen: z.boolean().optional(), + internalPage: browserInternalPageSchema.optional(), + zoomFactor: z.number().min(0.25).max(5).optional(), + deviceEmulation: browserDeviceEmulationSchema.optional(), groupId: z.string().optional(), }); export type BrowserTabInfo = z.infer; @@ -176,6 +193,133 @@ export const browserSetBookmarkBarVisiblePayloadSchema = z.object({ visible: z.boolean(), }); +export const browserOpenInternalPagePayloadSchema = z.object({ + page: browserInternalPageSchema, +}); + +export const browserFindInPagePayloadSchema = z.object({ + tabId: z.string().min(1), + text: z.string().min(1), + forward: z.boolean(), + findNext: z.boolean(), + matchCase: z.boolean(), +}); + +export const browserStopFindInPagePayloadSchema = z.object({ + tabId: z.string().min(1), + action: z.enum(["clearSelection", "keepSelection", "activateSelection"]), +}); + +export const browserFindResultSchema = z.object({ + tabId: z.string(), + requestId: z.number().int(), + activeMatchOrdinal: z.number().int().nonnegative(), + matches: z.number().int().nonnegative(), + finalUpdate: z.boolean(), +}); +export type BrowserFindResult = z.infer; + +export const browserPrintResultSchema = z.object({ + ok: z.boolean(), + error: z.string().optional(), +}); +export type BrowserPrintResult = z.infer; + +export const browserSetZoomFactorPayloadSchema = z.object({ + tabId: z.string().min(1), + zoomFactor: z.number().min(0.25).max(5), +}); + +export const browserSetDeviceEmulationPayloadSchema = z.object({ + tabId: z.string().min(1), + emulation: browserDeviceEmulationSchema.nullable(), +}); + +export const browserDownloadStateSchema = z.enum([ + "progressing", + "paused", + "completed", + "cancelled", + "interrupted", +]); +export type BrowserDownloadState = z.infer; + +export const browserDownloadSchema = z.object({ + id: z.string(), + filename: z.string(), + url: z.string(), + mimeType: z.string(), + state: browserDownloadStateSchema, + receivedBytes: z.number().int().nonnegative(), + totalBytes: z.number().int().nonnegative(), + startTime: z.number(), + endTime: z.number().optional(), + canResume: z.boolean(), +}); +export type BrowserDownloadInfo = z.infer; + +export const browserDownloadActionPayloadSchema = z.object({ + id: z.string().min(1), + action: z.enum(["pause", "resume", "cancel", "remove", "open", "show-in-folder"]), +}); + +export const browserCredentialSchema = z.object({ + id: z.string(), + origin: z.string(), + username: z.string(), + createdAt: z.number(), + updatedAt: z.number(), + source: z.string().optional(), +}); +export type BrowserCredentialInfo = z.infer; + +export const browserCredentialIdPayloadSchema = z.object({ + id: z.string().min(1), +}); + +export const browserCredentialPasswordResultSchema = z.object({ + password: z.string(), +}); + +export const browserUpsertCredentialPayloadSchema = z.object({ + id: z.string().optional(), + origin: z.string().min(1), + username: z.string(), + password: z.string(), +}); + +export const browserImportSourceSchema = z.object({ + id: z.string(), + browser: z.enum(["chrome", "edge", "brave", "chromium"]), + browserLabel: z.string(), + profileLabel: z.string(), + supportsPasswords: z.boolean(), + supportsCookies: z.boolean(), + hasAppBoundData: z.boolean(), +}); +export type BrowserImportSourceInfo = z.infer; + +export const browserImportDataPayloadSchema = z + .object({ + sourceId: z.string().min(1), + passwords: z.boolean(), + cookies: z.boolean(), + acknowledgeProtectedData: z.boolean(), + }) + .refine((value) => value.passwords || value.cookies, { + message: "Select at least one browser data type", + }); + +export const browserImportResultSchema = z.object({ + passwordsImported: z.number().int().nonnegative(), + cookiesImported: z.number().int().nonnegative(), + passwordsSkipped: z.number().int().nonnegative(), + cookiesSkipped: z.number().int().nonnegative(), + protectedItemsSkipped: z.number().int().nonnegative(), + errors: z.array(z.string()), +}); +export type BrowserImportResult = z.infer; + export const browserRecentHistoryPayloadSchema = z.object({ limit: z.number().int().positive().max(50), }); @@ -190,6 +334,11 @@ export const browserProcedures = { BrowserTabInfo, "main-local" >("browserCreateTab", "main-local", browserCreateTabPayloadSchema), + browserOpenInternalPage: definePayloadProcedure< + z.infer, + BrowserTabInfo, + "main-local" + >("browserOpenInternalPage", "main-local", browserOpenInternalPagePayloadSchema), browserCloseTab: definePayloadProcedure< z.infer, void, @@ -265,6 +414,31 @@ export const browserProcedures = { void, "main-local" >("browserToggleDevTools", "main-local", browserTabIdPayloadSchema), + browserFindInPage: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserFindInPage", "main-local", browserFindInPagePayloadSchema), + browserStopFindInPage: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserStopFindInPage", "main-local", browserStopFindInPagePayloadSchema), + browserPrint: definePayloadProcedure< + z.infer, + BrowserPrintResult, + "main-local" + >("browserPrint", "main-local", browserTabIdPayloadSchema), + browserSetZoomFactor: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserSetZoomFactor", "main-local", browserSetZoomFactorPayloadSchema), + browserSetDeviceEmulation: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserSetDeviceEmulation", "main-local", browserSetDeviceEmulationPayloadSchema), browserClearHistory: definePayloadProcedure< z.infer, void, @@ -329,6 +503,43 @@ export const browserProcedures = { BrowserHistoryEntryInfo[], "main-local" >("browserRecentHistory", "main-local", browserRecentHistoryPayloadSchema), + browserGetDownloads: defineNoArgProcedure( + "browserGetDownloads", + "main-local", + ), + browserDownloadAction: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserDownloadAction", "main-local", browserDownloadActionPayloadSchema), + browserListCredentials: defineNoArgProcedure( + "browserListCredentials", + "main-local", + ), + browserGetCredentialPassword: definePayloadProcedure< + z.infer, + z.infer, + "main-local" + >("browserGetCredentialPassword", "main-local", browserCredentialIdPayloadSchema), + browserUpsertCredential: definePayloadProcedure< + z.infer, + BrowserCredentialInfo, + "main-local" + >("browserUpsertCredential", "main-local", browserUpsertCredentialPayloadSchema), + browserDeleteCredential: definePayloadProcedure< + z.infer, + void, + "main-local" + >("browserDeleteCredential", "main-local", browserCredentialIdPayloadSchema), + browserListImportSources: defineNoArgProcedure( + "browserListImportSources", + "main-local", + ), + browserImportData: definePayloadProcedure< + z.infer, + BrowserImportResult, + "main-local" + >("browserImportData", "main-local", browserImportDataPayloadSchema), browserExtractToWindow: defineNoArgProcedure( "browserExtractToWindow", "main-local",