BRIC-18: Visual differentiation of thinking, content, and structured response#14
Conversation
8329f98 to
00edc20
Compare
…ed response - Add StreamEvent type with THINKING/CONTENT/MESSAGE - Update Message type with thinking field - Update chatApi to parse JSON SSE events - Update useChatStore with streamingThinking and structuredResponse - Add ThinkingBlock component (collapsible, muted, monospace) - Add StructuredResponseCard component (JSON card, copy button) - Update MessageList and ChatMessage to render all three sections - Update useStreamChat to route typed events
00edc20 to
99d9ff2
Compare
Kaiohz
left a comment
There was a problem hiding this comment.
Code Review: BRIC-18
📊 Score: 8/10
✅ Points positifs
1. Architecture propre
- Nouvelle entité domaine
StreamEventavec enum et interface bien typés - Séparation claire des couches: domain → infrastructure → application → components
- Respect de l'architecture hexagonale existante
2. Gestion d'état propre avec Zustand
appendStreamEventroute intelligemment les events vers les bons états- Reset complet dans
clearStream()pour tous les nouveaux états - Pattern cohérent avec l'existant
3. Composants bien conçus
ThinkingBlock: collapsible, accessible (aria-expanded), UX cleanStructuredResponseCard: bouton copy avec feedback visuel, gestion des erreurs JSON- Séparation des responsabilités, composants focalisés
4. Rétrocompatibilité
- Fallback dans
chatApi.tspour traiter les données brutes comme content - Migration progressive du streaming
5. UX soignée
- Thinking en monospace, fond muted (ne parasite pas le contenu)
- Structured response avec icône data_object, clairement différencié
- Auto-scroll mis à jour avec les nouvelles dépendances
🔧 Suggestions d'amélioration
1. Méthode non utilisée
// useChatStore.ts - ligne 24
appendStreamThinking: (chunk) => set(...),Cette méthode n'est jamais appelée - le store utilise appendStreamEvent qui gère le routing en interne. Soit la supprimer, soit l'utiliser dans le hook.
2. Erreur silencieuse dans StructuredResponseCard
// StructuredResponseCard.tsx - lignes 28-30
} catch {
/* ignore */
}Même si c'est un cas rare, logguer l'erreur en console.warn aide au debugging.
3. Default case dans appendStreamEvent
default:
return state;Ajouter un console.warn('Unknown event type:', ev.type) pour détecter les nouveaux types d'events non gérés.
4. Extraction possible
Le parsing JSON dans appendStreamEvent (case "message") pourrait être extrait dans une fonction utilitaire parseStructuredResponse() pour:
- Meilleure testabilité
- Séparation des préoccupations
📋 Checklist avant merge
- Types bien définis
- Composants accessibles
- State management cohérent
- Backward compatibility
- Tests unitaires pour
appendStreamEvent? - Nettoyer
appendStreamThinkingnon utilisée
🎯 Verdict
Fondations solides, architecture respectée. Quelques nettoyages mineurs (méthode inutilisée, error handling) mais rien de bloquant pour un draft. La feature apporte une vraie valeur UX pour différencier thinking/content/structured response.
Prêt pour review finale après adressage des suggestions mineures.
Kaiohz
left a comment
There was a problem hiding this comment.
📋 Code Review - BRIC-18
✅ Points Positifs
Architecture & Structure
- Séparation propre des responsabilités: nouveaux composants
ThinkingBlocketStructuredResponseCardbien isolés - Types TypeScript dans
domain/entities/chat/streamEvent.ts- clean et typé - Intégration propre dans le store Zustand avec
appendStreamEventqui dispatch selon le type - Backward compatibility dans
chatApi.tsavec fallback pour les anciens clients SSE
UX/UI
ThinkingBlockcollapsible avec icônelightbulb- bonne UX pour le contenu secondaireStructuredResponseCardavec bouton copy et état "Copied" - bon UX pattern- Styles cohérents avec le design system Material Design 3
Code Quality
- Imports propres, composants bien typés
- Gestion des états de stream avec les 3 types d'événements (thinking, content, message)
- Parsing JSON robuste avec try/catch
⚠️ Points à Améliorer
1. Typage du StructuredResponseCard
// Actuel
interface StructuredResponseCardProps {
data: unknown;
}
// Suggestion: définir un type plus spécifique
interface StructuredResponseCardProps {
data: Record<string, unknown> | null;
}2. Accessibilité du ThinkingBlock
Le bouton a aria-expanded mais manque aria-controls pour lier au contenu collapsible:
<button
type="button"
onClick={() => setIsExpanded((prev) => !prev)}
aria-expanded={isExpanded}
aria-controls="thinking-content" // Ajouter
// ...
>
// ...
<div id="thinking-content" ...> // Ajouter l'id3. Dépendances non utilisées?
package-lock.json ajoute @radix-ui/react-accordion, @radix-ui/react-switch, et js-yaml mais je ne vois pas leur utilisation directe dans les composants modifiés. À vérifier s'ils sont nécessaires ou s'ils proviennent d'un autre fichier non inclus dans cette PR.
4. Gestion d'erreur silencieuse dans appendStreamEvent
// Actuel
case "message":
try {
const msg = JSON.parse(ev.data);
return { structuredResponse: msg.structured_response ?? null };
} catch {
return state; // Silencieux - pourrait logger pour debug
}Suggestion: ajouter un console.warn en dev pour les parsing errors.
5. État initial du ThinkingBlock
Le bloc est fermé par défaut (isExpanded = false). Si le thinking est important pour l'UX, envisager de l'afficher ouvert pendant le streaming et fermé une fois terminé.
🧪 Tests
- Test pour
ThinkingBlock: vérifier le toggle expand/collapse - Test pour
StructuredResponseCard: vérifier le bouton copy et l'affichage JSON - Test pour
appendStreamEvent: vérifier le dispatch des différents types d'événements
📊 Score: 7/10
| Critère | Note | Commentaire |
|---|---|---|
| Architecture | 8/10 | Propre, bien séparée |
| Typage | 7/10 | unknown pourrait être plus spécifique |
| Accessibilité | 6/10 | Manque aria-controls |
| Tests | 5/10 | Pas de tests unitaires |
| Code Quality | 8/10 | Clean, bien structuré |
🔄 Recommandations
- Merge OK avec les points ci-dessus comme tech debt à adresser dans un follow-up
- Créer des tickets pour les améliorations d'accessibilité et de tests
- Vérifier les dépendances inutilisées dans
package.json
Bonne implémentation globale ! 🚀
…, scroll jank, a11y, timer cleanup
…ate getState calls
🤖 Review automatique de PR📊 Score de qualité: 7.5/10✅ Points positifs
|
Code Review: BRIC-18📊 Score: 8/10Une PR solide avec une architecture propre et une bonne séparation des préoccupations. Quelques points à améliorer mais rien de bloquant pour le merge. ✅ Points positifs
|
Handle "structured" stream events by parsing JSON data into state, with fallback logging on parse failure.
Kaiohz
left a comment
There was a problem hiding this comment.
📋 Review Code Review
✅ Points positifs
- Architecture hexagonale respectée : Nouvelle entité
StreamEventdansdomain/entities, mise à jour du portchatPort, et implémentation danschatApi - Accessibilité : Utilisation correcte de
aria-expanded,aria-controls,aria-livesur les nouveaux composants - Backward compatibility : Fallback intelligent qui traite les données non-JSON comme
CONTENT - Tests unitaires : Couverture des nouveaux cas
appendStreamEventdansuseChatStore.test.ts - Séparation des responsabilités : Composants
ThinkingBlocketStructuredResponseCardbien isolés
⚠️ Points à améliorer
1. Timer leak potentiel dans StructuredResponseCard
// L148: Le timer n'est pas nettoyé si le composant unmount pendant le setTimeout
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);Le cleanup nettoie le ref mais le timer peut encore être actif. Correction suggérée :
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
};
}, []);
const handleCopy = async () => {
// ...
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
timerRef.current = null;
setCopied(false);
}, 1500);
};2. Incohérence styling dans ThinkingBlock
// L30: Utilisation de inline style au lieu de Tailwind
style={{ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }Suggestion :
className={cn(
"material-symbols-outlined text-base text-secondary-brand transition-transform",
isExpanded && 'rotate-180'
)}📊 Score : 8/10
Code propre, bien structuré, qui suit les patterns existants du projet. Les points d'amélioration sont mineurs (cleanup timer, cohérence styling).
✅ Prêt à merger après correction mineure du timer leak.
🤖 Review par SoluBot
Jira
BRIC-18
Changes
StreamEventand routes thinking/content/message to separate statesThinkingBlock(collapsible, muted, monospace) andStructuredResponseCard(JSON card with copy button)MessageListandChatMessageto show thinking, content, and structured response in distinct visual sectionsuseChatStorenow tracksstreamingThinking,streamingContent, andstructuredResponseTests