Widget Categories
Key Capabilities
Quick Links
Getting Started
Build your first interactive app in a few minutes. No prior experience required.
The Builder Interface
Your First App — Step by Step
Open the Widgets tab in the left panel. Find a widget (e.g. Button) and drag it onto the canvas. It appears at your drop position.
Click the widget to select it. The right Properties Panel shows all configurable properties — change the label text, colours, size, and font directly.
In Properties Panel, scroll to Events and click the script icon next to onClick. The Script Editor opens — write your first script:
show toast "Hello, World!"
Click the ▶ Preview button in the top bar. Your app goes live in an interactive preview. Click your button to see the toast message appear.
Click the + next to the Card Tabs at the top of the canvas to add new pages/screens. Use go to card CardName in scripts to navigate between them.
When ready, click Publish in the top bar. The app appears in the App Library for regular users to launch. Or use Export to download a standalone HTML file.
Essential Script Patterns
Show / Hide Widgets
show widget MyPanel
hide widget LoadingSpinner
show loading -- full-screen overlay
hide loading
Read and Set Widget Properties
-- Read the current value of a widget
set variable userName to the text of widget NameField
-- Set a widget property
set the text of widget StatusLabel to "Welcome, " + userName + "!"
set the bgColor of widget MyButton to "#10b981"
set the value of widget VolumeSlider to 75
Variables
set variable count to 0
set variable message to "Hello"
set variable total to variable price * variable quantity
-- Variables persist across card navigations automatically
Navigation
go to card Dashboard
go to card SettingsPage
go back
-- Show/hide a card tab dynamically (role-based navigation)
set card AdminPanel visibility to false
set card AdminPanel visibility to true
-- Open an external URL (same tab or new tab)
open url "https://example.com"
open url "https://docs.example.com" in new tab
-- Print / PDF (single card)
print card as pdf
-- Batch print (multi-record — one page per DB record)
start print batch
-- ... loop updating widgets ...
add current card to batch
-- ... end loop ...
print batch as pdf
Conditions
if the text of widget EmailField is empty then
show toast "Please enter your email" with background color "#ef4444"
else if the text of widget EmailField contains "@" then
show toast "Valid email!"
else
show toast "Enter a real email address" with background color "#f59e0b"
end if
HTTP Requests
-- GET request
get url "https://api.example.com/products"
set variable products to result
-- POST request
set variable payload to "{\"name\":\"" + the text of widget NameField + "\"}"
post payload to url "https://api.example.com/users" as json
-- After any HTTP call, these variables are set:
-- result → response body
-- httpStatus → "200", "404", etc.
-- httpError → error message or "" on success
Tip: Watch variables update in real-time during preview. Switch to the Variables tab in the left panel while preview is running — every variable shows its live value.
Show Toast Notifications
show toast "Saved!"
show toast "Error saving" with background color "#ef4444"
show toast "Info message" with duration 5000 with position "top-right"
Loop Over Data
for each item in myList
print item
end for
repeat 3 times
set variable count to count + 1
end repeat
Undo / Redo & Snapshots
Use Ctrl+Z / Ctrl+Shift+Z to undo and redo canvas changes. Click the 📸 button in the top bar to save a named snapshot — restore it any time to jump back to that exact state.
SMART Script Reference
A plain-English scripting language. Attach scripts to widget events, card loads, or run them from other scripts.
Scripts are case-insensitive for keywords. Widget names are case-sensitive. Lines starting with -- are comments.
Variables
set variable myVar to "Hello World"
set variable count to 42
set variable price to 9.99
set variable isActive to true
set variable items to ["apple", "banana", "cherry"]
-- Math
set variable total to variable price * variable quantity
set variable pct to variable correct / variable total * 100
-- String concatenation
set variable greeting to "Hello, " + variable userName + "!"
-- String interpolation (use curly braces)
set variable msg to "Order #{orderId} is ready!"
Reading Widget Properties
-- Text inputs
set variable name to the text of widget NameField
-- Other properties
set variable checked to the checked of widget AgreeBox
set variable rating to the value of widget StarRating1
set variable tags to the tags of widget TagInput1
set variable data to the data of widget MyTable
Setting Widget Properties
set the text of widget MyLabel to "Updated!"
set the bgColor of widget MyCard to "#3b82f6"
set the value of widget MySlider to 75
set the data of widget MyTable to variable rows
set the visible of widget MyPanel to "false"
show widget MyPanel -- shorthand for visible = true
hide widget MyPanel -- shorthand for visible = false
Conditions
if variable count is greater than 10 then
show toast "Count exceeded!"
end if
if the text of widget Input is empty then
show toast "Required"
else if the text of widget Input contains "@" then
show toast "Looks like an email"
else
show toast "Text entered"
end if
-- Boolean conditions
if variable isActive is "true" then ... end if
if variable status is not "error" then ... end if
if variable name contains "Smith" then ... end if
if variable list is not empty then ... end if
-- AND / OR
if variable age is greater than 18 and variable country is "UK" then ... end if
if variable type is "admin" or variable type is "superadmin" then ... end if
Loops
-- Count loop
repeat 5 times
set variable total to total + 1
end repeat
-- For each loop
for each item in myList
print item
end for
-- While loop
set variable attempts to 0
repeat while attempts is less than 3
set variable attempts to attempts + 1
call webhook "TryConnect"
if webhookStatus is "200" then
break
end if
wait 1 seconds
end repeat
-- Until loop (inverse of while)
repeat until variable done is "true"
call webhook "PollStatus"
set variable done to webhookResponse
wait 2 seconds
end repeat
-- Loop control
break -- exit loop immediately
continue -- skip to next iteration
HTTP Requests
-- GET request
get url "https://api.example.com/data"
set variable data to result
set variable status to httpStatus
-- POST with JSON
set variable payload to "{\"name\":\"Alice\",\"age\":30}"
post payload to url "https://api.example.com/users" as json
-- POST with a variable as body
post variable formData to url "https://api.example.com/submit" as json
-- Custom headers
set header "Authorization" to "Bearer " + variable token
set header "X-Api-Key" to "my-key"
get url "https://api.example.com/protected"
clear headers
-- After any HTTP call:
-- result → raw response body
-- httpStatus → "200", "404", etc.
-- httpError → "" on success, error message on failure
-- Error handling
try
get url "https://api.example.com/data"
set the data of widget Table1 to result
on error
show toast "Failed: {httpError}" with background color "#ef4444"
end try
JSON Handling
-- Read a field from a JSON string
set variable name to json field "name" of result
set variable price to json field "data.product.price" of result
set variable first to json field "items[0].title" of result
-- Build JSON manually
set variable obj to "{\"key\":\"" + variable val + "\"}"
-- Parse JSON array
set variable list to result
for each row in list
set variable id to json field "id" of row
print id
end for
Math Functions
round(x) or round of Xfloor(x) or floor of Xceiling(x) or ceiling of Xabs(x) or abs of Xmin(x,y) or min of X and Ymax(x,y) or max of X and Ypow(x,n)sqrt(x)random(min,max) or random between X and YString Functions
length(x) or length of Xuppercase(x) or uppercase of Xlowercase(x) or lowercase of Xtrim(x) or trim of Xreplace(x,old,new)\n (newline), \r\n (CRLF), \t (tab)replacepattern(str,pattern,repl)"/foo/gi" or plain text. Backreferences: $1, $2…matchpattern(str,pattern)"" if none. Use in expressions: set variable d to matchpattern(text, "\d+")matchall(str,pattern)for each.replace pattern P in X with R [into V]replaceResult (or VResult with optional into).split(x,delim) or split X by Yindexof(str,search)substring(str,start,len)number(x) / tonumber(x)text(x) / totext(x)String Escape Sequences
Use these inside any string literal (double-quoted value) in SMART Script:
\n\r\r\n\t\\\"-- Strip CRLF line breaks from a database memo field before JSON parsing:
get url "https://yourserver/getClients.esp"
set variable myClients to replace(result, "\r\n", "")
set variable myClients to replace(myClients, "\n", "")
set the clients of widget RouteMap to myClients
-- Split tab-separated values:
set variable cols to split(rowData, "\t")
List / Array Operations
-- Create a list
set variable colors to ["red", "green", "blue"]
-- Add / remove items
add "yellow" to list colors
remove "red" from list colors
-- Read items
set variable first to item 1 of colors
set variable last to item last of colors
set variable count to count of colors
-- Sort and filter
set variable sorted to sort colors by "name" ascending
set variable active to filter myList where "status" equals "active"
set variable top5 to limit myList to 5
-- In-place mutations
sort list myList by "date"
reverse list myList
shuffle list myList
-- Aggregate
set variable total to sum of priceList
set variable avg to avg of scoreList
set variable unique to unique of tagList
-- Pluck and group
set variable names to pluck "name" from userList
set variable grouped to group userList by "department"
-- Multi-condition filter
set variable result to filter orders where "status" equals "open" and "amount" is greater than 100
Date & Time
set variable now to date now -- current date/time as string
set variable today to date today -- today's date (YYYY-MM-DD)
set variable ts to timestamp now -- Unix timestamp in ms
Storage (Persistent Variables)
-- Save a variable to localStorage (persists after page refresh)
save variable myData to storage
-- Load it back
load variable myData from storage
-- Clear a specific key
clear storage myData
Navigation
go to card HomePage
go to card OrderSummary
go back
-- Show/hide a card tab dynamically (role-based navigation)
set card AdminPanel visibility to false
set card AdminPanel visibility to true
-- Open an external URL
open url "https://example.com"
open url "https://example.com" in new tab
Print / PDF
-- Print the current card (opens browser Save as PDF dialog)
print card as pdf
-- Conditional print
if patientName is not empty then
print card as pdf
end if
Generate PDF (direct download, no dialog)
Use the PDF Generator widget or the generate pdf command to capture the current card as a PDF and download it immediately — no browser print dialog.
-- Download with default filename
generate pdf
-- Download with a custom filename
generate pdf "invoice-" & variable orderId & ".pdf"
-- Check result
if variable pdfStatus is "ok" then
show toast "PDF saved!"
else
show toast "Error: " & variable pdfError
end if
Batch Print (multi-record PDF)
Loop through database records and render each as a page in one PDF — for run sheets, meal cards, client summaries, etc.
-- 1. Reset the collector
start print batch
-- 2. Loop through records, updating widgets and snapshotting each
open table "Meals"
go to first record
set variable i to 1
repeat while i <= total
set the mealName of widget MealTitle to db field "mealName"
set the value of widget NutritionFacts to db field "nutritionJson"
add current card to batch
go to next record
set variable i to i + 1
end repeat
-- 3. Open one print dialog with all collected pages
print batch as pdf
File Manager
Use the File Manager widget to browse, upload, download, and delete uploaded assets. The refresh files command reloads the file list programmatically.
-- Reload the file list after an external action
refresh files of widget DocLibrary
-- React to file selection (onFileSelect event)
on widget DocLibrary onFileSelect
set the pdfUrl of widget DocViewer to selectedFileUrl
set the text of widget FileLabel to selectedFilename
end on
-- React to upload (onFileUpload event)
on widget DocLibrary onFileUpload
show toast "Uploaded: " & uploadedFilename
refresh files of widget DocLibrary
end on
Timers
-- Repeating timer
every 5 seconds run script UpdateFeed
every 1 seconds run script TickClock
-- One-shot delay
after 3 seconds run script ShowWelcome
after 0.5 seconds run script AnimateIn
-- Stop all timers
stop timer
Media & Animation
-- Video (Video widget)
play video MyPlayer
pause video MyPlayer
-- Sound (PlaySound widget / audio URL)
play sound "success"
stop sound "success"
-- Lottie Animation (LottieAnimation widget)
play animation widget MyAnim
pause animation widget MyAnim
stop animation widget MyAnim
-- Change animation properties at runtime
set the src of widget MyAnim to "https://assets.lottiefiles.com/lf20_xyz.json"
set the speed of widget MyAnim to 2
set the loop of widget MyAnim to false
-- Audio Player (AudioPlayer widget)
play audio widget MyAudio
pause audio widget MyAudio
stop audio widget MyAudio -- pause + reset to beginning
-- Set source and volume at runtime
set the src of widget MyAudio to "https://example.com/track.mp3"
set the volume of widget MyAudio to 0.5
-- Read current playback position (seconds — updated during playback)
set variable elapsed to audioCurrentTime
Vibrate / Haptic Feedback
vibrate -- 100 ms short tick (default)
vibrate 200 -- 200 ms
vibrate 500 -- 500 ms
-- Confirm a saved action
vibrate 150
show toast "Saved!"
-- Alert on error
vibrate 400
set the text of widget ErrorMsg to "Invalid input"
-- Double pulse (two short buzzes)
vibrate 80
wait 0.1 seconds
vibrate 80
Uses the browser Vibration API. Silently ignored on desktop and iOS Safari. Requires HTTPS or localhost.
Share (Web Share API)
-- Share a URL
share url "https://myapp.com"
-- Share URL with title and body text
share url "https://myapp.com/product/123" title "Check this out" text "Found something great!"
-- Share just text (no URL)
share text "I scored " & score & " points — can you beat me?"
-- Share using variables
share url pageUrl title pageTitle text pageDesc
Opens the device's native share sheet (Chrome Android, Safari iOS). All three parts — url, title, text — are optional but at least one should be provided. User dismissal is silently ignored. Requires HTTPS.
Text-to-Speech
-- Basic: speak any text or variable
speak text "Hello, welcome to the app!"
speak text myVariable
-- With optional voice parameters
speak text "Warning: step incomplete." with rate 0.9 with pitch 1.2 with volume 0.8
-- Stop any ongoing speech
stop speaking
-- Read a widget value aloud on button press
on button ReadOut pressed
speak text the text of widget ResultLabel
end
-- Step instructions at reduced speed
speak text "Step one: connect the cable." with rate 0.8
Uses the browser Web Speech API (SpeechSynthesis). Each speak text cancels any active utterance first. with rate (0.1–10), with pitch (0–2), with volume (0–1) are all optional. Silently ignored if the browser does not support speech synthesis.
Clipboard
-- Copy any value to the clipboard
copy "https://app.example.com/share/4821" to clipboard
copy the text of widget InviteCode to clipboard
copy myVariable to clipboard
-- Paste clipboard text into a variable
set variable pastedText to paste from clipboard
set the text of widget SearchBox to pastedText
-- clipboardStatus is set automatically after every copy / paste
if clipboardStatus is "success" then
show toast "Copied!" with duration 1500
else
show toast "Clipboard unavailable" with background color "#ef4444"
end if
Uses the browser Clipboard API (navigator.clipboard). Requires HTTPS or localhost. clipboardStatus is automatically set to "success" or "error" after every copy or paste call.
Theme Switching
-- Switch to any preset theme at runtime
set theme to "Light"
set theme to "Dark Blue"
set theme to "Midnight"
-- Toggle dark / light from a button
if variable appTheme is "light" then
set theme to "Dark Blue"
set variable appTheme to "dark"
else
set theme to "Light"
set variable appTheme to "light"
end if
-- Drive from a Dropdown widget
set theme to the value of widget ThemePicker
Available theme names: Dark Blue · Dark Cyan · Midnight · Ocean · Forest · Sunset · Rose · Light. The change takes effect immediately. The project's saved theme (App Settings) is restored when preview restarts.
Webhooks
-- Call a named webhook (configured in App Settings → Webhooks)
call webhook "SubmitOrder"
-- Call with specific variables as payload
call webhook "SubmitOrder" with data "orderId,total,email"
-- After call:
-- webhookStatus → HTTP status code
-- webhookResponse → raw response body
Send Email (Resend)
-- Configure API key in App Settings → Email first
send email to "user@example.com" subject "Welcome" body "Thanks for signing up!"
send email to recipient subject "Receipt #{orderId}" body "Total: £{total}" as html
-- After sending:
-- emailStatus → "200" = sent, other = error
-- emailResult → raw Resend API response
User-Defined Commands
-- Define a reusable command (in any script)
command RefreshDashboard
get url "https://api.example.com/stats"
set the data of widget StatsTable to result
set the value of widget RevGauge to json field "revenue" of result
end command
-- Call it anywhere
RefreshDashboard
Validation
validate widget EmailField as email
validate widget PhoneField as required
validate widget AgeField as min-value 18
validate widget DescriptionField as max-length 500
-- After validation:
-- validationPassed → "true" or "false"
-- validationError → error message or ""
if validationPassed is "false" then
show toast validationError with background color "#ef4444"
end if
clear validation widget EmailField
Form Groups
-- Collect all child inputs at once
submit form widget ContactForm -- triggers onSubmit with formData variable
reset form widget ContactForm -- clears all child inputs
Phone, Currency & Time Inputs
-- PhoneInput — read combined value (e.g. "+44 7700 900000")
set variable phone to phoneNumber -- set via outputVariable property
set the value of widget PhoneField to "7700 900123"
set the countryCode of widget PhoneField to "+44"
-- CurrencyInput — value is always a number
set variable amount to currencyValue -- set via outputVariable property
set the value of widget PriceBox to 99.99
-- TimePicker — value is always HH:MM (24-hour internally)
set variable t to the value of widget StartTime
set the value of widget StartTime to "09:00"
-- timeValue variable is also set automatically on every selection
-- Combine date + time for an appointment
set variable appointment to dateValue + " " + timeValue
File Download
-- Trigger a browser download from any variable — no server needed
save file "results.csv" with content csvData
save file "export.json" with content jsonString
save file "photo.png" with content croppedImageData -- base64 data URLs auto-decoded
-- Dynamic filename
set variable today to "2025-06-20"
save file "report-" + today + ".csv" with content reportCsv
-- MIME type is inferred from the extension:
-- .csv → text/csv .json → application/json .txt / .md → text/plain
-- .html → text/html .xml → application/xml
-- .png / .jpg / .gif / .svg → image/* .pdf → application/pdf
Download File from URL
Fetch a file from any URL and trigger a browser download. Filename is inferred from the URL if not given.
download file from url "https://api.example.com/report.pdf"
download file from url invoiceUrl as "invoice-" & invoiceNumber & ".pdf"
-- Variable set:
-- downloadStatus → "ok" or error message
Push Notifications
-- OS-level browser notification (appears in system tray / notification centre)
send notification "Title"
send notification "Order ready" with message "Your order #4821 is ready to collect."
send notification "Alert" with message "Low stock warning" with icon "https://yourapp.com/icon.png"
-- notificationStatus is set automatically:
-- "sent" → notification fired successfully
-- "denied" → user blocked permission
-- "unsupported" → browser doesn't support Notification API
if notificationStatus is not "sent"
show toast "Notifications blocked: " + notificationStatus
end if
Custom Analytics Events
-- Log a named event — appears in Analytics tab → Custom events
log event "ButtonClicked"
-- Log with an optional value for richer filtering
log event "ProductViewed" with value currentProduct
log event "FormSubmitted" with value "ContactForm"
-- Any expression works as the event name or value
log event "StepCompleted" with value stepNumber
Geolocation
-- Get device position (async — waits for browser permission + GPS fix)
get current location
-- Variables set automatically:
-- locationLat e.g. "51.5074"
-- locationLng e.g. "-0.1278"
-- locationAccuracy metres, e.g. "18"
-- locationStatus "ok" or error message
if locationStatus is "ok"
set the text of widget LatLabel to "Lat: " + locationLat
set the text of widget LngLabel to "Lng: " + locationLng
else
set the text of widget Status to "Error: " + locationStatus
end if
-- Build a map link
get current location
set variable mapUrl to "https://maps.google.com/?q=" + locationLat + "," + locationLng
Batch Geocoding (Geocode Widget)
The Geocode widget converts a JSON array of address strings into latitude/longitude coordinates. Set the addresses property, then click Geocode All in preview — or drive it from a script. Supports free Nominatim (OpenStreetMap, 1 req/sec) and Google Maps Geocoding API.
-- Load addresses from an API
on card LocationsPage load
get url "/api/delivery-stops"
set the addresses of widget Geocoder1 to json field "addresses" of response
end on
-- When batch completes — variables set automatically:
-- geocodeResults JSON array of {address, lat, lng, formattedAddress}
-- geocodeFailed JSON array of {address, error}
-- geocodeSuccessCount number of successful geocodes
-- geocodeFailCount number of failures
on widget Geocoder1 onGeocodeComplete
set the markers of widget LiveMap1 to geocodeResults
if geocodeFailCount > 0 then
show toast geocodeFailCount & " address(es) could not be resolved"
end if
end on
-- A single address failed — variables set:
-- geocodeAddress the address string that failed
-- geocodeError reason (e.g. "No results found")
on widget Geocoder1 onGeocodeError
show toast "Could not geocode: " & geocodeAddress
end on
-- User clicks a geocoded row — variables set:
-- geocodeAddress original address string
-- geocodeLat latitude (number)
-- geocodeLng longitude (number)
-- geocodeFormattedAddress full formatted address from geocoder
-- geocodeRow full JSON object {address, lat, lng, formattedAddress}
-- geocodeMarker map-ready JSON {id, lat, lon, label, color} — note "lon" not "lng"
on widget Geocoder1 onRowClick
set the markers of widget LiveMap1 to "[" & geocodeMarker & "]"
set the text of widget LatLabel to geocodeLat
set the text of widget LngLabel to geocodeLng
end on
Device Detection
Detect whether the app is running on a phone, tablet, or desktop. No permissions required — runs instantly.
get device type
-- Variable set:
-- deviceType → "phone" | "tablet" | "desktop"
-- Redirect to the right layout
get device type
if deviceType is "phone"
go to card MobileHome
else if deviceType is "tablet"
go to card TabletHome
else
go to card DesktopHome
end if
Script Output (Print / Toast)
print "Debug message" -- shows in Console (Variables panel)
Show Toast Notifications
Display a non-blocking popup message to the user. All options are optional and can be combined in any order.
Syntax
show toast "message"
show toast "message" with duration <milliseconds>
show toast "message" with position "<position>"
show toast "message" with background color "<hex>"
show toast "message" with text color "<hex>"
Duration
Duration is in milliseconds. Default is 3000 (3 seconds). Use 0 to keep it on screen until the next toast replaces it.
show toast "Saved!" with duration 2000
show toast "Processing…" with duration 8000
show toast "Done" with duration 1500
Position
Six positions are available:
show toast "Top left" with position "top-left"
show toast "Top centre" with position "top-center"
show toast "Top right" with position "top-right" -- default
show toast "Bottom left" with position "bottom-left"
show toast "Bottom centre" with position "bottom-center"
show toast "Bottom right" with position "bottom-right"
Background & Text Colour
-- Success (green)
show toast "Record saved!" with background color "#16a34a"
-- Warning (amber)
show toast "Low stock warning" with background color "#d97706"
-- Error (red)
show toast "Save failed" with background color "#dc2626"
-- Info (blue)
show toast "Sync complete" with background color "#2563eb"
-- Custom text colour
show toast "Note" with background color "#1e293b" with text color "#94a3b8"
Combining Options
-- Full example: colour + duration + position
show toast "✓ Delivery confirmed" with background color "#16a34a" with duration 4000 with position "bottom-right"
-- Error at top with longer display
show toast "⚠ Connection lost — retrying" with background color "#dc2626" with text color "#fff" with duration 6000 with position "top-center"
-- Quick dismissal
show toast "Copied!" with duration 1200 with position "bottom-center"
Dynamic Messages
-- Use variables and concatenation in the message
set variable clientName to "Alice"
show toast "Welcome back, " & clientName & "!"
-- Show count in toast
show toast geocodeSuccessCount & " addresses geocoded"
-- Conditional colour based on result
if httpStatus is "200" then
show toast "Saved!" with background color "#16a34a"
else
show toast "Error: " & httpStatus with background color "#dc2626"
end if
After HTTP Requests
post url "/api/orders" with body orderData
if response is empty then
show toast "No response from server" with background color "#dc2626"
else if httpStatus is "200" then
show toast "Order placed!" with background color "#16a34a" with duration 3000
else
show toast "Failed: " & httpStatus with background color "#f59e0b"
end if
Run Other Scripts
run script MyHelperScript
run script ValidateAndSubmit
Auth (Published Apps)
login user with variable credentials
logout user
restore session
-- After login:
-- sessionToken → JWT access token
-- userEmail → logged-in email
-- userRole → user's role
WebSockets & SSE
-- WebSocket
connect to websocket "wss://api.example.com/live"
-- Messages arrive as onMessage events (set on the script)
-- wsMessage contains the incoming text
-- Server-Sent Events
subscribe to sse "https://api.example.com/stream"
-- sseMessage contains the incoming data
Barcode Scanner
-- Open the scanner overlay and wait for a scan (camera / upload / manual)
scan barcode into parcelId
-- With a custom variable name
scan barcode into ticketCode
-- Handle cancel (resolves to empty string)
scan barcode into code
if code is "" then
show toast "Scan cancelled"
else
get url "https://api.example.com/items/" + code
end if
NFC
-- Read the next NFC tag and store its text (Chrome on Android, HTTPS only)
scan nfc tag into tagData
-- With a custom variable name
scan nfc tag into productId
-- Write plain text to a tag
write nfc tag with "Hello from Sentrix"
-- Write a JSON payload
write nfc tag with "{\"userId\":\"" + userId + "\"}"
-- Write using "with data" phrasing
write nfc tag with data accessCode
Signature Pad
-- Get the signature as a base64 PNG data URL
get signature of widget SigPad into mySig
-- Clear the canvas
clear signature of widget SigPad
-- Stamp HH:MM DD/MM/YYYY onto the signature
stamp widget SigPad with timestamp
Temperature Logging
-- Record a temperature reading (fires onLog / onFail)
log temperature 4.2 for widget TempLog
log temperature currentTemp for widget TempLog
-- Retrieve full log as JSON array string
get temperature log of widget TempLog into myLog
Row Status
-- Set the status of a row in any list widget
set row status of widget MyList row 2 to "complete"
-- Count all rows, or rows matching a status
get row count of widget MyList into total
get row count of widget MyList where status is "pending" into pendingCount
Row Colour Coding (DataTable)
Apply a background colour to individual rows in a DataTable. Row numbers are 1-based. Accepts any CSS colour string.
-- Colour a specific row
set row color of widget ClientTable row 1 to "#fee2e2"
set row color of widget ClientTable row 3 to "#dcfce7"
-- Clear one row's colour
set row color of widget ClientTable row 1 to ""
-- Clear all custom row colours
clear row colors of widget ClientTable
Background Push Notifications
True background push using the Web Push / VAPID protocol — notifications arrive even when the app tab is closed. Subscribe with your service worker, then send to your own devices or to a specific user in the same org.
-- Subscribe / unsubscribe (run after login)
subscribe to push notifications
unsubscribe from push notifications
-- Send to current user's own devices
send push notification "Title"
send push notification "Title" with body "Body text"
send push notification "Reminder" in 30 seconds
send push notification "Reminder" in 5 minutes
-- Send to a specific user by their user ID
send push notification to user assignedUserId "You have a new task"
send push notification to user assignedUserId "You have a new task" with body taskTitle
-- Variable set automatically:
-- pushStatus → "subscribed" | "unsubscribed" | "sent" | error message
Welfare Alerts — Push to Coordinators
Broadcasts a push notification to all coordinator-role devices in the organisation. Coordinator must call subscribe to push after login. Alert auto-includes driver name, wellness indicators, and GPS.
-- Coordinator setup (once, after login)
subscribe to push
-- Driver: escalate a welfare concern
send welfare alert for client "Mrs Johnson"
send welfare alert for client clientName with notes wellnessNotes
-- Variables set:
-- welfareAlertStatus → "sent" | "error" | "unavailable"
-- welfareAlertRecipients → number of coordinator devices notified
-- WellnessCheckPanel onSubmit — send alert if concerns noted
on widget WellnessCheck onSubmit
if concernsNoted is "yes"
send welfare alert for client currentClientName with notes wellnessNotes
show toast "⚠️ " & welfareAlertRecipients & " coordinator(s) notified"
end if
end on
Calendar Event Commands
Add, remove, and read events on a Calendar widget at runtime. Events are stored as a JSON array on the widget's events property. Each event object must include at minimum an id, title, date, startTime, and endTime.
-- Add an event to a Calendar widget
add event to widget MyCalendar using "{""id"":""e1"",""title"":""Team Meeting"",""date"":""2025-06-12"",""startTime"":""09:00"",""endTime"":""10:00"",""color"":""#3b82f6""}"
-- Remove an event by its id
remove event from widget MyCalendar where id is "e1"
-- Read the date the user last clicked / navigated to
set variable chosen to the selected date of widget MyCalendar
show toast "Selected: " & chosen
-- Dynamic booking flow example
on widget BookBtn onClick
set variable newEvent to "{""id"":""" & bookingId & """,""title"":""" & clientName & """,""date"":""" & bookingDate & """,""startTime"":""" & startTime & """,""endTime"":""" & endTime & """}"
add event to widget AppointmentCalendar using newEvent
show toast "Appointment added"
end on
-- Cancel a booking
on widget CancelBtn onClick
remove event from widget AppointmentCalendar where id is selectedBookingId
show toast "Booking removed"
end on
Time Slot Picker Commands
Control the available/booked/blocked state of individual time slots in a TimeSlotPicker widget. Slots are identified by their time string (e.g. "09:00"). Valid statuses are available, booked, and blocked.
-- Mark a slot as booked
mark slot "09:00" in widget MySlotPicker to "booked"
-- Mark a slot as blocked (unavailable for selection)
mark slot "14:30" in widget MySlotPicker to "blocked"
-- Restore a slot to available
mark slot "11:00" in widget MySlotPicker to "available"
-- Use a variable for the slot time
mark slot selectedTime in widget MySlotPicker to "booked"
-- Booking confirmation flow
on widget SlotPicker onSlotSelect
-- slotTime and slotStatus are set automatically by onSlotSelect
if slotStatus is "available"
mark slot slotTime in widget SlotPicker to "booked"
set the text of widget ConfirmLabel to "Booked: " & slotTime
end if
end on
-- Admin: block lunch break
on widget BlockLunchBtn onClick
mark slot "12:00" in widget SlotPicker to "blocked"
mark slot "12:30" in widget SlotPicker to "blocked"
show toast "Lunch break blocked"
end on
Interactive State Overrides
Configure visual changes that apply automatically when a widget enters hover, active, focus, or disabled state — no script required. Set up in the Properties panel → Interactive States section.
| State | When active |
|---|---|
hover | Pointer enters the widget |
active | Mouse/touch held down (pressed) |
focus | Widget has keyboard focus |
disabled | Widget's disabled or readonly property is true |
For each state you can override any visual property (bgColor, textColor, borderRadius, shadow, etc.). Overrides apply only in Preview; the canvas always shows design-time values.
-- No script needed — configure in Properties panel:
-- Button hover: bgColor #2563eb (darker blue on hover)
-- Button active: bgColor #1d4ed8 (even darker when pressed)
-- TextField focus: borderColor #06b6d4 (cyan focus ring)
-- Card hover: shadow true (elevation effect)
-- Label disabled: textColor #475569 (muted when readonly)
Visual Script Debugger
Step through scripts line-by-line with the built-in debugger. Enable it with the 🐛 Debug toggle, set breakpoints by clicking the editor gutter (or press F9 at cursor), then use the step controls.
| Key | Action |
|---|---|
| F5 | Continue — run to next breakpoint |
| F10 | Step Over — skip nested run script calls |
| F11 | Step Into — descend into run script calls |
| ⇧F11 | Step Out — return to the script caller |
| F9 | Toggle breakpoint at cursor line |
The 🐛 Debugger tab in the Preview right panel shows: current pause location, full call stack with line numbers, watch expressions, and all runtime variables. Auto-activates when execution pauses.
Data Layer
Connect REST APIs to your widgets without writing a single script line. Define sources, model their fields, then bind those fields to widget properties.
How It Works
Open the 🔌 Data tab in the left panel. Click Add Data Source. Paste a URL, choose GET/POST method, add optional headers and body, and toggle Auto-fetch if you want it to run when the card loads.
Click Test to make the actual API call and see the raw JSON response inline. Verify the shape of the data before moving on.
Click Add Data Model. Pick the source, then add fields using dot-notation paths to extract values from the JSON:
title → data.product.title
price → data.product.price
stock → data.inventory[0].qty
Click Test Model — each field lights up green showing its resolved value, or red if the path is wrong.
Select a widget on the canvas. Scroll to Data Bindings in the Properties Panel. Click Bind next to any property and choose a model field.
Press Preview — bound widgets populate automatically. No scripts needed.
Using Data Sources in Scripts
-- Trigger a fetch manually in a script
fetch data from ProductsAPI
-- After fetch, model field values are available as variables:
-- ModelName_fieldKey
set the text of widget PriceLabel to variable ProductModel_price
set the text of widget TitleLabel to variable ProductModel_title
Tip: Use Auto-fetch on your data source for data you always want loaded when the card appears. Use the script command fetch data from SourceName for on-demand or triggered fetches.
Real-Time Data
WebSocket
-- Open a persistent WebSocket connection
connect to websocket "wss://api.example.com/live"
-- The script's onMessage event fires for each message
-- wsMessage variable contains the incoming text or JSON
Server-Sent Events (SSE)
-- Subscribe to an SSE stream
subscribe to sse "https://api.example.com/stream"
-- The script's onMessage event fires for each event
-- sseMessage variable contains the event data
Webhooks (Outbound)
Configure outbound webhooks in App Settings → Webhooks. Add a name and URL, then call from any script:
call webhook "SubmitOrder"
call webhook "NotifySlack" with data "orderId,customerName,total"
-- After call:
-- webhookStatus → HTTP status code ("200" = success)
-- webhookResponse → raw response body
Email (Resend API)
Configure your Resend API key in App Settings → Email. Set the From Address and From Name.
send email to "customer@example.com" subject "Order Confirmed" body "Your order is on its way!"
send email to variable emailAddr subject "Invoice #{num}" body htmlContent as html
-- emailStatus → "200" on success
-- emailResult → raw API response
User Authentication (Published Apps)
-- Login
login user with variable credentials
-- Logout
logout user
-- Restore session on card load
restore session
-- After login:
-- sessionToken → JWT access token
-- userEmail → logged-in user's email
-- userRole → "admin" / "builder" / "viewer"
Authentication calls go directly from the browser to the backend. Ensure your backend is running and CORS is configured for the app's domain when using web exports.
Widget Reference
All 146 widgets available in Sentrix. Click any widget to see its full property reference and example scripts.
Keyboard Shortcuts
All shortcuts are active while the canvas has focus. They're automatically disabled when a text input or the script editor has focus.
Press ? at any time on the canvas to open the interactive shortcuts reference directly inside the app.