Référence API
Documentation complète de l'API Plugin Volt
Référence API Plugin
Documentation API complète pour les plugins Volt.
VoltAPI — API globale
Essentielle pour le développement d'extensions
L'objet window.VoltAPI est l'interface principale pour que les extensions interagissent avec Volt. Tous
les utilitaires, types et méthodes de communication sont accessibles via cet objet global.
Les extensions accèdent aux fonctionnalités de Volt via l'objet global window.VoltAPI :
interface VoltAPI {
// Types et enums de plugin
types: {
PluginResultType: typeof PluginResultType;
};
// Fonctions utilitaires
utils: {
fuzzyScore(query: string, target: string): number;
copyToClipboard(text: string): Promise<boolean>;
openUrl(url: string): Promise<void>;
formatNumber(num: number): string;
};
// Invoke Tauri (accès backend direct)
invoke: typeof invoke;
// Système d'événements
events: {
emit(event: string, detail?: unknown): void;
on(event: string, handler: (detail: unknown) => void): () => void;
};
// Notifications
notify(message: string, type?: "info" | "success" | "error"): void;
}VoltAPI.types
Accès aux définitions de types de plugin :
// Récupérer l'enum PluginResultType
const { PluginResultType } = window.VoltAPI.types;
// À utiliser dans vos résultats
const result: PluginResult = {
id: "1",
type: PluginResultType.Info,
title: "Mon résultat",
score: 100,
};VoltAPI.utils
Fonctions utilitaires disponibles pour toutes les extensions :
Prop
Type
Exemple d'utilisation :
async execute(result: PluginResult): Promise<void> {
const password = result.data?.password as string;
// Copier dans le presse-papiers
const success = await window.VoltAPI.utils.copyToClipboard(password);
if (success) {
window.VoltAPI.notify("Copié dans le presse-papiers !", "success");
} else {
window.VoltAPI.notify("Échec de la copie", "error");
}
}VoltAPI.invoke
Accès direct aux commandes du backend Tauri :
// Appeler n'importe quelle commande Tauri
const result = await window.VoltAPI.invoke("command_name", { param: "value" });
// Exemple : récupérer la configuration d'une extension
const config = await window.VoltAPI.invoke("get_plugin_config", {
pluginId: "my-extension",
});VoltAPI.events
Système d'événements pour la communication :
// Émettre un événement
window.VoltAPI.events.emit("my-custom-event", { data: "value" });
// S'abonner à un événement (retourne une fonction de désabonnement)
const unsubscribe = window.VoltAPI.events.on("some-event", (detail) => {
console.log("Événement reçu :", detail);
});
// Plus tard : se désabonner
unsubscribe();Événements disponibles :
plugin-update— Émis lorsqu'un plugin est mis à jourplugin-enabled— Émis lorsqu'un plugin est activéplugin-disabled— Émis lorsqu'un plugin est désactivéplugin-error— Émis lorsqu'un plugin rencontre une erreur
VoltAPI.notify
Affiche des notifications à l'utilisateur :
// Notification info (par défaut)
window.VoltAPI.notify("Traitement terminé");
// Notification de succès
window.VoltAPI.notify("Mot de passe copié !", "success");
// Notification d'erreur
window.VoltAPI.notify("Connexion échouée", "error");Interface Plugin
L'interface principale que tous les plugins doivent implémenter :
interface Plugin {
id: string;
name: string;
description: string;
enabled: boolean;
canHandle(context: PluginContext): boolean;
match(context: PluginContext): Promise<PluginResult[]> | PluginResult[] | null;
execute(result: PluginResult): Promise<void> | void;
}Propriétés du plugin
Prop
Type
Méthodes du plugin
canHandle(context: PluginContext): boolean
Critique pour la performance
Cette méthode doit être rapide (cible : <1 ms). Utilisez des vérifications de chaîne simples, pas de parsing complexe.
Détermine si ce plugin doit traiter la requête actuelle.
canHandle(context: PluginContext): boolean {
const query = context.query.toLowerCase().trim();
// Vérification rapide de préfixe
return query.startsWith('pass') || query.startsWith('password');
}match(context: PluginContext): Promise<PluginResult[]> | PluginResult[] | null
Génère des résultats pour la requête. Peut être sync ou async.
Timeout
Cette méthode a un timeout de 500 ms. Si votre plugin prend plus de temps, il sera ignoré.
async match(context: PluginContext): Promise<PluginResult[]> {
const query = context.query.replace('pass ', '');
const length = parseInt(query) || 16;
const password = generatePassword(length);
return [{
id: 'password-1',
type: window.VoltAPI.types.PluginResultType.Password,
title: password,
subtitle: `${length} caractères — Appuyez sur Entrée pour copier`,
icon: '🔐',
score: 100,
data: { password }
}];
}execute(result: PluginResult): Promise<void> | void
Appelée quand l'utilisateur sélectionne un résultat (appuie sur Entrée).
async execute(result: PluginResult): Promise<void> {
const password = result.data?.password as string;
await window.VoltAPI.utils.copyToClipboard(password);
window.VoltAPI.notify("Mot de passe copié !", "success");
}Types
PluginContext
Informations de contexte passées aux méthodes du plugin :
interface PluginContext {
query: string; // Requête de recherche de l'utilisateur
settings?: Record<string, unknown>; // Paramètres spécifiques au plugin
}PluginResult
Objet résultat renvoyé par les plugins :
interface PluginResult {
id: string; // ID unique du résultat
type: PluginResultType; // Type de résultat (affecte l'affichage)
title: string; // Texte d'affichage principal
subtitle?: string; // Texte secondaire
icon?: string; // Emoji ou chemin d'icône
badge?: string; // Texte de badge (ex. « Plugin »)
score: number; // Score de classement (0-100, plus haut = mieux)
data?: Record<string, unknown>; // Données personnalisées pour execute()
pluginId?: string; // Ajouté automatiquement par le registry
}Barème de scoring :
| Plage de score | Usage |
|---|---|
| 90-100 | Correspondances exactes |
| 70-89 | Correspondances fortes (la requête est sous-chaîne) |
| 50-69 | Correspondances partielles/floues |
| < 50 | Correspondances faibles |
PluginResultType
Types de résultats que les plugins peuvent renvoyer :
enum PluginResultType {
Calculator = "calculator",
WebSearch = "websearch",
SystemCommand = "systemcommand",
FileExplorer = "fileexplorer",
Timer = "timer",
SystemMonitor = "systemmonitor",
Steam = "steam",
Game = "game",
Clipboard = "clipboard",
Emoji = "emoji",
Info = "info",
Password = "password",
}Manifeste d'extension
Chaque extension doit avoir un fichier manifest.json à la racine :
{
"id": "password-generator",
"name": "Password Generator",
"version": "1.0.4",
"description": "Générer des mots de passe cryptographiquement sûrs",
"author": {
"name": "VoltLaunchr Community",
"github": "VoltLaunchr",
"email": "contact@volt.dev"
},
"main": "index.ts",
"icon": "assets/icon.png",
"category": "utilities",
"keywords": ["password", "security", "generator"],
"repository": "https://github.com/VoltLaunchr/volt-extensions",
"license": "MIT",
"minVoltVersion": "0.4.0",
"permissions": ["clipboard"]
}Champs du manifeste
Prop
Type
API Backend (Rust)
Pour des intégrations système avancées, vous pouvez créer des plugins backend en Rust.
Trait Plugin
use async_trait::async_trait;
#[async_trait]
pub trait Plugin: Send + Sync {
fn id(&self) -> &str;
fn name(&self) -> &str;
fn description(&self) -> &str;
fn is_enabled(&self) -> bool;
async fn initialize(&mut self) -> Result<(), String>;
async fn shutdown(&mut self) -> Result<(), String>;
}VoltPluginAPI (Rust)
VoltPluginAPI fournit système de fichiers, config, cache et logging aux plugins backend :
// Système de fichiers — isolé par répertoires de plugin
api.get_plugin_data_dir("my-plugin")?; // app_data/plugins/my-plugin/
api.get_plugin_cache_dir("my-plugin")?; // cache/plugins/my-plugin/
api.get_plugin_config_dir("my-plugin")?; // config/plugins/my-plugin/
// Persistance de configuration (JSON)
api.save_config("my-plugin", "settings", &config)?;
api.load_config::<MyConfig>("my-plugin", "settings")?;
// Cache binaire
api.write_cache("my-plugin", "data", &bytes)?;
api.read_cache("my-plugin", "data")?; // -> Vec<u8>
api.clear_cache("my-plugin")?;
// Logging
api.log("my-plugin", LogLevel::Info, "Scan...");
// Info de l'app
api.get_volt_version(); // -> &strSécurité : les IDs de plugins sont validés (max 64 caractères, alphanumérique + tirets/underscores). Les clés de cache/config sont validées pour empêcher le path traversal.
Exemple de plugin backend
use async_trait::async_trait;
use crate::core::traits::Plugin;
pub struct SteamPlugin {
enabled: bool,
}
#[async_trait]
impl Plugin for SteamPlugin {
fn id(&self) -> &str { "steam" }
fn name(&self) -> &str { "Steam Games" }
fn description(&self) -> &str { "Scanner et lancer les jeux Steam" }
fn is_enabled(&self) -> bool { self.enabled }
async fn initialize(&mut self) -> Result<(), String> {
// Scanner la bibliothèque Steam au démarrage
Ok(())
}
async fn shutdown(&mut self) -> Result<(), String> {
Ok(())
}
}Commandes Tauri
Commandes Tauri disponibles pour la gestion d'extensions :
get_plugin_config(pluginId: string)
Récupère la configuration d'un plugin spécifique.
const config = await window.VoltAPI.invoke('get_plugin_config', {
pluginId: 'my-plugin'
});set_plugin_config(pluginId: string, config: any)
Sauvegarde la configuration d'un plugin.
await window.VoltAPI.invoke('set_plugin_config', {
pluginId: 'my-plugin',
config: { enabled: true, apiKey: 'xxx' }
});install_extension(extensionId: string, downloadUrl: string)
Installe une extension depuis une URL.
uninstall_extension(extensionId: string)
Désinstalle une extension.
toggle_extension(extensionId: string, enabled: boolean)
Active ou désactive une extension.
get_installed_extensions()
Récupère la liste de toutes les extensions installées.
get_extension_details(extensionId: string)
Récupère des informations détaillées sur une extension.
fetch_extension_registry(url: string)
Récupère le registre d'extensions depuis une URL.
check_extension_updates()
Vérifie les mises à jour disponibles pour les extensions installées.
update_extension(extensionId: string, downloadUrl: string)
Met à jour une extension vers une nouvelle version.
Sandbox Web Worker
Modèle de sécurité
Les extensions communautaires s'exécutent dans un Web Worker dédié sans accès direct au DOM, au réseau ou au système de fichiers. Chaque action privilégiée est relayée par le thread principal qui applique les permissions déclarées.
Modèle d'exécution
| Phase | Où ça tourne | Contraintes |
|---|---|---|
canHandle | Thread principal | Déclaratif (prefix / keywords du manifeste). Doit être < 0,1 ms. |
match | Worker | Relayé via postMessage. Timeout 500 ms. |
execute | Worker | Mêmes contraintes que match. Les effets de bord passent par les API proxifiées. |
| Reprise sur crash | Hôte Volt | Les Workers terminés sont automatiquement recréés au prochain appel. |
Ce qui est bloqué dans le Worker
| API | Statut | Remplacement |
|---|---|---|
eval / Function() | ❌ Bloqué | Aucun (CSP appliquée) |
XMLHttpRequest | ❌ Bloqué | VoltAPI.fetch (gardé par permissions) |
WebSocket | ❌ Bloqué | Non exposé |
importScripts | ❌ Bloqué | Imports de modules uniquement |
| Accès DOM direct | ❌ Bloqué | Pas de document / window |
fetch | 🟡 Proxifié | Relayé au thread principal, scoping par hôte |
| Presse-papiers | 🟡 Proxifié | VoltAPI.utils.copyToClipboard |
| Notifications | 🟡 Proxifié | VoltAPI.notify |
Modèle de permissions
Les extensions déclarent les capacités requises dans manifest.json :
{
"permissions": {
"network": ["api.github.com", "raw.githubusercontent.com"],
"clipboard": true,
"notifications": true
}
}Au premier chargement, Volt affiche une boîte de dialogue de consentement listant chaque permission déclarée. Les permissions accordées sont persistées dans installed.json. L'hôte bloque tout appel ne correspondant pas à une permission accordée, même si le code de l'extension tente de la contourner.
Les listes d'hôtes réseau sont matchées exactement — les wildcards (*.example.com) sont supportés mais * seul est rejeté.
OAuth pour extensions
Les extensions qui doivent dialoguer avec une API tierce au nom de l'utilisateur peuvent utiliser le flux OAuth intégré à Volt.
const token = await window.VoltAPI.oauth.authorize({
provider: "github",
scopes: ["read:user", "repo"],
});
// Le token est à courte durée de vie ; rafraîchissement transparent via :
const fresh = await window.VoltAPI.oauth.getToken("github");Ce que Volt fournit :
- PKCE pour tous les flux (pas de client secret dans le bundle d'extension)
- Validation des claims JWT pour les fournisseurs émettant des ID tokens
- Protection CSRF via le paramètre
state - Rotation des refresh tokens avec retry automatique sur 401
- Scoping par extension — les tokens ne sont jamais partagés entre extensions
Fournisseurs supportés actuellement : GitHub, Notion, Google, OAuth 2.0 générique avec PKCE. Demandez l'ajout d'un fournisseur dans le registre des extensions.
Exemple complet d'extension
Voici une extension complète, prête pour la production :
// Extension Password Generator
// manifest.json: { "id": "password-generator", "main": "index.ts", ... }
interface Plugin {
id: string;
name: string;
description: string;
enabled: boolean;
canHandle(context: PluginContext): boolean;
match(context: PluginContext): Promise<PluginResult[]> | PluginResult[];
execute(result: PluginResult): Promise<void>;
}
interface PluginContext {
query: string;
settings?: Record<string, unknown>;
}
interface PluginResult {
id: string;
type: any;
title: string;
subtitle?: string;
icon?: string;
score: number;
data?: Record<string, unknown>;
}
const TRIGGERS = ["pass", "password", "pwd"];
function generatePassword(length: number): string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
const array = new Uint32Array(length);
crypto.getRandomValues(array);
return Array.from(array, (num) => charset[num % charset.length]).join("");
}
class PasswordGeneratorPlugin implements Plugin {
id = "password-generator";
name = "Password Generator";
description = "Générer des mots de passe cryptographiquement sûrs";
enabled = true;
canHandle(context: PluginContext): boolean {
const query = context.query.toLowerCase().trim();
return TRIGGERS.some((trigger) => query.startsWith(trigger));
}
match(context: PluginContext): PluginResult[] {
const query = context.query.toLowerCase().trim();
// Extraire la longueur de la requête (ex. "pass 20" → 20)
const parts = query.split(/\s+/);
const lengthArg = parts.find((p) => /^\d+$/.test(p));
const length = lengthArg ? parseInt(lengthArg, 10) : 16;
// Borner la longueur dans des limites raisonnables
const finalLength = Math.max(8, Math.min(128, length));
const password = generatePassword(finalLength);
const { PluginResultType } = window.VoltAPI.types;
return [
{
id: "generated-password",
type: PluginResultType.Password,
title: password,
subtitle: `${finalLength} caractères — Appuyez sur Entrée pour copier`,
icon: "🔐",
score: 100,
data: { password },
},
];
}
async execute(result: PluginResult): Promise<void> {
const password = result.data?.password as string;
if (password) {
const success = await window.VoltAPI.utils.copyToClipboard(password);
if (success) {
window.VoltAPI.notify("Mot de passe copié dans le presse-papiers !", "success");
} else {
window.VoltAPI.notify("Échec de la copie du mot de passe", "error");
}
}
}
}
export default PasswordGeneratorPlugin;Pour plus d'exemples, consultez la section Exemples de plugins. Pour publier votre extension, voir le Guide de publication.