refactor(ui): extract shared UI factories#33
Conversation
There was a problem hiding this comment.
Cursor auto review
Found 3 actionable issue(s) on changed lines.
Refactor looks directionally good, but there are a couple of high-confidence runtime issues in the new factories (missing import and incomplete teardown), plus a potential setState-on-unmounted path in the modal factory when closing during async submit.
Generated automatically when this PR was submitted using Cursor CLI with --model auto.
| let root: ReactDOM.Root | null = null | ||
| let styleHost: HTMLDivElement | null = null | ||
|
|
||
| const ui = await createShadowRootUi(ctx, { |
There was a problem hiding this comment.
createShadowRootUi is used here but not imported in this file, which will fail at build/runtime. Import it from WXT (same place it was previously imported from in content-ui.tsx).
| mount() { | ||
| ui.mount() | ||
| }, | ||
| destroy() { |
There was a problem hiding this comment.
destroy() currently unmounts React and removes styleHost, but it never tells the underlying WXT UI to unmount/remove its container/shadow root. This can leave DOM/UI artifacts around on invalidation; call the WXT UI cleanup method as part of destroy() (and consider also nulling refs).
|
|
||
| useEffect(() => { | ||
| const handleKeyDown = (e: KeyboardEvent) => { | ||
| if (e.key === 'Escape') onClose() |
There was a problem hiding this comment.
This closes the modal on Escape even while loading is true. If the async onSubmit later rejects, the catch path will call setError/setLoading on an unmounted component. Consider disabling close (Escape/overlay click) while loading and/or guarding state updates with an isMounted ref.
There was a problem hiding this comment.
2 issues found across 9 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Import createShadowRootUi in shadow UI factory - Ensure destroy() calls WXT remove() cleanup - Prevent modal close during submit + guard async state updates
|
re-review @cubic-dev-ai |
@fathiraz I have started the AI code review. It will take a few minutes to complete. |
Summary
Extract duplicated UI boilerplate into three shared factories:
createFeatureUifor shadow DOM content scripts,createModalfor confirmation modals, andprimerCssfor SX style presets.Changes
lib/shadow-ui-factory.tsx—createFeatureUi()wraps WXTcreateShadowRootUiwith StyleSheetManager, ShadowThemeProvider, ErrorBoundary, and optional Primer portal compatlib/primer-css-helper.ts—primerCssnamespace with 10 SX preset functions (buttonMotion, flatPanel, modalOverlay, etc.)lib/modal-factory.tsx—createModal<T>()HOC with overlay, escape key, loading/validation/error states, and toast notificationscontent-ui.tsxfrom 259 to 120 lines — 5 factory calls replace 5 copy-pasted shadow root blockscreateModalprimerCsspresetsTest Plan
pnpm typecheckpasses with 0 errorspnpm build:chrome,pnpm build:firefox,pnpm build:edgeall succeedSummary by cubic
Extracted shared UI factories to remove duplicated shadow-DOM and modal code, standardize styles, and simplify maintenance. This reduces lines across content UIs and modals, ensures consistent theming/overlays, and hardens cleanup and modal behavior.
New Features
createFeatureUi: wrapswxtshadow UI withstyled-componentsStyleSheetManager,ShadowThemeProvider,ErrorBoundary, optional Primer portal compat, and robust destroy() cleanup that calls WXTremove().createModal<T>: modal factory with overlay, Escape-to-close, validation/loading/error states, success toasts, and protections that block close during submit and guard async state updates.primerCss: SX style presets (e.g.,buttonMotion,modalOverlay,modalPanel,contentArea) for consistent UI with@primer/react.Refactors
createFeatureUiincontent-ui.tsx(259 → 120 lines) and centralized Primer portal setup.createModal(~75 → ~20 lines each) with built-in handling.primerCsspresets across features andui/actions.tsx.Written for commit 0e2cadf. Summary will update on new commits.
Review in cubic