diff --git a/src/main/browser/BrowserPanelManager.ts b/src/main/browser/BrowserPanelManager.ts index f0402f8f..6f1c6d22 100644 --- a/src/main/browser/BrowserPanelManager.ts +++ b/src/main/browser/BrowserPanelManager.ts @@ -15,6 +15,8 @@ import { saveClipboardImageFile } from "../attachments/localFiles"; import { IPC_EVENT_CHANNELS } from "@/shared/ipc"; import { BrowserLoginCaptureCoordinator } from "./BrowserLoginCaptureCoordinator"; import { BrowserTab, resolveWebContentsById } from "./BrowserTab"; +import { BrowserHistoryStore, fetchSearchSuggestions } from "./browserHistory"; +import { BrowserBookmarkStore, type BrowserBookmark } from "./browserBookmarks"; import { PICKER_COMMIT_ORIGIN, onPickerCommit } from "./picker/pickerProtocol"; import { buildPickerScript } from "./picker/pickerScript"; @@ -46,13 +48,20 @@ type PickerPayload = title: string; }; +interface BrowserPanelManagerOptions { + isExtracted?: () => boolean; + focusExtractedWindow?: () => void; +} + export class BrowserPanelManager { private tabs: BrowserTab[] = []; private activeTabId: string | null = null; - private host: BrowserWindow | null = null; + private hosts = new Set(); private pendingPicker: PendingPicker | null = null; private unsubscribePicker: (() => void) | null = null; private persistTimer: ReturnType | null = null; + private readonly history = new BrowserHistoryStore(); + private readonly bookmarks = new BrowserBookmarkStore(); private restored = false; private pickerKeyCleanup: (() => void) | null = null; private readonly loginCoordinator = new BrowserLoginCaptureCoordinator({ @@ -60,12 +69,13 @@ export class BrowserPanelManager { closeTab: (tabId) => this.closeTab(tabId), findTab: (tabId) => this.findTab(tabId), emit: (event) => this.emit(event), - hasHostWindow: () => this.host !== null && !this.host.isDestroyed(), + hasHostWindow: () => this.hasHostWindow(), }); constructor( private readonly paths: LightcodePaths, private readonly browserUserAgent: string, + private readonly options: BrowserPanelManagerOptions = {}, ) { this.unsubscribePicker = onPickerCommit((commit) => this.onPickerCommit(commit)); } @@ -112,16 +122,21 @@ export class BrowserPanelManager { } bindHost(window: BrowserWindow): void { - this.host = window; - window.webContents.on("before-input-event", (event, input) => { + this.hosts.add(window); + const onBeforeInputEvent = (event: Electron.Event, input: Electron.Input) => { if (!this.pendingPicker || !isEscapeKeyDown(input)) return; event.preventDefault(); this.cancelPicker(); - }); + }; + window.webContents.on("before-input-event", onBeforeInputEvent); window.once("closed", () => { - this.dispose(); + this.hosts.delete(window); + try { + window.webContents.removeListener("before-input-event", onBeforeInputEvent); + } catch {} }); void this.restoreFromDisk(); + this.emitState(); } dispose(): void { @@ -138,6 +153,7 @@ export class BrowserPanelManager { } this.tabs = []; this.activeTabId = null; + this.hosts.clear(); } private clearPickerShortcut(): void { @@ -164,20 +180,40 @@ export class BrowserPanelManager { } private emit(event: BrowserEvent): void { - if (!this.host || this.host.isDestroyed()) return; - try { - this.host.webContents.send(IPC_EVENT_CHANNELS.browserEvent, event); - } catch {} + for (const host of this.hosts) { + if (host.isDestroyed()) { + this.hosts.delete(host); + continue; + } + try { + host.webContents.send(IPC_EVENT_CHANNELS.browserEvent, event); + } catch {} + } } private emitState(): void { this.emit({ type: "state", state: this.snapshot() }); } + notifyState(): void { + this.emitState(); + } + revealPanel(): void { + if (this.options.isExtracted?.()) { + this.options.focusExtractedWindow?.(); + return; + } this.emit({ type: "open-panel" }); } + private hasHostWindow(): boolean { + for (const host of this.hosts) { + if (!host.isDestroyed()) return true; + } + return false; + } + private readLinkSettings(): { linkOpenTarget: BrowserLinkOpenTarget; linkPresentationMode: BrowserLinkPresentationMode; @@ -218,7 +254,11 @@ export class BrowserPanelManager { return this.openSystemBrowser(url.toString()); } - this.emit({ type: "open-panel", mode: settings.linkPresentationMode }); + if (this.options.isExtracted?.()) { + this.options.focusExtractedWindow?.(); + } else { + this.emit({ type: "open-panel", mode: settings.linkPresentationMode }); + } void this.createTab({ url: url.toString(), activate: true }).catch(() => {}); return true; } @@ -241,9 +281,27 @@ export class BrowserPanelManager { return { tabs: this.tabs.map((t) => this.toInfo(t)), activeTabId: this.activeTabId, + extracted: this.options.isExtracted?.() === true, + bookmarks: this.bookmarks.list(), + bookmarkBarVisible: this.bookmarks.isBarVisible(), }; } + addBookmark(bookmark: BrowserBookmark): void { + this.bookmarks.add(bookmark); + this.emitState(); + } + + removeBookmark(url: string): void { + this.bookmarks.remove(url); + this.emitState(); + } + + setBookmarkBarVisible(visible: boolean): void { + this.bookmarks.setBarVisible(visible); + this.emitState(); + } + private findTab(tabId: string): BrowserTab | undefined { return this.tabs.find((t) => t.tabId === tabId); } @@ -251,10 +309,15 @@ export class BrowserPanelManager { attachWebContents(tabId: string, webContentsId: number): void { const tab = this.findTab(tabId); if (!tab) return; - if (this.host?.webContents.id === webContentsId) 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; + } const wc = resolveWebContentsById(webContentsId); if (!wc) return; - if (this.host?.webContents === wc) return; + for (const host of this.hosts) { + if (host.webContents === wc) return; + } tab.attach(wc); } @@ -266,6 +329,7 @@ export class BrowserPanelManager { userAgent: this.browserUserAgent, onUpdate: (snap) => { this.emit({ type: "tab-updated", tab: { ...snap } }); + if (!snap.loading) this.history.record(snap.url, snap.title, Date.now()); this.schedulePersist(); }, onAttention: (id) => { @@ -374,11 +438,25 @@ export class BrowserPanelManager { } async clearHistory(tabId: string): Promise { + this.history.clear(); const t = this.findTab(tabId); if (!t) return; t.clearHistory(); } + async suggest(query: string): Promise<{ + history: Array<{ url: string; title: string }>; + suggestions: string[]; + }> { + const history = this.history.query(query, 6).map((e) => ({ url: e.url, title: e.title })); + const suggestions = await fetchSearchSuggestions(query, this.browserUserAgent); + return { history, suggestions }; + } + + recentHistory(limit: number): Array<{ url: string; title: string }> { + return this.history.recent(limit).map((e) => ({ url: e.url, title: e.title })); + } + async clearCookies(tabId: string): Promise { const t = this.findTab(tabId); if (!t) return; diff --git a/src/main/browser/browserBookmarks.test.ts b/src/main/browser/browserBookmarks.test.ts new file mode 100644 index 00000000..39aaf89e --- /dev/null +++ b/src/main/browser/browserBookmarks.test.ts @@ -0,0 +1,50 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const state = new Map(); +vi.mock("../db", () => ({ + dbGetState: (k: string) => state.get(k) ?? null, + dbSetState: (k: string, v: string) => { + state.set(k, v); + }, +})); + +import { BrowserBookmarkStore } from "./browserBookmarks"; + +describe("BrowserBookmarkStore", () => { + beforeEach(() => state.clear()); + + it("adds and dedupes by url", () => { + const s = new BrowserBookmarkStore(); + s.add({ url: "https://a.com/", title: "A", createdAt: 1 }); + s.add({ url: "https://a.com/", title: "A again", createdAt: 2 }); + expect(s.list()).toHaveLength(1); + expect(s.list()[0]?.title).toBe("A"); + }); + + it("ignores non-http(s) urls", () => { + const s = new BrowserBookmarkStore(); + s.add({ url: "about:blank", title: "x", createdAt: 1 }); + expect(s.list()).toHaveLength(0); + }); + + it("removes by url", () => { + const s = new BrowserBookmarkStore(); + s.add({ url: "https://a.com/", title: "A", createdAt: 1 }); + s.remove("https://a.com/"); + expect(s.list()).toHaveLength(0); + }); + + it("persists across instances", () => { + const s = new BrowserBookmarkStore(); + s.add({ url: "https://a.com/", title: "A", createdAt: 1 }); + expect(new BrowserBookmarkStore().list()).toHaveLength(1); + }); + + it("toggles and persists bar visibility", () => { + const s = new BrowserBookmarkStore(); + expect(s.isBarVisible()).toBe(false); + s.setBarVisible(true); + expect(s.isBarVisible()).toBe(true); + expect(new BrowserBookmarkStore().isBarVisible()).toBe(true); + }); +}); diff --git a/src/main/browser/browserBookmarks.ts b/src/main/browser/browserBookmarks.ts new file mode 100644 index 00000000..7fa49322 --- /dev/null +++ b/src/main/browser/browserBookmarks.ts @@ -0,0 +1,90 @@ +import { dbGetState, dbSetState } from "../db"; + +const BOOKMARKS_KEY = "browser-bookmarks-v1"; +const BAR_VISIBLE_KEY = "browser-bookmark-bar-visible-v1"; + +export interface BrowserBookmark { + url: string; + title: string; + faviconUrl?: string; + createdAt: number; +} + +/** + * Persistent bookmarks + bookmark-bar visibility, stored as app state (same + * mechanism as tabs/history). Deduped by URL. + */ +export class BrowserBookmarkStore { + private bookmarks: BrowserBookmark[] = []; + private barVisible = false; + private loaded = false; + + private load(): void { + if (this.loaded) return; + this.loaded = true; + try { + const raw = dbGetState(BOOKMARKS_KEY); + if (raw) { + const arr = JSON.parse(raw) as BrowserBookmark[]; + if (Array.isArray(arr)) { + this.bookmarks = arr.filter( + (b): b is BrowserBookmark => + !!b && typeof b.url === "string" && typeof b.title === "string", + ); + } + } + } catch {} + try { + this.barVisible = dbGetState(BAR_VISIBLE_KEY) === "1"; + } catch {} + } + + list(): BrowserBookmark[] { + this.load(); + return this.bookmarks; + } + + isBarVisible(): boolean { + this.load(); + return this.barVisible; + } + + add(bookmark: BrowserBookmark): void { + this.load(); + if (!/^https?:\/\//i.test(bookmark.url)) return; + if (this.bookmarks.some((b) => b.url === bookmark.url)) return; + this.bookmarks = [ + ...this.bookmarks, + { + url: bookmark.url, + title: bookmark.title || bookmark.url, + createdAt: bookmark.createdAt, + ...(bookmark.faviconUrl ? { faviconUrl: bookmark.faviconUrl } : {}), + }, + ]; + this.persist(); + } + + remove(url: string): void { + this.load(); + const next = this.bookmarks.filter((b) => b.url !== url); + if (next.length === this.bookmarks.length) return; + this.bookmarks = next; + this.persist(); + } + + setBarVisible(visible: boolean): void { + this.load(); + if (this.barVisible === visible) return; + this.barVisible = visible; + try { + dbSetState(BAR_VISIBLE_KEY, visible ? "1" : "0"); + } catch {} + } + + private persist(): void { + try { + dbSetState(BOOKMARKS_KEY, JSON.stringify(this.bookmarks)); + } catch {} + } +} diff --git a/src/main/browser/browserHistory.test.ts b/src/main/browser/browserHistory.test.ts new file mode 100644 index 00000000..86ff3d63 --- /dev/null +++ b/src/main/browser/browserHistory.test.ts @@ -0,0 +1,84 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const state = new Map(); +vi.mock("../db", () => ({ + dbGetState: (k: string) => state.get(k) ?? null, + dbSetState: (k: string, v: string) => { + state.set(k, v); + }, +})); + +import { BrowserHistoryStore, fetchSearchSuggestions } from "./browserHistory"; + +describe("BrowserHistoryStore", () => { + beforeEach(() => state.clear()); + + it("ranks frequent prefix matches first", () => { + const h = new BrowserHistoryStore(); + h.record("https://github.com/", "GitHub", 1000); + h.record("https://github.com/", "GitHub", 2000); + h.record("https://example.com/gh", "Example GH", 1500); + expect(h.query("git", 5)[0]?.url).toBe("https://github.com/"); + }); + + it("dedupes by url and keeps the latest title", () => { + const h = new BrowserHistoryStore(); + h.record("https://a.com/", "A", 1); + h.record("https://a.com/", "A2", 2); + const res = h.query("a.com", 5); + expect(res).toHaveLength(1); + expect(res[0]?.title).toBe("A2"); + }); + + it("matches both title and url substrings", () => { + const h = new BrowserHistoryStore(); + h.record("https://news.ycombinator.com/", "Hacker News", 1); + expect(h.query("hacker", 5)).toHaveLength(1); + expect(h.query("ycombinator", 5)).toHaveLength(1); + }); + + it("ignores non-http(s) urls", () => { + const h = new BrowserHistoryStore(); + h.record("about:blank", "blank", 1); + expect(h.query("blank", 5)).toHaveLength(0); + }); + + it("clears all entries", () => { + const h = new BrowserHistoryStore(); + h.record("https://a.com/", "A", 1); + h.clear(); + expect(h.query("a", 5)).toHaveLength(0); + }); +}); + +describe("fetchSearchSuggestions", () => { + afterEach(() => vi.unstubAllGlobals()); + + it("parses the DuckDuckGo type=list format", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ ok: true, json: async () => ["q", ["s1", "s2"]] }), + ); + expect(await fetchSearchSuggestions("q", "ua")).toEqual(["s1", "s2"]); + }); + + it("parses an array of phrase objects", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ ok: true, json: async () => [{ phrase: "a" }, { phrase: "b" }] }), + ); + expect(await fetchSearchSuggestions("q", "ua")).toEqual(["a", "b"]); + }); + + it("returns [] on network failure", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("offline"))); + expect(await fetchSearchSuggestions("q", "ua")).toEqual([]); + }); + + it("returns [] for an empty query without fetching", async () => { + const fetchMock = vi.fn<() => Promise>(); + vi.stubGlobal("fetch", fetchMock); + expect(await fetchSearchSuggestions(" ", "ua")).toEqual([]); + expect(fetchMock).not.toHaveBeenCalled(); + }); +}); diff --git a/src/main/browser/browserHistory.ts b/src/main/browser/browserHistory.ts new file mode 100644 index 00000000..01ee2a60 --- /dev/null +++ b/src/main/browser/browserHistory.ts @@ -0,0 +1,157 @@ +import { dbGetState, dbSetState } from "../db"; +import { stripScheme } from "@/shared/url"; + +const HISTORY_KEY = "browser-history-v1"; +const MAX_ENTRIES = 2000; +const PERSIST_DEBOUNCE_MS = 1000; +const SUGGEST_TIMEOUT_MS = 2500; + +export interface BrowserHistoryEntry { + url: string; + title: string; + visitCount: number; + lastVisitedAt: number; +} + +/** + * Persistent visited-URL history backing the address-bar omnibox. Stored as a + * JSON blob in app state (same mechanism the tab list uses), keyed by URL so a + * revisit bumps frequency/recency instead of duplicating. + */ +export class BrowserHistoryStore { + private entries = new Map(); + private loaded = false; + private persistTimer: ReturnType | null = null; + + private load(): void { + if (this.loaded) return; + this.loaded = true; + try { + const raw = dbGetState(HISTORY_KEY); + if (!raw) return; + const arr = JSON.parse(raw) as BrowserHistoryEntry[]; + if (!Array.isArray(arr)) return; + for (const e of arr) { + if (e && typeof e.url === "string" && typeof e.title === "string") { + this.entries.set(e.url, { + url: e.url, + title: e.title, + visitCount: typeof e.visitCount === "number" ? e.visitCount : 1, + lastVisitedAt: typeof e.lastVisitedAt === "number" ? e.lastVisitedAt : 0, + }); + } + } + } catch {} + } + + record(url: string, title: string, now: number): void { + if (!/^https?:\/\//i.test(url)) return; + this.load(); + const existing = this.entries.get(url); + if (existing) { + existing.visitCount += 1; + existing.lastVisitedAt = now; + if (title) existing.title = title; + } else { + this.entries.set(url, { url, title: title || url, visitCount: 1, lastVisitedAt: now }); + this.prune(); + } + this.schedulePersist(); + } + + query(query: string, limit: number): BrowserHistoryEntry[] { + this.load(); + const q = query.trim().toLowerCase(); + if (!q) return []; + const matched: Array<{ entry: BrowserHistoryEntry; score: number }> = []; + for (const entry of this.entries.values()) { + const url = entry.url.toLowerCase(); + const title = entry.title.toLowerCase(); + const urlIdx = url.indexOf(q); + const titleIdx = title.indexOf(q); + if (urlIdx === -1 && titleIdx === -1) continue; + // Prefix match on the URL (after the scheme) ranks highest, then frequency + // and recency. lastVisitedAt is a ms timestamp; scale it down so it acts + // as a tiebreaker rather than dominating the visit-count signal. + const startsWith = stripScheme(url).startsWith(q) || url.startsWith(q); + const score = + (startsWith ? 1000 : 0) + entry.visitCount * 10 + entry.lastVisitedAt / 1_000_000_000; + matched.push({ entry, score }); + } + matched.sort((a, b) => b.score - a.score); + return matched.slice(0, limit).map((m) => m.entry); + } + + recent(limit: number): BrowserHistoryEntry[] { + this.load(); + return [...this.entries.values()] + .sort((a, b) => b.lastVisitedAt - a.lastVisitedAt) + .slice(0, limit); + } + + clear(): void { + this.loaded = true; + this.entries.clear(); + this.schedulePersist(); + } + + private prune(): void { + if (this.entries.size <= MAX_ENTRIES) return; + const sorted = [...this.entries.values()].sort((a, b) => a.lastVisitedAt - b.lastVisitedAt); + const removeCount = this.entries.size - MAX_ENTRIES; + for (let i = 0; i < removeCount; i++) { + const victim = sorted[i]; + if (victim) this.entries.delete(victim.url); + } + } + + private schedulePersist(): void { + if (this.persistTimer) clearTimeout(this.persistTimer); + this.persistTimer = setTimeout(() => { + this.persistTimer = null; + try { + dbSetState(HISTORY_KEY, JSON.stringify([...this.entries.values()])); + } catch {} + }, PERSIST_DEBOUNCE_MS); + } +} + +/** + * Fetch search-engine autocomplete suggestions from DuckDuckGo's `ac` endpoint. + * Runs in the main process (no CORS), returns [] on any failure/timeout so the + * omnibox degrades gracefully to history-only suggestions. + */ +export async function fetchSearchSuggestions(query: string, userAgent: string): Promise { + const q = query.trim(); + if (!q) return []; + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), SUGGEST_TIMEOUT_MS); + try { + const res = await fetch(`https://duckduckgo.com/ac/?q=${encodeURIComponent(q)}&type=list`, { + signal: controller.signal, + headers: { "User-Agent": userAgent, Accept: "application/json" }, + }); + if (!res.ok) return []; + const data: unknown = await res.json(); + // `type=list` → ["query", ["s1", "s2", ...]]; default → [{ phrase }, ...]. + if (Array.isArray(data)) { + if (Array.isArray(data[1])) { + return data[1].filter((s): s is string => typeof s === "string"); + } + return data + .map((item) => + typeof item === "string" + ? item + : item && typeof (item as { phrase?: unknown }).phrase === "string" + ? (item as { phrase: string }).phrase + : null, + ) + .filter((s): s is string => s !== null); + } + return []; + } catch { + return []; + } finally { + clearTimeout(timer); + } +} diff --git a/src/main/ipc/localHandlers.ts b/src/main/ipc/localHandlers.ts index 83ccb29e..b8d492d6 100644 --- a/src/main/ipc/localHandlers.ts +++ b/src/main/ipc/localHandlers.ts @@ -63,6 +63,8 @@ interface CreateLocalIpcHandlersOptions { updatePowerSaveBlocker(): void; autoUpdater: AutoUpdaterController; onSharedSettingsChanged?(): void; + extractBrowserToWindow(): void; + injectBrowserToMain(): void; /** Relaunch the app (exposed via the relaunchApp IPC). */ requestRelaunch(): void; } @@ -343,6 +345,30 @@ export function createLocalIpcHandlers( browserCancelPicker: () => { requireBrowserPanel(options.getBrowserPanelManager).cancelPicker(); }, + browserSuggest: ({ query }) => + requireBrowserPanel(options.getBrowserPanelManager).suggest(query), + browserAddBookmark: ({ url, title, faviconUrl }) => { + requireBrowserPanel(options.getBrowserPanelManager).addBookmark({ + url, + title, + createdAt: Date.now(), + ...(faviconUrl ? { faviconUrl } : {}), + }); + }, + browserRemoveBookmark: ({ url }) => { + requireBrowserPanel(options.getBrowserPanelManager).removeBookmark(url); + }, + browserSetBookmarkBarVisible: ({ visible }) => { + requireBrowserPanel(options.getBrowserPanelManager).setBookmarkBarVisible(visible); + }, + browserRecentHistory: ({ limit }) => + requireBrowserPanel(options.getBrowserPanelManager).recentHistory(limit), + browserExtractToWindow: () => { + options.extractBrowserToWindow(); + }, + browserInjectToMain: () => { + options.injectBrowserToMain(); + }, startUsageLogin: (payload) => getUsageLoginManager( options.requireLightcodePaths, diff --git a/src/main/main.ts b/src/main/main.ts index 5c94162e..7e3ee5f0 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -86,6 +86,7 @@ let lightcodePaths: LightcodePaths | null = null; let windowsJobObjectManager: WindowsJobObjectManager | null = null; let browserPanelManager: BrowserPanelManager | null = null; let browserMcpIngress: BrowserMcpIngress | null = null; +let browserExtractWindow: BrowserWindow | null = null; // Retained module-scope so the native Tray icon stays reachable from GC. let tray: TrayHandle | null = null; let isQuitting = false; @@ -145,6 +146,99 @@ function handleMainWindowClose(event: Electron.Event): void { mainWindow.hide(); } +function showAndFocusWindow(window: BrowserWindow): void { + if (window.isMinimized()) { + window.restore(); + } + if (!window.isVisible()) { + window.show(); + } + window.focus(); +} + +function focusBrowserExtractWindow(): void { + if (!browserExtractWindow || browserExtractWindow.isDestroyed()) return; + showAndFocusWindow(browserExtractWindow); +} + +function revealBrowserInMainWindow(): void { + if (mainWindow && !mainWindow.isDestroyed()) { + showAndFocusWindow(mainWindow); + } + browserPanelManager?.notifyState(); + browserPanelManager?.revealPanel(); +} + +function createBrowserExtractWindow(): BrowserWindow { + const windowChrome = resolveWindowChromeOptions(); + const window = createMainWindow({ + title: `${getAppName(channel, isDev)} Browser`, + windowKind: "browserExtract", + boundsStateKey: "browser-extract-window-bounds", + defaultWidth: 1120, + defaultHeight: 760, + minWidth: 520, + minHeight: 420, + isDev, + channel, + preloadPath: join(__dirname, "preload.cjs"), + rendererHtmlPath: join(__dirname, "../renderer/index.html"), + appVersion: app.getVersion(), + posthogEnableDev, + posthogEnabled, + posthogHost, + posthogKey, + sentryEnabled, + windowChromeHeight: WINDOW_CHROME_HEIGHT, + browserUserAgent: chromeLikeUserAgent, + appearance: windowChrome.appearance, + sidebarTranslucency: windowChrome.sidebarTranslucency, + openDevTools: false, + ...(process.env.VITE_DEV_SERVER_URL ? { devServerUrl: process.env.VITE_DEV_SERVER_URL } : {}), + onClosed: () => { + browserExtractWindow = null; + browserPanelManager?.notifyState(); + // Closing the window — whether via the OS controls or "bring back to + // panel" (injectBrowserToMain) — returns the browser to the main window. + if (!isQuitting) { + revealBrowserInMainWindow(); + } + }, + onRendererProcessGone: (details) => { + captureMainException(new Error(`Browser renderer process gone: ${details.reason}`), { + "lightcode.feature_area": "browser", + "lightcode.process": "renderer", + }); + }, + }); + return window; +} + +function extractBrowserToWindow(): void { + if (browserExtractWindow && !browserExtractWindow.isDestroyed()) { + browserPanelManager?.notifyState(); + focusBrowserExtractWindow(); + return; + } + browserExtractWindow = createBrowserExtractWindow(); + // Bind the host (which emits state) only after `browserExtractWindow` is + // assigned, so the snapshot's `extracted` flag reads true. Otherwise the main + // window keeps showing its own browser until the next unrelated state emit. + browserPanelManager?.bindHost(browserExtractWindow); + focusBrowserExtractWindow(); +} + +function injectBrowserToMain(): void { + const window = browserExtractWindow; + if (!window || window.isDestroyed()) { + browserExtractWindow = null; + revealBrowserInMainWindow(); + return; + } + // The window's `onClosed` handler returns the browser to the main window. + window.close(); +} + const workingThreads = new Set(); const sleepInhibitor = createSleepInhibitor(); @@ -189,13 +283,7 @@ if (!hasSingleInstanceLock) { if (!mainWindow) { return; } - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - if (!mainWindow.isVisible()) { - mainWindow.show(); - } - mainWindow.focus(); + showAndFocusWindow(mainWindow); }); app.whenReady().then(async () => { @@ -278,7 +366,10 @@ if (!hasSingleInstanceLock) { }, ); - browserPanelManager = new BrowserPanelManager(lightcodePaths, chromeLikeUserAgent); + browserPanelManager = new BrowserPanelManager(lightcodePaths, chromeLikeUserAgent, { + isExtracted: () => browserExtractWindow !== null && !browserExtractWindow.isDestroyed(), + focusExtractedWindow: focusBrowserExtractWindow, + }); browserMcpIngress = new BrowserMcpIngress(); browserMcpIngress.setManagerAccessor(() => browserPanelManager); primeBrowserAllowFlags(); @@ -295,6 +386,8 @@ if (!hasSingleInstanceLock) { updatePowerSaveBlocker, autoUpdater: autoUpdaterController, onSharedSettingsChanged: primeBrowserAllowFlags, + extractBrowserToWindow, + injectBrowserToMain, requestRelaunch: () => { isQuitting = true; app.relaunch(); @@ -436,6 +529,8 @@ if (!hasSingleInstanceLock) { windowsJobObjectManager = null; browserMcpIngress?.dispose(); browserMcpIngress = null; + browserExtractWindow?.close(); + browserExtractWindow = null; browserPanelManager?.dispose(); browserPanelManager = null; sleepInhibitor.dispose(); diff --git a/src/main/preload.ts b/src/main/preload.ts index 13c63d7c..c8178c83 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -5,6 +5,7 @@ import { IPC_EVENT_CHANNELS, type BrowserEvent, type LightcodeBridge, + type LightcodeWindowKind, type SupervisorEvent, type UpdateStatus, } from "@/shared/ipc"; @@ -44,6 +45,10 @@ function resolveChannel(): LightcodeChannel { return "stable"; } +function resolveWindowKind(): LightcodeWindowKind { + return resolveArgValue("--lc-window-kind=") === "browserExtract" ? "browserExtract" : "main"; +} + function resolveSentryEnabled(): boolean { const prefix = "--lc-sentry-enabled="; for (const arg of process.argv) { @@ -78,6 +83,7 @@ const bridge: LightcodeBridge = { arch: process.arch, chromeVersion: process.versions.chrome ?? "unknown", isDev: resolveIsDev(), + windowKind: resolveWindowKind(), channel: resolveChannel(), electronVersion: process.versions.electron ?? "unknown", nodeVersion: process.versions.node, diff --git a/src/main/window/createMainWindow.ts b/src/main/window/createMainWindow.ts index 0b395c13..f07e3366 100644 --- a/src/main/window/createMainWindow.ts +++ b/src/main/window/createMainWindow.ts @@ -1,6 +1,7 @@ import { dbGetState, dbSetState } from "../db"; import { BrowserWindow, screen, type RenderProcessGoneDetails } from "electron"; import type { LightcodeChannel } from "@/shared/channel"; +import type { LightcodeWindowKind } from "@/shared/ipc"; import { installSessionPermissions } from "../browser/permissions"; import { supportsNativeWindowMaterial, syncNativeThemeForMaterial } from "./windowMaterial"; @@ -12,9 +13,9 @@ interface WindowBounds { isMaximized: boolean; } -function getSavedWindowBounds(): WindowBounds | null { +function getSavedWindowBounds(stateKey: string): WindowBounds | null { try { - const raw = dbGetState("window-bounds"); + const raw = dbGetState(stateKey); if (!raw) { return null; } @@ -42,14 +43,20 @@ function getSavedWindowBounds(): WindowBounds | null { } } -function saveWindowBounds(window: BrowserWindow): void { +function saveWindowBounds(window: BrowserWindow, stateKey: string): void { const isMaximized = window.isMaximized(); const { x, y, width, height } = window.getNormalBounds(); - dbSetState("window-bounds", JSON.stringify({ x, y, width, height, isMaximized })); + dbSetState(stateKey, JSON.stringify({ x, y, width, height, isMaximized })); } export interface CreateMainWindowOptions { title: string; + windowKind?: LightcodeWindowKind; + boundsStateKey?: string | null; + defaultWidth?: number; + defaultHeight?: number; + minWidth?: number; + minHeight?: number; isDev: boolean; channel: LightcodeChannel; preloadPath: string; @@ -70,10 +77,13 @@ export interface CreateMainWindowOptions { onClose?: (event: Electron.Event) => void; onRendererProcessGone?: (details: RenderProcessGoneDetails) => void; devServerUrl?: string; + openDevTools?: boolean; } export function createMainWindow(options: CreateMainWindowOptions): BrowserWindow { - const saved = getSavedWindowBounds(); + const boundsStateKey = + options.boundsStateKey === undefined ? "window-bounds" : options.boundsStateKey; + const saved = boundsStateKey ? getSavedWindowBounds(boundsStateKey) : null; const supportsTitleBarOverlay = process.platform === "win32" || process.platform === "linux"; const isDark = options.appearance === "dark"; // Base bg/symbol per appearance, matching styles.css and the runtime @@ -97,11 +107,11 @@ export function createMainWindow(options: CreateMainWindowOptions): BrowserWindo const window = new BrowserWindow({ title: options.title, show: false, - width: saved?.width ?? 1460, - height: saved?.height ?? 920, + width: saved?.width ?? options.defaultWidth ?? 1460, + height: saved?.height ?? options.defaultHeight ?? 920, ...(saved?.x != null && saved?.y != null ? { x: saved.x, y: saved.y } : {}), - minWidth: 540, - minHeight: 720, + minWidth: options.minWidth ?? 540, + minHeight: options.minHeight ?? 720, backgroundColor: isMacOS || winGlassAtStart ? "#00000000" : backgroundColor, autoHideMenuBar: true, ...(isMacOS @@ -129,6 +139,7 @@ export function createMainWindow(options: CreateMainWindowOptions): BrowserWindo additionalArguments: [ `--lc-app-version=${encodeURIComponent(options.appVersion)}`, `--lc-is-dev=${options.isDev ? "1" : "0"}`, + `--lc-window-kind=${options.windowKind ?? "main"}`, `--lc-channel=${options.channel}`, `--lc-posthog-enable-dev=${options.posthogEnableDev ? "1" : "0"}`, `--lc-posthog-enabled=${options.posthogEnabled ? "1" : "0"}`, @@ -193,7 +204,7 @@ export function createMainWindow(options: CreateMainWindowOptions): BrowserWindo }; loadRenderer(); - if (options.isDev) { + if (options.isDev && options.openDevTools !== false) { window.webContents.openDevTools({ mode: "detach" }); } @@ -226,7 +237,9 @@ export function createMainWindow(options: CreateMainWindowOptions): BrowserWindo if (boundsTimer) { clearTimeout(boundsTimer); } - boundsTimer = setTimeout(() => saveWindowBounds(window), 500); + if (boundsStateKey) { + boundsTimer = setTimeout(() => saveWindowBounds(window, boundsStateKey), 500); + } }; window.on("resize", debouncedSave); window.on("move", debouncedSave); @@ -236,7 +249,9 @@ export function createMainWindow(options: CreateMainWindowOptions): BrowserWindo if (boundsTimer) { clearTimeout(boundsTimer); } - saveWindowBounds(window); + if (boundsStateKey) { + saveWindowBounds(window, boundsStateKey); + } options.onClose?.(event); }); window.on("closed", options.onClosed); diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 2a4ca425..6d9d7281 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -4,6 +4,7 @@ import { useEffect } from "react"; import { PixelLoader } from "./components/common"; import { msg } from "@/shared/messages"; import type { RuntimeEvent } from "@/shared/contracts"; +import type { SupervisorEvent, UpdateStatus } from "@/shared/ipc"; import { readBridge } from "./bridge"; import { handleThreadStateNotification, @@ -24,6 +25,8 @@ import { AppProvider } from "./components/ui/provider"; import { ImageLightboxHost } from "./components/composer"; import { MainView } from "@/renderer/views/MainView/MainView"; import { CommandPalette } from "@/renderer/commands/CommandPalette"; +import { BrowserPanel } from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel"; +import { useBrowserSync } from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/hooks/useBrowserSync"; import { captureAppStarted, flushProductAnalytics, @@ -40,6 +43,7 @@ import { // so that Vite HMR can tear them down before re-executing the module. let threadStateNotificationsArmed = false; +const isBrowserExtractWindow = readBridge().windowKind === "browserExtract"; // ── Runtime event rAF batcher ─────────────────────────────────── // With 6-8 concurrent streaming chats, the supervisor produces ~500 @@ -88,7 +92,7 @@ function flushPendingRuntimeEventsSync(): void { if (pendingRuntimeEvents.size > 0) flushPendingRuntimeEvents(); } -const unsubSupervisor = readBridge().onSupervisorEvent((event) => { +function handleSupervisorEvent(event: SupervisorEvent): void { if ("threadId" in event && event.threadId.startsWith("shell:")) { if (event.type === "thread-output") { useDevTerminalStore.getState().noteShellOutput(event.threadId); @@ -185,9 +189,9 @@ const unsubSupervisor = readBridge().onSupervisorEvent((event) => { store.setWslAgentStatuses(event.statuses); } } -}); +} -const unsubUpdate = readBridge().onUpdateStatus((status) => { +function handleUpdateStatus(status: UpdateStatus): void { const store = useUpdateStore.getState(); switch (status.type) { case "checking": @@ -214,17 +218,24 @@ const unsubUpdate = readBridge().onUpdateStatus((status) => { toast.danger(msg("update.error", { detail: status.message })); break; } -}); +} -const uninstallRuntimePersister = installRuntimeItemsPersister(); +// The browser-extract window renders a standalone BrowserPanel; it has no use +// for supervisor/update streams or runtime persistence, so only the main window +// wires these up (and tears them down on HMR dispose). +const mainWindowCleanups: Array<() => void> = isBrowserExtractWindow + ? [] + : [ + readBridge().onSupervisorEvent(handleSupervisorEvent), + readBridge().onUpdateStatus(handleUpdateStatus), + installRuntimeItemsPersister(), + ]; let uninstallProductAnalytics: (() => void) | null = null; let productAnalyticsStarted = false; if (import.meta.hot) { import.meta.hot.dispose(() => { - unsubSupervisor(); - unsubUpdate(); - uninstallRuntimePersister(); + for (const cleanup of mainWindowCleanups) cleanup(); if (runtimeFlushHandle !== null) { cancelAnimationFrame(runtimeFlushHandle); runtimeFlushHandle = null; @@ -237,6 +248,25 @@ if (import.meta.hot) { } export function App() { + if (isBrowserExtractWindow) { + return ; + } + return ; +} + +function BrowserExtractApp() { + useBrowserSync(); + + return ( + +
+ +
+
+ ); +} + +function MainApp() { const { initialLoading, storeHydrated, loadT0 } = useAppHydration(); useEffect(() => { diff --git a/src/renderer/components/layout/BrowserDrawerShell.tsx b/src/renderer/components/layout/BrowserDrawerShell.tsx deleted file mode 100644 index bcc17af0..00000000 --- a/src/renderer/components/layout/BrowserDrawerShell.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { - useEffect, - useState, - type CSSProperties, - type MouseEvent as ReactMouseEvent, - type ReactNode, -} from "react"; -import { useLingui } from "@lingui/react/macro"; -import { usePanelStore } from "@/renderer/state/panelStore"; -import { pushEscapeHandler } from "./overlayEscapeStack"; - -/** - * Floating shell for the in-app browser overlay. Reuses the chrome of - * LoginTerminalOverlay (rounded floating card, same margins, border, shadow) - * and hosts both presentation modes — drawer and fullscreen — on a single - * mounted element so toggling maximize transitions size/position smoothly - * instead of unmounting and replaying the entrance animation. - * - * Behavior: - * - Animation: subtle slide-in combined with fade. Works for both drawer and - * fullscreen without the jarring full-width slide of fullscreen. - * - Maximized: leaves side margins so macOS traffic lights and Windows - * titleBarOverlay controls (top-left/top-right) sit outside the panel. - * The backdrop also leaves the titlebar strip exposed so OS controls and - * window-drag region stay live. - * - Backdrop: semi-transparent scrim outside the panel — dims the underlying - * overlay and consumes pointer events. Click to dismiss. - * - Resize: left-edge drag handle adjusts width in drawer mode. While - * dragging, a full-window cursor catcher sits above the embedded webview so - * pointer events keep flowing to the host window (webview otherwise eats - * them as soon as the cursor crosses into its area). - * - Escape: routed through the shared overlay escape stack so the underlying - * overlay below this one is not also dismissed. - */ -export function BrowserDrawerShell(props: { - open: boolean; - maximized: boolean; - onExited?: () => void; - children: ReactNode; -}) { - const { open, maximized, onExited, children } = props; - const { t } = useLingui(); - const drawerWidth = usePanelStore((s) => s.browserOverlayDrawerWidth); - const setDrawerWidth = usePanelStore((s) => s.setBrowserOverlayDrawerWidth); - const [mounted, setMounted] = useState(open); - const [visible, setVisible] = useState(false); - const [isResizing, setIsResizing] = useState(false); - - useEffect(() => { - if (open) { - setMounted(true); - let inner = 0; - const outer = requestAnimationFrame(() => { - inner = requestAnimationFrame(() => setVisible(true)); - }); - return () => { - cancelAnimationFrame(outer); - if (inner) cancelAnimationFrame(inner); - }; - } - setVisible(false); - }, [open]); - - function requestClose() { - setVisible(false); - (document.activeElement as HTMLElement | null)?.blur(); - } - - useEffect(() => { - if (!open || !onExited) return; - return pushEscapeHandler(requestClose); - // requestClose closes over stable setters/refs. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [open, onExited]); - - function handleTransitionEnd(event: React.TransitionEvent) { - if (visible) return; - if (event.propertyName !== "opacity") return; - setMounted(false); - onExited?.(); - } - - function handleResizeStart(event: ReactMouseEvent) { - event.preventDefault(); - const startX = event.clientX; - const startWidth = drawerWidth; - document.body.style.userSelect = "none"; - document.body.style.cursor = "ew-resize"; - setIsResizing(true); - - function onMove(e: MouseEvent) { - // Handle is on the LEFT edge of a panel anchored to the RIGHT viewport - // edge; dragging left (negative clientX delta) grows the panel. - const delta = startX - e.clientX; - setDrawerWidth(startWidth + delta); - } - function onUp() { - document.body.style.userSelect = ""; - document.body.style.cursor = ""; - setIsResizing(false); - window.removeEventListener("mousemove", onMove); - window.removeEventListener("mouseup", onUp); - } - window.addEventListener("mousemove", onMove); - window.addEventListener("mouseup", onUp); - } - - if (!mounted) return null; - - const maximizedOverrides: CSSProperties = maximized - ? { - top: 0, - right: 0, - bottom: 0, - width: "100vw", - maxWidth: "none", - borderRadius: 0, - } - : { width: `${drawerWidth}px` }; - - const animationTransition = isResizing - ? "transform 240ms ease-out, opacity 240ms ease-out, top 200ms ease-out, right 200ms ease-out, bottom 200ms ease-out, max-width 200ms ease-out, border-radius 200ms ease-out" - : "transform 240ms ease-out, opacity 240ms ease-out, top 200ms ease-out, right 200ms ease-out, bottom 200ms ease-out, width 200ms ease-out, max-width 200ms ease-out, border-radius 200ms ease-out"; - - return ( -
-
-
- {!maximized ? ( -
- ) : null} - {children} -
- {isResizing ? ( -
- ) : null} -
- ); -} diff --git a/src/renderer/components/layout/UnifiedRightPanel.tsx b/src/renderer/components/layout/UnifiedRightPanel.tsx index 05d24bbb..1d3f7ea1 100644 --- a/src/renderer/components/layout/UnifiedRightPanel.tsx +++ b/src/renderer/components/layout/UnifiedRightPanel.tsx @@ -7,6 +7,7 @@ import { Maximize2, NotebookPen, PanelRightClose, + PictureInPicture2, TerminalSquare, } from "lucide-react"; import { useLingui } from "@lingui/react/macro"; @@ -40,6 +41,7 @@ export function UnifiedRightPanel(props: { onExpandGitToOverlay?: () => void; onExpandFilesToOverlay?: () => void; onExpandBrowserToOverlay?: () => void; + onExtractBrowserToWindow?: () => void; onOpenGit?: () => void; onOpenTerminal?: () => void; onOpenFiles?: () => void; @@ -67,6 +69,7 @@ export function UnifiedRightPanel(props: { onExpandGitToOverlay, onExpandFilesToOverlay, onExpandBrowserToOverlay, + onExtractBrowserToWindow, onOpenGit, onOpenTerminal, onOpenFiles, @@ -134,6 +137,16 @@ export function UnifiedRightPanel(props: { )} + {activeTab === "browser" && onExtractBrowserToWindow && ( + + )} {activeTab === "usage" ? usageHeaderActions : null}
{showTerminalTab ? ( diff --git a/src/renderer/components/ui/provider.tsx b/src/renderer/components/ui/provider.tsx index f09fb0e2..ca3d84bc 100644 --- a/src/renderer/components/ui/provider.tsx +++ b/src/renderer/components/ui/provider.tsx @@ -73,11 +73,15 @@ function ToastAction({ actionProps, actionLabel, isCopyAction }: ToastActionProp ); } -export function AppProvider(props: { children: ReactNode; contentReady?: boolean }) { +export function AppProvider(props: { + children: ReactNode; + contentReady?: boolean; + syncWindowChrome?: boolean; +}) { // `contentReady` gates the glass material: the window stays opaque through // loading and only goes translucent once the main content is mounted, so the // app never shows a bare translucent window mid-load. - const { children, contentReady = false } = props; + const { children, contentReady = false, syncWindowChrome = true } = props; const themeMode = useSharedSettings((state) => state.themeMode); const themePreset = useSharedSettings((state) => state.themePreset); const locale = useSharedSettings((state) => state.locale); @@ -154,7 +158,7 @@ export function AppProvider(props: { children: ReactNode; contentReady?: boolean }, [glassEnabled, contentReady]); useEffect(() => { - if (typeof window === "undefined" || !("lightcode" in window)) { + if (!syncWindowChrome || typeof window === "undefined" || !("lightcode" in window)) { return; } @@ -180,7 +184,7 @@ export function AppProvider(props: { children: ReactNode; contentReady?: boolean captureRendererException(error, { featureArea: "window-chrome" }); // Keep renderer boot resilient if Electron rejects a color value. }); - }, [appearance, glassEnabled, contentReady]); + }, [appearance, glassEnabled, contentReady, syncWindowChrome]); // User-tuned sidebar frosting (Appearance slider): override the glass tint // alpha for the active appearance. No-op on platforms without a native blur diff --git a/src/renderer/locales/de/messages.po b/src/renderer/locales/de/messages.po index e529261d..e0862ca4 100644 --- a/src/renderer/locales/de/messages.po +++ b/src/renderer/locales/de/messages.po @@ -950,6 +950,14 @@ msgstr "blockiert:" msgid "Bold" msgstr "Fett" +#: 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 +msgid "Bookmarks" +msgstr "Lesezeichen" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Unten" @@ -978,6 +986,15 @@ msgstr "Branch-Schutzregeln haben diese Zusammenführung blockiert." msgid "Branches" msgstr "Branches" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Zurück ins Panel holen" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -1001,6 +1018,10 @@ msgstr "Durchsuchen Sie Ihre GitHub-Repositorys oder fügen Sie eine Klon-URL " msgid "Browser" msgstr "Browser" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Browser ist in einem separaten Fenster geöffnet" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Browser-MCP" @@ -1245,6 +1266,10 @@ msgstr "Claude-Profil entfernt." msgid "Cleanup script" msgstr "Bereinigungsskript" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Browserverlauf löschen" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Browserverlauf löschen" @@ -2535,6 +2560,10 @@ msgstr "Browseradressleiste fokussieren" msgid "Focus the in-app browser address bar" msgstr "In-App-Browser-Adressleiste fokussieren" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Zum Fenster wechseln" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Ordnername" @@ -2798,6 +2827,10 @@ msgstr "Seitenleiste ausblenden" msgid "Hide terminal" msgstr "Terminal ausblenden" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Verlauf" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3409,6 +3442,15 @@ msgstr "Meistgenutzte Reasoning-Stufe" msgid "Move" msgstr "Bewegen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Browser zurück ins Hauptfenster verschieben" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Browser in Fenster verschieben" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Änderungen in einen neuen Arbeitsbaum verschieben" @@ -3611,6 +3653,11 @@ msgstr "Noch keine KI-Commits, PRs oder Konfliktlösungen erfasst." msgid "No archived threads." msgstr "Keine archivierten Threads." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Noch keine Lesezeichen — fügen Sie die aktuelle Seite über den Stern " +"hinzu." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Keine Branches gefunden" @@ -3688,6 +3735,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Keine GitHub-CLI-Konten gefunden. Melden Sie sich mit <0>gh auth " "login an oder <1>fügen Sie eine Klon-URL ein." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Noch kein Verlauf" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Keine Übereinstimmungen für „{query}“." @@ -4578,6 +4629,11 @@ msgstr "{pattern} entfernen" msgid "Remove action" msgstr "Aktion entfernen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Lesezeichen entfernen" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Claude-Profil entfernen" @@ -4718,7 +4774,7 @@ msgstr "Mattierung der Seitenleiste auf Standard zurücksetzen" msgid "Reset to default" msgstr "Auf Standard zurücksetzen" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Größe der Browser-Schublade ändern" @@ -5031,7 +5087,7 @@ msgstr "Modelle suchen" msgid "Search models..." msgstr "Modelle suchen..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Adresse suchen oder eingeben" @@ -5152,6 +5208,7 @@ msgid "Set shortcut" msgstr "Tastenkürzel festlegen" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5190,6 +5247,10 @@ msgstr "Zeigen Sie einen projektlosen Home-Bereich für Agentensitzungen auf " msgid "Show all" msgstr "Alle anzeigen" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Lesezeichenleiste anzeigen" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Lesezeichenleiste anzeigen" @@ -5813,6 +5874,7 @@ msgstr "Theme" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Denkt …" diff --git a/src/renderer/locales/en/messages.po b/src/renderer/locales/en/messages.po index a7f15f0d..9d055e6e 100644 --- a/src/renderer/locales/en/messages.po +++ b/src/renderer/locales/en/messages.po @@ -945,6 +945,14 @@ msgstr "blocked:" msgid "Bold" msgstr "Bold" +#: 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 +msgid "Bookmarks" +msgstr "Bookmarks" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Bottom" @@ -972,6 +980,14 @@ msgstr "Branch protection rules blocked this merge." msgid "Branches" msgstr "Branches" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Bring back to panel" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -994,6 +1010,10 @@ msgstr "Browse your GitHub repositories or paste a clone URL." msgid "Browser" msgstr "Browser" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Browser is open in a separate window" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Browser MCP" @@ -1237,6 +1257,10 @@ msgstr "Claude profile removed." msgid "Cleanup script" msgstr "Cleanup script" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Clear browsing history" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Clear Browsing History" @@ -2509,6 +2533,10 @@ msgstr "Focus browser address bar" msgid "Focus the in-app browser address bar" msgstr "Focus the in-app browser address bar" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Focus window" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Folder name" @@ -2768,6 +2796,10 @@ msgstr "Hide sidebar" msgid "Hide terminal" msgstr "Hide terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "History" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3377,6 +3409,15 @@ msgstr "Most used reasoning" msgid "Move" msgstr "Move" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Move browser back to main window" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Move browser to window" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Move changes to a new worktree" @@ -3578,6 +3619,10 @@ msgstr "No AI commits, PRs, or conflict resolutions tracked yet." msgid "No archived threads." msgstr "No archived threads." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "No bookmarks yet — add the current page with the star." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "No branches found" @@ -3655,6 +3700,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " "<1>paste a clone URL." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "No history yet" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "No matches for “{query}”." @@ -4539,6 +4588,11 @@ msgstr "Remove {pattern}" 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/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Remove bookmark" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Remove Claude profile" @@ -4678,7 +4732,7 @@ msgstr "Reset sidebar frosting to default" msgid "Reset to default" msgstr "Reset to default" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Resize browser drawer" @@ -4988,7 +5042,7 @@ msgstr "Search models" msgid "Search models..." msgstr "Search models..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Search or enter address" @@ -5107,6 +5161,7 @@ msgid "Set shortcut" msgstr "Set shortcut" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5144,6 +5199,10 @@ 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 +msgid "Show bookmark bar" +msgstr "Show bookmark bar" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Show Bookmark Bar" @@ -5756,6 +5815,7 @@ msgstr "Theme" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Thinking" diff --git a/src/renderer/locales/es/messages.po b/src/renderer/locales/es/messages.po index babcbff8..0dcdfe9c 100644 --- a/src/renderer/locales/es/messages.po +++ b/src/renderer/locales/es/messages.po @@ -947,6 +947,14 @@ msgstr "bloqueado:" msgid "Bold" msgstr "Negrita" +#: 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 +msgid "Bookmarks" +msgstr "Marcadores" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Abajo" @@ -974,6 +982,14 @@ msgstr "Las reglas de protección de ramas bloquearon esta fusión." msgid "Branches" msgstr "Ramas" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Devolver al panel" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -996,6 +1012,10 @@ msgstr "Explora tus repositorios de GitHub o pega una URL de clonación." msgid "Browser" msgstr "Navegador" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "El navegador está abierto en una ventana separada" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Browser MCP" @@ -1242,6 +1262,10 @@ msgstr "Perfil de Claude eliminado." msgid "Cleanup script" msgstr "Script de limpieza" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Borrar historial de navegación" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Borrar historial de navegación" @@ -2526,6 +2550,10 @@ msgstr "Enfocar barra de direcciones del navegador" msgid "Focus the in-app browser address bar" msgstr "Enfocar la barra de direcciones del navegador integrado" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Enfocar ventana" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Nombre de carpeta" @@ -2789,6 +2817,10 @@ msgstr "Ocultar barra lateral" msgid "Hide terminal" msgstr "Ocultar terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Historial" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3402,6 +3434,15 @@ msgstr "Razonamiento más usado" msgid "Move" msgstr "Mover" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Mover navegador de vuelta a la ventana principal" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Mover navegador a una ventana" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Mover cambios a un nuevo worktree" @@ -3604,6 +3645,10 @@ msgstr "Aún no se han registrado commits, PRs ni resoluciones de conflictos con msgid "No archived threads." msgstr "No hay hilos archivados." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Todavía no hay marcadores — añade la página actual con la estrella." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "No se encontraron ramas" @@ -3681,6 +3726,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "No se encontraron cuentas de GitHub CLI. Inicia sesión con <0>gh auth " "login, o <1>pega una URL de clonación." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Aún no hay historial" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "No hay coincidencias para “{query}”." @@ -4572,6 +4621,11 @@ msgstr "Eliminar {pattern}" 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/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Eliminar marcador" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Eliminar perfil de Claude" @@ -4713,7 +4767,7 @@ msgstr "Restablecer el esmerilado del panel lateral a sus valores " msgid "Reset to default" msgstr "Restablecer valores predeterminados" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Redimensionar cajón del navegador" @@ -5025,7 +5079,7 @@ msgstr "Buscar modelos" msgid "Search models..." msgstr "Buscar modelos..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Buscar o introducir dirección" @@ -5144,6 +5198,7 @@ msgid "Set shortcut" msgstr "Asignar atajo" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5182,6 +5237,10 @@ 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 +msgid "Show bookmark bar" +msgstr "Mostrar la barra de marcadores" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Mostrar barra de marcadores" @@ -5799,6 +5858,7 @@ msgstr "Tema" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Pensando" diff --git a/src/renderer/locales/fr/messages.po b/src/renderer/locales/fr/messages.po index 69d6f97e..2f4ebcab 100644 --- a/src/renderer/locales/fr/messages.po +++ b/src/renderer/locales/fr/messages.po @@ -952,6 +952,14 @@ msgstr "bloqué :" msgid "Bold" msgstr "Gras" +#: 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 +msgid "Bookmarks" +msgstr "Favoris" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "En bas" @@ -979,6 +987,15 @@ msgstr "Les règles de protection des branches ont bloqué cette fusion." msgid "Branches" msgstr "Branches" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Ramener dans le panneau" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -1001,6 +1018,10 @@ msgstr "Parcourez vos référentiels GitHub ou collez une URL de clonage." msgid "Browser" msgstr "Navigateur" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Le navigateur est ouvert dans une fenêtre séparée" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Navigateur MCP" @@ -1247,6 +1268,10 @@ msgstr "Profil de Claude supprimé." msgid "Cleanup script" msgstr "Script de nettoyage" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Effacer l’historique de navigation" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Effacer l'historique de navigation" @@ -2534,6 +2559,10 @@ msgstr "Accéder à la barre d'adresse du navigateur" msgid "Focus the in-app browser address bar" msgstr "Accéder à la barre d'adresse du navigateur dans l'application" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Activer la fenêtre" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Nom du dossier" @@ -2798,6 +2827,10 @@ msgstr "Masquer la barre latérale" msgid "Hide terminal" msgstr "Masquer le terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Historique" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3410,6 +3443,15 @@ msgstr "Raisonnement le plus utilisé" msgid "Move" msgstr "Déplacer" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Replacer le navigateur dans la fenêtre principale" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Déplacer le navigateur vers une fenêtre" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Déplacer les modifications vers un nouvel arbre de travail" @@ -3611,6 +3653,10 @@ msgstr "Aucun commit IA, PR ou résolution de conflit suivi pour l'instant." msgid "No archived threads." msgstr "Aucun fil de discussion archivé." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Aucun favori pour l’instant — ajoutez la page actuelle avec l’étoile." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Aucune branche trouvée" @@ -3688,6 +3734,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Aucun compte GitHub CLI trouvé. Connectez-vous avec <0>gh auth " "login ou <1>collez une URL de clonage." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Aucun historique pour l’instant" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Aucune correspondance pour « {query} »." @@ -4580,6 +4630,11 @@ msgstr "Supprimer {pattern}" msgid "Remove action" msgstr "Supprimer l'action" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Supprimer le favori" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Supprimer le profil de Claude" @@ -4720,7 +4775,7 @@ msgstr "Réinitialiser le givrage de la barre latérale" msgid "Reset to default" msgstr "Réinitialiser par défaut" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Redimensionner le tiroir du navigateur" @@ -5033,7 +5088,7 @@ msgstr "Rechercher des modèles" msgid "Search models..." msgstr "Rechercher des modèles..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Rechercher ou saisir une adresse" @@ -5153,6 +5208,7 @@ msgid "Set shortcut" msgstr "Définir un raccourci" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5191,6 +5247,10 @@ msgstr "Afficher une portée Accueil sans projet pour les sessions d'agent au " msgid "Show all" msgstr "Afficher tout" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Afficher la barre de favoris" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Afficher la barre de favoris" @@ -5812,6 +5872,7 @@ msgstr "Thème" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Réflexion" diff --git a/src/renderer/locales/ja/messages.po b/src/renderer/locales/ja/messages.po index 90af3082..5cb49723 100644 --- a/src/renderer/locales/ja/messages.po +++ b/src/renderer/locales/ja/messages.po @@ -939,6 +939,14 @@ msgstr "ブロックされました:" msgid "Bold" msgstr "太字" +#: 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 +msgid "Bookmarks" +msgstr "ブックマーク" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "下" @@ -965,6 +973,14 @@ msgstr "ブランチ保護ルールにより、このマージがブロックさ msgid "Branches" msgstr "ブランチ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "パネルに戻す" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring it back into this panel, or jump to the window it’s running in." +msgstr "このパネルに戻すか、実行中のウィンドウに移動します。" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -987,6 +1003,10 @@ msgstr "GitHub リポジトリを参照するか、クローン URL を貼り付 msgid "Browser" msgstr "ブラウザ" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "ブラウザは別のウィンドウで開いています" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "ブラウザMCP" @@ -1229,6 +1249,10 @@ msgstr "Claude プロファイルが削除されました。" msgid "Cleanup script" msgstr "クリーンアップスクリプト" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "閲覧履歴を消去" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "閲覧履歴を消去" @@ -2489,6 +2513,10 @@ msgstr "ブラウザのアドレスバーをフォーカス" msgid "Focus the in-app browser address bar" msgstr "組み込みブラウザのアドレスバーをフォーカス" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "ウィンドウに切り替え" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "フォルダ名" @@ -2745,6 +2773,10 @@ msgstr "サイドバーを隠す" msgid "Hide terminal" msgstr "ターミナルを隠す" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "履歴" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3349,6 +3381,15 @@ msgstr "よく使う推論レベル" msgid "Move" msgstr "移動" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "ブラウザーをメインウィンドウに戻す" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "ブラウザーをウィンドウに移動" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "変更を新しいワークツリーに移動する" @@ -3549,6 +3590,10 @@ msgstr "AIによるコミット、PR、競合解決はまだ記録されてい msgid "No archived threads." msgstr "アーカイブされたスレッドはありません。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "ブックマークはまだありません。星アイコンで現在のページを追加できます。" + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "ブランチが見つかりませんでした" @@ -3626,6 +3671,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "GitHub CLI アカウントが見つかりません。 <0>gh auth loginでサインインするか、<1>クローン URL " "を貼り付けます。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "履歴はまだありません" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "「{query}」に一致するものはありません。" @@ -4505,6 +4554,11 @@ msgstr "{pattern}を削除" msgid "Remove action" msgstr "アクションの削除" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "ブックマークを削除" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Claude プロファイルを削除" @@ -4644,7 +4698,7 @@ msgstr "サイドバーのすりガラス効果をデフォルトに戻す" msgid "Reset to default" msgstr "デフォルトに戻す" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "ブラウザドロワーのサイズを変更する" @@ -4950,7 +5004,7 @@ msgstr "モデルを検索する" msgid "Search models..." msgstr "モデルを検索..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "アドレスを検索または入力" @@ -5069,6 +5123,7 @@ msgid "Set shortcut" msgstr "ショートカットを設定" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5106,6 +5161,10 @@ msgstr "OS レベルのエージェント セッション用に、プロジェ msgid "Show all" msgstr "すべて表示" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "ブックマークバーを表示" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "ブックマークバーを表示" @@ -5714,6 +5773,7 @@ msgstr "テーマ" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "思考中" diff --git a/src/renderer/locales/ko/messages.po b/src/renderer/locales/ko/messages.po index df55fef8..cf92c157 100644 --- a/src/renderer/locales/ko/messages.po +++ b/src/renderer/locales/ko/messages.po @@ -939,6 +939,14 @@ msgstr "차단됨:" msgid "Bold" msgstr "굵게" +#: 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 +msgid "Bookmarks" +msgstr "북마크" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "아래쪽" @@ -965,6 +973,14 @@ msgstr "브랜치 보호 규칙이 이 병합을 차단했습니다." msgid "Branches" msgstr "브랜치" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "패널로 되돌리기" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring it back into this panel, or jump to the window it’s running in." +msgstr "이 패널로 되돌리거나 실행 중인 창으로 이동하세요." + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -987,6 +1003,10 @@ msgstr "GitHub 리포지토리를 찾아보거나 복제 URL을 붙여넣으세 msgid "Browser" msgstr "브라우저" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "브라우저가 별도의 창에서 열려 있습니다" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "브라우저 MCP" @@ -1229,6 +1249,10 @@ msgstr "Claude 프로필이 삭제되었습니다." msgid "Cleanup script" msgstr "정리 스크립트" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "방문 기록 지우기" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "검색 기록 지우기" @@ -2489,6 +2513,10 @@ msgstr "브라우저 주소 표시줄에 포커스" msgid "Focus the in-app browser address bar" msgstr "인앱 브라우저 주소 표시줄에 포커스" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "창 활성화" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "폴더 이름" @@ -2745,6 +2773,10 @@ msgstr "사이드바 숨기기" msgid "Hide terminal" msgstr "터미널 숨기기" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "기록" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3350,6 +3382,15 @@ msgstr "가장 많이 사용한 추론 강도" msgid "Move" msgstr "이동" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "브라우저를 메인 창으로 다시 이동" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "브라우저를 창으로 이동" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "변경 사항을 새 작업 트리로 이동" @@ -3550,6 +3591,10 @@ msgstr "아직 추적된 AI 커밋, PR, 충돌 해결이 없습니다." msgid "No archived threads." msgstr "보관된 스레드가 없습니다." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "아직 북마크가 없습니다 — 별표로 현재 페이지를 추가하세요." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "브랜치를 찾을 수 없습니다." @@ -3627,6 +3672,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "GitHub CLI 계정을 찾을 수 없습니다. <0>gh auth login으로 로그인하거나 <1>복제 URL을 " "붙여넣으세요." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "아직 기록이 없습니다" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "\"{query}\"과(와) 일치하는 항목이 없습니다." @@ -4505,6 +4554,11 @@ msgstr "{pattern} 제거" msgid "Remove action" msgstr "작업 제거" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "북마크 삭제" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Claude 프로필 삭제" @@ -4644,7 +4698,7 @@ msgstr "사이드바 프로스트를 기본값으로 재설정" msgid "Reset to default" msgstr "기본값으로 재설정" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "브라우저 서랍 크기 조정" @@ -4950,7 +5004,7 @@ msgstr "모델 검색" msgid "Search models..." msgstr "모델 검색..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "주소 검색 또는 입력" @@ -5068,6 +5122,7 @@ msgid "Set shortcut" msgstr "단축키 설정" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5105,6 +5160,10 @@ msgstr "OS 수준 에이전트 세션에 대해 프로젝트 없는 홈 범위 msgid "Show all" msgstr "모두 표시" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "북마크 막대 표시" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "북마크바 표시" @@ -5712,6 +5771,7 @@ msgstr "테마" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "생각 중" diff --git a/src/renderer/locales/pl/messages.po b/src/renderer/locales/pl/messages.po index ecf49630..c594d6e6 100644 --- a/src/renderer/locales/pl/messages.po +++ b/src/renderer/locales/pl/messages.po @@ -950,6 +950,14 @@ msgstr "zablokowane:" msgid "Bold" msgstr "Pogrubienie" +#: 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 +msgid "Bookmarks" +msgstr "Zakładki" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Dół" @@ -977,6 +985,14 @@ msgstr "Reguły ochrony gałęzi zablokowały to scalanie." msgid "Branches" msgstr "Gałęzie" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Przywróć do panelu" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -999,6 +1015,10 @@ msgstr "Przeglądaj swoje repozytoria GitHub lub wklej adres URL do klonowania." msgid "Browser" msgstr "Przeglądarka" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Przeglądarka jest otwarta w osobnym oknie" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Przeglądarka MCP" @@ -1242,6 +1262,10 @@ msgstr "Usunięto profil Claude'a." msgid "Cleanup script" msgstr "Skrypt czyszczący" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Wyczyść historię przeglądania" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Wyczyść historię przeglądania" @@ -2526,6 +2550,10 @@ msgstr "Przejdź do paska adresu przeglądarki" msgid "Focus the in-app browser address bar" msgstr "Przejdź do paska adresu przeglądarki w aplikacji" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Przejdź do okna" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Nazwa folderu" @@ -2789,6 +2817,10 @@ msgstr "Ukryj pasek boczny" msgid "Hide terminal" msgstr "Ukryj terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Historia" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3400,6 +3432,15 @@ msgstr "Najczęściej używane rozumowanie" msgid "Move" msgstr "Przenieś" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Przenieś przeglądarkę z powrotem do głównego okna" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Przenieś przeglądarkę do okna" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Przenieś zmiany do nowego drzewa roboczego" @@ -3602,6 +3643,10 @@ msgstr "Nie zarejestrowano jeszcze żadnych commitów AI, PR ani rozwiązanych " msgid "No archived threads." msgstr "Brak zarchiwizowanych wątków." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Brak zakładek — dodaj bieżącą stronę gwiazdką." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Nie znaleziono gałęzi" @@ -3679,6 +3724,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Nie znaleziono kont GitHub CLI. Zaloguj się za pomocą <0>gh auth " "login lub <1>wklej sklonowany adres URL." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Brak historii" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Brak dopasowań dla „{query}”." @@ -4569,6 +4618,11 @@ msgstr "Usuń {pattern}" 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/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Usuń zakładkę" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Usuń profil Claude" @@ -4708,7 +4762,7 @@ msgstr "Przywróć domyślne zmatowienie paska bocznego" msgid "Reset to default" msgstr "Przywróć domyślne" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Zmień rozmiar szuflady przeglądarki" @@ -5019,7 +5073,7 @@ msgstr "Wyszukaj modele" msgid "Search models..." msgstr "Wyszukaj modele..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Wyszukaj lub wpisz adres" @@ -5138,6 +5192,7 @@ msgid "Set shortcut" msgstr "Ustaw skrót" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5176,6 +5231,10 @@ msgstr "Wyświetl bezprojektowy zakres główny dla sesji agenta na poziomie " msgid "Show all" msgstr "Pokaż wszystko" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Pokaż pasek zakładek" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Pokaż pasek zakładek" @@ -5791,6 +5850,7 @@ msgstr "Motyw" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Myślenie" diff --git a/src/renderer/locales/pt-BR/messages.po b/src/renderer/locales/pt-BR/messages.po index fad7843b..20b17068 100644 --- a/src/renderer/locales/pt-BR/messages.po +++ b/src/renderer/locales/pt-BR/messages.po @@ -950,6 +950,14 @@ msgstr "bloqueado:" msgid "Bold" msgstr "Negrito" +#: 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 +msgid "Bookmarks" +msgstr "Favoritos" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Inferior" @@ -977,6 +985,15 @@ msgstr "As regras de proteção de branch bloquearam essa mesclagem." msgid "Branches" msgstr "Branches" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Trazer de volta ao painel" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -999,6 +1016,10 @@ msgstr "Navegue pelos seus repositórios do GitHub ou cole uma URL de clonagem." msgid "Browser" msgstr "Navegador" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "O navegador está aberto em uma janela separada" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "MCP do navegador" @@ -1244,6 +1265,10 @@ msgstr "Perfil de Claude removido." msgid "Cleanup script" msgstr "Script de limpeza" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.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 Browsing History" msgstr "Limpar histórico de navegação" @@ -2528,6 +2553,10 @@ msgstr "Acessar barra de endereços do navegador" msgid "Focus the in-app browser address bar" msgstr "Acessar barra de endereços do navegador no aplicativo" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Focar a janela" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Nome da pasta" @@ -2791,6 +2820,10 @@ msgstr "Ocultar barra lateral" msgid "Hide terminal" msgstr "Ocultar terminal" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Histórico" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3403,6 +3436,15 @@ msgstr "Raciocínio mais usado" msgid "Move" msgstr "Mover" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Mover navegador de volta para a janela principal" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Mover navegador para uma janela" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Mover alterações para uma nova árvore de trabalho" @@ -3604,6 +3646,10 @@ msgstr "Nenhum commit, PR ou resolução de conflito por IA registrado ainda." msgid "No archived threads." msgstr "Nenhum tópico arquivado." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Ainda não há favoritos — adicione a página atual com a estrela." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Nenhuma branch encontrada" @@ -3681,6 +3727,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Nenhuma conta GitHub CLI encontrada. Faça login com <0>gh auth " "login ou <1>cole um URL clone." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Ainda não há histórico" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Nenhuma correspondência para “{query}”." @@ -4571,6 +4621,11 @@ msgstr "Remover {pattern}" msgid "Remove action" msgstr "Remover ação" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Remover favorito" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Remover perfil do Claude" @@ -4711,7 +4766,7 @@ msgstr "Redefinir efeito fosco da barra lateral para o padrão" msgid "Reset to default" msgstr "Redefinir para o padrão" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Redimensionar gaveta do navegador" @@ -5023,7 +5078,7 @@ msgstr "Pesquisar modelos" msgid "Search models..." msgstr "Pesquisar modelos..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Pesquise ou digite o endereço" @@ -5142,6 +5197,7 @@ msgid "Set shortcut" msgstr "Definir atalho" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5180,6 +5236,10 @@ msgstr "Mostre um escopo inicial sem projeto para sessões de agente no nível d msgid "Show all" msgstr "Mostrar tudo" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Mostrar barra de favoritos" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Mostrar barra de favoritos" @@ -5797,6 +5857,7 @@ msgstr "Tema" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Pensando" diff --git a/src/renderer/locales/ru/messages.po b/src/renderer/locales/ru/messages.po index 34686cbd..668317c3 100644 --- a/src/renderer/locales/ru/messages.po +++ b/src/renderer/locales/ru/messages.po @@ -949,6 +949,14 @@ msgstr "заблокировано:" msgid "Bold" msgstr "Полужирный" +#: 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 +msgid "Bookmarks" +msgstr "Закладки" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Снизу" @@ -976,6 +984,14 @@ msgstr "Правила защиты ветки заблокировали это msgid "Branches" msgstr "Ветки" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Вернуть на панель" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring it back into this panel, or jump to the window it’s running in." +msgstr "Верните его на эту панель или перейдите к окну, в котором он запущен." + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -998,6 +1014,10 @@ msgstr "Просмотрите свои репозитории GitHub или в msgid "Browser" msgstr "Браузер" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Браузер открыт в отдельном окне" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Browser MCP" @@ -1243,6 +1263,10 @@ msgstr "Профиль Claude удалён." msgid "Cleanup script" msgstr "Скрипт очистки" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Очистить историю просмотров" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Очистить историю просмотров" @@ -2526,6 +2550,10 @@ msgstr "Перейти на адресную строку браузера" msgid "Focus the in-app browser address bar" msgstr "Перейти на адресную строку встроенного браузера" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Перейти к окну" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Имя папки" @@ -2788,6 +2816,10 @@ msgstr "Скрыть боковую панель" msgid "Hide terminal" msgstr "Скрыть терминал" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "История" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3400,6 +3432,15 @@ msgstr "Самый используемый уровень рассуждени msgid "Move" msgstr "Переместить" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Вернуть браузер в главное окно" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Переместить браузер в окно" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Переместить изменения в новый worktree" @@ -3601,6 +3642,10 @@ msgstr "Пока нет отслеженных коммитов, PR или ра msgid "No archived threads." msgstr "Нет архивированных тредов." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Закладок пока нет — добавьте текущую страницу звёздочкой." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Ветки не найдены" @@ -3678,6 +3723,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Учётные записи GitHub CLI не найдены. Войдите с помощью <0>gh auth " "login или <1>вставьте URL для клонирования." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Истории пока нет" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Нет совпадений для “{query}”." @@ -4567,6 +4616,11 @@ msgstr "Удалить {pattern}" msgid "Remove action" msgstr "Удалить действие" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Удалить закладку" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Удалить профиль Claude" @@ -4706,7 +4760,7 @@ msgstr "Сбросить матовость боковой панели к зн msgid "Reset to default" msgstr "Сбросить к значению по умолчанию" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Изменить размер панели браузера" @@ -5017,7 +5071,7 @@ msgstr "Поиск моделей" msgid "Search models..." msgstr "Поиск моделей..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Поиск или ввод адреса" @@ -5136,6 +5190,7 @@ msgid "Set shortcut" msgstr "Назначить сочетание клавиш" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5174,6 +5229,10 @@ msgstr "Показывать домашнюю область без проект msgid "Show all" msgstr "Показать всё" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Показать панель закладок" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Показать панель закладок" @@ -5788,6 +5847,7 @@ msgstr "Тема" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Размышление" diff --git a/src/renderer/locales/tr/messages.po b/src/renderer/locales/tr/messages.po index 2215731b..4da573e7 100644 --- a/src/renderer/locales/tr/messages.po +++ b/src/renderer/locales/tr/messages.po @@ -949,6 +949,14 @@ msgstr "engellendi:" msgid "Bold" msgstr "Kalın" +#: 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 +msgid "Bookmarks" +msgstr "Yer imleri" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Alt" @@ -976,6 +984,14 @@ msgstr "Dal koruma kuralları bu birleştirmeyi engelledi." msgid "Branches" msgstr "Şubeler" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Panele geri getir" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -998,6 +1014,10 @@ msgstr "GitHub depolarınıza göz atın veya bir klon URL'si yapıştırın." msgid "Browser" msgstr "Tarayıcı" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Tarayıcı ayrı bir pencerede açık" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Tarayıcı MCP'si" @@ -1242,6 +1262,10 @@ msgstr "Claude profili kaldırıldı." msgid "Cleanup script" msgstr "Temizleme komut dosyası" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Tarama geçmişini temizle" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Tarama Geçmişini Temizle" @@ -2525,6 +2549,10 @@ msgstr "Tarayıcı adres çubuğuna odakla" msgid "Focus the in-app browser address bar" msgstr "Uygulama içi tarayıcı adres çubuğuna odakla" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Pencereyi öne getir" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Klasör adı" @@ -2787,6 +2815,10 @@ msgstr "Kenar çubuğunu gizle" msgid "Hide terminal" msgstr "Terminali gizle" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Geçmiş" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3398,6 +3430,15 @@ msgstr "En çok kullanılan akıl yürütme" msgid "Move" msgstr "Taşı" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Tarayıcıyı ana pencereye geri taşı" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Tarayıcıyı pencereye taşı" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Değişiklikleri yeni bir çalışma ağacına taşı" @@ -3599,6 +3640,10 @@ msgstr "Henüz yapay zeka commit'leri, PR'ları veya çakışma çözümleri izl msgid "No archived threads." msgstr "Arşivlenmiş konu yok." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Henüz yer imi yok — geçerli sayfayı yıldızla ekleyin." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Şube bulunamadı" @@ -3676,6 +3721,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "GitHub CLI hesabı bulunamadı. <0>gh auth login ile oturum açın veya " "<1>bir klon URL'si yapıştırın." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Henüz geçmiş yok" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "“{query}” için eşleşme yok." @@ -4563,6 +4612,11 @@ msgstr "{pattern} öğesini kaldır" 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/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Yer imini kaldır" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Claude profilini kaldır" @@ -4702,7 +4756,7 @@ msgstr "Kenar çubuğu buzlu cam efektini varsayılana sıfırla" msgid "Reset to default" msgstr "Varsayılana sıfırla" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Tarayıcı çekmecesini yeniden boyutlandır" @@ -5015,7 +5069,7 @@ msgstr "Modelleri ara" msgid "Search models..." msgstr "Modelleri ara..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Adres arayın veya girin" @@ -5135,6 +5189,7 @@ msgid "Set shortcut" msgstr "Kısayol belirle" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5173,6 +5228,10 @@ 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 +msgid "Show bookmark bar" +msgstr "Yer imi çubuğunu göster" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Yer İşareti Çubuğunu Göster" @@ -5790,6 +5849,7 @@ msgstr "Tema" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Düşünme" diff --git a/src/renderer/locales/uk/messages.po b/src/renderer/locales/uk/messages.po index 7a3686d8..7ce4933a 100644 --- a/src/renderer/locales/uk/messages.po +++ b/src/renderer/locales/uk/messages.po @@ -949,6 +949,14 @@ msgstr "заблоковано:" msgid "Bold" msgstr "Жирний" +#: 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 +msgid "Bookmarks" +msgstr "Закладки" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Знизу" @@ -976,6 +984,14 @@ msgstr "Правила захисту гілки заблокували це з msgid "Branches" msgstr "Гілки" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Повернути на панель" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring it back into this panel, or jump to the window it’s running in." +msgstr "Поверніть його на цю панель або перейдіть до вікна, де він запущений." + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -998,6 +1014,10 @@ msgstr "Перегляньте свої репозиторії GitHub або в msgid "Browser" msgstr "Браузер" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Браузер відкритий в окремому вікні" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "Browser MCP" @@ -1242,6 +1262,10 @@ msgstr "Профіль Claude видалено." msgid "Cleanup script" msgstr "Скрипт очищення" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "Очистити історію перегляду" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "Очистити історію переглядів" @@ -2525,6 +2549,10 @@ msgstr "Встановити фокус на адресний рядок бра msgid "Focus the in-app browser address bar" msgstr "Встановити фокус на адресний рядок браузера у програмі" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Перейти до вікна" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Ім'я папки" @@ -2788,6 +2816,10 @@ msgstr "Сховати бічну панель" msgid "Hide terminal" msgstr "Сховати термінал" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Історія" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3400,6 +3432,15 @@ msgstr "Найуживаніший рівень міркувань" msgid "Move" msgstr "Перемістити" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Повернути браузер у головне вікно" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Перемістити браузер у вікно" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Перемістити зміни в новий worktree" @@ -3601,6 +3642,10 @@ msgstr "Ще не відстежено жодних комітів, PR чи ро msgid "No archived threads." msgstr "Немає архівованих тредів." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Закладок ще немає — додайте поточну сторінку зірочкою." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Гілки не знайдено" @@ -3678,6 +3723,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Облікові записи GitHub CLI не знайдено. Увійдіть за допомогою <0>gh " "auth login або <1>вставте URL для клонування." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Історії ще немає" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Немає збігів для “{query}”." @@ -4566,6 +4615,11 @@ msgstr "Видалити {pattern}" msgid "Remove action" msgstr "Видалити дію" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Видалити закладку" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Видалити профіль Claude" @@ -4705,7 +4759,7 @@ msgstr "Скинути матовість бічної панелі до тип msgid "Reset to default" msgstr "Скинути до типового" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "Змінити розмір панелі браузера" @@ -5016,7 +5070,7 @@ msgstr "Пошук моделей" msgid "Search models..." msgstr "Пошук моделей..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Пошук або введення адреси" @@ -5136,6 +5190,7 @@ msgid "Set shortcut" msgstr "Призначити комбінацію клавіш" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5173,6 +5228,10 @@ msgstr "Показувати домашню область без проєкту msgid "Show all" msgstr "Показати все" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "Показати панель закладок" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Показати панель закладок" @@ -5787,6 +5846,7 @@ msgstr "Тема" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Міркування" diff --git a/src/renderer/locales/vi/messages.po b/src/renderer/locales/vi/messages.po index 208b2f9a..e24744e2 100644 --- a/src/renderer/locales/vi/messages.po +++ b/src/renderer/locales/vi/messages.po @@ -948,6 +948,14 @@ msgstr "bị chặn:" msgid "Bold" msgstr "Đậm" +#: 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 +msgid "Bookmarks" +msgstr "Dấu trang" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "Dưới cùng" @@ -975,6 +983,14 @@ msgstr "Quy tắc bảo vệ nhánh đã chặn lần hợp nhất này." msgid "Branches" msgstr "Nhánh" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "Đưa về bảng điều khiển" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +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/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -997,6 +1013,10 @@ msgstr "Duyệt qua kho GitHub của bạn hoặc dán URL nhân bản." msgid "Browser" msgstr "Trình duyệt" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "Trình duyệt đang mở trong một cửa sổ riêng" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "MCP trình duyệt" @@ -1241,6 +1261,10 @@ msgstr "Hồ sơ Claude đã bị xóa." msgid "Cleanup script" msgstr "Tập lệnh dọn dẹp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.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 Browsing History" msgstr "Xóa lịch sử duyệt web" @@ -2520,6 +2544,10 @@ msgstr "Tập trung vào thanh địa chỉ trình duyệt" msgid "Focus the in-app browser address bar" msgstr "Tập trung vào thanh địa chỉ của trình duyệt tích hợp" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "Chuyển đến cửa sổ" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "Tên thư mục" @@ -2781,6 +2809,10 @@ msgstr "Ẩn thanh bên" msgid "Hide terminal" msgstr "Ẩn thiết bị đầu cuối" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "Lịch sử" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3392,6 +3424,15 @@ msgstr "Mức suy luận dùng nhiều nhất" msgid "Move" msgstr "Di chuyển" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "Chuyển trình duyệt về cửa sổ chính" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "Chuyển trình duyệt sang cửa sổ" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "Di chuyển các thay đổi sang một cây làm việc mới" @@ -3593,6 +3634,10 @@ msgstr "Chưa ghi nhận commit, PR hay giải quyết xung đột nào bằng A msgid "No archived threads." msgstr "Không có luồng nào đã lưu trữ." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "Chưa có dấu trang nào — thêm trang hiện tại bằng biểu tượng ngôi sao." + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "Không tìm thấy chi nhánh nào" @@ -3670,6 +3715,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " msgstr "Không tìm thấy tài khoản GitHub CLI nào. Đăng nhập bằng <0>gh auth " "login hoặc <1>dán URL sao chép." +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "Chưa có lịch sử" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "Không có kết quả phù hợp cho “{query}”." @@ -4558,6 +4607,11 @@ msgstr "Xóa {pattern}" 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/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "Xóa dấu trang" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "Xóa hồ sơ Claude" @@ -4698,7 +4752,7 @@ 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/components/layout/BrowserDrawerShell.tsx +#: 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" @@ -5008,7 +5062,7 @@ msgstr "Tìm kiếm mô hình" msgid "Search models..." msgstr "Tìm kiếm mô hình..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "Tìm kiếm hoặc nhập địa chỉ" @@ -5127,6 +5181,7 @@ msgid "Set shortcut" msgstr "Đặt phím tắt" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5165,6 +5220,10 @@ 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 +msgid "Show bookmark bar" +msgstr "Hiển thị thanh dấu trang" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "Hiển thị thanh dấu trang" @@ -5780,6 +5839,7 @@ msgstr "Chủ đề" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "Đang suy nghĩ" diff --git a/src/renderer/locales/zh-CN/messages.po b/src/renderer/locales/zh-CN/messages.po index 65f09f6d..92eadc14 100644 --- a/src/renderer/locales/zh-CN/messages.po +++ b/src/renderer/locales/zh-CN/messages.po @@ -937,6 +937,14 @@ msgstr "已阻止:" msgid "Bold" msgstr "加粗" +#: 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 +msgid "Bookmarks" +msgstr "书签" + #: src/renderer/views/SettingsOverlay/parts/settingsOptions.ts msgid "Bottom" msgstr "底部" @@ -963,6 +971,14 @@ msgstr "分支保护规则阻止了这次合并。" msgid "Branches" msgstr "分支" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring back to panel" +msgstr "移回面板" + +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Bring it back into this panel, or jump to the window it’s running in." +msgstr "将其移回此面板,或跳转到正在运行它的窗口。" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx #: src/renderer/views/MainView/parts/CreateProject/CreateProjectModal.tsx msgid "Browse for parent folder" @@ -985,6 +1001,10 @@ msgstr "浏览您的 GitHub 存储库或粘贴克隆 URL。" msgid "Browser" msgstr "浏览器" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Browser is open in a separate window" +msgstr "浏览器已在单独的窗口中打开" + #: src/renderer/components/composer/MentionPopover.tsx msgid "Browser MCP" msgstr "浏览器 MCP" @@ -1227,6 +1247,10 @@ msgstr "Claude 配置文件已删除。" msgid "Cleanup script" msgstr "清理脚本" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Clear browsing history" +msgstr "清除浏览历史记录" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Clear Browsing History" msgstr "清除浏览历史记录" @@ -2486,6 +2510,10 @@ msgstr "聚焦浏览器地址栏" msgid "Focus the in-app browser address bar" msgstr "聚焦应用内浏览器地址栏" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx +msgid "Focus window" +msgstr "切换到窗口" + #: src/renderer/views/MainView/parts/CreateProject/CloneProjectModal.tsx msgid "Folder name" msgstr "文件夹名称" @@ -2739,6 +2767,10 @@ msgstr "隐藏侧边栏" msgid "Hide terminal" msgstr "隐藏终端" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "History" +msgstr "历史记录" + #: src/renderer/views/HomeView.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/WelcomeOverlay.tsx @@ -3342,6 +3374,15 @@ msgstr "最常用思考级别" msgid "Move" msgstr "移动" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser back to main window" +msgstr "将浏览器移回主窗口" + +#: src/renderer/components/layout/UnifiedRightPanel.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel.tsx +msgid "Move browser to window" +msgstr "将浏览器移到窗口" + #: src/renderer/components/common/BranchSelector/parts/BranchFooterActions.tsx msgid "Move changes to a new worktree" msgstr "将更改移至新工作树" @@ -3542,6 +3583,10 @@ msgstr "暂未记录任何AI提交、PR或冲突解决。" msgid "No archived threads." msgstr "没有存档的线程。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +msgid "No bookmarks yet — add the current page with the star." +msgstr "暂无书签——点击星标添加当前页面。" + #: src/renderer/components/common/BranchSelector/parts/BranchListBox.tsx msgid "No branches found" msgstr "未找到分支" @@ -3618,6 +3663,10 @@ msgid "No GitHub CLI accounts found. Sign in with <0>gh auth login, or " "<1>paste a clone URL." msgstr "未找到 GitHub CLI 帐户。使用<0>gh auth login登录,或<1>粘贴克隆网址。" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "No history yet" +msgstr "暂无历史记录" + #: src/renderer/components/thread/ChatPane/ChatRuntimeDebugPanel.tsx msgid "No matches for “{query}”." msgstr "没有匹配“{query}”。" @@ -4496,6 +4545,11 @@ msgstr "删除{pattern}" msgid "Remove action" msgstr "删除动作" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserBookmarkBar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Remove bookmark" +msgstr "移除书签" + #: src/renderer/views/SettingsOverlay/parts/ClaudeProfileSettings.tsx msgid "Remove Claude profile" msgstr "删除 Claude 配置文件" @@ -4635,7 +4689,7 @@ msgstr "将侧边栏磨砂效果重置为默认" msgid "Reset to default" msgstr "重置为默认" -#: src/renderer/components/layout/BrowserDrawerShell.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx msgid "Resize browser drawer" msgstr "调整浏览器抽屉的大小" @@ -4941,7 +4995,7 @@ msgstr "搜索模型" msgid "Search models..." msgstr "搜索模型..." -#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserOmnibox.tsx msgid "Search or enter address" msgstr "搜索或输入地址" @@ -5058,6 +5112,7 @@ msgid "Set shortcut" msgstr "设置快捷键" #: src/renderer/components/thread/ThreadAuthRequiredDock.tsx +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx #: src/renderer/views/MainView/parts/Sidebar/Sidebar.tsx #: src/renderer/views/SettingsOverlay/SettingsOverlay.tsx msgid "Settings" @@ -5095,6 +5150,10 @@ msgstr "为操作系统级别的代理会话显示一个无项目的主页范围 msgid "Show all" msgstr "显示全部" +#: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx +msgid "Show bookmark bar" +msgstr "显示书签栏" + #: src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/parts/BrowserToolbar.tsx msgid "Show Bookmark Bar" msgstr "显示书签栏" @@ -5701,6 +5760,7 @@ msgstr "主题" #: src/renderer/components/common/EffortContextMenu/EffortContextMenu.tsx #: src/renderer/components/thread/ChatPane/parts/items/Reasoning.tsx +#: src/renderer/components/thread/ThreadComposer.tsx msgid "Thinking" msgstr "思考" diff --git a/src/renderer/state/browserDockStore.ts b/src/renderer/state/browserDockStore.ts new file mode 100644 index 00000000..76fff430 --- /dev/null +++ b/src/renderer/state/browserDockStore.ts @@ -0,0 +1,21 @@ +import { create } from "zustand"; + +/** + * Bridges the right-panel browser "dock slot" (a placeholder rendered inside + * {@link ProjectAuxiliaryPanel}) to the top-level {@link BrowserHost}. + * + * The browser webview is mounted once in a `document.body` portal so it never + * unmounts (and thus never reloads the page) as it moves between docked, + * floating, and fullscreen presentations. While docked it must visually sit + * over the panel's browser tab area, so the slot publishes its DOM element here + * and the host tracks its rect imperatively. + */ +interface BrowserDockState { + slotEl: HTMLElement | null; + setSlotEl: (el: HTMLElement | null) => void; +} + +export const useBrowserDockStore = create((set) => ({ + slotEl: null, + setSlotEl: (el) => set((s) => (s.slotEl === el ? {} : { slotEl: el })), +})); diff --git a/src/renderer/state/browserPanelStore.ts b/src/renderer/state/browserPanelStore.ts index c4c6adc5..0df2b4fa 100644 --- a/src/renderer/state/browserPanelStore.ts +++ b/src/renderer/state/browserPanelStore.ts @@ -1,6 +1,6 @@ import { create } from "zustand"; import type { UsageLoginConfirmationRequest, UsageLoginDeviceCode } from "@/shared/contracts"; -import type { BrowserState, BrowserTabInfo } from "@/shared/ipc"; +import type { BrowserBookmarkInfo, BrowserState, BrowserTabInfo } from "@/shared/ipc"; export interface PendingPickerAttachment { attachmentPath: string; @@ -15,6 +15,9 @@ export interface PendingPickerAttachment { interface BrowserPanelState { tabs: BrowserTabInfo[]; activeTabId: string | null; + extracted: boolean; + bookmarks: BrowserBookmarkInfo[]; + bookmarkBarVisible: boolean; pickerActive: boolean; attentionTabId: string | null; pendingPickerAttachment: PendingPickerAttachment | null; @@ -35,6 +38,9 @@ interface BrowserPanelState { export const useBrowserPanelStore = create((set) => ({ tabs: [], activeTabId: null, + extracted: false, + bookmarks: [], + bookmarkBarVisible: false, pickerActive: false, attentionTabId: null, pendingPickerAttachment: null, @@ -43,8 +49,25 @@ export const useBrowserPanelStore = create((set) => ({ setState: (state) => set((s) => { - if (s.activeTabId === state.activeTabId && tabsEqual(s.tabs, state.tabs)) return {}; - return { tabs: state.tabs, activeTabId: state.activeTabId }; + const extracted = state.extracted === true; + const bookmarks = state.bookmarks ?? []; + const bookmarkBarVisible = state.bookmarkBarVisible === true; + if ( + s.activeTabId === state.activeTabId && + s.extracted === extracted && + s.bookmarkBarVisible === bookmarkBarVisible && + bookmarksEqual(s.bookmarks, bookmarks) && + tabsEqual(s.tabs, state.tabs) + ) { + return {}; + } + return { + tabs: state.tabs, + activeTabId: state.activeTabId, + extracted, + bookmarks, + bookmarkBarVisible, + }; }), upsertTab: (tab) => set((s) => { @@ -82,6 +105,16 @@ function tabsEqual(a: BrowserTabInfo[], b: BrowserTabInfo[]): boolean { return a.length === b.length && a.every((tab, i) => tabInfoEqual(tab, b[i]!)); } +function bookmarksEqual(a: BrowserBookmarkInfo[], b: BrowserBookmarkInfo[]): boolean { + return ( + a.length === b.length && + a.every( + (bm, i) => + bm.url === b[i]!.url && bm.title === b[i]!.title && bm.faviconUrl === b[i]!.faviconUrl, + ) + ); +} + function tabInfoEqual(a: BrowserTabInfo, b: BrowserTabInfo): boolean { return ( a.tabId === b.tabId && diff --git a/src/renderer/state/panelStore.test.ts b/src/renderer/state/panelStore.test.ts index abd32cf0..fb3e144d 100644 --- a/src/renderer/state/panelStore.test.ts +++ b/src/renderer/state/panelStore.test.ts @@ -128,24 +128,37 @@ describe("browserOverlayMaximized lifecycle", () => { expect(usePanelStore.getState().browserOverlayMaximized).toBe(false); }); - it("is reset when the browser panel is closed entirely", () => { + it("survives hiding the right-panel browser (overlay is independent)", () => { const { setBrowserOverlayOpen, setBrowserOverlayMaximized, setBrowserPanelOpen } = usePanelStore.getState(); + setBrowserPanelOpen(true); setBrowserOverlayOpen(true); setBrowserOverlayMaximized(true); + // Hiding the docked panel must not tear down a maximized overlay, otherwise + // the fullscreen page would vanish when the right panel is hidden. setBrowserPanelOpen(false); - expect(usePanelStore.getState().browserOverlayMaximized).toBe(false); - expect(usePanelStore.getState().browserOverlayOpen).toBe(false); + expect(usePanelStore.getState().browserPanelOpen).toBe(false); + expect(usePanelStore.getState().browserOverlayMaximized).toBe(true); + expect(usePanelStore.getState().browserOverlayOpen).toBe(true); }); - it("is reset by closeAllPanels", () => { - const { setBrowserOverlayOpen, setBrowserOverlayMaximized, closeAllPanels } = - usePanelStore.getState(); + it("survives closeAllPanels (e.g. the narrow-viewport right-panel auto-hide)", () => { + const { + setBrowserPanelOpen, + setBrowserOverlayOpen, + setBrowserOverlayMaximized, + closeAllPanels, + } = usePanelStore.getState(); + setBrowserPanelOpen(true); setBrowserOverlayOpen(true); setBrowserOverlayMaximized(true); + // closeAllPanels backs the right-panel auto-hide on resize; it must close + // the docked panel but leave the standalone browser overlay intact. closeAllPanels(); - expect(usePanelStore.getState().browserOverlayMaximized).toBe(false); + expect(usePanelStore.getState().browserPanelOpen).toBe(false); + expect(usePanelStore.getState().browserOverlayOpen).toBe(true); + expect(usePanelStore.getState().browserOverlayMaximized).toBe(true); }); }); diff --git a/src/renderer/state/panelStore.ts b/src/renderer/state/panelStore.ts index 22325c38..8a966173 100644 --- a/src/renderer/state/panelStore.ts +++ b/src/renderer/state/panelStore.ts @@ -191,19 +191,18 @@ export const usePanelStore = create((set) => ({ }), setRightPanelTab: (tab) => set((state) => (state.rightPanelTab === tab ? {} : { rightPanelTab: tab })), + // Toggling the docked right-panel browser is independent of the floating + // overlay (drawer/fullscreen): hiding the panel must NOT tear down an active + // overlay, otherwise maximizing the browser and then hiding the right panel + // would make the fullscreen page vanish. Callers that genuinely want to + // dismiss both (e.g. the last tab closing) close the overlay explicitly. setBrowserPanelOpen: (v) => - set((state) => - state.browserPanelOpen === v && (v || !state.browserOverlayOpen) - ? {} - : { - browserPanelOpen: v, - ...(v ? {} : { browserOverlayOpen: false, browserOverlayMaximized: false }), - }, - ), + set((state) => (state.browserPanelOpen === v ? {} : { browserPanelOpen: v })), // NOTE: overlay state is intentionally independent of the right-panel - // browser. Opening the overlay does NOT enable the right-panel browser tab, - // and closing the overlay leaves the right panel in whatever state the user - // had it. Maximized resets on close so the next open lands in drawer mode. + // browser in both directions. Opening the overlay does NOT enable the + // right-panel browser tab, and closing the overlay leaves the right panel in + // whatever state the user had it. Maximized resets on close so the next open + // lands in drawer mode. setBrowserOverlayOpen: (v) => set((state) => state.browserOverlayOpen === v @@ -279,14 +278,17 @@ export const usePanelStore = create((set) => ({ closeAllPanels: () => { localStorage.removeItem(STORAGE_KEY); set((state) => { + // The floating browser overlay (drawer/fullscreen) is intentionally NOT + // touched here: it is a standalone surface with its own close controls. + // Closing the docked right panel — including the narrow-viewport auto-hide + // that fires when the window shrinks — must not tear it down, otherwise a + // maximized browser vanishes the moment the right panel auto-closes. if ( state.gitReviewContext === null && state.filesPanelContext === null && !state.browserPanelOpen && !state.usagePanelOpen && - !state.notesPanelOpen && - !state.browserOverlayOpen && - !state.browserOverlayMaximized + !state.notesPanelOpen ) { return {}; } @@ -296,8 +298,6 @@ export const usePanelStore = create((set) => ({ browserPanelOpen: false, usagePanelOpen: false, notesPanelOpen: false, - browserOverlayOpen: false, - browserOverlayMaximized: false, }; }); }, diff --git a/src/renderer/views/MainView/parts/AppOverlays.tsx b/src/renderer/views/MainView/parts/AppOverlays.tsx index 3a5fd163..18d364f6 100644 --- a/src/renderer/views/MainView/parts/AppOverlays.tsx +++ b/src/renderer/views/MainView/parts/AppOverlays.tsx @@ -36,7 +36,7 @@ import { Button } from "@/renderer/components/common/Button"; import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import type { UsageLoginConfirmationAction } from "@/shared/contracts"; import { WelcomeOverlay } from "@/renderer/views/WelcomeOverlay"; -import { BrowserOverlay } from "@/renderer/views/MainView/parts/BrowserOverlay"; +import { BrowserHost } from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost"; import { LoginTerminalOverlay } from "@/renderer/views/LoginTerminalOverlay/LoginTerminalOverlay"; import { CreateProjectModal } from "@/renderer/views/MainView/parts/CreateProject/CreateProjectModal"; import { CloneProjectModal } from "@/renderer/views/MainView/parts/CreateProject/CloneProjectModal"; @@ -60,7 +60,6 @@ export function AppOverlays() { ? projects.find((p) => p.id === prReviewContext.projectId) : undefined; const prReviewVisible = !!prReviewContext && !!prReviewProject; - const browserOverlayOpen = usePanelStore((s) => s.browserOverlayOpen); return ( <> @@ -180,7 +179,7 @@ export function AppOverlays() { ) : null} - + diff --git a/src/renderer/views/MainView/parts/BrowserOverlay.tsx b/src/renderer/views/MainView/parts/BrowserOverlay.tsx deleted file mode 100644 index 887a92f1..00000000 --- a/src/renderer/views/MainView/parts/BrowserOverlay.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { usePanelStore } from "@/renderer/state/panelStore"; -import { BrowserDrawerShell } from "@/renderer/components/layout/BrowserDrawerShell"; -import { BrowserPanel } from "./RightPanel/parts/BrowserPanel/BrowserPanel"; - -export function BrowserOverlay(props: { open: boolean }) { - const { open } = props; - const maximized = usePanelStore((s) => s.browserOverlayMaximized); - const setBrowserOverlayOpen = usePanelStore((s) => s.setBrowserOverlayOpen); - - return ( - setBrowserOverlayOpen(false)} - > - - - ); -} diff --git a/src/renderer/views/MainView/parts/ProjectAuxiliaryPanel.tsx b/src/renderer/views/MainView/parts/ProjectAuxiliaryPanel.tsx index 6e77afed..79bf40fa 100644 --- a/src/renderer/views/MainView/parts/ProjectAuxiliaryPanel.tsx +++ b/src/renderer/views/MainView/parts/ProjectAuxiliaryPanel.tsx @@ -2,7 +2,11 @@ import { useRef } from "react"; import { useLingui } from "@lingui/react/macro"; import type { Project } from "@/shared/contracts"; import { isHomeProjectId } from "@/shared/homeScope"; -import { BrowserPanel } from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserPanel"; +import { BrowserDockSlot } from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot"; +import { + extractBrowserToWindow, + injectBrowserToMain, +} from "@/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/browserWindowActions"; import { DevTerminalPanel } from "@/renderer/views/MainView/parts/RightPanel/parts/DevTerminalPanel/DevTerminalPanel"; import { UnifiedRightPanel, @@ -13,6 +17,7 @@ import { NotesPanel } from "@/renderer/views/MainView/parts/RightPanel/parts/Not import { UsagePanel } from "@/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/UsagePanel"; import { UsagePanelHeaderActions } from "@/renderer/views/MainView/parts/RightPanel/parts/UsagePanel/parts/UsagePanelHeaderActions"; import { useAppStore } from "@/renderer/state/appStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; import { useDevTerminalStore } from "@/renderer/state/devTerminalStore"; import { useFileEditorStore, type FileEditorRootContext } from "@/renderer/state/fileEditorStore"; import { @@ -73,7 +78,7 @@ export function ProjectAuxiliaryPanel(props: { includeTerminal: boolean }) { const rightPanelTab = usePanelStore((s) => s.rightPanelTab); const setRightPanelTab = usePanelStore((s) => s.setRightPanelTab); const browserPanelOpen = usePanelStore((s) => s.browserPanelOpen); - const browserOverlayOpen = usePanelStore((s) => s.browserOverlayOpen); + const browserExtracted = useBrowserPanelStore((s) => s.extracted); const usagePanelOpen = usePanelStore((s) => s.usagePanelOpen); const setUsagePanelOpen = usePanelStore((s) => s.setUsagePanelOpen); const notesPanelOpen = usePanelStore((s) => s.notesPanelOpen); @@ -195,7 +200,7 @@ export function ProjectAuxiliaryPanel(props: { includeTerminal: boolean }) { const renderTerminalContent = props.includeTerminal && terminalOpen; const renderGitContent = gitPanelOpen; const renderFilesContent = filesPanelOpen; - const renderBrowserContent = browserPanelOpen && !browserOverlayOpen; + const renderBrowserContent = browserPanelOpen; const renderUsageContent = usagePanelOpen; const renderNotesContent = notesPanelOpen && notesProjectId !== undefined; @@ -218,7 +223,15 @@ export function ProjectAuxiliaryPanel(props: { includeTerminal: boolean }) { ) : undefined } - browserContent={renderBrowserContent ? : undefined} + browserContent={ + renderBrowserContent ? ( + + ) : undefined + } usageContent={renderUsageContent ? : undefined} notesContent={ renderNotesContent && notesProjectId ? ( @@ -239,10 +252,15 @@ export function ProjectAuxiliaryPanel(props: { includeTerminal: boolean }) { setBrowserOverlayMaximized(true); setBrowserOverlayOpen(true); }} + onExtractBrowserToWindow={extractBrowserToWindow} onOpenGit={handleOpenGit} onOpenFiles={handleOpenFiles} {...(props.includeTerminal ? { onOpenTerminal: handleOpenTerminal } : {})} onOpenBrowser={() => { + if (browserExtracted) { + extractBrowserToWindow(); + return; + } setBrowserPanelOpen(true); setRightPanelTab("browser"); }} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx new file mode 100644 index 00000000..d54dbf52 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserDockSlot.tsx @@ -0,0 +1,53 @@ +import { useCallback } from "react"; +import { Trans } from "@lingui/react/macro"; +import { ExternalLink, PictureInPicture2 } from "lucide-react"; +import { useBrowserDockStore } from "@/renderer/state/browserDockStore"; + +/** + * Right-panel browser tab content. The browser webview itself is rendered by + * {@link BrowserHost} in a body portal; this component only marks the area it + * should dock over (publishing the element to {@link useBrowserDockStore}), or + * shows a placeholder while the browser lives in a separate window. + */ +export function BrowserDockSlot(props: { + extracted: boolean; + onBringBack: () => void; + onFocusWindow: () => void; +}) { + const setSlotEl = useBrowserDockStore((s) => s.setSlotEl); + const slotRef = useCallback((el: HTMLDivElement | null) => setSlotEl(el), [setSlotEl]); + + if (props.extracted) { + return ( +
+ +
+ Browser is open in a separate window +
+

+ Bring it back into this panel, or jump to the window it’s running in. +

+
+ + +
+
+ ); + } + + return
; +} diff --git a/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx new file mode 100644 index 00000000..aa6a3011 --- /dev/null +++ b/src/renderer/views/MainView/parts/RightPanel/parts/BrowserPanel/BrowserHost.tsx @@ -0,0 +1,235 @@ +import { + memo, + useLayoutEffect, + useRef, + useState, + type CSSProperties, + type MouseEvent as ReactMouseEvent, +} from "react"; +import { createPortal } from "react-dom"; +import { useLingui } from "@lingui/react/macro"; +import { usePanelStore } from "@/renderer/state/panelStore"; +import { useBrowserPanelStore } from "@/renderer/state/browserPanelStore"; +import { useBrowserDockStore } from "@/renderer/state/browserDockStore"; +import { pushEscapeHandler } from "@/renderer/components/layout/overlayEscapeStack"; +import { BrowserPanel } from "./BrowserPanel"; + +const MemoBrowserPanel = memo(BrowserPanel); + +type BrowserHostMode = "hidden" | "docked" | "drawer" | "fullscreen"; + +/** + * Mounts the in-app browser exactly once, in a `document.body` portal, and + * repositions it per presentation mode (docked over the right-panel slot, + * floating drawer, or fullscreen). Keeping a single mounted instance is what + * lets the live page survive every docked↔drawer↔fullscreen transition — each + * `` owns its own guest WebContents, so remounting would reload it. + * + * Rendering from the body (rather than inside the right panel) is also required + * for correctness: the panel lives under a `will-change-transform` ancestor + * (AsideSlot) and inside a `z-index` stacking context (UnifiedRightPanel tab + * layer), both of which would clip a nested `position: fixed` overlay. + * + * Positioning is applied imperatively so per-frame rect tracking never + * re-renders the embedded webview. + */ +export function BrowserHost() { + const { t } = useLingui(); + const browserPanelOpen = usePanelStore((s) => s.browserPanelOpen); + const browserOverlayOpen = usePanelStore((s) => s.browserOverlayOpen); + const browserOverlayMaximized = usePanelStore((s) => s.browserOverlayMaximized); + const drawerWidth = usePanelStore((s) => s.browserOverlayDrawerWidth); + const setDrawerWidth = usePanelStore((s) => s.setBrowserOverlayDrawerWidth); + const rightPanelTab = usePanelStore((s) => s.rightPanelTab); + const setBrowserOverlayOpen = usePanelStore((s) => s.setBrowserOverlayOpen); + const setBrowserOverlayMaximized = usePanelStore((s) => s.setBrowserOverlayMaximized); + const setRightPanelTab = usePanelStore((s) => s.setRightPanelTab); + const extracted = useBrowserPanelStore((s) => s.extracted); + + const mode: BrowserHostMode = extracted + ? "hidden" + : browserOverlayOpen + ? browserOverlayMaximized + ? "fullscreen" + : "drawer" + : browserPanelOpen + ? "docked" + : "hidden"; + + const wrapperRef = useRef(null); + const [isResizing, setIsResizing] = useState(false); + + const dockedVisible = rightPanelTab === "browser"; + + // Position imperatively for every mode so leftover inline styles never fight + // the next mode's layout. While docked, track the panel slot's rect each + // frame — sidebar collapse / panel resize can move it without a React render. + useLayoutEffect(() => { + const w = wrapperRef.current; + if (!w) return; + if (mode === "fullscreen") { + Object.assign(w.style, { top: "0px", left: "0px", right: "0px", bottom: "0px" }); + w.style.width = ""; + w.style.height = ""; + w.style.maxWidth = ""; + // Clear the docked z-index override so the fullscreen class (z-80) wins. + w.style.zIndex = ""; + return; + } + if (mode === "drawer") { + Object.assign(w.style, { + top: "2rem", + right: "2rem", + bottom: "2rem", + left: "auto", + width: `${drawerWidth}px`, + maxWidth: "calc(100vw - 4rem)", + }); + w.style.height = ""; + // Clear the docked z-index override so the drawer class (z-60) wins. + w.style.zIndex = ""; + return; + } + // docked + if (!dockedVisible) return; + let raf = 0; + let last = ""; + let lastOverlay: boolean | null = null; + const measure = () => { + const el = useBrowserDockStore.getState().slotEl; + if (!el) return; + // On narrow viewports the right panel that hosts the slot floats as a + // fixed, opaque overlay (z-50). The webview is body-portaled at z-30, so + // it would paint *behind* that panel. Detect the overlay from the slot's + // own ancestry — its containing panel