Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4d60463
Client-side migration: replace server APIs with local alternatives
wikirby Mar 6, 2026
ba9c05e
Inline external CSS at capture time for full page screenshot renderer
wikirby Mar 6, 2026
fe8d38a
Iframe scroll blocking for full page screenshot renderer
wikirby Mar 7, 2026
ac0c6a8
Fix scroll stall causing repeated screenshots, add content height cro…
wikirby Mar 7, 2026
5a20adc
Harden renderer interaction blocking and fix cleanup race condition
wikirby Mar 7, 2026
7a986b3
Replace per-event interaction blocking with transparent overlay
wikirby Mar 7, 2026
16b8df5
Move image stitching from helper into renderer for reduced storage usage
wikirby Mar 7, 2026
4644c6c
Update client-side migration docs for renderer-side stitching
wikirby Mar 7, 2026
79cce20
Harden renderer: pointer-events blocking, resize lock, disconnect han…
wikirby Mar 7, 2026
6d7ea31
Fix offscreen communicator context bug, strip script preloads, re-foc…
wikirby Mar 10, 2026
a4aaa0d
Unified window with sidebar: progress panel, sidebar cropping, i18n, …
wikirby Mar 10, 2026
8450a87
V2 unified window: mode selection, article/bookmark, section picker, …
wikirby Mar 10, 2026
e11b5b7
V2 unified window: sign-out, section refresh, region capture, post-si…
wikirby Mar 11, 2026
61506de
Tech debt cleanup: session storage, promise leak, Readability, region…
wikirby Mar 11, 2026
b86fc69
Self-contained sign-in: renderer handles auth directly, no clipperInject
wikirby Mar 11, 2026
c5d9f79
Add telemetry, token refresh before save, update documentation
wikirby Mar 11, 2026
ba106c7
contentCaptureInject: full DomUtils DOM cleaning pipeline, no imports
wikirby Mar 11, 2026
fa6ab8a
Master-compatible DOM extraction, sticky fix, remove stylesheet caching
wikirby Mar 11, 2026
c8e817b
Update docs: resolve sticky duplication, remove stale stylesheet refs
wikirby Mar 13, 2026
8f2f8e4
Article mode ONML cleanup, OneNote page styling, known limitations
wikirby Mar 19, 2026
0063ce5
Preserve body font-size from original page during DOM capture
wikirby Mar 19, 2026
6efd2f6
Add feedback link, session USID, error diagnostics copy button
wikirby Mar 20, 2026
c30de2f
i18n, accessibility, contrast fixes for renderer UI
wikirby Mar 24, 2026
1f1ca8e
Add region overlay instruction bar, mode button tooltips, notebook re…
wikirby Mar 25, 2026
01e8a2f
Bump to 3.11.0, fix prod build minification, fix capture progress tex…
wikirby Mar 26, 2026
996c142
Add charset utf-8 to renderer.html, update NVDA screen reader docs
wikirby Mar 26, 2026
04e5ccd
Revert mode buttons from radio to toolbar with aria-pressed
wikirby Mar 26, 2026
994946f
Article preview header: highlighter, font toggle, size controls, save…
wikirby Mar 26, 2026
0e2d2fe
Add custom highlight cursor on highlighter toggle
wikirby Mar 26, 2026
db621ae
Separate success banner from Clip button, add View in OneNote button
wikirby Mar 26, 2026
74683e3
Hierarchical section picker, success banner, client-side save timeout…
wikirby Mar 26, 2026
9539dd9
Fix lint errors, add SW keepalive and 5-minute inactivity auto-close
wikirby Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

-------------------------------------------

@mozilla/readability

Copyright (c) 2010 Arc90 Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
254 changes: 254 additions & 0 deletions docs/client-side-migration.md

Large diffs are not rendered by default.

184 changes: 184 additions & 0 deletions docs/i18n-a11y-contrast-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Renderer UI: i18n, Accessibility & Contrast Fixes

## Context

The new renderer-based unified window (V3) replaces the old Mithril-based injected sidebar. While the new UI uses better semantic HTML (`<button>`, `<textarea>`, `<label>`), it regressed in three areas compared to the old UI:

1. **i18n** — ~15 hardcoded English strings that the old UI localized via `Localization.getLocalizedString()`
2. **Accessibility** — Lost ARIA state attributes, keyboard navigation, focus outlines, and aria-live regions
3. **Contrast** — Error text color `#ff6b6b` fails WCAG AA; region button border fails non-text contrast

The old UI's patterns are the blueprint — most string keys already exist in `strings.json`, and the ARIA patterns are well-documented in the old components.

### How i18n works in this extension

Strings are fetched from `https://www.onenote.com/strings?ids=WebClipper.&locale={locale}` at startup by `extensionBase.ts`, stored in `localStorage.locStrings`. The renderer reads them via `loc(key, fallback)`. The `strings.json` file in the repo is the **English fallback only** — actual translations for 60 locales come from the server. New keys added to `strings.json` will only have English until the server is updated, so **reuse existing keys wherever possible**.

### How locale is detected

`extensionBase.ts` line 133: `navigator.language || navigator.userLanguage` → stored in `localStorage.locale`. A user override is also supported via `localStorage.displayLocaleOverride`. The old UI only set `<html lang="en">` statically — never dynamically. RTL was handled by loading separate CSS files (`clipper-rtl.css`), not via `lang`/`dir` attributes.

## Files Modified

| File | Changes |
|------|---------|
| `src/renderer.html` | `lang` attr, ARIA roles/attributes, `for` associations, aria-live region |
| `src/scripts/renderer.ts` | Wire `loc()` to all remaining strings, dynamic `lang` attr, ARIA state management, keyboard nav, focus management, aria-live announcements |
| `src/styles/renderer.less` | Focus outlines, error color fix, region button contrast, sr-only class, high-contrast media query |

---

## Phase 1: i18n (mirror old `Localization.getLocalizedString()` via existing `loc()`)

### 1a. Wire sign-in panel to `loc()` in renderer.ts

Sign-in panel HTML strings (`signin-description`, `signin-msa-btn`, `signin-orgid-btn`, `signin-progress`) were never replaced by JS. Now wired to existing keys:

- `WebClipper.Label.SignInDescription` → sign-in description
- `WebClipper.Action.SigninMsa` → MSA button
- `WebClipper.Action.SigninOrgId` → OrgId button
- "Signing in..." kept as hardcoded English (no existing key, brief transient state)

### 1b. Wire field labels to `loc()`

- Note label → `WebClipper.Label.Annotation` ("Note")
- Save to label → `WebClipper.Label.ClipLocation` ("Location")
- Source label → kept as hardcoded "Source" (no old UI equivalent, new element)
- Title label → kept as hardcoded "Title" (old UI had no visible label, used placeholder only)

### 1c. Wire remaining hardcoded strings to `loc()`

| String | Key | Key status |
|--------|-----|-----------|
| `"Capture complete"` | — | **REMOVED** — dead code, never referenced |
| `"No notebooks available"` | `WebClipper.SectionPicker.NoNotebooksFound` | EXISTS (60 locales) |
| `"Error loading notebooks"` | `WebClipper.SectionPicker.NotebookLoadFailureMessage` | EXISTS (60 locales) |
| `"Loading article..."` | `WebClipper.Preview.LoadingMessage` | EXISTS (60 locales) |
| `"Article content not available..."` | `WebClipper.Preview.NoContentFound` | EXISTS (60 locales) |
| `"Unknown error"` | — | kept as-is (technical fallback) |
| `"Sign-in failed..."` | `WebClipper.Error.SignInUnsuccessful` | EXISTS (60 locales) |

### 1d. New keys in strings.json — ZERO

All meaningful strings wired to existing server-translated keys. Four strings remain English-only as acceptable fallbacks (Title, Source, Signing in..., Copy diagnostics aria-label).

---

## Phase 2: Contrast Fixes

### 2a. Error text color (CRITICAL — 3.8:1 → 5.3:1)

`.signin-error` color: `#ff6b6b` → `#ff9999` (~5.3:1 on `#56197c`, passes WCAG AA)

### 2b. Region add-button border (1.7:1 → 3.4:1)

Border: `#bbb` → `#999` (~3.4:1 on `#f3f2f1`, passes SC 1.4.11)
Text: `#666` → `#555` (~5.9:1, improvement)

### 2c. Focus outlines (mirroring old `@FocusOnPurpleBackground: #f8f8f8`)

- `#sidebar` interactive elements: `outline: solid 1px #f8f8f8 !important; outline-offset: 1px`
- Sign-in buttons: `outline-offset: 2px`
- High contrast mode: `outline: solid 2px Highlight !important; outline-offset: 2px !important`

---

## Phase 3: Accessibility — ARIA & Keyboard

### 3a. `<html lang>` attribute (WCAG 3.1.1 Level A)

Static `lang="en"` in HTML + dynamic override in JS reading `localStorage.locale` (or `displayLocaleOverride`). Converts `_` to `-` for BCP 47 (e.g., `zh_CN` → `zh-CN`).

### 3b. Mode buttons — ARIA state

- Container: `role="toolbar"` with localized `aria-label`
- Buttons: standard `<button>` elements with `aria-pressed` (toggle button pattern)
- Reverted from `role="radio"` / radiogroup — buttons are more natural for mode selection

### 3c. Mode buttons — arrow key navigation

Arrow Up/Down/Left/Right + Home/End navigation between mode buttons. Mirrors old `enableAriaInvoke()` from `componentBase.ts`.

### 3d. Section picker — ARIA combobox

- Trigger: `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded`
- List items: `role="option"`, `aria-selected`
- Escape to close, arrow keys to navigate

### 3e. Label `for` associations

- `<label for="title-field">` and `<label for="note-field">`
- `aria-labelledby` on source-url and section-selected (non-input elements)

### 3f. aria-live regions

- `<div id="aria-status" class="sr-only" aria-live="polite" aria-atomic="true">`
- `announceToScreenReader()` helper for: capture start/complete, mode change, save start/success/error, sign-in error

### 3g. Sign-in focus management

Focus first sign-in button on overlay show; focus first mode button on overlay hide.

### 3h. Copy diagnostics button

`aria-label="Copy diagnostic information"` (hardcoded English — technical label).

### 3i. Sign-out disabled state

Replace `pointer-events: none` + opacity with `aria-disabled="true"` + `tabindex="-1"` (accessible to keyboard/screen readers).

---

## RTL Support Assessment (Deferred — Separate Effort)

**How old UI handled RTL:**
- `localeSpecificTasks.ts` calls `Rtl.isRtl(locale)` (checks `ar, fa, he, sd, ug, ur`)
- Loads `clipper-rtl.css` / `sectionPicker-rtl.css` instead of LTR versions
- `styledFrameFactory.ts` flips iframe position (`left: 0` instead of `right: 0`)

**Locale override:** No UI exists for switching locale. `localStorage.displayLocaleOverride` is a developer/testing mechanism only (set via console).

**What RTL would need for the renderer (estimated ~50-80 LESS lines + testing):**
1. Detect RTL locale and set `dir="rtl"` on `<html>`
2. Convert `renderer.less` to CSS logical properties (`margin-inline-start/end`, etc.)
3. Flip: sidebar position, section picker arrow, user-info alignment, feedback icon margin
4. Test with at least one RTL locale (ar or he)

**Why defer:** RTL affects layout fundamentals. Best done as a dedicated pass.

---

## Implementation Order

1. **Phase 2a** — Error contrast fix (LESS)
2. **Phase 1a–1c** — i18n wiring (renderer.ts only, reuse existing keys)
3. **Phase 2b–2c** — Remaining contrast + focus outlines (LESS)
4. **Phase 3a** — `<html lang>` + dynamic locale (HTML + TS)
5. **Phase 3e** — Label `for` associations (HTML)
6. **Phase 3b–3c** — Mode button ARIA + arrow keys (TS)
7. **Phase 3d** — Section picker ARIA (HTML + TS)
8. **Phase 3f** — aria-live regions (HTML + LESS + TS)
9. **Phase 3g–3i** — Focus management, copy button label, signout disabled state (TS)

---

## Verification

1. **Build**: `npm run build` — check for TS compilation errors
2. **Edge target**: Verify `renderer.js` and `renderer.css` in target output
3. **Manual testing in Edge** (verified):
- Sign-in panel: localized text appears
- Mode buttons: arrow key navigation, `aria-checked` updates in devtools
- Section picker: `aria-expanded` toggles, Escape closes, arrow keys navigate items
- Save error: `#ff9999` error text readable
- Tab order: mode buttons → title → note → section → Clip → Cancel → feedback → sign out (wraps)
- Focus outlines: `2px solid #f8f8f8` visible on all sidebar controls
- Focus management: Cancel focused during capture → Full Page button after capture → sign-in button on overlay
4. **No functional regressions**: Capture, save, region, article, bookmark modes all work
5. **Screen reader testing** (verified with NVDA + Edge):
- ARIA roles, states, and aria-live announcements are implemented and working
- Accessibility tree verified correct via `edge://accessibility` — all nodes present with proper roles
- NVDA reads sidebar controls, mode buttons, section picker, and aria-live announcements
- Blur handler was removed to avoid conflicting with screen reader focus management
- Keydown handler allows navigation keys (arrows, Tab, Escape, Home/End, PageUp/PageDown) and modifier combos to pass through for screen reader compatibility
- **Note**: Previous testing on a stale devbox showed Edge not activating accessibility API flags for extension popup windows. A devbox reboot resolved this — NVDA works correctly with the renderer window
Loading
Loading