Metafore
Building Apps

Pulse Dashboards

Create, configure, and deploy AI-generated widget dashboards for your application users.

Pulse is the dashboard builder inside the App Workspace. It lets you create interactive widget boards — KPIs, charts, tables, action cards, and more — powered by AI generation. Each Pulse dashboard is scoped to a specific Agent and Role, so different users see dashboards tailored to their responsibilities.

The Pulse tab

In the App Workspace, select the Pulse tab to open the Widget Generator. This is your home base for managing all Pulse dashboards in the current application.

Existing Pulses list

The main view shows a table of every Pulse dashboard that has been created. Each row displays:

  • Name — the dashboard name, with a "Draft" badge if it has not been deployed yet
  • Agent — the AI agent this dashboard is attached to
  • Role — the user role that will see this dashboard
  • Created By — who originally created the pulse
  • Last Edited — when it was last modified

You can search across pulses using the search bar (results update as you type), and adjust pagination at the bottom of the table.

Row actions

Each row offers these actions:

  • Preview on Platform — opens the deployed dashboard in a new browser tab so you can see it as an end user would
  • Edit — opens the Add/Edit modal so you can change the pulse name, agent, role, or prompt
  • Delete — permanently removes the pulse after a confirmation prompt

If you have read-only access (BUILD_APPS_VIEW permission), edit and delete actions are hidden.

Creating a new Pulse

Click + Add a Pulse to open the creation modal. Fill in the four required fields:

  1. Pulse Name — a descriptive name for this dashboard (e.g., "Sales Overview", "Support Metrics")
  2. Agent — select which AI agent powers this dashboard from the dropdown. At least one agent must exist in the application before you can create a pulse.
  3. Role — select the user role this dashboard is built for
  4. Pulse Prompt — describe in plain language what this dashboard should contain. For example: "Show total revenue this quarter, top 5 products by sales, a trend chart of monthly orders, and a list of recent high-priority support tickets."

Click the Generate Pulse button (with the sparkle icon) to create the dashboard. The system sends your prompt to the AI, which generates an initial set of widgets. You are taken directly into the Pulse builder workspace where you can see the generated widgets streaming in.

How generation works

When you generate a pulse, the AI reads your prompt and produces a set of widget specifications using the Pulse Widget DSL — a declarative JSON format that defines each widget's layout, data bindings, and appearance. The generation happens via a real-time stream (Server-Sent Events), so you see widgets appear progressively rather than waiting for everything at once.

Editing an existing Pulse

Click Edit from a pulse row's action menu to reopen the modal. In edit mode you can change the pulse name, agent, or role. The prompt field shows the original prompt used during creation. Click Update Pulse to save changes.

Note that editing the pulse metadata (name, agent, role) does not regenerate widgets — it only updates the pulse configuration. To modify widgets, open the pulse by clicking its row, which takes you into the builder workspace.

The Pulse builder workspace

Clicking a pulse row (or generating a new one) opens a fullscreen workspace where you design the dashboard. The workspace has several key areas.

Header and navigation

The top bar shows a breadcrumb trail: Pulse > [Dashboard Name]. If the application has multiple pulses for the same agent, a dropdown lets you switch between them without leaving the workspace. The header also contains action buttons described below.

Tab strip

A Pulse dashboard can have multiple tabs, each containing its own set of widgets. Tabs appear as pill-style buttons in a horizontal strip below the header.

Adding a tab: Click the + button at the right end of the tab strip to add a new tab. Provide a name and an optional prompt describing what this tab should contain.

Renaming a tab: Double-click any tab label to enter edit mode. Type the new name and press Enter to confirm, or Escape to cancel.

Reordering tabs: Drag a tab by its grip handle (the six-dot icon to the left of the tab name) and drop it at the desired position.

Deleting a tab: Hover over a tab to reveal the trash icon. Click it to mark the tab for deletion. The tab is only removed when you save or deploy — until then, you can undo the change.

All tab changes (adds, renames, reorders, deletes) are tracked as pending changes and are only committed when you save or deploy.

The widget board

The main content area displays widgets in a responsive masonry grid layout. Widgets are arranged in a multi-column grid where each widget can span a configurable number of columns.

Selecting a widget: Click any widget to open it in the individual widget editor (described below).

Resizing a widget: Each widget tile has width controls at the bottom — use the decrease-width and increase-width buttons to step through the available column spans (quarter, half, three-quarter, and full width).

Removing a widget: Click the trash icon on any widget tile's footer bar to remove it from the board.

Reordering widgets: Click the Reorder button in the header to enter drag-and-drop reorder mode. Grab any widget tile and drag it to a new position. The masonry layout recalculates automatically. Click the button again to exit reorder mode.

Adding a new widget

Click + Add Widget in the header to open the widget creation workspace. This view provides:

  • Prompt suggestions — four quick-start suggestions appear as clickable chips (e.g., "Show a revenue trend chart", "Display top customers"). Click one to populate the prompt, or type your own.
  • AI prompt dock — a text area at the bottom where you describe the widget you want. Type your description and click Make it (sparkle icon) to generate the widget via AI.
  • Live preview — the generated widget renders in real time above the prompt dock so you can see exactly what it will look like.
  • Undo / Redo — step backward and forward through generation iterations.
  • Code panel — click the Code button to open a side panel showing the raw DSL JSON. You can edit the JSON directly and changes are reflected in the preview.
  • Add button — once satisfied, click Add to place the widget onto the dashboard board.

Editing an individual widget

Click any widget on the board (or click the Improve with AI sparkle link on its footer) to open the widget editor. This workspace is similar to the add-widget view but operates on an existing widget:

  • Breadcrumb — shows Pulse > [Dashboard Name] > [Widget Name] so you know exactly which widget you are editing
  • AI refinement prompt — describe how you want to change the widget. For example: "Change the chart color to blue", "Add a percentage column", or "Show only the top 10 items". Click Make it to apply the AI refinement.
  • Live preview — see the widget update in real time as refinements are applied
  • Undo / Redo — roll back or reapply changes across your editing session
  • Code panel — view and hand-edit the DSL JSON for fine-grained control
  • Info popover — click the (i) button to see metadata about the widget (type, role, purpose)
  • Save — click Save to persist your changes back to the board. The button is disabled if no changes have been made or if the JSON has syntax errors.

The Code drawer

Both the add-widget and edit-widget workspaces offer a Code panel that slides in from the right side. This panel shows the widget's DSL specification as formatted JSON.

  • Edits you make in the code panel are parsed and validated in real time (with a 400ms debounce)
  • If the JSON is invalid, an error message appears and the preview will not update
  • Valid JSON changes update the live preview immediately
  • On larger screens, the code panel appears side-by-side with the preview (taking roughly 40% width); on smaller screens it stacks below

This is useful for power users who want to fine-tune widget properties, data bindings, or conditional logic beyond what the AI prompt produces.

Widget types

Pulse supports 15 pre-built widget components, each designed for a specific visualization pattern:

WidgetPurpose
AlertCardStackNotification-style cards with title, subtitle, body, and action buttons
MetricCardStackKPI cards showing a metric value, trend indicator, and optional sparkline
MetricDetailedChartStackMetric cards with an embedded detailed chart visualization
PieChartStackPie or donut chart visualizations
LineChartTime-series or trend line charts
VerticalBarChartStackVertical bar chart comparisons
HorizontalBarChartStackHorizontal bar chart comparisons
SimpleCardA basic content card for text or summary information
SimpleCardListA scrollable list of simple content cards
IconCardStackCards featuring a prominent icon with supporting text
FeatureListCardA card listing features or items with descriptions
QuickActionCardCards with action buttons for common tasks
StepCardStep-by-step process or workflow visualization
SummaryCardCondensed overview cards for key information
MetricChartStackCombined metric display with an integrated chart

Each widget type has a defined schema that controls how many items it can display, what data fields are available, and how wide it can be on the grid. The AI selects the appropriate widget type based on your prompt.

Saving and deploying

The builder workspace footer shows two action buttons:

Save as Draft

Click Save as Draft to persist your current work without making it visible to end users. Draft pulses show a "Draft" badge in the Pulses list. You can return to edit a draft at any time.

Deploy (Create / Save Pulse)

Click Create Pulse (for new dashboards) or Save Pulse (for existing ones) to deploy the dashboard. Deploying makes the dashboard live — end users with the matching agent and role will see it in their application.

Before saving or deploying, the system automatically flushes any pending tab changes (new tabs, renames, reorders, deletions) in the correct order.

Unsaved changes protection

If you try to close the builder, switch to a different pulse, or change tabs while you have unsaved widget edits, a confirmation modal appears warning you about unsaved changes. You can choose to discard changes and proceed, or cancel and return to save your work first.

Pulse Widget DSL overview

Under the hood, every widget is defined by a Pulse Widget DSL specification — a declarative JSON document. While the AI handles DSL generation for you, understanding the basics can help when using the Code panel for fine-tuning.

Structure

A DSL spec has this general shape:

{
  "component": "MetricCardStack",
  "props": {
    "items": [
      {
        "title": "Total Revenue",
        "metric": { "value": 42500, "unit": "$" },
        "trend": { "value": 12, "unit": "%", "label": "vs last month" }
      }
    ]
  },
  "$data": {
    "revenue": {
      "connector": "rest",
      "@config": { "url": "/api/metrics/revenue", "method": "GET" }
    }
  },
  "@on": {
    "press": { "action": "navigate", "payload": { "to": "/details" } }
  }
}

component — which widget type to render. props — the widget's visual configuration. Values can use JSONata expressions (prefixed with =) to bind to live data. $data — data connector definitions that fetch data from APIs, databases, or static sources. @on — event handlers for user interactions (press, submit, change).

Data connectors

Widgets can connect to five types of data sources:

  • rest — HTTP API calls (GET, POST, etc.)
  • static — hardcoded data embedded directly in the spec
  • supabase — direct Supabase database queries
  • supabase-edge — Supabase Edge Function calls
  • sse — Server-Sent Events for real-time streaming data

Dynamic expressions

Props can use JSONata expressions for dynamic data binding. Prefix any value with = to make it an expression:

{
  "title": "='Total: ' & $string($data.revenue.total)"
}

Available variables in expressions include $data (fetched data), $localState (widget-level state), and $sharedState (dashboard-wide state).

Conditional rendering and iteration

The DSL supports control-flow operators for dynamic layouts:

  • #if / #else — conditionally show or hide sections based on data
  • #each — repeat a section for each item in an array

Actions and events

Widgets can respond to user interactions through the @on system:

  • send_message — send a message to the AI agent
  • open_panel — open a detail panel
  • navigate — go to another page or URL
  • set_local_state / set_shared_state — update widget or dashboard state
  • refetch_data — reload a data connector
  • invoke_connector — trigger a data operation

Theme support

Click the theme toggle button in the header to switch between light and dark mode preview. This lets you verify that your widgets look correct in both themes before deploying.

Switching between Pulses

If the current agent has multiple Pulse dashboards, a dropdown appears in the header breadcrumb area. Select a different pulse from the dropdown to switch to it. If you have unsaved changes, the unsaved-changes confirmation appears first.

When you switch pulses, the workspace loads the selected pulse's tabs and widgets. The header updates to reflect the new pulse's name.

Tips for effective Pulse dashboards

  • Be specific in your prompts. Instead of "show sales data", try "show a bar chart of monthly sales for the last 6 months, with a KPI card showing total revenue and percentage change from last quarter."
  • Use multiple tabs to organize different aspects of a dashboard — e.g., "Overview", "Detailed Metrics", "Alerts".
  • Iterate with AI refinement. Generate a first pass, then use the refinement prompt to adjust colors, add columns, change chart types, or modify data filters.
  • Use the Code panel for precise adjustments the AI might not capture from a prompt — like exact color codes, specific number formatting, or custom JSONata expressions.
  • Preview in both themes before deploying to ensure readability in light and dark mode.
  • Save drafts frequently while building complex dashboards — the unsaved-changes guard helps, but regular saves are a good habit.