@micheeeeel/yinyangapp 1.0.6 → 2.0.0

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 (118) hide show
  1. package/README.md +105 -0
  2. package/dist/bundle.js +2 -1
  3. package/dist/bundle.js.LICENSE.txt +1 -0
  4. package/dist/types/adapters/yinYangAdapter.d.ts +10 -0
  5. package/dist/types/animations/AnimationsMngr.d.ts +7 -19
  6. package/dist/types/api-film.d.ts +22 -0
  7. package/dist/types/api.d.ts +48 -0
  8. package/dist/types/benchmark/runner.d.ts +5 -0
  9. package/dist/types/benchmark/scenarios.d.ts +118 -0
  10. package/dist/types/components/action-image/ActionImageModal.d.ts +42 -0
  11. package/dist/types/components/controller/YinYangController.d.ts +946 -18
  12. package/dist/types/components/controller/action-stack/ActionStackController.d.ts +149 -0
  13. package/dist/types/components/controller/action-stack/ActionStackDragHandler.d.ts +111 -0
  14. package/dist/types/components/controller/action-stack/ActionStackManager.d.ts +118 -0
  15. package/dist/types/components/controller/action-stack/ActionStackState.d.ts +169 -0
  16. package/dist/types/components/controller/actions/ActionProjectionService.d.ts +73 -0
  17. package/dist/types/components/controller/actions/ChildActionMenuController.d.ts +83 -0
  18. package/dist/types/components/controller/actions/CoreEventMenuController.d.ts +90 -0
  19. package/dist/types/components/controller/actions/radialItemMenu.d.ts +117 -0
  20. package/dist/types/components/controller/children/ChildInteractionController.d.ts +38 -0
  21. package/dist/types/components/controller/children/ChildrenOrbitService.d.ts +14 -0
  22. package/dist/types/components/controller/force-field/EventFieldLifecycleController.d.ts +104 -0
  23. package/dist/types/components/controller/force-field/EventFieldRenderer.d.ts +86 -0
  24. package/dist/types/components/controller/force-field/ForceFieldComputationService.d.ts +50 -0
  25. package/dist/types/components/controller/force-field/ForceFieldDetailsPanel.d.ts +639 -0
  26. package/dist/types/components/controller/force-field/ForceFieldLifecycleController.d.ts +53 -0
  27. package/dist/types/components/controller/force-field/ForceFieldRenderer.d.ts +67 -0
  28. package/dist/types/components/controller/force-field/types.d.ts +42 -0
  29. package/dist/types/components/controller/labels/ChildScoresDeltaRenderer.d.ts +33 -0
  30. package/dist/types/components/controller/labels/ContextLabelsRenderer.d.ts +155 -0
  31. package/dist/types/components/controller/labels/WeightPreviewRingRenderer.d.ts +61 -0
  32. package/dist/types/components/controller/odd/ChildrenOrbitManager.d.ts +25 -0
  33. package/dist/types/components/controller/odd/OddViewManager.d.ts +44 -0
  34. package/dist/types/components/controller/search/SearchPanelSynchronizer.d.ts +125 -0
  35. package/dist/types/components/controller/settings/ControllerSettingsApplier.d.ts +50 -0
  36. package/dist/types/components/controller/state/BlockingAnimationTracker.d.ts +17 -0
  37. package/dist/types/components/controller/state/YinYangControllerState.d.ts +176 -0
  38. package/dist/types/components/controller/utils/actionStackMath.d.ts +13 -0
  39. package/dist/types/components/controller/utils/actionStackReorder.d.ts +7 -0
  40. package/dist/types/components/controller/utils/childrenOrbit.d.ts +18 -0
  41. package/dist/types/components/controller/utils/cuttingLabels.d.ts +9 -0
  42. package/dist/types/components/controller/utils/eventFieldStyle.d.ts +13 -0
  43. package/dist/types/components/controller/utils/eventFieldTargets.d.ts +47 -0
  44. package/dist/types/components/controller/utils/forceFieldMetadata.d.ts +4 -0
  45. package/dist/types/components/controller/utils/forceFieldPresentation.d.ts +9 -0
  46. package/dist/types/components/controller/utils/forceFieldStyle.d.ts +10 -0
  47. package/dist/types/components/controller/utils/labelGeometry.d.ts +5 -0
  48. package/dist/types/components/controller/utils/oddCutting.d.ts +2 -0
  49. package/dist/types/components/controller/utils/svgCoordinates.d.ts +9 -0
  50. package/dist/types/components/controller/utils/svgTransform.d.ts +5 -0
  51. package/dist/types/components/controller/utils/textLayout.d.ts +2 -0
  52. package/dist/types/components/controls/VizControlsPanel.d.ts +14 -0
  53. package/dist/types/components/factory/YinYangAppFactory.d.ts +4 -6
  54. package/dist/types/components/model/YinYangModel.d.ts +4 -30
  55. package/dist/types/components/panel/DiagramPanelBridge.d.ts +34 -0
  56. package/dist/types/components/panel/PanelManager.d.ts +150 -0
  57. package/dist/types/components/search/GlobalSearchController.d.ts +396 -0
  58. package/dist/types/components/stack-nav/StackNavOverlay.d.ts +82 -0
  59. package/dist/types/components/view/BorderDrawer.d.ts +203 -13
  60. package/dist/types/components/view/SpikeDrawer.d.ts +37 -31
  61. package/dist/types/components/view/ViewInitializer.d.ts +438 -2
  62. package/dist/types/components/view/YinYangDrawer.d.ts +414 -11
  63. package/dist/types/config/animationConfig.d.ts +37 -0
  64. package/dist/types/config/deltaColors.d.ts +64 -0
  65. package/dist/types/config/labelStyleConfig.d.ts +50 -0
  66. package/dist/types/data/OddCategoryColors.d.ts +12 -2
  67. package/dist/types/data/adverse-action-planner.d.ts +51 -0
  68. package/dist/types/data/adverse-action-planner.test.d.ts +1 -0
  69. package/dist/types/data/amplifier-engine.d.ts +107 -0
  70. package/dist/types/data/amplifier-engine.test.d.ts +1 -0
  71. package/dist/types/data/combo-engine.d.ts +94 -0
  72. package/dist/types/data/combo-engine.test.d.ts +1 -0
  73. package/dist/types/data/odd_generic_descriptions.d.ts +4 -0
  74. package/dist/types/data/odd_labels.d.ts +5 -0
  75. package/dist/types/data/yasuni_actions.d.ts +47 -0
  76. package/dist/types/data/yasuni_adverse_actions.d.ts +2 -0
  77. package/dist/types/data/yasuni_adverse_key_events.d.ts +2 -0
  78. package/dist/types/data/yasuni_cause.d.ts +33 -0
  79. package/dist/types/data/yasuni_graph.d.ts +22 -0
  80. package/dist/types/data/yasuni_key_events.d.ts +61 -0
  81. package/dist/types/data/yasuni_propagation_layers.d.ts +3 -0
  82. package/dist/types/interfaces/IActions.d.ts +1 -0
  83. package/dist/types/interfaces/ICauseGraph.d.ts +9 -0
  84. package/dist/types/interfaces/ICauseModel.d.ts +33 -0
  85. package/dist/types/interfaces/IGraphicsStrategy.d.ts +9 -5
  86. package/dist/types/interfaces/IPropagationLayers.d.ts +27 -0
  87. package/dist/types/interfaces/IViewData.d.ts +12 -0
  88. package/dist/types/interfaces/IYinYangAppFactory.d.ts +3 -3
  89. package/dist/types/interfaces/IYinYangController.d.ts +301 -6
  90. package/dist/types/interfaces/IYinYangGeometry.d.ts +43 -0
  91. package/dist/types/interfaces/IYinYangInternalModel.d.ts +34 -0
  92. package/dist/types/interfaces/IYinYangModel.d.ts +3 -11
  93. package/dist/types/main.d.ts +9 -0
  94. package/dist/types/utils/actionImages.d.ts +14 -0
  95. package/dist/types/utils/actionPropagation.d.ts +74 -0
  96. package/dist/types/utils/appMode.d.ts +5 -0
  97. package/dist/types/utils/causeGraph.d.ts +6 -0
  98. package/dist/types/utils/causeMetrics.d.ts +26 -0
  99. package/dist/types/utils/chainCardHeight.d.ts +27 -0
  100. package/dist/types/utils/d3/D3Element.d.ts +2 -2
  101. package/dist/types/utils/d3/D3GraphicsStrategy.d.ts +21 -4
  102. package/dist/types/utils/deltaChips.d.ts +15 -0
  103. package/dist/types/utils/deltaLabels.d.ts +54 -0
  104. package/dist/types/utils/facetteImages.d.ts +1 -0
  105. package/dist/types/utils/geometryUtils.d.ts +0 -1
  106. package/dist/types/utils/hierBadge.d.ts +44 -0
  107. package/dist/types/utils/idleSphereImage.d.ts +8 -0
  108. package/dist/types/utils/keyEventImages.d.ts +41 -0
  109. package/dist/types/utils/oddLegend.d.ts +30 -0
  110. package/dist/types/utils/positionalEfficiency.d.ts +16 -0
  111. package/dist/types/utils/yinYangWatermark.d.ts +29 -0
  112. package/package.json +27 -12
  113. package/dist/index.html +0 -1
  114. package/dist/types/data/coupe_rase_Orangs-outans/data_Kalimantan.d.ts +0 -3
  115. package/dist/types/data/coupe_rase_Orangs-outans/data_PT_Mayawana_Persada.d.ts +0 -3
  116. package/dist/types/data/data_institu_bancaire.d.ts +0 -3
  117. package/dist/types/interfaces/IOddItem.d.ts +0 -6
  118. package/dist/types/script.d.ts +0 -13
@@ -2,15 +2,73 @@ import { ViewInitializer } from "./ViewInitializer";
2
2
  import { IYinOrYangPattern } from "../../interfaces/IYinOrYangPattern";
3
3
  export declare class YinYangDrawer {
4
4
  private ViewInitializer;
5
- private viewData;
6
5
  private yinYangGroup;
7
- private yinOrYangGroup;
8
6
  private graphicStrategy;
9
- private positionX;
10
- private positionY;
11
- private categories;
7
+ private geometry;
12
8
  private containerId;
13
9
  private key;
10
+ private readonly WAVE_COLOR;
11
+ private readonly WAVE_MIN_RADIUS_DIFF;
12
+ private readonly MAX_WAVE_OPACITY;
13
+ private readonly ADAPTIVE_WAVE_FULL_DIFF_RATIO;
14
+ private readonly GHOST_MAIN_SELECTOR;
15
+ private readonly GHOST_WAVE_SELECTOR;
16
+ /** Corps des lignes du badge du ghost, et écart entre elles. Plus gros que
17
+ * les deltas des satellites : le badge est seul au bord du grand cercle,
18
+ * quand les leurs doivent cohabiter avec la nuée de satellites. */
19
+ private readonly GHOST_DELTA_BADGE_FONT_SIZE;
20
+ private readonly GHOST_DELTA_BADGE_ROW_GAP;
21
+ /** Part du diamètre du noyau que le badge occuperait à l'échelle
22
+ * géométrique exacte (diagonale du bloc inscrite dans le disque) — voir
23
+ * ViewInitializer.ghostDeltaBadgeSizeBoost pour l'agrandissement, réglable
24
+ * en direct, appliqué au-delà de cette base. */
25
+ private readonly GHOST_DELTA_BADGE_NUCLEUS_FILL;
26
+ private categoryArcGhostEntries;
27
+ private categoryArcGhostFrameId;
28
+ private readonly CHILD_SCORE_BAND_FRACTION;
29
+ private readonly CHILD_WEIGHT_LABEL_BAND_FRACTION;
30
+ private readonly BASE_MAIN_SELECTOR;
31
+ private readonly IDLE_SPHERE_OVERLAY_ROLE;
32
+ private readonly MIN_VISIBLE_ARC_ANGLE_RAD;
33
+ private pulseAnimationFrameId;
34
+ private outerSparkTimer;
35
+ private outerSparkStartTimeMs;
36
+ private outerSparkPhaseMs;
37
+ private outerSparkRotationDurationMs;
38
+ private outerSparkCircumference;
39
+ private outerSparkLastAngleRad;
40
+ private balanceSparkOnComplete;
41
+ private readonly balanceSparkInstances;
42
+ private balanceSparkTadpoleIdSeq;
43
+ private readonly balanceSparkTadpoles;
44
+ private balanceSparkTadpoleTimer;
45
+ /** Cruising speed (px/ms) and cutoff (as a multiple of the main circle's
46
+ * radius) for the lone-survivor drift — see the fragment block in
47
+ * startBalanceSparkAnimation's timer. */
48
+ private readonly BALANCE_SPARK_DRIFT_SPEED_PX_PER_MS;
49
+ private readonly BALANCE_SPARK_DRIFT_INITIAL_SPEED_PX_PER_MS;
50
+ private readonly BALANCE_SPARK_DRIFT_SPEED_EASE_MS;
51
+ private readonly BALANCE_SPARK_DRIFT_TURN_RATE_RAD_PER_MS;
52
+ private readonly BALANCE_SPARK_DRIFT_MAX_FRAGMENTS;
53
+ private readonly BALANCE_SPARK_DRIFT_FAN_SPREAD_RAD;
54
+ private readonly BALANCE_SPARK_SWIM_WAVE_AMPLITUDE_PX;
55
+ private readonly BALANCE_SPARK_SWIM_WAVE_FREQUENCY;
56
+ private readonly BALANCE_SPARK_SWIM_WAVE_SPEED;
57
+ private readonly BALANCE_SPARK_SWIM_WAVE_TAPER;
58
+ /** How long the "consuming" phase lasts once both sparks reach their
59
+ * shared meeting point — see triggerBalanceSpark / the consume branch
60
+ * in startBalanceSparkAnimation's timer. */
61
+ private readonly BALANCE_SPARK_CONSUME_DURATION_MS;
62
+ private readonly BALANCE_SPARK_EMBER_COUNT;
63
+ private readonly BALANCE_SPARK_EMBER_ANGLE_OFFSETS;
64
+ private readonly BALANCE_SPARK_EMBER_DISTANCE_OFFSETS;
65
+ private readonly BALANCE_SPARK_EMBER_PHASE_OFFSETS;
66
+ private activeOddId;
67
+ private hoverOddId;
68
+ /** Callback déclenché quand la souris entre/sort d'un arc ODD (lien inverse
69
+ * vers les chips de la légende ODD du panneau de détail). */
70
+ private _onOddArcHoverCb;
71
+ private pulseStates;
14
72
  constructor(ViewInitializer: ViewInitializer);
15
73
  /**
16
74
  * Initializes all the necessary properties from the ViewInitializer.
@@ -21,10 +79,122 @@ export declare class YinYangDrawer {
21
79
  * Creates the full Yin Yang pattern, including the main circle, semi-circles, and the Yin Yang symbols.
22
80
  */
23
81
  createYinYangPattern(): void;
82
+ refreshOuterSpark(): void;
83
+ /**
84
+ * Draws the ghost Yin/Yang symbols using the ghost groups and tension.
85
+ */
86
+ drawGhostYinYangSymbols(): void;
87
+ /**
88
+ * Ensures ghost rendering can work in update-only flows (without explicit initialize call).
89
+ */
90
+ private ensureGhostRenderContext;
24
91
  /**
25
92
  * Draws the main outer circle representing the Yin Yang.
26
93
  */
27
94
  drawMainCircle(): void;
95
+ /**
96
+ * Draws and animates a short dash over a full circular stroke.
97
+ *
98
+ * Illusion principle:
99
+ * - stroke-dasharray is configured as: "visibleDash hiddenRemainder"
100
+ * - so only a tiny segment is visible while the rest of the circumference is hidden
101
+ * - changing stroke-dashoffset each frame shifts this dash around the perimeter,
102
+ * creating a rotating spark without moving the circle itself.
103
+ */
104
+ private startOuterSparkAnimation;
105
+ private clearOuterSpark;
106
+ private normalizeAngle;
107
+ private getOrCreateSparkLayer;
108
+ /**
109
+ * Triggers the "étincelle d'équilibre": two one-shot sparks running at
110
+ * once, each tied to one orientation — the requested one (primary) and
111
+ * its inverse (the other pole) — unlike the perimeter spark which loops
112
+ * forever and ignores orientation.
113
+ *
114
+ * Each spark starts on the main circle at the tangent point of the
115
+ * OPPOSITE lobe, sweeps half the main circle's circumference to reach its
116
+ * own lobe's tangent point, then switches to the sub-circle for the
117
+ * second half. Because internally tangent circles share the same tangent
118
+ * line at their contact point, and both halves are swept in the same
119
+ * rotational sense, the direction is continuous across the switch — no
120
+ * visible kink. Primary and inverse therefore sweep opposite sides of the
121
+ * main circle (mirrored), each stopping once it has covered half of its
122
+ * own sub-circle's circumference.
123
+ *
124
+ * `orientation` must be the CURRENT FACETTE'S OWN orientation, i.e. its
125
+ * dominant lobe. Everything downstream calibrates on the primary spark
126
+ * and assumes it is the longer of the two: the tail growth rate (so the
127
+ * primary reaches exactly balanceSparkMaxLength) and the shared shrink
128
+ * rate (so the primary burns out exactly at CONSUME_DURATION_MS, the
129
+ * lifetime cutoff). Pass the recessive lobe instead and the inverse
130
+ * spark outlives that cutoff, getting killed the instant its sibling
131
+ * dies — before it can shed a single tadpole.
132
+ *
133
+ * `onComplete`, if given, fires exactly once, the instant both the
134
+ * primary and inverse sparks have naturally finished (tail burnt out AND
135
+ * every shed tadpole drifted away) — see clearBalanceSparkInstance. Lets
136
+ * a caller repeat the whole animation back-to-back (e.g. the dev button,
137
+ * which loops it for as long as it stays toggled on) without needing to
138
+ * know its total duration, which varies with the current facette's own
139
+ * geometry.
140
+ */
141
+ triggerBalanceSpark(orientation?: "yin" | "yang", onComplete?: () => void): void;
142
+ /**
143
+ * Geometry shared by a balance spark's setup and its per-frame animation:
144
+ * the main-circle radius, its sub-circle's center/radius, the angle where
145
+ * the two are tangent, and the arc lengths of each half-lap.
146
+ */
147
+ private computeBalanceSparkPathGeometry;
148
+ /**
149
+ * Builds a single closed SVG path (absolute coordinates — no `transform`
150
+ * needed) for a tadpole/sperm-shaped tail: pointed at the tail end,
151
+ * swelling to `maxHalfWidth` at the head. `segments` must be ordered
152
+ * tail-to-head and can span two different circles (the sub-circle for
153
+ * the near part of the tail and the main circle for whatever hasn't fit
154
+ * there yet — see the overflow handling in the timer below); the taper
155
+ * is parameterized across all of them combined so the width stays
156
+ * continuous at the seam between circles.
157
+ */
158
+ private buildTaperedSparkPath;
159
+ /**
160
+ * Same tadpole silhouette as buildTaperedSparkPath (thin tail, swollen
161
+ * head), but swimming along a straight line instead of a circular arc —
162
+ * used for the lone-survivor drift. The head tracks `(headX, headY)`
163
+ * exactly; the tail whips side to side in a traveling sine wave whose
164
+ * amplitude fades to zero at the head, so only the tail visibly
165
+ * undulates while the head cuts a clean straight path, like a real
166
+ * tadpole swimming.
167
+ */
168
+ private buildSwimmingTadpolePath;
169
+ /**
170
+ * Ember shower at a frozen contact point: each ember flies outward
171
+ * along its own fixed direction and fades out, looping on its own
172
+ * short, staggered cycle — a simple stand-in for a welding-spark
173
+ * crackle. Shared by both places a balance spark sits still and
174
+ * "welds" against its own shrinking tail: the death-race meeting point
175
+ * and, once a survivor arrives there, the tangent point (see the
176
+ * "racing"/"shedding" branches in startBalanceSparkAnimation's timer).
177
+ */
178
+ private renderBalanceSparkEmbers;
179
+ private startBalanceSparkAnimation;
180
+ private clearBalanceSparkInstance;
181
+ private getOrCreateBalanceSparkLayer;
182
+ private getOrCreateBalanceSparkTadpoleLayer;
183
+ /**
184
+ * Detaches one shed batch of tadpoles from the instance that produced
185
+ * them (see the "shedding" branch in startBalanceSparkAnimation's timer)
186
+ * so they go on drifting under their own, independent clock — see
187
+ * balanceSparkTadpoles' own doc comment for why.
188
+ */
189
+ private spawnBalanceSparkTadpoles;
190
+ /** Ticks every still-drifting tadpole from every past shed batch — see
191
+ * balanceSparkTadpoles' own doc comment — independently of whichever
192
+ * spark instance(s) happen to be running right now, including none at
193
+ * all. Tadpoles never expire — once spawned they keep swimming
194
+ * (cruising speed, steering toward the shared target) indefinitely, so
195
+ * in practice this timer runs for as long as any have ever been
196
+ * shed. */
197
+ private tickBalanceSparkTadpoles;
28
198
  /**
29
199
  * Draws a semi-circle (black half) within the main Yin Yang circle.
30
200
  */
@@ -32,10 +202,17 @@ export declare class YinYangDrawer {
32
202
  /**
33
203
  * Loops through both Yin and Yang groups and draws the respective symbols (small circles).
34
204
  */
35
- drawYinYangSymbols(yinOrYangGroup: {
36
- yin: IYinOrYangPattern;
37
- yang: IYinOrYangPattern;
38
- }): void;
205
+ drawYinYangSymbols(): void;
206
+ /**
207
+ * Mode idle (racine, aucune facette sélectionnée) : le cercle principal
208
+ * n'a aucune donnée pertinente à montrer (le yin/yang de base reste sur
209
+ * la composante 0 par défaut — voir ViewInitializer.currentComponentIndex
210
+ * — ce qui affiche une répartition arbitraire tant que rien n'a vraiment
211
+ * été sélectionné). On masque donc les deux lobes et on affiche à la
212
+ * place une illustration neutre (Homme de Vitruve), retirée dès qu'une
213
+ * facette est activée pour laisser réapparaître le yin/yang réel.
214
+ */
215
+ refreshIdleSphereOverlay(): void;
39
216
  /**
40
217
  * Draws either the Yin or Yang circle based on the type provided.
41
218
  * It also draws the small central circle and the arcs for categories within the symbol.
@@ -44,7 +221,115 @@ export declare class YinYangDrawer {
44
221
  * @param radius - The radius of the symbol (Yin or Yang).
45
222
  * @param type - Whether the symbol is "yin" or "yang".
46
223
  */
47
- drawYinOrYang(centerY: number, radius: number, type: "yin" | "yang"): void;
224
+ private drawYinOrYangWithGroup;
225
+ /**
226
+ * Affiche le score d'état ("40/100") dans l'anneau extérieur de la moitié
227
+ * dominante du satellite — entre le petit cercle "noyau"
228
+ * (smallRadius) et le bord du lobe (radius) — plutôt qu'en son centre
229
+ * exact, pour laisser le noyau visible. La moitié orientée grandit
230
+ * toujours avec le score et occupe au moins 50% du disque, donc reste
231
+ * toujours assez grande pour accueillir le texte. Remplace l'ancienne
232
+ * ligne "état/100" du panneau flottant (ChildScoresDeltaRenderer), qui ne
233
+ * montre plus que les deltas et le poids. Même logique de contraste que
234
+ * le fond (texte noir sur le lobe blanc yang, blanc sur le lobe noir yin)
235
+ * plutôt que le contour cyan des odd-labels, puisque le fond ici est un
236
+ * noir/blanc pur.
237
+ *
238
+ * Décalage vertical plutôt que centroïde d'arc façon odd-labels : un
239
+ * satellite n'a qu'une seule catégorie couvrant tout le lobe (pas de
240
+ * portion à répartir), et yang/yin sont toujours dessinés respectivement
241
+ * au-dessus/en-dessous du centre du satellite (voir centerYang/centerYin
242
+ * dans ViewInitializer) — s'éloigner du centre du satellite le long de cet
243
+ * axe reste donc toujours dans le lobe dominant, à l'opposé de la zone
244
+ * d'enchevêtrement avec l'autre couleur.
245
+ *
246
+ * Taille proportionnelle au rayon RÉEL de ce lobe (et non à
247
+ * labelsFontAutoScale, calibré pour le cercle parent) : un satellite au
248
+ * milieu d'une pile dense peut être minuscule, un autre isolé beaucoup
249
+ * plus grand, et le texte doit suivre dans les deux cas.
250
+ */
251
+ private renderChildStateLabel;
252
+ /**
253
+ * Affiche le poids ("6/10") dans le même lobe dominant que le score, mais
254
+ * sur le rayon OPPOSÉ (score : vers l'extérieur du satellite ; poids :
255
+ * vers son centre) — même mécanique que renderChildStateLabel (taille
256
+ * proportionnelle au rayon réel) pour la même nature visuelle "posée sur
257
+ * le disque", avec deux différences volontaires : la couleur de
258
+ * remplissage (la teinte du poids, reprise du chip des cartes et de l'onde
259
+ * de prévisualisation) pour rester identifiable comme "poids" au premier
260
+ * coup d'œil, et un fond en dégradé radial (pas de liseret) qui calme le
261
+ * lobe sous les chiffres. Même technique que les libellés d'arcs du cercle
262
+ * central (renderOddArcLabels) : cercle rempli d'un radialGradient
263
+ * couleur→transparent, dimensionné sur le bbox du texte.
264
+ *
265
+ * Teinte comme voile suivent le lobe (weightColorForLobe, labelVeilColor) :
266
+ * doré sur fond noir, ambre profond sur fond blanc, où le doré ne tient
267
+ * pas — c'est la même règle que le score, qui lui s'inverse franchement.
268
+ */
269
+ private renderChildWeightLabel;
270
+ /**
271
+ * Affiche le score de la facette courante ("40/100") dans le cercle
272
+ * principal — même règle que renderChildStateLabel au niveau d'un
273
+ * satellite (bande [smallRadius, radius] du lobe dominant, même
274
+ * contraste), maintenant que les arcs ODD n'affichent plus systématiquement
275
+ * cette information (voir buildOddArcLabelLines, réservé à l'ODD
276
+ * sélectionné). Contrairement au satellite, le suffixe "/100" est affiché
277
+ * (même convention que les libellés d'arcs) : au niveau facette il n'y a
278
+ * pas de fratrie pour donner le contexte d'échelle par comparaison.
279
+ */
280
+ private renderFacetteStateLabel;
281
+ /**
282
+ * Affiche le poids de la facette courante ("18/40") dans le cercle
283
+ * principal, sur le rayon opposé au score — même règle que
284
+ * renderChildWeightLabel au niveau d'un satellite (fond en dégradé
285
+ * radial, teinte du poids). Le poids et son plafond sont la somme des
286
+ * poids de tous les ODD de la facette (voir
287
+ * ViewInitializer.getFacetteWeight/getFacetteWeightMax), la même formule
288
+ * que celle qui dimensionne chaque arc.
289
+ */
290
+ private renderFacetteWeightLabel;
291
+ private drawGhostCircle;
292
+ private clearGhostVisuals;
293
+ private getOrCreateGhostMainCircle;
294
+ private getAdaptiveWaveCount;
295
+ private reconcileGhostWaves;
296
+ private updateBasePulseAnimations;
297
+ private updateBasePulseAnimationForType;
298
+ private ensurePulseAnimationLoop;
299
+ private stopPulseAnimationLoop;
300
+ private hasActivePulseAnimations;
301
+ private applyPulseFrame;
302
+ private getGhostWaveColor;
303
+ /**
304
+ * Animates wave rings that continuously travel from the base circle's
305
+ * radius toward the ghost circle's radius, looping. The direction of
306
+ * travel (outward on growth, inward on shrink) matches the sign of the
307
+ * projected change, so motion itself communicates the trend.
308
+ */
309
+ private updateGhostWaveAnimation;
310
+ /**
311
+ * Affiche, sur le lobe qui grandit et à la pointe extérieure de son cercle
312
+ * ghost — là où les ondes convergent —, ce que l'action projetée changerait :
313
+ * le delta de score de la facette, et son delta de poids s'il y en a un.
314
+ * Lecture stable, qui ne dépend pas de l'écart de rayon pour être remarquée.
315
+ *
316
+ * Les deux lignes sont écrites dans le format commun des deltas (Δ signé
317
+ * vert/rouge, triangle doré pour le poids — voir utils/deltaLabels), le même
318
+ * qu'à côté des satellites : c'est la même information à un autre niveau
319
+ * d'agrégation, elle ne doit pas s'écrire autrement. Auparavant ce badge
320
+ * montrait un pourcentage de tension en valeur absolue, une unité qui
321
+ * n'existait nulle part ailleurs et dont le signe était perdu.
322
+ */
323
+ private updateGhostDeltaBadge;
324
+ private ghostDeltaBadgeSelector;
325
+ /** Retire le badge d'un lobe. Nécessaire explicitement depuis que le badge
326
+ * ne vit plus dans le groupe du ghost : il ne disparaît plus avec lui. */
327
+ private removeGhostDeltaBadge;
328
+ private getBaseMainCircle;
329
+ private resetBaseCircle;
330
+ private getCircleNumericAttribute;
331
+ private lerp;
332
+ private shouldRenderGhost;
48
333
  /**
49
334
  * Draws category-specific arcs with gradient fills and masks to represent different ODDs (Sustainable Development Goals).
50
335
  *
@@ -53,5 +338,123 @@ export declare class YinYangDrawer {
53
338
  * @param type - Whether the symbol is "yin" or "yang".
54
339
  * @param smallRadius - The radius of the small circle within the symbol.
55
340
  */
56
- drawCategoryArcs(centerY: number, radius: number, type: "yin" | "yang", smallRadius: number): void;
341
+ drawCategoryArcs(centerY: number, radius: number, type: "yin" | "yang", smallRadius: number, pattern: IYinOrYangPattern, idSuffix: string): void;
342
+ /**
343
+ * Draws the ODD-arc ghost overlay from ViewInitializer.categoriesGhost on
344
+ * both yin and yang: a dashed gold wedge at each ODD's projected boundary
345
+ * (filled wash if it grows, outline only if it shrinks — same asymmetry as
346
+ * BorderDrawer's border-segment ghost / WeightPreviewRingRenderer), plus a
347
+ * traveling wave wedge sweeping between the real and projected boundary.
348
+ * Because buildGhostCategories renormalizes the whole facette, every ODD
349
+ * is redrawn here, not just the one whose own weightDelta actually changed.
350
+ * Called after every drawYinYangSymbols (so it always reflects the latest
351
+ * base arcs) and standalone from ViewInitializer.updateCategoryGhost for
352
+ * weight-only preview updates.
353
+ */
354
+ drawCategoryArcsGhost(): void;
355
+ private drawCategoryArcsGhostForType;
356
+ private drawCategoryArcGhostShapes;
357
+ private ensureCategoryArcGhostLoop;
358
+ private stopCategoryArcGhostLoop;
359
+ /** Traveling wave that sweeps each ODD's boundary angle between its real
360
+ * position and its projected ghost position, on the same lap
361
+ * duration/easing as the border-segment ghost's wave (see
362
+ * BorderDrawer.updateBorderGhostWave and
363
+ * ViewInitializer.waveTravelDurationSec). Re-queries the layer node each
364
+ * frame instead of caching it, since a full redraw (clearGroupChildren)
365
+ * can replace it between frames. */
366
+ private updateCategoryArcGhostWave;
367
+ /**
368
+ * Les libellés d'arcs ne s'affichent qu'une facette ouverte. Au repos
369
+ * (aucune facette activée), le cercle central n'est plus qu'une vue
370
+ * d'ensemble de la sphère : le score et le poids d'un ODD n'y répondent à
371
+ * aucune question posée, et n'y font qu'encombrer les arcs.
372
+ *
373
+ * L'état est lu en direct plutôt que figé au dessin : la bascule repos ↔
374
+ * facette ouverte ne touche pas la géométrie, donc ne relance pas
375
+ * forcément drawCategoryArcs — c'est YinYangController.setChildrenEnabled
376
+ * qui demande le rafraîchissement au bon moment.
377
+ */
378
+ private shouldShowOddArcLabels;
379
+ /**
380
+ * Le score/poids de la facette (renderFacetteStateLabel/WeightLabel)
381
+ * cède la place à ceux de l'ODD sélectionné (buildOddArcLabelLines) : les
382
+ * deux répondraient à la même question au même endroit du cercle sinon.
383
+ */
384
+ private shouldShowFacetteLabels;
385
+ refreshOddArcLabelsVisibility(): void;
386
+ refreshOddArcLabelsOrientation(): void;
387
+ private isArcVisibleForLabel;
388
+ private renderOddArcLabels;
389
+ private applyOddArcLabelCounterRotation;
390
+ private getRotationAngleFromTransform;
391
+ private getDisplayedArcAngles;
392
+ /**
393
+ * Ajuste la plaque sombre posée derrière un libellé d'arc à la taille
394
+ * réelle de son texte : un disque quand il est court, une ellipse quand il
395
+ * s'allonge. Appelée après chaque écriture des lignes, rendu complet comme
396
+ * rafraîchissement — la bascule compact ↔ nom + métriques change le nombre
397
+ * de lignes, et une plaque figée laisserait le texte déborder dans le vide.
398
+ *
399
+ * La forme est choisie à chaque passage, et l'ancienne retirée quelle que
400
+ * soit sa nature : sans cela un libellé passé une fois en ellipse ne
401
+ * pouvait plus jamais redevenir disque, et empilait une ellipse de plus à
402
+ * chaque rendu.
403
+ */
404
+ private sizeOddArcLabelBackground;
405
+ /**
406
+ * (Ré)écrit les lignes d'un libellé d'arc ODD dans son `<text>` — partagé
407
+ * par le rendu complet (renderOddArcLabels) et par le rafraîchissement
408
+ * (refreshOddArcLabelsVisibility), qui reconstruit lui aussi les tspans et
409
+ * passe APRÈS : deux écritures divergentes et le second effacerait
410
+ * silencieusement ce que le premier a posé.
411
+ */
412
+ private writeOddArcLabelText;
413
+ /**
414
+ * Lignes du libellé posé sur un arc ODD du cercle central : le score de
415
+ * l'ODD puis son poids, tous deux suivis de leur maximum — à la place du
416
+ * numéro d'ODD ("ODD16") qui occupait seul cette place. Ce numéro se lit
417
+ * dans le panneau de description dès qu'on désigne l'arc, alors que ces
418
+ * deux métriques ne se lisaient nulle part au niveau ODD ; et ce sont les
419
+ * mêmes que celles portées par chaque satellite
420
+ * (renderChildStateLabel/renderChildWeightLabel), donc le cercle central
421
+ * et son cortège se lisent enfin de la même façon.
422
+ *
423
+ * Le maximum du poids n'est PAS WEIGHT_MAX : un ODD pèse la somme des
424
+ * poids de ses satellites (même formule que ViewInitializer.getOddWeight,
425
+ * celle qui fixe l'angle de l'arc — le chiffre affiché explique donc la
426
+ * taille de l'arc sous lui), et son plafond est WEIGHT_MAX × nombre de
427
+ * satellites. Il est affiché tel quel plutôt que ramené à une échelle /10
428
+ * qui n'existe qu'au niveau satellite.
429
+ *
430
+ * En mode non compact, le nom court de l'ODD reste affiché au-dessus des
431
+ * deux métriques : c'est une information que le numéro ne portait pas.
432
+ */
433
+ private buildOddArcLabelLines;
434
+ /** Nom court de l'ODD, replié sur `oddLabelMaxLines` lignes — n'alimente
435
+ * plus que le mode non compact des libellés d'arcs (voir
436
+ * buildOddArcLabelLines). */
437
+ private wrapOddLabel;
438
+ private wrapTextByLines;
439
+ private getOrCreateInteractiveArcLayer;
440
+ private drawInteractiveArcZone;
441
+ /** Callback appelé quand la souris entre/sort d'un arc ODD du diagramme —
442
+ * permet de surligner en retour le chip correspondant dans la légende ODD. */
443
+ setOnOddArcHover(cb: (oddId: string | null) => void): void;
444
+ /**
445
+ * Sets the active ODD id and refreshes the visual highlight on interactive arc zones.
446
+ */
447
+ getActiveOddId(): string | null;
448
+ setActiveOddId(oddId: string | null): void;
449
+ /**
450
+ * Applique un survol léger sur l'arc ODD depuis l'arbre Recherche.
451
+ * N'affecte pas activeOddId — retiré au mouseleave.
452
+ */
453
+ setHoverOddId(oddId: string | null): void;
454
+ private refreshHoverArcStyles;
455
+ /**
456
+ * Re-applies the highlight / dim styles on all existing interactive arc hit-zones
457
+ * based on the current `activeOddId`, without recreating the DOM elements.
458
+ */
459
+ private refreshInteractiveArcStyles;
57
460
  }
@@ -0,0 +1,37 @@
1
+ export declare const animationConfig: {
2
+ componentSwitchDurationMs: number;
3
+ borderColorCycleDurationMs: number;
4
+ rotationDurationMs: number;
5
+ forceFieldRevealDurationMs: number;
6
+ forceFieldRevealChainStepMs: number;
7
+ /** Fondu de sortie du champ de force d'action ET du champ d'événement quand
8
+ * ils n'ont plus rien à montrer (désélection, menu fermé, switch de
9
+ * facette) — plus court que forceFieldRevealDurationMs : disparaître doit
10
+ * se sentir plus vif qu'apparaître. Partagée par les deux champs pour
11
+ * qu'ils se sentent comme une seule grammaire visuelle. */
12
+ fieldFadeOutDurationMs: number;
13
+ stackItemReflowDurationMs: number;
14
+ /** Cross-fade (fade-out old text / fade-in new text) when the top-left
15
+ * active-facette label text changes on click — independent of the much
16
+ * slower diagram switch animation. */
17
+ componentTitleCrossfadeMs: number;
18
+ /** Menu radial d'actions d'un satellite (déploiement/retrait des boutons) :
19
+ * volontairement bien plus court que componentSwitchDurationMs — c'est un
20
+ * geste local, pas un mouvement d'ensemble du diagramme. */
21
+ childActionMenuOpenDurationMs: number;
22
+ childActionMenuCloseDurationMs: number;
23
+ /** Délai entre le départ de chaque bouton, pour un effet d'éventail en
24
+ * cascade plutôt qu'un bloc synchronisé. */
25
+ childActionMenuStaggerMs: number;
26
+ /** Fondu croisé entre l'illustration idle du cercle principal (Homme de
27
+ * Vitruve) et le yin/yang réel, dans les deux sens — assez lent pour se
28
+ * sentir comme une transition plutôt qu'un clignotement, sans ralentir
29
+ * la sélection d'une facette. */
30
+ idleSphereCrossfadeMs: number;
31
+ /**
32
+ * Minimum opacity for category blend masks when a category is fully faded out.
33
+ * 0 = blend mask fully transparent at end of fade (recommended).
34
+ * Higher values leave residual blending even for disappearing arcs.
35
+ */
36
+ categoryBlendFadeMinOpacity: number;
37
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Source de vérité pour les couleurs de delta partagées entre le diagramme
3
+ * (satellites) et les cartes de description (ForceFieldDetailsPanel).
4
+ *
5
+ * Ces valeurs doivent rester synchronisées avec src/styles/details-panel.css :
6
+ * - SCORE_DELTA_POSITIVE_COLOR == .ff-delta-pos / .ff-card-delta.ff-delta-pos
7
+ * - SCORE_DELTA_NEGATIVE_COLOR == .ff-delta-neg / .ff-card-delta.ff-delta-neg
8
+ * - WEIGHT_DELTA_COLOR == .ff-card-weight-chip
9
+ * (le CSS ne peut pas importer ce fichier — si l'une de ces couleurs change
10
+ * ici, reporter le changement dans details-panel.css, et inversement).
11
+ */
12
+ /** Delta de score positif (amélioration) — même vert que les cartes. */
13
+ export declare const SCORE_DELTA_POSITIVE_COLOR = "#8ef2b4";
14
+ export declare const SCORE_DELTA_POSITIVE_GLOW_COLOR = "rgba(142, 242, 180, 0.9)";
15
+ /** Delta de score négatif (dégradation) — même rouge que les cartes. */
16
+ export declare const SCORE_DELTA_NEGATIVE_COLOR = "#f28e8e";
17
+ export declare const SCORE_DELTA_NEGATIVE_GLOW_COLOR = "rgba(242, 142, 142, 0.9)";
18
+ /**
19
+ * Variantes des deux couleurs de delta pour un fond CLAIR — le noyau blanc du
20
+ * lobe yin, quand c'est lui qui accueille le badge du ghost. Mêmes teintes
21
+ * assombries jusqu'à tenir au-delà de 4:1 sur du blanc, là où les versions
22
+ * lumineuses y tournent autour de 1,5:1 (même principe que
23
+ * WEIGHT_DELTA_COLOR_ON_LIGHT). La lueur, elle, s'inverse : blanche autour
24
+ * d'un texte sombre, sans quoi elle le baverait au lieu de le détacher.
25
+ */
26
+ export declare const SCORE_DELTA_POSITIVE_COLOR_ON_LIGHT = "#1f7a45";
27
+ export declare const SCORE_DELTA_NEGATIVE_COLOR_ON_LIGHT = "#a83a33";
28
+ export declare const DELTA_GLOW_ON_LIGHT = "rgba(255, 255, 255, 0.92)";
29
+ /** Couleur (fill + glow) d'un delta de score selon son signe — seule règle
30
+ * conservée : un palier "doré" existait auparavant au-delà d'un certain
31
+ * gain (getForceFieldColors), volontairement abandonné ici. Un delta
32
+ * positif est toujours vert, quelle que soit son ampleur. `onLight` bascule
33
+ * sur les variantes sombres, pour un fond clair. */
34
+ export declare function scoreDeltaColor(delta: number, onLight?: boolean): {
35
+ fill: string;
36
+ glow: string;
37
+ };
38
+ /** Delta de poids (importance d'un satellite dans sa facette) — même doré que
39
+ * le chip des cartes et que le ghost/onde de prévisualisation du poids
40
+ * (WeightPreviewRingRenderer). Toujours cette seule couleur, quel que soit
41
+ * le signe — le sens est porté par la flèche ▲/▼, pas par la couleur : un
42
+ * poids en baisse n'est pas forcément un problème pour le joueur. */
43
+ export declare const WEIGHT_DELTA_COLOR = "#f5c860";
44
+ export declare const WEIGHT_DELTA_GLOW_COLOR = "rgba(245, 200, 96, 0.9)";
45
+ /**
46
+ * Variante du doré pour un fond CLAIR — le lobe blanc yang, seul endroit où
47
+ * du poids se pose sur autre chose que du sombre. Le doré lumineux n'y offre
48
+ * que 1,6:1 de contraste (il faut 3:1 pour du gros texte), cet ambre profond
49
+ * en donne 3,9 tout en restant lisible comme "la couleur du poids" : c'est
50
+ * la même teinte, assombrie, pas une autre couleur. Aucune couleur unique ne
51
+ * peut faire les deux — au-dessus d'une certaine luminance, le blanc gagne
52
+ * toujours.
53
+ *
54
+ * Diagramme uniquement : les chips des cartes et le ghost de prévisualisation
55
+ * sont toujours sur fond sombre et gardent WEIGHT_DELTA_COLOR (et donc
56
+ * .ff-card-weight-chip n'a pas d'équivalent à synchroniser côté CSS).
57
+ */
58
+ export declare const WEIGHT_DELTA_COLOR_ON_LIGHT = "#b06f00";
59
+ /** Couleur du poids selon le lobe qui l'accueille. */
60
+ export declare function weightColorForLobe(orientation: "yin" | "yang"): string;
61
+ /** Plancher appliqué à un poids projeté (weightDelta) pour éviter un ghost
62
+ * de taille/angle nulle ou négative — partagé par tous les ghosts de poids
63
+ * (satellites, arcs de bordure) pour rester cohérents entre eux. */
64
+ export declare const MIN_PROJECTED_WEIGHT = 0.1;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Centralized label styling configuration for hierarchy-based visual differentiation.
3
+ *
4
+ * Design principle: Styles reflect label importance and type for better visual hierarchy.
5
+ * All colors and effects maintain coherence with the app's cyan accent and glow aesthetic.
6
+ */
7
+ export declare const LABEL_STYLE_HIERARCHY: {
8
+ COMPONENT_TITLE: {
9
+ fillColor: string;
10
+ glowColor: string;
11
+ glowIntensity: string;
12
+ fontWeight: number;
13
+ textShadow: string;
14
+ };
15
+ ODD_LABELS: {
16
+ fillColor: string;
17
+ strokeColor: string;
18
+ strokeWidth: string;
19
+ glowColor: string;
20
+ fontWeight: number;
21
+ textShadow: string;
22
+ };
23
+ CHILD_LABELS: {
24
+ fillColor: string;
25
+ strokeColor: string;
26
+ fontWeight: number;
27
+ textShadow: string;
28
+ };
29
+ COMPONENT_LEGEND: {
30
+ fillColorInactive: string;
31
+ fillColorActive: string;
32
+ glowColorActive: string;
33
+ fontWeight: number;
34
+ textShadowActive: string;
35
+ };
36
+ CHILD_DELTA: {
37
+ fillColor: string;
38
+ fontWeight: number;
39
+ };
40
+ ACTION_LABELS: {
41
+ fillColor: string;
42
+ strokeColor: string;
43
+ fontWeight: number;
44
+ textShadow: string;
45
+ };
46
+ };
47
+ /**
48
+ * Helper to get concrete text-shadow values from config keys
49
+ */
50
+ export declare const getTextShadowValue: (shadowType: string, textShadowEnabled?: boolean) => string;
@@ -1,3 +1,13 @@
1
- export declare const categoryColorNames: {
2
- [key: string]: string;
1
+ /**
2
+ * Cause format: ODD (Sustainable Development Goals) colors
3
+ * Hex values for proper RGB interpolation during animations
4
+ */
5
+ export declare const causeOddColors: {
6
+ [oddId: string]: string;
3
7
  };
8
+ /**
9
+ * Cause format: component colors (fallback palette for unknown components)
10
+ * Hex values for proper RGB interpolation during animations
11
+ */
12
+ export declare const causeComponentColors: string[];
13
+ export declare function getCauseNamedColor(key: string): string;