Per review feedback on #174: the hardcoded if/else in
normalizeLanguageCode required touching the function for every new
language, so a bare `ko` would miss the `ko-KR` resource #161 is about
to add and Korean users would land on English despite the fix.
Replace it with createLanguageNormalizer(resourceKeys), a factory that
derives its mapping from the resource keys i18next is configured with:
1. exact resource key -> unchanged
2. one resource for the language -> that key (ko -> ko-KR)
3. several (zh-CN / zh-TW) -> script subtag, then region
4. nothing shipped -> unchanged, fallbackLng applies
getLanguageDetectionOptions now takes the resource keys and i18n.ts
passes Object.keys(resources). Chinese disambiguation stays as the only
special case - one language, two scripts; every other mapping derives
from the index automatically.
Test expectations for the zh-TW family change from "zh-TW" to "zh-CN":
only zh-CN ships today and the factory maps to shipped keys only, so
Traditional resolves directly to Simplified instead of leaking into the
fallback chain. The rendered UI is identical either way, and the new
extensibility cases lock in the automatic disambiguation once a zh-TW
bundle registers - plus the auto-resolution of a newly registered
language like ko-KR.
28 -> 31 tests; extension suite 832/832; tsc, biome and ext:build clean.
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
Classify Chrome extension-frame access failures and provide an actionable CLI hint. Recover navigate and reload only when their initial CDP setup is denied, using browser navigation events and verifying that CDP access returns.
Preserve normal navigation, Agent Window checks, transfer error details, and network-idle semantics. Never replay an already-dispatched navigation.
Two issues prevented the detached update helper from completing:
1. Only CREATE_NO_WINDOW was set — the child cmd.exe process was still
attached to the parent console session and got killed when the
terminal closed. Add CREATE_NEW_PROCESS_GROUP to fully detach it.
2. `timeout /t 1 /nobreak` requires a console and fails silently in a
no-window context, causing the retry loop to spin with no delay.
Replace with `ping -n 2 127.0.0.1` which works in any environment.
Co-authored-by: Cursor <cursoragent@cursor.com>
- scripts/release.mjs: unified version bump + tag + push automation
- CHANGELOG.md: document all changes from 0.1.x through 0.2.0
- popup: show "CLI {ver} / Ext {ver}" instead of bare version numbers
Co-authored-by: Cursor <cursoragent@cursor.com>
Restore the lean style from PR #167 while integrating essential file
transfer documentation for upload/download. Reduces the skill from
362 lines / 26k chars to 180 lines / 9.2k chars.
A/B eval (24 browser runs, Composer 2.5 Fast, core suite) confirms
no pass-rate regression: candidate 12/12 vs baseline 10/12 (baseline
failures were a network blip and a behavioral variance, not skill
quality).
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep feat/expand-upload-download (drop mode superset) over the base
branch version for all 12 conflicting files. The drop-to-upload branch
already includes all upload/download functionality plus the new --mode
drop mechanism, overlay suppression, OOPIF coordinate handling, and
expanded error reasons.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the expanded command-reference tables from feat/upload-download
(including the new File Transfer section) over main's compact two-line
format.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Document PiP window had no close/return entry once popped out —
users could not dismiss it from inside the window. Add an onClosePip
callback and render a close button in the PiP header; clicking it
calls pipWindow.close() and the content returns to its carrier.
The observation thumbnail publisher hardcoded mediaType as "image/png"
regardless of the actual capture bytes. Reuse the existing
sniffImageMediaType helper (exported from image.ts) so the HTTP
thumbnail endpoint declares the correct Content-Type when Chromium
returns JPEG captures.
Closes#153 (Bug 2 fully; Bug 1 was fixed in #135, this covers the
remaining observation-thumbnail path).
Co-authored-by: Cursor <cursoragent@cursor.com>
The borrow confirmation overlay was easily missed because the Agent
Window stole focus and the 5-second auto-deny was too aggressive for
users who had context-switched away.
- Focus the user window proactively when a borrow request is issued so
the overlay is immediately visible even when the Agent Window was
last focused.
- Raise CONFIRMATION_TIMEOUT_MS from 5 s to 60 s, giving users enough
time to notice and respond — the OS notification with
requireInteraction: true now has a matching window of usefulness.
Co-authored-by: Cursor <cursoragent@cursor.com>