diff --git a/CHANGELOG.md b/CHANGELOG.md index 8880294..4a439c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,15 +11,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **Protected-term restoration corrupted correct CJK prose.** Across ko/ja/zh-CN/zh-TW, common words were mapped as brand "wrong-forms" (클라우드→Claude, 인류→Anthropic, 企业→Enterprise, …), so `restoreProtectedTerms` rewrote correct translations into wrong English (e.g. "클라우드 컴퓨팅" → "Claude 컴퓨팅"). Removed the ambiguous common-word wrong-forms; intended brand restoration (클로드→Claude, etc.) still works. (#172) - **AI tutor spinner could hang forever** when the Puter bridge wasn't ready — `chatStream` resolved to a discarded error string instead of throwing, so the caller never rendered the error+retry. It now rejects on bridge-not-ready. (#174) - **Gemini verify could render a non-translation as the translation.** A short affirmation ("Okay", "OK입니다", "OK?") or whitespace reply fell through and was cached/shown in place of the correct translation (an empty reply blanked the element). Added a length guard so anything far shorter than the source keeps the Google translation. (#175, #176) +- **The AI-Tutor floating button rendered as a blank circle.** The host page's SVG sizing reset collapsed the chat-bubble icon to 0px wide (presentation attributes have specificity 0). Pinned an explicit icon size; the FAB now also lives in the shadow root (see Added), which makes this whole leak class impossible. (#182, #183) +- **The flashcards "Reset Progress" button rendered in Skilljar's blue.** `.si18n-history-clear` had no base rule, so the host's `button { background: #0164cc }` leaked through. Grouped it with the sibling header icon-button rules (transparent, brand-neutral). (#185) +- **Translated brand terms could render as "Claude(Claude)".** Google Translate appends an English gloss in parens for proper nouns; protected-term restoration then turned the transliteration back to English, duplicating it. Exact self-duplicates now collapse via a backreference (ASCII + fullwidth parens); legitimate parentheticals and code are untouched. (#187) +- **Tutor suggestion chips were misaligned with the greeting bubble** — the chip row sat flush to the messages gutter while the bubble is indented past the avatar. Indented the chips by the same 36px (logical property, RTL-safe). (#191) ### Added - **Locale cross-contamination guard** (`scripts/check-locale-contamination.js`, `npm run check:locales`, wired into CI). Fails when any locale shares >8% of its long strings with another — the bug class the key/shape checks (`check-i18n`, `check-dict-coverage`) cannot see because they only verify structure, not language. Clean locales sit at ≤2.1%; the contaminated Italian file was 51%. (#166) - **`skillbridge-academy-terms` companion Claude Code plugin** (`claude-plugin/`) re-exposing the curated Academy terminology dictionary for Claude Code / Cowork. Its data is generated from `src/data/*.json` and kept in sync by CI (`npm run check:plugin`). (#170) +- **Shadow-root style isolation for injected overlay UI.** The floating tutor button, the tutor sidebar, and the reading-aid TOC now live in an open shadow root (`#skillbridge-root`) the host page's stylesheet cannot reach — making the host-CSS-leak bug class (the FAB icon and reset-button bugs above) structurally impossible. `content.css` is fetched once, its ancestor theme selectors are rewritten to `:host(...)` form, and the result is adopted into the root; dark/locale state is mirrored onto the shadow host. Header controls (language selector, dark toggle) stay in the light DOM **by design** — they borrow Skilljar's own header classes to blend in. A new E2E suite injects hostile host CSS and proves it cannot reach the shadowed UI. (#188, #189, #190, #192) ### Changed - **build-plugin generator** now reads `FLASHCARD_COURSE_MAP` via the same evaluation the sibling checkers use (was a fragile regex parse of the source); `--check` also detects orphan output files. (#171) - **jest** no longer warns about a Haste name collision from `dist/` builds (`modulePathIgnorePatterns`). (#179) - **Privacy policy** permission table realigned with the manifest (removed the stale `tabs` entry, disclosed `api.github.com`); language count corrected to 32. (#180) +- **README telemetry wording aligned with reality** — nothing is collected, not even opt-in error reports (the old line implied an opt-in error reporter exists; none is implemented). (#186) +- **`npm run capture:store` repoints at `@starter-series/shotkit`**, the extracted store-asset generator, after the in-repo harness copy was removed. (#192) ## [3.5.39] - 2026-06-01 diff --git a/src/content/header-controls.js b/src/content/header-controls.js index 799db2d..b875700 100644 --- a/src/content/header-controls.js +++ b/src/content/header-controls.js @@ -71,6 +71,15 @@ const linksContainer = headerRight.querySelector(SKILLJAR_SELECTORS.headerLinks); if (!linksContainer) return; + // Header controls are *blend-in* components and stay in the light DOM by + // design (decided during the 2026-06 shadow migration, #188–#192): this + // wrapper deliberately borrows Skilljar's own `headerheight align-vertical` + // classes so the selector sits exactly like a native header item. Shadow- + // isolating it would cut off those borrowed styles and force us to + // replicate the host header's metrics by hand — brittle against Skilljar + // header changes, for no benefit (no host-CSS leak ever observed here). + // Overlay components with their own visual identity (FAB, sidebar, TOC) + // are the ones that live in the #skillbridge-root shadow root. const wrapper = document.createElement('div'); wrapper.id = 'si18n-header-lang'; wrapper.className = 'headerheight align-vertical';