Register and update a panel#
module.exports = {
onInit(context) {
context.ui.registerTab({
id: 'status', title: 'Status', icon: 'terminal',
actions: [{ id: 'refresh', label: 'Refresh',
location: 'custom_tab', actionId: 'refresh' }]
});
context.ui.registerButton({
id: 'open-status', label: 'Status', icon: 'terminal',
location: 'sidebar_actions', actionId: 'refresh', openTabId: 'status'
});
context.ui.setTabContent('status', '<h3>Ready</h3><p>Plugin loaded.</p>');
},
onAction(actionId, context) {
if (actionId !== 'refresh') throw new Error('Unknown action');
context.ui.setTabContent('status', '<p>Updated: ' + new Date().toISOString() + '</p>');
}
};Register first, then update content. Content updates to an unknown tab are ignored. Use short, semantic HTML and the native action bar to run backend work.
HTML content and escaping#
Custom tab content is inserted with React dangerouslySetInnerHTML into the main interface. It is not an iframe and has no isolated iframe sandbox. Script tags inserted this way are not a supported event-binding mechanism. Use declared actions for interaction rather than inline handlers.
function escapeHTML(value) {
return String(value).replace(/[&<>"']/g, character => ({
'&': '&', '<': '<', '>': '>',
'"': '"', "'": '''
})[character]);
}
// Escape user-controlled names before including them in HTML.
context.ui.setTabContent('status', '<p>' + escapeHTML(clip.name) + '</p>');Do not insert raw remote response bodies, user-supplied attributes or global CSS selectors into tab content. Keep CSS scoped to a plugin-specific wrapper so it does not alter HydroClips controls.
Icons and persistence#
Sidebar and custom tab mappings include zap, play, sparkles, radio, camera, sliders, terminal, bot, message-square, cloud and refresh. Unrecognized names fall back to Puzzle. The plugin-list avatar has a smaller mapping, so an icon may look different there.
Register/remove calls write the manifest. updateTab and setTabContent update memory and the renderer; reload reconstructs UI from the manifest plus onInit. Recreate dynamic content in onInit or derive it from saved config.
Implementation reference: PluginCustomTab.tsx; HydroLiquidSidebar.tsx; src/main/plugins/PluginManager.ts · native 2.0.7.