Aujourd'hui, un board Kanbante ouvert dans le navigateur est figé tant qu'on ne recharge pas la page : si une carte bouge ailleurs (autre navigateur, Gitea, un outil, l'agent), le board ouvert ne le montre pas. On veut l'inverse : voir les cartes se déplacer toutes seules, en direct, sans refresh.
État actuel (diagnostic, vérifié dans le code 0.8.0)
Un mécanisme « live » existe depuis la 0.4.0, mais il a trois limites :
Diffusion partielle : le flux SSE (GET /api/stream) n'émet que lorsqu'un webhook Gitea arrive (hub.broadcast n'est appelé que dans le gestionnaire de webhooks). Les déplacements faits dans Kanbante même (autre session, capture, API) ne sont jamais diffusés.
Webhooks à configurer : sans webhook déclaré dans les dépôts/organisation Gitea, aucun événement n'arrive — le board ne bouge jamais tout seul. Aucun mécanisme de repli (polling) n'existe.
Rechargement, pas déplacement : quand un événement arrive, live.js fait un location.reload() (sous conditions : onglet visible, pas de panneau ouvert, pas de saisie, pas de drag — sinon pastille cliquable). On perd le défilement, les filtres et le contexte ; la carte ne « glisse » pas, la page entière se recharge.
De plus, le webhook ne traite que closed / reopened : un changement d'étiquette kanban:* dans Gitea ou une nouvelle issue ne déclenchent rien côté board ouvert (l'adoption d'étiquettes de la 0.6.0 n'a lieu qu'au chargement de page suivant).
Comportement visé
Un board ouvert reflète l'état réel en continu : toute carte créée, déplacée, fermée ou réétiquetée — depuis n'importe quelle source — glisse visiblement à sa nouvelle place en moins de ~2 s (avec webhooks) ou au cycle de repli (sans webhooks).
La mise à jour préserve le contexte de l'utilisateur : filtres actifs, défilement, panneau détail ouvert, drag en cours.
Aucune action manuelle, aucun refresh.
Conception proposée
1. Diffusion complète côté serveur
Publier sur le hub SSE à chaque mutation connue, pas seulement les webhooks : POST /api/move, création d'issue depuis le board, capture éclair (#12), transitions webhook (déjà fait), adoption d'étiquette et fermetures détectées lors des chargements/réconciliations. Événement typé {repo, type: "board-changed"}.
2. État du board en JSON
Nouvel endpoint léger GET /api/board/{owner}/{repo} → colonnes et cartes ordonnées (numéro, titre, étiquettes, âge), même logique de fusion/réconciliation que la page. C'est la source du re-rendu partiel.
3. Application en place côté client (le cœur)
À la réception d'un événement (ou au cycle de repli), le board récupère l'état JSON et applique le diff au DOM au lieu de recharger :
carte existante déplacée → le nœud est déplacé dans la nouvelle colonne, avec animation FLIP (transform), compteurs et indices « Glissez une carte ici » recalculés ;
nouvelle carte → insertion à la bonne position ; carte disparue/fermée → retrait ou glissement vers Terminé ;
filtres réappliqués après le diff ; panneau détail ouvert jamais fermé (sa pastille de colonne suit) ;
si un drag local est en cours, l'application du diff est mise en attente jusqu'à la fin du drag ;
prefers-reduced-motion → pas d'animation, application instantanée.
4. Déclencheurs externes élargis (webhook)
Traiter aussi les actions labeled / unlabeled, created et edited des événements d'issues : invalidation du cache + diffusion (l'adoption d'étiquettes et la réconciliation font le reste au refetch). Idempotence conservée (les échos des propres appels de Kanbante restent des no-ops côté journal).
5. Repli sans webhooks
Si aucun événement SSE n'est reçu et que l'onglet est visible, un polling discret toutes les 45 s récupère l'état et applique le même diff. Le « dynamique » fonctionne donc même sur une installation où les webhooks Gitea n'ont pas été configurés.
6. Périmètre pages
Board d'abord. Vue Focus : même refetch partiel. Wallboard : le rechargement complet actuel est acceptable (aucune interaction à protéger) — le conserver, mais il bénéficie de la diffusion complète (point 1) et du repli (point 5).
Critères d'acceptation
Deux navigateurs sur le même board : un drag dans l'un déplace la carte dans l'autre en < 2 s, sans refresh, avec animation.
Issue fermée dans Gitea (webhook configuré) : la carte glisse vers « Terminé » dans le board ouvert.
Étiquette kanban:* changée dans Gitea : la carte change de colonne dans le board ouvert, sans refresh.
Sans aucun webhook : un déplacement externe apparaît au plus tard après un cycle de repli (45 s), onglet visible.
Filtres actifs + panneau détail ouvert + scroll : rien n'est perdu lors d'une mise à jour distante ; un drag local en cours n'est jamais interrompu.
Aucun rechargement complet de page dans le flux nominal (vérifiable : l'état JS de la page survit aux mises à jour).
Tests : endpoint /api/board/..., diffusion sur move/capture, parse des nouveaux événements webhook ; suite complète verte.
Hors périmètre
Présence/curseurs collaboratifs et édition simultanée fine.
Notifications push du navigateur (le sujet est la fraîcheur du board, pas l'alerte).
Notes d'implémentation
Réutiliser le hub SSE existant et EventSource (reconnexion automatique du navigateur).
Le diff DOM doit manipuler les mêmes nœuds que SortableJS (ne pas recréer les cartes : conserver les écouteurs et l'état du drag).
live.js actuel peut être remplacé par board-live.js (application du diff) ; la pastille « Nouvelle activité » devient le repli ultime si le diff échoue (filet : rechargement manuel proposé, jamais forcé en cours d'interaction).
Documentation rédigée le 2026-10-11 (Spark) à la demande de Bruno — issue créée par Bruno, corps initialement vide.
## Contexte
Aujourd'hui, un board Kanbante ouvert dans le navigateur est **figé** tant qu'on ne recharge pas la page : si une carte bouge ailleurs (autre navigateur, Gitea, un outil, l'agent), le board ouvert ne le montre pas. On veut l'inverse : **voir les cartes se déplacer toutes seules, en direct, sans refresh.**
## État actuel (diagnostic, vérifié dans le code 0.8.0)
Un mécanisme « live » existe depuis la 0.4.0, mais il a trois limites :
1. **Diffusion partielle** : le flux SSE (`GET /api/stream`) n'émet que lorsqu'un **webhook Gitea** arrive (`hub.broadcast` n'est appelé que dans le gestionnaire de webhooks). Les déplacements faits dans Kanbante même (autre session, capture, API) ne sont jamais diffusés.
2. **Webhooks à configurer** : sans webhook déclaré dans les dépôts/organisation Gitea, aucun événement n'arrive — le board ne bouge jamais tout seul. Aucun mécanisme de repli (polling) n'existe.
3. **Rechargement, pas déplacement** : quand un événement arrive, `live.js` fait un `location.reload()` (sous conditions : onglet visible, pas de panneau ouvert, pas de saisie, pas de drag — sinon pastille cliquable). On perd le défilement, les filtres et le contexte ; la carte ne « glisse » pas, la page entière se recharge.
De plus, le webhook ne traite que `closed` / `reopened` : un changement d'étiquette `kanban:*` dans Gitea ou une nouvelle issue ne déclenchent rien côté board ouvert (l'adoption d'étiquettes de la 0.6.0 n'a lieu qu'au chargement de page suivant).
## Comportement visé
- Un board ouvert reflète l'état réel **en continu** : toute carte créée, déplacée, fermée ou réétiquetée — depuis n'importe quelle source — **glisse visiblement** à sa nouvelle place en moins de ~2 s (avec webhooks) ou au cycle de repli (sans webhooks).
- La mise à jour préserve le contexte de l'utilisateur : filtres actifs, défilement, panneau détail ouvert, drag en cours.
- Aucune action manuelle, aucun refresh.
## Conception proposée
### 1. Diffusion complète côté serveur
Publier sur le hub SSE à **chaque mutation connue**, pas seulement les webhooks : `POST /api/move`, création d'issue depuis le board, capture éclair (#12), transitions webhook (déjà fait), adoption d'étiquette et fermetures détectées lors des chargements/réconciliations. Événement typé `{repo, type: "board-changed"}`.
### 2. État du board en JSON
Nouvel endpoint léger `GET /api/board/{owner}/{repo}` → colonnes et cartes ordonnées (numéro, titre, étiquettes, âge), même logique de fusion/réconciliation que la page. C'est la source du re-rendu partiel.
### 3. Application en place côté client (le cœur)
À la réception d'un événement (ou au cycle de repli), le board récupère l'état JSON et applique le **diff au DOM** au lieu de recharger :
- carte existante déplacée → le nœud est déplacé dans la nouvelle colonne, avec **animation FLIP** (transform), compteurs et indices « Glissez une carte ici » recalculés ;
- nouvelle carte → insertion à la bonne position ; carte disparue/fermée → retrait ou glissement vers Terminé ;
- filtres réappliqués après le diff ; panneau détail ouvert jamais fermé (sa pastille de colonne suit) ;
- si un drag local est en cours, l'application du diff est **mise en attente** jusqu'à la fin du drag ;
- `prefers-reduced-motion` → pas d'animation, application instantanée.
### 4. Déclencheurs externes élargis (webhook)
Traiter aussi les actions `labeled` / `unlabeled`, `created` et `edited` des événements d'issues : invalidation du cache + diffusion (l'adoption d'étiquettes et la réconciliation font le reste au refetch). Idempotence conservée (les échos des propres appels de Kanbante restent des no-ops côté journal).
### 5. Repli sans webhooks
Si aucun événement SSE n'est reçu et que l'onglet est visible, un **polling discret toutes les 45 s** récupère l'état et applique le même diff. Le « dynamique » fonctionne donc même sur une installation où les webhooks Gitea n'ont pas été configurés.
### 6. Périmètre pages
Board d'abord. Vue **Focus** : même refetch partiel. **Wallboard** : le rechargement complet actuel est acceptable (aucune interaction à protéger) — le conserver, mais il bénéficie de la diffusion complète (point 1) et du repli (point 5).
## Critères d'acceptation
- [ ] Deux navigateurs sur le même board : un drag dans l'un déplace la carte dans l'autre en < 2 s, sans refresh, avec animation.
- [ ] Issue fermée dans Gitea (webhook configuré) : la carte glisse vers « Terminé » dans le board ouvert.
- [ ] Étiquette `kanban:*` changée dans Gitea : la carte change de colonne dans le board ouvert, sans refresh.
- [ ] Sans aucun webhook : un déplacement externe apparaît au plus tard après un cycle de repli (45 s), onglet visible.
- [ ] Filtres actifs + panneau détail ouvert + scroll : rien n'est perdu lors d'une mise à jour distante ; un drag local en cours n'est jamais interrompu.
- [ ] Aucun rechargement complet de page dans le flux nominal (vérifiable : l'état JS de la page survit aux mises à jour).
- [ ] Tests : endpoint `/api/board/...`, diffusion sur move/capture, parse des nouveaux événements webhook ; suite complète verte.
## Hors périmètre
- Présence/curseurs collaboratifs et édition simultanée fine.
- WebSockets : SSE suffit (flux serveur → client uniquement).
- Notifications push du navigateur (le sujet est la fraîcheur du board, pas l'alerte).
## Notes d'implémentation
- Réutiliser le hub SSE existant et `EventSource` (reconnexion automatique du navigateur).
- Le diff DOM doit manipuler les mêmes nœuds que SortableJS (ne pas recréer les cartes : conserver les écouteurs et l'état du drag).
- `live.js` actuel peut être remplacé par `board-live.js` (application du diff) ; la pastille « Nouvelle activité » devient le repli ultime si le diff échoue (filet : rechargement manuel proposé, jamais forcé en cours d'interaction).
---
*Documentation rédigée le 2026-10-11 (Spark) à la demande de Bruno — issue créée par Bruno, corps initialement vide.*
bruno
added this to the Différenciation — feuille de route milestone 2026-10-11 12:52:29 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Contexte
Aujourd'hui, un board Kanbante ouvert dans le navigateur est figé tant qu'on ne recharge pas la page : si une carte bouge ailleurs (autre navigateur, Gitea, un outil, l'agent), le board ouvert ne le montre pas. On veut l'inverse : voir les cartes se déplacer toutes seules, en direct, sans refresh.
État actuel (diagnostic, vérifié dans le code 0.8.0)
Un mécanisme « live » existe depuis la 0.4.0, mais il a trois limites :
GET /api/stream) n'émet que lorsqu'un webhook Gitea arrive (hub.broadcastn'est appelé que dans le gestionnaire de webhooks). Les déplacements faits dans Kanbante même (autre session, capture, API) ne sont jamais diffusés.live.jsfait unlocation.reload()(sous conditions : onglet visible, pas de panneau ouvert, pas de saisie, pas de drag — sinon pastille cliquable). On perd le défilement, les filtres et le contexte ; la carte ne « glisse » pas, la page entière se recharge.De plus, le webhook ne traite que
closed/reopened: un changement d'étiquettekanban:*dans Gitea ou une nouvelle issue ne déclenchent rien côté board ouvert (l'adoption d'étiquettes de la 0.6.0 n'a lieu qu'au chargement de page suivant).Comportement visé
Conception proposée
1. Diffusion complète côté serveur
Publier sur le hub SSE à chaque mutation connue, pas seulement les webhooks :
POST /api/move, création d'issue depuis le board, capture éclair (#12), transitions webhook (déjà fait), adoption d'étiquette et fermetures détectées lors des chargements/réconciliations. Événement typé{repo, type: "board-changed"}.2. État du board en JSON
Nouvel endpoint léger
GET /api/board/{owner}/{repo}→ colonnes et cartes ordonnées (numéro, titre, étiquettes, âge), même logique de fusion/réconciliation que la page. C'est la source du re-rendu partiel.3. Application en place côté client (le cœur)
À la réception d'un événement (ou au cycle de repli), le board récupère l'état JSON et applique le diff au DOM au lieu de recharger :
prefers-reduced-motion→ pas d'animation, application instantanée.4. Déclencheurs externes élargis (webhook)
Traiter aussi les actions
labeled/unlabeled,createdetediteddes événements d'issues : invalidation du cache + diffusion (l'adoption d'étiquettes et la réconciliation font le reste au refetch). Idempotence conservée (les échos des propres appels de Kanbante restent des no-ops côté journal).5. Repli sans webhooks
Si aucun événement SSE n'est reçu et que l'onglet est visible, un polling discret toutes les 45 s récupère l'état et applique le même diff. Le « dynamique » fonctionne donc même sur une installation où les webhooks Gitea n'ont pas été configurés.
6. Périmètre pages
Board d'abord. Vue Focus : même refetch partiel. Wallboard : le rechargement complet actuel est acceptable (aucune interaction à protéger) — le conserver, mais il bénéficie de la diffusion complète (point 1) et du repli (point 5).
Critères d'acceptation
kanban:*changée dans Gitea : la carte change de colonne dans le board ouvert, sans refresh./api/board/..., diffusion sur move/capture, parse des nouveaux événements webhook ; suite complète verte.Hors périmètre
Notes d'implémentation
EventSource(reconnexion automatique du navigateur).live.jsactuel peut être remplacé parboard-live.js(application du diff) ; la pastille « Nouvelle activité » devient le repli ultime si le diff échoue (filet : rechargement manuel proposé, jamais forcé en cours d'interaction).Documentation rédigée le 2026-10-11 (Spark) à la demande de Bruno — issue créée par Bruno, corps initialement vide.