From 763886ea015d7b169ee178c6f23f0c2f3ff5f98c Mon Sep 17 00:00:00 2001 From: Fini Date: Tue, 24 Feb 2026 01:44:01 +0800 Subject: [PATCH] fix(ai): add line/polyline/polygon parsing to Feather icon bundler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit featherBodyToPathD was missing , , and support. Feather icons heavily use these elements — e.g. arrow-right, menu, check, x all use or . Without this support, many common icons returned d=null and were never added to ICON_PATH_MAP, causing them to fall back to async Iconify fetch or the generic star placeholder instead of rendering correctly. Now all 286 Feather icons are fully parsed and available offline. --- src/services/ai/icon-resolver.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/services/ai/icon-resolver.ts b/src/services/ai/icon-resolver.ts index d9bfa01ad..4d58af0b7 100644 --- a/src/services/ai/icon-resolver.ts +++ b/src/services/ai/icon-resolver.ts @@ -186,6 +186,37 @@ function featherBodyToPathD(body: string): string | null { } } + // → M x1 y1 L x2 y2 + const lineRe = /]+>/g + while ((m = lineRe.exec(body)) !== null) { + const tag = m[0] + const x1 = parseFloat(tag.match(/\bx1="([^"]+)"/)?.[1] ?? 'NaN') + const y1 = parseFloat(tag.match(/\by1="([^"]+)"/)?.[1] ?? 'NaN') + const x2 = parseFloat(tag.match(/\bx2="([^"]+)"/)?.[1] ?? 'NaN') + const y2 = parseFloat(tag.match(/\by2="([^"]+)"/)?.[1] ?? 'NaN') + if (!isNaN(x1) && !isNaN(y1) && !isNaN(x2) && !isNaN(y2)) { + parts.push(`M ${x1} ${y1} L ${x2} ${y2}`) + } + } + + // → M x1 y1 L x2 y2 ... + // → same but closed with Z + const polyRe = /<(polyline|polygon)([^>]+)>/g + while ((m = polyRe.exec(body)) !== null) { + const tag = m[0] + const closed = m[1] === 'polygon' + const pointsAttr = tag.match(/\bpoints="([^"]+)"/)?.[1] + if (!pointsAttr) continue + const coords = pointsAttr.trim().split(/[\s,]+/).map(Number) + if (coords.length < 4 || coords.some(isNaN)) continue + const cmds: string[] = [`M ${coords[0]} ${coords[1]}`] + for (let i = 2; i + 1 < coords.length; i += 2) { + cmds.push(`L ${coords[i]} ${coords[i + 1]}`) + } + if (closed) cmds.push('Z') + parts.push(cmds.join(' ')) + } + return parts.length > 0 ? parts.join(' ') : null }