Conversation
Adds consistent loading/empty/error primitives under ui/src/components/empty and applies them to every fetching route (Home, Notes*, Documents*, Graph, MCPConsole). Addresses Block 5.2 of the production-polish roadmap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catches render errors at the Suspense fallback level and shows a sanitized state card with "Reload this view" (resets the boundary) and "Report" (opens a mailto with a truncated stack). Vitest-covered via a child that throws on render. Addresses Block 5.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends useDocumentTitle to accept optional `parts` for dynamic segments (document/note titles) and threads them through DocumentView + NoteView. All titles suffixed " — docsiq". Addresses Block 5.3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Header padding-top uses max(var(--header-pad), env(safe-area-inset-top)) and sidebar/main respect left/right/bottom insets. Covered by a Playwright iPhone-14-viewport test (Chromium project, logical 390x844 viewport) asserting paddingTop >= 16px. Addresses Block 5.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: useLastVisit returned a fresh `touch` function on every render. Home.tsx then used that `touch` in a useEffect cleanup with `touch` as the sole dependency. React re-subscribed the effect every render, running the prior cleanup (which calls `touch` → setLast → re-render) in a loop. Fix: wrap `touch` in useCallback with a stable dep list. `touch` is now reference-stable, so the Home unmount-cleanup useEffect only runs once. Regression coverage: - ui/e2e/no-console-errors.spec.ts — Playwright console capture across all five primary routes, asserts no "Maximum update depth" warnings. - ui/src/hooks/__tests__/useLastVisit.test.ts — asserts `touch` identity is stable across re-renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds @axe-core/playwright audit asserting 0 wcag2a/wcag2aa violations on /, /notes, /docs, /graph, and /mcp. Violations fixed: - button-name: project SelectTrigger in app-sidebar.tsx had no accessible name. Added aria-label="Select project". - color-contrast: primary green (oklch(0.68 0.16 152) / ~#32b364) against white --primary-foreground only reached 2.7:1. Darkened to oklch(0.52 0.16 152) → ~5.1:1, passing WCAG AA. --ring kept at original brightness since it's a focus outline, not text. Covered across all primary routes via e2e/a11y.spec.ts (Chromium project, /mcp reached via SPA history to avoid the Vite dev-server proxy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e (5.7) Adds fade/slide/pop transition factories that collapse duration to 0 when useReducedMotion() returns true, plus fadeInVariants/slideUpVariants for common mount animations. Preserves existing enterTransition/exitTransition exports for backward compat. No motion.* callsites exist in src/ today, so Step 6 wiring is vacuous — the factories are ready for future callsites. Also adds a prefers-reduced-motion CSS media query so non-Framer animations (CSS keyframes, transitions) also respect user preference. Covered by Vitest (ui/src/lib/__tests__/motion.test.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shell now captures document.activeElement when the palette opens and restores focus on close via requestAnimationFrame. Also exposes an invoker-aware openPalette callback that both the header button and the Ctrl/Cmd+K hotkey call through. Playwright coverage (ui/e2e/focus.spec.ts): - skip-link is the first Tab target and Enter moves focus to main#main - palette returns focus to the invoking search button on close - Radix dialog traps focus (20 consecutive Tabs stay inside the dialog) Addresses Block 5.8. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline <head> script reads the Zustand-persisted theme at key `docsiq-ui`
and applies `document.documentElement.classList` and `data-theme` before
React hydrates, eliminating FOUC on dark-theme first paint.
The script is defensive (try/catch silently falls through when localStorage
is unavailable, e.g. privacy mode) and kept small (~40 lines). Note: when
Block 2's CSP rolls out with `script-src` restrictions, the SHA-256 hash of
this inline script must be added to the allowlist (computable via
`node -e "const crypto=require('crypto');..."`). Left as a Block-2 follow-up.
Playwright coverage (ui/e2e/theme-flash.spec.ts):
- dark: .dark class applied + data-theme="dark"
- light: no .dark class
- system: resolves via prefers-color-scheme
Smoke suite also passes unchanged.
Addresses Block 5.9.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…l-screen palette (5.10) Enforces 44x44 CSS-pixel minimum on header buttons under 480px, makes the command palette fill the viewport on small screens, and wraps the DocumentsList table in .table-scroll so horizontal overflow stays contained (not pushed to body). Playwright coverage at 375x812 (ui/e2e/mobile.spec.ts): - sidebar collapses/hides - all header buttons >= 44x44 - command palette >= 320px wide (fills viewport via CSS) - /docs body does not horizontally overflow Addresses Block 5.10. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Block 5 of the docsiq production-polish roadmap — ten items making the React 19 + TypeScript + Vite SPA production-grade on desktop and iOS Safari.
All changes live in
ui/. No Go code, no CI workflow touched.Tasks landed
EmptyState,LoadingSkeleton,ErrorState) underui/src/components/empty/, threaded into every fetching route (Home, Notes*, Documents*, Graph, MCPConsole).RouteBoundarywrapping<Suspense>inApp.tsx. Catches render errors, shows sanitized card with Reload/Report (mailto with truncated stack).document.title— extendeduseDocumentTitlewith optionalpartsthreading, wired throughNoteView+DocumentView.env(safe-area-inset-*)); Playwright iPhone-14 viewport test.useLastVisitreturned a freshtoucheach render. Home's cleanup useEffect depended ontouch, causing infinite re-renders. Fixed withuseCallback. Playwright console-capture regression across all 5 routes.@axe-core/playwright@^4.11. Violations fixed: SelectTriggeraria-labelin app-sidebar; primary green darkened from oklch(0.68...) to oklch(0.52...) for 5.1:1 contrast (was 2.7:1). 0 wcag2a/wcag2aa violations across all 5 routes.fadeTransition/slideTransition/popTransitionfactories + CSSprefers-reduced-motionglobal gate. Nomotion.*callsites exist in src/ today, so factories ship ready-for-use. Legacy exports preserved.<head>script reads Zustand-persisted theme at keydocsiq-uiand applies.dark+data-themebefore React hydrates. Playwright covers dark/light/system..table-scrollwrapper on DocumentsList table. 4 Playwright assertions at 375×812.Verification
npm run typecheck— cleannpm test -- --run— 25 files, 81 tests passing (up from 18/54 baseline)npm run build— succeedsNotable deviations
/mcproute: The Vite dev-server proxy rule for/mcpintercepts document requests, so the axe + no-console-errors specs navigate to/mcpvia SPAhistory.pushStateinstead ofpage.goto("/mcp"). Noted inline.devices["iPhone 14"](which pins webkit). The CSS is engine-agnostic so the test value is preserved.eslintbaseline:npm run lintis pre-broken on main (missingeslint.config.jsafter ESLint v9 migration). Not in Block 5 scope.<motion.*>callsites exist insrc/, so the motion-preset wiring is prep-only. Factories + CSS gate both ship.Follow-ups
markdown-it/shiki, tree-shake framer-motion).eslint.config.jsmigration (ESLint v9) — pre-broken at main.🤖 Generated with Claude Code