From f25ed61c6248426495a43dc2e89a8df92fe81025 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Mar 2026 00:17:19 +0000 Subject: [PATCH] Add BACKLOG.md with prioritized improvement tasks for future agents 10 actionable tasks from comprehensive code analysis, covering error handling, accessibility, security, UX, and code quality improvements. https://claude.ai/code/session_01Xx3WiQTh15p986kaBy8Ja4 --- BACKLOG.md | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 BACKLOG.md diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..4d72dbc --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,244 @@ +# BACKLOG.md — Improvement Tasks for Future Agents + +This document captures prioritized, actionable improvement tasks identified +through a comprehensive code analysis. Each task includes the problem, affected +files/lines, and a concrete implementation plan. + +**Ground rules for agents working on these tasks:** +- Run `node .claude/hooks/run-tests.js` before and after every change +- Add tests in `tests.html` for any new logic +- Keep the zero-dependency, no-build-step constraint +- Do not add npm packages or bundlers +- All JS must work without transpilation (no JSX, no ES modules) + +--- + +## Task 1 — Add React Error Boundary and Crash Recovery + +**Priority:** HIGH | **Effort:** Low | **Category:** Reliability + +**Problem:** No error boundary exists. If any React component throws, the entire +app goes blank with no recovery path. Users must hard-refresh and may not +understand what happened. + +**Files:** +- `index.html` — wrap App component (around line 1295) + +**Implementation:** +1. Add an `ErrorBoundary` class component before the App definition: + ```js + class ErrorBoundary extends React.Component { + constructor(props) { super(props); this.state = { hasError: false, error: null }; } + static getDerivedStateFromError(error) { return { hasError: true, error: error }; } + componentDidCatch(error, info) { console.error('App crash:', error, info); } + render() { + if (this.state.hasError) { + return React.createElement('div', { style: { padding: 40, textAlign: 'center' } }, + React.createElement('h2', null, 'Something went wrong'), + React.createElement('p', null, this.state.error && this.state.error.message), + React.createElement('button', { onClick: function() { location.reload(); } }, 'Reload'), + React.createElement('button', { onClick: function() { localStorage.clear(); location.reload(); } }, 'Reset all data') + ); + } + return this.props.children; + } + } + ``` +2. Wrap `` in `` in the `ReactDOM.createRoot` call +3. Add a test: render ErrorBoundary with a child that throws, assert fallback UI appears + +--- + +## Task 2 — Handle localStorage Quota/Disabled Errors + +**Priority:** HIGH | **Effort:** Low | **Category:** Data Safety + +**Problem:** `lsSave` (line ~309-312) and `srsSave` (line ~472-476) silently +catch exceptions. If localStorage is full or disabled, users lose progress with +no warning. + +**Files:** +- `index.html` — `lsSave`, `srsSave`, and all `localStorage.setItem` calls + +**Implementation:** +1. Create a `safeSave(key, value)` helper that wraps `localStorage.setItem` in + try/catch and returns `{ ok: boolean, error: string|null }` +2. On failure, show a non-blocking notification bar at the top of the page: + "Unable to save progress — storage may be full or disabled" +3. Add a `storageAvailable()` check at app startup; show a persistent warning + if localStorage is not available +4. Replace all direct `localStorage.setItem` calls with `safeSave` +5. Add test: mock `localStorage.setItem` to throw, verify `safeSave` returns error + +--- + +## Task 3 — Add Accessibility Attributes + +**Priority:** HIGH | **Effort:** Medium | **Category:** Accessibility + +**Problem:** No `aria-label`, `role`, or `tabindex` attributes on interactive +elements. Screen reader users cannot navigate the app. + +**Files:** +- `index.html` — all React component `createElement` calls + +**Implementation (incremental — can be split across multiple PRs):** +1. **Speak buttons:** Add `aria-label="Listen to pronunciation"` to all TTS buttons +2. **Quiz buttons:** Add `role="button"` and `aria-label` with the answer text +3. **Feedback messages:** Add `aria-live="polite"` to exercise result text +4. **Navigation:** Add `role="navigation"` to header, `role="main"` to content area +5. **Review cards:** Make card flippable via Enter/Space key (add `tabIndex={0}` + and `onKeyDown` handler) +6. **Overview calendar:** Add `aria-label` to day cells with day number and status +7. Verify with a screen reader or axe-core + +--- + +## Task 4 — Sanitize SVG in dangerouslySetInnerHTML + +**Priority:** MEDIUM | **Effort:** Low | **Category:** Security + +**Problem:** Line ~579 uses `dangerouslySetInnerHTML` for stroke order SVGs +fetched via `fetch()`. While currently fetched from a known source, this is a +latent XSS vector. + +**Files:** +- `index.html` — `CharCard` component (line ~579), `loadStrokeOrderSvg` (line ~343) + +**Implementation:** +1. Add a `sanitizeSvg(raw)` function that: + - Parses with DOMParser + - Removes `