Examples
This page collects common extension patterns.
Read active map and points
Manifest permissions:
{
"permissions": ["maps:read"]
}Panel code:
async function readActivePoints() {
const active = await window.mantis.maps.getActive();
const points = await window.mantis.maps.getPoints(active && active.mapId);
return {
active,
pointCount: points.length,
firstPoint: points[0] || null,
};
}Read clusters for selected points
Manifest permissions:
{
"permissions": ["maps:read", "selection:read"]
}Panel code:
async function readSelectedPointClusters() {
const active = await window.mantis.maps.getActive();
const selection = await window.mantis.selection.get(active && active.mapId);
const clusters = await window.mantis.clusters.list(active && active.mapId);
const clusterById = new Map(clusters.map((cluster) => [cluster.id, cluster]));
return selection.selected.map((point) => ({
pointId: point.id,
cluster: clusterById.get(point.cluster) || null,
}));
}Read selected point ids
Manifest permissions:
{
"permissions": ["selection:read"]
}Panel code:
function selectedPointIds(selection) {
return (Array.isArray(selection.selected) ? selection.selected : [])
.map((point) => (typeof point === 'string' ? point : point && point.id))
.filter(Boolean);
}
async function readSelectedPointIds() {
const selection = await window.mantis.selection.get();
return selectedPointIds(selection);
}Open a point’s map
Manifest permissions:
{
"permissions": ["maps:read"]
}Panel code:
async function openPointMap(mapId) {
await window.mantis.maps.open(mapId);
}You cannot also select the point
The obvious next line would be window.mantis.selection.set([pointId], mapId), but that needs selection:write, and a manifest declaring that permission is rejected by the install validator. Nothing installs. maps.open is also panel-only: it does not exist in the host worker. See SDK reference.
Create a bag from the current selection
Manifest permissions:
{
"permissions": ["selection:read", "bags:write"]
}Panel code:
async function createBagFromSelection() {
const selection = await window.mantis.selection.get();
const pointIds = selectedPointIds(selection);
if (!pointIds.length) {
throw new Error('Select points before creating a bag.');
}
return window.mantis.bags.create('Extension Bag', pointIds);
}Subscribe to selection changes
Manifest permissions:
{
"permissions": ["maps:read", "selection:read"]
}Panel code:
async function watchSelection(render) {
const active = await window.mantis.maps.getActive();
return window.mantis.events.subscribe(
'selection.changed',
{ mapId: active && active.mapId },
(payload) => render(payload),
);
}The returned value is disposable:
const subscription = await watchSelection(console.log);
await subscription.dispose();Register and execute a command
Manifest:
{
"permissions": ["commands:execute"],
"main": "dist/extension.js",
"activationEvents": ["onCommand:demo.sample-panel.describeSelection"],
"contributes": {
"commands": [
{
"id": "demo.sample-panel.describeSelection",
"title": "Describe Selection"
}
]
}
}Host code:
exports.activate = async function activate(context) {
context.subscriptions.push(
await mantis.commands.registerCommand(
'demo.sample-panel.describeSelection',
async () => {
const selection = await mantis.selection.get();
return {
selectedCount: Array.isArray(selection.selected) ? selection.selected.length : 0,
};
},
),
);
};Panel code:
const result = await window.mantis.commands.execute(
'demo.sample-panel.describeSelection',
);With onCommand:demo.sample-panel.describeSelection, Mantis activates the host before executing the command.
Store extension state from the host
Manifest:
{
"main": "dist/extension.js",
"activationEvents": ["onStartup"]
}Host code:
exports.activate = async function activate(context) {
const launches = await context.workspaceState.get('launches', 0);
await context.workspaceState.update('launches', launches + 1);
};Invoke Python backend
Manifest:
{
"permissions": ["backend:invoke"],
"backend": {
"runtime": "python",
"entry": "backend/main.py",
"actions": ["count"]
}
}Panel code:
const result = await window.mantis.backend.invoke('count', {
values: [1, 2, 3],
});Backend:
def count(payload, ctx):
values = payload.get("values")
if not isinstance(values, list):
raise ValueError("values must be a list")
return {
"count": len(values),
"extension_id": ctx.get("extension_id"),
}Backend dependency
Manifest:
{
"permissions": ["backend:invoke"],
"backend": {
"runtime": "python",
"entry": "backend/main.py",
"requirements": "humanize==4.10.0\n",
"actions": ["humanizeCount"]
}
}Backend:
import humanize
def humanizeCount(payload, ctx):
count = int(payload.get("count", 0))
return {"value": humanize.intword(count)}Open a panel
Manifest permissions:
{
"permissions": ["panels:write"]
}Panel code:
await window.mantis.panels.open('Inspector');Host code can open one of its own contributed extension panels too:
exports.activate = async function activate(context) {
context.subscriptions.push(
await mantis.events.subscribe('selection.changed', async (payload) => {
const selectedBags = Array.isArray(payload.selectedBags) ? payload.selectedBags : [];
if (selectedBags.length) await mantis.panels.open('lifecyclePanel');
}),
);
};That pattern lets a background extension surface its panel automatically when relevant workspace state changes.
Show all permission failures clearly
async function run(output, action) {
output.textContent = 'Running...';
try {
const result = await action();
output.textContent = JSON.stringify(result, null, 2);
} catch (error) {
output.textContent = error && error.message ? error.message : String(error);
}
}Use this pattern during development to quickly see missing permissions, invalid backend actions, or package problems.