SYS.HOME/GITMANAGER/PLUGIN_DOCS

SYS.PLUGIN_DOCS

> GITMANAGER — Plugin Development Guide

Everything you need to build a GitManager plugin: the chroma API, every extension point, and a complete working example.

What Plugins Can Do

ChromaLabs GitManager can be extended with plugins. A plugin can:

  • >add tags/badges to repositories (sidebar + repo header) and to files (changes list + file explorer)
  • >add buttons — repo actions in the header, and right-click actions on files
  • >add custom tabs on every repository with fully custom content
  • >open custom windows (modal panels) with any content
  • >register file viewers and editors for specific file extensions in the Files tab
  • >run git commands, read/write files inside repos (text and binary), and keep persistent per-plugin storage

Installing a Plugin

A plugin is a folder. Drop it into the plugins directory and reload:

  1. In GitManager: Settings → Plugins → Open plugins folder (%APPDATA%\ChromaLabs GitManager\plugins)
  2. Copy the plugin folder in (e.g. plugins\my-plugin\)
  3. Settings → Plugins → Reload plugins (or restart the app)

Plugins can be disabled per-plugin from Settings → Plugins without deleting them.

SECURITY NOTE

Plugins run inside GitManager with full access, exactly like any installed application. Only install plugins you trust.

Anatomy of a Plugin

my-plugin/
├── plugin.json      required manifest
└── main.js          entry script

plugin.json:

{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "What it does",
  "author": "You",
  "main": "main.js"
}
  • >id — lowercase letters, digits, dashes/underscores. Must be unique.
  • >main — a filename inside the plugin folder (no paths).

main.js runs once at load with one global: chroma, the plugin API. Everything you contribute is registered through it:

'use strict'

chroma.addFileBadge((file) =>
  file.path.endsWith('.lua') ? { text: 'LUA', color: '#22d3ee' } : null
)

Badges (Tags)

chroma.addRepoBadge((repo) => badge | null)
chroma.addFileBadge((file) => badge | null)
  • >badge = { text: string, color?: '#hex', tooltip?: string }
  • >Repo badges show in the sidebar row and the repo header. File badges show on rows in the changes list and the file explorer.
  • >Providers are called synchronously on render and must be fast; return null for no badge. Exceptions are swallowed.

repo context:

{ id, name, nickname, path, groupId, branch, status }
// status (may be null):
// { branch, upstream, ahead, behind, files: [{path, kind, staged}], hasConflicts, ... }

file context: { repoId, path, kind? }path is repo-relative with forward slashes; kind (added/modified/…) is present for changes-list rows.

Buttons

chroma.addRepoAction({ id: 'my-action', label: 'Do thing', onClick: async (repo) => { ... } })
chroma.addFileAction({ label: 'Process', extensions: ['.csv'], onClick: async (file) => { ... } })
  • >Repo actions render as buttons in the repo header.
  • >File actions appear in the file explorer's right-click menu; omit extensions to apply to every file.
  • >Async onClick errors are caught and shown as an error toast.

Custom Tabs

chroma.addTab({
  id: 'dashboard',
  title: 'Dashboard',
  render(container, repo) {
    container.innerHTML = '<div style="padding:16px">…</div>'
    return () => { /* optional cleanup when the tab unmounts */ }
  }
})

The tab appears after the built-in tabs on every repository. render receives a plain DOM element — use any DOM technique you like. Return a cleanup function if you attach timers/listeners.

Custom Windows

chroma.openWindow({ title: 'Results', width: 640, render(container) { ... } })

Opens a modal panel. Same render/cleanup contract as tabs.

File Viewers & Editors

chroma.registerFileViewer({
  extensions: ['.md'],
  canEdit: true,
  render(container, ctx) {
    // ctx: { repoId, path, content, save? }
  }
})
  • >Takes over the preview pane in the Files tab for matching extensions (the user can always flip to the raw view with the Raw toggle).
  • >ctx.content is the file text (empty string for binary files — read binary content yourself with chroma.readFileBinary).
  • >With canEdit: true, ctx.save(newText) writes the file and refreshes. Binary editors should write with chroma.writeFileBinary instead.

Working with Repos and Files

const r = await chroma.git(repoId, ['log', '--oneline', '-n', '10'])
// -> { ok, stdout, stderr }   (non-interactive; never prompts)

const text  = await chroma.readFile(repoId, 'path/in/repo.txt')
await chroma.writeFile(repoId, 'path/in/repo.txt', 'new content')

const bytes = await chroma.readFileBinary(repoId, 'textures/thing.dds')   // Uint8Array
await chroma.writeFileBinary(repoId, 'textures/thing.dds', bytes)

const entries = await chroma.listDir(repoId, 'subdir')  // [{ name, dir }]
chroma.refreshRepo(repoId)  // re-check git status after you changed files

All paths are repo-relative and confined to the repository — a plugin cannot reach outside a repo through these calls.

Import/Export Dialogs

For touching files outside a repo, plugins go through the user:

const picked = await chroma.openFileDialog([{ name: 'Images', extensions: ['png', 'jpg'] }])
// -> { name, data: Uint8Array } | null

const savedPath = await chroma.saveFileDialog('texture.png',
  [{ name: 'PNG', extensions: ['png'] }], bytes)  // -> path | null

Storage & Toasts

const data = await chroma.storage.get()      // whole storage object (or null)
await chroma.storage.set({ anything: true }) // persisted as JSON per plugin

chroma.toast('success', 'Done!')             // levels: info | success | warn | error

Styling

Plugin DOM lives inside the app, so the theme's CSS variables are available:

var(--bg)  var(--bg2)  var(--border)  var(--text)  var(--text-dim)
var(--accent)  var(--success)  var(--warn)  var(--error)  var(--mono)

Built-in utility classes like btn btn-tiny, btn btn-small, btn-primary also work on your buttons. Using these keeps plugins looking native in light, dark, and ChromaLabs themes.

Complete Example

The Markdown Tools plugin is a working example that demonstrates every extension point: an MD file badge, an MD* repo badge for uncommitted markdown, a TODO-scanner repo button with a results window, a per-repo Notes tab persisted via plugin storage, and a rendered markdown viewer with an edit/save mode.

Download Markdown Tools from the plugins page →

Distributing Plugins

Zip the plugin folder and share it — installing is unzip → plugins folder → Reload plugins. There is no store or signing (yet); treat plugin zips like any other software you'd run. Want your plugin on the approved plugins list? Share it in the ChromaLabs Discord for review.

Troubleshooting

  • >A plugin that fails to load shows its error in Settings → Plugins and in a toast at startup.
  • >plugin.json problems (bad id, missing main) are reported per-plugin; other plugins still load.
  • >Exceptions inside badge providers are silently ignored (so a broken badge can't break the UI); exceptions in actions/tabs/viewers surface as toasts or inline error text.

Questions about the plugin API?

Join Our Discord Support Server