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.
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)
-
Copy this folder to GitCaddy's addons directory:
- Windows:
%APPDATA%/GitCaddy/addons/ - macOS:
~/Library/Application Support/GitCaddy/addons/ - Linux:
~/.config/GitCaddy/addons/
- Windows:
-
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
- Use the logger - Always use
context.loginstead ofconsole.logfor proper addon logging - Handle errors - Wrap async operations in try/catch and log errors appropriately
- Clean up - Always dispose of event listeners and resources in the
disposemethod - Secure API keys - Never expose API keys in logs or UI; use the
hasApiKeypattern - Test on all platforms - If using native hosts, ensure they work on Windows, macOS, and Linux
Resources
- Octicons - Icon library for toolbar buttons
- GitCaddy Documentation - Full addon API documentation
License
MIT - Use this as a template for your own addons!