fix(KB): TierSelectionModal hook order + register IconLibrary

Two related fixes surfaced by armandoescalante in #915 when clicking a
Content Explorer category card (e.g. Medicine) on v1.32.0-rc.6:

1. TierSelectionModal placed a useMemo for freeBytes *after* the
   `if (!category) return null` early return (introduced in PR #901's
   guardrail integration). When `category` transitioned from null to
   non-null on first open, React saw a different hook count between
   renders and crashed the entire component tree with "Rendered more
   hooks than during the previous render", blanking the modal. Moved
   the freeBytes useMemo above the early return so hook order is
   constant.

2. `IconLibrary` was used as the icon prop on the Manage Custom
   Libraries button in remote-explorer.tsx but never registered in
   the DynamicIcon allowlist at admin/inertia/lib/icons.ts. Added it
   to both the import block and the icons map so the warning stops
   firing and the icon renders.

Closes #915.
This commit is contained in:
Chris Sherwood 2026-05-18 15:23:18 -07:00 committed by Jake Turner
parent a9c48fc098
commit 6e5284e563
2 changed files with 14 additions and 8 deletions

View File

@ -96,6 +96,18 @@ const TierSelectionModal: React.FC<TierSelectionModalProps> = ({
// underneath. Cancel returns to the tier modal as-is; Proceed closes both
// and runs the original onSelectTier path.
const [guardrailVerdict, setGuardrailVerdict] = useState<GuardrailVerdict | null>(null)
// Compute disk-free bytes from system info; 0 means "unknown", which the
// guardrail helper treats as "skip the relative-disk check".
// Must be declared before the `!category` early return so the hook count
// stays constant across renders (category transitions null → non-null when
// the user opens the modal).
const freeBytes = useMemo<number>(() => {
const primary = getPrimaryDiskInfo(systemInfo?.disk, systemInfo?.fsSize)
if (!primary) return 0
return Math.max(0, primary.totalSize - primary.totalUsed)
}, [systemInfo])
const ingestPolicy: 'Always' | 'Manual' =
ingestPolicySetting?.value === 'Manual' ? 'Manual' : 'Always'
@ -114,14 +126,6 @@ const TierSelectionModal: React.FC<TierSelectionModalProps> = ({
}
}
// Compute disk-free bytes from system info; 0 means "unknown", which the
// guardrail helper treats as "skip the relative-disk check".
const freeBytes = useMemo<number>(() => {
const primary = getPrimaryDiskInfo(systemInfo?.disk, systemInfo?.fsSize)
if (!primary) return 0
return Math.max(0, primary.totalSize - primary.totalUsed)
}, [systemInfo])
/**
* Runs the original onSelectTier-then-onClose flow. Pulled out of
* handleSubmit so the guardrail modal's confirm path can call it after

View File

@ -32,6 +32,7 @@ import {
IconInfoCircle,
IconBug,
IconCopy,
IconLibrary,
IconServer,
IconMenu2,
IconArrowLeft,
@ -75,6 +76,7 @@ export const icons = {
IconDownload,
IconHome,
IconInfoCircle,
IconLibrary,
IconLogs,
IconMap,
IconMenu2,