feat(web): UI responsive mobile (header, barre chat, review) (v0.35.0)
Header flex-wrap + détails masqués sous sm ; barre chat input min-w-0 + boutons compacts + label Envoyer masqué sous sm ; actions Review empilées pleine largeur sur mobile. Build web vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
This commit is contained in:
@@ -6,6 +6,15 @@ incompatibles. Chaque ligne renvoie à un commit dédié (un artefact = un commi
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.35.0] — 2026-06-23 — UI responsive (mobile)
|
||||||
|
### Fixed
|
||||||
|
- **Header** : `flex-wrap` + paddings/marges réduits sur petit écran, « · N outils »
|
||||||
|
masqué sous `sm` (ne déborde plus).
|
||||||
|
- **Barre chat** : `input` en `min-w-0`, boutons `shrink-0` compactés
|
||||||
|
(`px-2.5 sm:px-3`), label « Envoyer » masqué sous `sm` → tient sur mobile.
|
||||||
|
- **Review** : actions Approuver/Refuser pleine largeur empilées sous `sm`.
|
||||||
|
- Web : typecheck + build OK.
|
||||||
|
|
||||||
## [0.34.2] — 2026-06-23 — bump Ollama 0.30.10 (modèles :cloud)
|
## [0.34.2] — 2026-06-23 — bump Ollama 0.30.10 (modèles :cloud)
|
||||||
### Fixed
|
### Fixed
|
||||||
- `ollama/ollama:0.6.8` → **`0.30.10`** : 0.6.8 refuse les modèles `:cloud`
|
- `ollama/ollama:0.6.8` → **`0.30.10`** : 0.6.8 refuse les modèles `:cloud`
|
||||||
|
|||||||
+6
-5
@@ -14,8 +14,8 @@ function Shell() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-dvh flex flex-col">
|
<div className="min-h-dvh flex flex-col">
|
||||||
<header className="flex items-center gap-2 border-b border-border bg-surface px-4 py-2">
|
<header className="flex flex-wrap items-center gap-x-2 gap-y-1 border-b border-border bg-surface px-3 sm:px-4 py-2">
|
||||||
<span className="font-bold tracking-wide text-accent glow mr-2">CHLOVA</span>
|
<span className="font-bold tracking-wide text-accent glow mr-1 sm:mr-2">CHLOVA</span>
|
||||||
<nav className="flex gap-1">
|
<nav className="flex gap-1">
|
||||||
<NavLink to="/chat" className={link}>
|
<NavLink to="/chat" className={link}>
|
||||||
<MessageSquare size={16} /> Chat
|
<MessageSquare size={16} /> Chat
|
||||||
@@ -27,10 +27,11 @@ function Shell() {
|
|||||||
)}
|
)}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
<span className="ml-auto flex items-center gap-1.5 text-xs text-muted" title="Phase · outils">
|
<span className="ml-auto flex items-center gap-1.5 text-xs text-muted whitespace-nowrap" title={`Phase ${phase} · ${tools} outils`}>
|
||||||
<Cpu size={14} /> {phase || "…"} · {tools} outils
|
<Cpu size={14} /> {phase || "…"}
|
||||||
|
<span className="hidden sm:inline">· {tools} outils</span>
|
||||||
</span>
|
</span>
|
||||||
<button onClick={logout} className="ml-3 text-muted hover:text-fg cursor-pointer" aria-label="Déconnexion" title="Déconnexion">
|
<button onClick={logout} className="ml-1 sm:ml-3 shrink-0 text-muted hover:text-fg cursor-pointer" aria-label="Déconnexion" title="Déconnexion">
|
||||||
<LogOut size={18} />
|
<LogOut size={18} />
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -100,20 +100,20 @@ export function Chat() {
|
|||||||
<div ref={bottom} />
|
<div ref={bottom} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={submit} className="flex items-center gap-2 border-t border-border bg-surface p-3">
|
<form onSubmit={submit} className="flex items-center gap-1.5 border-t border-border bg-surface p-2 sm:p-3">
|
||||||
{speech.ttsSupported && (
|
{speech.ttsSupported && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleSpeak}
|
onClick={toggleSpeak}
|
||||||
aria-label={speakReplies ? "Couper la voix" : "Activer la voix"}
|
aria-label={speakReplies ? "Couper la voix" : "Activer la voix"}
|
||||||
title={speakReplies ? "Voix activée" : "Voix coupée"}
|
title={speakReplies ? "Voix activée" : "Voix coupée"}
|
||||||
className={`rounded-md border px-3 py-2 cursor-pointer ring-accent ${speakReplies ? "border-accent text-accent" : "border-border text-muted"}`}
|
className={`shrink-0 rounded-md border px-2.5 sm:px-3 py-2 cursor-pointer ring-accent ${speakReplies ? "border-accent text-accent" : "border-border text-muted"}`}
|
||||||
>
|
>
|
||||||
{speakReplies ? <Volume2 size={18} /> : <VolumeX size={18} />}
|
{speakReplies ? <Volume2 size={18} /> : <VolumeX size={18} />}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
className="flex-1 rounded-md bg-surface-2 border border-border px-3 py-2 text-fg placeholder:text-muted ring-accent"
|
className="flex-1 min-w-0 rounded-md bg-surface-2 border border-border px-3 py-2 text-fg placeholder:text-muted ring-accent"
|
||||||
placeholder={speech.listening ? "Écoute…" : "Message…"}
|
placeholder={speech.listening ? "Écoute…" : "Message…"}
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(e) => setInput(e.target.value)}
|
onChange={(e) => setInput(e.target.value)}
|
||||||
@@ -124,7 +124,7 @@ export function Chat() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={mic}
|
onClick={mic}
|
||||||
aria-label={speech.listening ? "Arrêter le micro" : "Parler"}
|
aria-label={speech.listening ? "Arrêter le micro" : "Parler"}
|
||||||
className={`rounded-md border px-3 py-2 cursor-pointer ring-accent ${speech.listening ? "border-accent text-accent animate-pulse" : "border-border text-muted"}`}
|
className={`shrink-0 rounded-md border px-2.5 sm:px-3 py-2 cursor-pointer ring-accent ${speech.listening ? "border-accent text-accent animate-pulse" : "border-border text-muted"}`}
|
||||||
>
|
>
|
||||||
{speech.listening ? <Square size={18} /> : <Mic size={18} />}
|
{speech.listening ? <Square size={18} /> : <Mic size={18} />}
|
||||||
</button>
|
</button>
|
||||||
@@ -135,7 +135,7 @@ export function Chat() {
|
|||||||
onClick={toggleHandsFree}
|
onClick={toggleHandsFree}
|
||||||
aria-label={speech.handsFree ? "Couper les mains libres" : "Activer les mains libres"}
|
aria-label={speech.handsFree ? "Couper les mains libres" : "Activer les mains libres"}
|
||||||
title="Mains libres (wake-word CHLOVA)"
|
title="Mains libres (wake-word CHLOVA)"
|
||||||
className={`rounded-md border px-3 py-2 cursor-pointer ring-accent ${speech.handsFree ? "border-accent text-accent" : "border-border text-muted"}`}
|
className={`shrink-0 rounded-md border px-2.5 sm:px-3 py-2 cursor-pointer ring-accent ${speech.handsFree ? "border-accent text-accent" : "border-border text-muted"}`}
|
||||||
>
|
>
|
||||||
<Radio size={18} />
|
<Radio size={18} />
|
||||||
</button>
|
</button>
|
||||||
@@ -144,9 +144,9 @@ export function Chat() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
disabled={busy || !input.trim()}
|
disabled={busy || !input.trim()}
|
||||||
aria-label="Envoyer"
|
aria-label="Envoyer"
|
||||||
className="flex items-center gap-1.5 rounded-md bg-accent px-4 py-2 font-medium text-bg disabled:opacity-50 ring-accent cursor-pointer"
|
className="flex shrink-0 items-center gap-1.5 rounded-md bg-accent px-3 sm:px-4 py-2 font-medium text-bg disabled:opacity-50 ring-accent cursor-pointer"
|
||||||
>
|
>
|
||||||
<Send size={16} /> Envoyer
|
<Send size={16} /> <span className="hidden sm:inline">Envoyer</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ export function Review() {
|
|||||||
expire {new Date(a.expiresAt).toISOString().slice(0, 10)}
|
expire {new Date(a.expiresAt).toISOString().slice(0, 10)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<div className="ml-auto flex gap-2">
|
<div className="flex gap-2 w-full sm:w-auto sm:ml-auto">
|
||||||
<button
|
<button
|
||||||
onClick={() => void approve(a.id)}
|
onClick={() => void approve(a.id)}
|
||||||
className="flex items-center gap-1 rounded-md bg-success/15 text-success border border-success/40 px-3 py-1 text-sm cursor-pointer ring-accent"
|
className="flex flex-1 sm:flex-none items-center justify-center gap-1 rounded-md bg-success/15 text-success border border-success/40 px-3 py-1 text-sm cursor-pointer ring-accent"
|
||||||
>
|
>
|
||||||
<Check size={15} /> Approuver
|
<Check size={15} /> Approuver
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => onRefuse(a.id)}
|
onClick={() => onRefuse(a.id)}
|
||||||
className="flex items-center gap-1 rounded-md bg-danger/15 text-danger border border-danger/50 px-3 py-1 text-sm cursor-pointer ring-accent"
|
className="flex flex-1 sm:flex-none items-center justify-center gap-1 rounded-md bg-danger/15 text-danger border border-danger/50 px-3 py-1 text-sm cursor-pointer ring-accent"
|
||||||
>
|
>
|
||||||
<X size={15} /> Refuser
|
<X size={15} /> Refuser
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user