logikonline d5be7cc604 docs: update addon API documentation and examples
Add comprehensive documentation for:
- Event subscription API with examples
- License status implementation for commercial addons
- Header actions configuration for view panels
- View communication using postMessage instead of bridge
- Clarify activate() must return true for successful activation

Update demo and settings views to reflect new communication patterns.
2026-01-18 14:03:23 -05:00

My First Addon

A comprehensive demo addon for GitCaddy that showcases all available addon features and APIs.

Overview

This addon demonstrates:

  • Addon Manifest (addon.json) - How to define your addon's metadata, permissions, and contributions
  • Main Process Module (main/index.js) - Lifecycle methods, IPC, settings, and method invocations
  • Renderer Process Module (renderer/index.js) - UI components and renderer-side communication
  • Custom Views - HTML-based views for repository panels and settings
  • Shared Types - Code shared between main and renderer processes

Project Structure

myfirst-addon/
├── addon.json           # Addon manifest (required)
├── main/
│   └── index.js         # Main process module (required)
├── renderer/
│   └── index.js         # Renderer process module (optional)
├── shared/
│   └── types.js         # Shared constants and types
├── views/
│   ├── demo.html        # Demo repository view
│   └── settings.html    # Settings panel view
└── README.md            # This file

Addon Manifest (addon.json)

The manifest defines everything about your addon:

Required Fields

Field Description
id Unique identifier (reverse domain notation: com.yourcompany.addon-name)
name Display name shown in Addon Manager
version Semantic version (major.minor.patch)
minAppVersion Minimum GitCaddy version required
description Short description for the addon list
main Path to main process JavaScript module

Optional Fields

Field Description
renderer Path to renderer process module for UI components
author Author information (name, email, url)
host Native host configuration for .NET/Go/Rust addons
permissions Required permissions (shown to user before install)
capabilities What your addon can do
contributes UI elements your addon adds

Permissions

"permissions": [
  "repository:read",     // Read repository data
  "repository:write",    // Modify repository
  "diff:read",           // Read diff/changes
  "commit:read",         // Read commit history
  "branch:read",         // Read branch information
  "network:localhost",   // Make local network requests
  "network:external",    // Make external network requests
  "settings:store",      // Store addon settings
  "notifications:show"   // Show notifications
]

Capabilities

"capabilities": [
  "commit-message-generation",  // Can generate commit messages
  "repository-analysis",        // Can analyze repositories
  "custom-view"                 // Has custom UI views
]

Main Process Module

The main module handles:

Lifecycle Methods

class MyAddon {
  // Called once when addon is loaded
  async initialize(context) { }

  // Called when addon is enabled - MUST return true for successful activation
  async activate() {
    // ... setup code ...
    return true;
  }

  // Called when addon is disabled
  async deactivate() { }

  // Called when addon is unloaded
  dispose() { }
}

Important: The activate() method must return true to indicate successful activation. If it returns undefined or false, the addon will fail to activate.

Context Object

The context object provides:

context.addonId       // Your addon's ID
context.addonPath     // Path to addon directory
context.manifest      // Parsed addon.json
context.log           // Logger (.debug, .info, .warn, .error)
context.settings      // Settings storage
context.events        // Event subscription
context.ipc           // IPC communication
context.hostPort      // Port of native host (if configured)

Event Subscription

Subscribe to app events via context.events:

// Repository changed
const disposable = context.events.onRepositorySelected((repo) => {
  console.log('Repository:', repo.path);
});

// Commit created
context.events.onCommitCreated((commit) => {
  console.log('New commit:', commit.sha);
});

// Files changed
context.events.onFilesChanged((files) => {
  console.log('Files changed:', files.length);
});

// App ready
context.events.onAppReady(() => {
  console.log('App is ready');
});

// App will quit
context.events.onAppWillQuit(() => {
  // Clean up resources
});

// Remember to dispose when done
disposable.dispose();

License Status (Optional)

For commercial addons, implement getLicenseStatus():

getLicenseStatus() {
  // For free addons, always return valid
  return { valid: true };

  // For commercial addons:
  // return {
  //   valid: this.isLicenseValid,
  //   message: this.isLicenseValid ? undefined : 'License expired'
  // };
}

Invoke Method

GitCaddy calls your addon's invoke method for all actions:

async invoke(method, args) {
  switch (method) {
    case 'myAction':
      return this.myAction(args[0], args[1]);
    default:
      throw new Error(`Unknown method: ${method}`);
  }
}

Contributions

Toolbar Buttons

{
  "id": "my-button",
  "tooltip": "Button Tooltip",
  "icon": { "type": "octicon", "value": "rocket" },
  "position": "after-push-pull",
  "onClick": {
    "action": "show-view",
    "target": "my-view"
  },
  "badge": {
    "method": "getBadgeContent"
  }
}

Position options: start, end, after-push-pull, after-branch

Menu Items

{
  "id": "my-menu-item",
  "menu": "repository",
  "label": "My Action",
  "accelerator": "CmdOrCtrl+Shift+M",
  "onClick": {
    "action": "invoke-method",
    "method": "menuAction"
  }
}

Repository Views

{
  "id": "my-view",
  "title": "My View",
  "icon": "rocket",
  "renderer": {
    "type": "iframe",
    "source": "views/my-view.html"
  },
  "context": ["repository"],
  "headerActions": [
    {
      "id": "refresh",
      "label": "Refresh",
      "icon": "🔄",
      "primary": true
    },
    {
      "id": "settings",
      "label": "Settings",
      "icon": "⚙️"
    }
  ]
}

Context options: repository, diff, commit, always

Header Actions: Buttons displayed in GitCaddy's view header bar (next to the close button). Use this instead of creating your own header inside the HTML view. The primary property highlights the button.

Context Menu Items

{
  "id": "file-action",
  "context": "file-list",
  "label": "Process Files",
  "onClick": {
    "action": "invoke-method",
    "method": "processFiles"
  }
}

Context options: file-list, commit-list

Settings Definitions

{
  "key": "enableFeature",
  "type": "boolean",
  "default": true,
  "description": "Enable the main feature"
}

Types: boolean, string, number, select

View Communication

Views (iframes) communicate with GitCaddy via postMessage:

Receiving Messages from GitCaddy

const ADDON_ID = 'com.example.myfirst-addon';

window.addEventListener('message', (event) => {
  const { type, data, actionId, requestId, result, error } = event.data || {};

  switch (type) {
    case 'addon:init-context':
      // Received initial context (repositoryPath, hostPort)
      const { repositoryPath, hostPort } = data;
      initializeView();
      break;

    case 'addon:header-action':
      // Header button was clicked
      handleHeaderAction(actionId);
      break;

    case 'addon:invoke-response':
      // Response from an invoke request
      handleInvokeResponse(requestId, result, error);
      break;
  }
});

Invoking Addon Methods

// Helper for invoke with Promise support
const pendingRequests = new Map();
let requestIdCounter = 0;

function invokeAddon(method, ...args) {
  return new Promise((resolve, reject) => {
    const requestId = ++requestIdCounter;
    pendingRequests.set(requestId, { resolve, reject });

    window.parent.postMessage({
      type: 'addon:invoke',
      requestId,
      addonId: ADDON_ID,
      method,
      args
    }, '*');

    // Timeout after 30 seconds
    setTimeout(() => {
      if (pendingRequests.has(requestId)) {
        pendingRequests.delete(requestId);
        reject(new Error('Request timed out'));
      }
    }, 30000);
  });
}

// Usage
const data = await invokeAddon('getDemoData');

Other Messages to GitCaddy

// Open addon settings dialog
window.parent.postMessage({
  type: 'addon:open-settings',
  addonId: ADDON_ID
}, '*');

// Refresh toolbar badge
window.parent.postMessage({
  type: 'addon:refresh-badge',
  addonId: ADDON_ID,
  buttonId: 'my-button',
  data: { count: 5 }
}, '*');

Dark Mode Styling

GitCaddy injects CSS variables for theming. Use them to match the app's appearance:

Theme Colors

/* Background colors */
background-color: var(--background, #1e1e1e);
background-color: var(--background-secondary, #252526);

/* Text colors */
color: var(--foreground, #cccccc);
color: var(--foreground-muted, #999999);

/* Border */
border-color: var(--border, #3c3c3c);

/* Accent colors */
color: var(--accent, #0e639c);
color: var(--success, #4ec9b0);
color: var(--warning, #fcba03);
color: var(--error, #f14c4c);
color: var(--info, #75beff);

Dark Mode Scrollbars

Always style scrollbars to match dark mode:

/* Webkit browsers (Chrome, Safari, Edge) */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: var(--background-secondary, #252526);
  border-radius: 5px;
}

::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb, #5a5a5a);
  border-radius: 5px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover, #7a7a7a);
}

/* Firefox */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb, #5a5a5a) var(--background-secondary, #252526);
}

Native Hosts

For addons that need native code (.NET, Go, Rust, etc.):

"host": {
  "directory": "host",
  "executable": "MyAddon.Host",
  "platforms": {
    "win32-x64": "host/win-x64/MyAddon.Host.exe",
    "linux-x64": "host/linux-x64/MyAddon.Host",
    "darwin-x64": "host/darwin-x64/MyAddon.Host",
    "darwin-arm64": "host/darwin-arm64/MyAddon.Host"
  },
  "healthCheck": "/health",
  "startupTimeout": 15000
}

Communicate with your host via HTTP:

const response = await fetch(`http://127.0.0.1:${this.context.hostPort}/my-endpoint`);

Installation

For Development (Sideloading)

  1. Copy this folder to GitCaddy's addons directory:

    • Windows: %APPDATA%/GitCaddy/addons/
    • macOS: ~/Library/Application Support/GitCaddy/addons/
    • Linux: ~/.config/GitCaddy/addons/
  2. Restart GitCaddy or reload addons from the Addon Manager

For Distribution

Package your addon as a .zip file and distribute it. Users can install via:

  • Addon Manager's "Install from file" option
  • Dropping the zip into the addons directory

Tips

  1. Use the logger - Always use context.log instead of console.log for proper addon logging
  2. Handle errors - Wrap async operations in try/catch and log errors appropriately
  3. Clean up - Always dispose of event listeners and resources in the dispose method
  4. Secure API keys - Never expose API keys in logs or UI; use the hasApiKey pattern
  5. Test on all platforms - If using native hosts, ensure they work on Windows, macOS, and Linux

Resources

License

MIT - Use this as a template for your own addons!

Description
A comprehensive demo addon for GitCaddy that showcases all available addon features and APIs.
http://www.gitcaddy.com
Readme MIT 162 KiB
2026-01-28 08:16:33 +00:00
Languages
HTML 61.1%
JavaScript 37.7%
C# 1.2%