.cursor/skills/webviewer-build/SKILL.md
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.
npx skillsauth add petrowsky/agentic-fm webviewer-buildInstall 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.
Generate a complete web application that runs inside a FileMaker Web Viewer object, along with the companion FM scripts that connect it to FileMaker data. The output is a self-contained HTML file plus fmxmlsnippet scripts — together they form a bidirectional data bridge between the web UI and the FM solution.
Read agent/config/automation.json and check project_tier (preferred) or default_tier:
Also read companion_url from automation.json for preview pushes.
Read agent/CONTEXT.json for:
current_layout — the layout where the Web Viewer will be placedtables — the data schema (fields, types, TOs) that the web app will display and editrelationships — related data the web app needs to accessscripts — existing scripts the web app may need to callvalue_lists — for dropdowns, filters, and validationcustom_functions — for any calculations the FM scripts needIf CONTEXT.json is absent or scoped to the wrong layout, ask the developer to run Push Context on the target layout.
Read the theme data from agent/context/{solution}/:
cat agent/context/*/theme.css 2>/dev/null
cat agent/context/*/theme-manifest.json 2>/dev/null
theme.css — inline this into the HTML for visual consistency with the FM solutiontheme-manifest.json — color palette for matching FM's visual languageIf no theme data exists, note this and suggest running python3 agent/scripts/extract_theme.py. Proceed with a clean, neutral stylesheet if the developer wants to continue.
Understand what the web app needs to do. Key questions:
If the developer provided a spec from layout-spec or a design brief, extract the answers from that.
Build a single, self-contained HTML file. All CSS and JavaScript are inline — no external dependencies. The file must work when loaded via Set Web Viewer with a data:text/html URL or from a file path.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{App Title}</title>
<style>
/* FM theme CSS for visual consistency */
{theme_css_content}
/* Application styles */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
/* ... app-specific styles ... */
</style>
</head>
<body>
<!-- Application markup -->
<div id="app">
<!-- Loading state shown until FM pushes data -->
<div id="loading">Loading...</div>
<!-- Main content hidden until data arrives -->
<div id="content" style="display: none;">
<!-- ... -->
</div>
</div>
<script>
// === FM Bridge ===
/**
* Called by FM via Perform JavaScript in Web Viewer.
* Receives JSON data from the FM data-loading script.
* @param {string} json — JSON string from FM
*/
function fmCallback(json) {
try {
const data = JSON.parse(json);
renderApp(data);
} catch (e) {
console.error("fmCallback parse error:", e);
}
}
/**
* Call an FM script with a JSON parameter.
* Uses the FileMaker.PerformScript() JS-to-FM bridge.
* @param {string} scriptName — the FM script to call
* @param {object} param — parameter object (will be JSON-stringified)
*/
function callFM(scriptName, param) {
if (typeof FileMaker !== "undefined") {
FileMaker.PerformScript(scriptName, JSON.stringify(param));
} else {
console.log("FM bridge not available. Would call:", scriptName, param);
}
}
// === Application Logic ===
function renderApp(data) {
document.getElementById("loading").style.display = "none";
document.getElementById("content").style.display = "block";
// ... render data into the DOM ...
}
// ... event handlers, UI logic ...
</script>
</body>
</html>
fmCallback(). The web viewer loads before FM can call JavaScript.fmCallback receives incomplete JSON, render what is available and show placeholders for the rest.FileMaker.PerformScript() is not available (opened in a browser outside FM), log calls to the console instead of crashing. This allows browser-based development and testing.The web viewer needs two FM scripts to function:
This script gathers data from the FM solution and pushes it to the web viewer via Perform JavaScript in Web Viewer. It is called when:
HR format outline:
# Push data to web viewer
Set Error Capture [ On ]
Allow User Abort [ Off ]
#
# Gather data as JSON
Set Variable [ $json ; Value:
JSONSetElement ( "{}" ;
["records" ; <related records or found set as JSON array> ; JSONArray] ;
["meta" ; <metadata object> ; JSONObject]
)
]
#
# Push to web viewer
Perform JavaScript in Web Viewer [ Object Name: "{webviewer_object_name}" ;
Function Name: "fmCallback" ;
Parameters: $json ]
Generate this as fmxmlsnippet XML using the step catalog. The data-gathering logic depends on what the web app needs — typically a combination of:
GetFieldName / field referencesExecuteSQL or looping through a portal relationshipValueListItemsThis script receives calls from the web viewer's FileMaker.PerformScript(). It parses the JSON parameter and dispatches to the appropriate action.
HR format outline:
# Handle web viewer event
Set Error Capture [ On ]
Allow User Abort [ Off ]
#
Set Variable [ $param ; Value: Get ( ScriptParameter ) ]
Set Variable [ $action ; Value: JSONGetElement ( $param ; "action" ) ]
#
If [ $action = "save" ]
# Parse fields from $param and set them
Set Field [ {TO}::{Field} ; JSONGetElement ( $param ; "fields.{fieldName}" ) ]
Commit Records/Requests [ With dialog: Off ]
# Refresh the web viewer with updated data
Perform Script [ "Data Loader Script" ; Parameter: "" ]
Else If [ $action = "delete" ]
Delete Record/Request [ With dialog: Off ]
Else If [ $action = "navigate" ]
Set Variable [ $layout ; Value: JSONGetElement ( $param ; "layout" ) ]
Go to Layout [ $layout ]
End If
Generate this as fmxmlsnippet XML. The action cases depend on the web app's interaction design from Step 3.
Follow all standard script creation rules from CLAUDE.md:
agent/docs/CODING_CONVENTIONS.mdagent/sandbox/python3 agent/scripts/validate_snippet.pyPush the HTML to the webviewer for the developer to preview:
curl -s -X POST -H "Content-Type: application/json" \
-d '{"type": "layout-preview", "content": "<html content>"}' \
{companion_url}/webviewer/push
If the companion server is not reachable, write the HTML to agent/sandbox/{app-name}.html and instruct the developer to open it in a browser.
The web viewer app has been pushed to the webviewer for preview. Note that FM bridge calls (
FileMaker.PerformScript) will log to the browser console since there is no FM context — the UI and layout are what to review here.
Iterate on feedback. Each revision generates an updated HTML file and a new push.
Write all output files to agent/sandbox/:
| File | Contents |
|------|----------|
| {app-name}.html | The self-contained web viewer HTML application |
| {app-name}-data-loader.xml | fmxmlsnippet for the data-loading FM script |
| {app-name}-event-handler.xml | fmxmlsnippet for the event-handling FM script |
Deploy the companion FM scripts per the current tier:
Tier 1:
python3 agent/scripts/clipboard.py write agent/sandbox/{app-name}-data-loader.xml
The data loader script is on your clipboard. To install it:
- Create a new script named {Data Loader Script Name} in Script Workspace
- ⌘V — paste
Repeat for the event handler script.
Tier 2/3: Deploy via agent/scripts/deploy.py.
Provide instructions for setting up the Web Viewer object:
To install the web viewer:
- Open {Layout Name} in Layout Mode
- Add a Web Viewer object — size it to fill the desired area
- Set the Web Viewer's Object Name to "{webviewer_object_name}" (Inspector > Position > Name)
- Set the Web Viewer's URL to one of:
- File path:
"file:" & Get ( DocumentsPath ) & "{app-name}.html"(place the HTML file in the Documents folder)- Data URL:
"data:text/html," & {a global field or variable containing the HTML}(for self-contained deployment)- Set the OnLayoutEnter script trigger to run {Data Loader Script Name}
- Switch to Browse Mode and test
The web viewer approach is stronger than native FM layout objects when:
The native FM path is stronger when:
FileMaker.PerformScript() bridge is only available when the HTML is loaded inside a FileMaker Web Viewer — the code must handle its absence gracefully for browser testing.Perform JavaScript in Web Viewer requires the Web Viewer object to have a named Object Name set in the Inspector — always specify what this name should be.<Script> wrapper, validated before deployment.agent/docs/CODING_CONVENTIONS.md for all FM script calculations.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.
development
Generate a companion verification script that exercises a target script with known inputs and asserts expected outputs. Uses the fm-debug infrastructure to report pass/fail results back to the agent. At Tier 1 the developer runs the test script manually. At Tier 3 the agent deploys, runs, and reads results autonomously. Triggers on phrases like "test this script", "write a test", "verification script", "assert results", or "prove this works".