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.aisSDK - Verwendung von
ais.chat.getHistory()zum Lesen von Nachrichten - Verwendung von
ais.storagezum 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:
| Feld | Wert | Warum |
|---|---|---|
name | word-counter | Eindeutiger Bezeichner (Kleinbuchstaben, nur Bindestriche) |
abi | 1 | Erforderlich – entspricht aktueller Plattform-ABI |
type | mini-program | Teilt der Plattform mit, dass dies eine sandboxed iframe-App ist |
entry | index.html | Die zu ladende HTML-Datei |
base_url | https://localhost:8080/ | Wo Assets gehostet werden (für Produktion aktualisieren) |
permissions | ["storage", "chat:read", "ui:toast"] | Wir müssen Chat lesen und Statistiken speichern |
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
ais.ready()– Wartet darauf, dass die SDK-Bridge verbunden ist, bevor Logik ausgeführt wird.ais.storage.get('last-stats')– Stellt zuvor gespeicherte Statistiken wieder her, damit der Benutzer beim Start sofort Daten sieht.ais.chat.getHistory(500)– Ruft bis zu 500 Nachrichten aus der aktiven Konversation ab.- Wortzählung – Iteriert durch Nachrichten, teilt den Inhalt an Leerzeichen auf und zählt nach Rolle.
ais.storage.set('last-stats', stats)– Persistiert die Ergebnisse für das nächste Mal.ais.ui.toast()– Zeigt eine Benachrichtigung an, wenn die Analyse abgeschlossen ist.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:
- Öffnen Sie aiscouncil.net und melden Sie sich an
- Klicken Sie auf das Apps-Symbol in der linken Seitenleiste
- Im Sideload-Bereich fügen Sie ein:
http://localhost:8080/manifest.json - Klicken Sie auf Installieren
- Überprüfen Sie die Berechtigungen (storage, chat:read, ui:toast) und klicken Sie auf Erlauben
- Klicken Sie auf Öffnen auf der installierten App-Karte
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
| Problem | Lö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 Seite | Prü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-Änderungen | Deinstallieren 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
- Gehen Sie in der Plattform zu Apps und klicken Sie auf App hochladen
- Wählen Sie
word-counter.ais - Überprüfen Sie die Berechtigungen und bestätigen Sie
- Die App wird aus dem Bundle mit allen inlineierten Assets installiert
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.jsonhttps://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 (
#1a1a2eoder ä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-fitzur 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 begrenzen –
ais.chat.getHistory(500)ist normalerweise genug. Vermeiden Sie unbegrenzte Verlaufs-Anfragen. - Kein Polling – Verwenden Sie
ais.chat.onMessage()für Echtzeit-Updates statt wiederholtergetHistory-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 speichern –
ais.storageist nicht verschlüsselt. Speichern Sie niemals Passwörter, Tokens oder API-Schlüssel (außer Ihre App hatsecrets:syncund behandelt explizit Credential-Übertragung).
Kompatibilität
ais.platform.abiprü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.