Glimpse — Micro-UI natif

Affichez des interfaces utilisateur natives à partir de scripts et d'agents — boîtes de dialogue, formulaires, visualisations, widgets flottants, compagnons de curseur. Prend en charge macOS, Linux et Windows. À utiliser lorsque vous devez afficher du HTML à l'utilisateur, collecter une saisie, montrer un graphique, rendre du Markdown ou créer toute interaction visuelle sans navigateur.

Qu'est-ce que Glimpse — Micro-UI natif ?

Glimpse est une compétence d'agent qui ouvre une fenêtre native avec une webview en moins de 50 ms, permettant aux agents d'afficher des interfaces utilisateur riches sans navigateur. Elle prend en charge macOS (WKWebView), Linux (WebKitGTK) et Windows (WebView2), et offre une communication bidirectionnelle via `window.glimpse.send()` (webview → Node) et `.send(js)` (Node → webview). Les cas d'utilisation incluent les boîtes de dialogue de confirmation, les formulaires de saisie, les visualisations de données, les notifications flottantes et les compagnons de curseur. La configuration se fait via un script shell qui installe `glimpseui` (npm + compilation native) et imprime le chemin absolu pour l'importation. La compétence fournit deux API principales : `prompt()` pour les boîtes de dialogue à usage unique qui renvoient une promesse, et `open()` pour les fenêtres persistantes avec gestion d'événements. Elle détecte automatiquement le mode sombre, la couleur d'accent, les informations d'écran et la position du curseur via le système, permettant une adaptation dynamique de l'interface utilisateur.

Compatible avec✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/danmactough/glimpse-everything/tree/HEAD/plugin/skills/glimpse

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait Glimpse — Micro-UI natif ?

Glimpse ouvre une fenêtre native avec une webview en moins de 50 ms. Vous écrivez du HTML, l'utilisateur le voit instantanément. Communication bidirectionnelle via window.glimpse.send() (webview → Node) et .send(js) (Node → webview). Fonctionne sur macOS (WKWebView), Linux (WebKitGTK) et Windows (WebView2).

Quand utiliser Glimpse :

  • Vous avez besoin d'une saisie utilisateur plus complexe que oui/non (formulaires, sélections, texte)
  • Vous voulez afficher quelque chose de visuel (graphiques, Markdown, images, diffs)
  • Vous voulez confirmer une action destructrice avec une boîte de dialogue appropriée
  • Vous voulez un indicateur flottant, une notification ou un widget compagnon
  • Vous avez besoin que l'utilisateur interagisse avec du contenu riche

Applications de bureau : Vérifiez si vous êtes dans une application de bureau qui peut afficher du HTML, des graphiques et des formulaires en ligne dans la conversation :

  • Application de bureau Claude : CLAUDE_CODE_ENTRYPOINT est claude-desktop.
  • Codex dans l'application de bureau ChatGPT : CODEX_INTERNAL_ORIGINATOR_OVERRIDE est Codex Desktop ou codex_work_desktop. Son affichage en ligne est la compétence visualize intégrée.
echo "claude=${CLAUDE_CODE_ENTRYPOINT:-} codex=${CODEX_INTERNAL_ORIGINATOR_OVERRIDE:-}"

Dans une application de bureau, préférez l'affichage en ligne de l'application. Utilisez Glimpse uniquement lorsque l'affichage en ligne ne peut pas faire ce dont vous avez besoin : un script en cours d'exécution qui bloque jusqu'à ce que l'utilisateur réponde, une fenêtre qui reste visible en dehors de l'application (flottante, élément de la barre d'état, suivi du curseur) ou une interface utilisateur pour un script qui s'exécute sans l'agent. Dans un terminal (toute autre valeur, ou les deux variables non définies), utilisez Glimpse comme décrit ci-dessous.

Configuration (plugin glimpse-everything) : glimpseui est installé dans ce plugin lors de la première utilisation. Avant d'écrire un script Glimpse, exécutez ceci une fois par session. Il installe glimpseui si nécessaire, compile son helper natif et affiche le chemin absolu du paquet installé sur stdout. Ce chemin imprimé est <RESOLVED_PATH> partout ci-dessous.

bash "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-}}/bin/glimpse-path.sh"

Si ni CLAUDE_PLUGIN_ROOT ni CODEX_PLUGIN_ROOT n'est défini, exécutez ../../bin/glimpse-path.sh résolu par rapport au répertoire de ce fichier de compétence à la place. La première exécution peut prendre un certain temps (npm install plus une compilation native) ; la progression va sur stderr. En cas de sortie non nulle, affichez son stderr à l'utilisateur, qui nomme l'outil de compilation manquant.

Importation : Utilisez toujours le chemin absolu vers glimpse.mjs dans le paquet installé — le spécificateur nu 'glimpseui' échoue lorsque les scripts sont exécutés depuis /tmp ou n'importe où sans node_modules.

Sur macOS/Linux, utilisez un chemin direct :

import { open, prompt } from '<RESOLVED_PATH>/src/glimpse.mjs';

Sur Windows, les importations ESM nécessitent une URL file:/// :

import { open, prompt } from 'file:///<RESOLVED_PATH>/src/glimpse.mjs';
// ex. import { open, prompt } from 'file:///C:/Users/moi/.pi/agent/.../glimpseui/src/glimpse.mjs';

Référence rapide

Boîte de dialogue unique (prompt)

import { prompt } from '<RESOLVED_PATH>/src/glimpse.mjs';

const answer = await prompt(html, {
  width: 400, height: 300,    // taille de la fenêtre
  title: 'Ma boîte de dialogue', // texte de la barre de titre
  frameless: true,             // pas de barre de titre
  transparent: true,           // arrière-plan transparent
});
// answer = données de window.glimpse.send(), ou null si l'utilisateur a fermé la fenêtre

Fenêtre persistante (open)

import { open } from '<RESOLVED_PATH>/src/glimpse.mjs';

const win = open(html, options);
win.on('ready', (info) => {});       // HTML chargé — info contient écran, apparence, curseur
win.on('message', data => {});       // interaction utilisateur
win.on('info', info => {});          // informations système fraîches (après getInfo())
win.on('closed', () => {});          // fenêtre disparue
win.send('document.title = "Salut"'); // évaluer du JS dans la webview
win.setHTML('<h1>Nouveau contenu</h1>'); // remplacer le HTML
win.info;                            // dernières informations système connues
win.getInfo();                       // demander des informations fraîches
win.close();                         // fermer la fenêtre

Toutes les options

{
  width, height,          // pixels (défaut : 800×600)
  title,                  // titre de la fenêtre (défaut : "Glimpse")
  frameless: true,        // pas de barre de titre, déplaçable par l'arrière-plan
  floating: true,         // toujours au premier plan
  transparent: true,      // arrière-plan de fenêtre transparent
  clickThrough: true,     // la souris traverse la fenêtre
  followCursor: true,     // la fenêtre suit le curseur de la souris
  followMode: 'spring',  // 'snap' (instantané, défaut) ou 'spring' (élastique)
  cursorAnchor: 'top-right', // point d'accroche : top-left, top-right, right, bottom-right, bottom-left, left
  cursorOffset: {x, y},   // décalage par rapport au curseur (défaut : 20, -20)
  openLinks: true,        // ouvrir les liens http/https cliqués dans le navigateur par défaut
  openLinksApp: '/Applications/Google Chrome.app', // chemin optionnel du bundle d'application
  autoClose: true,        // fermer après le premier message
  noDock: true,           // pas d'icône dans le Dock ni d'entrée dans le sélecteur d'applications (macOS)
  x, y,                   // position exacte à l'écran
  timeout,                // pour prompt() seulement — ms avant le rejet
}

Informations système (disponibles au ready)

win.on('ready', (info) => {
  // Écran
  info.screen.width          // 2560 (résolution complète)
  info.screen.height         // 1440
  info.screen.scaleFactor    // 2 (Retina)
  info.screen.visibleWidth   // 2560 (hors dock)
  info.screen.visibleHeight  // 1367 (hors barre de menus)

  // Apparence
  info.appearance.darkMode         // true
  info.appearance.accentColor      // "#007AFF"
  info.appearance.reduceMotion     // false
  info.appearance.increaseContrast // false

  // Curseur
  info.cursor.x   // 500
  info.cursor.y   // 800

  // Tous les moniteurs
  info.screens    // [{ x, y, width, height, scaleFactor, ... }, ...]
});

// Accès à tout moment après ready :
win.info.screen.width
win.info.appearance.darkMode

Pont JavaScript dans la page

window.glimpse.send(data)  // envoyer des données à Node (toute valeur sérialisable en JSON)
window.glimpse.close()     // fermer la fenêtre depuis JS

Modèles

1. Boîte de dialogue de confirmation

Le cas d'utilisation le plus simple. Demander oui/non, obtenir la réponse, passer à la suite.

const answer = await prompt(`
<body style="font-family: system-ui; padding: 24px; background: white;">
  <h2 style="margin-top: 0;">Supprimer 47 fichiers ?</h2>
  <p style="color: #666;">Cette action est irréversible.</p>
  <div style="display: flex; gap: 8px; justify-content: flex-end;">
    <button onclick="window.glimpse.send({ok: false})"
      style="padding: 10px 20px; font-size: 14px; border: 1px solid #ddd; border-radius: 8px; background: white; cursor: pointer;">
      Annuler
    </button>
    <button onclick="window.glimpse.send({ok: true})" autofocus
      style="padding: 10px 20px; font-size: 14px; border: none; border-radius: 8px; background: #e53e3e; color: white; cursor: pointer;">
      Supprimer
    </button>
  </div>
  <script>
    document.addEventListener('keydown', e => {
      if (e.key === 'Escape') window.glimpse.send({ok: false});
      if (e.key === 'Enter') window.glimpse.send({ok: true});
    });
  </script>
</body>
`, { width: 340, height: 180, title: 'Confirmation' });

if (answer?.ok) { /* procéder à la suppression */ }

2. Formulaire de saisie de texte

Collecte une entrée structurée. Entrée valide, Échap annule.

const result = await prompt(`
<body style="font-family: system-ui; padding: 24px; background: white;">
  <style>
    input, select { padding: 8px 12px; font-size: 14px; border: 1px solid #ddd; border-radius: 6px; width: 100%; margin-bottom: 12px; }
    input:focus, select:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66,153,225,0.15); }
    button { padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; }
  </style>
  <h3 style="margin-top: 0;">Nouveau composant</h3>
  <input id="name" placeholder="Nom du composant" autofocus />
  <select id="type">
    <option value="page">Page</option>
    <option value="component">Composant</option>
    <option value="layout">Disposition</option>
  </select>
  <div style="display: flex; gap: 8px; justify-content: flex-end;">
    <button onclick="window.glimpse.send(null)" style="background: #eee;">Annuler</button>
    <button onclick="submit()" style="background: #4299e1; color: white;">Créer</button>
  </div>
  <script>
    function submit() {
      window.glimpse.send({
        name: document.getElementById('name').value,
        type: document.getElementById('type').value,
      });
    }
    document.getElementById('name').addEventListener('keydown', e => {
      if (e.key === 'Enter') submit();
      if (e.key === 'Escape') window.glimpse.send(null);
    });
  </script>
</body>
`, { width: 380, height: 260, title: 'Créer' });

if (result) console.log(`Création ${result.type} : ${result.name}`);

3. Liste de sélection

Choisissez parmi une liste d'options. Cliquez ou utilisez les flèches + Entrée.

function pickFromList(title, items) {
  const itemsHTML = items.map((item, i) =>
    `<div class="item${i === 0 ? ' selected' : ''}" data-index="${i}" onclick="pick(${i})">${item}</div>`
  ).join('');

  return prompt(`
  <body style="font-family: system-ui; margin: 0; background: white;">
    <style>
      .header { padding: 12px 16px; font-size: 13px; color: #888; border-bottom: 1px solid #eee; }
      .item { padding: 10px 16px; cursor: pointer; font-size: 14px; }
      .item:hover, .item.selected { background: #4299e1; color: white; }
    </style>
    <div class="header">${title}</div>
    <div id="list">${itemsHTML}</div>
    <script>
      const items = document.querySelectorAll('.item');
      let sel = 0;
      function pick(i) { window.glimpse.send({index: i, value: items[i].textContent}); }
      document.addEventListener('keydown', e => {
        if (e.key === 'ArrowDown') { items[sel].classList.remove('selected'); sel = (sel+1) % items.length; items[sel].classList.add('selected'); }
        if (e.key === 'ArrowUp') { items[sel].classList.remove('selected'); sel = (sel-1+items.length) % items.length; items[sel].classList.add('selected'); }
        if (e.key === 'Enter') pick(sel);
        if (e.key === 'Escape') window.glimpse.send(null);
        e.preventDefault();
      });
    </script>
  </body>
  `, { width: 300, height: 40 + items.length * 38, frameless: true });
}

const choice = await pickFromList('Choisissez un framework', ['React', 'Vue', 'Svelte', 'Solid', 'Angular']);

4. Visualiseur de contenu Markdown / Riche

Affichez du contenu formaté. Utile pour prévisualiser de la documentation générée, des README ou des diffs.

// Rendre du Markdown avec une bibliothèque CDN
const markdown = `# Hello World\n\nCeci est **gras** et ceci est \`code\`.`;

const win = open(`
<body style="font-family: system-ui; padding: 24px; background: white;">
  <div id="content"></div>
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script>
    document.getElementById('content').innerHTML = marked.parse(${JSON.stringify(markdown)});
  </script>
</body>
`, { width: 600, height: 400, title: 'Aperçu' });

5. Progression en direct / Sortie en streaming

Poussez des mises à jour dans la fenêtre au fur et à mesure de l'avancement du travail.

const win = open(`
<body style="font-family: monospace; padding: 16px; background: #1a1a2e; color: #0f0; font-size: 13px;">
  <div id="log" style="white-space: pre-wrap;"></div>
  <script>
    window.appendLog = (text) => {
      document.getElementById('log').textContent += text + '\\n';
      window.scrollTo(0, document.body.scrollHeight);
    };
  </script>
</body>
`, { width: 500, height: 300, title: 'Progression de la construction' });

win.on('ready', () => {
  win.send(`appendLog('Démarrage de la construction...')`);
  // Au fur et à mesure que votre processus s'exécute, poussez les mises à jour :
  win.send(`appendLog('✓ 42 fichiers compilés')`);
  win.send(`appendLog('✓ Tests réussis')`);
  win.send(`appendLog('✓ Terminé !')`);
});

6. Mode sombre/clair adaptatif

Utilisez les informations système pour styliser l'interface utilisateur afin qu'elle corresponde à l'apparence de l'utilisateur.

const win = open('', { width: 400, height: 200 });

win.on('ready', ({ appearance, screen }) => {
  const bg = appearance.darkMode ? '#1a1a2e' : '#ffffff';
  const fg = appearance.darkMode ? '#ffffff' : '#333333';
  const accent = appearance.accentColor;

  win.setHTML(`
    <body style="font-family: system-ui; padding: 24px; background: ${bg}; color: ${fg};">
      <h2 style="color: ${accent};">Interface adaptative</h2>
      <p>Mode sombre : ${appearance.darkMode ? 'activé' : 'désactivé'}</p>
      <p>Écran : ${screen.width}×${screen.height} @${screen.scaleFactor}x</p>
      <button onclick="window.glimpse.close()"
        style="padding: 8px 16px; background: ${accent}; color: white; border: none; border-radius: 6px; cursor: pointer;">
        Fermer
      </button>
    </body>
  `);
});

7. Notification flottante

Un toast bref qui se ferme automatiquement. Aucune interaction nécessaire.

function notify(message, durationMs = 3000) {
  const win = open(`
  <body style="margin: 0; background: transparent !important;">
    <div style="
      background: rgba(0,0,0,0.85); color: white; padding: 12px 20px;
      border-radius: 10px; font-family: system-ui; font-size: 14px;
      backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
    ">${message}</div>
  </body>
  `, { width: 300, height: 60, frameless: true, transparent: true, floating: true, clickThrough: true });

  win.on('ready', () => setTimeout(() => win.close(), durationMs));
}

notify('✅ Déployé en production');

8. Compagnon de curseur

Un élément visuel qui suit le curseur. Idéal pour les indicateurs d'état d'agent.

// Anneau rotatif qui suit le curseur
const win = open(`
<body style="background: transparent !important; margin: 0;">
  <svg width="50" height="50" style="filter: drop-shadow(0 0 6px rgba(0,255,200,0.5));">
    <circle cx="25" cy="25" r="18" fill="none" stroke="cyan" stroke-width="2" stroke-dasharray="20 60">
      <animateTransform attributeName="transform" type="rotate"
        from="0 25 25" to="360 25 25" dur="1s" repeatCount="indefinite"/>
    </circle>
  </svg>
</body>
`, { width: 50, height: 50, frameless: true, transparent: true, floating: true, followCursor: true });

Skills associés