Recherche partout (#68, chantier 2+3) : onglets, Tout voir sur place, focus, mise en page #46

Merged
bruno merged 4 commits from recherche-onglets-ux-68 into main 2026-10-28 14:25:54 -04:00
Owner

Recherche partout — chantier 2+3 : mise en page + UX + onglets (fusion #68, suite de validation)

Branche recherche-onglets-ux-68 → main. Ne pas merger sans le go du coordinateur ; aucune version bumpée, aucune issue ouverte/fermée par cette PR. Référence de validation : issue Projets/fusion-trois-apps #68 (son état n'est pas changé par cette PR).

Ce qui est corrigé, point par point

  1. Mise en page TikTak : cause trouvée — recherche.html étend base.html sans layout_class, donc .rp-page (enfant direct de .layout) tombait en auto-placement dans la 2e colonne de la grille (largeur sidebar), d'où l'étroite colonne à gauche. Correctif dans recherche.css seul, gardé par l'ancêtre .layout (TikTak seul) : .layout > .rp-page { grid-column: 2 / -1; max-width: none; } — la page occupe toute la largeur disponible juste à droite du rail d'icônes, sans toucher aux autres pages (elles ne portent pas .rp-page) ; en mobile (≤760 px) elle prend la colonne unique.
  2. Mise en page ObsiGate : voir le paragraphe propre à ObsiGate ci-dessous.
  3. Focus automatique : l'attribut autofocus seul ne survivait pas au chargement des surcouches (barre, palette, app). La page appelle désormais focus() explicitement en fin d'init, plus un rappel requestAnimationFrame/setTimeout et un rappel pageshow ; cliquer la barre « Rechercher partout » (data-act="palette") alors qu'on est déjà sur la page replace le curseur dans la boîte (et sélectionne le texte) au lieu de recharger ; / et Ctrl+K / ⌘K inchangés.
  4. « Tout voir » : ne quitte plus la page. Le clic bascule vers l'onglet de l'application (son href reste une URL partageable ?q=&tab= au cas où le JS ne tournerait pas) ; dans l'onglet propre à l'app, le lien est masqué (redondant).
  5. Onglets : Tout, ObsiGate, Flowdeck, TikTak, Kanbante, Console Spark — mêmes onglets, mêmes compteurs sur les 4 hôtes. « Tout » = les 4 sections groupées, chaque liste tronquée à 8 ; un onglet app = tous les résultats de cette app (requête ObsiGate en limit=100, les autres API renvoient leur jeu complet, aucune troncature côté client). L'onglet actif vit dans l'URL (?tab=, absent = Tout) : partageable, et le retour arrière navigateur le restaure (popstate). Clavier : ←/→, Début/Fin entre onglets ; ↑/↓ puis Entrée sur les résultats visibles conservés (les liens des sections masquées sont exclus de la navigation).
  6. Compteurs et états : chaque onglet affiche le TOTAL de son application (pas le nombre tronqué de « Tout »), « … » pendant le chargement, « 0 » pour un vide honnête, rien pour une section en erreur (pas de faux 0). « Tout » affiche « … » tant qu'une section charge, sinon la somme des totaux connus. Une section injoignable ne casse pas les autres (état par section, comme en #68) ; le correctif CORS lui-même est chez un autre travailleur et n'est pas touché ici (aucun fichier CORS/middleware, aucune ligne d'onglets de la barre modifiée).
  7. Console Spark : AUCUN endpoint de recherche console n'est mergé à ce jour (agent-commun : seule la PR #12 est ouverte, autre programme — ce dépôt n'est pas touché ici, et l'endpoint n'est pas réimplémenté ici). Le code tente l'appel conventionnel GET {base-console}/api/search?q= (même convention que les autres hôtes ; chemin À CONFIRMER avec le travailleur console dès que son endpoint atterrit) et, en cas d'échec, de HTTP non-OK ou de format non reconnu, affiche honnêtement « Console Spark : recherche indisponible pour l'instant » — jamais de faux résultats, jamais de faux 0. La base console vient de data-base-console rendu par l'hôte, sinon de window.DRACO_URLS.agent / window.DRACO_AGENT_URL, sinon de la dérivation -dev, sinon du href d'un onglet de barre console/spark, sinon du défaut prod agent.dracodev.net. L'onglet console n'est interrogé que lorsqu'il est affiché ; « Tout » ne le contient pas, pour ne pas polluer la vue groupée d'un message d'indisponibilité.

Fichiers

  • Page de recherche de l'hôte (gabarit + onglets + section console), recherche.js et recherche.css vendangés à l'identique sur les 4 hôtes, tests de logique pure étendus. Aucun autre fichier touché.

Tests (chiffres réels, pas de déployé, pas de capture)

  • Tests de logique pure : TikTak/Kanbante/Flowdeck node tests/recherche.test.js → 59 assertions OK chacun ; ObsiGate node tests/frontend/recherche.test.mjs → 42 assertions OK. Nouveautés couvertes : parseTab, visibleKeys (Tout = 4 sections, sans console), troncature 8 vs complet, compteurs (…/total/0/vide), buildSearchUrl, searchPath/normalize complets, normalizeConsole (URL absolue/relative conservée, sans URL = pas de faux lien, format inconnu = non reconnu), resolveConsoleBase (explicite > injecté > -dev > href barre > prod). Suites existantes sans régression (les assertions #68 d'origine passent toutes).
  • Harnais DOM local (hors PR, fait tourner le vrai recherche.js avec un fetch factice : ObsiGate 12 résultats, Flowdeck 3, TikTak vide, Kanbante en erreur, console en erreur) → 23 assertions OK : focus au chargement, « Tout » = 8 liens affichés pour ObsiGate avec compteur 12, compteurs 12/3/0/vide, « Tout » = 15, section Kanbante en erreur sans casser la page, « Tout voir » ObsiGate → onglet avec les 12 résultats + URL ?q=test&tab=obsigate + focus onglet, flèche droite → Flowdeck, console = message indisponible et compteur vide, popstate restaure requête + onglet, clic palette → focus + sélection, Entrée → premier résultat visible.
  • Écarts préexistants nommés : aucun écart nouveau constaté dans les fichiers touchés ; les validations déployées ci-dessous restent à faire par Bruno après merge/publication (aucun test déployé, aucune capture dans cette PR).

À valider en direct par Bruno après merge/publication

Mise en page TikTak (pleine largeur à droite du rail, mobile compris) ; mise en page ObsiGate (voir paragraphe ObsiGate) ; focus (clic barre, ⌘K, /, rechargement) ; « Tout voir » (reste sur la page, bascule d'onglet) ; onglets + URL (partage ?tab=, retour arrière) ; compteurs (totaux, …, 0, section en erreur) ; onglet Console Spark (indisponible honnête tant que l'endpoint console n'est pas mergé, puis résultats réels après confirmation du chemin avec l'autre travailleur).

Paragraphe propre à ObsiGate

Mise en page (point 2) : la page n'est plus nue. La route /recherche reste servie hors SPA (avant le catch-all, comme /admin.html — backend/main.py n'est PAS touché ici), mais recherche.html reprend désormais le gabarit de l'app : feuilles /static/style.css + /static/draco-barre.css, en-tête de fusion copié statiquement depuis index.html (snippet figé commons v0.1.6, onglets = liens réels via jetons {{URL_*}}, bouton central = focus dans la boîte), en-tête ObsiGate simplifié, sidebar statique (Tableau de bord, Rechercher partout) dans les classes de l'app (.app-container, .header, .main-body, .sidebar, .content-wrapper, .content-area), et navigation .rp-og-nav dans recherche.css. Base console : window.DRACO_URLS.agent, déjà injecté par la route (aucun jeton ajouté, aucun fichier serveur touché).
Deltas explicites à valider en direct : (a) la sidebar est statique — les vaults, tags et récents dynamiques restent dans la SPA (une intégration dans le routeur SPA aurait exigé de toucher index.html/app.js, hors périmètre de ce correctif) ; (b) les menus, le panneau Spark, les alertes et le domaine de la barre copiée ne sont pas rejoués sur cette page autonome — l'autre travailleur (onglet Console Spark de la barre) devra reporter son ajout dans cette copie au re-vendoring, comme pour tout hôte ; (c) style.css importe les polices Google Fonts, que la CSP stricte de /recherche (style-src 'self') bloque : la page retombe sur les polices système (repli typographique, pas de page cassée) — élargir la CSP serait une décision séparée, non prise ici. Fichiers : frontend/recherche.html, frontend/js/recherche.js, frontend/recherche.css, tests/frontend/recherche.test.mjs.

## Recherche partout — chantier 2+3 : mise en page + UX + onglets (fusion #68, suite de validation) Branche `recherche-onglets-ux-68` → `main`. Ne pas merger sans le go du coordinateur ; aucune version bumpée, aucune issue ouverte/fermée par cette PR. Référence de validation : issue Projets/fusion-trois-apps #68 (son état n'est pas changé par cette PR). ### Ce qui est corrigé, point par point 1. Mise en page TikTak : cause trouvée — `recherche.html` étend `base.html` sans `layout_class`, donc `.rp-page` (enfant direct de `.layout`) tombait en auto-placement dans la 2e colonne de la grille (largeur sidebar), d'où l'étroite colonne à gauche. Correctif dans `recherche.css` seul, gardé par l'ancêtre `.layout` (TikTak seul) : `.layout > .rp-page { grid-column: 2 / -1; max-width: none; }` — la page occupe toute la largeur disponible juste à droite du rail d'icônes, sans toucher aux autres pages (elles ne portent pas `.rp-page`) ; en mobile (≤760 px) elle prend la colonne unique. 2. Mise en page ObsiGate : voir le paragraphe propre à ObsiGate ci-dessous. 3. Focus automatique : l'attribut `autofocus` seul ne survivait pas au chargement des surcouches (barre, palette, app). La page appelle désormais `focus()` explicitement en fin d'init, plus un rappel `requestAnimationFrame`/`setTimeout` et un rappel `pageshow` ; cliquer la barre « Rechercher partout » (`data-act="palette"`) alors qu'on est déjà sur la page replace le curseur dans la boîte (et sélectionne le texte) au lieu de recharger ; `/` et Ctrl+K / ⌘K inchangés. 4. « Tout voir » : ne quitte plus la page. Le clic bascule vers l'onglet de l'application (son `href` reste une URL partageable `?q=&tab=` au cas où le JS ne tournerait pas) ; dans l'onglet propre à l'app, le lien est masqué (redondant). 5. Onglets : Tout, ObsiGate, Flowdeck, TikTak, Kanbante, Console Spark — mêmes onglets, mêmes compteurs sur les 4 hôtes. « Tout » = les 4 sections groupées, chaque liste tronquée à 8 ; un onglet app = tous les résultats de cette app (requête ObsiGate en `limit=100`, les autres API renvoient leur jeu complet, aucune troncature côté client). L'onglet actif vit dans l'URL (`?tab=`, absent = Tout) : partageable, et le retour arrière navigateur le restaure (`popstate`). Clavier : ←/→, Début/Fin entre onglets ; ↑/↓ puis Entrée sur les résultats visibles conservés (les liens des sections masquées sont exclus de la navigation). 6. Compteurs et états : chaque onglet affiche le TOTAL de son application (pas le nombre tronqué de « Tout »), « … » pendant le chargement, « 0 » pour un vide honnête, rien pour une section en erreur (pas de faux 0). « Tout » affiche « … » tant qu'une section charge, sinon la somme des totaux connus. Une section injoignable ne casse pas les autres (état par section, comme en #68) ; le correctif CORS lui-même est chez un autre travailleur et n'est pas touché ici (aucun fichier CORS/middleware, aucune ligne d'onglets de la barre modifiée). 7. Console Spark : AUCUN endpoint de recherche console n'est mergé à ce jour (agent-commun : seule la PR #12 est ouverte, autre programme — ce dépôt n'est pas touché ici, et l'endpoint n'est pas réimplémenté ici). Le code tente l'appel conventionnel `GET {base-console}/api/search?q=` (même convention que les autres hôtes ; chemin À CONFIRMER avec le travailleur console dès que son endpoint atterrit) et, en cas d'échec, de HTTP non-OK ou de format non reconnu, affiche honnêtement « Console Spark : recherche indisponible pour l'instant » — jamais de faux résultats, jamais de faux 0. La base console vient de `data-base-console` rendu par l'hôte, sinon de `window.DRACO_URLS.agent` / `window.DRACO_AGENT_URL`, sinon de la dérivation -dev, sinon du href d'un onglet de barre console/spark, sinon du défaut prod `agent.dracodev.net`. L'onglet console n'est interrogé que lorsqu'il est affiché ; « Tout » ne le contient pas, pour ne pas polluer la vue groupée d'un message d'indisponibilité. ### Fichiers - Page de recherche de l'hôte (gabarit + onglets + section console), `recherche.js` et `recherche.css` vendangés à l'identique sur les 4 hôtes, tests de logique pure étendus. Aucun autre fichier touché. ### Tests (chiffres réels, pas de déployé, pas de capture) - Tests de logique pure : TikTak/Kanbante/Flowdeck `node tests/recherche.test.js` → 59 assertions OK chacun ; ObsiGate `node tests/frontend/recherche.test.mjs` → 42 assertions OK. Nouveautés couvertes : `parseTab`, `visibleKeys` (Tout = 4 sections, sans console), troncature 8 vs complet, compteurs (`…`/total/`0`/vide), `buildSearchUrl`, `searchPath`/`normalize` complets, `normalizeConsole` (URL absolue/relative conservée, sans URL = pas de faux lien, format inconnu = non reconnu), `resolveConsoleBase` (explicite > injecté > -dev > href barre > prod). Suites existantes sans régression (les assertions #68 d'origine passent toutes). - Harnais DOM local (hors PR, fait tourner le vrai `recherche.js` avec un `fetch` factice : ObsiGate 12 résultats, Flowdeck 3, TikTak vide, Kanbante en erreur, console en erreur) → 23 assertions OK : focus au chargement, « Tout » = 8 liens affichés pour ObsiGate avec compteur 12, compteurs 12/3/0/vide, « Tout » = 15, section Kanbante en erreur sans casser la page, « Tout voir » ObsiGate → onglet avec les 12 résultats + URL `?q=test&tab=obsigate` + focus onglet, flèche droite → Flowdeck, console = message indisponible et compteur vide, `popstate` restaure requête + onglet, clic palette → focus + sélection, Entrée → premier résultat visible. - Écarts préexistants nommés : aucun écart nouveau constaté dans les fichiers touchés ; les validations déployées ci-dessous restent à faire par Bruno après merge/publication (aucun test déployé, aucune capture dans cette PR). ### À valider en direct par Bruno après merge/publication Mise en page TikTak (pleine largeur à droite du rail, mobile compris) ; mise en page ObsiGate (voir paragraphe ObsiGate) ; focus (clic barre, ⌘K, `/`, rechargement) ; « Tout voir » (reste sur la page, bascule d'onglet) ; onglets + URL (partage `?tab=`, retour arrière) ; compteurs (totaux, `…`, `0`, section en erreur) ; onglet Console Spark (indisponible honnête tant que l'endpoint console n'est pas mergé, puis résultats réels après confirmation du chemin avec l'autre travailleur). ### Paragraphe propre à ObsiGate Mise en page (point 2) : la page n'est plus nue. La route `/recherche` reste servie hors SPA (avant le catch-all, comme `/admin.html` — `backend/main.py` n'est PAS touché ici), mais `recherche.html` reprend désormais le gabarit de l'app : feuilles `/static/style.css` + `/static/draco-barre.css`, en-tête de fusion copié statiquement depuis `index.html` (snippet figé commons v0.1.6, onglets = liens réels via jetons `{{URL_*}}`, bouton central = focus dans la boîte), en-tête ObsiGate simplifié, sidebar statique (Tableau de bord, Rechercher partout) dans les classes de l'app (`.app-container`, `.header`, `.main-body`, `.sidebar`, `.content-wrapper`, `.content-area`), et navigation `.rp-og-nav` dans `recherche.css`. Base console : `window.DRACO_URLS.agent`, déjà injecté par la route (aucun jeton ajouté, aucun fichier serveur touché). Deltas explicites à valider en direct : (a) la sidebar est statique — les vaults, tags et récents dynamiques restent dans la SPA (une intégration dans le routeur SPA aurait exigé de toucher `index.html`/`app.js`, hors périmètre de ce correctif) ; (b) les menus, le panneau Spark, les alertes et le domaine de la barre copiée ne sont pas rejoués sur cette page autonome — l'autre travailleur (onglet Console Spark de la barre) devra reporter son ajout dans cette copie au re-vendoring, comme pour tout hôte ; (c) `style.css` importe les polices Google Fonts, que la CSP stricte de `/recherche` (`style-src 'self'`) bloque : la page retombe sur les polices système (repli typographique, pas de page cassée) — élargir la CSP serait une décision séparée, non prise ici. Fichiers : `frontend/recherche.html`, `frontend/js/recherche.js`, `frontend/recherche.css`, `tests/frontend/recherche.test.mjs`.
bruno added 4 commits 2026-10-28 13:51:04 -04:00
bruno merged commit 9c9828e9bd into main 2026-10-28 14:25:54 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Projets/ObsiGate#46