.cursor/skills/icon-swap/SKILL.md
Swap SVG icons in FileMaker Button and ButtonBar layout objects with icons from open-source libraries (Lucide, Heroicons, Tabler, Phosphor, Bootstrap, Iconoir, MDI, Ionicons). Handles stroke-to-fill conversion for FM compatibility. Use when the developer says "swap icons", "replace icons", "change icons to", "use lucide icons", "use heroicons", or wants to update button icons from a library.
npx skillsauth add petrowsky/agentic-fm icon-swapInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Replace embedded SVG icons in FileMaker layout objects with icons from open-source libraries. Supports both fill-based and stroke-based icon libraries — stroke-based icons are automatically converted to FM-compatible filled SVGs via a rasterise-then-trace pipeline.
The icon extraction and XML replacement functionality uses stdlib Python only. Swapping icons from fill-based libraries (Heroicons solid, MDI, Bootstrap Icons, Phosphor, Tabler filled, Ionicons filled) works without any extra dependencies.
Stroke-based libraries (Lucide, Heroicons outline, Tabler, Iconoir, Ionicons outline) require conversion dependencies. Check first:
python3 agent/scripts/fm_svg_convert.py --check-deps
If deps are missing, guide the developer through setup:
Stroke-to-fill conversion requires additional dependencies.
These are needed because FileMaker button icons only support filled shapes — stroke-based SVG libraries (like Lucide) must be converted.
Option A: venv in the project folder (recommended)
python3 -m venv agent/.venv source agent/.venv/bin/activate pip install cairosvg PillowSystem dependency (potrace):
brew install potrace # macOSOnce set up, run commands with the venv active, or prefix with
agent/.venv/bin/python3.
If the developer declines, suggest a fill-based alternative library instead:
Same as icon-extract skill — the input can come from:
A) File in sandbox:
python3 agent/scripts/fm_icon_extract.py agent/sandbox/<filename>.xml --json
B) Clipboard via companion server:
curl -s http://local.hub:8765/clipboard | python3 -c "
import json, sys
data = json.load(sys.stdin)
if data.get('success'):
with open('agent/sandbox/_clipboard_input.xml', 'w') as f:
f.write(data['xml'])
print('Saved clipboard content to agent/sandbox/_clipboard_input.xml')
else:
print('ERROR:', data.get('error', 'Unknown error'), file=sys.stderr)
sys.exit(1)
"
Fall back to host.docker.internal:8765 if local.hub:8765 is unreachable.
C) Developer pastes XML directly — save to sandbox and proceed.
Run extraction:
python3 agent/scripts/fm_icon_extract.py agent/sandbox/<input>.xml --json
Also save individual SVGs for visual reference:
python3 agent/scripts/fm_icon_extract.py agent/sandbox/<input>.xml --output-dir agent/sandbox/icons_current/
Present the catalogue to the developer:
| # | Label | Current Icon | Suggested Replacement | Preview |
|---|-------|--------------|-----------------------|---------|
| 0 | Dashboard | grid/table (filled) | layout-dashboard | view |
| 1 | Companies | building (filled) | building-2 | view |
| 2 | Contacts | person (filled) | contact | view |
Replace the preview URLs with the correct pattern for the target library (see URL patterns in Step 3).
Use the button name and label to suggest matching icons from the target library. Common mappings for business apps:
| Label pattern | Lucide suggestion | Heroicons suggestion |
|---|---|---|
| Dashboard | layout-dashboard | squares-2x2 |
| Company/Companies | building-2 | building-office-2 |
| Contacts | contact | user-group |
| Leads | target | flag |
| Opportunities | trending-up | chart-bar |
| Quotes/Estimates | file-text | document-text |
| Orders | shopping-cart | shopping-cart |
| Purchase Orders/POs | clipboard-list | clipboard-document-list |
| Shipments | truck | truck |
| Receiving | package-check | inbox-arrow-down |
| Returns | undo-2 | arrow-uturn-left |
| Work Orders | wrench | wrench-screwdriver |
| Reports | bar-chart-3 | chart-bar |
| Settings/Setup | settings | cog-6-tooth |
| Builds | hammer | wrench |
| Inventory | warehouse | archive-box |
| Invoices | receipt | document |
| Payments | credit-card | credit-card |
| Calendar/Schedule | calendar | calendar |
| Search | search | magnifying-glass |
| Users | users | users |
| Home | home | home |
| Email | mail | envelope |
| Phone | phone | phone |
| Notes | sticky-note | document |
Always present the proposed mapping and ask the developer to confirm or override before proceeding. Include a Preview column with a direct link to the icon on the library's website so the developer can visually verify each choice.
Icon preview URL patterns by library:
| Library | URL pattern |
|---|---|
| Lucide | https://lucide.dev/icons/{name} |
| Heroicons (outline/solid) | https://heroicons.com/outline#{name} or https://heroicons.com/solid#{name} |
| Tabler | https://tabler.io/icons/icon/{name} |
| Phosphor | https://phosphoricons.com/icons/{name} |
| Bootstrap | https://icons.getbootstrap.com/icons/{name}/ |
| Iconoir | https://iconoir.com/icons/{name} |
| MDI | https://pictogrammers.com/library/mdi/icon/{name}/ |
| Ionicons | https://ionic.io/ionicons/ionicons/#{name} |
Example presentation:
Proposed icon mapping (library: Lucide)
| # | Label | Replacement icon | Preview | |---|-------|-----------------|---------| | 0 | Dashboard |
layout-dashboard| view | | 1 | Companies |building-2| view | | ... | | | |Edit any names, or reply go to proceed.
If the developer provides explicit icon names (one-for-one list), use those directly. The developer may provide them as:
0: home, 1: building, 2: usershome, building, users, target, ...{"Dashboard": "layout-dashboard", "Companies": "building-2"}For each icon in the mapping:
python3 agent/scripts/fm_svg_convert.py --fetch lucide:layout-dashboard --fm -o agent/sandbox/icons_new/layout-dashboard.svg
If the icon is not found (404), inform the developer and ask for an alternative name. Common issues:
users vs user)shopping-cart vs shoppingcart)file-text in Lucide vs document-text in Heroicons)If the library is stroke-based, the --fm flag on fm_svg_convert.py handles conversion automatically. If conversion deps are not available, the script will error — fall back to a fill-based library.
Whether fetched as fill-based or converted from stroke, the final SVG must have:
<g class="fm_fill"> wrapping all shape elementsviewBox="0 0 24 24" (or matching dimensions)width and height attributes with px unitsstroke attributes on any elementfill="none" on shape elements<?xml version="1.0" encoding="utf-8"?>The format_for_fm() function in fm_svg_convert.py handles this, and is also invoked by the --fm flag.
For fill-based libraries that don't need stroke conversion, use the formatting function directly:
from agent.scripts.fm_svg_convert import format_for_fm
formatted = format_for_fm(svg_text)
Or from the CLI:
python3 -c "
import sys; sys.path.insert(0, '.')
from agent.scripts.fm_svg_convert import format_for_fm, fetch_icon
svg = fetch_icon('heroicons-solid', 'building-office-2')
print(format_for_fm(svg))
" > agent/sandbox/icons_new/building-office-2.svg
Use the replace_icons_in_file function from fm_icon_extract.py:
python3 -c "
import sys; sys.path.insert(0, '.')
from agent.scripts.fm_icon_extract import replace_icons_in_file
# Read all replacement SVGs
replacements = {}
# Map: index -> path to replacement SVG
mapping = {
0: 'agent/sandbox/icons_new/layout-dashboard.svg',
1: 'agent/sandbox/icons_new/building-2.svg',
# ...
}
for idx, path in mapping.items():
with open(path) as f:
replacements[idx] = f.read()
replace_icons_in_file(
'agent/sandbox/<input>.xml',
replacements,
'agent/sandbox/<input>_updated.xml'
)
"
The function:
<HexData> with the new SVG encoded as uppercase hex<Stream size=""> attribute to match the new byte countGLPH stream and all other XML structure unchanged_updated suffix fileIf the input came from clipboard, write the updated XML back to clipboard:
python3 agent/scripts/clipboard.py write agent/sandbox/<input>_updated.xml
Then instruct the developer:
The updated layout object is on your clipboard. In FileMaker Layout Mode:
- Delete the original button bar
- Cmd+V to paste the updated version
If the input came from a file, inform the developer:
The updated XML is at
agent/sandbox/<input>_updated.xml. You can load it to clipboard with:python3 agent/scripts/clipboard.py write agent/sandbox/<input>_updated.xml
| Library | ID | Style | Conversion needed? |
|---|---|---|---|
| Lucide | lucide | stroke | Yes |
| Heroicons (outline) | heroicons-outline | stroke | Yes |
| Heroicons (solid) | heroicons-solid | fill | No |
| Tabler Icons | tabler | stroke | Yes |
| Tabler Icons (filled) | tabler-filled | fill | No |
| Phosphor Icons | phosphor | fill | No |
| Bootstrap Icons | bootstrap | fill | No |
| Iconoir | iconoir | stroke | Yes |
| Material Design Icons | mdi | fill | No |
| Ionicons (outline) | ionicons-outline | stroke | Yes |
| Ionicons (filled) | ionicons-filled | fill | No |
Browse icons at their respective websites to find names:
--render-size 2048.<g class="fm_fill"> and no stroke attributes. Check that the XML declaration is present.stream_size must match the actual byte count of the hex-decoded SVG. The replacement function handles this automatically.development
Generate a complete web application inside a FileMaker Web Viewer — self-contained HTML/CSS/JS styled with the FM theme, plus companion FM bridge scripts for bidirectional data flow. Use when the developer says "web viewer", "webviewer app", "HTML in FileMaker", "build web viewer", or when the layout-design skill delegates to the web-first output path. Recommended for modern, responsive UI, complex interactions (drag-and-drop, charts, rich text), or solutions considering future migration off FileMaker.
development
Trace references to a FileMaker object across the entire solution. Supports usage reports ("where is this field used?"), impact analysis ("what breaks if I rename this?"), and dead object scans ("show unused fields/scripts"). Use when the developer says "trace", "find references", "where is X used", "impact of renaming", "unused fields/scripts", "dead code", "what references X", or "is X used anywhere".
development
Analyze a FileMaker solution and produce a structured profile covering data model, business logic, UI layer, integrations, and health metrics. Uses on-disk pre-processing to handle solutions of any size without sending raw XML through the agent. Use when the developer says "analyze solution", "solution overview", "solution analysis", "solution profile", "solution spec", "what does this solution do", "solution summary", or wants a high-level understanding of an entire FileMaker solution.
development
Interactive setup wizard for agentic-fm. Detects what's already configured, walks the user through each remaining step, and verifies completion before proceeding. Use when the developer says "help me set up", "setup", "get started", "onboard", "first time setup", "install agentic-fm", "configure agentic-fm", or is clearly new to the project and needs guidance.