display_chart tool from a previous execute_sql result, and both can be adjusted afterwards without re-running the query.
Chart types
The agent picks a chart type from the result set, and you can change it later from the edit dialog.
Stacked bar, stacked area, and horizontal bar charts also have a normalized variant that plots each series as a share of its category total on a 0-100% axis. See Normalize to 100 percent below.
For large numeric values, nao shortens Y-axis labels to values such as
100K, 1.5M, or 2B to keep charts readable.
Pie and donut charts
Pie and donut charts take exactly one series. Categories are shown in a legend rather than as labels drawn on the slices, and slices are separated by a small gap so adjacent categories stay distinguishable. When a result set has more than 10 categories, nao keeps the 10 largest slices by value and sums the remainder into a single Other slice. If the data already contains a category called “Other”, the remainder is merged into it rather than creating a duplicate.Horizontal bar charts
A horizontal bar chart draws one row per category, each with a sideways bar in a grey track. The category sits on the left and the value in a column on the right, and every bar is sized against the largest value in the data. Values are shown on the bars by default and can be turned off with the Show data labels toggle in the edit dialog. A single value column gives one bar per row. With two or more series the bars stack within each row, and a legend appears. Because the rows are ranked visually, the agent is instructed to sort and limit the rows in SQL so the chart stays readable. With two or more series you can also normalize to 100%: each row then fills the same width and its bars show each series’ share of that row’s total.Editing a chart
Click the pencil icon next to a chart’s download button to open the edit dialog. Changes apply to the existing chart config, so the query is not re-run. The dialog lets you set:- Title
- Chart type
- X-axis type:
Auto,Category,Date, orNumber - X-axis column
- X-axis label: an optional custom label drawn under the axis
- Series: for each series, the data column, an optional label, a color, a value format (number format, unit, and placement), and - on mixed charts - a series type and Y-axis side. Use Add series to plot another column, or the bin icon to remove one (at least one series is required).
- Y-axis range
- Show data labels
Normalize to 100 percent
When the selected chart type is a stacked bar or stacked area chart, the dialog shows a Normalize to 100% toggle. Turning it on switches the chart to its 100% stacked variant: each series is drawn as a share of the category total and the axis runs from 0 to 100%. Tooltips show percentages instead of absolute values. Horizontal bar charts have the same toggle, but only once they carry two or more series to split each row between - a single-series horizontal bar has nothing to normalize. Use it when the composition matters more than the absolute totals. Turning the toggle off returns the chart to absolute stacking.Y-axis range
By default the Y axis adapts to the values in the result set. Line and scatter charts auto-scale to a readable range rather than forcing a zero baseline. To pin the scale, enter a Min, a Max, or both in the Y-axis range fields. Leaving a field on itsAuto placeholder keeps that bound automatic. The minimum must be lower than the maximum, otherwise the dialog rejects the change.
The Y-axis range fields are not shown for pie, KPI card, and radar charts, which have no Y axis.
Data labels
The Show data labels toggle prints the numeric value of each data point directly on the chart, instead of requiring a hover. You can also ask the agent for it in plain English (for example: “show the values on the chart”). On line and area charts, labels are limited to the most significant points so a dense series stays legible.Combo charts and a second Y-axis
Pick the Mixed chart type to draw several series with different shapes in one chart - for example bars for revenue and a line for a conversion rate. Each series then gets two extra controls in its row of the edit dialog:- Series type - draw that series as a
bar,line, orarea, independent of the others. - Y-axis side - plot the series against the
leftorrightaxis. A right axis appears as soon as any series uses it, which is what lets you compare metrics on very different scales or units in a single chart.
KPI comparison pills
A KPI card can show a small change pill under its number, comparing the latest value to the previous period. Turn it on with the Comparison pill control in the edit dialog (it appears only for KPI cards), or ask the agent for it. There are three styles:- Percentage - the signed percentage change, colored green or red with an arrow.
- Variation - the signed absolute change, colored green or red with an arrow.
- Absolute - just the magnitude of the change, with no color or arrow.
Hiding the tooltip total
When a chart stacks several series, the tooltip shows a Total row by default. That total is meaningless when the series can’t be meaningfully added up - unrelated metrics, or different units or currencies. The agent can set a chart-widehide_total option to drop the Total row in those cases; additive charts still show it, and percentage charts still show 100%.
Value formatting
By default chart values render as plain numbers. Value formatting lets you show a currency symbol, a%, or any unit (V, kg, GB, …) and control how numbers are rounded and abbreviated. Formatting is set per series, so a chart can mix, for example, a dollar series and a percentage series.
A format applies everywhere the series’ value appears: the value axis, tooltips, data labels, and KPI cards. It also carries through into stories, including their HTML and PDF exports. The value axis automatically widens to fit longer labels (such as $1,000,000) so they don’t clip.
A value format has three parts:
- A number format - a d3-format specifier that controls rounding, thousands separators, and abbreviation of the raw number.
- A unit - free text such as a currency symbol (
$,€,¥,£), a%, or a unit likeV,kg,GB. - A placement - whether the unit sits before the number (Prefix, for currencies) or after it (Suffix, for
%and most units).
Setting it in the editor
In the chart edit dialog, each series row has a Number format, a Unit, and a Placement (Prefix or Suffix). Any part can be left blank.Asking the agent
You usually don’t need to open the editor - ask in plain English and the agent sets the format for you:“Format revenue as US dollars.” “Show the conversion rate as a percentage with one decimal.” “Label the storage column in GB.”
d3-format number cheat sheet
The number format field takes a raw d3-format specifier and applies it to the value as-is (it does not multiply or divide). Copy-paste one of these:
The pattern is
[,][.precision][type]: a leading , adds thousands separators, .N sets precision, and the trailing letter is the type - f for fixed decimals or s for SI-prefix abbreviation. See the full d3-format docs for more.
Copy-paste recipes (format + unit)
Percentages
Do not use d3’s% format type - it multiplies the value by 100. If your data is already stored as a percentage (for example 42.5 meaning 42.5%), use a plain number format (.1f) with a % unit set to Suffix placement. That renders 42.5 as 42.5%.
Abbreviating large numbers
The.2s (SI-prefix) number format abbreviates large numbers. How the abbreviation letters display depends on the series’ compact mode, which the agent picks based on the kind of value:
- Financial (the default) is meant for money and counts:
kbecomesKandGbecomesB, whileMandTare unchanged - so you get1.2K,3.4M,5.6B. - SI keeps d3’s scientific letters unchanged, which is what you want for scientific units such as bytes (
1.2k,3.4M,5.6G).
The editor accepts number formats that render identically in the interactive chart and in static story exports - the
f and s types, a bare ,, and a precision (for example ,.2f, .2f, ,, and .2s). If you enter a format that would export differently, the dialog shows a warning and blocks saving until you adjust it. The agent is not limited to these when it sets a format directly.Viewing the SQL and data behind a chart
Every chart is backed by a SQL query and its result set. Click the View SQL query and data action (the<> icon) on the chart toolbar to open a side panel with the execute_sql query on top and the result rows below, so you can verify exactly how a chart was built.
Tables
Ask for a table instead of a chart and the agent renders the result set withdisplay_chart using chart_type: "table".
Tables include:
- Right-aligned numeric columns with tabular figures, so digits line up.
- Sortable columns - click a column header to sort by it. Each click cycles through ascending, descending, and back to the original order (the chevron shows the current direction). Column widths stay stable across sorting and paging. This works on tables in chat, in stories, and on
execute_sqlresults.
Exporting table data
Any table - in a chat thread, in a story, or from anexecute_sql result - can be downloaded from its toolbar. The export menu offers two formats:
- CSV - a plain comma-separated file.
- Excel (
.xlsx) - a native spreadsheet file.
Conditional formatting
Numeric, boolean, and text columns can be colored by rule. Open the Edit table formatting dialog from the table toolbar and pick a rule per column - columns default to None. Available rules depend on the column type:Numeric
Color scale shades each cell on a gradient between the column minimum and maximum. Threshold colors cells matching a comparison (
≥, >, ≤, <, =) against a value.Boolean
Boolean sets a background color for true cells, false cells, or both. Leave a color unset to skip that case.
Text
String colors cells that match an operator:
equals for an exact match, in list for one of several values, or contains for a case-insensitive substring.“Show revenue by region as a table and highlight anything under 10k in red.” “Put a color scale on the conversion rate column.”The agent is instructed to use real conditional formatting for these requests rather than faking it with emoji or by adding an extra status column to the data.
Date format
Dates rendered by nao follow a project-level setting rather than each viewer’s locale, so everyone on the project reads the same format. Set it in Settings -> Project on the Date format card. This is an admin setting.
European is the default. Choosing Custom lets you compose a pattern from the supported tokens:
YYYY, YY, MMMM, MMM, MM, M, DD, D, dddd, and ddd. The card previews the result on a sample date as you type.
The setting applies to chart axes, tooltips, legends, and KPI cards, to SQL result tables, and to the charts and tables inside stories and their PDF and HTML exports.
Maps
When a query returns geographic data, the agent can plot it on an interactive map instead of a chart, using thedisplay_map tool. Maps render over an OpenStreetMap-based basemap, and you can pan and zoom them like any web map. Ask in plain English: “map our customers by city.”
Maps are off by default. An admin turns them on under Settings -> Project -> Agent, on the Maps card. Until then the agent won’t offer the map tool.
Map types
A choropleth gets its shapes from one of three sources: a built-in boundary set (
world_countries, france_regions) matched on a region column, a public GeoJSON URL, or a geometry column in the result set (for example ST_AsGeoJSON output from PostGIS). Prefer a URL over a geometry column when a public one exists - it avoids pulling large polygons through SQL.
The map toolbar
Every map carries the same toolbar as a chart:- View map, View data table, and View SQL query switch between the map, the rows behind it, and the
execute_sqlquery that produced them. - Add to story drops the map into a story.
- Download saves the map as a PNG, or exports the underlying rows as CSV or Excel when you are on the data view.
- Edit map (the pencil icon) opens the edit dialog.
Editing a map
The edit dialog covers presentation, so the query is never re-run. You can set:- Title
- GeoJSON boundaries - on a choropleth built from a URL, the URL itself; on one built from a boundary set, a read-only summary of which set it uses
- Color - the marker color, or the base color of the choropleth scale
- Marker size - the radius for points, or the largest bubble for a scatter bubble map (not shown on choropleths)
Maps in stories and exports
Maps behave like charts everywhere else in nao:- Map blocks render in stories and in story thumbnails.
- PNG and PDF exports include the map as rendered.
- Slack, Teams, Telegram, WhatsApp, and Mattermost previews render the map as an image that mirrors what you see in the web chat.
Custom GeoJSON boundaries
Built-in boundary sets only cover countries and French regions. To shade anything else - postal codes, sales territories, store catchment areas - an admin can add your own boundaries to the GeoJSON Boundary Library under Settings -> Project -> Agent.1
Point nao at the GeoJSON
Paste an HTTPS URL to a GeoJSON
FeatureCollection. nao loads it, validates it, and previews it on a map so you can confirm you have the right shapes.2
Name the set
Give it a Label (what admins see, e.g.
French postal codes) and a Key (what the agent addresses it by, e.g. french_postal_codes).3
Say how to join it
Pick the Join property - the GeoJSON feature property whose value must match the region column in your SQL. nao lists the properties it found in the file. Add a Region key hint describing what those values look like, so the agent knows what to select.
Custom charts
Beyond the built-in chart types, a project can ship its own custom charts - browser-rendered visualizations authored as small JavaScript modules inagent/charts/. Use them when a question needs a visual that nao’s built-in types don’t cover.
Each custom chart is a file agent/charts/<type>.js that exports a render function:
<type>must start with a lowercase letter and must not collide with a built-in type (bar,line,pie,table, …); names that break the rules are ignored.contextcarries thedatarows from the referencedexecute_sqlresult, the chartconfig, the naocolorsandtheme, and a set of chartinglibs.- Charts render in the browser only. Stories, exports, automations, MCP embeds, and messaging previews keep using the built-in chart types.
build-custom-charts skill in your project for the full authoring reference.