Skip to content

chore(web): apply react-doctor cleanup#3146

Open
jeanduplessis wants to merge 2 commits intomainfrom
chore/web-react-doctor-cleanups
Open

chore(web): apply react-doctor cleanup#3146
jeanduplessis wants to merge 2 commits intomainfrom
chore/web-react-doctor-cleanups

Conversation

@jeanduplessis
Copy link
Copy Markdown
Contributor

@jeanduplessis jeanduplessis commented May 9, 2026

Summary

Applies low-risk React Doctor cleanup across touched web app surfaces and adds the local React Doctor skill.

Why this change is needed

React Doctor identified many small correctness, accessibility, performance, and design hygiene issues in apps/web. This change addresses the safe mechanical items while leaving higher-risk architectural findings for focused follow-ups.

How this is addressed

  • Adds the repo-local react-doctor skill guidance so future React cleanup work can use the same workflow.
  • Cleans up safe React patterns such as eager state initializers, stale state updates, state-only hover behavior, timeout cleanup, and in-place array sorting.
  • Improves low-risk UI/accessibility details including associated labels, semantic color tokens, typographic ellipses, and Tailwind size-* utilities.
  • Hoists or caches repeated formatter construction where doing so is mechanical and preserves behavior.
  • Leaves higher-risk items, such as hydration-time formatting, component decomposition, effect architecture, metadata, and derived state refactors, for separate targeted work.
Before After
cmux 2026-05-09 08 37 26 cmux 2026-05-09 09 26 09

Human Verification

  • Ran React Doctor in diff mode after cleanup. The changed-files score is 90/100, with remaining warnings limited to larger or higher-risk follow-ups.

Reviewer Notes

Human Reviewer Flags

  • Includes .agents/skills/react-doctor/SKILL.md as requested, adding local agent workflow guidance.
  • Remaining React Doctor warnings are intentionally not all fixed here because several require behavior or architecture decisions.

Code Reviewer Agent

Code Reviewer Notes
  • The cleanup is intentionally mechanical and broad, mostly touching existing behavior-preserving patterns.
  • Timeout cleanup changes preserve current polling/redirect behavior while avoiding orphaned timers.
  • Currency/date formatter changes cache formatter instances without changing displayed formats.
  • Accessibility changes associate labels with existing inputs and avoid changing form behavior.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 9, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

This is a clean mechanical cleanup PR. All changes are low-risk and behavior-preserving. The incremental commits continue in the same vein as the initial batch.

Notable Improvements — incremental commits (click to expand)

TypingIndicator.tsx (both cloud-agent and cloud-agent-next):
Replaced animate-bounce + hardcoded bg-gray-400 dots with a custom animate-typing-dot animation using semantic bg-muted-foreground. The new animation is defined in globals.css with a prefers-reduced-motion variant that removes the translate transform — correct accessibility handling.

command.tsxcmdk-input-wrapperdata-cmdk-input-wrapper:
The cmdk library changed the attribute from a bare cmdk-input-wrapper prop to a data-cmdk-input-wrapper data attribute. This update is applied consistently: the wrapper <div> uses data-cmdk-input-wrapper="", the CSS selector in CommandDialog targets [data-cmdk-input-wrapper], and NewSessionPanel.tsx updates its own inline selector to match. All three sites are in sync.

ProviderCard.tsx — keyboard accessibility:
Added role="button", tabIndex={0}, and onKeyDown to the provider header <div>, making it keyboard-navigable. The handleProviderKeyDown guard correctly skips the checkbox subtree so Space on the checkbox doesn't also toggle expansion.

CondensedProviderAndModelsList.tsx<div onClick><button type="button">:
Clickable elements promoting default model selection are now semantic <button> elements, which are natively focusable and keyboard-accessible.

AuditLogsFilters.tsx — scoped CSS:
Replaced <style jsx> (Next.js JSX styles) with standard <style> and scoped the date-picker rules under .audit-logs-filters to avoid leaking styles globally. This is the correct approach.

ModelCard.tsx — redundant e.stopPropagation() removed:
The onClick={e => e.stopPropagation()} on the checkbox wrapper <div> was removed. Since the checkbox now uses data-model-checkbox (mirroring the pattern in ProviderCard), and click bubbling to the card is handled appropriately, this is safe.

Notable Improvements — initial commits (click to expand)

Timeout cleanup fixes (ChannelPairingStep.tsx, vscode-marketplace/page.tsx, useImageUpload.ts):
All setTimeout IDs are now correctly captured and cleared in effect cleanup functions, preventing orphaned timers on unmount.

Lazy state initializers:
useState(() => ...) form used for JSON.stringify, toDatetimeLocalInput, and currentSeatCount.toString() calls — prevents those computations from running on every re-render, not just the first mount.

Stale-state update fix (KiloclawOrphansTab.tsx):
Switching from setScanResult({ ...scanResult, ... }) (closure-captured value) to the functional updater setScanResult(prev => ...) is correct and avoids the stale-closure bug.

Formatter caching (admin-utils.ts, utils.ts, OrganizationInvoicesCard.tsx, KiloClawScheduledActionBanner.tsx, subscription/utils.ts):
Intl.NumberFormat and Intl.DateTimeFormat instances are now module-level singletons or lazy-cached via Map, avoiding repeated construction on every render/call.

React key improvement (GlobToolCard.tsx, ListToolCard.tsx):
Switched from index-based key={idx} to content-based key={file} / key={entry}. File paths and directory entries are unique within a single output, so this is safe and avoids unnecessary re-renders.

AnimatedLogo.tsx:
Removed the isHovered state + useEffect pattern in favor of direct DOM event handlers on onMouseEnter/onMouseLeave. No state needed when you're just calling imperative video methods — eliminates an unnecessary render cycle.

Accessibility (KiloclawOrphansTab.tsx, SeatChangeModal.tsx):
<label htmlFor="..."> properly associated with inputs. Unrelated display text (Current Seats, Active Usage) correctly changed from <label> to <div><label> without a corresponding input is invalid HTML.

toSorted() vs .sort() (ModeForm.tsx):
Replacing .sort() with .toSorted() avoids mutating the array in-place. Correct fix.

Files Reviewed (37 files)

Initial batch (27 files):

  • .agents/skills/react-doctor/SKILL.md
  • apps/web/src/app/(app)/claw/components/ChannelPairingStep.tsx
  • apps/web/src/app/(app)/claw/components/KiloClawScheduledActionBanner.tsx
  • apps/web/src/app/(app)/install/page.tsx
  • apps/web/src/app/(app)/learn/page.tsx
  • apps/web/src/app/admin/components/BlacklistedDomains.tsx
  • apps/web/src/app/admin/components/KiloclawInstances/KiloclawOrphansTab.tsx
  • apps/web/src/app/auth/verify-magic-link/page.tsx
  • apps/web/src/app/vscode-marketplace/page.tsx
  • apps/web/src/components/AnimatedLogo.tsx
  • apps/web/src/components/cloud-agent-next/EnvVarsDialog.tsx
  • apps/web/src/components/cloud-agent-next/GlobToolCard.tsx
  • apps/web/src/components/cloud-agent-next/ListToolCard.tsx
  • apps/web/src/components/cloud-agent-next/SetupCommandsDialog.tsx
  • apps/web/src/components/cloud-agent/EnvVarsDialog.tsx
  • apps/web/src/components/cloud-agent/SetupCommandsDialog.tsx
  • apps/web/src/components/organizations/OrganizationInvoicesCard.tsx
  • apps/web/src/components/organizations/custom-modes/ModeForm.tsx
  • apps/web/src/components/organizations/subscription/SeatChangeModal.tsx
  • apps/web/src/components/organizations/subscription/SubscriptionQuickActions.tsx
  • apps/web/src/components/organizations/subscription/utils.ts
  • apps/web/src/hooks/useImageUpload.ts
  • apps/web/src/lib/admin-utils.ts
  • apps/web/src/lib/blacklist-domains-config.ts
  • apps/web/src/lib/organizations/organization-types.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/routers/admin/blacklist-domains-router.ts

Incremental batch (10 files):

  • apps/web/src/app/globals.css
  • apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx
  • apps/web/src/components/cloud-agent-next/TypingIndicator.tsx
  • apps/web/src/components/cloud-agent/TypingIndicator.tsx
  • apps/web/src/components/models/CondensedProviderAndModelsList.tsx
  • apps/web/src/components/models/ModelCard.tsx
  • apps/web/src/components/models/ProviderCard.tsx
  • apps/web/src/components/organizations/audit-logs/AuditLogsFilters.tsx
  • apps/web/src/components/payment/CreditPurchaseOptions.tsx
  • apps/web/src/components/ui/command.tsx

Reviewed by claude-4.6-sonnet-20260217 · 623,995 tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant