(node: SceneNode, logDetails = false)
| 151 | * @returns True if the node is likely an icon, false otherwise. |
| 152 | */ |
| 153 | export function isLikelyIcon(node: SceneNode, logDetails = false): boolean { |
| 154 | const info: string[] = [`Node: ${node.name} (${node.type}, ID: ${node.id})`]; |
| 155 | let result = false; |
| 156 | let reason = ""; |
| 157 | |
| 158 | // --- 1. Initial Filtering (Disallowed Types First) --- |
| 159 | if (DISALLOWED_ICON_TYPES.has(node.type)) { |
| 160 | reason = `Disallowed Type: ${node.type}`; |
| 161 | result = false; |
| 162 | } |
| 163 | // --- 2. Check for SVG Export Settings (Only if not disallowed) --- |
| 164 | else if (hasSvgExportSettings(node)) { |
| 165 | reason = "Has SVG export settings"; |
| 166 | result = true; |
| 167 | } |
| 168 | // --- 3. Dimension Check --- |
| 169 | else if ( |
| 170 | !("width" in node && "height" in node && node.width > 0 && node.height > 0) |
| 171 | ) { |
| 172 | // Exception: Allow specific types even without dimensions initially. |
| 173 | if (ICON_TYPES_IGNORE_SIZE.has(node.type)) { |
| 174 | reason = `Direct ${node.type} type (no dimensions check needed)`; |
| 175 | result = true; |
| 176 | } else { |
| 177 | reason = "No dimensions"; |
| 178 | result = false; |
| 179 | } |
| 180 | } else { |
| 181 | // --- 4. Direct Vector/Boolean/Primitive --- |
| 182 | // Special case: VECTOR, BOOLEAN_OPERATION, POLYGON, STAR are always icons |
| 183 | if (ICON_TYPES_IGNORE_SIZE.has(node.type)) { |
| 184 | reason = `Direct ${node.type} type (size ignored)`; |
| 185 | result = true; |
| 186 | } |
| 187 | // Check other primitives (ELLIPSE, RECTANGLE, LINE) with size constraint |
| 188 | else if (ICON_PRIMITIVE_TYPES.has(node.type)) { |
| 189 | if (isTypicalIconSize(node)) { |
| 190 | reason = `Direct ${node.type} with typical size`; |
| 191 | result = true; |
| 192 | } else { |
| 193 | reason = `Direct ${node.type} but too large (${Math.round(node.width)}x${Math.round(node.height)})`; |
| 194 | result = false; |
| 195 | } |
| 196 | } |
| 197 | // --- 5. Container Logic --- |
| 198 | else if (ICON_CONTAINER_TYPES.has(node.type) && "children" in node) { |
| 199 | // Container size check still uses the simplified isTypicalIconSize |
| 200 | if (!isTypicalIconSize(node)) { |
| 201 | reason = `Container but too large (${Math.round(node.width)}x${Math.round(node.height)})`; |
| 202 | result = false; |
| 203 | } else { |
| 204 | const visibleChildren = node.children.filter( |
| 205 | (child) => child.visible !== false, |
| 206 | ); |
| 207 | |
| 208 | if (visibleChildren.length === 0) { |
| 209 | // Check for styling on empty containers (size already checked) |
| 210 | const hasVisibleFill = |
no test coverage detected