@cjylzs/dsh-browser 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +232 -2
- package/README.zh.md +233 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +2575 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +8444 -0
- package/lib/index.js.map +1 -0
- package/package.json +96 -4
- package/skills/dsh-browser/SKILL.md +52 -0
- package/skills/dsh-browser/references/ambiguous-controls.md +21 -0
- package/skills/dsh-browser/references/reading-a-page.md +28 -0
- package/skills/dsh-browser/references/visual-evidence.md +20 -0
- package/skills/dsh-browser/references/waiting-for-app-state.md +25 -0
- package/skills/dsh-browser/references/when-nothing-happened.md +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["transport","useState","useRef","useCallback","AddressGlobe","useSyncExternalStore","BrowserGlyph","useCallback","useSyncExternalStore","useState","useRef","BrowserGlyph"],"sources":["../src/client/api.ts","../src/client/clipboard.ts","../src/browser/keys.ts","../src/client/keys.ts","../src/client/pages.ts","../src/client/stream-url.ts","../src/client/locales.ts","../src/client/view.tsx","../src/client/chip.ts","../src/client/title.tsx","../src/client/definition.ts","../src/client/reveal.ts","../src/client/close.ts","../src/client/settings-layout.ts","../src/client/settings.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Browser-side read of the host's live browser state.\n *\n * Plain same-origin fetch against the plugin's own route, the way the other\n * plugin in this profile talks to its host half. The route applies the same\n * trust check as the viewer socket, so the browser's authentication cookie is\n * what makes this succeed.\n */\n\n/** Lifecycle of one session's browser, as the settings page reports it. */\nexport type ClientBrowserState = 'idle' | 'starting' | 'ready' | 'failed' | 'closed'\n\n/** One page a session's browser holds open. */\nexport interface ClientTab {\n /** Position in the browser's own page list. */\n readonly index: number\n /** Address the page reports. */\n readonly url: string\n /** Whether this is the page the tools act on. */\n readonly active: boolean\n /**\n * The page's CDP target id — its stable identity, and what the sidebar's own\n * tab for it is addressed by. Absent only in the moment between a page\n * existing and its session answering for it.\n */\n readonly targetId?: string\n /** What the page last said its title was, when that is known. */\n readonly title?: string\n}\n\n/** What the host says about one session's browser. */\nexport interface ClientBrowserStatus {\n /** The session this browser belongs to. */\n readonly sessionId: string\n /**\n * Which browser process serves this session.\n *\n * Two rows reporting the same id are two conversations on one process — the\n * `workspace` profile mode — and share one profile and one CDP port.\n */\n readonly browserId?: string | undefined\n /**\n * The session's title, when the host could read one.\n *\n * A session id is not a name: every id starts with `session-` and the rest is\n * hexadecimal. What a person recognizes is the title, and the harness has one\n * for every conversation that has said anything.\n */\n readonly title?: string | undefined\n readonly state: ClientBrowserState\n /** External CDP port once the browser runs. */\n readonly debugPort?: number | undefined\n /** State the current instance was started through, e.g. `headless`. */\n readonly mode?: string | undefined\n /** Address the active page is on. */\n readonly url?: string | undefined\n /** Open pages this session owns, in the browser's own order. */\n readonly tabs?: readonly ClientTab[] | undefined\n /** How many pages this session owns. */\n readonly ownedPages?: number | undefined\n /** Why the browser is unusable, when it is. */\n readonly error?: string | undefined\n}\n\n/** Every browser the plugin is running. */\nexport interface ClientBrowserReport {\n /** How many session browsers may run at once. */\n readonly maxInstances: number\n /** One entry per session that has a browser. */\n readonly instances: readonly ClientBrowserStatus[]\n}\n\n/**\n * Read the live browser state.\n * @returns every session's browser, as the host sees it.\n * @throws {Error} when the route refuses or the answer is not a report.\n */\nexport async function browserStatus(): Promise<ClientBrowserReport> {\n const response = await fetch('/dsh-browser/status', { headers: { Accept: 'application/json' } })\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n return await response.json() as ClientBrowserReport\n}\n\n/**\n * Ask the host for a new page in one session's browser.\n *\n * The Sidebar's browser entry is the caller: the pane that asks exists to hold\n * the page this produces, and one ask is one page — a browser already running\n * gets another blank one rather than the tab that is already there.\n * `request` is the asking tab's own id, so a pane remounted by a tab switch, a\n * Session change, or a client reload is the same ask and opens nothing new.\n * @param sessionId - the session whose browser should open a page.\n * @param request - the asking tab's id.\n * @param url - address to load in the new page; a blank page when absent.\n * @returns the new page's CDP target id, when the host named it.\n * @throws {Error} when the route refuses or the browser cannot start.\n */\nexport async function openPageRequest(sessionId: string, request: string, url?: string): Promise<string | undefined> {\n const response = await fetch('/dsh-browser/pages', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'open',\n sessionId,\n request,\n ...url === undefined ? {} : { url },\n }),\n })\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const answer = await response.json() as { targetId?: unknown }\n return typeof answer.targetId === 'string' ? answer.targetId : undefined\n}\n\n/**\n * Ask the host to close one browser page.\n *\n * This is what closing a sidebar tab does to the page it mirrored: the tab's\n * own close handler fires here, before the tab is removed. The host decides\n * what the close means — one page among several closes alone, the last one\n * stops the browser — so the client never has to count pages it cannot see.\n * @param sessionId - the session whose browser holds the page.\n * @param targetId - the page's CDP target id.\n * @throws {Error} when the route refuses.\n */\nexport async function closePageRequest(sessionId: string, targetId: string): Promise<void> {\n const response = await fetch('/dsh-browser/pages', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, targetId }),\n })\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n}\n\n/**\n * Ask the host to stop one session's browser, and every page it holds.\n *\n * This is the settings page's control, and it is the one ask in the plugin that\n * means \"this conversation does not want a browser\" rather than \"close the page\n * I am looking at\". The host decides what that costs: it stops the browser, and\n * a viewer coming back does not start another.\n * @param sessionId - the session whose browser should stop.\n * @throws {Error} when the route refuses.\n */\nexport async function closeBrowserRequest(sessionId: string): Promise<void> {\n const response = await fetch('/dsh-browser/pages', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'close', sessionId, all: true }),\n })\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n}\n\n/** What the pane's restart control has to do to get a browser back. */\nexport type RestartRoute =\n /** A viewer socket is up: the host already holds this viewer, so ask there. */\n | { readonly via: 'socket' }\n /** No socket: watch never starts a browser, so the page ask is the only path. */\n | { readonly via: 'http' }\n /** No socket and no tab to ask under; there is nothing this pane can start. */\n | { readonly via: 'none' }\n\n/**\n * Decide how the restart control reaches the host.\n *\n * The control exists for a browser the pane cannot use, and the pane loses its\n * socket in exactly the case that matters: watching never starts a browser, so\n * after a close from the settings page the host refuses the subscription and\n * closes the socket. A message sent then is dropped in silence, which is how\n * this control came to do nothing when it was most needed. The page ask over\n * HTTP is the one path that does start a browser.\n * @param socketOpen - whether the viewer socket is open right now.\n * @param tabId - the asking tab's id, which the HTTP ask must carry.\n * @returns the route the caller should take.\n */\nexport function restartRoute(socketOpen: boolean, tabId: string | undefined): RestartRoute {\n if (socketOpen) return { via: 'socket' }\n if (tabId !== undefined && tabId !== '') return { via: 'http' }\n return { via: 'none' }\n}\n\n","/**\n * The pane's own clipboard: which keystroke is a copy, a cut or a paste, and\n * what the pane does with it once the mirror answers.\n *\n * The mirrored page can never receive one of these keystrokes. Chromium runs the\n * clipboard shortcuts in the **browser process**, and an injected key event does\n * not get there: measured against this plugin's own browser, `Ctrl+C`, `Ctrl+V`\n * and `Ctrl+X` produce no DOM `keydown` at all in the page, while `Ctrl+A`,\n * `Ctrl+K` and `Ctrl+Z` arrive normally. CDP's `commands` field runs editing\n * commands but not the clipboard ones, even with the clipboard permission\n * granted to the page.\n *\n * The pane, on the other hand, runs in the browser the *user* is in: the same\n * keystroke there is a trusted event with the system clipboard one Web API away.\n * That is where every browser-based remote desktop puts this, and the decisions\n * with a right answer live here so they can be tested without a browser:\n * VS Code's web clipboard writes with `navigator.clipboard` and falls back to a\n * hidden textarea plus `execCommand('copy')`; xterm.js terminals read\n * `navigator.clipboard.readText()` on the paste shortcut; Guacamole's rule for\n * its own bridge is that it must never disturb the clipboard unless the local\n * one actually changed, which is what {@link clipboardReply} keeps.\n */\nimport type { KeyMessage } from './keys.ts'\n\n/** The parts of a DOM key event this decision is given. */\nexport interface ChordEventLike {\n /** DOM `key` value. */\n readonly key: string\n /** Whether the platform's primary modifier is held. */\n readonly ctrlKey: boolean\n /** Whether Meta (Command on macOS) is held. */\n readonly metaKey: boolean\n /** Whether Alt is held. */\n readonly altKey: boolean\n /** Whether Shift is held. Read by nothing here: it takes no shortcut away. */\n readonly shiftKey: boolean\n}\n\n/** What a keystroke asks the pane's clipboard to do. */\nexport type ClipboardChord = 'copy' | 'cut' | 'paste'\n\n/** The chords that read the mirror's selection before doing anything. */\nexport type ReadingChord = Exclude<ClipboardChord, 'paste'>\n\n/**\n * Whether this keystroke is one of the clipboard shortcuts the pane owns.\n *\n * Alt disqualifies it: on Windows AltGr arrives as Control+Alt and still types a\n * character, and the pane's own key path sends that as text. Shift does not:\n * `Control+Shift+V` is a browser's paste-as-plain-text, which is exactly what the\n * mirror's paste is, and nothing here claims Shift+C or Shift+X.\n * @param event - the keydown to classify.\n * @returns the chord to handle, or undefined when the key belongs to the mirror.\n */\nexport function clipboardChord(event: ChordEventLike): ClipboardChord | undefined {\n if (event.altKey) return undefined\n if (!event.ctrlKey && !event.metaKey) return undefined\n switch (event.key.toLowerCase()) {\n case 'c': return 'copy'\n case 'x': return 'cut'\n case 'v': return 'paste'\n default: return undefined\n }\n}\n\n/** What the pane does once the mirror has reported its selection. */\nexport interface ClipboardReply {\n /** The text to put on the clipboard, or undefined when there is nothing to copy. */\n readonly write: string | undefined\n /** The input message to send after a cut, or undefined when nothing was cut. */\n readonly after: KeyMessage | undefined\n}\n\n/**\n * What a copy or a cut does with the text the mirror reported.\n *\n * An empty selection writes nothing: a browser's own copy with nothing selected\n * leaves the clipboard as it was, and overwriting it with \"\" would be the pane\n * clobbering a clipboard the user did not ask it to touch. A cut that did have\n * something deletes it in the mirror, which is the whole difference between the\n * two chords.\n * @param chord - the reading chord that was pressed.\n * @param text - the selection the mirror reported.\n * @returns the clipboard write and, for a cut, the delete that follows it.\n */\nexport function clipboardReply(chord: ReadingChord, text: string): ClipboardReply {\n if (text === '') return { write: undefined, after: undefined }\n return { write: text, ...chord === 'cut' ? { after: { type: 'key', key: 'Delete' } } : { after: undefined } }\n}\n\n/**\n * What a paste sends the mirror, if anything.\n *\n * An empty clipboard sends nothing: `Input.insertText` with \"\" would be a\n * keystroke the page never saw, and there is nothing to insert anyway.\n * @param text - the clipboard text the pane read.\n * @returns the text input message, or undefined when there is nothing to paste.\n */\nexport function pasteMessage(text: string): KeyMessage {\n return text === '' ? undefined : { type: 'text', text }\n}\n","/**\n * The keyboard vocabulary the viewer channel speaks.\n *\n * A key is either a named key the page must receive as a key event (Enter, Tab,\n * the arrows, the editing keys) or a single character; modifiers may be joined\n * to either with `+`, because a page that only reacts to a keyboard shortcut\n * reacts to nothing else — a site that opens its command palette on Ctrl+K, or\n * a form that submits on Cmd+Enter, is unreachable without one. A character\n * pressed with a modifier that suppresses text is sent as a raw key with no\n * text, which is what a real keyboard sends, and a letter is pressed as the case\n * Shift gives it.\n *\n * This table is read by both halves: the host dispatches what `parseKeyStroke`\n * resolves, and the pane asks `isDispatchableKey` before it sends anything. The\n * pane keeping its own copy of the key names is exactly how a lone Ctrl came to\n * be sent as `Control+Control` — refused by the host, and shown in the pane as a\n * browser failure.\n */\n\n/** A named key the page receives as a key event rather than as inserted text. */\ninterface NamedKey {\n /** DOM `code` value. */\n readonly code: string\n /** Windows virtual key code CDP expects. */\n readonly keyCode: number\n /** Text the key produces, when it produces one. */\n readonly text?: string\n}\n\n/** Keys worth dispatching as key events; anything else printable goes through `insertText`. */\nconst NAMED_KEYS: Readonly<Record<string, NamedKey>> = {\n Enter: { code: 'Enter', keyCode: 13, text: '\\r' },\n Tab: { code: 'Tab', keyCode: 9 },\n Backspace: { code: 'Backspace', keyCode: 8 },\n Delete: { code: 'Delete', keyCode: 46 },\n Escape: { code: 'Escape', keyCode: 27 },\n ArrowLeft: { code: 'ArrowLeft', keyCode: 37 },\n ArrowUp: { code: 'ArrowUp', keyCode: 38 },\n ArrowRight: { code: 'ArrowRight', keyCode: 39 },\n ArrowDown: { code: 'ArrowDown', keyCode: 40 },\n Home: { code: 'Home', keyCode: 36 },\n End: { code: 'End', keyCode: 35 },\n PageUp: { code: 'PageUp', keyCode: 33 },\n PageDown: { code: 'PageDown', keyCode: 34 },\n}\n\n/**\n * The modifiers a chord may name, as the bit field CDP takes.\n *\n * The aliases are the ones a keyboard and the platform APIs give the same four\n * keys, so a chord written either way presses the same thing.\n */\nconst MODIFIERS: Readonly<Record<string, number>> = {\n Alt: 1,\n Option: 1,\n Control: 2,\n Ctrl: 2,\n Meta: 4,\n Command: 4,\n Cmd: 4,\n Shift: 8,\n}\n\n/** How to spell the modifiers in a message about them. */\nconst MODIFIER_NAMES = 'Control, Meta, Alt, Shift'\n\n/** Shift, as the bit field spells it. */\nconst SHIFT = 8\n\n/** Modifiers that stop the key from producing text, as a browser does. */\nconst SUPPRESSES_TEXT = 1 | 2 | 4\n\n/** One key press, resolved into what the protocol is told. */\nexport interface KeyStroke {\n /** DOM `key` value. */\n readonly key: string\n /** DOM `code` value, when the key has one. */\n readonly code: string | undefined\n /** Windows virtual key code, when the key has one. */\n readonly keyCode: number | undefined\n /** Text the press produces, absent when it produces none. */\n readonly text: string | undefined\n /** The modifiers held while it is pressed. */\n readonly modifiers: number\n}\n\n/**\n * One character pressed as a key.\n *\n * Only a letter's case follows Shift, because only a letter's case is the same\n * on every keyboard: `Shift+1` is `!` on a US layout and something else on the\n * next one, so a character that is not a letter is pressed as it is written and\n * typing it as text is the caller's job.\n * @param token - the character the chord named.\n * @param modifiers - the modifiers held.\n * @returns the resolved press.\n */\nfunction characterStroke(token: string, modifiers: number): KeyStroke {\n const letter = /^[a-z]$/iu.test(token)\n const digit = /^[0-9]$/u.test(token)\n const upper = token.toUpperCase()\n const key = letter ? ((modifiers & SHIFT) === 0 ? token.toLowerCase() : upper) : token\n const code = letter ? `Key${upper}` : digit ? `Digit${token}` : token === ' ' ? 'Space' : undefined\n const keyCode = letter ? upper.charCodeAt(0) : digit ? 48 + Number(token) : token === ' ' ? 32 : undefined\n const text = (modifiers & SUPPRESSES_TEXT) === 0 ? key : undefined\n return { key, code, keyCode, text, modifiers }\n}\n\n/**\n * Resolve one chord into the press it describes.\n *\n * @param chord - a key name, a single character, or modifiers joined to either\n * with `+`.\n * @returns what to dispatch.\n * @throws {Error} when the chord names no key, an unknown modifier, or a key\n * that has no dispatch mapping.\n */\nexport function parseKeyStroke(chord: string): KeyStroke {\n const tokens = chord.split('+')\n const named = tokens.slice(0, -1)\n const last = tokens.at(-1) ?? ''\n let modifiers = 0\n for (const token of named) {\n const bit = MODIFIERS[token]\n if (bit === undefined) {\n throw new Error(\n `dsh-browser: \"${token}\" is not a modifier; use one of ${MODIFIER_NAMES}, `\n + `joined to the key with \"+\", such as \"Control+A\"`,\n )\n }\n modifiers |= bit\n }\n if (MODIFIERS[last] !== undefined && named.length > 0) {\n throw new Error(\n `dsh-browser: \"${chord}\" names no key after its modifiers; write the key last, such as \"Control+A\"`,\n )\n }\n if (MODIFIERS[last] !== undefined) {\n throw new Error(\n `dsh-browser: \"${chord}\" is a modifier with no key after it; write the key last, such as \"Control+A\"`,\n )\n }\n if (last === '') {\n throw new Error(\n `dsh-browser: \"${chord}\" names no key after its modifiers; write the key last, such as \"Control+A\"`,\n )\n }\n const key = NAMED_KEYS[last]\n if (key !== undefined) {\n const text = (modifiers & SUPPRESSES_TEXT) === 0 ? key.text : undefined\n return { key: last, code: key.code, keyCode: key.keyCode, text, modifiers }\n }\n if ([...last].length === 1) return characterStroke(last, modifiers)\n throw new Error(\n `dsh-browser: \"${chord}\" is not a dispatchable key; send a single character, `\n + 'or text through the text message',\n )\n}\n\n/**\n * Whether the host would dispatch this chord.\n *\n * The pane asks before it sends, and it must ask here rather than of its own\n * table: a chord the host refuses comes back on the same error frame the pane\n * uses for a browser in trouble, so a pane that guesses turns an ordinary\n * keypress — a lone modifier, a key this table does not carry — into a UI\n * failure. One table, one answer.\n * @param chord - a key name, a single character, or modifiers joined to either.\n * @returns true when `parseKeyStroke` resolves it, false when it would refuse.\n */\nexport function isDispatchableKey(chord: string): boolean {\n try {\n parseKeyStroke(chord)\n return true\n } catch {\n return false\n }\n}\n","/**\n * What one keydown in the pane becomes on the wire.\n *\n * The decision lives here rather than in the canvas that listens, because it is\n * the one piece of the pane's input path with a right answer: whether a\n * keystroke is text, a chord, or nothing at all. It asks the host's own\n * vocabulary (`../browser/keys.ts`) before naming a chord, so the pane cannot\n * ask for something the host will refuse — a refusal comes back on the frame the\n * pane reserves for a browser in trouble, which is how pressing Ctrl once came\n * to look like a broken browser.\n */\nimport { isDispatchableKey } from '../browser/keys.ts'\n\n/** The parts of a DOM key event this decision reads. */\nexport interface KeyEventLike {\n /** DOM `key` value. */\n readonly key: string\n /** Whether the platform's primary modifier is held. */\n readonly ctrlKey: boolean\n /** Whether Meta (Command on macOS) is held. */\n readonly metaKey: boolean\n /** Whether Alt is held. */\n readonly altKey: boolean\n /** Whether Shift is held. */\n readonly shiftKey: boolean\n}\n\n/** What one keydown becomes on the wire, or nothing at all. */\nexport type KeyMessage =\n | { readonly type: 'text'; readonly text: string }\n | { readonly type: 'key'; readonly key: string }\n | undefined\n\n/**\n * The modifiers a key event is holding, spelled the way a chord writes them.\n * @param event - the key event.\n * @returns the modifier names, in the order a chord writes them.\n */\nfunction modifiersOf(event: KeyEventLike): string[] {\n const held: string[] = []\n if (event.ctrlKey) held.push('Control')\n if (event.metaKey) held.push('Meta')\n if (event.altKey) held.push('Alt')\n if (event.shiftKey) held.push('Shift')\n return held\n}\n\n/**\n * Resolve one keydown into the message the viewer channel carries.\n *\n * AltGr arrives as Control+Alt on Windows and still produces a character — a\n * German or Polish layout types its symbols that way — so it goes as text;\n * sending it as a chord would swallow the character the user meant to type. A\n * chord is a keystroke, not text: sending Ctrl+A as the character \"a\" is what\n * this path used to do, and a page that selects everything on Ctrl+A had an \"a\"\n * typed into it instead. And a key that is only a modifier resolves to nothing,\n * because a chord writes its key last: pressing Ctrl starts a chord rather than\n * being one.\n *\n * @param event - the keydown to resolve.\n * @returns the text or chord to send, or undefined when this key is nothing the\n * page can be sent.\n */\nexport function keyMessage(event: KeyEventLike): KeyMessage {\n const altGraph = event.ctrlKey && event.altKey && event.key.length === 1\n if (event.key.length === 1 && (!(event.ctrlKey || event.metaKey || event.altKey) || altGraph)) {\n return { type: 'text', text: event.key }\n }\n const chord = [...modifiersOf(event), event.key].join('+')\n return isDispatchableKey(chord) ? { type: 'key', key: chord } : undefined\n}\n","/**\n * What the client half knows about a browser's pages, and the one rule that\n * keeps the Sidebar's tabs matched to them.\n *\n * A page's stable identity is its CDP target id — the same string external\n * DevTools sees — and a tab's address is built from it, so a tab keeps naming\n * its page across navigations, active-page changes, and client reloads. The\n * sync loop reads the host's report and reconciles: one tab per page the\n * browser holds, no tab for a page it does not.\n */\n\n/** The address scheme a browser page's tab is recorded under. */\nexport const PAGE_PREFIX = 'dsh-resource://dsh-browser-page/'\n\n/**\n * The address a page's tab carries.\n * @param targetId - the page's CDP target id.\n * @returns the address to open the tab with.\n */\nexport function pageAddressOf(targetId: string): string {\n return PAGE_PREFIX + encodeURIComponent(targetId)\n}\n\n/**\n * The page a tab address names.\n * @param address - the tab's content id.\n * @returns the page's CDP target id, or `undefined` for an address that is not a page's.\n */\nexport function targetIdOf(address: string): string | undefined {\n if (!address.startsWith(PAGE_PREFIX)) return undefined\n const rest = address.slice(PAGE_PREFIX.length)\n if (rest === '') return undefined\n try {\n return decodeURIComponent(rest)\n } catch {\n // A malformed tail is still an attempt at a name; hand it back as it came.\n return rest\n }\n}\n\n/** One tab the Sidebar already shows, as the reconciliation reads it. */\nexport interface ActualTab {\n /** The tab's own id, for closing it. */\n readonly id: string\n /** What the tab names: a page address, or a legacy page-type address. */\n readonly contentId: string\n}\n\n/**\n * The one rule: a tab for every page the browser holds, and none for a page it\n * does not.\n *\n * Tabs that name a page are closed the moment their page is gone. A tab that\n * names no page — the guide entry's, which exists to start a browser nobody is\n * watching yet — is kept until there are page tabs to hand the story to, so\n * picking the entry still opens a pane while the browser is starting.\n * @param desired - the page addresses the browser holds, in tab order.\n * @param actual - the tabs of this kind the Sidebar already shows.\n * @returns what to open and what to close.\n */\nexport function reconcile(\n desired: readonly string[],\n actual: readonly ActualTab[],\n): { open: string[]; close: string[] } {\n const held = new Set(actual.map(tab => tab.contentId))\n const open: string[] = []\n for (const address of desired) {\n if (!held.has(address)) open.push(address)\n }\n const wanted = new Set(desired)\n const close: string[] = []\n for (const tab of actual) {\n const isPageTab = targetIdOf(tab.contentId) !== undefined\n if (isPageTab && !wanted.has(tab.contentId)) close.push(tab.id)\n // The un-named tab hands over once page tabs exist, and not before: while\n // the browser is starting it is the only pane there is.\n if (!isPageTab && desired.length > 0) close.push(tab.id)\n }\n return { open, close }\n}\n\n/**\n * Pages a close was asked for, so the loop does not open a tab for a page that\n * is on its way out.\n *\n * Closing a tab closes its page, and the host's report is a snapshot: for a\n * moment after the ask, the page is still listed. Without this the loop would\n * read that moment as \"a page with no tab\" and put the tab back.\n */\nconst closing = new Map<string, number>()\n\n/** How long a close ask stands, when the report never stops listing the page. */\nconst CLOSING_TIMEOUT_MS = 15_000\n\n/**\n * Record that a page's close was asked for.\n * @param sessionId - the session whose browser holds the page.\n * @param targetId - the page's CDP target id.\n * @param at - when the ask was made; defaults to now (tests hand a fixed one).\n */\nexport function markClosing(sessionId: string, targetId: string, at: number = Date.now()): void {\n closing.set(`${sessionId}\\n${targetId}`, at)\n}\n\n/**\n * Whether a page's close was asked for and is not yet known to have happened.\n * @param sessionId - the session whose browser holds the page.\n * @param targetId - the page's CDP target id.\n */\nexport function isClosing(sessionId: string, targetId: string): boolean {\n return closing.has(`${sessionId}\\n${targetId}`)\n}\n\n/**\n * Drop the asks that have answered.\n *\n * A page the report no longer lists is one the close reached; the rest expire,\n * which is what brings a tab back when a close could not be carried out — the\n * page is still there, so a tab for it is the honest picture.\n * @param listed - every page still reported, as `sessionId` and target id pairs.\n * @param now - the time to expire against.\n */\nexport function settleClosing(listed: Iterable<readonly [string, string]>, now: number): void {\n const stillThere = new Set([...listed].map(([sessionId, targetId]) => `${sessionId}\\n${targetId}`))\n for (const [key, at] of [...closing]) {\n if (!stillThere.has(key) || now - at > CLOSING_TIMEOUT_MS) closing.delete(key)\n }\n}\n\n/** What the host said about one page, as the tab chip and the loop read it. */\nexport interface PageFacts {\n /** What the page says its title is. */\n readonly title: string\n /** The address the page is on. */\n readonly url: string\n}\n\n/** Page facts by tab address; replaced wholesale only when something changed. */\nlet facts: ReadonlyMap<string, PageFacts> = new Map()\nconst listeners = new Set<() => void>()\n\n/**\n * Replace the page facts the report produced.\n *\n * Unchanged entries keep their object, so a reader that compares references\n * (React's snapshot contract) sees no change when nothing changed — a poll\n * that learned nothing must not re-render a chip.\n * @param entries - one address and its facts per page the host reported.\n */\nexport function publishPageFacts(entries: Iterable<readonly [string, PageFacts]>): void {\n const next = new Map<string, PageFacts>()\n let changed = false\n for (const [address, page] of entries) {\n const previous = facts.get(address)\n const same = previous !== undefined && previous.title === page.title && previous.url === page.url\n if (!same) changed = true\n next.set(address, same ? previous : page)\n }\n // A page the report stopped listing is a change the loop above cannot see.\n if (next.size !== facts.size) changed = true\n if (!changed) return\n facts = next\n for (const listener of [...listeners]) listener()\n}\n\n/**\n * The facts of one page's tab.\n * @param address - the tab's content id.\n * @returns the facts, or `undefined` before the first report named that page.\n */\nexport function pageFactsOf(address: string): PageFacts | undefined {\n return facts.get(address)\n}\n\n/**\n * Observe page facts.\n * @param listener - called when a fact changed.\n * @returns unsubscribe callback.\n */\nexport function subscribePageFacts(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n","/**\n * Where the viewer socket dials.\n *\n * The mirror is a WebSocket to the plugin's own host half, so what it needs is\n * the Host's origin rather than the document's. A Web page is served by that\n * Host and the two agree; a Desktop window is a `dsh-app://app` document, whose\n * `location.host` is the scheme's own `app` and reaches no Host at all. The shell\n * publishes the Host's HTTP origin as `__DSH_TRANSPORT__.streamBaseUrl`, and\n * Desktop attaches the Host's authentication cookie to a `ws://127.0.0.1/*`\n * handshake from that window — the seam the Gateway's own mux socket uses.\n *\n * HTTP stays relative: the Desktop scheme forwards it to the Host with the\n * cookie already, and an absolute cross-origin call would meet the trust fence.\n */\n\n/** The transport descriptor the shell publishes for the page it serves. */\ninterface ClientTransport {\n readonly streamBaseUrl?: string\n}\n\n/**\n * Origin of the Host this page belongs to.\n * @returns the shell-published origin, or the document's own when it names none.\n */\nexport function hostOrigin(): string {\n const transport = globalThis as { __DSH_TRANSPORT__?: ClientTransport }\n const base = transport.__DSH_TRANSPORT__?.streamBaseUrl\n return base === undefined || base === '' ? location.origin : base\n}\n\n/**\n * Absolute address of one of the Host's WebSocket routes.\n * @param path - Host-absolute path, e.g. `/dsh-browser/stream`.\n * @param query - parameters to carry in the handshake.\n * @returns the `ws:`/`wss:` address to dial.\n */\nexport function streamUrl(path: string, query: Readonly<Record<string, string>>): string {\n const url = new URL(path, hostOrigin())\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'\n for (const [name, value] of Object.entries(query)) url.searchParams.set(name, value)\n return url.href\n}\n","/**\n * Copy for the viewer, in both shipped languages.\n *\n * Client UI text is locale-owned, so every string the viewer renders comes from\n * here rather than from the component body.\n */\n\n/** This plugin's copy namespace. */\nexport const NS = 'dshBrowser'\n\n/** English dictionary; the key set is the contract. */\nexport const en = {\n title: 'Browser',\n guideTitle: 'Browser',\n guideDescription: 'Mirror a real local browser, drive it, and attach DevTools to the same port.',\n connecting: 'Connecting to the mirror…',\n idle: 'The browser is not running yet.',\n starting: 'Starting the browser…',\n failed: 'The browser could not start.',\n closed: 'The browser has stopped.',\n address: 'Address',\n secure: 'Secure connection (https)',\n insecure: 'Not secure (plain http)',\n go: 'Go',\n reload: 'Reload',\n restart: 'Restart browser',\n endpoint: 'CDP',\n waiting: 'Frames appear here as the page changes.',\n settingsTitle: 'Browser',\n settingsIntro: 'The local browser this plugin mirrors into the Sidebar and drives for the agent. Every conversation gets its own browser, profile, and CDP port, except in \"Whole workspace\" mode, where the conversations of one project share a process. Changing a value here restarts all of them, and the panes reconnect on their own.',\n settingsAdvanced: 'Advanced',\n settingsAdvancedHint: 'The browser works without changing any of these.',\n settingsAdvancedOverridden: '{count} changed',\n settingsGroupProcess: 'Browser process',\n settingsGroupProcessHint: 'Which binary opens, which ports it may listen on, and whether the automation markers stay hidden.',\n settingsGroupMirror: 'Mirror and page size',\n settingsGroupMirrorHint: 'Headless page size, and the JPEG quality the Sidebar shows.',\n settingsGroupSnapshot: 'Agent snapshot',\n settingsGroupSnapshotHint: 'What a snapshot prints beside each node\\'s role and name.',\n settingsMode: 'Window mode',\n settingsHeadless: 'Headless (no window)',\n settingsHeadful: 'With a window',\n settingsHeadlessNote: 'Headless survives being covered or minimized, but sites that check for automation can tell it apart from a browser you opened yourself.',\n settingsProfileDir: 'Profile directory',\n settingsProfileMode: 'Sign-ins',\n settingsProfileModeHint: 'Who shares a sign-in, and whether it outlives the browser. \"Temporary\" keeps nothing; \"This conversation\" gives each conversation its own sign-ins, still there after a restart; \"Whole workspace\" gives every conversation in one project the same ones, so signing in once carries over — at the cost that a conversation can read another conversation\\'s sign-in for the same sites.',\n settingsProfileModeTemp: 'Temporary (deleted on close)',\n settingsProfileModeSession: 'This conversation (kept)',\n settingsProfileModeWorkspace: 'Whole workspace (shared sign-in)',\n settingsProfileDirHintSession: 'Empty puts them under the temporary directory, one subdirectory per conversation. This is a parent directory: each conversation gets its own subdirectory here, so a sign-in in one is not visible in another. It must be a full path; a directory another running browser already holds fails to open.',\n settingsProfileDirHintWorkspace: 'Empty puts them in the project\\'s own .dsh\\\\browser, so the profile follows the project. This is a parent directory: each workspace gets one subdirectory under it, shared by every conversation of that workspace. It must be a full path; naming one takes the profile out of the project.',\n settingsProfileDirPlaceholderSession: 'Empty = one per conversation, under the temp directory',\n settingsProfileDirPlaceholderWorkspace: 'Empty = .dsh\\\\browser inside the project',\n settingsBrowser: 'Browser',\n settingsChannelChrome: 'Google Chrome',\n settingsChannelEdge: 'Microsoft Edge',\n settingsExecutable: 'Executable path (overrides the browser choice)',\n settingsWidth: 'Width',\n settingsHeight: 'Height',\n settingsQuality: 'Mirror quality (JPEG 1-100)',\n settingsDebugPortMin: 'CDP port range — from',\n settingsDebugPortMax: 'CDP port range — to',\n settingsDebugPortHint: 'Each browser needs its own port, so this is the window those ports come from: the lowest free one goes first, and a browser that finds nothing free inside the window fails rather than listening outside it. One browser per conversation, or one per workspace in \"Whole workspace\" mode.',\n settingsSnapshotAttributes: 'Properties printed per node',\n settingsSnapshotAttributesHint: 'Comma-separated accessibility properties the snapshot prints beside a role and a name — url, level, placeholder, and so on. The default list is what changes what the agent can do; empty prints none.',\n settingsSnapshotIgnore: 'Selectors no snapshot prints',\n settingsSnapshotIgnoreHint: 'Comma-separated CSS selectors whose elements, and everything inside them, are left out of a snapshot. A page can exempt itself with data-dsh-browser-ignore; add more here, for example nav, footer.',\n settingsUnavailable: 'Settings are unavailable in this deployment; the composed configuration is in force.',\n settingsLoading: 'Loading settings…',\n settingsReset: 'Reset',\n settingsApplied: 'Launch settings restart the browser; snapshot settings apply to the next snapshot.',\n settingsStealth: 'Automation markers',\n settingsStealthOn: 'Hidden (recommended)',\n settingsStealthOff: 'Left visible',\n settingsStealthNote: 'Playwright-started browsers report navigator.webdriver and, headless, spell their user agent HeadlessChrome. Sites that check for automation block those: with the markers left visible Google served its \"unusual traffic\" page three times out of three, and none with them hidden.',\n settingsStatusReading: 'Reading the browser state…',\n settingsStatusRunning: '{count} session browser(s) running',\n settingsStatusStarting: '{count} session browser(s) starting',\n settingsStatusNone: 'No session browser is running — one starts when a conversation opens it.',\n settingsStatusUnknown: 'The browser state could not be read: {error}',\n settingsSessionsCount: 'Sessions with a browser ({count})',\n settingsSessionUnnamed: 'Unnamed session',\n settingsSessionClose: 'Close browser',\n settingsSessionClosing: 'Closing…',\n settingsSessionCloseFailed: 'Could not close: {error}',\n settingsInstanceStateRunning: 'running',\n settingsInstanceStateStarting: 'starting',\n settingsInstanceStateStopped: 'stopped',\n settingsInstanceStateFailed: 'failed',\n settingsInstancePages: '{count} page(s)',\n settingsInstanceShared: 'shares a browser with {count} conversations',\n settingsRefresh: 'Refresh',\n settingsSaved: 'Saved. The browser restarts when a launch setting changed.',\n settingsSaveFailed: 'Could not save: {error}',\n}\n\n/** Chinese dictionary. */\nexport const zh: typeof en = {\n title: '浏览器',\n guideTitle: '浏览器',\n guideDescription: '镜像本机真实浏览器,可操控,也可用同一端口附加 DevTools。',\n connecting: '正在连接镜像…',\n idle: '浏览器尚未启动。',\n starting: '正在启动浏览器…',\n failed: '浏览器启动失败。',\n closed: '浏览器已停止。',\n address: '地址',\n secure: '安全连接(https)',\n insecure: '不安全(明文 http)',\n go: '前往',\n reload: '刷新',\n restart: '重启浏览器',\n endpoint: 'CDP',\n waiting: '页面发生变化时,画面会出现在这里。',\n settingsTitle: '浏览器',\n settingsIntro: '本插件镜像到侧栏、并由 agent 驱动的本机浏览器。每个对话一个浏览器、自己的 profile 和 CDP 端口;「整个工作区」模式下同一项目的对话共用一个进程。在此改动会重启全部浏览器,侧栏里的画面会自行重连。',\n settingsAdvanced: '高级设置',\n settingsAdvancedHint: '不改这里也能正常使用。',\n settingsAdvancedOverridden: '{count} 项已自定义',\n settingsGroupProcess: '浏览器进程',\n settingsGroupProcessHint: '用哪个可执行文件、在哪个端口范围里监听、是否继续隐藏自动化标记。',\n settingsGroupMirror: '镜像与页面尺寸',\n settingsGroupMirrorHint: '无头下的页面尺寸,以及侧栏镜像的 JPEG 画质。',\n settingsGroupSnapshot: 'Agent 快照',\n settingsGroupSnapshotHint: '快照在每个节点的 role 与 name 之外还打印什么。',\n settingsMode: '窗口模式',\n settingsHeadless: '无头(无窗口)',\n settingsHeadful: '带窗口',\n settingsHeadlessNote: '无头在窗口被遮挡或最小化时不受影响,但会做自动化检测的站点能把它和你自己打开的浏览器区分开。',\n settingsProfileDir: 'Profile 目录',\n settingsProfileMode: '登录态',\n settingsProfileModeHint: '谁共享登录态,以及关掉之后还留不留。「临时」什么都不保留;「本对话」让每个对话各有各的登录态,重启后还在;「整个工作区」让同一项目的所有对话共用一份,于是一处登录处处可用——代价是同一工作区里的对话能读到彼此的登录态。',\n settingsProfileModeTemp: '临时(关闭即删)',\n settingsProfileModeSession: '本对话(关闭后保留)',\n settingsProfileModeWorkspace: '整个工作区(共享登录态)',\n settingsProfileDirHintSession: '留空则放在临时目录下、每个对话一个子目录。这里填的是父目录:每个对话在它下面各有各的子目录,因此一个对话里的登录态不会出现在另一个对话里。必须是完整路径;若该目录已被另一个运行中的浏览器占用,启动会失败。',\n settingsProfileDirHintWorkspace: '留空则放在项目自己的 .dsh\\\\browser 下,profile 跟着项目走。这里填的是父目录:每个工作区在它下面有一个子目录,同一工作区的全部对话共用它。必须是完整路径;填写后不再落在项目里。',\n settingsProfileDirPlaceholderSession: '留空 = 临时目录下的每对话一份',\n settingsProfileDirPlaceholderWorkspace: '留空 = 项目内的 .dsh\\\\browser',\n settingsBrowser: '浏览器',\n settingsChannelChrome: 'Google Chrome',\n settingsChannelEdge: 'Microsoft Edge',\n settingsExecutable: '可执行文件路径(覆盖浏览器选择)',\n settingsWidth: '宽度',\n settingsHeight: '高度',\n settingsQuality: '镜像画质(JPEG 1-100)',\n settingsDebugPortMin: 'CDP 端口范围(起)',\n settingsDebugPortMax: 'CDP 端口范围(止)',\n settingsDebugPortHint: '每台浏览器各占一个端口,所以这里给的是一个范围:从最小的空闲端口开始分配;范围里没有空端口时直接报错,而不会跑到范围外去监听。浏览器按对话划分,「整个工作区」模式下则按工作区。',\n settingsSnapshotAttributes: '每个节点打印的属性',\n settingsSnapshotAttributesHint: '快照在 role 与 name 之外打印的无障碍属性,用逗号分隔——url、level、placeholder 等。默认这一组决定了 agent 能做什么;留空则一个都不打印。',\n settingsSnapshotIgnore: '快照不打印的选择器',\n settingsSnapshotIgnoreHint: '这些 CSS 选择器匹配到的元素及其内部内容不会进入快照,用逗号分隔。页面自己可以用 data-dsh-browser-ignore 豁免;需要更多就在这里补,例如 nav, footer。',\n settingsUnavailable: '当前部署没有设置服务,使用组合配置。',\n settingsLoading: '正在载入设置…',\n settingsReset: '重置',\n settingsApplied: '启动项会重启浏览器;快照相关设置从下一次快照起生效。',\n settingsStealth: '自动化标记',\n settingsStealthOn: '隐藏(推荐)',\n settingsStealthOff: '保留',\n settingsStealthNote: 'Playwright 启动的浏览器会报告 navigator.webdriver,无头时 UA 还写成 HeadlessChrome。做自动化检测的站点会因此拦截:保留标记时 Google 连续三次搜索全部被拦,隐藏后三次全部通过。',\n settingsStatusReading: '正在读取浏览器状态…',\n settingsStatusRunning: '{count} 个会话浏览器在运行',\n settingsStatusStarting: '{count} 个会话浏览器正在启动',\n settingsStatusNone: '当前没有会话浏览器在运行——某个对话用到时才会启动。',\n settingsStatusUnknown: '读不到浏览器状态:{error}',\n settingsSessionsCount: '有浏览器的会话({count})',\n settingsSessionUnnamed: '未命名会话',\n settingsSessionClose: '关闭浏览器',\n settingsSessionClosing: '正在关闭…',\n settingsSessionCloseFailed: '关闭失败:{error}',\n settingsInstanceStateRunning: '运行中',\n settingsInstanceStateStarting: '启动中',\n settingsInstanceStateStopped: '已停止',\n settingsInstanceStateFailed: '启动失败',\n settingsInstancePages: '{count} 个页面',\n settingsInstanceShared: '与 {count} 个对话共用这个浏览器',\n settingsRefresh: '刷新',\n settingsSaved: '已保存。改的是启动项时会重启浏览器。',\n settingsSaveFailed: '保存失败:{error}',\n}\n\n/** One copy key of this namespace. */\nexport type DshBrowserKey = keyof typeof en\n","/**\n * The mirror's viewer: a canvas fed by the plugin's WebSocket, and the few\n * controls that act on the browser behind it.\n *\n * Input leaves as fractions of the frame rather than pixels, so the host never\n * needs to know the viewer's size and resizing the Sidebar cannot shift a\n * click. Which message a keystroke becomes — text, a chord, or nothing — is\n * decided in `keys.ts`, against the same key table the host dispatches from.\n *\n * Styles are inline: this plugin builds its client bundle outside the\n * repository's stylesheet pipeline, so a CSS import would have no owner.\n */\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type ReactElement, type ReactNode } from 'react'\nimport type { SidebarRightTabInfo, UseSidebarRightTabInfo } from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport { openPageRequest, restartRoute } from './api.ts'\nimport { clipboardChord, clipboardReply, pasteMessage, type ReadingChord } from './clipboard.ts'\nimport { AddressGlobe } from './glyph.ts'\nimport { keyMessage } from './keys.ts'\nimport { targetIdOf } from './pages.ts'\nimport { streamUrl } from './stream-url.ts'\nimport { en, type DshBrowserKey } from './locales.ts'\n\n/** Absolute path the host serves the mirror on. */\nconst STREAM_PATH = '/dsh-browser/stream'\n\n/** Milliseconds between forwarded pointer moves, so a drag does not flood the socket. */\nconst MOVE_INTERVAL_MS = 33\n\n/** What the viewer knows about the browser it mirrors. */\ninterface MirrorStatus {\n readonly state: 'idle' | 'starting' | 'ready' | 'failed' | 'closed'\n readonly debugPort?: number | undefined\n readonly url?: string | undefined\n readonly error?: string | undefined\n}\n\n/** Composed slot props for this body. */\nexport interface BrowserBodyProps {\n /**\n * The session this pane belongs to.\n *\n * It is what the viewer names on the socket, and therefore the only thing\n * that decides which browser is mirrored here: two conversations open the\n * same pane and see two different browsers.\n */\n readonly sessionId: string\n /** Copy for this namespace; without it the built-in English dictionary is used. */\n readonly t?: (key: DshBrowserKey) => string\n /**\n * The tab this body is mounted in.\n *\n * Supplied by the Sidebar's own seat alongside whatever `inject` names. A tab\n * names the page it mirrors — its address is built from the page's CDP target\n * id — and that is what decides which page is mirrored here and driven from\n * here: one tab, one page. A tab that names no page (the guide entry's, while\n * the browser is starting) mirrors the page the tools act on.\n */\n readonly useTabInfo?: UseSidebarRightTabInfo | undefined\n}\n\n/** Translate one key, falling back to the bundled dictionary at this dynamic boundary. */\nfunction copyOf(t: BrowserBodyProps['t']): (key: DshBrowserKey) => string {\n return key => t === undefined ? en[key] : t(key)\n}\n\n/**\n * Where a pointer event landed, as a fraction of the frame.\n *\n * The canvas fits the frame into its box with `object-fit: contain`, so the\n * drawn image is smaller than the box on one axis and centred in it. Measuring\n * against the box would offset every click wherever the two aspect ratios\n * differ, which is exactly the case for a wide page in a narrow Sidebar.\n * @param element - the canvas the event arrived on.\n * @param clientX - pointer x in viewport coordinates.\n * @param clientY - pointer y in viewport coordinates.\n * @returns the position in `0..1` of the frame, clamped to the frame.\n */\nfunction fractionOf(element: HTMLCanvasElement, clientX: number, clientY: number): { x: number; y: number } {\n const rect = element.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0 || element.width === 0 || element.height === 0) return { x: 0, y: 0 }\n const scale = Math.min(rect.width / element.width, rect.height / element.height)\n const drawnWidth = element.width * scale\n const drawnHeight = element.height * scale\n const x = (clientX - rect.left - (rect.width - drawnWidth) / 2) / drawnWidth\n const y = (clientY - rect.top - (rect.height - drawnHeight) / 2) / drawnHeight\n return { x: Math.min(1, Math.max(0, x)), y: Math.min(1, Math.max(0, y)) }\n}\n\n/**\n * Name a DOM button number the way the host's input messages spell it.\n * @param button - the event's button number.\n * @returns the matching button name.\n */\nfunction buttonOf(button: number): 'left' | 'middle' | 'right' {\n if (button === 1) return 'middle'\n if (button === 2) return 'right'\n return 'left'\n}\n\n/**\n * Put text on the system clipboard from the page the user is looking at.\n *\n * The async API is the first choice; a browser that refuses it outside its own\n * idea of a gesture still honours the older hidden-textarea route, which is the\n * fallback VS Code's web clipboard keeps as well. Focus is restored either way:\n * the temporary field is a document-wide side effect, and the user's next\n * keystroke belongs wherever it was going.\n * @param text - the text to place on the clipboard.\n */\nasync function writeClipboard(text: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(text)\n return\n } catch {\n // The async API is refused here; the route below is why this function exists.\n }\n const previous = document.activeElement\n const field = document.createElement('textarea')\n field.setAttribute('aria-hidden', 'true')\n field.style.cssText = 'position:absolute;left:-9999px;top:0;width:1px;height:1px'\n field.value = text\n document.body.appendChild(field)\n field.select()\n try {\n document.execCommand('copy')\n } finally {\n field.remove()\n if (previous instanceof HTMLElement) previous.focus()\n }\n}\n\n/** Height of the address bar, which its pill radius is derived from. */\nconst OMNIBOX_HEIGHT = 30\n\n/**\n * The design tokens the address bar borrows, so it matches the app in either\n * theme. `--dsw-alias-*` are the theme's own aliases: a layer for the field, a\n * border for its edge, and label colours for the text and the icons.\n */\nconst style: Readonly<Record<string, CSSProperties>> = {\n // Both of these state a height and then add padding or a border to it, so they\n // size to the border box: without that the pane is 12px taller than the box it\n // is given and cuts off its own last row, and the address bar is 2px taller\n // than the radius drawn on it.\n root: {\n boxSizing: 'border-box', display: 'flex', flexDirection: 'column',\n height: '100%', minHeight: 0, gap: '6px', padding: '6px',\n },\n omnibox: {\n boxSizing: 'border-box', display: 'flex', alignItems: 'center', gap: '2px', flex: '0 0 auto',\n height: `${String(OMNIBOX_HEIGHT)}px`, padding: '0 3px 0 9px',\n borderRadius: `${String(OMNIBOX_HEIGHT / 2)}px`,\n background: 'var(--dsw-alias-bg-layer-3)',\n border: '0.5px solid var(--dsw-alias-border-l4)',\n },\n omniboxFocused: { borderColor: 'var(--dsw-alias-state-business-primary)' },\n scheme: { display: 'flex', alignItems: 'center', flex: '0 0 auto', color: 'var(--dsw-alias-label-tertiary)' },\n schemeInsecure: { color: 'var(--dsw-alias-state-warn-label)' },\n address: {\n flex: '1 1 auto', minWidth: 0, height: '100%', padding: '0 4px',\n border: 'none', outline: 'none', background: 'transparent',\n color: 'var(--dsw-alias-label-primary)', font: 'inherit', fontSize: '12px',\n },\n iconButton: {\n display: 'flex', alignItems: 'center', justifyContent: 'center', flex: '0 0 auto',\n width: `${String(OMNIBOX_HEIGHT - 6)}px`, height: `${String(OMNIBOX_HEIGHT - 6)}px`,\n padding: 0, border: 'none', borderRadius: '50%', cursor: 'pointer',\n background: 'transparent', color: 'var(--dsw-alias-label-secondary)',\n },\n iconButtonHovered: {\n background: 'var(--dsw-alias-interactive-bg-hover)',\n color: 'var(--dsw-alias-label-primary)',\n },\n stage: { position: 'relative', flex: '1 1 auto', minHeight: 0, overflow: 'hidden', background: '#101418' },\n canvas: { display: 'block', width: '100%', height: '100%', objectFit: 'contain', outline: 'none' },\n // The paste sink stays in the document but out of the way: a browser pastes\n // into whatever is focused, and for the length of a Ctrl+V this is it.\n pasteSink: {\n position: 'absolute', left: '-9999px', top: 0, width: '1px', height: '1px',\n padding: 0, border: 'none', opacity: 0, resize: 'none',\n },\n note: {\n position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', gap: '8px',\n alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: '12px',\n fontSize: '12px', color: '#93a1b0', pointerEvents: 'none',\n },\n // The canvas is the page, so the overlay sits on whatever colour that page\n // happens to be: without its own background it disappears on a white one.\n noteCard: {\n display: 'flex', flexDirection: 'column', gap: '8px', alignItems: 'center',\n maxWidth: '90%', padding: '10px 14px', borderRadius: '8px',\n background: 'rgba(16, 20, 24, 0.88)', color: '#d7dee6', lineHeight: 1.5,\n },\n // The overlay ignores pointer events so a click reaches the page beneath it;\n // its button has to opt back in.\n noteButton: {\n padding: '4px 8px', fontSize: '12px', cursor: 'pointer', pointerEvents: 'auto',\n background: 'var(--dsw-alias-button-primary-fill)', color: 'var(--dsw-alias-label-primary-foreground)',\n border: 'none', borderRadius: 'var(--dsw-radius-sm)',\n },\n status: {\n flex: '0 0 auto', display: 'flex', gap: '6px', alignItems: 'center',\n fontSize: '11px', color: 'var(--dsw-alias-label-tertiary)',\n },\n statusText: { flex: '1 1 auto', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n statusButton: {\n flex: '0 0 auto', padding: '2px 8px', fontSize: '11px', cursor: 'pointer', borderRadius: 'var(--dsw-radius-sm)',\n border: '0.5px solid var(--dsw-alias-border-l2)', background: 'transparent',\n color: 'var(--dsw-alias-label-secondary)',\n },\n}\n\n/** The lock a secure address shows, drawn at the size the bar's icons share. */\nconst LOCK_ICON = (\n <>\n <rect x=\"3.5\" y=\"7\" width=\"9\" height=\"6.5\" rx=\"2\" />\n <path d=\"M5.75 7V5.25a2.25 2.25 0 0 1 4.5 0V7\" />\n </>\n)\n\n/** The circular arrow a page that needs no navigation shows. */\nconst RELOAD_ICON = (\n <>\n <path d=\"M12.75 8a4.75 4.75 0 1 1-1.4-3.36\" />\n <path d=\"M12.9 2.6v3.1h-3.1\" />\n </>\n)\n\n/** The arrow an edited address shows, which submits it. */\nconst GO_ICON = (\n <>\n <path d=\"M3 8h9.5\" />\n <path d=\"M9 4.5 12.5 8 9 11.5\" />\n </>\n)\n\n/**\n * One icon control inside the address bar.\n *\n * Hover is tracked in state rather than by a stylesheet: this plugin's client\n * bundle has no stylesheet of its own, so every appearance here is an inline\n * style, and a pseudo-class is not one.\n * @param props - the icon, its accessible name, and what it does.\n * @returns the button.\n */\nfunction IconButton(props: {\n readonly label: string\n readonly icon: ReactNode\n readonly onClick: () => void\n readonly submit?: boolean\n}): ReactElement {\n const [hovered, setHovered] = useState(false)\n return (\n <button\n type={props.submit === true ? 'submit' : 'button'}\n aria-label={props.label}\n title={props.label}\n onClick={props.onClick}\n onMouseEnter={() => { setHovered(true) }}\n onMouseLeave={() => { setHovered(false) }}\n style={{ ...style.iconButton, ...hovered ? style.iconButtonHovered : {} }}\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.4\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {props.icon}\n </svg>\n </button>\n )\n}\n\n/**\n * The mirror's body: canvas, address bar, and the browser's own status.\n * @param props - composed slot props.\n * @returns the viewer.\n */\nexport function BrowserBody({ sessionId, t, useTabInfo }: BrowserBodyProps): ReactNode {\n const copy = copyOf(t)\n // Read once per render, the way a hook is read: it is what says which page\n // this pane is on.\n const tab: SidebarRightTabInfo | undefined = useTabInfo?.()\n const pageId = tab === undefined ? undefined : targetIdOf(tab.tab.contentId)\n /**\n * The tab's own id, which is what a pane with no page asks under: the ask is\n * the tab's, so the same tab asking again — remounted by a tab switch, a\n * Session change, or a client reload — opens nothing new.\n */\n const tabId = tab === undefined ? undefined : tab.tab.id\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const socketRef = useRef<WebSocket | undefined>(undefined)\n const lastMoveRef = useRef(0)\n const pasteSinkRef = useRef<HTMLTextAreaElement | null>(null)\n /**\n * What to do with the selection the mirror is about to report.\n *\n * The copy is armed here, on the keystroke, and spent when the answer arrives:\n * the answer is a round trip away, and the chord is what says whether there is\n * anything to write at all.\n */\n const clipboardRef = useRef<((text: string) => void) | undefined>(undefined)\n const [address, setAddress] = useState('')\n const [status, setStatus] = useState<MirrorStatus | undefined>(undefined)\n const [connected, setConnected] = useState(false)\n const [painted, setPainted] = useState(false)\n const [failure, setFailure] = useState<string | undefined>(undefined)\n const [focused, setFocused] = useState(false)\n\n /**\n * Draw one received frame.\n * @param buffer - the JPEG bytes of the frame.\n */\n const drawFrame = useCallback(async (buffer: ArrayBuffer): Promise<void> => {\n const canvas = canvasRef.current\n if (canvas === null) return\n const bitmap = await createImageBitmap(new Blob([buffer], { type: 'image/jpeg' }))\n if (canvas.width !== bitmap.width) canvas.width = bitmap.width\n if (canvas.height !== bitmap.height) canvas.height = bitmap.height\n canvas.getContext('2d')?.drawImage(bitmap, 0, 0)\n bitmap.close()\n setPainted(true)\n }, [])\n\n useEffect(() => {\n const query: Record<string, string> = { session: sessionId }\n // A tab names its page; the socket is bound to that page, so its frames,\n // its input, and its address bar are the page's whether or not the tools\n // act on it right now.\n if (pageId !== undefined) query.page = pageId\n let cancelled = false\n let socket: WebSocket | undefined\n // A pane with no page is the browser entry's own: it exists to hold the page\n // this ask produces, so it asks for one. This is the only thing that opens a\n // page on the user's behalf — a browser already running gets a tab of its\n // own here rather than the one that is already there.\n //\n // The socket opens after the ask answers, not beside it. Watching never\n // starts a browser — that is what makes closing one from the settings page\n // stick — so a socket opened first would find nothing to watch for exactly\n // as long as the browser takes to start, and draw a failure over it.\n const asking = pageId !== undefined || tabId === undefined\n ? Promise.resolve()\n : openPageRequest(sessionId, tabId).then(() => undefined, () => undefined)\n void asking.then(() => {\n if (cancelled) return\n const opened = new WebSocket(streamUrl(STREAM_PATH, query))\n socket = opened\n socketRef.current = opened\n opened.binaryType = 'arraybuffer'\n opened.onopen = () => { setConnected(true) }\n opened.onclose = () => { setConnected(false) }\n opened.onerror = () => { setConnected(false) }\n opened.onmessage = (event: MessageEvent<string | ArrayBuffer>) => {\n if (typeof event.data !== 'string') {\n void drawFrame(event.data)\n return\n }\n const parsed = JSON.parse(event.data) as { type?: string; status?: MirrorStatus; message?: unknown; text?: unknown }\n if (parsed.type === 'status' && parsed.status !== undefined) {\n setStatus(parsed.status)\n setFailure(undefined)\n return\n }\n // A refused action is otherwise invisible: the status the viewer holds is\n // unchanged, so the failure is what explains why nothing moved.\n if (parsed.type === 'error') setFailure(String(parsed.message))\n // The mirror's selection, on its way back from the host. It is spent at\n // once and can only be spent once: it belongs to the chord that asked.\n if (parsed.type === 'clipboard') {\n const settle = clipboardRef.current\n clipboardRef.current = undefined\n settle?.(String(parsed.text ?? ''))\n }\n }\n })\n return () => {\n cancelled = true\n socketRef.current = undefined\n socket?.close()\n }\n }, [drawFrame, sessionId, pageId, tabId])\n\n // The agent drives the same browser, so the address bar follows the page\n // rather than owning it.\n const browserUrl = status?.url\n useEffect(() => {\n if (browserUrl !== undefined && browserUrl !== '' && browserUrl !== 'about:blank') setAddress(browserUrl)\n }, [browserUrl])\n\n /**\n * Send one viewer message.\n * @param payload - the message to encode.\n */\n const send = useCallback((payload: unknown): void => {\n const socket = socketRef.current\n if (socket !== undefined && socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(payload))\n }, [])\n\n /**\n * Send one input message.\n * @param message - the host's input message.\n */\n const sendInput = useCallback((message: unknown): void => {\n send({ type: 'input', message })\n }, [send])\n\n /**\n * Ask the host for a browser this pane can use again.\n *\n * The socket carries the ask when it is up, because that is the connection the\n * host already holds for this viewer. It is not up in the one case the button\n * exists for: watching never starts a browser, so a session whose browser was\n * closed from the settings page has no socket at all — the host refused the\n * subscription and closed it. Sending there would be dropped in silence by\n * {@link send}, which is what made this button do nothing exactly when it was\n * needed. The HTTP ask is the path that does start a browser, so it is the\n * fallback: same page ask the entry makes, under the same tab id, which keeps\n * a second click from opening a second page.\n */\n const restart = useCallback((): void => {\n const socket = socketRef.current\n const route = restartRoute(socket !== undefined && socket.readyState === WebSocket.OPEN, tabId)\n if (route.via === 'socket') {\n send({ type: 'restart' })\n return\n }\n if (route.via === 'http') void openPageRequest(sessionId, tabId as string).catch(() => {})\n }, [send, sessionId, tabId])\n\n /**\n * Arm the clipboard for the chord just pressed.\n *\n * A copy with nothing selected must leave the clipboard alone, and only the\n * mirror knows what is selected, so the write waits for its answer:\n * {@link clipboardReply} is the decision, and this is only where it lands.\n * @param chord - the reading chord that was pressed.\n */\n const armClipboard = useCallback((chord: ReadingChord): void => {\n clipboardRef.current = (text: string): void => {\n const reply = clipboardReply(chord, text)\n if (reply.write !== undefined) void writeClipboard(reply.write)\n if (reply.after !== undefined) sendInput(reply.after)\n }\n }, [sendInput])\n\n /**\n * Navigate to what the address bar holds.\n *\n * Both Enter and the arrow button land here, because an implicit form\n * submission is not the only way this pane is asked to navigate and the two\n * must not drift apart.\n */\n const submit = useCallback((): void => {\n const target = address.trim()\n if (target !== '') send({ type: 'navigate', url: target })\n }, [address, send])\n\n const state = status?.state\n const shownUrl = status?.url ?? ''\n /** Whether the address bar holds something the browser is not on. */\n const dirty = address.trim() !== '' && address !== shownUrl\n const secure = shownUrl.startsWith('https://')\n const note = failure !== undefined\n ? failure\n : !connected\n ? copy('connecting')\n : painted && state !== 'failed' && state !== 'closed'\n ? undefined\n : state === 'failed'\n ? `${copy('failed')} ${status?.error ?? ''}`.trim()\n : state === 'starting'\n ? copy('starting')\n : state === 'ready'\n ? copy('waiting')\n : state === 'closed'\n ? `${copy('closed')} ${status?.error ?? ''}`.trim()\n : copy('idle')\n // A browser that is gone or broken cannot be recovered by waiting: the pane\n // offers the one action that does recover it, which is starting a new one.\n const recoverable = failure !== undefined || state === 'failed' || state === 'closed'\n\n return (\n <div style={style.root}>\n <form\n style={{ ...style.omnibox, ...focused ? style.omniboxFocused : {} }}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n >\n <span\n style={{ ...style.scheme, ...secure ? {} : style.schemeInsecure }}\n title={secure ? copy('secure') : copy('insecure')}\n >\n {secure\n ? (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.3\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {LOCK_ICON}\n </svg>\n )\n : <AddressGlobe size={14} />}\n </span>\n <input\n style={style.address}\n value={address}\n placeholder={copy('address')}\n spellCheck={false}\n autoComplete=\"off\"\n aria-label={copy('address')}\n onFocus={(event) => {\n setFocused(true)\n // A browser hands you the whole address to replace, not a caret in it.\n event.currentTarget.select()\n }}\n onBlur={() => { setFocused(false) }}\n onChange={(event) => { setAddress(event.target.value) }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n submit()\n }\n }}\n />\n {dirty\n ? <IconButton label={copy('go')} icon={GO_ICON} onClick={submit} submit />\n : <IconButton label={copy('reload')} icon={RELOAD_ICON} onClick={() => { send({ type: 'reload' }) }} />}\n </form>\n <div style={style.stage}>\n <canvas\n ref={canvasRef}\n tabIndex={0}\n style={style.canvas}\n onContextMenu={(event) => { event.preventDefault() }}\n onMouseDown={(event) => {\n const at = fractionOf(event.currentTarget, event.clientX, event.clientY)\n sendInput({ type: 'mouse', action: 'down', x: at.x, y: at.y, button: buttonOf(event.button), clickCount: 1 })\n }}\n onMouseUp={(event) => {\n const at = fractionOf(event.currentTarget, event.clientX, event.clientY)\n sendInput({ type: 'mouse', action: 'up', x: at.x, y: at.y, button: buttonOf(event.button), clickCount: 1 })\n }}\n onMouseMove={(event) => {\n const now = Date.now()\n if (now - lastMoveRef.current < MOVE_INTERVAL_MS) return\n lastMoveRef.current = now\n const at = fractionOf(event.currentTarget, event.clientX, event.clientY)\n sendInput({ type: 'mouse', action: 'move', x: at.x, y: at.y })\n }}\n onWheel={(event) => {\n const at = fractionOf(event.currentTarget, event.clientX, event.clientY)\n sendInput({ type: 'wheel', x: at.x, y: at.y, deltaX: event.deltaX, deltaY: event.deltaY })\n }}\n onKeyDown={(event) => {\n // The clipboard shortcuts are the pane's, not the mirror's: the page\n // cannot receive them at all, and the system clipboard belongs to the\n // browser the user is in. `clipboard.ts` says why, and which chords.\n const chord = clipboardChord(event)\n if (chord === 'paste') {\n // The browser pastes into whatever is focused when this handler\n // returns, so hand it the sink; the sink's own `paste` event is\n // where the text is read. No permission is involved: the user's\n // keystroke is what lets the browser part with the clipboard.\n pasteSinkRef.current?.focus()\n return\n }\n if (chord !== undefined) {\n event.preventDefault()\n armClipboard(chord)\n send({ type: 'selection' })\n return\n }\n // Text, a chord, or nothing: the decision, and the reasons it is\n // shaped that way, are in `keys.ts` with tests of their own.\n const message = keyMessage(event)\n if (message === undefined) return\n sendInput(message)\n event.preventDefault()\n }}\n />\n {/* The paste sink: the browser's own paste needs something editable to\n land in, and this is where the pane reads it from. Kept out of the\n way and out of the tab order — it is a clipboard pipe, not a field. */}\n <textarea\n ref={pasteSinkRef}\n aria-hidden=\"true\"\n tabIndex={-1}\n value=\"\"\n style={style.pasteSink}\n onChange={() => {}}\n onPaste={(event) => {\n const text = event.clipboardData.getData('text')\n event.preventDefault()\n canvasRef.current?.focus()\n const message = pasteMessage(text)\n if (message !== undefined) sendInput(message)\n }}\n />\n {note === undefined ? null : (\n <div style={style.note}>\n <div style={style.noteCard}>\n <span>{note}</span>\n {recoverable\n ? (\n <button type=\"button\" style={style.noteButton} onClick={() => { restart() }}>\n {copy('restart')}\n </button>\n )\n : null}\n </div>\n </div>\n )}\n </div>\n <div style={style.status}>\n {/* The address bar carries the page; this row carries what the address\n bar cannot — which browser is behind it. A page is closed from its\n tab, not from inside the pane: the tab is what names it. */}\n <span style={style.statusText}>\n {status?.debugPort === undefined ? '' : `${copy('endpoint')} 127.0.0.1:${status.debugPort}`}\n </span>\n </div>\n </div>\n )\n}\n","/**\n * How the browser's glyph is placed inside a chip title.\n *\n * The chip's title row (`[data-dockkit-tab-title]`) is already\n * `display: flex; gap: 5px; align-items: center`, and it reaches registrants\n * through a `display: contents` slot wrapper — so the harness's own chips hand\n * it a bare `<svg>`: a flex item, blockified, centred on the row's line, one\n * 5px gap from the label.\n *\n * This bundle has no stylesheet of its own, so the wrapper below is where that\n * has to be said instead. It exists for `flex: 0 0 auto` (without it a long\n * label shrinks the globe) and must add nothing else. Two measured ways of\n * getting it wrong, both seen on 2026-09-24 with the pane open in the GUI:\n *\n * - `display: inline-block` wrapping the icon leaves the *svg* inline, sitting\n * on the wrapper's first baseline while the row centres the wrapper — an\n * 18.2px line box for a 14px glyph, 2.1px above the label's centre.\n * - `margin-right` here doubles the row's gap: 10px from the label against the\n * sibling chips' 5px.\n *\n * `verticalAlign` is not part of it either: its wrapper is a block-level flex\n * item, where the property has no effect (which is why `-3px` never moved the\n * glyph that looked high).\n */\nexport const CHIP_GLYPH: Readonly<Record<string, string>> = {\n display: 'flex', alignItems: 'center', flex: '0 0 auto',\n}\n\n/**\n * The chip glyph's edge in px — the size the sibling chips draw their own\n * glyphs at (`FileTypeIcon size={16}` in the files and text titles) and the\n * built-in Browser tab's default for this same globe.\n */\nexport const CHIP_GLYPH_SIZE = 16\n","/**\n * The browser's chip title: the globe before the tab's own label.\n *\n * Registered under `sidebar.right.pane.tab.title`; without a registrant the chip\n * falls back to the registry's captured title text, which is how the browser\n * used to be the only tab in the strip with no glyph of its own.\n *\n * The label is the page's own, live: a tab names a page, and what makes the\n * tabs tell each other apart is the page renaming itself, not the moment the\n * tab was opened. The facts come from the loop that follows the host's report\n * (`pages.ts`); until they name this tab's page the captured text stands in.\n */\nimport { useSyncExternalStore, type ReactNode } from 'react'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport { CHIP_GLYPH, CHIP_GLYPH_SIZE } from './chip.ts'\nimport { BrowserGlyph } from './glyph.ts'\nimport { pageFactsOf, subscribePageFacts } from './pages.ts'\n\n/**\n * The title as the chip and a floating panel's header show it.\n * @param props - the tab information hook.\n * @returns the globe followed by the tab's title text.\n */\nexport function BrowserTitle({ useTabInfo }: PropsRuntime<'sidebar.right.pane.tab.title'>): ReactNode {\n const { tab } = useTabInfo()\n const facts = useSyncExternalStore(subscribePageFacts, () => pageFactsOf(tab.contentId))\n const label = facts !== undefined && facts.title !== '' ? facts.title : tab.title\n return (\n <>\n <span style={CHIP_GLYPH}><BrowserGlyph size={CHIP_GLYPH_SIZE} /></span>\n {label}\n </>\n )\n}\n","/**\n * Stage one of this plugin's tab registration: what the mirror's tab type IS.\n *\n * The kind is `cdpBrowser`, deliberately not `browser`. A later harness ships\n * `ui-sidebar-browser` under `browser`, and that tab embeds a page inside the\n * app; this one mirrors a separate browser process over CDP. Sharing the kind\n * would make installing this plugin silently replace that tab with a different\n * capability.\n *\n * A tab of this kind is addressed by the page it mirrors: one tab per browser\n * page, its address built from the page's CDP target id, so opening the same\n * page again reveals the tab that is already there and a tab survives a client\n * reload still pointing at its page.\n *\n * The guide entry is a page-type open that `multiple` keeps a tab of its own:\n * each pick opens a pane with no page, that pane asks the host for a page, and\n * the entry hands over to the per-page tab once the page exists. Picking it\n * twice therefore opens two pages rather than settling on the first one.\n */\nimport type { ComponentType } from 'react'\nimport type { SidebarRightTabDefinition } from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport type { IconProps } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { PAGE_PREFIX } from './pages.ts'\n\n/** The tab kind this plugin owns. */\nexport const BROWSER_KIND = 'cdpBrowser'\n\n/** This implementation's identity: the key its body and title register under. */\nexport const BROWSER_ID = 'dsh-browser'\n\n/** The address glob a page tab matches: the page scheme, anything after it. */\nconst PAGE_PATTERN = `${PAGE_PREFIX}**`\n\n/**\n * The mirror's registry definition.\n *\n * The guide entry carries the glyph the caller hands in: without one the guide\n * draws its cube placeholder, which says nothing about what picking the entry\n * opens. The chip draws the same glyph through this module's own title\n * registrant, so the two places the user meets the browser agree on what it\n * looks like.\n * @param title - the tab chip's text in the current language.\n * @param guideTitle - the guide entry's title in the current language.\n * @param guideDescription - the guide entry's one-line description.\n * @param icon - the browser's glyph, supplied by the caller so this module stays\n * loadable outside the bundle (see `glyph.ts`).\n * @returns the definition to register.\n */\nexport function browserDefinition(\n title: () => string,\n guideTitle: () => string,\n guideDescription: () => string,\n icon: ComponentType<IconProps>,\n): SidebarRightTabDefinition {\n return {\n id: BROWSER_ID,\n kind: BROWSER_KIND,\n multiple: true,\n priority: 'extension',\n title,\n patterns: [PAGE_PATTERN],\n canOpen: address => address.startsWith(PAGE_PREFIX),\n guide: [{ id: BROWSER_ID, order: 40, title: guideTitle, description: guideDescription, icon }],\n }\n}\n","/**\n * Keep the Sidebar's browser tabs matched to the browser's pages.\n *\n * The browser belongs to the Session; its pages belong to the user's eyes. A\n * link that opens a tab, a `window.open`, a page that closes itself — each one\n * changes a page set the user cannot see anywhere else, and a sidebar that\n * stayed as it was would show a browser the user cannot recognize: tabs for\n * pages that are gone, no tab for the page that just came up. So the client\n * half reads the host's report on a fixed beat and reconciles every session it\n * names: one tab per page the browser holds, named by the page's CDP target id,\n * and none for a page it does not.\n *\n * Closing follows the same rule in reverse, and it is why the match can be\n * unconditional: closing a tab closes that tab's page (the close handler asks\n * the host), so a tab the user closed is a page that is gone — there is no\n * state where the sidebar shows less than the browser and something must put\n * it back. The one race — the report still listing a page whose close was just\n * asked for — is what the closing ledger is for.\n *\n * The route is polled rather than pushed because this half has no channel of\n * its own to the host: the viewer socket exists only once a pane is open, and\n * most of the changes this follows happen with every pane closed.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport type { TabId } from '@deepseek-ai/dsh-client-ui-dockkit'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport { browserStatus, type ClientBrowserStatus } from './api.ts'\nimport { BROWSER_KIND } from './definition.ts'\nimport {\n isClosing,\n pageAddressOf,\n publishPageFacts,\n reconcile,\n settleClosing,\n targetIdOf,\n type ActualTab,\n} from './pages.ts'\n\n/** How long the loop waits between looks at the host's browser state. */\nconst POLL_MS = 1500\n\n/**\n * Read the host's report once and bring every named session's tabs in line\n * with its pages.\n * @param ctx - client context carrying the Sidebar's navigation face.\n * @param report - what the host last answered.\n */\nexport function followOneReport(ctx: ClientContext, report: { readonly instances: readonly ClientBrowserStatus[] }): void {\n // The chips' facts are for every session the host named: a tab in a session\n // the user is only about to visit shows its page's title all the same.\n const facts: [string, { title: string; url: string }][] = []\n /** The pages each session holds, by session id. */\n const wanted = new Map<string, string[]>()\n for (const instance of report.instances) {\n const addresses: string[] = []\n for (const tab of instance.tabs ?? []) {\n if (tab.targetId === undefined) continue\n const address = pageAddressOf(tab.targetId)\n addresses.push(address)\n facts.push([address, { title: tab.title ?? '', url: tab.url }])\n }\n wanted.set(instance.sessionId, addresses)\n }\n publishPageFacts(facts)\n // A close that has answered is no longer a reason to hold a tab back.\n const listed: (readonly [string, string])[] = []\n for (const instance of report.instances) {\n for (const tab of instance.tabs ?? []) {\n if (tab.targetId !== undefined) listed.push([instance.sessionId, tab.targetId])\n }\n }\n settleClosing(listed, Date.now())\n\n for (const [sessionId, addresses] of wanted) {\n const actual: ActualTab[] = ctx.sidebarRight.openTabs.getSnapshot()\n .filter(tab => tab.kind === BROWSER_KIND && tab.sessionId === sessionId)\n .map(tab => ({ id: tab.tabId, contentId: tab.contentId }))\n const plan = reconcile(addresses, actual)\n for (const tabId of plan.close) {\n // A tab that names a page which is gone closes quietly; the close\n // handler fires for it too, and the host answers \"no such page\" — which\n // is exactly the truth, and changes nothing.\n ctx.sidebarRight.closeIn(sessionId as SessionId, tabId as TabId)\n }\n for (const address of plan.open) {\n const targetId = targetIdOf(address)\n if (targetId === undefined || isClosing(sessionId, targetId)) continue\n ctx.sidebarRight.openResourceIn(sessionId as SessionId, address)\n }\n }\n}\n\n/**\n * Follow the host's browser state for as long as the plugin is loaded, and keep\n * every session's browser tabs matched to its pages.\n * @param ctx - client context carrying the Sidebar's navigation face.\n */\nexport function followBrowserPages(ctx: ClientContext): void {\n ctx.effect(() => {\n let timer: ReturnType<typeof setInterval> | undefined\n let looking = false\n let stopped = false\n\n /** Read the host's state once, and reconcile every session it named. */\n const look = async (): Promise<void> => {\n if (looking || stopped) return\n looking = true\n try {\n const report = await browserStatus()\n if (stopped) return\n followOneReport(ctx, report)\n } catch {\n // An unanswered read is not a browser event; the next look tries again.\n } finally {\n looking = false\n }\n }\n\n const follow = (): void => {\n if (timer !== undefined) clearInterval(timer)\n timer = setInterval(() => { void look() }, POLL_MS)\n void look()\n }\n follow()\n return () => {\n stopped = true\n if (timer !== undefined) clearInterval(timer)\n }\n }, 'dsh-browser: keep sidebar tabs matched to the browser pages')\n}\n","/**\n * Closing a tab closes the page it mirrors.\n *\n * The Sidebar asks a kind's close handler before it removes a tab, which is\n * what makes the rule hold no matter how the close arrived — the strip's ✕, a\n * menu, or the pane itself. The handler only records the ask and sends it on:\n * the tab is going away either way, and a close that never lands brings the\n * tab back on a later look at the host's report (`reveal.ts`).\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport { closePageRequest } from './api.ts'\nimport { BROWSER_KIND } from './definition.ts'\nimport { markClosing, targetIdOf } from './pages.ts'\n\n/**\n * Register the close handler for the browser's tabs.\n *\n * The registration is scoped to the plugin's fiber: `registerCloseHandler`\n * returns the disposer that takes the entry out of the Sidebar's map, and a\n * resident controller's map outlives a plugin that is applied again in the same\n * page. Dropping that disposer makes the second apply throw \"close handler\n * already registered\", which leaves the client half dead for that page until a\n * full reload — every other registration in `index.ts` is effect-scoped for\n * exactly this reason.\n * @param ctx - client context carrying the Sidebar's registry.\n */\nexport function followPageCloses(ctx: ClientContext): void {\n ctx.effect(() => ctx.sidebarRight.registerCloseHandler(BROWSER_KIND, (sessionId, tab) => {\n const targetId = targetIdOf(tab.contentId)\n // A tab that names no page (the guide entry's) has nothing to close.\n if (targetId === undefined) return\n markClosing(sessionId, targetId)\n // The tab is going away either way; the close travels on its own, and a\n // close that never lands brings the tab back on a later look.\n void closePageRequest(sessionId, targetId).catch(() => {})\n }), 'dsh-browser: close handler')\n}\n","/**\n * The shape of the 浏览器 settings page, as data.\n *\n * The page is one form with two tiers. The first is what a browser needs to\n * start at all — which one, whether it gets a window, and where its profile\n * lives. The second is everything that already has a working default and only\n * gets touched when something is unusual: automation markers, the executable\n * path, the CDP port, the headless page size, the mirror quality, and what the\n * agent's snapshot prints. The second tier is folded away.\n *\n * The tables here are the single source of that shape: the component renders the\n * groups in the order declared, and TypeScript refuses to build a component that\n * has no control for a placed field. The point is that adding a field to the\n * configuration cannot silently leave the page with a control that never\n * renders — or with a field the user can no longer override.\n *\n * It is a plain `.ts` module because the section itself is a `.tsx`, which no\n * test can import: Node strips types, not JSX. Everything here is free of React,\n * so the page's copy decisions — the fold's own summary line, the group\n * headings, the status banner — are testable on their own.\n */\nimport type { DshBrowserKey } from './locales.ts'\nimport type { ClientBrowserReport, ClientBrowserStatus } from './api.ts'\n\n/** The configuration fields the settings page edits. */\nexport interface BrowserSettingsView {\n readonly channel: 'chrome' | 'msedge'\n readonly executablePath: string\n readonly headless: boolean\n readonly stealth: boolean\n readonly userDataDir: string\n readonly profileMode: 'temp' | 'session' | 'workspace'\n readonly debugPortMin: number\n readonly debugPortMax: number\n readonly viewportWidth: number\n readonly viewportHeight: number\n readonly quality: number\n readonly snapshotAttributes: string\n readonly snapshotIgnore: string\n}\n\n/**\n * The blocks inside the fold, in page order.\n *\n * This list is the page's structure, not a hint: a group missing from it would\n * have its fields placed but never rendered, which the tests reject. A group is\n * folded as a whole — splitting one would leave a heading with nothing under it\n * in the visible tier.\n */\nexport const ADVANCED_GROUPS = ['process', 'mirror', 'snapshot'] as const\n\n/** One block inside the fold. */\nexport type AdvancedGroup = typeof ADVANCED_GROUPS[number]\n\n/** Every block of the page; `everyday` is the visible tier. */\nexport type SettingsGroup = 'everyday' | AdvancedGroup\n\n/** One field's place on the page. */\nexport interface FieldPlacement {\n /** The configuration field this placement renders. */\n readonly field: keyof BrowserSettingsView\n /** The block it belongs to. */\n readonly group: SettingsGroup\n}\n\n/**\n * Every editable field, in the order the page renders it.\n *\n * The set is exactly the volatile half of the configuration schema — a field the\n * loader lets the settings page write has to appear here, and\n * `test/settings-layout.test.ts` fails when the two lists drift apart.\n */\nexport const FIELD_PLACEMENTS: readonly FieldPlacement[] = [\n { field: 'headless', group: 'everyday' },\n { field: 'channel', group: 'everyday' },\n { field: 'profileMode', group: 'everyday' },\n { field: 'userDataDir', group: 'everyday' },\n { field: 'stealth', group: 'process' },\n { field: 'executablePath', group: 'process' },\n { field: 'debugPortMin', group: 'process' },\n { field: 'debugPortMax', group: 'process' },\n { field: 'viewportWidth', group: 'mirror' },\n { field: 'viewportHeight', group: 'mirror' },\n { field: 'quality', group: 'mirror' },\n { field: 'snapshotAttributes', group: 'snapshot' },\n { field: 'snapshotIgnore', group: 'snapshot' },\n]\n\n/** The label and hint one field's row renders. */\nexport interface FieldCopy {\n /** The row's visible label. */\n readonly label: DshBrowserKey\n /** The line under it, when the label alone does not explain the field. */\n readonly hint?: DshBrowserKey\n}\n\n/**\n * Copy for every placed field.\n *\n * A separate table from {@link FIELD_PLACEMENTS} because the order the page\n * renders in and the words it uses are different concerns that happen to share a\n * key; keeping the record total means a new field cannot appear unlabelled.\n */\nexport const FIELD_COPY: Record<keyof BrowserSettingsView, FieldCopy> = {\n headless: { label: 'settingsMode', hint: 'settingsHeadlessNote' },\n channel: { label: 'settingsBrowser' },\n // The mode is the field that decides who shares a sign-in, so it sits with\n // the visible tier: a user who does not find it reads the per-session default\n // as \"logins never carry over\" rather than as a choice.\n profileMode: { label: 'settingsProfileMode', hint: 'settingsProfileModeHint' },\n // Label and hint both follow the mode, because the field means something\n // different in each one: a directory name in `session` mode, a parent for one\n // directory per project in `workspace` mode, and nothing at all in `temp`.\n // The hint is therefore not a constant here — `profileDirHint()` answers it.\n userDataDir: { label: 'settingsProfileDir' },\n stealth: { label: 'settingsStealth', hint: 'settingsStealthNote' },\n executablePath: { label: 'settingsExecutable' },\n debugPortMin: { label: 'settingsDebugPortMin', hint: 'settingsDebugPortHint' },\n debugPortMax: { label: 'settingsDebugPortMax' },\n viewportWidth: { label: 'settingsWidth' },\n viewportHeight: { label: 'settingsHeight' },\n quality: { label: 'settingsQuality' },\n snapshotAttributes: { label: 'settingsSnapshotAttributes', hint: 'settingsSnapshotAttributesHint' },\n snapshotIgnore: { label: 'settingsSnapshotIgnore', hint: 'settingsSnapshotIgnoreHint' },\n}\n\n/** The heading and one-line explanation of a block inside the fold. */\nexport const GROUP_COPY: Record<AdvancedGroup, { readonly title: DshBrowserKey; readonly hint: DshBrowserKey }> = {\n process: { title: 'settingsGroupProcess', hint: 'settingsGroupProcessHint' },\n mirror: { title: 'settingsGroupMirror', hint: 'settingsGroupMirrorHint' },\n snapshot: { title: 'settingsGroupSnapshot', hint: 'settingsGroupSnapshotHint' },\n}\n\n/** Modes whose profiles outlive the browser process. */\nconst KEEPING_MODES: readonly BrowserSettingsView['profileMode'][] = ['session', 'workspace']\n\n/**\n * Whether the directory row applies to a mode.\n *\n * `temp` keeps nothing, so it has no parent directory to point anywhere: asking\n * for one would offer a setting that cannot take effect.\n * @param mode - the profile mode in force.\n * @returns whether the page shows the directory row.\n */\nexport function profileDirVisible(mode: BrowserSettingsView['profileMode']): boolean {\n return KEEPING_MODES.includes(mode)\n}\n\n/**\n * The hint the directory row carries for a mode.\n *\n * The field means something different in each keeping mode — one directory per\n * conversation versus a parent for one directory per project — and a single\n * sentence cannot say both without being wrong in one of them.\n * @param mode - the profile mode in force.\n * @returns the copy key for that mode's explanation.\n */\nexport function profileDirHint(mode: BrowserSettingsView['profileMode']): DshBrowserKey {\n return mode === 'workspace' ? 'settingsProfileDirHintWorkspace' : 'settingsProfileDirHintSession'\n}\n\n/**\n * The placeholder the directory field shows for a mode.\n *\n * It is the only place the row can say what an empty value will do, and that\n * answer differs per mode: a default directory, or the project's own.\n * @param mode - the profile mode in force.\n * @returns the copy key for that mode's placeholder.\n */\nexport function profileDirPlaceholder(mode: BrowserSettingsView['profileMode']): DshBrowserKey {\n return mode === 'workspace' ? 'settingsProfileDirPlaceholderWorkspace' : 'settingsProfileDirPlaceholderSession'\n}\n\n/**\n * The placements of one block, in page order.\n * @param group - the block to render.\n * @returns its fields, in the order they are declared.\n */\nexport function placementsIn(group: SettingsGroup): readonly FieldPlacement[] {\n return FIELD_PLACEMENTS.filter(placement => placement.group === group)\n}\n\n/**\n * The fields that start a browser, and therefore stay visible.\n * @returns the placements outside the fold.\n */\nexport function visiblePlacements(): readonly FieldPlacement[] {\n return placementsIn('everyday')\n}\n\n/**\n * The fields behind the fold, in page order.\n * @returns every folded placement.\n */\nexport function foldedPlacements(): readonly FieldPlacement[] {\n return ADVANCED_GROUPS.flatMap(group => placementsIn(group))\n}\n\n/**\n * How many of these fields the user layer holds.\n *\n * The fold hides its fields' controls, and with them their reset buttons, so a\n * change made inside it would otherwise be invisible from the collapsed page.\n * @param user - the user's own settings layer, as the form reports it.\n * @param placements - the fields to look at.\n * @returns the number of them the user has overridden.\n */\nexport function overriddenCount(\n user: Record<string, unknown>,\n placements: readonly FieldPlacement[],\n): number {\n return placements.filter(placement => Object.hasOwn(user, placement.field)).length\n}\n\n/**\n * The line the fold shows while closed.\n * @param t - copy for this namespace.\n * @param overridden - how many hidden fields the user has changed.\n * @returns the summary, mentioning them when there are any.\n */\nexport function advancedSummary(t: (key: DshBrowserKey) => string, overridden: number): string {\n const title = t('settingsAdvanced')\n if (overridden === 0) return title\n return `${title} · ${t('settingsAdvancedOverridden').replace('{count}', String(overridden))}`\n}\n\n/**\n * What the banner says about the browsers right now.\n *\n * The state is the host's, not the form's: it is the only way a user can tell a\n * saved value from a value a browser is actually running with.\n * @param report - the last read, or `undefined` before the first answer.\n * @param t - copy for this namespace.\n * @returns the banner's summary line.\n */\nexport function statusLine(report: ClientBrowserReport | undefined, t: (key: DshBrowserKey) => string): string {\n if (report === undefined) return t('settingsStatusReading')\n const running = report.instances.filter(instance => instance.state === 'ready').length\n const starting = report.instances.filter(instance => instance.state === 'starting').length\n if (running > 0) return t('settingsStatusRunning').replace('{count}', String(running))\n if (starting > 0) return t('settingsStatusStarting').replace('{count}', String(starting))\n return t('settingsStatusNone')\n}\n\n/**\n * One session's browser as a single line: which session, what state, and the\n * facts only a running one has.\n * @param instance - one entry of the report.\n * @param t - copy for this namespace.\n * @param shared - how many conversations use the same process as this one; 1\n * means it is this conversation's own, so nothing is said about sharing.\n * @returns the line to show for it.\n */\nexport function instanceLine(\n instance: ClientBrowserStatus,\n t: (key: DshBrowserKey) => string,\n shared = 1,\n): string {\n const state = instance.state === 'ready'\n ? t('settingsInstanceStateRunning')\n : instance.state === 'starting'\n ? t('settingsInstanceStateStarting')\n : instance.state === 'failed'\n ? `${t('settingsInstanceStateFailed')} — ${instance.error ?? ''}`.trim()\n : t('settingsInstanceStateStopped')\n const parts = [instanceLabel(instance, t), state]\n if (instance.debugPort !== undefined) parts.push(`CDP 127.0.0.1:${instance.debugPort}`)\n if (instance.mode !== undefined) parts.push(instance.mode)\n const pages = instance.ownedPages ?? instance.tabs?.length ?? 0\n if (pages > 0) parts.push(t('settingsInstancePages').replace('{count}', String(pages)))\n // A row is a conversation, and several of them can be one browser: saying so\n // is what stops \"three rows\" from reading as \"three processes\".\n if (shared > 1) parts.push(t('settingsInstanceShared').replace('{count}', String(shared)))\n return parts.join(' · ')\n}\n\n/**\n * How many conversations share each browser process in a report.\n *\n * Keyed by the host's `browserId`, which is the pool key: two rows with the same\n * one are two conversations on one process. A report whose rows carry no\n * `browserId` — an older host — counts each session as its own, which is what\n * those rows mean.\n * @param instances - every row of the report.\n * @returns one count per session id.\n */\nexport function sharedCounts(instances: readonly ClientBrowserStatus[]): Map<string, number> {\n const byBrowser = new Map<string, string[]>()\n for (const instance of instances) {\n const key = instance.browserId ?? instance.sessionId\n const held = byBrowser.get(key)\n if (held === undefined) byBrowser.set(key, [instance.sessionId])\n else held.push(instance.sessionId)\n }\n const counts = new Map<string, number>()\n for (const sessions of byBrowser.values()) {\n for (const sessionId of sessions) counts.set(sessionId, sessions.length)\n }\n return counts\n}\n\n/**\n * What one session's browser is called on the page.\n *\n * The title when the host could read one, and \"unnamed session\" when it could\n * not. The raw id is deliberately not the fallback: every id starts with\n * `session-`, so shortening one prints the same eight hexadecimal characters\n * for every row and tells the user nothing about which conversation it is. The\n * id belongs in the row's tooltip, where it costs nothing and settles a tie\n * between two conversations with the same name.\n * @param instance - one entry of the report.\n * @param t - copy for this namespace.\n * @returns the label to show for its row.\n */\nexport function instanceLabel(instance: ClientBrowserStatus, t: (key: DshBrowserKey) => string): string {\n const title = instance.title?.trim() ?? ''\n return title === '' ? t('settingsSessionUnnamed') : title\n}\n","/**\n * The 浏览器 settings section.\n *\n * Values arrive from the plugin's settings namespace — the composition entry\n * with the user's overrides resolved over it — and every control writes one\n * field back through the bound scope, so the page never holds a draft that can\n * disagree with what the browser was launched from.\n *\n * The profile settings are two questions, not one asked twice: the mode decides\n * who shares a sign-in and whether anything is kept, and the directory only\n * decides where. A blank directory is a legitimate answer in every mode — it\n * means \"this mode's own default\" — which is why the field is never a switch.\n *\n * One row can be absent: the directory row is not rendered in `temp` mode,\n * because that mode keeps nothing and therefore has no directory to name.\n * `settings-layout.ts` owns which modes that is, so the rule is testable.\n *\n * Which fields exist, where they sit, and what they are called all come from\n * `settings-layout.ts`: this file only turns each placement into a control. That\n * split is what keeps a new configuration field from slipping onto the page\n * unlabelled or missing entirely, and it is what the tests can reach.\n *\n * Styles are inline: this plugin builds its client bundle outside the\n * repository's stylesheet pipeline, so a CSS import would have no owner.\n */\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore, type CSSProperties, type ReactElement, type ReactNode } from 'react'\nimport type { ConfigForm, SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { DshBrowserKey } from './locales.ts'\nimport { browserStatus, closeBrowserRequest, type ClientBrowserReport } from './api.ts'\nimport {\n ADVANCED_GROUPS,\n FIELD_COPY,\n GROUP_COPY,\n advancedSummary,\n foldedPlacements,\n instanceLine,\n overriddenCount,\n placementsIn,\n profileDirHint,\n profileDirPlaceholder,\n profileDirVisible,\n sharedCounts,\n statusLine,\n type BrowserSettingsView,\n type FieldPlacement,\n} from './settings-layout.ts'\n\n/** Injected face bound in the plugin's apply closure. */\nexport interface BrowserSettingsInjected {\n /** The namespace form this page reads and writes. */\n readonly form: ConfigForm<BrowserSettingsView>\n /** Copy for this namespace. */\n readonly t: (key: DshBrowserKey) => string\n}\n\n/**\n * Every value here is a `--dsw-*` token from ui-theme: the page is read in both\n * palettes, and a name the theme does not define falls back to nothing at all\n * rather than to a colour that happens to suit one of them. The materials are the\n * shared settings ones, so this page sits beside the harness's own cards: a\n * control is `bg-layer-3` inside a `border-l4` hairline at `radius-md`, a label\n * is 13px primary, a hint is 12px tertiary, and a card is `radius-xl` over the\n * settings-card fill and stroke.\n */\nconst styles: Readonly<Record<string, CSSProperties>> = {\n page: { display: 'flex', flexDirection: 'column', gap: '14px', maxWidth: '720px' },\n intro: { margin: 0, fontSize: '12px', lineHeight: 1.6, color: 'var(--dsw-alias-label-tertiary)' },\n group: { display: 'flex', flexDirection: 'column', gap: '4px', borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: '6px' },\n row: { display: 'flex', gap: '12px', alignItems: 'flex-start', justifyContent: 'space-between', padding: '10px 0' },\n label: { display: 'flex', flexDirection: 'column', gap: '2px', flex: '1 1 auto', minWidth: 0 },\n name: { fontSize: '13px', fontWeight: 500, lineHeight: 1.5, color: 'var(--dsw-alias-label-primary)' },\n hint: { fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)' },\n control: { display: 'flex', gap: '8px', alignItems: 'center', flex: '0 0 auto' },\n input: {\n boxSizing: 'border-box', padding: '6px 12px', width: '240px', font: 'inherit', fontSize: '13px', lineHeight: 1.5,\n border: '0.5px solid var(--dsw-alias-border-l4)', borderRadius: 'var(--dsw-radius-md)',\n background: 'var(--dsw-alias-bg-layer-3)', color: 'var(--dsw-alias-label-primary)',\n },\n number: {\n boxSizing: 'border-box', padding: '6px 12px', width: '84px', font: 'inherit', fontSize: '13px', lineHeight: 1.5,\n border: '0.5px solid var(--dsw-alias-border-l4)', borderRadius: 'var(--dsw-radius-md)',\n background: 'var(--dsw-alias-bg-layer-3)', color: 'var(--dsw-alias-label-primary)',\n },\n select: {\n boxSizing: 'border-box', padding: '6px 12px', font: 'inherit', fontSize: '13px', lineHeight: 1.5,\n border: '0.5px solid var(--dsw-alias-border-l4)', borderRadius: 'var(--dsw-radius-md)',\n background: 'var(--dsw-alias-bg-layer-3)', color: 'var(--dsw-alias-label-primary)',\n },\n reset: {\n border: 'none', background: 'none', padding: 0, font: 'inherit',\n fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer',\n },\n note: { margin: 0, fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)' },\n status: {\n display: 'flex', gap: '10px', alignItems: 'center', justifyContent: 'space-between',\n padding: '10px 12px', fontSize: '12px', lineHeight: 1.5, borderRadius: 'var(--dsw-radius-xl)',\n border: '0.5px solid var(--dsw-alias-settings-card-stroke)', background: 'var(--dsw-alias-settings-card-fill)',\n color: 'var(--dsw-alias-label-secondary)',\n },\n statusText: { display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 },\n statusDetail: { fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)', wordBreak: 'break-word', display: 'block' },\n statusRow: { display: 'flex', gap: '8px', alignItems: 'baseline', justifyContent: 'space-between' },\n statusSummary: { fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)', cursor: 'pointer' },\n statusFailed: { color: 'var(--dsw-alias-state-error-primary)' },\n fold: { display: 'flex', flexDirection: 'column', gap: '10px', borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: '12px' },\n foldSummary: { cursor: 'pointer', fontSize: '13px', fontWeight: 500, lineHeight: 1.5, color: 'var(--dsw-alias-label-primary)' },\n block: { display: 'flex', flexDirection: 'column', gap: '10px' },\n blockDivided: {\n display: 'flex', flexDirection: 'column', gap: '10px',\n borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: '12px',\n },\n blockTitle: { margin: 0, fontSize: '13px', fontWeight: 500, lineHeight: 1.5, color: 'var(--dsw-alias-label-primary)' },\n blockHint: { margin: 0, fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)' },\n save: { margin: 0, fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-label-tertiary)', minHeight: '18px' },\n saveFailed: { margin: 0, fontSize: '12px', lineHeight: 1.5, color: 'var(--dsw-alias-state-error-primary)', minHeight: '18px' },\n}\n\n/**\n * A control is missing.\n *\n * This only compiles while the caller's switch covers every field of\n * {@link BrowserSettingsView}, so adding a configuration field fails the build\n * here until it gets a control — the runtime throw is a backstop that never\n * runs in a build that passed.\n * @param field - the field nothing renders.\n * @returns never.\n */\nfunction unreachableField(field: never): ReactElement {\n throw new Error(`no control for ${String(field)}`)\n}\n\n/** One labelled control, with a reset affordance once the user layer holds the field. */\nfunction Row(props: {\n readonly label: string\n readonly hint: string | undefined\n readonly overridden: boolean\n readonly resetLabel: string\n readonly onReset: () => void\n readonly children: ReactNode\n}): ReactElement {\n return (\n <div style={styles.row}>\n <div style={styles.label}>\n <span style={styles.name}>{props.label}</span>\n {props.hint === undefined ? null : <span style={styles.hint}>{props.hint}</span>}\n </div>\n <div style={styles.control}>\n {props.children}\n {props.overridden\n ? <button type=\"button\" style={styles.reset} onClick={props.onReset}>{props.resetLabel}</button>\n : null}\n </div>\n </div>\n )\n}\n\n/** One block inside the fold: what it covers, then its rows. */\nfunction Block(props: {\n readonly title: string\n readonly hint: string\n readonly divided: boolean\n readonly children: ReactNode\n}): ReactElement {\n // A labelled region rather than a heading: the settings page this renders into\n // has no document outline of its own, so a heading level here would be an\n // invented one. The label is what a screen reader announces on entry.\n return (\n <section aria-label={props.title} style={props.divided ? styles.blockDivided : styles.block}>\n <div style={styles.blockTitle}>{props.title}</div>\n <p style={styles.blockHint}>{props.hint}</p>\n {props.children}\n </section>\n )\n}\n\n/**\n * The 浏览器 settings page.\n * @param props - section owner props plus the bound namespace scope.\n * @returns the section element.\n */\nexport function BrowserSettingsSection(\n props: SettingsSectionOwnerProps & BrowserSettingsInjected,\n): ReactElement {\n const { form, t } = props\n const subscribe = useCallback((listener: () => void) => form.subscribe(listener), [form])\n const snapshot = useSyncExternalStore(subscribe, () => form.getSnapshot())\n // Held above the early returns so the hook order never changes between the\n // loading, unavailable, and ready renders.\n const [status, setStatus] = useState<ClientBrowserReport | undefined>(undefined)\n const [statusError, setStatusError] = useState<string | undefined>(undefined)\n const [saveError, setSaveError] = useState<string | undefined>(undefined)\n const [saved, setSaved] = useState(false)\n /** The session whose browser is being stopped right now, if one is. */\n const [closing, setClosing] = useState<string | undefined>(undefined)\n const [closeError, setCloseError] = useState<string | undefined>(undefined)\n const timers = useRef<number[]>([])\n\n /** Read the host's live state; the page cannot see the browser any other way. */\n const refresh = useCallback((): void => {\n void browserStatus().then((next) => {\n setStatus(next)\n setStatusError(undefined)\n }).catch((error: unknown) => {\n setStatusError(error instanceof Error ? error.message : String(error))\n })\n }, [])\n\n useEffect(() => {\n refresh()\n const pending = timers.current\n return () => {\n for (const id of pending) window.clearTimeout(id)\n pending.length = 0\n }\n }, [refresh])\n\n /**\n * Stop one session's browser and re-read the report it should leave.\n *\n * The button belongs to a session rather than to the process: what it asks is\n * \"this conversation does not want a browser\", and the host decides whether\n * that stops a process. The row goes away when the ask answers, because the\n * report lists the browsers that exist and not the ones that did.\n * @param sessionId - the session whose browser should stop.\n */\n const closeBrowser = useCallback((sessionId: string): void => {\n setClosing(sessionId)\n setCloseError(undefined)\n void closeBrowserRequest(sessionId).then(() => {\n refresh()\n }).catch((error: unknown) => {\n setCloseError(error instanceof Error ? error.message : String(error))\n }).finally(() => {\n setClosing(undefined)\n })\n }, [refresh])\n\n /**\n * Report one settings write, then re-read the browser it may have restarted.\n * A launch field closes the running browser and the next frame request starts\n * a new one, so the later reads are what catch the restarted instance rather\n * than the one that is on its way out. Resetting a field is a write like any\n * other and goes through here too — otherwise the banner keeps reporting the\n * browser the user just replaced.\n * @param operation - the form write to track.\n */\n const commit = (operation: Promise<unknown>): void => {\n setSaved(false)\n setSaveError(undefined)\n void operation.then(() => {\n setSaved(true)\n for (const delay of [1_500, 4_000, 8_000]) {\n timers.current.push(window.setTimeout(refresh, delay))\n }\n }).catch((error: unknown) => {\n setSaveError(error instanceof Error ? error.message : String(error))\n })\n }\n\n /**\n * Write one field and report both the write and the browser it may restart.\n * @param field - the configuration field.\n * @param next - its new value.\n */\n const write = (field: keyof BrowserSettingsView, next: unknown): void => {\n commit(form.set(field, next))\n }\n\n if (snapshot.status === 'loading') return <p style={styles.intro}>{t('settingsLoading')}</p>\n const value = snapshot.value\n if (value === undefined) return <p style={styles.intro}>{t('settingsUnavailable')}</p>\n\n const user = (typeof snapshot.user === 'object' && snapshot.user !== null ? snapshot.user : {}) as Record<string, unknown>\n /** A field the user layer holds, so clearing it restores the composed value. */\n const owns = (field: keyof BrowserSettingsView): boolean => Object.hasOwn(user, field)\n /** A field is overridden once the user layer holds it. */\n const row = (\n label: string,\n field: keyof BrowserSettingsView,\n control: ReactNode,\n hint?: string,\n ): ReactElement => (\n <Row\n label={label}\n hint={hint}\n overridden={owns(field)}\n resetLabel={t('settingsReset')}\n onReset={() => { commit(form.unset(field)) }}\n >\n {control}\n </Row>\n )\n\n /** The control one placed field renders. */\n const control = (field: keyof BrowserSettingsView): ReactElement => {\n switch (field) {\n case 'headless':\n return (\n <select\n style={styles.select}\n value={value.headless ? 'headless' : 'headful'}\n onChange={(event) => { write('headless', event.target.value === 'headless') }}\n >\n <option value=\"headless\">{t('settingsHeadless')}</option>\n <option value=\"headful\">{t('settingsHeadful')}</option>\n </select>\n )\n case 'channel':\n return (\n <select\n style={styles.select}\n value={value.channel}\n onChange={(event) => { write('channel', event.target.value) }}\n >\n <option value=\"chrome\">{t('settingsChannelChrome')}</option>\n <option value=\"msedge\">{t('settingsChannelEdge')}</option>\n </select>\n )\n case 'profileMode':\n return (\n <select\n style={styles.select}\n value={value.profileMode}\n onChange={(event) => {\n const next = event.target.value as BrowserSettingsView['profileMode']\n write('profileMode', next)\n }}\n >\n <option value=\"temp\">{t('settingsProfileModeTemp')}</option>\n <option value=\"session\">{t('settingsProfileModeSession')}</option>\n <option value=\"workspace\">{t('settingsProfileModeWorkspace')}</option>\n </select>\n )\n case 'userDataDir':\n // A location, not a persistence switch: the mode above already answered\n // \"is anything kept\", so this only asks where.\n return (\n <input\n style={styles.input}\n value={value.userDataDir}\n spellCheck={false}\n placeholder={t(profileDirPlaceholder(value.profileMode))}\n onChange={(event) => { write('userDataDir', event.target.value) }}\n />\n )\n case 'stealth':\n return (\n <select\n style={styles.select}\n value={value.stealth ? 'on' : 'off'}\n onChange={(event) => { write('stealth', event.target.value === 'on') }}\n >\n <option value=\"on\">{t('settingsStealthOn')}</option>\n <option value=\"off\">{t('settingsStealthOff')}</option>\n </select>\n )\n case 'executablePath':\n return (\n <input\n style={styles.input}\n value={value.executablePath}\n spellCheck={false}\n onChange={(event) => { write('executablePath', event.target.value) }}\n />\n )\n case 'debugPortMin':\n return (\n <input\n style={styles.number}\n type=\"number\"\n min={1}\n max={65535}\n value={value.debugPortMin}\n onChange={(event) => { write('debugPortMin', Number(event.target.value)) }}\n />\n )\n case 'debugPortMax':\n return (\n <input\n style={styles.number}\n type=\"number\"\n min={1}\n max={65535}\n value={value.debugPortMax}\n onChange={(event) => { write('debugPortMax', Number(event.target.value)) }}\n />\n )\n case 'viewportWidth':\n return (\n <input\n style={styles.number}\n type=\"number\"\n min={320}\n max={3840}\n value={value.viewportWidth}\n onChange={(event) => { write('viewportWidth', Number(event.target.value)) }}\n />\n )\n case 'viewportHeight':\n return (\n <input\n style={styles.number}\n type=\"number\"\n min={240}\n max={2160}\n value={value.viewportHeight}\n onChange={(event) => { write('viewportHeight', Number(event.target.value)) }}\n />\n )\n case 'quality':\n return (\n <input\n style={styles.number}\n type=\"number\"\n min={1}\n max={100}\n value={value.quality}\n onChange={(event) => { write('quality', Number(event.target.value)) }}\n />\n )\n case 'snapshotAttributes':\n return (\n <input\n style={styles.input}\n value={value.snapshotAttributes}\n spellCheck={false}\n onChange={(event) => { write('snapshotAttributes', event.target.value) }}\n />\n )\n case 'snapshotIgnore':\n return (\n <input\n style={styles.input}\n value={value.snapshotIgnore}\n spellCheck={false}\n onChange={(event) => { write('snapshotIgnore', event.target.value) }}\n />\n )\n }\n return unreachableField(field)\n }\n\n /** One placed field as a labelled row. */\n const placedRow = (placement: FieldPlacement): ReactElement => {\n const copy = FIELD_COPY[placement.field]\n // The directory row is the one field whose meaning — and therefore whose\n // explanation — changes with another field's value.\n const hint = placement.field === 'userDataDir'\n ? profileDirHint(value.profileMode)\n : copy.hint\n return row(\n t(copy.label),\n placement.field,\n control(placement.field),\n hint === undefined ? undefined : t(hint),\n )\n }\n\n const hiddenChanges = overriddenCount(user, foldedPlacements())\n const shared = sharedCounts(status?.instances ?? [])\n\n return (\n <div style={styles.page}>\n <p style={styles.intro}>{t('settingsIntro')}</p>\n\n <div style={styles.status}>\n <div style={styles.statusText}>\n <span style={statusError !== undefined ? styles.statusFailed : undefined}>\n {statusError !== undefined\n ? t('settingsStatusUnknown').replace('{error}', statusError)\n : statusLine(status, t)}\n </span>\n {(status?.instances.length ?? 0) === 0 ? null : (\n <details>\n <summary style={styles.statusSummary}>\n {t('settingsSessionsCount').replace('{count}', String(status?.instances.length ?? 0))}\n </summary>\n {status?.instances.map(instance => (\n <div key={instance.sessionId} style={styles.statusRow}>\n <span style={styles.statusDetail} title={instance.sessionId}>\n {instanceLine(instance, t, shared.get(instance.sessionId) ?? 1)}\n </span>\n <button\n type=\"button\"\n style={styles.reset}\n disabled={closing === instance.sessionId}\n title={t('settingsSessionClose')}\n onClick={() => { closeBrowser(instance.sessionId) }}\n >\n {closing === instance.sessionId ? t('settingsSessionClosing') : t('settingsSessionClose')}\n </button>\n </div>\n ))}\n {closeError === undefined\n ? null\n : <span style={styles.statusFailed}>{t('settingsSessionCloseFailed').replace('{error}', closeError)}</span>}\n </details>\n )}\n </div>\n <button type=\"button\" style={styles.reset} onClick={refresh}>{t('settingsRefresh')}</button>\n </div>\n\n {saveError !== undefined\n ? <p style={styles.saveFailed}>{t('settingsSaveFailed').replace('{error}', saveError)}</p>\n : <p style={styles.save}>{saved ? t('settingsSaved') : ''}</p>}\n\n <div style={styles.group}>\n {placementsIn('everyday').map(placement =>\n // The directory row is the one placed field that can be absent: in\n // `temp` mode there is no directory to name.\n placement.field === 'userDataDir' && !profileDirVisible(value.profileMode) ? null : placedRow(placement))}\n </div>\n\n <details style={styles.fold}>\n <summary style={styles.foldSummary}>{advancedSummary(t, hiddenChanges)}</summary>\n <p style={styles.note}>{t('settingsAdvancedHint')}</p>\n {ADVANCED_GROUPS.map((group, index) => (\n <Block\n key={group}\n title={t(GROUP_COPY[group].title)}\n hint={t(GROUP_COPY[group].hint)}\n divided={index > 0}\n >\n {placementsIn(group).map(placement => placedRow(placement))}\n </Block>\n ))}\n </details>\n\n <p style={styles.note}>{t('settingsApplied')}</p>\n </div>\n )\n}\n","/**\n * Client half: register the mirror's tab type, its viewer body, and its copy.\n *\n * Registration follows the Sidebar's public two-stage path — the type into\n * `ctx.sidebarRightTabs`, the body into the keyed `sidebar.right.pane.tab`\n * seat under the same definition `id`. Nothing here reaches into the Sidebar's\n * store or panes.\n */\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport type { SessionIdOf } from '@deepseek-ai/dsh-client-ui-slots'\nimport { BrowserBody } from './view.tsx'\nimport { BrowserTitle } from './title.tsx'\nimport { followBrowserPages } from './reveal.ts'\nimport { followPageCloses } from './close.ts'\nimport { BrowserSettingsSection } from './settings.tsx'\nimport type { BrowserSettingsView } from './settings-layout.ts'\nimport { BROWSER_ID, browserDefinition } from './definition.ts'\nimport { BrowserGlyph } from './glyph.ts'\nimport { NS, en, zh, type DshBrowserKey } from './locales.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Viewer copy, address bar, and status lines. */\n dshBrowser: DshBrowserKey\n }\n}\n\n/** Required browser services: the tab registry, its navigation face, the slot registry, and copy. */\nexport const inject = ['slots', 'locale', 'sidebarRightTabs', 'sidebarRight']\n\n/** The settings namespace the host half registers, and this page edits. */\nconst SETTINGS_NAMESPACE = 'dsh-browser'\n\n/**\n * Client plugin body: register the dictionaries, the tab type, its body, and\n * the settings page.\n * @param ctx - client root context carrying the registries and copy.\n */\nexport function apply(ctx: ClientContext): void {\n const t = ctx.locale.bind(NS)\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-browser: dictionaries')\n ctx.effect(() => ctx.sidebarRightTabs.register(browserDefinition(\n () => t('title'),\n () => t('guideTitle'),\n () => t('guideDescription'),\n BrowserGlyph,\n )), 'dsh-browser: cdpBrowser type')\n // The pane is a session-scoped slot, so its registration factory is handed\n // the session it is rendering for — which is exactly what the viewer has to\n // name on the socket to be given that session's browser.\n ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register(\n {\n name: 'sidebar.right.pane.tab',\n key: BROWSER_ID,\n locale: NS,\n inject: (sessionId: SessionIdOf) => ({ sessionId }),\n },\n BrowserBody,\n )), 'dsh-browser: viewer body')\n // The chip's glyph, registered the same keyed way the shipped guide type does\n // it: without a registrant the strip shows the tab's captured title text alone,\n // which is how the browser got a chip with no icon.\n ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab.title', () => ctx.slots.register(\n { name: 'sidebar.right.pane.tab.title', key: BROWSER_ID },\n BrowserTitle,\n )), 'dsh-browser: viewer title')\n // The browser is the Session's, and its pages are the user's: this is what\n // keeps one sidebar tab on every page the browser holds, and no tab on a\n // page it does not.\n followBrowserPages(ctx)\n // Closing a tab is closing the page it mirrored. The kit's own removal path\n // asks this handler first, which is what makes the rule hold no matter how\n // the close arrived — the strip's ✕, a menu, or the pane itself.\n followPageCloses(ctx)\n // The page appears only while the Host serves this namespace: a deployment\n // that never loaded the plugin's host half has no configuration to edit, so\n // the page registers nothing and the tab shows no trace of it.\n ctx.inject(['configForms'], (settingsCtx) => {\n const form = settingsCtx.configForms.get<BrowserSettingsView>(SETTINGS_NAMESPACE)\n settingsCtx.effect(\n () => settingsCtx.configForms.whileServed([SETTINGS_NAMESPACE], () =>\n settingsCtx.slots.inject('settings.section', () => settingsCtx.slots.register({\n name: 'settings.section',\n id: BROWSER_ID,\n order: 46,\n label: () => t('settingsTitle'),\n inject: () => ({ form, t }),\n }, BrowserSettingsSection))),\n 'dsh-browser: settings section',\n )\n })\n}\n"],"mappings":"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"}
|