Plugin index.js Context API
The plugin's index.js runs in a Node.js sandbox environment as a backend script. The system injects a set of global objects for plugin use.
Global Context Objects
Plugin index.js can access the following APIs via globalThis:
const {
// Core APIs
logger, // Logger
http, // HTTP requests (axios)
clipboard, // Clipboard operations
storage, // Plugin-specific storage
// Feature APIs
feature, // Feature SDK (push search results)
search, // Search manager (deprecated, use feature)
box, // CoreBox control
boxItems, // BoxItem management
// Plugin Management
plugin, // Current plugin info
plugins, // Other plugins API
features, // Dynamic Feature management
// Communication
channel, // IPC channel bridge
$event, // Feature event listeners
// UI
dialog, // System dialogs
divisionBox, // DivisionBox SDK
// Utilities
TuffItemBuilder, // Search result builder
URLSearchParams, // URL parameter handling
openUrl, // Open external links
} = globalThis
logger
Plugin logger - logs are saved to the plugin's log directory.
logger.info('Info message', { extra: 'data' })
logger.warn('Warning message')
logger.error('Error message', error)
logger.debug('Debug message')
http
HTTP request library (axios-based):
// GET request
const response = await http.get('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer token' },
signal // AbortSignal for cancellation
})
// POST request
const result = await http.post('https://api.example.com/submit', {
data: 'payload'
}, { signal })
clipboard
Clipboard operations:
// Write text
clipboard.writeText('Copied content')
// Read text
const text = clipboard.readText()
// Read image
const image = clipboard.readImage()
// Write image
clipboard.writeImage(nativeImage)
storage
Plugin-specific storage (10MB limit per plugin):
// Read config file
const config = storage.getFile('providers_config')
// Save config file
storage.setFile('providers_config', { key: 'value' })
// Delete config file
storage.deleteFile('old_config')
// List all files
const files = storage.listFiles() // ['file1', 'file2']
// Watch for config changes
const unsubscribe = storage.onDidChange('providers_config', (newConfig) => {
console.log('Config updated:', newConfig)
})
// Unsubscribe
unsubscribe()
feature
Feature SDK for managing search results:
// Push search results
feature.pushItems([
new TuffItemBuilder('item-1')
.setTitle('Search Result Title')
.setSubtitle('Subtitle')
.setIcon({ type: 'file', value: 'assets/icon.svg' })
.build()
])
// Clear current plugin's search results
feature.clearItems()
// Get current plugin's search results
const items = feature.getItems()
box
CoreBox control SDK:
// Hide CoreBox
box.hide()
// Show CoreBox
box.show()
// Set input content
box.setInput('New input content')
// Get input content
const input = box.getInput()
boxItems
BoxItem management SDK (new API):
// Push single item
boxItems.push(item)
// Push multiple items
boxItems.pushItems([item1, item2])
// Update specific item
boxItems.update('item-id', { title: 'New Title' })
// Remove specific item
boxItems.remove('item-id')
// Clear all items for this plugin
boxItems.clear()
// Get all items for this plugin
const items = boxItems.getItems()
plugin
Current plugin info API:
// Get complete plugin info
const info = plugin.getInfo()
// { name, version, desc, readme, dev, status, features, issues, ... }
// Get plugin path
const path = plugin.getPath()
// Get data directory
const dataPath = plugin.getDataPath()
// Get config directory
const configPath = plugin.getConfigPath()
// Get logs directory
const logsPath = plugin.getLogsPath()
// Get temp directory
const tempPath = plugin.getTempPath()
// Get current status
const status = plugin.getStatus()
// Get dev configuration
const devInfo = plugin.getDevInfo()
// Get platform support info
const platforms = plugin.getPlatforms()
plugins
Other plugins API (read-only access):
// Get all plugins list
const allPlugins = await plugins.list()
// Get specific plugin info
const otherPlugin = await plugins.get('other-plugin-name')
// Get plugin status
const status = await plugins.getStatus('other-plugin-name')
features
Dynamic Feature management:
// Add Feature dynamically
features.addFeature({
id: 'dynamic-feature',
name: 'Dynamic Feature',
desc: 'Runtime-added feature',
icon: { type: 'file', value: 'assets/icon.svg' },
push: true,
commands: [{ type: 'over', value: ['dynamic'] }],
priority: 5
})
// Remove Feature
features.removeFeature('dynamic-feature')
// Get all Features
const allFeatures = features.getFeatures()
// Get specific Feature
const feature = features.getFeature('feature-id')
// Set priority
features.setPriority('feature-id', 10)
// Get priority
const priority = features.getPriority('feature-id')
// Get sorted by priority
const sorted = features.getFeaturesByPriority()
channel
IPC channel bridge:
// Send message to main process
const result = await channel.sendToMain('event-name', { data: 'payload' })
// Send message to renderer process
await channel.sendToRenderer('event-name', { data: 'payload' })
// Listen to main process messages
const dispose = channel.onMain('event-name', (data) => {
console.log('Received from main:', data)
})
// Listen to renderer process messages
const dispose = channel.onRenderer('event-name', (data) => {
console.log('Received from renderer:', data)
})
// Access raw channel object
channel.raw
$event
Feature event listeners:
// Listen to Feature lifecycle
$event.onFeatureLifeCycle('feature-id', {
onLaunch: (feature) => { console.log('Launched', feature) },
onFeatureTriggered: (data, feature) => { console.log('Triggered', data) },
onInputChanged: (input) => { console.log('Input changed', input) },
onClose: (feature) => { console.log('Closed', feature) }
})
// Remove listener
$event.offFeatureLifeCycle('feature-id', callback)
dialog
System dialogs:
// Message dialog
await dialog.showMessageBox({
type: 'info',
title: 'Title',
message: 'Message content',
buttons: ['OK', 'Cancel']
})
// Open file dialog
const result = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [{ name: 'Images', extensions: ['jpg', 'png'] }]
})
// Save file dialog
const result = await dialog.showSaveDialog({
defaultPath: 'file.txt'
})
divisionBox
DivisionBox SDK for creating independent windows:
// Open DivisionBox
const session = await divisionBox.open({
url: 'plugin://my-plugin/index.html',
title: 'Independent Window',
size: 'medium', // 'compact' | 'medium' | 'expanded'
keepAlive: true
})
// Close DivisionBox
await divisionBox.close(session.sessionId)
// Listen to state changes
divisionBox.onStateChange(session.sessionId, (state) => {
console.log('State changed:', state)
})
TuffItemBuilder
Search result builder:
const item = new TuffItemBuilder('unique-id')
.setSource('plugin', 'plugin-features')
.setTitle('Title')
.setSubtitle('Subtitle')
.setIcon({ type: 'file', value: 'assets/icon.svg' })
.createAndAddAction('action-id', 'copy', 'Copy', 'Content to copy')
.addTag('Tag', 'blue')
.setMeta({
pluginName: 'my-plugin',
featureId: 'my-feature',
customData: 'any value'
})
.build()
openUrl
Open external links:
openUrl('https://example.com')
Lifecycle Hooks
Plugin index.js must export a lifecycle hooks object:
const pluginLifecycle = {
/**
* Called when a Feature is triggered
* @param {string} featureId - Feature ID
* @param {string|TuffQuery} query - Query content
* @param {IPluginFeature} feature - Feature definition
* @param {AbortSignal} signal - For cancellation
*/
async onFeatureTriggered(featureId, query, feature, signal) {
// Compatibility: query can be string or TuffQuery object
const queryText = typeof query === 'string' ? query : query?.text
// Handle Feature logic...
},
/**
* Called when a search result item is clicked
* @param {TuffItem} item - The clicked item
*/
async onItemAction(item) {
if (item.meta?.defaultAction === 'copy') {
const copyAction = item.actions.find(a => a.type === 'copy')
if (copyAction?.payload) {
clipboard.writeText(copyAction.payload)
box.hide()
}
}
}
}
module.exports = pluginLifecycle
Best Practices
- Use AbortSignal: Pass signal parameter in async operations for cancellation support
- Error Handling: Wrap all async operations with try-catch
- Logging: Use logger instead of console for debugging and collection
- Storage Limits: Mind the 10MB storage limit, use tempPath for large files
- TuffQuery Compatibility: Handle query as both string and object formats
Related Documentation
- Feature SDK - Feature detailed API
- DivisionBox API - Independent window system
- Flow Transfer API - Plugin data transfer