Priority: π’ Low β Target: post-v2.0.0
Summary
Both ControlCenter.tsx and ControlCenterExpanded.tsx use direct DOM manipulation in their profile image onError handlers instead of React state. This is fragile and relies on DOM element ordering.
Current Implementation
Both files use identical patterns:
<img
src={profileImageUrl}
onError={e => {
(e.target as HTMLImageElement).style.display = 'none';
(e.target as HTMLImageElement).nextElementSibling?.classList.remove('hidden')
}}
/>
<Coffee className="hidden" weight="fill" /> {/* Fallback icon */}
Problems
- Relies on DOM sibling order β if the JSX structure changes,
nextElementSibling may point to the wrong element
- Direct DOM mutation β
style.display = 'none' and classList.remove bypass React's reconciliation
- Not testable β React Testing Library can't easily verify DOM mutations made outside React
- Duplicated β Same pattern in both ControlCenter and ControlCenterExpanded
Proposed Solution
Use React state to control visibility:
const [imgError, setImgError] = useState(false)
// In render:
{!imgError && profileImageUrl ? (
<img
src={profileImageUrl}
onError={() => setImgError(true)}
alt={profileName}
/>
) : (
<Coffee weight="fill" />
)}
If the useProfileImage hook from #184 is implemented, this state can be encapsulated there:
function useProfileImage(profileName: string | null) {
const [url, setUrl] = useState<string | null>(null)
const [error, setError] = useState(false)
// ... fetch logic
return { url, error, onError: () => setError(true) }
}
Files to Modify
Acceptance Criteria
Related
Priority: π’ Low β Target: post-v2.0.0
Summary
Both
ControlCenter.tsxandControlCenterExpanded.tsxuse direct DOM manipulation in their profile imageonErrorhandlers instead of React state. This is fragile and relies on DOM element ordering.Current Implementation
Both files use identical patterns:
Problems
nextElementSiblingmay point to the wrong elementstyle.display = 'none'andclassList.removebypass React's reconciliationProposed Solution
Use React state to control visibility:
If the
useProfileImagehook from #184 is implemented, this state can be encapsulated there:Files to Modify
apps/web/src/components/ControlCenter.tsxβ Replace DOM manipulation with stateapps/web/src/components/ControlCenterExpanded.tsxβ Same fixapps/web/src/hooks/useProfileImage.ts(if created as part of refactor: Extract useMachineActions hook β eliminate Control Center duplication and fix canAbortWarmup bugΒ #184)Acceptance Criteria
style.display,classList) in image error handlersprofileImageUrlis null/emptyRelated