Fork feature update: node/edge editing, formula columns, selection panel, mobile UX & pre-production CI - #327
Open
JeanGarf wants to merge 205 commits into
Open
Fork feature update: node/edge editing, formula columns, selection panel, mobile UX & pre-production CI#327JeanGarf wants to merge 205 commits into
JeanGarf wants to merge 205 commits into
Conversation
…t on fullGraph reference changes
…/selection refresh after an edge edit
…ion, delete) to the graph view selection panel Reuses the existing Data view icon buttons for the per-item panel in the Graph view, adds a select-neighbors button for nodes and an invert-direction button for edges, placed just above the attribute list.
Keeps it consistent with the Node ID/Edge ID column already moved to the end in the data table views.
…ew selection panel
# Conflicts: # packages/gephi-lite/src/views/graphPage/Selection.tsx
Remove the CNAME pointing to the upstream lite.gephi.org domain, switch redirect URLs to relative paths, trigger deploy-app on push to main_jg instead of main (kept in sync with upstream), and disable the automatic Docker Hub publish workflow.
- Nouvelle icône dans le volet de sélection d'un nœud ouvrant le formulaire de création d'edge avec ce nœud pré-rempli comme source et le focus placé sur le champ cible. - Support d'un prop autoFocus dans GraphSearch. - Correction d'un crash latent : le rendu d'un item de sélection était tenté avant que le dataset ait rattrapé la sélection (fenêtre transitoire après création), provoquant un accès sur des données undefined. Le rendu est désormais ignoré pour ce frame et se corrige au rendu suivant. Les deux listes partagent le même renderItem factorisé. - Traductions create_edge_from_node ajoutées (dev, en, fr, hu, ko, uk).
Lors de la sélection d'un ou plusieurs edges, les nœuds source et cible sont désormais mis en avant (labels forcés) et tous les autres nœuds sont estompés avec leur label masqué, de la même façon que lors de la sélection d'un nœud.
…s scriptés 1) Réordonnancement Les filtres s'appliquent en cascade, chacun sur le graphe produit par les précédents : leur ordre est donc porteur de sens, mais n'était pas modifiable. Ajoute un couple de boutons monter/descendre sur chaque filtre (désactivés aux extrémités), adossés à un producteur `moveFilter` calqué sur `moveFieldModel`. 2) Attributs scriptés (colonnes formule) Les valeurs des colonnes formule ne sont pas stockées : elles sont recalculées à la volée et rangées dans le canal de données « dynamique », comme les attributs topologiques (degré...). Or les filtres ne construisaient ce canal que lorsque `field.dynamic` était vrai — jamais le cas d'une colonne formule — et ne calculaient de toute façon que la moitié topologique. Résultat : un attribut scripté était systématiquement lu comme `undefined`, donc traité comme une valeur manquante. Concrètement l'histogramme restait vide et le filtre ne filtrait rien (ou tout, selon « conserver les valeurs manquantes »). Factorise le calcul complet du canal dynamique — attributs topologiques ET colonnes formule — dans `computeAllComputedAttributes`, seul point d'entrée désormais utilisé par `filterGraph`, l'histogramme, la liste de valeurs et l'atome de données dynamiques (qui dupliquait cette logique). Les scripts sont toujours pris dans le modèle de champs courant, si bien qu'éditer une formule rafraîchit les filtres qui s'en servent. Les deux composants de filtre qui recalculaient ce canal chacun de leur côté passent par un hook partagé `useFilterItemData`, pour qu'ils décrivent exactement ce que `filterGraph` gardera ou écartera.
…s filtres + attributs scriptés filtrables
…dans les données) Pour tracer l'histogramme, la plage de valeurs est découpée en tranches arrondies à une unité lisible (ex. par 2, par 5...). Les champs « De »/« à » affichaient ensuite les bornes de ces tranches arrondies plutôt que le vrai min/max des données : sur un attribut Degree variant de 1 à 36, le panneau affichait par exemple 0 et 39. Utilise désormais le vrai min/max des valeurs, calculé indépendamment du découpage en tranches, pour ces deux champs.
…s De/à du filtre plage
…n/max Jusqu'ici, l'échelle min/max utilisée pour interpoler la taille se basait tantôt sur tout le jeu de données, tantôt sur les seuls éléments visibles après filtrage, selon que l'attribut choisi était calculé (Degree, colonne formule) ou non - un attribut calculé recalcule ses valeurs sur le graphe filtré, ce qui restreignait l'échelle sans le vouloir. Concrètement : filtrer pour ne garder que les deux plus grandes valeurs faisait remonter la seconde à la taille min, comme si elle était la plus petite de tout le graphe. Ajoute une case à cocher, décochée par défaut, sous « Interpoler entre les valeurs personnalisées min et max » : décochée, l'échelle est toujours calculée sur l'ensemble du jeu de données (comme dans la Vue Data), quel que soit le filtrage en cours et quel que soit le type d'attribut ; cochée, elle se restreint aux éléments actuellement conservés par les filtres. La valeur propre à chaque nœud/lien (utilisée pour son rendu) n'est pas affectée par ce réglage : seule l'échelle d'interpolation change.
…rs filtrées pour la taille
1) Le widget « élément le plus récent » (Nœuds/Liens) affichait un simple "?" lorsque cet élément était filtré, sans expliquer pourquoi. Affiche désormais "Filtered"/"Filtré" selon la langue - ce même repli étant partagé par NodeComponentById/EdgeComponentById, la Vue Data (colonne aperçu) et la recherche fuzzy en bénéficient aussi. 2) L'icône entonnoir du volet gauche n'avait ni info-bulle ni action : un badge purement décoratif, visible seulement quand des filtres étaient actifs. Elle est maintenant toujours affichée (creuse si aucun filtre actif, pleine sinon), avec une info-bulle explicite, et cliquable pour ouvrir directement le panneau Filtres (Vue Graphe comme Vue Data). 3) Sélectionner les liens d'un nœud ne garde que les liens non filtrés, sans l'indiquer. Le titre du volet de sélection affiche maintenant, dès qu'un élément sélectionné est filtré, "(3, 5 filtered)" (visibles, filtrés) au lieu du seul total ; ce format remplace le message "X hidden selected nodes" qui faisait doublon juste en dessous du titre. La liste dépliable des éléments masqués plus bas, elle, reste inchangée.
…tres actifs dans le volet gauche
- l'icône entonnoir du volet gauche ne reste plus pleine quand tous les filtres sont désactivés - « Sélectionner les liens / les voisins » d'un nœud partent du graphe complet, donc les éléments filtrés restent dans la sélection au lieu d'être silencieusement perdus ; les boutons sont désactivés pour un nœud isolé, qui n'a rien à sélectionner - volet de sélection : le décompte « (9, 10 filtrés) » passe à la ligne sous le titre, la partie filtrée est en rouge, et les éléments filtrés sont regroupés sous un titre rouge de même niveau que celui du volet, suivi d'un séparateur et repliable (replié par défaut) - un élément filtré garde son libellé mais est toujours dessiné en gris (disque creux, lien pointillé, libellé gris italique) au lieu d'être remplacé par « Filtered » : même rendu dans le volet de sélection, le résumé du graphe et la vue Data - les décomptes n'écrivent plus les nombres en toutes lettres (« 1 filtré » et non « un filtré ») et parlent de « filtrés » plutôt que de « masqués »
…s éléments filtrés
Un lien absent du graphe filtré se lit désormais comme filtré dans son ensemble : ses nœuds source et cible sont dessinés en cercle creux gris avec un libellé gris italique, même lorsque ces nœuds passent eux-mêmes les filtres.
Quand exactement deux nœuds sont sélectionnés, un bouton apparaît à côté du bouton de localisation : il sélectionne les liens d'un plus court chemin entre les deux nœuds et cadre la caméra dessus. Le rendu du graphe met déjà en avant les liens sélectionnés et leurs extrémités en estompant le reste, donc aucun mécanisme d'affichage n'est ajouté. Le chemin est cherché dans le graphe complet, sans tenir compte du sens des liens : un lien masqué par un filtre en fait donc partie et est signalé comme filtré dans le volet, comme pour toute autre sélection. Si aucun chemin ne relie les deux nœuds, une notification le signale et la sélection reste inchangée. Le parcours en largeur est isolé dans core/graph/utils (getShortestPathEdges / getEdgesBetween) et couvert par des tests.
… entre deux nœuds sélectionnés
Un écran tactile n'a pas de touche Ctrl : taper un deuxième nœud ne pouvait que remplacer la sélection, ce qui rendait inaccessible sur mobile tout ce qui demande plusieurs éléments sélectionnés (dont le nouveau bouton de chemin entre deux nœuds). Un appui maintenu bascule désormais l'élément dans la sélection, comme le fait Ctrl+clic. La durée est mesurée au relâchement plutôt qu'avec un minuteur : sigma ne signale un clic que si le doigt a très peu bougé, donc un appui long qui se transforme en déplacement de nœud ou en panoramique ne bascule rien. Le comportement est restreint au tactile, pour qu'une hésitation avant de déplacer un nœud à la souris ne modifie pas la sélection.
…ter à la sélection
Sur petit écran le volet de sélection et le panneau de menu sont empilés et ne peuvent pas être ouverts en même temps, et le seul bouton pour refermer le volet vidait la sélection : le champ de recherche n'était donc atteignable qu'en perdant ce qui était sélectionné. Ce bouton devient un chevron qui replie simplement le volet, la sélection étant conservée. Un second appui ouvre le panneau de menu et donne accès à la recherche. Toute modification de la sélection redéploie le volet, ce qui est le chemin de retour. Vider la sélection se fait désormais depuis une croix ajoutée dans la barre de titre du volet, à droite du bouton de localisation et séparée de lui pour éviter les appuis manqués. Sur grand écran rien ne change : la croix en haut à droite du volet vide toujours la sélection, et la nouvelle croix n'y est pas affichée.
… de sélection sur mobile
Un bouton dans la barre de titre bascule l'ordre de la liste entre alphabétique (par libellé pour un nœud ; par source, cible puis libellé pour un lien, l'ordre existant) et taille décroissante. La taille est celle du rendu, calculée par Apparence > Taille, donc l'ordre du volet correspond à ce que montre le graphe ; à taille égale, le tri alphabétique départage pour que la liste reste lisible. Les nœuds n'étaient jusqu'ici pas triés du tout et apparaissaient dans l'ordre d'insertion. Le réglage est persisté dans les préférences, séparément pour les nœuds et pour les liens. Corrige au passage le désérialiseur du SDK : il prenait pour un graphe sérialisé tout objet possédant les clés « nodes » et « edges », sans vérifier qu'il s'agissait bien de tableaux. Un état simplement indexé par type d'élément faisait donc échouer l'analyse et l'ensemble du document était silencieusement abandonné - ce qui rendait ici toutes les préférences non persistantes, thème compris.
…lphabétique / taille)
…pas un
Après une sauvegarde, la date de référence était mémorisée depuis la
réponse du PATCH, alors que toutes les vérifications ultérieures lisent
la date depuis GET /gists/{id}. Les deux peuvent différer pour la
version qui vient d'être écrite, et le garde-fou croyait alors que le
distant avait bougé - à chaque vérification, jusqu'à la sauvegarde
suivante. La date est désormais relue depuis le même endpoint que les
vérifications, comme le fait déjà l'ouverture d'un fichier.
Deuxième garde-fou, indépendant de la cause : avant d'alerter, le
contenu distant est téléchargé et comparé à l'empreinte de ce qui a été
synchronisé en dernier. La date de mise à jour d'un gist bouge en effet
pour des raisons qui ne touchent pas au fichier (étoile, fork,
commentaire, ou deux lectures de la même version qui ne s'accordent
pas) ; seules des données réellement différentes déclenchent désormais
l'avertissement. Quand elles sont identiques, la nouvelle date est
adoptée comme référence pour ne pas re-télécharger à chaque minute.
L'empreinte est rangée dans l'état fichier, donc persistée : elle
survit à un rechargement de page. Sans empreinte connue, le
comportement précédent s'applique (comparaison de dates seule), et un
échec de téléchargement alerte aussi - le doute doit rester du côté qui
protège le travail distant.
…arde-fou de fraîcheur GitHub
Le bouton Retour d'Android fermait l'éditeur sans condition, effaçant au passage ce que l'utilisateur avait commencé à saisir. Il demande désormais confirmation dès que quelque chose a été modifié, et la réponse par défaut - bouton principal, focalisé, et réponse de la touche Échap - est de poursuivre la saisie. Sans aucune modification, la fermeture reste immédiate. Même traitement pour les deux autres fermetures involontaires que ces éditeurs autorisent, la touche Échap et le clic à l'extérieur : elles perdaient la saisie exactement de la même façon. Les boutons de fermeture et d'annulation restent sans confirmation, un clic dessus étant une intention explicite. La demande de fermeture passe maintenant par l'état des modales (`requestCloseModal`) plutôt que par une fermeture directe, ce qui permet à la modale ouverte d'intervenir ; les modales sans saisie en cours se ferment comme avant. L'état « saisie en cours » vient de `dirtyFields` et non de `formState.isDirty`, ce dernier étant déjà vrai à l'ouverture parce qu'un champ dont la valeur par défaut est indéfinie rapporte une chaîne vide dès son enregistrement.
…t de perdre une saisie en cours
Les deux croix de fermeture - celle de l'en-tête de la modale et celle du pied de formulaire - passent désormais par la même demande de fermeture que la touche Retour, Échap et le clic à l'extérieur : sans saisie en cours elles ferment directement, sinon elles affichent la confirmation qui, par défaut, poursuit la saisie. La modale expose sa demande de fermeture par contexte, ce qui permet au pied de formulaire - construit par le formulaire et non par la modale - de l'utiliser sans dupliquer la logique. Hors modale (les mêmes formulaires sont aussi rendus dans un volet latéral), le bouton d'annulation retombe sur son gestionnaire d'origine. Ajoute au CLAUDE.md la règle correspondante : une même action déclenchable de plusieurs façons doit passer par un seul et même code, et une demande visant un seul de ces déclencheurs s'applique aux autres.
…emande aussi confirmation
Sous le numéro de version, en gris et en petit pour ne pas attirer l'œil : le hash de commit git et la date/heure de build, de manière à pouvoir vérifier qu'on est bien sur la dernière version déployée. Les deux valeurs sont injectées une fois pour toutes à la compilation (vite.config.mts, via `define`), donc gravées dans le bundle : rien à calculer ni à récupérer au chargement de la page. Absentes (nœud null) dans un environnement sans checkout git pour lire le hash, auquel cas la ligne ne s'affiche pas du tout plutôt que d'afficher une valeur trompeuse.
L'empreinte de référence était prise sur les octets envoyés lors d'une sauvegarde, alors que la vérification compare avec les octets que le distant renvoie. Toute différence introduite par GitHub sur le trajet suffisait donc à faire échouer chaque comparaison ultérieure, et la protection par contenu ajoutée précédemment ne servait plus à rien. La sauvegarde relit désormais le gist et prend l'empreinte de ce qu'il contient réellement — même lecture, même côté du fil. Une seule requête ramène maintenant métadonnées et contenu (readGist), ce qui évite aussi de mélanger une date issue d'une lecture avec un contenu issu d'une autre. Par ailleurs, répondre « Conserver mes modifications » ne changeait rien à l'état : la vérification suivante retrouvait le même écart de date et réaffichait l'alerte, toutes les minutes. La version distante ainsi écartée est désormais mémorisée et n'est plus resignalée ; une version distante réellement nouvelle alerte toujours, et la vérification avant sauvegarde reste inchangée pour ne pas écraser sans confirmation. Enfin, quand aucune empreinte n'est connue (état enregistré par une version antérieure), celle du distant est mémorisée au passage, pour que la vérification suivante puisse trancher au lieu de redemander sans fin.
… des fausses alertes de fraîcheur GitHub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull request type
Check the type of change your PR introduces:
Summary of changes (
d906a95→9db74cb)This range covers the full set of changes accumulated on this fork since the base commit, spanning many feature/fix branches merged into
developand periodically promoted tomain_jg.Save & session
Fix GitHub file being overwritten after creating a new workspace (Critical Bug)
Add a save icon next to "Workspace", greyed out until there are unsaved changes
Persist session settings (layout & metrics parameters) together with the graph
Node & Edge editing
Avoid Gephi Lite to crash when deleting an Edge [Deleting Edge makes Gephi lite to crash #318] (critical bug)
Prevent creation of duplicate nodes/edges
Support changing edge extremities (source/target) via
updateEdge, add a swap button in the edit form, and an "Invert arrow's direction" menu item [Add button to invert Source and Target of an Edge #321 ]Pre-fill default attribute values on node/edge creation [Default value for attributes #326 ]
Auto-focus the first empty field in node/edge modals
Numeric attributes: cross-platform +/− stepper
Add a validation button in the modal header and duplicate the OK button in the modal footer (espacially for Android)
Fix edge weight resetting to 1 on creation [weight field is reseted to 1 #322 ]
Move ID field to the end of node/edge create/edit forms and data columns; move x/y position fields to the end of the edit form
Formula (scripted) columns & data columns
Scripted columns: persisted script, live recompute, editable label, and a "Freeze values" action to materialize computed values into a static attribute
Add automatic "Update Date" / "Creation Date" columns, with backfill for graphs already loaded in session
GEXF export now materializes formula column values
Rename "Delete attribute" → "Delete column"; rename "Identifier" → "ID"
Selection panel & Graph view
Data tables
Color palette
Mobile & navigation
Default labels
Pre-production banner & CI
develop; configure GitHub Pages for this fork; grantcontents:writepermission to the deploy workflowMisc / internal
fullGraphreference changesmerge --no-ff)