Sidebar Tab API
JavaScript API injected by the Canopi host as window.canopi.
Initialization
javascript
const { registerTab, bridge, permissions, storage, user, theme, diagnostics } = window.canopi;
console.log(canopi.version); // e.g. "1.0.0"TypeScript: include docs/types/canopi-sdk.d.ts from the repo.
registerTab()
Register a tab with the sidebar host.
javascript
const tab = canopi.registerTab({
id: 'archive-assistant',
title: 'Archive',
icon: 'archive-icon',
position: 'right', // 'left' | 'right'
render: async (container, context) => {
container.innerHTML = '<button id="go">Archive</button>';
},
onActivate: async (context) => { /* tab selected */ },
onDeactivate: async (context) => { /* tab hidden */ },
onDestroy: () => { /* cleanup */ },
badge: {
getValue: async () => 3,
updateInterval: 30000,
},
});TabContext
| Field | Type | Description |
|---|---|---|
container | HTMLElement | Mount point for tab UI |
isActive | boolean | Tab currently visible |
userId | string | null | Authenticated user |
permissions | PermissionStatus | Granted scopes |
Bridge communication
Request/response over the host bridge:
javascript
// Get current page URL
const url = await bridge.request('page:getUrl');
// Send notification (no response)
bridge.notify('tab:ready', { tabId: 'archive-assistant' });
// Listen for host events
bridge.on('theme:changed', (payload) => {
console.log('Theme:', payload.mode);
});Permissions
javascript
const status = await permissions.check('archive:wayback');
if (!status.granted) {
await permissions.request('archive:wayback');
}Storage
javascript
// Local (tab-scoped)
await storage.local.set('lastArchive', url);
const last = await storage.local.get('lastArchive');
// Sync (cross-device, when available)
await storage.sync.set('prefs', { service: 'wayback' });User context
javascript
const profile = await user.getProfile();
// { id, displayName, avatarUrl, ... }
const prefs = await user.getPreferences();Theme
javascript
const mode = theme.getMode(); // 'light' | 'dark'
theme.onChange((mode) => applyStyles(mode));Diagnostics
javascript
diagnostics.info('archive', 'Started archive job', { url });
diagnostics.error('archive', 'Failed', { error: err.message });Error handling
javascript
try {
await bridge.request('archive:wayback', { url });
} catch (err) {
if (err.code === 'PERMISSION_DENIED') {
await permissions.request('archive:wayback');
}
}Related
Full spec: docs/SIDEBAR_TAB_SDK_API.md in the repo.

