Volt LogoVolt
Extension Development

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 à jour
  • plugin-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 scoreUsage
90-100Correspondances exactes
70-89Correspondances fortes (la requête est sous-chaîne)
50-69Correspondances partielles/floues
< 50Correspondances 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();  // -> &str

Sé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

PhaseOù ça tourneContraintes
canHandleThread principalDéclaratif (prefix / keywords du manifeste). Doit être < 0,1 ms.
matchWorkerRelayé via postMessage. Timeout 500 ms.
executeWorkerMêmes contraintes que match. Les effets de bord passent par les API proxifiées.
Reprise sur crashHôte VoltLes Workers terminés sont automatiquement recréés au prochain appel.

Ce qui est bloqué dans le Worker

APIStatutRemplacement
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 :

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 :

index.ts
// 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.

On this page