Skip to content

Examples ​

This page collects common extension patterns.

Read active map and points ​

Manifest permissions:

json
{
  "permissions": ["maps:read"]
}

Panel code:

js
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:

json
{
  "permissions": ["maps:read", "selection:read"]
}

Panel code:

js
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:

json
{
  "permissions": ["selection:read"]
}

Panel code:

js
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:

json
{
  "permissions": ["maps:read"]
}

Panel code:

js
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:

json
{
  "permissions": ["selection:read", "bags:write"]
}

Panel code:

js
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:

json
{
  "permissions": ["maps:read", "selection:read"]
}

Panel code:

js
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:

js
const subscription = await watchSelection(console.log);
await subscription.dispose();

Register and execute a command ​

Manifest:

json
{
  "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:

js
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:

js
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:

json
{
  "main": "dist/extension.js",
  "activationEvents": ["onStartup"]
}

Host code:

js
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:

json
{
  "permissions": ["backend:invoke"],
  "backend": {
    "runtime": "python",
    "entry": "backend/main.py",
    "actions": ["count"]
  }
}

Panel code:

js
const result = await window.mantis.backend.invoke('count', {
  values: [1, 2, 3],
});

Backend:

python
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:

json
{
  "permissions": ["backend:invoke"],
  "backend": {
    "runtime": "python",
    "entry": "backend/main.py",
    "requirements": "humanize==4.10.0\n",
    "actions": ["humanizeCount"]
  }
}

Backend:

python
import humanize


def humanizeCount(payload, ctx):
    count = int(payload.get("count", 0))
    return {"value": humanize.intword(count)}

Open a panel ​

Manifest permissions:

json
{
  "permissions": ["panels:write"]
}

Panel code:

js
await window.mantis.panels.open('Inspector');

Host code can open one of its own contributed extension panels too:

js
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 ​

js
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.