@micheeeeel/yinyangapp 2.2.2 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/README.md +3 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/types/api.d.ts +7 -0
  4. package/dist/types/components/controller/YinYangController.d.ts +167 -5
  5. package/dist/types/components/controller/action-stack/ActionStackController.d.ts +15 -0
  6. package/dist/types/components/controller/actions/ChildActionMenuController.d.ts +40 -0
  7. package/dist/types/components/controller/children/ChildInteractionController.d.ts +9 -0
  8. package/dist/types/components/controller/force-field/ForceFieldDetailsPanel.d.ts +59 -8
  9. package/dist/types/components/controller/force-field/ForceFieldLifecycleController.d.ts +17 -0
  10. package/dist/types/components/controller/force-field/ForceFieldRenderer.d.ts +25 -0
  11. package/dist/types/components/controller/force-field/types.d.ts +16 -0
  12. package/dist/types/components/controller/odd/OddHighlightEffects.d.ts +78 -0
  13. package/dist/types/components/controller/odd/OddViewManager.d.ts +92 -1
  14. package/dist/types/components/controller/odd/childHighlightStyles.d.ts +79 -0
  15. package/dist/types/components/controller/search/SearchPanelSynchronizer.d.ts +9 -0
  16. package/dist/types/components/controller/state/YinYangControllerState.d.ts +23 -0
  17. package/dist/types/components/view/BorderDrawer.d.ts +8 -9
  18. package/dist/types/components/view/SpikeDrawer.d.ts +28 -0
  19. package/dist/types/components/view/ViewInitializer.d.ts +1 -1
  20. package/dist/types/components/workspace/workspaceTemplate.d.ts +1 -1
  21. package/dist/types/config/animationConfig.d.ts +7 -0
  22. package/dist/types/config/propagationCometConfig.d.ts +41 -0
  23. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { IYinYangView } from "../../../interfaces/IYinYangView";
3
3
  import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
4
4
  import { ForceFieldDetailsPanel } from "../force-field/ForceFieldDetailsPanel";
5
5
  import { ActionStackController } from "../action-stack/ActionStackController";
6
+ import { OddHighlightIntensity, ChildHighlightState } from "./childHighlightStyles";
6
7
  import type { CumulativeBlock } from "../YinYangController";
7
8
  type ChildOddMeta = {
8
9
  oddId: string;
@@ -27,18 +28,108 @@ type OddViewManagerDeps = {
27
28
  id: string;
28
29
  }) => CumulativeBlock[];
29
30
  onNavigateToAction: (actionId: string) => void;
31
+ /** Centre orbital courant d'un satellite dans le repère du globalGroup de
32
+ * la facette (identique à celui que consomme WeightPreviewRingRenderer). */
33
+ getChildRenderedCenter: (childController: IYinYangController) => {
34
+ x: number;
35
+ y: number;
36
+ } | null;
37
+ getChildRadius: (childController: IYinYangController) => number;
38
+ /** Satellites atteints par la propagation de l'action en cours — exemptés
39
+ * de l'extinction appliquée aux non-membres. */
40
+ getPropagationTargetKeys: () => Set<string>;
30
41
  };
31
42
  export declare class OddViewManager {
32
43
  private deps;
44
+ private readonly highlightEffects;
45
+ /** ODD mis en valeur (clic ou survol d'un arc / d'un chip de légende). */
46
+ private oddHighlight;
47
+ /** Satellite sélectionné (menu d'action ouvert) — il porte la même mise en
48
+ * valeur que celle d'un ODD, mais resserrée sur lui seul. */
49
+ private selectedChildKey;
50
+ /** Aperçu transitoire au survol d'un item de la pile — prioritaire tant
51
+ * qu'il dure, et sans effets de sélection. */
52
+ private stackPreviewKeys;
53
+ /** Laquelle des deux sélections délibérées — un ODD, ou un satellite — est
54
+ * la plus récente. Elles peuvent coexister (le menu d'un satellite reste
55
+ * ouvert quand on clique un chip d'ODD) ; c'est la dernière voulue par
56
+ * l'utilisateur qui porte la mise en valeur. */
57
+ private lastDeliberate;
58
+ /** Résultat des trois couches ci-dessus, relu par
59
+ * ChildInteractionController pour que son style "satellite actif"
60
+ * n'écrase pas la mise en valeur. */
61
+ private currentHighlight;
62
+ getCurrentHighlight(): ChildHighlightState;
33
63
  constructor(deps: OddViewManagerDeps);
34
- highlightOddInChildren(oddId: string | null): void;
64
+ highlightOddInChildren(oddId: string | null, intensity?: OddHighlightIntensity): void;
65
+ /**
66
+ * Sélectionne (ou désélectionne) le satellite qui porte la mise en valeur.
67
+ * Elle prime sur celle d'un ODD : sélectionner un satellite resserre le
68
+ * focus sur lui seul, et le relâcher rend la main à l'ODD éventuellement
69
+ * sélectionné.
70
+ */
71
+ setSelectedChildHighlight(childKey: string | null): void;
72
+ /**
73
+ * Recalcule la mise en valeur effective à partir des trois couches — aperçu
74
+ * de pile, satellite sélectionné, ODD — puis la pose sur les satellites et
75
+ * synchronise les effets réservés à la sélection.
76
+ */
77
+ private applyHighlight;
78
+ /**
79
+ * Couche gagnante. L'aperçu de pile et le survol d'un ODD priment toujours
80
+ * — ce sont des états transitoires, qui doivent rester lisibles même quand
81
+ * quelque chose est sélectionné. Entre les deux sélections durables, ODD et
82
+ * satellite, c'est la plus récente qui l'emporte.
83
+ */
84
+ private resolveHighlight;
85
+ private buildSelectedChildLayer;
86
+ private buildOddLayer;
87
+ /**
88
+ * Un ODD *sélectionné* (arc actif, ou ODD affiché en vue ODD) passe en
89
+ * "full" ; tout le reste — au premier chef le survol d'un arc voisin —
90
+ * reste en "soft". Corollaire utile : quand la souris quitte un arc
91
+ * survolé, YinYangController retombe sur l'ODD actif, qui retrouve donc
92
+ * automatiquement son intensité pleine.
93
+ */
94
+ private resolveIntensity;
95
+ /** Allume (ou éteint) tout ce qui n'appartient qu'à la sélection : anneaux
96
+ * de visée + pings + onde de scan, et spikes énergisés avec leurs
97
+ * comètes. */
98
+ private syncFullHighlightEffects;
99
+ /** Relu à chaque frame par OddHighlightEffects : les satellites orbitent
100
+ * sous les anneaux, et peuvent disparaître (satellites masqués, facette
101
+ * changée) sans que la mise en valeur soit repassée. */
102
+ private buildHighlightTargets;
35
103
  highlightOddByStackItemId(stackItemId: string | null): void;
36
104
  cancelOddHighlight(): void;
37
105
  showOddInfo(oddId: string): void;
38
106
  showComposanteInfo(): void;
39
107
  applyOddHighlight(oddId: string): void;
40
108
  applyEnterOddView(oddId: string): void;
109
+ /**
110
+ * Sélectionner un satellite ne sélectionne PAS son ODD : la mise en valeur
111
+ * d'un ODD est réservée à un clic sur l'ODD lui-même (arc du diagramme ou
112
+ * chip de légende). Seule subsiste ici la navigation propre à la vue ODD,
113
+ * qui n'affiche qu'un ODD à la fois et doit donc basculer sur celui du
114
+ * satellite cliqué.
115
+ */
41
116
  applyOddStateForCutting(oddId: string): void;
117
+ /** Repose sur un satellite le filtre que son état courant impose — utilisé
118
+ * après un aperçu qui l'avait écrasé (survol depuis l'arbre Recherche),
119
+ * pour ne pas le rendre "sans filtre" alors qu'un ODD est mis en valeur. */
120
+ refreshChildFilter(childController: IYinYangController): void;
121
+ /** Recalcule la mise en valeur sans changer de cible — appelé quand le
122
+ * champ de force change, puisque les satellites qu'il touche échappent à
123
+ * l'extinction. */
124
+ refreshHighlight(): void;
42
125
  private applyChildOddHighlight;
126
+ /**
127
+ * Halo coloré d'un satellite membre : deux cercles (lueur + trait net)
128
+ * dans son yinYangGroup, au même endroit que l'anneau de sélection STACK
129
+ * (ChildScoresDeltaRenderer) — ils suivent donc l'orbite et la respiration
130
+ * du satellite sans aucun travail par frame, là où un drop-shadow sur le
131
+ * groupe était recalculé à chaque déplacement.
132
+ */
133
+ private updateChildHaloRing;
43
134
  }
44
135
  export {};
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Source unique du filtre CSS posé sur le groupe d'un satellite.
3
+ *
4
+ * Trois acteurs y touchaient chacun de leur côté — la mise en valeur d'un ODD
5
+ * (OddViewManager), le style "satellite actif" et le survol
6
+ * (ChildInteractionController) — et le dernier à passer gagnait : la
7
+ * sélection d'un ODD était effacée dès qu'un menu d'action se rafraîchissait.
8
+ * Tout le monde passe désormais par `resolveChildFilter`.
9
+ */
10
+ /**
11
+ * Deux niveaux de mise en valeur d'un ODD sur ses satellites :
12
+ * - "soft" (survol d'un arc) : uniquement le halo coloré des membres et
13
+ * l'extinction des autres. Réversible, sans animation démarrée, pour que
14
+ * balayer le cercle central ne fasse pas clignoter toute la constellation.
15
+ * - "full" (sélection) : ajoute les anneaux de visée et les pings
16
+ * (OddHighlightEffects), les spikes énergisés avec leurs comètes
17
+ * (SpikeDrawer.setOddSpikeHighlight) et l'onde de scan initiale.
18
+ */
19
+ export type OddHighlightIntensity = "soft" | "full";
20
+ /**
21
+ * Mise en valeur courante sur les satellites d'une facette. Elle porte
22
+ * l'ensemble des satellites concernés plutôt qu'un ODD, parce que la même
23
+ * mécanique sert deux cibles : tous les satellites d'un ODD (clic sur son
24
+ * arc ou son chip de légende) ou le seul satellite sélectionné.
25
+ */
26
+ export type ChildHighlightState = {
27
+ /** Identité de la cible — sert à ne tirer l'onde de scan qu'une fois par
28
+ * mise en valeur, et non à chaque re-rendu. */
29
+ key: string;
30
+ color: string;
31
+ intensity: OddHighlightIntensity;
32
+ /** Clés (`controller.getKey()`) des satellites mis en valeur. */
33
+ memberKeys: Set<string>;
34
+ } | null;
35
+ /** Anneau de halo posé sur le bord du satellite membre, dans la couleur de
36
+ * l'ODD — la même que celle de son arc dans le cercle central (voir
37
+ * YinYangDrawer.refreshHoverArcStyles) — pour que le lien arc ↔ satellites
38
+ * se lise sans avoir à le chercher. Deux traits : une lueur large et
39
+ * translucide, un trait net par-dessus ; largeurs en fraction du rayon. */
40
+ export declare const HALO_RING: Record<OddHighlightIntensity, {
41
+ coreWidth: number;
42
+ glowWidth: number;
43
+ coreOpacity: number;
44
+ glowOpacity: number;
45
+ }>;
46
+ /** Satellite dont le menu d'action est ouvert : blanc pur et plus large, pour
47
+ * ne pas se confondre avec un membre de l'ODD mis en valeur. */
48
+ export declare const ACTIVE_CHILD_FILTER = "drop-shadow(0 0 12px rgba(255, 255, 255, 0.9)) brightness(1.1)";
49
+ /** Survol direct d'un satellite — aperçu par-dessus tout le reste. */
50
+ export declare const HOVERED_CHILD_FILTER = "brightness(1.3) drop-shadow(0 0 7px rgba(255,255,255,0.4))";
51
+ /** Aucun ODD mis en valeur et satellite ni actif ni survolé. */
52
+ export declare const IDLE_CHILD_FILTER = "brightness(0.85)";
53
+ /** Sans transition, l'extinction des non-membres se lit comme un
54
+ * clignotement quand la souris balaye les arcs du cercle central. */
55
+ export declare const CHILD_FILTER_TRANSITION = "filter 0.22s ease-out";
56
+ /**
57
+ * Groupe sur lequel poser le filtre d'un satellite : son `globalGroup`, et
58
+ * surtout pas son `yinYangGroup`. Les disques yin/yang — c'est-à-dire le
59
+ * corps même du satellite — sont créés dans le globalGroup, en frères du
60
+ * yinYangGroup (voir ViewInitializer.createYinOrYangGroups) : un filtre posé
61
+ * sur le yinYangGroup n'atteignait presque rien, ce qui rendait toute mise
62
+ * en valeur invisible quelle que soit son intensité.
63
+ */
64
+ export declare function getChildFilterTarget(childController: {
65
+ view: {
66
+ viewInitializer: {
67
+ globalGroup: {
68
+ node: () => unknown;
69
+ };
70
+ };
71
+ };
72
+ }): SVGGElement | null;
73
+ export declare function resolveChildFilter(params: {
74
+ isMember: boolean;
75
+ isActiveChild: boolean;
76
+ /** Satellite atteint par la propagation de l'action en cours. */
77
+ isPropagationTarget: boolean;
78
+ highlight: ChildHighlightState;
79
+ }): string;
@@ -58,6 +58,12 @@ type SearchPanelSynchronizerDeps = {
58
58
  onDiagramNavChange?: (event: DiagramCuttingNavEvent) => void;
59
59
  applyEnterOddView: (oddId: string) => void;
60
60
  applyOddHighlight: (oddId: string) => void;
61
+ /** Aperçu de survol d'un ODD, `null` retombant sur l'ODD actif — même
62
+ * point d'entrée que le survol d'un arc du diagramme. */
63
+ previewOddHighlight: (oddId: string | null) => void;
64
+ /** Repose sur un satellite le filtre imposé par l'état courant, après un
65
+ * aperçu de survol qui l'avait écrasé. */
66
+ refreshChildFilter: (childController: IYinYangController) => void;
61
67
  focusChildByOddCutting: (oddId: string, cuttingIndex: number, forceOpen?: boolean) => void;
62
68
  focusChildByOddCuttingAndAction: (oddId: string, cuttingIndex: number, actionId: string) => void;
63
69
  validateActionSelection: (childController: IYinYangController, actionId: string, label: string, insertAtVisibleIndex?: number | null, entryOrigin?: {
@@ -87,6 +93,9 @@ type SearchPanelSynchronizerDeps = {
87
93
  export declare class SearchPanelSynchronizer {
88
94
  private deps;
89
95
  private searchHoverChildNode;
96
+ /** Satellite dont l'aperçu de survol est en cours — mémorisé en plus de son
97
+ * nœud pour pouvoir lui rendre son filtre d'état à la sortie. */
98
+ private searchHoverChild;
90
99
  constructor(deps: SearchPanelSynchronizerDeps);
91
100
  searchHandleComposanteClick(componentIndex: number): void;
92
101
  searchNavigateToComponent(componentIndex: number): void;
@@ -100,6 +100,13 @@ export declare class YinYangControllerState {
100
100
  orbitData: ChildrenOrbitEntry[] | null;
101
101
  revealStartedAt: number | undefined;
102
102
  selectedLinkIndex: number | null;
103
+ /** Item de pile en focus — persistant, posé par le clic sur l'en-tête
104
+ * d'un bloc du panneau de description. Survit à la navigation entre
105
+ * satellites et facettes ; effacé quand l'item quitte la pile active. */
106
+ focusedStackItemId: string | null;
107
+ /** Aperçu temporaire (survol d'un bloc du panneau ou d'un item de la
108
+ * pile) — prime sur le focus tant qu'il dure. */
109
+ hoveredStackItemId: string | null;
103
110
  };
104
111
  eventField: {
105
112
  targets: EventFieldTarget[];
@@ -122,10 +129,21 @@ export declare class YinYangControllerState {
122
129
  resetOrbitAngle(): void;
123
130
  clearMenuActiveSelection(): void;
124
131
  setMenuActiveChild(childKey: string | null): void;
132
+ /** Notifié à chaque changement du satellite sélectionné (menu d'action
133
+ * ouvert), quel que soit le chemin — le nombre de points d'entrée qui
134
+ * posent ou effacent cette sélection rend l'observation plus sûre que
135
+ * d'appeler la mise à jour visuelle sur chacun d'eux. */
136
+ private _onMenuActiveChildChange;
137
+ setOnMenuActiveChildChange(cb: (childKey: string | null) => void): void;
125
138
  setActionSourceChildKey(childKey: string | null): void;
126
139
  setMenuActiveAction(actionId: string | null): void;
127
140
  setHasActiveChildAction(isActive: boolean): void;
128
141
  setForceFieldSourceChildIndex(index: number): void;
142
+ setFocusedStackItemId(id: string | null): void;
143
+ setHoveredStackItemId(id: string | null): void;
144
+ /** Item de pile dont le satellite source porte l'emphase : l'aperçu par
145
+ * survol l'emporte, sinon le focus persistant. */
146
+ getEmphasisStackItemId(): string | null;
129
147
  setForceFieldSourceChildIndices(indices: number[]): void;
130
148
  setIncomingForceFieldLinks(links: ForceFieldLink[], allDetails: ForceFieldLinkDetail[]): void;
131
149
  setForceFieldRandomCount(count: number): void;
@@ -163,6 +181,11 @@ export declare class YinYangControllerState {
163
181
  setForceFieldSelectedLinkIndex(index: number | null): void;
164
182
  setForceFieldRevealStartedAt(timestamp: number | undefined): void;
165
183
  setForceFieldLinksAndDetails(links: ForceFieldLink[], allLinkDetails: ForceFieldLinkDetail[]): void;
184
+ /** Notifié à chaque recomposition des liens du champ de force — les
185
+ * satellites qu'ils touchent échappent à l'extinction appliquée par une
186
+ * mise en valeur (voir resolveChildFilter). */
187
+ private _onForceFieldLinksChange;
188
+ setOnForceFieldLinksChange(cb: () => void): void;
166
189
  setEventFieldTargets(targets: EventFieldTarget[]): void;
167
190
  setEventFieldFrameId(frameId: number | undefined): void;
168
191
  setEventFieldLastTimestamp(timestamp: number | undefined): void;
@@ -146,9 +146,8 @@ export declare class BorderDrawer {
146
146
  /** Lifts the "voile terne" on one facette's image, veiling every other one
147
147
  * — only one can be unveiled at a time. Driven by the outer spark
148
148
  * sweeping across border arcs. Veils *all* others rather than just the
149
- * previously-tracked index, so this also correctly restores the
150
- * single-spotlight invariant the first time it's called after Idle
151
- * (where every image starts unveiled, not just one). */
149
+ * previously-tracked index, so the single-spotlight invariant holds
150
+ * whatever state the ring was left in. */
152
151
  revealFacetteImage(segmentIndex: number): void;
153
152
  /** Center + radius (pre-scale coordinate space) of a facette's image
154
153
  * thumbnail, so labels can be anchored onto the image instead of
@@ -172,13 +171,13 @@ export declare class BorderDrawer {
172
171
  * timers D3. */
173
172
  activateSegment(segmentIndex: number, immediate?: boolean): void;
174
173
  /** Resets all arc segments to inactive style (e.g. when satellites are
175
- * folded) and returns to Idle: every facette image goes back to full
176
- * color, exactly like a fresh load. */
174
+ * folded) and returns to Idle: every facette image goes back to black and
175
+ * white, exactly like a fresh load. */
177
176
  deactivateSegment(): void;
178
- /** Restores every facette image to full color — the Idle visual, reached
179
- * both at true init and whenever no facette is currently active (e.g.
180
- * after deselecting one via deactivateSegment). */
181
- revealAllFacetteImages(): void;
177
+ /** Veils every facette image — the Idle visual, reached both at true init
178
+ * and whenever no facette is currently active (e.g. after deselecting one
179
+ * via deactivateSegment): no arc is active, so no image is in color. */
180
+ veilAllFacetteImages(): void;
182
181
  /**
183
182
  * Highlights the source component border arc with a temporary beacon.
184
183
  * Used during a component switch when the active action has no visible force field.
@@ -8,6 +8,13 @@ export declare class SpikeDrawer {
8
8
  spikesEnabled: boolean;
9
9
  private spikeGhostEntries;
10
10
  private spikeGhostFrameId;
11
+ /** ODD actuellement sélectionné dans le cercle central (clic sur un arc) :
12
+ * ses spikes restent énergisés en continu et une comète les remonte.
13
+ * Mémorisé parce qu'unhighlightSpikes() est appelé au mouseleave d'un
14
+ * satellite et ne doit pas éteindre au passage la sélection en cours. */
15
+ private oddSpikeHighlight;
16
+ private oddCometEntries;
17
+ private oddCometFrameId;
11
18
  constructor(ViewInitializer: ViewInitializer);
12
19
  /**
13
20
  * Initializes the necessary data for drawing spikes.
@@ -57,4 +64,25 @@ export declare class SpikeDrawer {
57
64
  * Restores all spikes to their default appearance.
58
65
  */
59
66
  unhighlightSpikes(): void;
67
+ /**
68
+ * Énergise en continu les spikes de l'ODD sélectionné — la ligne de force
69
+ * qui relie son arc du cercle central à chacun de ses satellites — et
70
+ * éteint les autres, puis lance les comètes qui les remontent. `oddId` à
71
+ * null rend leur apparence par défaut à tous les spikes.
72
+ *
73
+ * C'est l'effet qui *montre* le rattachement satellite ↔ ODD, là où le
74
+ * halo posé sur les satellites (voir OddViewManager) ne fait que le
75
+ * suggérer. Réservé à l'intensité "full" (clic).
76
+ */
77
+ setSpikeHighlight(target: {
78
+ oddId: string;
79
+ cuttingIndex?: number;
80
+ } | null, color: string): void;
81
+ /** Vrai si ce spike fait partie de la mise en valeur courante. */
82
+ private isHighlightedSpike;
83
+ private forEachSpikeLine;
84
+ private applyOddSpikeHighlight;
85
+ private refreshOddSpikeComets;
86
+ private ensureOddCometLoop;
87
+ private clearOddSpikeComets;
60
88
  }
@@ -176,7 +176,7 @@ export declare class ViewInitializer {
176
176
  /** Exponent (0-1) applied to each facette's weight ratio (its weight over
177
177
  * the heaviest facette's weight) before it scales the thumbnail radius:
178
178
  * radius = baseRadius * weightRatio^curve. 1 = linear (proportional to
179
- * weight, the default, and the strongest shrinkage). Below 1 attenuates
179
+ * weight, and the strongest shrinkage). Below 1 attenuates
180
180
  * the falloff — lighter facettes stay closer to full size — down to 0,
181
181
  * where every facette renders at the same full size regardless of weight. */
182
182
  facetteImageWeightCurve: number;