Importers and exporters

Add a file format to Athanor's Export window with a plugin exporter, return text or binary files, and use 3D coordinates. Importers are reserved.

What an exporter is

An exporter adds a file format to Athanor's Export window. When the person picks it, Athanor asks your plugin for the file, and Athanor saves it. Your plugin never writes files itself and never chooses where they go.

Exporters appear in the Export window (Main menu › File › Export…, or Ctrl+Shift+E) under From plugins, each as "format label · plugin name". The format's notes show as its tooltip.

A minimal exporter

This is the Exporter template from New plugin: Exporter template…. It writes every structure's SMILES and name, one per line.

manifest.json:

{
  "id": "local.smiles-list",
  "name": "SMILES list",
  "version": "0.1.0",
  "api": "1.0.0",
  "author": "You",
  "description": "SMILES list, made from the exporter template.",
  "main": "main.js",
  "permissions": ["document.read"],
  "contributes": {
    "formats": [
      { "key": "smiles-list", "label": "SMILES list", "extensions": ["txt"], "mime": "text/plain", "export": true }
    ]
  }
}

main.js:

// @ts-check
/// <reference path="./athanor.d.ts" />
 
// An exporter: appears in Export under "From plugins". Return the file; Athanor saves it.
athanor.formats.onExport('smiles-list', async () => {
  const page = await athanor.snapshot();
  const lines = page.structures.map((s) => `${s.smiles}\t${s.name || s.formula}`);
  return { filename: 'structures.txt', text: lines.join('\n') + '\n' };
});

The first argument to onExport is the format's key from the manifest.

What the handler returns

Field Meaning
filename The suggested file name. Cut at 120 characters; backslash, slash, colon, asterisk, question mark, double quote, angle brackets and the vertical bar become _. When missing, Athanor uses export.<first extension>
text The file's contents as text, up to 50,000,000 characters
base64 The file's contents as base64, for binary formats. Use text or base64, not both

A handler that returns neither shows "Plugin returned no file." in the Export window.

Using 3D coordinates

athanor.conformer(structureId) gives a structure's 3D coordinates from Athanor's own engine, hydrogens included: { atoms: [{ element, x, y, z }] } in angstroms. It needs Read the open document.

This example plugin writes an XYZ file with every structure on the page, ten angstroms apart so none overlap:

async function all() {
  const page = await athanor.snapshot();
  const out = [];
  for (const [k, s] of page.structures.entries()) {
    const c = await athanor.conformer(s.id);
    if (!c) continue;
    // Each structure 10 angstroms along x from the last, so none overlap.
    out.push({ name: s.name || s.formula, atoms: c.atoms.map((a) => ({ ...a, x: a.x + k * 10 })) });
  }
  return { title: page.title || 'Athanor export', mols: out };
}
const f = (v, w, d) => v.toFixed(d).padStart(w);
athanor.formats.onExport('xyz3d', async () => {
  const { title, mols } = await all();
  const atoms = mols.flatMap((m) => m.atoms);
  const lines = [String(atoms.length), title.replace(/\s+/g, ' ').slice(0, 80)];
  for (const a of atoms) lines.push(a.element.padEnd(3) + f(a.x, 12, 6) + f(a.y, 12, 6) + f(a.z, 12, 6));
  return { filename: 'structures.xyz', text: lines.join('\n') + '\n' };
});

Its manifest declares the format like this:

{ "key": "xyz3d", "label": "XYZ (3D)", "extensions": ["xyz"], "mime": "chemical/x-xyz", "export": true,
  "notes": "3D coordinates in angstroms, hydrogens written out; structures 10 Å apart." }

Rules and limits

  • An exporter reads the document, so the manifest must hold document.read. Without it the manifest is refused: "Exporters and checks read the document: add the "document.read" permission."
  • Up to 50 formats per plugin. key follows the command key rule; label is required, at most 40 characters.
  • extensions: one to five, letters and digits only, up to 10 characters each, written without the dot (a leading dot is removed).
  • mime defaults to application/octet-stream. notes is optional, at most 200 characters.
  • A format must say "export": true, "import": true, or both.
  • The handler gets the same time limit as any other code: 5 seconds of synchronous work (15 for official plugins).

Importers

The manifest accepts "import": true on a format, and the API has athanor.formats.onImport(key, fn) to register a handler. API 1 does not call import handlers yet: no file the person opens is passed to a plugin. Declare export only until a later API version says otherwise in the Changelog.

Is this helpful?

Last updated Nov 11, 2026