@@ -202,19 +202,62 @@ final class ImagesLoader {
202202 // MARK: - Helpers
203203
204204 private func fetchImageComponents( fileId: String , frameName: String , filter: String ? = nil ) throws -> [ NodeId : Component ] {
205- var components = try loadComponents ( fileId: fileId)
206- . filter {
207- $0. containingFrame. name == frameName && $0. useForPlatform ( platform)
208- }
205+ let allComponents = try loadComponents ( fileId: fileId)
206+
207+ // 1. Initial filter based on your Frame Regex and Page name
208+ let filtered = allComponents. filter { component in
209+ guard let name = component. containingFrame. name else { return false }
210+ let isFrameMatch = name. range ( of: frameName, options: [ . regularExpression, . caseInsensitive] ) != nil
211+ let isPageMatch = component. containingFrame. pageName. contains ( " Icons " )
212+ return isFrameMatch && isPageMatch
213+ }
209214
210- if let filter {
211- let assetsFilter = AssetsFilter ( filter: filter)
212- components = components. filter { component -> Bool in
213- assetsFilter. match ( name: component. name)
215+ // 2. Prepare for deduplication using the final name as the key
216+ var finalComponentsByName : [ String : Component ] = [ : ]
217+
218+ for component in filtered {
219+ guard let frameName = component. containingFrame. name, frameName. contains ( " / " ) else { continue }
220+
221+ let frameBaseName = frameName. components ( separatedBy: " / " ) . last?
222+ . trimmingCharacters ( in: . whitespaces) ?? " "
223+
224+ let variantValue = component. name. components ( separatedBy: " = " ) . last?
225+ . trimmingCharacters ( in: . whitespaces) ?? " "
226+
227+ let finalName = ( frameBaseName + variantValue. capitalized)
228+ . replacingOccurrences ( of: " " , with: " " )
229+
230+ let newComponent = Component (
231+ key: component. key,
232+ nodeId: component. nodeId,
233+ name: finalName,
234+ description: component. description,
235+ containingFrame: component. containingFrame
236+ )
237+
238+ // 3. Deduplication: Only store one version for each finalName
239+ if let existing = finalComponentsByName [ finalName] {
240+ // Priority: Keep the iOS version if we find multiple
241+ if component. name. lowercased ( ) . contains ( " platform=ios " ) {
242+ finalComponentsByName [ finalName] = newComponent
243+ }
244+ } else {
245+ finalComponentsByName [ finalName] = newComponent
214246 }
215247 }
216248
217- return Dictionary ( uniqueKeysWithValues: components. map { ( $0. nodeId, $0) } )
249+ guard !finalComponentsByName. isEmpty else {
250+ throw FigmaExportError . componentsNotFound
251+ }
252+
253+ // 4. IMPORTANT: Map back using the ORIGINAL nodeId as the key
254+ // This ensures the API call 'GET /v1/images/' uses valid IDs
255+ var result : [ NodeId : Component ] = [ : ]
256+ for (_, component) in finalComponentsByName {
257+ result [ component. nodeId] = component
258+ }
259+
260+ return result
218261 }
219262
220263 private func _loadImages(
@@ -266,7 +309,11 @@ final class ImagesLoader {
266309 let isRTL = component. useRTL ( )
267310 return Image ( name: name, scale: . all, idiom: idiom, url: url, format: params. format, isRTL: isRTL)
268311 }
269- return ImagePack ( name: packName, images: packImages, platform: platform)
312+ var imagePack = ImagePack ( name: packName, images: packImages, platform: platform)
313+ if let containingFrameName = components. first? . value. containingFrame. name, containingFrameName. hasPrefix ( " ic / img_ " ) {
314+ imagePack. renderMode = . original
315+ }
316+ return imagePack
270317 }
271318 return imagePacks
272319 }
0 commit comments