packages/webviewer/skills/webdirect-runtime/SKILL.md
FileMaker WebDirect ProofKit Web Viewer runtime behavior refresh resilience session state localStorage browser resize reload same deployment embedded bundle avoid separate deployment avoid separate web server @proofkit/webviewer fmFetch callFMScript WebViewerAdapter WebDirect page refresh
npx skillsauth add proofgeist/proofkit webdirect-runtimeInstall 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.
WebDirect does not require separate JavaScript code, a separate build, a separate deployment target, or a separate web server. A ProofKit Web Viewer app runs in the Web Viewer with the same @proofkit/webviewer APIs used in FileMaker Pro.
The main WebDirect-specific concern is refresh resilience. WebDirect can refresh the Web Viewer more often than a normal browser app, including from actions like resizing the browser window. Design the app so unexpected page reloads are recoverable.
Use the normal ProofKit Web Viewer patterns:
import { fmFetch, globalSettings } from "@proofkit/webviewer";
globalSettings.setWebViewerName("web");
const result = await fmFetch("GetDashboardState", { recordId: "123" });
Deploy the app the same way as any FileMaker Web Viewer app. Embedded bundles stored in the FileMaker file are valid for WebDirect. Do not tell users to host WebDirect apps separately unless they independently chose the hosted deployment method for other reasons.
Persist critical client state so a WebDirect-triggered reload is not destructive.
Good candidates for localStorage:
Keep FileMaker as the source of truth for committed data. Use localStorage only for client recovery state, then revalidate or reload FileMaker data after the app starts.
const STORAGE_KEY = "invoice-review:draft";
export function saveDraft(draft: InvoiceDraft) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(draft));
}
export function loadDraft(): InvoiceDraft | null {
const value = localStorage.getItem(STORAGE_KEY);
return value ? (JSON.parse(value) as InvoiceDraft) : null;
}
On startup, restore local UI state first, then call FileMaker for authoritative context:
const draft = loadDraft();
const session = await fmFetch("GetWebViewerSession", {
restoredDraftId: draft?.id,
});
Prefer explicit save, resume, and discard flows. A WebDirect user should not lose important work because the browser window resized or the Web Viewer refreshed.
For forms and multi-step workflows:
For dashboards and list screens:
Wrong:
Because this app runs in WebDirect, deploy it to Vercel and point the Web Viewer at the hosted URL.
Correct:
Use the same ProofKit Web Viewer deployment. The bundle can live in the FileMaker file for FileMaker Pro, FileMaker Go, and WebDirect.
WebDirect changes runtime refresh behavior, not the deployment model.
Wrong:
const [draft, setDraft] = useState<InvoiceDraft>(emptyDraft);
Correct:
const [draft, setDraft] = useState<InvoiceDraft>(() => loadDraft() ?? emptyDraft);
function updateDraft(next: InvoiceDraft) {
setDraft(next);
saveDraft(next);
}
React state is lost on Web Viewer refresh. Persist state needed to recover the user flow.
Wrong:
const invoice = loadDraft();
renderInvoice(invoice);
Correct:
const draft = loadDraft();
const invoice = await fmFetch("GetInvoice", { id: draft?.invoiceId });
renderInvoice({ ...invoice, draft });
Use local storage for recovery state. Re-read FileMaker for authoritative data.
Wrong:
window.addEventListener("beforeunload", () => {
localStorage.removeItem("invoice-review:draft");
});
Correct:
async function save() {
await fmFetch("SaveInvoiceReview", draft);
localStorage.removeItem("invoice-review:draft");
}
In WebDirect, refresh can be incidental. Clear recovery state only after an explicit successful action.
fmFetch, callFMScript, WebViewerAdapter, callback setup, and script bridge mechanics.development
webviewer fmFetch callFMScript WebViewerAdapter globalSettings setWebViewerName SendCallback window.FileMaker browser-only FileMaker Web Viewer script execution fire-and-forget FMScriptOption PerformScript callback fetchId handleFmWVFetchCallback
development
ENTRY POINT for @proofkit/fmodata projects. Generate TypeScript table schemas with entity IDs from FileMaker OData metadata using @proofkit/typegen. Covers proofkit-typegen-config.jsonc for OData mode, npx @proofkit/typegen setup, fmTableOccurrence generation, entity IDs (FMFID/FMTID), generated output structure, field exclusion, type overrides, InferTableSchema, env var configuration, OData prerequisites, fmodata privilege, and why typegen is required for entity ID correctness.
development
OData performance patterns for @proofkit/fmodata. Covers defaultSelect schema vs all, select() for minimal field fetching, select("all") override, pagination with top/skip, default 1000 record limit, batch operations for reducing round trips, entity IDs FMFID FMTID for rename resilience, null field query performance, getQueryString() debugging, relationship query performance testing, FileMaker OData optimization, avoiding OData service overload during testing.
tools
fmodata OData FMServerConnection fmTableOccurrence field builders textField numberField dateField timestampField containerField calcField listField query builder execute() filter operators eq ne gt gte lt lte contains startsWith endsWith matchesPattern inArray notInArray isNull isNotNull and or not tolower toupper trim CRUD insert update delete byId where navigate expand relationships batch Result error handling Effect.ts pattern FMODataError HTTPError ODataError ValidationError BatchTruncatedError entity IDs FMTID FMFID defaultSelect readValidator writeValidator orderBy asc desc top skip single maybeSingle count getSingleField FileMaker OData API schema management webhooks getTableColumns select("all")