fix(ai): normalize gradient stop offsets to prevent NaN display

AI sometimes omits the offset field on gradient stops, causing
Math.round(undefined * 100) = NaN in the UI. Fix at two layers:
- normalize-pen-file.ts: auto-distribute missing offsets evenly across
  [0,1] and convert percentage-format offsets (>1) to 0-1 range
- fill-section.tsx: defensive guard in the stop percentage display
This commit is contained in:
Fini 2026-02-24 04:06:19 +08:00
parent 763886ea01
commit f4d313464e
2 changed files with 21 additions and 9 deletions

View file

@ -221,7 +221,7 @@ export default function FillSection({
onChange={(c) => handleStopColorChange(i, c)}
/>
<NumberInput
value={Math.round(stop.offset * 100)}
value={Math.round((Number.isFinite(stop.offset) ? stop.offset : i / Math.max(currentStops.length - 1, 1)) * 100)}
onChange={(v) => handleStopOffsetChange(i, v)}
min={0}
max={100}

View file

@ -156,19 +156,31 @@ function normalizeSingleFill(
function normalizeGradientStops(
raw: unknown[] | undefined,
): GradientStop[] {
if (!Array.isArray(raw)) return []
return raw.map((s: unknown) => {
if (!Array.isArray(raw) || raw.length === 0) return []
// First pass: parse offsets, collecting which ones are explicitly set
const parsed = raw.map((s: unknown) => {
const stop = s as Record<string, unknown>
const rawOffset =
typeof stop.offset === 'number' && Number.isFinite(stop.offset)
? stop.offset
: typeof stop.position === 'number' && Number.isFinite(stop.position)
? stop.position
: null
// Normalize percentage-format offsets (AI sometimes outputs 0-100 instead of 0-1)
const offset = rawOffset !== null && rawOffset > 1 ? rawOffset / 100 : rawOffset
return {
offset:
typeof stop.offset === 'number' && Number.isFinite(stop.offset)
? stop.offset
: typeof stop.position === 'number' && Number.isFinite(stop.position)
? stop.position
: 0,
offset,
color: typeof stop.color === 'string' ? stop.color : '#000000',
}
})
// Second pass: auto-distribute any stops that are missing an offset
const n = parsed.length
return parsed.map((s, i) => ({
color: s.color,
offset: s.offset !== null ? Math.max(0, Math.min(1, s.offset!)) : i / Math.max(n - 1, 1),
}))
}
// ---------------------------------------------------------------------------