The extension ships en-US and zh-CN resources, but only en-US was ever
selected: every other browser language fell through to fallbackLng
("zh-CN"), so users on en, en-GB, en-CN, en-AU, ja-JP, fr-FR saw Chinese.
Match on the BCP 47 language subtag and normalise it to a resource key:
en* -> en-US
zh-Hans* / zh-CN / zh-SG / zh -> zh-CN
zh-Hant* / zh-TW / zh-HK / zh-MO -> zh-TW
anything else -> unchanged (fallbackLng default: en-US)
Simplified vs Traditional is decided by the script subtag (Hans/Hant) when
present, then by the region subtag, since Chrome reports zh-CN / zh-TW
rather than script subtags. The zh-TW branch is written now so adding a
Traditional translation later needs no code change.
chrome.i18n.getUILanguage() is now preferred over navigator.language, which
is unreliable on the chrome-extension:// origin the popup runs on.
Adds a locale-resolution regression matrix (28 cases).
Closes#168
Register bundled browser instructions at agent scope, prioritize foreground commands over thumbnail capture, and unify cancellation and session cleanup.
- @browser-skill/ui and @remixicon/react move to devDependencies (the client
bundle inlines both; the host face never imports them — verified).
- Drop private, add license: MIT, ship the repo-root MIT LICENSE in files.
- peerDependencies: add the runtime-required @deepseek-ai/schemastery, and
the previously dangling meta entries @deepseek-ai/dsh-attachment and
@deepseek-ai/dsh-llm (optional, matching actual type-level use).
- prepack runs the full build; npm pack now yields exactly 7 files
(lib/index.mjs, lib/index.d.mts, lib/client.cjs, cordis.patch.yml,
README.md, LICENSE, package.json).
- publint clean: the client face ships as lib/client.cjs (CJS extension in
a type:module package); add lint:publint script. Built declarations pass a
NodeNext consumer typecheck (strict, skipLibCheck false).
The previous pass aligned the observation overlay with the dsh shell; the
product call is the opposite direction — the floating card should read as a
BrowserSkill surface, not a shell-native widget.
- Reuse @browser-skill/ui directly (Button, cn); status dots spec'd after the
extension popup's ConnectionStatusIndicator; Remix icons for
interrupt/pin/warn/pop-out.
- Compile the BSK tailwind utility sheet scoped under the .bsk-obs root class
(scripts/build-client-css.mjs + postcss-prefix-selector) and ship the oklch
design tokens on the same scope, so nothing leaks into the host shell and
the shell theme cannot bleed back. tsdown injects .nomodule.css verbatim
(minified, unhashed) alongside the hashed CSS modules.
- BSK ships no shared tooltip, so the one-time interrupt semantics hint is a
card-spec bubble (bg-card/border/12px radius), retired after first use.
- Tests: pin react/react-dom to this package's 18.x copies in vitest — the ui
package sources peer on react ^19 and would otherwise emit react-19
(transitional) elements that the react-18 renderer rejects. Production is
unaffected (react stays external, provided by the shell).
- stylelint: ignore the generated bsk-ui.nomodule.css.
Client half of the PiP observation window, wired to the host
ObservationService over the /bsk-observation HTTP+SSE seam:
- ObservationClientStore: initial state fetch + SSE increments +
on-demand thumbnail blob loading (session-authorized readAttachment,
same path as the toolview) + interrupt POST. All I/O injectable.
- ObservationOverlay registered into the shell.overlay list seat (the
sanctioned frame-wide surface; root is off-limits). Hidden with no
owned sessions; appears on first start; vanishes when all stop.
- Focus view: status row (green/grey/red dot + session + action + mm:ss
ticking), breathing thumbnail (new frames fade in, failures keep the
last frame with a warning badge, pre-navigate placeholder), and the
action area.
- Interrupt: single click, no confirm; greys into 'Interrupting…' and
back; disabled when nothing is in flight; one-time tooltip explains
the semantics (stops the current action only; the run continues).
- Card: drag-move via the header, corner-handle resize (min 240x180,
max 80% viewport), both clamped and kept for the page lifetime;
collapsible status capsule.
- PiP: Pop out (user gesture) opens Document PiP with the card's
current size, portals the same content in, and clones the document's
style nodes; pagehide falls back to the card with state intact;
unsupported browsers hide the button; nothing auto-pops.
Tests (+17, 89 total): store (fetch/SSE apply/malformed frames/
thumbnail lifecycle/interrupt wire/stop cleanup) and overlay (hidden->
visible lifecycle, status row, thumbnail via loader, interrupt
disabled/active/hint-once, capsule, resize clamps, move clamps, PiP
unsupported, PiP pop/fallback). Styling follows web-styling (dsw-alias
tokens, CSS Modules, focus-visible, reduced-motion).
Make the package dual-face: a dsh.client declaration (platform 'web')
plus an exports["./client"] bundle built in dsh's closure-factory shape
(window.__ModuleLoader__.load handoff, platform modules external,
everything else inlined, CSS Modules via lightningcss).
The client registers a keyed 'tool.call.toolview' view for
browser_screenshot that keeps the terminal block (command + output) and,
when the settled result carries an image block, resolves the durable
attachment through the client session's authorized readAttachment RPC
and renders it with the shared MessageImage thumbnail/lightbox atoms.
Path-only results render unchanged; every other browser_* tool keeps
the stock terminal card.
Tests: view-model derivation (running/ok/error, image vs path-only),
component rendering through @testing-library/react + happy-dom, and the
registration key. biome.json now excludes the package's lib/ build
output (same treatment as dist/).
Add a new monorepo package that registers BrowserSkill (bsk) browser
automation as model-visible tools in DeepSeek Harness (dsh):
- 11 tools mapping to bsk CLI --json commands: session start/stop/list,
navigate, snapshot, observe, click, fill, press, screenshot, emulate
- multi-session support: optional session arg with a current-session
pointer, session id echoed in every result, configurable concurrency
cap (default 5), and dispose-time cleanup of plugin-started sessions
- cancellation: exec.signal aborts kill the underlying bsk child process
- terminal-style UI cards (command line as the call card, output as the
result card); screenshots inline the PNG through the host attachment
store when the model route accepts image input, else return the path
- config: bskPath / defaultTimeoutMs / maxSessions via Schemastery schema
- install guidance when the bsk binary is missing; bsk JSON error
envelopes (code/message/hint) surfaced to the model
- unit tests mock the bsk runner (no real browser required); the root
lint script now also runs the package's typecheck + tests so the
existing CI frontend job covers the new package (a dedicated CI job
diff is provided in the PR; the pushing token lacks workflow scope)