Zum Hauptinhalt springen

Tutorial: Erste App erstellen

In diesem Tutorial erstellen Sie ein Wortzähler-Mini-Programm, das den aktiven Chat-Verlauf liest, Wörter nach Rolle (Benutzer vs. Assistant) zählt und Statistiken anzeigt. Am Ende wissen Sie, wie Sie ein Mini-Programm erstellen, testen, paketieren und veröffentlichen.

Was Sie lernen werden:

  • Erstellen einer manifest.json
  • Schreiben von App-HTML mit dem window.ais SDK
  • Verwendung von ais.chat.getHistory() zum Lesen von Nachrichten
  • Verwendung von ais.storage zum Persistieren von Daten über Sitzungen hinweg
  • Lokales Testen mit Sideloading
  • Paketieren als .ais-Bundle
  • Veröffentlichen in der Community-Registry

Zeitaufwand: Etwa 15 Minuten.


Schritt 1: Manifest erstellen

Jedes Mini-Programm benötigt eine manifest.json, die die App, ihre Berechtigungen und ihren Einstiegspunkt beschreibt. Erstellen Sie ein neues Verzeichnis und fügen Sie diese Datei hinzu:

mkdir word-counter
cd word-counter

Erstellen Sie manifest.json:

{
"name": "word-counter",
"version": "1.0.0",
"abi": 1,
"type": "mini-program",
"title": "Wortzähler",
"description": "Zählen Sie Wörter in Ihrem Chat-Verlauf nach Rolle",
"author": { "name": "Ihr Name" },
"entry": "index.html",
"base_url": "https://localhost:8080/",
"permissions": ["storage", "chat:read", "ui:toast"],
"keywords": ["statistik", "werkzeug"]
}

Wichtige Felder:

FeldWertWarum
nameword-counterEindeutiger Bezeichner (Kleinbuchstaben, nur Bindestriche)
abi1Erforderlich – entspricht aktueller Plattform-ABI
typemini-programTeilt der Plattform mit, dass dies eine sandboxed iframe-App ist
entryindex.htmlDie zu ladende HTML-Datei
base_urlhttps://localhost:8080/Wo Assets gehostet werden (für Produktion aktualisieren)
permissions["storage", "chat:read", "ui:toast"]Wir müssen Chat lesen und Statistiken speichern
Info

Die storage-Berechtigung wird immer automatisch gewährt, aber es ist gute Praxis, sie explizit aufzulisten, damit Benutzer wissen, dass Ihre App Daten speichert.


Schritt 2: HTML erstellen

Erstellen Sie index.html im selben Verzeichnis. Dies ist Ihre gesamte App – HTML, CSS und JavaScript in einer Datei.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 20px;
color: #e0e0e0;
background: #1a1a2e;
min-height: 100vh;
}
h1 {
font-size: 22px;
margin-bottom: 16px;
}

.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
margin-bottom: 20px;
}
.stat-card {
background: #16213e;
border: 1px solid #333;
border-radius: 8px;
padding: 16px;
text-align: center;
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: #58a6ff;
font-variant-numeric: tabular-nums;
}
.stat-label {
font-size: 14px;
color: #888;
margin-top: 4px;
}

.actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
button {
min-height: 48px;
padding: 10px 20px;
font-size: 16px;
border: 1px solid #444;
border-radius: 6px;
background: #2a2a4e;
color: #e0e0e0;
cursor: pointer;
}
button:hover {
background: #3a3a5e;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-primary {
background: #1a73e8;
border-color: #1a73e8;
}
.btn-primary:hover {
background: #1557b0;
}
.btn-close {
background: none;
border-color: #666;
color: #888;
}

#last-updated {
margin-top: 16px;
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<h1>Wortzähler</h1>

<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="total-words">--</div>
<div class="stat-label">Wörter gesamt</div>
</div>
<div class="stat-card">
<div class="stat-value" id="user-words">--</div>
<div class="stat-label">Ihre Wörter</div>
</div>
<div class="stat-card">
<div class="stat-value" id="ai-words">--</div>
<div class="stat-label">KI-Wörter</div>
</div>
<div class="stat-card">
<div class="stat-value" id="msg-count">--</div>
<div class="stat-label">Nachrichten</div>
</div>
</div>

<div class="actions">
<button class="btn-primary" id="analyze">Chat analysieren</button>
<button class="btn-close" id="close">Schließen</button>
</div>

<div id="last-updated"></div>

<script>
// Hilfsfunktion: Wörter in einem String zählen
function countWords(text) {
if (!text || typeof text !== "string") return 0;
return text.trim().split(/\s+/).filter(Boolean).length;
}

// Zahlen mit Tausendertrennzeichen formatieren (1234 -> "1.234")
function fmt(n) {
return n.toLocaleString("de-DE");
}

ais.ready(async function () {
// Panel-Titel setzen
ais.ui.setTitle("Wortzähler");

// Versuche zuletzt gespeicherte Statistiken wiederherzustellen
var saved = await ais.storage.get("last-stats");
if (saved) {
showStats(saved);
}

// Analyse-Button
document
.getElementById("analyze")
.addEventListener("click", async function () {
this.disabled = true;
this.textContent = "Analysiere...";

try {
// Bis zu 500 Nachrichten aus dem Chat-Verlauf abrufen
var messages = await ais.chat.getHistory(500);

var userWords = 0;
var aiWords = 0;
var msgCount = messages.length;

for (var i = 0; i < messages.length; i++) {
var msg = messages[i];
var words = countWords(msg.content);
if (msg.role === "user") {
userWords += words;
} else if (msg.role === "assistant") {
aiWords += words;
}
}

var stats = {
total: userWords + aiWords,
user: userWords,
ai: aiWords,
messages: msgCount,
timestamp: Date.now(),
};

showStats(stats);

// Statistiken für das nächste Mal speichern
await ais.storage.set("last-stats", stats);

ais.ui.toast(msgCount + " Nachrichten analysiert!");
} catch (err) {
ais.ui.toast("Fehler: " + err.message);
}

this.disabled = false;
this.textContent = "Chat analysieren";
});

// Schließen-Button
document.getElementById("close").addEventListener("click", function () {
ais.close();
});
});

function showStats(stats) {
document.getElementById("total-words").textContent = fmt(stats.total);
document.getElementById("user-words").textContent = fmt(stats.user);
document.getElementById("ai-words").textContent = fmt(stats.ai);
document.getElementById("msg-count").textContent = fmt(stats.messages);

if (stats.timestamp) {
var date = new Date(stats.timestamp);
document.getElementById("last-updated").textContent =
"Zuletzt analysiert: " + date.toLocaleString("de-DE");
}
}
</script>
</body>
</html>

Was dieser Code macht

  1. ais.ready() – Wartet darauf, dass die SDK-Bridge verbunden ist, bevor Logik ausgeführt wird.
  2. ais.storage.get('last-stats') – Stellt zuvor gespeicherte Statistiken wieder her, damit der Benutzer beim Start sofort Daten sieht.
  3. ais.chat.getHistory(500) – Ruft bis zu 500 Nachrichten aus der aktiven Konversation ab.
  4. Wortzählung – Iteriert durch Nachrichten, teilt den Inhalt an Leerzeichen auf und zählt nach Rolle.
  5. ais.storage.set('last-stats', stats) – Persistiert die Ergebnisse für das nächste Mal.
  6. ais.ui.toast() – Zeigt eine Benachrichtigung an, wenn die Analyse abgeschlossen ist.
  7. ais.close() – Kehrt zur Chat-Ansicht zurück, wenn der Benutzer auf Schließen klickt.

Schritt 3: Lokal testen

Sie benötigen einen lokalen HTTP-Server, um das Manifest und die HTML-Datei bereitzustellen. Verwenden Sie ein beliebiges Tool:

# Python 3
cd word-counter
python3 -m http.server 8080

# oder Node.js
npx serve -p 8080

# oder PHP
php -S localhost:8080

Installieren Sie nun die App in der Plattform:

  1. Öffnen Sie aiscouncil.net und melden Sie sich an
  2. Klicken Sie auf das Apps-Symbol in der linken Seitenleiste
  3. Im Sideload-Bereich fügen Sie ein: http://localhost:8080/manifest.json
  4. Klicken Sie auf Installieren
  5. Überprüfen Sie die Berechtigungen (storage, chat:read, ui:toast) und klicken Sie auf Erlauben
  6. Klicken Sie auf Öffnen auf der installierten App-Karte
Tipp

Für den schnellsten Entwicklungs-Loop verwenden Sie HTML-Upload statt URL-Sideloading. Laden Sie Ihre index.html direkt hoch – kein Server nötig. Die Plattform erstellt automatisch ein synthetisches Manifest. Sie können deinstallieren und bei jeder Änderung erneut hochladen.

Fehlerbehebung

ProblemLösung
"Failed to fetch manifest"Stellen Sie sicher, dass Ihr lokaler Server läuft und CORS-Header bereitstellt. Versuchen Sie python3 -m http.server 8080, das CORS-sicher bereitstellt.
App zeigt leere weiße SeitePrüfen Sie die Browser-Konsole auf Fehler. Das häufigste Problem ist der Aufruf von ais.*-Methoden vor ais.ready().
"PermissionDenied: chat:read"Ihr Manifest enthält chat:read nicht im Berechtigungs-Array. Aktualisieren Sie das Manifest und installieren Sie neu.
App aktualisiert sich nicht nach Code-ÄnderungenDeinstallieren Sie die App zuerst (X-Button auf der Karte klicken), dann neu installieren. Entry-HTML wird bei der Installation gecacht.

Schritt 4: Etwas verbessern

Fügen wir eine Funktion hinzu: Echtzeit-Wortzählung, wenn neue Nachrichten eintreffen.

Fügen Sie diesen Code innerhalb des ais.ready()-Callbacks nach dem Schließen-Button-Handler hinzu:

// Neue Nachrichten für Echtzeit-Zählung abonnieren
ais.chat.onMessage(function (msg) {
// Gespeicherte Statistiken neu lesen und die Wörter der neuen Nachricht hinzufügen
ais.storage.get("last-stats").then(function (stats) {
if (!stats) return;
var words = countWords(msg.content);
if (msg.role === "user") stats.user += words;
else if (msg.role === "assistant") stats.ai += words;
stats.total = stats.user + stats.ai;
stats.messages++;
stats.timestamp = Date.now();
showStats(stats);
ais.storage.set("last-stats", stats);
});
});

Jetzt werden die Zähler live aktualisiert, während der Benutzer chattet, ohne erneut auf "Analysieren" klicken zu müssen.


Schritt 5: Als .ais-Bundle paketieren

Ein .ais-Bundle ist ein ZIP-Archiv, das Ihr Manifest und alle App-Dateien enthält. Die Plattform extrahiert das ZIP, liest das Manifest und inlineiert alle Assets (CSS, JS, Bilder) in das Entry-HTML.

Für eine Single-File-App wie unsere ist das Bundle einfach:

cd word-counter
zip -r ../word-counter.ais manifest.json index.html

Das erstellt word-counter.ais im übergeordneten Verzeichnis.

Das Bundle testen

  1. Gehen Sie in der Plattform zu Apps und klicken Sie auf App hochladen
  2. Wählen Sie word-counter.ais
  3. Überprüfen Sie die Berechtigungen und bestätigen Sie
  4. Die App wird aus dem Bundle mit allen inlineierten Assets installiert
Info

Bundles sind in sich geschlossen. Der Benutzer benötigt keinen Netzwerkzugriff auf die ursprüngliche base_url – alles ist bei der Installation inlineiert. Das macht Bundles ideal für Offline-Verbreitung und Teilen.

Multi-File-Bundles

Wenn Ihre App separate CSS-, JavaScript- oder Bilddateien hat, fügen Sie sie alle ins ZIP ein:

zip -r ../word-counter.ais manifest.json index.html style.css app.js icon.png

Die Plattform inlineiert automatisch:

  • <link rel="stylesheet" href="style.css"> wird zu <style>...</style>
  • <script src="app.js"></script> wird zu <script>...</script>
  • <img src="icon.png"> wird zu <img src="data:image/png;base64,...">

Schritt 6: In der Registry veröffentlichen

Sobald Ihre App für andere bereit ist, veröffentlichen Sie sie in der Community-Registry.

1. Dateien hosten

Laden Sie Ihr Manifest und Entry-HTML auf ein öffentliches CDN hoch. GitHub Pages ist kostenlos und einfach:

# In Ihrem GitHub-Repo (z.B. github.com/yourname/word-counter)
# Pushen Sie manifest.json und index.html in den main Branch
# Aktivieren Sie GitHub Pages in den Repo-Einstellungen (Quelle: main Branch, Root)

Ihre Dateien sind verfügbar unter:

  • https://yourname.github.io/word-counter/manifest.json
  • https://yourname.github.io/word-counter/index.html

Aktualisieren Sie base_url in Ihrem Manifest entsprechend:

"base_url": "https://yourname.github.io/word-counter/"

2. aiscouncil Repo forken

Gehen Sie zu github.com/nicholasgasior/bcz und klicken Sie auf Fork.

3. Paketeintrag hinzufügen

Bearbeiten Sie registry/packages.json und fügen Sie einen Eintrag zum packages-Array hinzu:

{
"name": "word-counter",
"type": "mini-program",
"version": "1.0.0",
"manifest": "https://yourname.github.io/word-counter/manifest.json",
"tier": "community",
"category": "utilities",
"description": "Zählen Sie Wörter in Ihrem Chat-Verlauf nach Rolle",
"icon": "https://yourname.github.io/word-counter/icon.png",
"added": "2026-02-19",
"price": 0,
"currency": "USD",
"seller": null
}

4. Validieren

Führen Sie das Validierungsskript aus, um Ihren Eintrag zu prüfen:

python3 registry/validate.py packages

Wenn die Validierung besteht, sind Sie bereit zum Einreichen.

5. Pull Request erstellen

Pushen Sie Ihre Änderungen zu Ihrem Fork und erstellen Sie einen PR gegen das Haupt-Repo. Wenn die automatisierte Validierung besteht, kann der PR gemergt werden und Ihre App erscheint im App Store-Bereich der Plattform.

Siehe In der Registry veröffentlichen für vollständige Details zu Preisen, Seller-Setup und Verifizierungsstufen.


Tipps und Best Practices

Design

  • Dunkles Theme als Standard – Die meisten Plattform-Benutzer nutzen Dark Mode. Entwerfen Sie für dunkle Hintergründe (#1a1a2e oder ähnlich) mit hellem Text (#e0e0e0).
  • 48px Mindest-Touch-Targets – Buttons und interaktive Elemente sollten mindestens 48px hoch sein für Barrierefreiheit und VLM-freundliche Interaktion.
  • 14px Mindestschriftgröße – Der gesamte Text muss mindestens 14px sein für Lesbarkeit.
  • Responsives Layout – Die Apps-Panel-Breite variiert. Verwenden Sie CSS Grid oder Flexbox mit auto-fit zur Anpassung.

Performance

  • Ergebnisse im Speicher cachen – Verwenden Sie ais.storage, um berechnete Ergebnisse zu speichern. Stellen Sie sie beim Start wieder her, damit der Benutzer sofort Daten sieht.
  • Chat-Verlauf-Anfragen begrenzenais.chat.getHistory(500) ist normalerweise genug. Vermeiden Sie unbegrenzte Verlaufs-Anfragen.
  • Kein Polling – Verwenden Sie ais.chat.onMessage() für Echtzeit-Updates statt wiederholter getHistory-Aufrufe.

Sicherheit

  • Minimale Berechtigungen anfordern – Listen Sie nur die Berechtigungen auf, die Ihre App tatsächlich verwendet. Weniger Berechtigungen bedeuten, dass mehr Benutzer Ihrer App vertrauen und sie installieren.
  • Alle Eingaben validieren – Daten aus ais.chat.getHistory() enthalten benutzergenerierte Inhalte. Bereinigen Sie vor dem Einfügen ins DOM.
  • Keine sensiblen Daten speichernais.storage ist nicht verschlüsselt. Speichern Sie niemals Passwörter, Tokens oder API-Schlüssel (außer Ihre App hat secrets:sync und behandelt explizit Credential-Übertragung).

Kompatibilität

  • ais.platform.abi prüfen – Wenn Ihre App von spezifischen SDK-Funktionen abhängt, prüfen Sie die ABI-Version und zeigen Sie eine hilfreiche Nachricht an, wenn die Plattform älter ist.
  • SDK-Aufrufe in try/catch wickeln – Berechtigungsfehler und Plattform-Versionsunterschiede können Rejections verursachen. Behandeln Sie sie elegant.
  • Mit dem hello-world-Beispiel testen – Die Plattform wird mit einem Beispiel-Mini-Programm ausgeliefert, das Sie als Referenz verwenden können.