Plugins extend MMA with custom functionality: new tools, integrations with external scripts, custom UI panels, and more.
User plugins live in your app data folder:
Windows: %APPDATA%/app.map-making.local/plugins/<plugin-id>/
Linux: ~/.local/share/app.map-making.local/plugins/<plugin-id>/
macOS: ~/Library/Application Support/app.map-making.local/plugins/<plugin-id>/
Grab the plugin scaffold directly into your plugins directory:
mkdir %APPDATA%/app.map-making.local/plugins
cd %APPDATA%/app.map-making.local/plugins
npx degit ccmdi/mma/plugins
cd sample
npm install
npm run buildReopen a map in MMA and enable it in the marketplace. Edit src/index.ts and rebuild to iterate.
For a faster edit-reload loop, use the dev script instead of manual builds + copies:
node plugins/dev.mjs plugins/my-plugin # JS watch + auto-sync to appdata
node plugins/dev.mjs plugins/my-plugin --sidecar # also watches the sidecar binaryThis watches your source, rebuilds on change, and copies index.js, manifest.json, and (with --sidecar) the debug binary into the appdata plugin folder. Reopen a map to pick up changes.
For sidecar plugins, run cargo build in plugins/<id>/sidecar/ in a separate terminal - the dev script polls for binary changes and copies them over.
Each plugin is a folder containing at minimum:
manifest.json— plugin identityindex.js(or whatevermainpoints to) — plugin behavior
The manifest is the plugin's identity:
{
"id": "my-plugin",
"name": "My Plugin",
"description": "What it does",
"icon": "M20.5,11H19V7...",
"version": "1.0.0",
"main": "index.js"
}id— unique identifier (kebab-case recommended, defaults to folder name)name— display name shown in the plugin marketplacedescription— short description (optional)icon— MDI SVG path string (get one from pictogrammers.com/library/mdi, ornpm install -D @mdi/jsand import the constant)main— entry point JS file, loaded as an ES module (defaults toindex.js)experimental—truemarks the plugin as experimental (optional). The marketplace card shows a flask label so users know to expect rough edges.minAppVersion- lowest app version this build works on (required). The registry only serves the latest build of each plugin, so older apps use this to refuse an install/update that needs a newerwindow.MMAinstead of breaking. CI typechecks every plugin against the SDK as it was at that version (node plugins/check-floors.mjs), so an API newer than the floor fails the build: raise the floor or drop the call.
Your entry point is an ES module that calls MMA.registerPlugin():
MMA.registerPlugin({
activate() {
// Called when the plugin activates (map opens + plugin enabled)
// Use MMA.* to interact with the editor
return () => {
// Optional cleanup — called on deactivate
};
},
});The global MMA object is the single API surface. It provides:
- Map & location CRUD
- Tag management
- Selection queries
- Event subscription
- Shell command spawning
- File dialogs
- Raw Tauri IPC for advanced use
See plugins/types/mma.d.ts for the full API surface.
Plugins can provide React components for richer UI:
MMA.registerPlugin({
activate() {},
sidebar: MySidebarComponent,
modal: MyModalComponent,
locationPanel: MyPanelComponent,
});Component props:
sidebarreceives{ onClose: () => void }modalreceives{ onClose: () => void }locationPanelreceives no props
The plugin template's build config automatically deduplicates libraries the app already bundles (React, deck.gl, luma.gl). Just write normal imports -- the build handles the rest. Libraries the app doesn't have get bundled into your plugin automatically.
To install a plugin:
- Copy the plugin folder into your plugins directory
- Reopen a map in MMA
- Enable the plugin in the marketplace (gear icon)
To share: zip the folder and distribute however you like (GitHub, Discord, etc.).
mma-plugin-types declares the libraries the SDK types import from (react, deck.gl, tauri, google.maps) as its own dependencies, so installing it pulls the whole type closure -- nothing else to install. They are types-only: at runtime your plugin shares the app's copies (see Shared modules).
For the icon field, @mdi/js has the icon paths, or just copy the SVG path string from pictogrammers.com/library/mdi.