CommunityCodierung & Entwicklunggithub.com

Glimpse — Native Micro-UI

Zeige native UI aus Skripten und Agents an — Dialoge, Formulare, Visualisierungen, Floating-Widgets, Cursor-Begleiter. Unterstützt macOS, Linux und Windows. Verwende es, wenn du HTML anzeigen, Eingaben sammeln, ein Diagramm zeigen, Markdown rendern oder eine visuelle Interaktion ohne Browser erstellen musst.

Was ist Glimpse — Native Micro-UI?

Glimpse ist ein leistungsstarkes Skill für KI-Agenten, das eine native Micro-UI direkt auf dem Desktop des Benutzers erstellt. Es öffnet ein natives Fenster mit einem Webview in unter 50 ms, sodass Agenten HTML-Code rendern und bidirektional mit der Benutzeroberfläche kommunizieren können — über `window.glimpse.send()` vom Webview zum Node.js-Prozess und über `.send(js)` vom Node zum Webview. Der Skill ist plattformübergreifend (macOS, Linux, Windows) und eignet sich ideal für Situationen, in denen eine einfache Ja/Nein-Abfrage nicht ausreicht, z. B. für Formulare, Diagramme, Markdown-Vorschauen, Bestätigungsdialoge, fliegende Benachrichtigungen oder Cursor-Begleiter. Er erkennt, ob er in einer Desktop-App (z. B. Claude Desktop oder Codex) läuft, und passt sein Verhalten entsprechend an: In Desktop-Apps wird die Inline-Anzeige bevorzugt, im Terminal kommt Glimpse voll zum Einsatz. Die Einrichtung erfolgt einfach über das Plugin `glimpse-everything`, das bei Bedarf die `glimpseui`-Bibliothek installiert. Die API bietet Funktionen wie `prompt()` für einmalige Dialoge und `open()` für persistente Fenster mit Ereignisbehandlung, Systeminformationen (Bildschirm, Darstellung, Cursorposition) und einer Vielzahl von Konfigurationsoptionen (z. B. Rahmenlosigkeit, Transparenz, Floating-Modus, Cursorfolge).

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

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Glimpse — Native Micro-UI?

Glimpse öffnet ein natives Fenster mit einem Webview in unter 50 ms. Du schreibst HTML, der Benutzer sieht es sofort. Bidirektionale Kommunikation über window.glimpse.send() (Webview → Node) und .send(js) (Node → Webview). Funktioniert auf macOS (WKWebView), Linux (WebKitGTK) und Windows (WebView2).

Wann sollte Glimpse verwendet werden:

  • Du benötigst Benutzereingaben, die über Ja/Nein hinausgehen (Formulare, Auswahlen, Texteingabe)
  • Du möchtest etwas Visuelles anzeigen (Diagramme, Markdown, Bilder, Diffs)
  • Du möchtest eine destruktive Aktion mit einem ordentlichen Dialog bestätigen
  • Du möchtest einen schwebenden Indikator, eine Benachrichtigung oder ein Companion-Widget
  • Der Benutzer soll mit umfangreichen Inhalten interagieren

Desktop-Apps: Überprüfe, ob du in einer Desktop-App läufst, die HTML, Diagramme und Formulare inline im Gespräch anzeigen kann:

  • Claude Desktop-App: CLAUDE_CODE_ENTRYPOINT ist claude-desktop.
  • Codex in der ChatGPT Desktop-App: CODEX_INTERNAL_ORIGINATOR_OVERRIDE ist Codex Desktop oder codex_work_desktop. Die Inline-Anzeige ist das gebündelte Skill visualize.
echo "claude=${CLAUDE_CODE_ENTRYPOINT:-} codex=${CODEX_INTERNAL_ORIGINATOR_OVERRIDE:-}"

In einer Desktop-App bevorzugst du die Inline-Anzeige der App. Verwende Glimpse dort nur, wenn die Inline-Anzeige etwas nicht leisten kann: ein laufendes Skript, das blockiert, bis der Benutzer antwortet; ein Fenster, das außerhalb der App sichtbar bleibt (schwebend, Status-Punkt, Cursor-folgend); oder eine Benutzeroberfläche für ein Skript, das ohne den Agenten läuft. Verwende in einem Terminal (jeder andere Wert oder beide Variablen nicht gesetzt) Glimpse wie unten beschrieben.

Einrichtung (glimpse-everything-Plugin): glimpseui wird bei der ersten Verwendung in dieses Plugin installiert. Führe dies einmal pro Sitzung aus, bevor du ein Glimpse-Skript schreibst. Es installiert glimpseui bei Bedarf, erstellt den nativen Helfer und gibt den absoluten Pfad des installierten Pakets auf stdout aus. Dieser ausgegebene Pfad ist <RESOLVED_PATH> überall unten.

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

Wenn weder CLAUDE_PLUGIN_ROOT noch CODEX_PLUGIN_ROOT gesetzt ist, führe stattdessen ../../bin/glimpse-path.sh relativ zum Verzeichnis dieser Skill-Datei aus. Die erste Ausführung kann eine Weile dauern (npm install plus nativer Build); der Fortschritt geht an stderr. Wenn es mit einem Fehler beendet wird, zeige dem Benutzer das stderr, in dem das fehlende Build-Tool genannt wird.

Import: Verwende immer den absoluten Pfad zu glimpse.mjs innerhalb des installierten Pakets — der reine Bezeichner 'glimpseui' schlägt fehl, wenn Skripte von /tmp oder von einem Ort ohne node_modules ausgeführt werden.

Auf macOS/Linux verwende einen direkten Pfad:

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

Auf Windows erfordern ESM-Importe eine file:///-URL:

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

Kurzreferenz

Einmaliger Dialog (prompt)

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

const answer = await prompt(html, {
  width: 400, height: 300,    // Fenstergröße
  title: 'Mein Dialog',         // Text der Titelleiste
  frameless: true,             // keine Titelleiste
  transparent: true,           // durchsichtiger Hintergrund
});
// answer = Daten von window.glimpse.send() oder null, wenn der Benutzer das Fenster geschlossen hat

Beständiges Fenster (open)

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

const win = open(html, options);
win.on('ready', (info) => {});       // HTML geladen — info enthält Bildschirm, Darstellung, Cursor
win.on('message', data => {});       // Benutzerinteraktion
win.on('info', info => {});          // aktuelle Systeminformationen (nach getInfo())
win.on('closed', () => {});          // Fenster weg
win.send('document.title = "Hi"');   // JS im Webview auswerten
win.setHTML('<h1>Neuer Inhalt</h1>'); // HTML ersetzen
win.info;                            // zuletzt bekannte Systeminformationen
win.getInfo();                       // aktuelle Informationen anfordern
win.close();                         // Fenster schließen

Alle Optionen

{
  width, height,          // Pixel (Standard: 800×600)
  title,                  // Fenstertitel (Standard: "Glimpse")
  frameless: true,        // keine Titelleiste, verschiebbar über Hintergrund
  floating: true,         // immer im Vordergrund
  transparent: true,      // durchsichtiger Fensterhintergrund
  clickThrough: true,     // Maus geht durch das Fenster hindurch
  followCursor: true,     // Fenster folgt dem Mauszeiger
  followMode: 'spring',  // 'snap' (sofort, Standard) oder 'spring' (elastisch)
  cursorAnchor: 'oben-rechts', // Eimrastpunkt: oben-links, oben-rechts, rechts, unten-rechts, unten-links, links
  cursorOffset: {x, y},   // Versatz vom Cursor (Standard: 20, -20)
  openLinks: true,        // angeklickte http/https-Links im Standardbrowser öffnen
  openLinksApp: '/Applications/Google Chrome.app', // optionaler App-Bundle-Pfad
  autoClose: true,        // nach der ersten Nachricht schließen
  noDock: true,           // kein Dock-Symbol oder App-Umschalter-Eintrag (macOS)
  x, y,                   // exakte Bildschirmposition
  timeout,                // für prompt() — ms vor dem Ablehnen
}

Systeminformationen (verfügbar mit ready)

win.on('ready', (info) => {
  // Bildschirm
  info.screen.width          // 2560 (volle Auflösung)
  info.screen.height         // 1440
  info.screen.scaleFactor    // 2 (Retina)
  info.screen.visibleWidth   // 2560 (ohne Dock)
  info.screen.visibleHeight  // 1367 (ohne Menüleiste)

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

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

  // Alle Monitore
  info.screens    // [{ x, y, width, height, scaleFactor, ... }, ...]
});

// Jederzeit nach ready zugreifbar:
win.info.screen.width
win.info.appearance.darkMode

JavaScript-Brücke in der Seite

window.glimpse.send(data)  // Daten an Node senden (jeder JSON-serialisierbare Wert)
window.glimpse.close()     // Fenster aus JS schließen

Muster

1. Bestätigungsdialog

Der einfachste Anwendungsfall. Frage Ja/Nein, erhalte die Antwort, mach weiter.

const answer = await prompt(`
<body style="font-family: system-ui; padding: 24px; background: white;">
  <h2 style="margin-top: 0;">47 Dateien löschen?</h2>
  <p style="color: #666;">Dies kann nicht rückgängig gemacht werden.</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;">
      Abbrechen
    </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;">
      Löschen
    </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: 'Bestätigen' });

if (answer?.ok) { /* mit dem Löschen fortfahren */ }

2. Texteingabe-Formular

Erfasse strukturierte Eingaben. Enter sendet ab, Escape bricht ab.

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;">Neue Komponente</h3>
  <input id="name" placeholder="Komponentenname" autofocus />
  <select id="type">
    <option value="page">Seite</option>
    <option value="component">Komponente</option>
    <option value="layout">Layout</option>
  </select>
  <div style="display: flex; gap: 8px; justify-content: flex-end;">
    <button onclick="window.glimpse.send(null)" style="background: #eee;">Abbrechen</button>
    <button onclick="submit()" style="background: #4299e1; color: white;">Erstellen</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: 'Erstellen' });

if (result) console.log(`Erstelle ${result.type}: ${result.name}`);

3. Auswahlliste

Wähle aus einer Liste von Optionen. Klicke oder verwende Pfeiltasten + Enter.

function pickFromList(titel, 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">${titel}</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('Wähle ein Framework', ['React', 'Vue', 'Svelte', 'Solid', 'Angular']);

4. Markdown-/Rich-Content-Viewer

Zeige formatierten Inhalt an. Nützlich für die Vorschau von generierten Dokumentationen, READMEs oder Diffs.

// Markdown mit einer CDN-Bibliothek rendern
const markdown = `# Hallo Welt\n\nDas ist **fett** und das ist \`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: 'Vorschau' });

5. Live-Fortschritt / Streaming-Ausgabe

Schiebe Aktualisierungen in das Fenster, während die Arbeit fortschreitet.

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: 'Build-Fortschritt' });

win.on('ready', () => {
  win.send(`appendLog('Baue start...')`);
  // Während dein Prozess läuft, Aktualisierungen senden:
  win.send(`appendLog('✓ 42 Dateien kompiliert')`);
  win.send(`appendLog('✓ Tests bestanden')`);
  win.send(`appendLog('✓ Fertig!')`);
});

6. Adaptive Dunkel-/Hell-Modus

Verwende Systeminformationen, um die Benutzeroberfläche an die Erscheinung des Benutzers anzupassen.

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};">Adaptive Benutzeroberfläche</h2>
      <p>Dunkelmodus: ${appearance.darkMode ? 'an' : 'aus'}</p>
      <p>Bildschirm: ${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;">
        Schließen
      </button>
    </body>
  `);
});

7. Schwebende Benachrichtigung

Ein kurzer, automatisch verschwindender Toast. Keine Interaktion erforderlich.

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('✅ In Produktion bereitgestellt');

8. Cursor-Begleiter

Ein visuelles Element, das dem Cursor folgt. Großartig für Agent-Statusanzeigen.

// Rotierender Ring, der dem Cursor folgt
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="1.5s" repeatCount="indefinite" />
    </circle>
  </svg>
</body>
`, { width: 50, height: 50, frameless: true, transparent: true, floating: true, followCursor: true });

// Später, wenn die Arbeit erledigt ist:
// win.close();

Verwandte Skills