Reading time: 24 minutes and 45 seconds.
What's new?
No matter which mods API version you want to target, you should always use the latest compatible version of the NPM packages:
- @spotfire/mods-api - Use the latest patch version for the mods API you are targeting.
- @spotfire/mods-sdk
- @spotfire/mods-dev-server
If you have started from an SDK template, simply run npm update to get the latest compatible versions.
Mods API 2.5
Version 2.5 of the mods API is centered on action mods and introduces insight agents — AI-powered scripts that analyze a visualization or a marked column, reason about the data with a large language model, and report back ranked, actionable recommendations to the user. Using features introduced in the 2.5 API requires Spotfire version 15.0 or later. To upgrade an existing mod, follow these steps:
- Update the
"apiVersion"field in the mod manifest to"2.5". - Update the version of
"@spotfire/mods-api"in thepackage.jsonfile to~2.5.0. - Update
"@spotfire/mods-sdk"to the latest version. Insight agents require a newer SDK; older versions cannot generate the agent types, so the build will fail. - Run
npm update.
Action mods
Insight agents
An insight agent is a new kind of action mod entry point that Spotfire can run on the user’s behalf to discover insights in an analysis.
Agents are declared in a new "agents" section of the mod manifest, where each agent has a "type" that is either "visual" or "marking":
- A visual agent runs against a visualization. Its entry function receives a VisualInsightAgentContext, exposing the originating Visual snapshot.
- A marking agent runs when the user marks rows. Its entry function receives a MarkingInsightAgentContext, which additionally exposes the AnalyzedColumn that was marked.
An agent runs in two phases.
In the insight phase, the entry function executes on a background thread against a read-only snapshot of the document, so it can read tables, axes, and marking without blocking the user or risking a partial document modification.
When the agent finds something worth acting on, it builds one or more ActionModInsight results.
Each insight wraps an invocation of an existing action mod script, created with utils.CreateScriptInvocation("script-id", { ... }) and passing arguments such as ActionModScriptArgumentLiteral and ActionModScriptArgumentNode.
In the action phase, if the user chooses to act on an insight, that script runs on the application thread against the live document and performs the actual modification.
The agent surfaces everything to the user by calling ReportResult(title, summary, insights?).
Insights can carry a preview through the optional last argument of ActionModInsight, an InsightImage.
Use InsightImage.LivePreview() to render a live page or visual, InsightImage.CustomIcon(svg) for a custom SVG, InsightImage.Binary(filePath) for a bundled image resource, or InsightImage.ActionIcon() / InsightImage.None().
During the insight phase, an agent can interact with the user through UserInteraction: report progress with ReportProgress, or ask a question with PromptUser, optionally offering a set of UserQuestionOption choices.
To get started, follow the new insight agent tutorial, which builds a visual agent that suggests calculated columns.
The AI service
The new Spotfire.Dxp.Framework.Ai namespace gives agents access to the AI model configured for the Spotfire environment.
Reach it from any agent context via context.AiService, an AiService that exposes HasModels and GetDefaultModel.
Build the conversation with an AiChatHistory (AddSystemMessage, AddUserMessage, AddAssistantMessage, AddToolMessage) and call AiModel.ChatCompletion.
For structured output, pass a JSON schema as the constructor argument of CompletionSettings so the model returns easily parseable JSON (the insight agent tutorial shows how to parse this defensively with Zod). To let the model call back into your own code mid-conversation, pass AiTool instances — typically an ActionAiTool created from a name, description, parameter JSON schema, and a ToolCallback. When a request fails, the model throws an AiModelException carrying IsRetryable, HttpStatusCode, ModelName, and ProviderErrorMessage, so an agent can react to transport errors, and report a meaningful result. The System.Net.HttpStatusCode enum is now available to branch on those status codes.
Follow the insight agent tutorial for an example of a complete agent that uses the AI service.
Data transformations in the insight phase
An agent can also run data transformations and data functions during the insight phase, entirely in the background.
The existing DataFlowBuilder pipeline can be connected and executed without binding the result into the document, so an agent can compute derived columns or aggregations to base its insights on.
In API version 2.5, the DataFunctionExecutorService, DataFunctionInvocation, and DataFunctionInvocationBuilder no longer require single-input/single-output of DataFunctionTransformation. This means that an agent can now feed multiple named inputs (data-flow readers and scalar values) into any built-in or saved data function, and read back multiple named outputs as DataRowReaders.
The executor is resolved as a global service via context.ImportContext.GetService(DataFunctionExecutorService).
See the snippets Run a data flow during the insight phase and Run a data function during the insight phase for example usages of these APIs.
Document-model additions
Spotfire 15.0 brings a number of visualization and data-model additions that are available to action mod scripts and are readable from insight agents.
Curve fitting and forecasting
The FittingModelCollection gains two new models: The ArpsDeclineCurveFittingModel implements the Arps decline curve used for oil-and-gas production forecasting, with editable fit parameters exposed through the reusable CurveFitParameter node (each settable to automatic, bounded, or fixed via CurveFitParameterMode), a configurable TimeUnit (ArpsTimeUnit), and a forecast that ends either at an economic limit or after a fixed time. A new Q1Q3LineFittingModel and a FittingModelLayer (for reordering fitting models among other reference layers) round out the additions.
Probit plot
The new Spotfire.Dxp.Application.Visuals.Distributions namespace adds the ProbitPlot visualization, which plots each row’s value against the quantile of a theoretical normal distribution to assess normality.
It supports the usual color, shape, size, rotation and labeling axes, fitting models, and a ShowProbitScale toggle.
Create this visualization by passing the ProbitPlot type to the page’s Visuals.AddNew method (page.Visuals.AddNew(ProbitPlot)).
See the snippet Create and configure a probit plot.
Reference layers
Programmatic control over reference layers is substantially expanded. A new ReferenceLayerData node, reached via ReferenceLayer.Data, lets a reference layer be driven from a different DataTableReference and WhereClauseExpression than its parent visualization, with its own marking and filter limiting (LimitByMarking, ApplyParentDataLimiting). New per-layer axes — ReferenceLayerColorAxis (with InheritScale), ReferenceLayerXAxis and ReferenceLayerElementByAxis — give per-layer coloring, axis-range participation, and grouping that are independent of the parent visualization. See the snippets Drive a reference layer from a separate table and Give a reference layer an independent color scale.
Adaptive time labels and log-scale gridlines
Every continuous scale now exposes an AdaptiveTimeLabels settings node, via ScaleBase.AdaptiveTimeLabels, that automatically tailors date/time label formatting to the visible range, with a DateFormat choice between Standard and a compact AbbreviatedMonthName format (e.g. “1 Apr 2026”), plus MultipleLines and Enabled toggles.
Another addition is opt-in minor gridlines for logarithmic scales through ScaleBase.ShowAdditionalLogScaleGridlines.
See the snippet Enable adaptive time labels on a date axis.
Mods API 2.4
This version of the mods API extends the multiple data view definition feature introduced in version 2.3 by allowing visualization mods to define a dynamic number of data views. This feature enables the development of visualization mods with a dynamic number of layers of a certain type. The update also includes several other additions for visualization mods, such as the ability to extend the axes of another data view, new transaction APIs, and new readable APIs.
Version 2.4 does not contain any additions to the action mods framework itself, but many API additions have been introduced as part of Spotfire 14.8.
Visualization mods
Dynamic layers
With version 2.4 of the visualization mods API, you can define layer types via the "layers" property in the mod manifest.
Layer types may contain a single main data view definition and multiple named data view definitions.
A layer may also define its own mod properties, whose values are unique to each instance of the layer.
A layer can be added via the Spotfire UI (unless "disableAddRemove" is set to true) or via the ModLayers.add API.
You can think of layers as sub-visualizations inside your visualization mod, and the layer ModLayer instance type supports many of the same APIs that the parent mod visualization does.
The dynamic nature of layers allows you to create advanced visualizations that contain sub-elements of a certain type. For instance, you can now create a graphical table where each column defines a mini visualization such as a scatter plot or a bar chart. Another use case is layered visualizations, such as map charts, where you may have many feature or scatter layers stacked on top of each other.
To keep axes in sync across data views, you can inherit the axis and data configuration via the "extendedAxes" property.
This property takes an array of axis names defined further up in the manifest.
To see a simple example of a visualization using the layers feature, see the ts-dev-graphical-table example project. This project implements a graphical table supporting the bar chart and scatter plot mini visualizations. It uses the extended axes feature to sync the “Row by” axis across the mini visualizations.
The most straightforward way to show layers in a visualization mod is to add the ModVisualization.layers readable to your main render loop and render each layer instance consecutively. For example:
Spotfire.initialize(async (mod: Spotfire.Mod) => {
const reader = mod.createReader(mod.visualization.layers());
const context = mod.getRenderContext();
reader.subscribe(render);
async function render(layers: Spotfire.ModLayers) {
// ...
for (const layer of layers.items) {
const data = await layer.data();
const errors = await data.getErrors();
if (errors.length > 0) {
// Show the error.
continue;
}
// Render the layer according to its type.
switch (layer.type) {
case "FeatureLayer": {
// Render feature layer
}
// ...
}
}
}
});
The data views for layers are eagerly materialized for best performance in most cases.
If your visualization mod requires a more advanced rendering flow, you can pass a LayerMode argument to the layers readable to gain greater control over when data is materialized.
For instance, passing "LazyData" means Spotfire will not attempt to materialize the data view until the ModLayer.data readable is awaited.
For total control, you can pass the "CollectionOnly" layer mode and set up individual readables on each layer instance.
Extended axes
A visualization mod containing multiple data views often needs to share some common splitting axes across all views being visualized.
For instance, a graphical table contains a top-level “Row by” axis that every mini visualization must respect.
Previously, this required duplicating the “Row by” axis across all data view definitions and adding error modes when the axes’ data tables and expressions went out of sync.
The "extendedAxes" property allows you to solve this issue by specifying a list of axes you want to inherit from an ancestor data view.
For instance, the manifest of a graphical table containing a bar chart mini visualization may look something like this:
{
"apiVersion": "2.4",
"dataViewDefinition": {
"axes": [{ "name": "Row by", "mode": "categorical", "placement": "left" }]
},
"layers": [
{
"type": "Bar Chart",
"dataViewDefinition": {
"extendedAxes": ["Row by"],
"axes": [
{
"name": "Category",
"mode": "categorical"
},
{
"name": "Value",
"mode": "continuous"
}
]
}
}
]
}
When configuring a bar chart instance in this visualization, the “Data” configuration will not be available since it is controlled by the top-level data view containing the “Row by” axis. The axis can be read via the layer’s regular data readable, similar to any other axis, but attempting to configure it will throw an error since its expression is controlled by the top-level data view.
Title
There is a new readable, ModVisualization.title, that allows a mod to read, subscribe to, and modify the mod title. Dynamic layers also have an identical readable, ModLayer.title.
Pause/resume
Sometimes a mod must pause rendering, for example when the user begins a long-running operation like area marking. In version 2.4, the reader can be temporarily stopped using the pause function and resumed using the resume function. While paused, no document changes will cause the render callback function to be invoked. Upon resume, the API will first await any current modifications, like marking operations, before the next render callback will occur. This ensures a non-flickering experience on resume.
Readable API additions
All readables in version 2.4 and upwards will cache their results internally, and return new instances only when there is a new value available on the backend. The underlying mechanism used is the same as when a reader invokes its registered callback function. This is an optimization required for the new layer functionality, and should in most cases not require any code changes in existing mods. Only mods that rely on reference equality on readables will need to be refactored. A performance benefit is that awaiting a readable like (await mod.property("myProperty")).value() will immediately return the cached result until the mod property changes in the backend.
In version 2.4, it is also valid to create multiple readers for a single readable. For instance, in the example below, every "layerReader" could include a mod global property, mod.property("renderStyle"), that would affect the rendering of all layers.
Dynamically creating readers
Before the introduction of dynamic layers, all readables that could be used when creating a reader were statically known from the API and the manifest. With dynamic layers, it is not always possible to statically know which readables a reader requires. For example, a mod that renders layers independently from each other may want to set up a separate reader for every layer instance. While bookkeeping of new and removed layers must be performed by the mod itself, it may be handy to create one reader per layer. This is per se not
Spotfire.initialize(async (mod: Spotfire.Mod) => {
const reader = mod.createReader(mod.visualization.layers("CollectionOnly"));
const layerReaders = new Map<String, Spotfire.ReaderSubscription>();
reader.subscribe(render);
async function render(layers: Spotfire.ModLayers) {
const layerIds = layers.items.map((l) => l.id);
const newLayers = layers.items.filter((l) => !layerReaders.has(l.id));
const removedLayers = Array.from(layerReaders.keys()).filter((k) => !layerIds.includes(k));
for (const layer of newLayers) {
layerReaders.set(layer.id, createReader(layer.id, layer));
}
for (const id of removedLayers) {
layerReaders.get(id)?.cancel();
layerReaders.delete(id);
console.log(`Removed layer: ${id}`);
}
}
function createReader(id: string, layer: Spotfire.ModLayer) {
console.log(`Add layer: ${id}`);
const layerReader = mod.createReader(layer.data(), mod.property<number>("renderStyle"));
return layerReader.subscribe(renderLayer);
function renderLayer(data: Spotfire.DataView, renderStyle: Spotfire.ModPropertyValue<number>) {
console.log(`Render layer: ${id} with style ${renderStyle.value()}`);
}
}
});
Using Reader.hasReadableChanged
A layered mod that requires all layers when rendering may still want to query the API for update status on layer readables, similar to the Reader.hasValueChanged method. This method will not work for layer.data() in the example below, since it expects the resolved value, not the readable itself. There is a new similar method in API version 2.4, Reader.hasReadableChanged, that will indicate whether a readable has a new value available since the last query. This is subtly different from its sibling method that indicates whether the current argument has a new value compared to the last invocation.
Spotfire.initialize(async (mod: Spotfire.Mod) => {
const reader = mod.createReader(mod.visualization.layers());
reader.subscribe(render);
async function render(layers: Spotfire.ModLayers) {
for (const layer of layers.items) {
if (reader.hasReadableChanged(layer.data()))
{
// Render the layer.
}
}}
});
Any modification requiring prior user activation
Mods specifying API version 2.4 will require a prior user gesture before attempting to modify any part of the document. The main purpose of this is to prevent a mod from modifying the document in a render loop, which is an API misuse that mainly breaks the undo/redo mechanism, causes export errors or introduces other more subtle bugs. Any user gesture, such as a keydown, mousedown, or scroll wheel event, is enough to allow API modifications. Mods specifying an API version lower than 2.4 will, in Spotfire 14.8 or later, yield a browser console warning.
Invisible and sticky transactions
When modifying the Spotfire analysis via the visualization mods API, it may sometimes be useful to control the undo/redo stack, which is done using the transaction API. Prior to 2.4, it was only possible to group a set of modifications into one undo step, and for most usages, this was handled implicitly by the API.
-
Sticky transactions will automatically merge a set of modifications with the preceding transactions if they share the supplied transaction identifier. This type of transaction is useful, for example, when the mod updates a mod property while dragging a slider handle, resulting in only the final value being recorded in the undo/redo stack.
-
Invisible transactions allow the mod author to make document modifications that will be invisible with regard to the undo/redo stack. For example, if the mod has a notion of a currently active section for interaction within the mod, this may be recorded as an invisible transaction. These changes will never produce their own undo/redo steps.
Reading data from dual mode axis
Reading row data for a dual mod axis in API versions prior to 2.4 is somewhat cumbersome. Before using either of the methods DataViewRow.categorical or DataViewRow.continuous, you first need to check the current axis mode to know which of the two methods to use, or the method will throw an error.
With API version 2.4, there are two new sibling methods: DataViewRow.tryCategorical or DataViewRow.tryContinuous. These methods will return null for a dual mode axis that currently has an empty expression or does not match the current mode. They will however still throw an error if the "axisName" does not match a dual mode axis.
Action mods
There are no new additions to the action mods framework. However, Spotfire 14.8 introduces new features that are supported in the API. These include:
- The ability to configure visualization mods that define dynamic layers via ModVisualization.Layers.
- Adding statistic layers to reference elements via LayerCollection.AddNewReferenceStatisticLayer.
Mods API 2.3
Version 2.3 of the mods API introduces the ability to define multiple data views in visualization mods. This feature enables the ability to develop mod visualizations which contain sub-visualizations (layers), or which consume data from multiple data tables.
- Update the
"apiVersion"version in the mod manifest to"2.3". - Update the version of
"@spotfire/mods-api"in thepackage.jsonfile to~2.3.0. - Run
npm update.
Visualization mods
Version 2.3 introduces the biggest new feature to visualization mods since its release; the ability to define multiple data view definitions.
Previously, a mod could only contain a single data view definition, declared in the mod manifest using the "dataViewDefinition" property.
This limited mods to only being able to consume data from a single data table, or to require the user of a mod to prepare their data in such a way that it could fit the data view definition.
Now we have added the ability to define multiple data view definitions via the "namedDataViewDefinitions" property in the manifest.
For example, a network graph may want to consume data from two different data tables, one table which contains the node definitions, and another which describes the edges between nodes. For such a mod, these definitions can be specified as follows:
{
"apiVersion": "2.3",
"dataViewDefinition": {},
"namedDataViewDefinitions": [
{
"name": "Nodes",
"icon": "nodes.svg",
"axes": [
{
"name": "NodeID",
"mode": "categorical",
"dropTarget": { "icon": "Column", "description": "Set {0} on the NodeID axis" }
}
]
},
{
"name": "Edges",
"icon": "edges.svg",
"axes": [
{
"name": "From",
"mode": "categorical",
"dropTarget": { "icon": "Column", "description": "Set {0} on the Edge From axis" }
},
{
"name": "To",
"mode": "categorical",
"dropTarget": { "icon": "Column", "description": "Set {0} on the Edge To axis" }
}
]
}
]
}
Note that we have left the "dataViewDefinition" completely empty to illustrate the fact that you do not need to declare a “main” data view definition.
Data view definitions defined within the "namedDataViewDefinitions" must declare a name and may optionally declare an icon (shown in the contextual visualization properties panel).
For the manifest defined above, the data can be consumed using the familiar mod.visualization.data API by providing the name of the data view you want to subscribe to.
Note that other APIs used to get or set data table or axis information now also take an optional name parameter for the cases where they are defined in a named data view definition.
The render function for the network graph can look something like this:
Spotfire.initialize(async (mod: Spotfire.Mod) => {
const reader = mod.createReader(mod.visualization.data("Nodes"), mod.visualization.data("Edges"));
const context = mod.getRenderContext();
reader.subscribe(render);
async function render(nodesDataView: Spotfire.DataView, edgesDataView: Spotfire.DataView) {
const errors = (await Promise.all([nodesDataView.getErrors(), edgesDataView.getErrors()])).flatMap(
errs => errs
);
if (errors.length > 0) {
mod.controls.errorOverlay.show(errors);
return;
}
const [nodeRows, edgeRows] = await Promise.all([nodesDataView.allRows(), edgesDataView.allRows()]);
if (nodeRows == null || edgeRows == null) {
mod.controls.errorOverlay.show("No nodes or edges to render.");
return;
}
mod.controls.errorOverlay.hide();
// Render the network graph.
context.signalRenderComplete();
}
});
The nodeRows and edgeRows results may contain a different number of rows in the case where they are configured with different data tables.
A limitation is that axes cannot be shared between data view definitions.
This means that it is up to you as the mod developer to perform any necessary matching, and to keep axes consistent between data view definitions.
For instance, to support trellising, you need to add a “Trellis by” axis to both “Nodes” and “Edges”, and report errors if the axes are out of sync or become inconsistent.
Action mods
No new additions to the action mods framework have been added. However, Spotfire 14.7 introduces many new features which are supported in the API. These include:
- The ability to configure a mod visualization defining multiple named data views via ModVisualization.TryGetNamedDataView and ModVisualization.NamedDataViews.
- Setting the orientation of line charts via LineChart.Orientation.
- Configuring the legend and tooltips of reference layers via ReferenceLayer.Details and ReferenceLayer.Legend.
Mods API 2.2
Version 2.2 of the mods API introduces optional axes to visualization mods, as well as exposing new action mods APIs for working with reference layers. Using API features introduced in the 2.2 API requires Spotfire version 14.6 or later. To upgrade an existing mod, follow these steps:
- Update the
"apiVersion"version in the mod manifest to"2.2". - Update the version of
"@spotfire/mods-api"in thepackage.jsonfile to~2.2.0. - Run
npm update.
Action mods
No new additions to the action mods framework has been added however, Spotfire 14.6 introduces many new features which are supported in the API. One such new feature is “reference elements”, which allows you to add reference layers to certain visualizations and show trend lines. As an example on how to use the new APIs for this feature, see the snippet “Add a reference layer showing trend lines”.
Visualization mods
Version 2.2 of the visualization mods API adds the ability to mark an axis “optional”.
This can be done by setting the "optional" property of the axis object in the manifest to true.
An axis marked optional indicates that it should not be displayed in the new visualization properties panel when in an unconfigured state.
It also makes it possible to clear the axis expression by removing the card from the visualization properties panel if it has been configured.
To control the automatic configuration of an optional axis, the API also introduces the "disableOnAutoConfigure" property on the "automaticConfiguration" object.
An optional axis with a defined automatic configuration will be configured when the mod visualization is initially added to the document.
By setting "disableOnAutoConfigure" to false, the axis will only be automatically configured when its axis card is added in the visualization properties panel.
Mods API 2.1
Version 2.1 of the mods API contains many improvements to both action mods and visualization mods. Using API features introduced in the 2.1 API requires Spotfire version 14.5 or later. To upgrade your mod, follow these steps:
- Update the
"apiVersion"field in the manifest to"2.1". - Update the version of
@spotfire/mods-apiin thepackage.jsonfile to~2.1.0. - Run
npm update.
Action mods
Version 2.1 of the API contains a lot of improvements and new features for action mods.
New parameter types
This API introduces two new types with two variants each: DataColumn (array: true/false) and DataViewDefinition (singleColumn: true/false). Additionally, it is now possible to define parameters as optional (see Optional), to limit numerics to a range (see Numeric ranges), and to specify a set of allowed values for string parameters (see Enum).
DataColumn
Scripts can now define DataColumn as a possible parameter type.
This can be done by setting "type": "DataColumn" for the parameter in the manifest.
Defining a parameter of this type will allow you to select a column from an existing data table during configuration or when the parameter is prompted.
Data column parameters are useful in scripts which previously required a GUID or column name as a parameter.
Previously you had to pass a GUID or string to DataColumnCollection.TryGetValue and check that the method returned true, likely throwing an error if not.
This was clunky, required writing boilerplate code, and prone to input errors such as misspelling the column name or entering an incorrect GUID.
Take for instance the “Read the values of a column” snippet which previously started with this code:
export function readColumnValues({ document, table, column }: ReadColumnValuesParameters) {
const dataColumn = OutParam.create(DataColumn);
if (!table.Columns.TryGetValue(column, dataColumn.out)) {
throw new Error(`Data table '${table.Name}' contains no column with name '${column}'.`);
}
// ...
}
By changing the column parameter type from "String" to "DataColumn", this code can be completely removed.
The table parameter is no longer necessary because it can be retrieved via the column’s document node context.
See the snippet for the updated code and manifest.
The new DataColumn type also supports setting property "array": true to allow multiple columns as a value.
When configured as an array, the script argument will have type Iterable<DataColumn> and can be consumed by the script via for ... of loops or functions which operate on iterables such as Array.from.
Additionally, the set of possible columns that the user is allowed to choose from can be limited by specifying allowedDataTypes. For example, to limit to only integer, columns set "allowedDataTypes": ["Integer"].
DataViewDefinition
Scripts can now define DataViewDefinition as a possible parameter type.
This can be done by setting "type": "DataViewDefinition" for the parameter in the manifest.
Defining a parameter of this type can be configured by selecting columns, a column search expression, or a custom expression.
The data view definition can also contain a number of limitations, such as limit by marking or filtering scheme (unless "disableLimitations": true is set in the manifest).
A data view definition is a comma separated list of column expressions.
The data view can be limited to only a single column expression by setting the "singleColumn": true property.
The main purpose of this parameter is to be passed along to data functions. This is made possible by a new overload of DataFunctionInputCollection.SetInput which takes a ActionDataViewDefinition as its second argument, as an example see snippet “Add a data function using a data view definition”.
Optional
A parameter can be marked as optional by defining "optional": true.
When a parameter has been defined as optional, it is not necessary to provide an input value during configuration.
Adding optional parameters to existing scripts does not invalidate existing configurations.
When defining a parameter of some type T as optional the argument will be typed as T?, meaning that the value may either be provided or undefined.
Optional parameters are a great way to update existing scripts without breaking existing usages (the API will remain stable). They can also be used to reduce the amount of configuration in cases where you can assume some default value for an optional parameter.
Enum
You can now limit string parameters to allow specific values by specifying "enum": ["Value 1", "Value 2"].
A string parameter limited in this way will provide the user with a drop-down list of the available options during configuration.
Adding a value to an existing enum will not invalidate existing configurations and will therefore not break existing usages.
Numeric ranges
Numeric parameters can now be restricted to a specified range in the manifest.
For instance, for a Real that is used to represent a value between 0% and 100%, you can restrict the parameter to that range by defining "range": { "min": 0, "max": 1 }.
Another useful range would be an Integer used to represent a count starting from 0.
This can be achieved by specifying "range": { "min": 0 }.
For more information, see the parameter documentation for Numerics.
New APIs
Some new and previously existing .NET APIs have now been made available to action mods. These include:
- Adding image layers to map charts via MapChartLayerCollection.AddNewImageLayer. This API can be used in combination with a mod resource, see “Add resources to your action mod” below.
- Loading STDF data via the Data.Format.Stdf APIs, see snippet “Create a table using the STDF APIs”.
- Looping through each descendant of a document node with a specified type via DocumentNode.ForEachNodeInSubTree. This is a very powerful API for making large updates to the document, see for instance the “Modify all expressions in every visualization” snippet.
- Add data functions to the document and configure them via DataFunctionCollection, DataFunctionInputCollection, and DataFunctionOutputBuilder. Note that the new DataViewDefinition type can be passed to a data function via the DataFunctionInputCollection.SetInput API.
- Creating and configuring visualizations from the visualization mods maintained by the ModManager. This includes loading visualizations from the library. See snippet “Add Latest Mod Visualization”.
Add resources to your action mod
File resources, specified using the files property in the manifest, can now be accessed as Streams inside a script.
Specifying the path relative to manifest in the files field will include the file in the mod, the mod can then be accessed via the resources parameter provided to every script’s entry point function.
Let’s try to add a PNG image to a text area on the active page.
-
Create a
staticfolder in your mod project next to the manifest. -
Add a PNG image in the folder called
my-image.png. -
In the manifest, reference your image by adding
"files": ["static/my-image.png"].All scripts in the mod should now be provided with a
resourcesparameter. If you have the SDK build watcher running you can see this in theenv.d.tsfile. -
Create a new script via
npx @spotfire/mods-sdk add-script add-image-to-text-areaand open the script file.
If you are using the latest SDK, the entrypoint should automatically destructure the resources field from the parameter object.
For existing scripts, simply add resources after document.
Our image can now be retrieved via resources.LoadResource("static/my-image.png").
This method returns a Stream, which is a type accepted by many APIs.
Let’s modify the script so that it creates a text area, adds the image to the text area’s image collection, and displays it as HTML.
The script should look like this:
const { HtmlTextArea } = Spotfire.Dxp.Application.Visuals;
export function addImageToTextArea({ document, application, resources }: AddImageToTextAreaParameters) {
const page = document.ActivePageReference;
if (!page) {
throw new Error(`Cannot create a text area without an active page`);
}
const textArea = page.Visuals.AddNew(HtmlTextArea);
textArea.Images.Add("my-image", resources.LoadResource("static/my-image.png"));
textArea.HtmlContent = `<img src="my-image" />`;
textArea.Title = "Text are containing an image";
}
RegisterEntryPoint(addImageToTextArea);
Spotfire previously used to re-fetch the entire mod on any file update, which would cause performance to slow down when mods with large resource files were developed. Make sure you are using version 1.2 or later of mods-dev-server to allow Spotfire to only fetch the files that have actually changed.
Unique icon per script
Each script can now specify its own unique SVG icon, which will show up in various parts of the UI such as floating buttons, title bar buttons, and in the Actions flyout.
Scripts that do not specify an icon will fall back to the icon of the action mod.
To specify an icon, add an "icon": "path/to/icon.svg" property to the script in the manifest.
Visualization mods
Version 2.1 of the API also contains some smaller improvements to visualization mods.
Axis expression heuristics
When a new instance of a visualization mod is created, heuristics are used to assign initial expressions to the axes of the visualization.
You can now specify a string, like count(), to use as the initial expression. This is made in the automaticConfiguration/expressionHeuristics
property of an axis specification in the manifest.
When the default heuristics are used for a continuous axis, the allowNonAggregatingMeasures can be set to "prefer" to control the heuristics to attempt to select
an expression that is not aggregating.
More styling information
The StylingInfo object has been extended with more information describing the current theme. For instance regarding how zoom sliders and trellis panel headers are styled.