Second Brain documentation

ADR 0088 – Accueil « Maintenant » côté front : layout focus, lignes de l'accueil, barre de charge découpée, digest et puces

Accepté (2026-10-17, dans le cadre de l'ADR 0086 §1 validé par Aurélien)

← Toutes les décisions

ADR 0088 – Accueil « Maintenant » côté front : layout focus, lignes de l’accueil, barre de charge découpée, digest et puces

Statut : Accepté (2026-10-17, dans le cadre de l’ADR 0086 §1 validé par Aurélien)

Suite de l’ADR 0087 (moteur de dashboards, côté backend). Cet ADR décrit ce que le front en fait : lignes A2 à A4 du backlog du jalon 4. Maquettes : canvas « Second Brain · Charte graphique », planche Accueil · Maintenant (web). L’enchaînement (chain), la coquille allégée et la version mobile détaillée sont les lignes suivantes du fil A.

Contexte

Le client généré porte DashboardDefinition.layout et capture, et sur les widgets column, split, chain, sources et items. Le front n’a aucune connaissance du domaine : il ne sait ni ce qu’est une tâche ni quel champ est une échéance. Tout ce qui suit se déduit des clés du YAML et des définitions déjà exposées.

Décision

Layout focus et capture

  • layout: focus : deux colonnes, principale 7/12 et latérale 5/12, chacune dans l’ordre du YAML. Un widget sans column va dans la colonne principale. Sous 900 px, une seule colonne : la principale puis la latérale (l’ordre du DOM, sans réordonnancement CSS, donc le même pour les lecteurs d’écran). Le répartiteur est une fonction pure (focusColumns).
  • capture: true : le composant QuickCapture existant passe dans l’en-tête, à droite de la date ; l’import de fichiers reste dessous. Sans capture, la rangée de capture actuelle ne change pas. Sans layout, la grille de deux colonnes de l’ADR 0038 est inchangée, width y compris.

Lignes de l’accueil

Dans un dashboard focus, un widget view affiche ses lignes avec TaskRows au lieu de la liste de l’ADR 0030. Les colonnes se lisent par leur rôle déclaré dans le widget, pas par leur nom :

Cellule Source
Quand (« en retard · 7 j », « aujourd’hui », « demain », « dans 3 j »), en alerte si passé le champ reschedule du widget
Estimation (« 45 min », « — » si absente) le champ sum du widget
Contexte (projet…) avec une pastille de la couleur du type du nœud cité les autres colonnes, hors title et type
Case « Fait » une action rapide de la ligne qui écrit un état final du workflow de son aspect (workflow.final) ; absente si la ligne n’en offre pas
Bouton « Reporter » (demain) présent si le widget déclare reschedule, même écriture que « Replanifier » (reschedule(), ADR 0038)
  • « Fait » passe par l’écriture annulable (ADR 0049, API des lots, action rapide ADR 0025) : « Annuler » reste proposé 10 secondes, les vues se relisent. Un refus du serveur (workflow, condition) s’affiche dans le widget.
  • Configuration : l’aspect tache gagne l’action rapide « Fait » (set: { statut: fait }), ajoutée en dernier pour ne pas déplacer l’index des actions existantes. Le front la reconnaît parce qu’elle écrit un état final, pas parce qu’elle s’appelle « Fait ». Une facture « Payée aujourd’hui » n’en est pas une (elle n’écrit pas le workflow) ; un autre aspect obtient sa case en déclarant une action vers un état final.
  • Le glisser vers « Demain » de l’ADR 0070 reste sur la liste des autres dashboards ; dans focus, le bouton le remplace (cible tactile de 44 px sur téléphone).

Barre de charge découpée

Pour un widget view avec split (qui exige sum et capacity, ADR 0087) :

  • le front évalue la condition split sur chaque ligne reçue et cumule sum en « en retard » (condition vraie) et « prévu » (les autres) ; « libre » est la capacité moins le total, jamais négative. La barre est colorée par la charte (alerte, accent, fond), jamais par le YAML ;
  • le texte devient « 4 h 10 engagées sur 6 h » (avec « au moins » quand la vue est tronquée, « (n sans estimation) » et « Trop chargé de … »), la légende donne les trois parts, le nom accessible de la barre les répète ;
  • les totaux réutilisent la règle d’estimation de l’ADR 0070 (dayLoad) : seules les valeurs numériques positives comptent, les autres sont comptées à part.

L’évaluateur (core/where-eval.ts) est un petit analyseur à descente récursive, testé, du sous-ensemble du langage where (ADR 0028) nécessaire :

  • opérandes : champ de colonne, texte entre apostrophes ('' pour une apostrophe), nombre, true, false, null, today et now avec décalage ± N d|w|m ;
  • opérateurs : == != < <= > >=, && || !, parenthèses, et un champ booléen seul.

Limites assumées, documentées en tête du fichier :

  • il ne lit que ViewRow.values, donc les colonnes de la vue (le serveur l’impose au chargement pour split, ADR 0087) ; un champ absent vaut null ;
  • un ordre avec null ou deux types différents est faux, comme en SQL ; != avec null est vrai ;
  • une date seule comparée à un instant se compare au jour local de l’instant ; deux instants se comparent par l’heure ;
  • today est le jour du fuseau du navigateur, celui que le client envoie déjà au serveur pour les vues ;
  • les champs calculés, node et toute syntaxe inconnue sont refusés (SplitUnsupported). Dans ce cas le widget retombe sur la barre simple (« Charge estimée : … sur 6 h ») sans casser l’accueil.

Si le langage where grandit, le serveur reste l’arbitre à l’écriture des définitions ; l’évaluateur front doit alors suivre, sinon la barre simple s’affiche. Le risque est un découpage faux sans erreur si la sémantique diverge sur un cas limite : la table des cas est dans where-eval.spec.ts.

Widget digest

  • Une requête par source de vue, avec pour limite celle de la source plus celles des sources précédentes (au pire, toutes les lignes précédentes sont des doublons) : digestFetchLimit. Le serveur ne dédoublonne pas (ADR 0087).
  • Première source gagnante : une ligne n’apparaît qu’une fois, dans la première source qui la porte ; chaque source montre au plus sa limit lignes une fois les doublons retirés, sans être complétée au-delà (buildDigest, fonction pure).
  • L’affichage réutilise le composant de vue liste sans requête propre (ADR 0030) : une seule liste dont les colonnes et les références sont l’union de celles des sources. ViewList gagne une entrée facultative tags (étiquette par id de nœud), rendue en puce devant le titre.
  • La puce prend la couleur du type du nœud (data-node-type, charte C3), pas une couleur libre : la maquette montre des couleurs par source, la décision de l’ADR 0087 prévaut.
  • Une source en erreur est signalée (« Une vue est indisponible ») sans masquer les autres ; pendant un rechargement la dernière liste reste affichée.

Des puces vers l’Inbox (/inbox, avec le compteur « à trier » de l’InboxStore), une revue (/reviews/<nom>) et une vue (/views/<nom>, compteur facultatif avec count: true, plafonné comme le widget counter). Une requête par vue comptée. Une puce sans cible reconnue est ignorée.

Fichiers

features/dashboards/ : focus-model.ts (répartition, digest, puces, libellé d’échéance, purs), task-rows.ts, load-bar.ts, focus-widgets.ts (DigestWidget, LinksWidget) ; ViewWidget gagne l’entrée focus ; core/where-eval.ts, core/day-plan.ts (splitLoad), core/quick-actions.ts (completionAction).

Alternatives écartées

  • Reconnaître « Fait » par son libellé ou par le champ statut : c’est du domaine dans le code. L’état final du workflow est déjà dans la définition.
  • Évaluer split par une requête serveur : écarté dans l’ADR 0087 ; le front a déjà les lignes.
  • Une liste par source dans le digest : ne donnerait pas la liste unique étiquetée de la maquette et compliquerait le dédoublonnage.
  • Réordonner les colonnes par CSS (order) sur mobile : l’ordre visuel différerait de l’ordre de lecture.

Conséquences

  • Aucune migration ; une action rapide ajoutée à config/aspects/tache.yaml.
  • /dashboards/aujourdhui reste atteignable et inchangé (grille de l’ADR 0038).
  • L’enchaînement (chain), la coquille allégée, la barre basse mobile et le raffinement de la planche mobile restent à faire (lignes A5 et suivantes) : chain est lu mais pas encore affiché.
  • Limite connue, légende de la barre de charge : les libellés de load-bar.ts (« Charge du jour », « engagées », « en retard », « libres ») sont écrits dans le composant, pas dans le YAML ; ils supposent une charge répartie en retard / du jour / libre. Les rendre configurables (clés legend du widget) demanderait une évolution du méta-schéma des dashboards ; ce n’est pas fait. Les autres libellés génériques de l’accueil et d’« Enchaîner » sont neutres (« terminé », « élément »).
  • Écarts de grammaire de where-eval avec le serveur (guillemets doubles, nombres négatifs, (a) == b refusés ici ; == null sur un champ nombre contenant du texte) : documentés en tête du fichier, sans effet sur les conditions acceptées des deux côtés ; une condition refusée donne la barre sans découpage.