diff --git a/Dockerfile b/Dockerfile
index 7a8edee..318a190 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,6 +31,20 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/loki ./cmd/loki
# limité à cette étape de build.
RUN GOBIN=/out GOTOOLCHAIN=auto go install golang.org/x/tools/gopls@latest
+# ── Étape 1 bis : whisper-cli (dictée vocale) ───────────────────────────
+# whisper.cpp compilé CPU seul, en statique : un binaire unique, aucune .so à
+# trimballer, et pas de compilation CUDA (des minutes de build pour un gain
+# nul sur des dictées de quelques secondes). Ubuntu 22.04 : glibc plus
+# ancienne que l'image runtime, donc compatible quoi qu'elle embarque.
+FROM ubuntu:22.04 AS whisperbuild
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ build-essential cmake git ca-certificates \
+ && rm -rf /var/lib/apt/lists/*
+RUN git clone --depth 1 https://github.com/ggml-org/whisper.cpp /w \
+ && cmake -S /w -B /w/build -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF -DWHISPER_BUILD_TESTS=OFF \
+ && cmake --build /w/build -j --target whisper-cli
+
# ── Étape 2 : runtime sur l'image serveur CUDA officielle ───────────────
FROM ${LLAMACPP_IMAGE} AS runtime
@@ -84,6 +98,9 @@ RUN if [ "$PLAYWRIGHT" = "1" ]; then \
COPY --from=gobuild /out/loki /usr/local/bin/loki
COPY --from=gobuild /out/gopls /usr/local/bin/gopls
+# Dictée vocale (POST /api/transcribe). Le modèle (~190 Mo) n'est PAS dans
+# l'image : téléchargé au premier usage dans /data/whisper/.
+COPY --from=whisperbuild /w/build/bin/whisper-cli /usr/local/bin/whisper-cli
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && mkdir -p /data /models
@@ -92,6 +109,7 @@ ENV LOKI_CONTAINER=1 \
LOKI_HOME=/data \
LOKI_MODEL_DIRS=/models \
LOKI_ENGINE_BIN=/app/llama-server \
+ LOKI_WHISPER_BIN=/usr/local/bin/whisper-cli \
LD_LIBRARY_PATH=/app \
NVIDIA_VISIBLE_DEVICES=all \
NVIDIA_DRIVER_CAPABILITIES=compute,utility
diff --git a/README.md b/README.md
index 91b4952..e2c6d07 100644
--- a/README.md
+++ b/README.md
@@ -327,6 +327,12 @@ Ajoutées par ce fork :
- **Nom du modèle sur chaque réponse** : une pastille à côté de « Loki » dit
quel modèle a produit la réponse. Elle est journalisée avec le tour : elle
survit au rechargement, et un vieux tour garde le modèle de l'époque.
+- **Dictée vocale** : un bouton micro dans la carte de saisie enregistre,
+ transcrit **en local** (whisper.cpp, compilé dans l'image ; modèle
+ `small-q5_1` multilingue ~190 Mo téléchargé au premier usage dans
+ `/data/whisper/`) et pose le texte dans le champ. ⚠️ le navigateur n'autorise
+ le micro qu'en **HTTPS** (ou sur `localhost`) — derrière un reverse proxy
+ TLS, rien à faire ; en `http://IP:8090`, le bouton l'explique.
- **Cartes raisonnement/outils à hauteur bornée** : un long raisonnement ne
fait plus grandir la page de plusieurs écrans — la carte reste à taille fixe
et défile toute seule pendant la génération. Sur un raisonnement géant, seul
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 9aa152c..1dbe935 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -32,6 +32,13 @@ entier à chaque token, en O(n²)). La carte reste maintenant à taille fixe et
suit la génération en défilant toute seule ; en direct, seul le bas d'un bloc
géant est re-rendu, et le texte complet est posé en fin de bloc.
+## Dictée vocale
+
+Un bouton micro dans la carte de saisie : tu parles, le texte s'écrit. La
+transcription est 100 % locale — whisper.cpp compilé dans l'image, modèle
+multilingue téléchargé au premier usage (~190 Mo, dans `/data/whisper/`).
+Le navigateur exige HTTPS (ou localhost) pour donner accès au micro.
+
## Divers
- Jeton Hugging Face réglable dans l'interface (dépôts verrouillés : le
diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html
index eb577bb..6491eb5 100644
--- a/internal/loki/ui/index.html
+++ b/internal/loki/ui/index.html
@@ -2059,6 +2059,12 @@ html[data-files="1"] #files-btn{color:var(--accent)}
.rolebadge{display:inline-block;margin-left:6px;padding:0 6px;border:1px solid var(--accent);border-radius:3px;color:var(--accent);font-size:9px;letter-spacing:.05em;text-transform:uppercase;vertical-align:middle}
+/* Dictée : anneau rouge pulsé pendant l'enregistrement, estompé pendant la
+ transcription. Le bouton garde le gabarit de ses voisins (joindre, fichiers). */
+#mic.rec{color:var(--err);border-color:var(--err);animation:micpulse 1.2s ease-in-out infinite}
+@keyframes micpulse{50%{box-shadow:0 0 0 5px color-mix(in srgb,var(--err) 25%,transparent)}}
+#mic.busy{opacity:.55}
+
/* ── Réglages en modale ─────────────────────────────────────────────────────
Deux volets : nav des sections à gauche, LE panneau choisi à droite. Les
panneaux réutilisent tels quels les gabarits de la barre latérale (.slist,
@@ -2284,6 +2290,12 @@ html[data-files="1"] #files-btn{color:var(--accent)}
+
+