@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
@@ -0,0 +1,83 @@
1
+ import { IYinYangController } from "../../../interfaces/IYinYangController";
2
+ import { IYinYangView } from "../../../interfaces/IYinYangView";
3
+ import { type ActionDefinition } from "../../../data/yasuni_actions";
4
+ import { ChildrenOrbitEntry } from "../utils/childrenOrbit";
5
+ import { type Point } from "./radialItemMenu";
6
+ type ChildActionMenuContext = {
7
+ orbitData: ChildrenOrbitEntry[];
8
+ activeActionMenuChildKey: string | null;
9
+ activeActionMenuActionId: string | null;
10
+ childActionMenuItemDistance: number;
11
+ childActionMenuItemScale: number;
12
+ childActionIconFontScale: number;
13
+ /** Actions filtrées pour le cutting actif (depuis yasuniCause + actionMap). */
14
+ actionsForActiveChild: ActionDefinition[];
15
+ };
16
+ export declare class ChildActionMenuController {
17
+ private readonly view;
18
+ private readonly getChildRenderedCenter;
19
+ private readonly refreshActionStackVisual;
20
+ /** Aperçu — 1er clic sur une action : affiche sa carte, ne joue rien. */
21
+ private readonly activateActionFromChild;
22
+ /** Validation — 2e clic sur l'action déjà en aperçu : l'ajoute à la pile. */
23
+ private readonly validateActionSelection;
24
+ /** Glisser une bulle vers une position précise de la pile — cœur partagé
25
+ * avec le glisser-déposer depuis la hiérarchie RECHERCHE et depuis le
26
+ * menu des Événements Clés (voir SearchPanelSynchronizer.startActionDrag,
27
+ * entièrement générique). Retourne une poignée d'annulation, conservée
28
+ * dans activeDragCancel. */
29
+ private readonly startActionDrag;
30
+ private childActionMenuGroup;
31
+ /** Groupe séparé pour les traits satellite → bulle, repositionné à chaque
32
+ * update() juste devant children-group (voir ensureLineLayer) pour
33
+ * qu'ils s'affichent derrière les satellites plutôt que par-dessus —
34
+ * même technique que CoreEventMenuController.coreEventLineLayer. */
35
+ private childActionLineLayer;
36
+ private lastChildActionMenuSourceCenter;
37
+ /** Poignée d'annulation du glisser-déposer en cours (voir startActionDrag),
38
+ * ou null si aucun glisser n'est en cours. */
39
+ private activeDragCancel;
40
+ /** Bulle survolée — sert uniquement à allumer l'impulsion de son lien
41
+ * (voir linkStyleGetter) ; l'aperçu reste porté par le contexte. */
42
+ private hoveredActionId;
43
+ /** Résolveur de style des liens, reconstruit à chaque update() (il capture
44
+ * le contexte courant) et rejoué au survol : rallumer une impulsion ne
45
+ * doit pas relancer la chorégraphie d'ouverture. */
46
+ private linkStyleGetter;
47
+ constructor(view: IYinYangView, getChildRenderedCenter: (childController: IYinYangController) => Point | null, refreshActionStackVisual: () => void,
48
+ /** Aperçu — 1er clic sur une action : affiche sa carte, ne joue rien. */
49
+ activateActionFromChild: (childController: IYinYangController, actionId: string, label?: string) => void,
50
+ /** Validation — 2e clic sur l'action déjà en aperçu : l'ajoute à la pile. */
51
+ validateActionSelection: (childController: IYinYangController, actionId: string, label: string, insertAtVisibleIndex?: number | null, entryOrigin?: Point) => void,
52
+ /** Glisser une bulle vers une position précise de la pile — cœur partagé
53
+ * avec le glisser-déposer depuis la hiérarchie RECHERCHE et depuis le
54
+ * menu des Événements Clés (voir SearchPanelSynchronizer.startActionDrag,
55
+ * entièrement générique). Retourne une poignée d'annulation, conservée
56
+ * dans activeDragCancel. */
57
+ startActionDrag: (iconUrl: string, startEvent: PointerEvent, sourceEl: Element, onDrop: (insertAtVisibleIndex: number | null, entryOrigin: Point) => void) => () => void);
58
+ /** Callback déclenché quand la souris entre/sort d'un cercle d'action dans le menu radial. */
59
+ private _onActionHoverCb?;
60
+ setOnActionHover(cb: (actionId: string | null, activeChildKey: string) => void): void;
61
+ /**
62
+ * Applique un highlight de survol sur le cercle d'action identifié, ou le retire si null.
63
+ * Le highlight est posé sur le <g> wrapper (pas sur le <circle>) pour éviter le conflit
64
+ * avec le filtre inline déjà présent sur le cercle.
65
+ */
66
+ highlightActionById(actionId: string | null): void;
67
+ /** Restyle les seuls liens (impulsion allumée/éteinte), sans toucher à leur
68
+ * géométrie : le survol ne doit rien réanimer. */
69
+ private setHoveredAction;
70
+ retractAndDispose(): void;
71
+ clearImmediately(): void;
72
+ /**
73
+ * Starts the fold-out animation on the visible action buttons, then removes
74
+ * the group once every item has finished animating. Nulls the group
75
+ * reference immediately so that any synchronous clearImmediately() call
76
+ * that follows is a no-op and won't cancel the in-flight transition.
77
+ */
78
+ animateClose(): void;
79
+ update(context: ChildActionMenuContext): void;
80
+ private ensureChildActionMenuGroup;
81
+ private ensureLineLayer;
82
+ }
83
+ export {};
@@ -0,0 +1,90 @@
1
+ import { IYinYangView } from "../../../interfaces/IYinYangView";
2
+ import type { KeyEventDefinition } from "../../../data/yasuni_key_events";
3
+ import { type Point } from "./radialItemMenu";
4
+ type CoreEventMenuContext = {
5
+ /** null/false ferme le menu (même logique que activeActionMenuChildKey côté satellite). */
6
+ isOpen: boolean;
7
+ keyEventsForActiveFacette: KeyEventDefinition[];
8
+ /** Id de l'événement actuellement en aperçu (carte affichée), ou null. */
9
+ activeKeyEventId: string | null;
10
+ /** Même valeur que le contrôle "Distance boutons" du groupe "Grappe Actions"
11
+ * (state.menu.itemDistance) — pas de contrôle dédié : voir le calcul de
12
+ * offsetDistance plus bas, qui la réutilise telle quelle. */
13
+ childActionMenuItemDistance: number;
14
+ /** Distance centre du cercle central → satellites de la facette active
15
+ * (moyenne des rayons d'orbite réels, cf. buildChildrenOrbitData) — le
16
+ * "connu" dont dérive offsetDistance plus bas. */
17
+ childrenOrbitRadius: number;
18
+ /** Taille des bulles — contrôle dédié "Taille boutons" du groupe
19
+ * "Événements clés" (state.menu.coreEventMenuItemScale) : les Événements
20
+ * Clés se dimensionnent indépendamment des boutons d'action des
21
+ * satellites. */
22
+ coreEventMenuItemScale: number;
23
+ coreEventIconFontScale: number;
24
+ /** Ajustement fin (px) de la distance des boutons, de part et d'autre de
25
+ * la longueur auto-calculée — contrôle "Distance boutons" du groupe
26
+ * "Événements clés", voir state.menu.coreEventMenuDistanceOffset. */
27
+ coreEventMenuDistanceOffset: number;
28
+ };
29
+ /**
30
+ * Menu radial des Événements Clés, déclenché par un clic sur le cercle
31
+ * central (yin-yang) de la facette active — même langage visuel et même
32
+ * sémantique de clic que ChildActionMenuController (bulles + icône,
33
+ * chorégraphie D3 en éventail, 1er clic = aperçu / 2e clic sur l'item déjà
34
+ * actif = validation — factorisées dans radialItemMenu.ts), mais ancré sur
35
+ * le centre fixe de la facette plutôt que sur un satellite, et déployé à
36
+ * angle égal sur 360° plutôt que sur un axe source.
37
+ */
38
+ export declare class CoreEventMenuController {
39
+ private readonly view;
40
+ /** Aperçu — 1er clic sur un événement : affiche sa carte, ne joue rien. */
41
+ private readonly previewKeyEvent;
42
+ /** Validation — 2e clic sur l'événement déjà en aperçu : l'ajoute à la pile
43
+ * (au sommet par défaut, ou à insertAtVisibleIndex si fourni par un
44
+ * glisser-déposer — voir startKeyEventDrag). */
45
+ private readonly validateKeyEventSelection;
46
+ /** Glisser une bulle vers une position précise de la pile — même cœur
47
+ * partagé que ChildActionMenuController.startActionDrag (voir
48
+ * SearchPanelSynchronizer.startActionDrag, entièrement générique).
49
+ * Retourne une poignée d'annulation, conservée dans activeDragCancel. */
50
+ private readonly startKeyEventDrag;
51
+ private coreEventMenuGroup;
52
+ /** Groupe séparé pour les traits centre → bulle, abaissé au fond du
53
+ * z-order (voir ensureLineLayer) pour qu'ils s'affichent derrière le
54
+ * disque central et les satellites plutôt que par-dessus. */
55
+ private coreEventLineLayer;
56
+ /** Poignée d'annulation du glisser-déposer en cours (voir startKeyEventDrag),
57
+ * ou null si aucun glisser n'est en cours — même rôle que
58
+ * ChildActionMenuController.activeDragCancel. */
59
+ private activeDragCancel;
60
+ /** Bulle actuellement survolée — sert uniquement à allumer l'impulsion de
61
+ * son lien (voir linkStyleGetter) ; l'état d'aperçu, lui, reste porté par
62
+ * context.activeKeyEventId. */
63
+ private hoveredKeyEventId;
64
+ /** Résolveur de style des liens, reconstruit à chaque update() (il capture
65
+ * le contexte courant) et rejoué tel quel au survol : rallumer une
66
+ * impulsion ne doit pas relancer toute la chorégraphie. */
67
+ private linkStyleGetter;
68
+ constructor(view: IYinYangView,
69
+ /** Aperçu — 1er clic sur un événement : affiche sa carte, ne joue rien. */
70
+ previewKeyEvent: (keyEvent: KeyEventDefinition) => void,
71
+ /** Validation — 2e clic sur l'événement déjà en aperçu : l'ajoute à la pile
72
+ * (au sommet par défaut, ou à insertAtVisibleIndex si fourni par un
73
+ * glisser-déposer — voir startKeyEventDrag). */
74
+ validateKeyEventSelection: (keyEvent: KeyEventDefinition, insertAtVisibleIndex?: number | null, entryOrigin?: Point) => void,
75
+ /** Glisser une bulle vers une position précise de la pile — même cœur
76
+ * partagé que ChildActionMenuController.startActionDrag (voir
77
+ * SearchPanelSynchronizer.startActionDrag, entièrement générique).
78
+ * Retourne une poignée d'annulation, conservée dans activeDragCancel. */
79
+ startKeyEventDrag: (iconUrl: string, startEvent: PointerEvent, sourceEl: Element, onDrop: (insertAtVisibleIndex: number | null, entryOrigin: Point) => void) => () => void);
80
+ private _onHoverCb?;
81
+ setOnHover(cb: (keyEventId: string | null) => void): void;
82
+ clearImmediately(): void;
83
+ update(context: CoreEventMenuContext): void;
84
+ /** Restyle les seuls liens (impulsion allumée/éteinte) sans toucher à leur
85
+ * géométrie : le survol ne doit rien réanimer. */
86
+ private setHoveredKeyEvent;
87
+ private ensureGroup;
88
+ private ensureLineLayer;
89
+ }
90
+ export {};
@@ -0,0 +1,117 @@
1
+ import * as d3 from "d3";
2
+ export type Point = {
3
+ x: number;
4
+ y: number;
5
+ };
6
+ export type FanLayoutMode = "arc" | "full-circle";
7
+ export type RadialMenuItemPosition<T> = {
8
+ uniqueKey: string;
9
+ id: string;
10
+ x: number;
11
+ y: number;
12
+ r: number;
13
+ item: T;
14
+ };
15
+ /**
16
+ * Calcule la position en éventail de chaque item autour d'un centre.
17
+ * - mode "arc" : réparti sur ±maxAngularOffset autour de `axis` (menu
18
+ * satellite — doit respecter un axe "depuis la source").
19
+ * - mode "full-circle" : réparti à angle égal sur les 360° (menu du
20
+ * cercle central — pas d'axe source à respecter, maximise l'écart
21
+ * angulaire entre bulles).
22
+ */
23
+ export declare function computeFanPositions<T extends {
24
+ id: string;
25
+ }>(items: T[], opts: {
26
+ mode: FanLayoutMode;
27
+ center: Point;
28
+ axis: number;
29
+ maxAngularOffset?: number;
30
+ offsetDistance: number;
31
+ itemRadius: number;
32
+ keyPrefix: string;
33
+ }): RadialMenuItemPosition<T>[];
34
+ /**
35
+ * Referme et retire toutes les bulles d'un menu radial (repli en
36
+ * éventail), utilisé à la fois quand update() ne reçoit plus de
37
+ * contexte actif et par les fermetures explicites (animateClose).
38
+ */
39
+ export declare function closeAllRadialItems(groupNode: SVGGElement, dataRole: string, onAllClosed?: () => void): void;
40
+ /**
41
+ * Chorégraphie D3 commune (data join, transition de sortie, création
42
+ * ligne+cercle+image à l'entrée, transition d'ouverture) — le style
43
+ * visuel par item (couleurs, tailles selon état actif) reste à la
44
+ * charge de l'appelant via un `.each()` sur la sélection retournée,
45
+ * les deux menus ayant des palettes distinctes.
46
+ */
47
+ export declare function renderRadialItemsChoreography<T>(params: {
48
+ groupNode: SVGGElement;
49
+ dataRole: string;
50
+ positions: RadialMenuItemPosition<T>[];
51
+ exitAnimationCenter: Point;
52
+ enterCenter: Point;
53
+ getIconUrl: (id: string) => string;
54
+ /** Décor optionnel inséré entre la plaque (cercle) et le pictogramme, sur
55
+ * la seule sélection enter() — c'est par là que le menu des Événements
56
+ * Clés glisse son filigrane Yin/Yang, que le menu des actions n'a pas.
57
+ * Appelé à cet endroit précis pour que le z-order (plaque → décor →
58
+ * icône) tombe juste sans réordonnancement. */
59
+ decorateItemEnter?: (enter: d3.Selection<SVGGElement, RadialMenuItemPosition<T>, SVGGElement, unknown>) => void;
60
+ onItemClick: (item: T) => void;
61
+ onItemHoverEnter?: (item: T) => void;
62
+ onItemHoverLeave?: (item: T) => void;
63
+ }): d3.Selection<SVGGElement, RadialMenuItemPosition<T>, SVGGElement, unknown>;
64
+ /** Style d'un lien, résolu par l'appelant depuis son propre état (les deux
65
+ * menus ont des palettes distinctes — ambre pour les Événements Clés,
66
+ * vert/cyan pour les actions — mais la même mise en scène). */
67
+ export type RadialItemLinkStyle = {
68
+ /** Teinte du faisceau. L'alpha du rgba est celui de l'extrémité BULLE :
69
+ * le dégradé se charge de le ramener à zéro côté parent. */
70
+ color: string;
71
+ /** Item en aperçu : faisceau plus large et plus net. */
72
+ isActive?: boolean;
73
+ /** Cœur en pointillés (Événement Clé verrouillé). */
74
+ dashed?: boolean;
75
+ /** Impulsion qui remonte le lien — actif ou survolé uniquement. */
76
+ pulse?: boolean;
77
+ };
78
+ /**
79
+ * Applique le style (teinte, épaisseur, pointillés, impulsion) sur les liens
80
+ * déjà en place, SANS toucher à leur géométrie ni relancer d'animation
81
+ * d'ouverture. Séparé du rendu pour que le survol d'une bulle puisse
82
+ * rallumer l'impulsion de son seul lien à coût nul.
83
+ */
84
+ export declare function styleRadialItemLinks<T extends {
85
+ id: string;
86
+ }>(groupNode: SVGGElement | null, dataRole: string, getStyle: (d: RadialMenuItemPosition<T>) => RadialItemLinkStyle): void;
87
+ /**
88
+ * Chorégraphie D3 des liens parent → item, rendus dans un groupe SÉPARÉ de
89
+ * celui des bulles (voir CoreEventMenuController : ce groupe est glissé plus
90
+ * tôt dans le z-order pour passer derrière les satellites et le disque
91
+ * central, alors que les bulles restent devant dans leur propre groupe).
92
+ * Anime directement x2/y2 des lignes : un lien entrant grandit depuis le bord
93
+ * du parent vers l'item, un lien sortant s'y rétracte puis disparaît — même
94
+ * easing/cascade que renderRadialItemsChoreography pour rester visuellement
95
+ * synchronisé avec les bulles. `positions: []` ferme tous les liens existants
96
+ * (repli complet), sans qu'un appelant ait besoin d'une fonction dédiée.
97
+ */
98
+ export declare function renderRadialItemLinks<T extends {
99
+ id: string;
100
+ }>(params: {
101
+ groupNode: SVGGElement;
102
+ dataRole: string;
103
+ positions: RadialMenuItemPosition<T>[];
104
+ center: Point;
105
+ /** Rayon du disque parent : le faisceau démarre à son bord. 0 = au centre. */
106
+ startRadius?: number;
107
+ getStyle?: (d: RadialMenuItemPosition<T>) => RadialItemLinkStyle;
108
+ }): void;
109
+ /**
110
+ * Sémantique de clic partagée par les deux menus radiaux : un premier
111
+ * clic sur un item prévisualise (affiche sa carte, ne joue rien) ; un
112
+ * second clic sur ce même item déjà actif valide (l'ajoute à la
113
+ * pile). Cliquer un autre item pendant qu'un aperçu est actif bascule
114
+ * l'aperçu sans valider — c'est simplement le cas "isAlreadyActive
115
+ * === false" pour ce nouvel item.
116
+ */
117
+ export declare function handleRadialItemClick(isAlreadyActive: boolean, onPreview: () => void, onValidate: () => void): void;
@@ -0,0 +1,38 @@
1
+ import { IYinYangController } from "../../../interfaces/IYinYangController";
2
+ import { IYinYangView } from "../../../interfaces/IYinYangView";
3
+ import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
4
+ type ChildOddMeta = {
5
+ oddId: string;
6
+ score: number;
7
+ cuttingIndex: number;
8
+ };
9
+ type ChildInteractionControllerDeps = {
10
+ view: IYinYangView;
11
+ getCurrentComponent: () => YinYangInternalModel["components"][number] | null;
12
+ getActiveChildKey: () => string | null;
13
+ setActiveChildKey: (childKey: string | null) => void;
14
+ openActionMenuFromChild: (childController: IYinYangController) => void;
15
+ highlightOddInChildren: (oddId: string | null) => void;
16
+ cancelOddHighlight: () => void;
17
+ syncGhostTensionFromAction: () => void;
18
+ renderGhostDetailsForActiveAction: () => void;
19
+ onUserCuttingClicked?: (childController: IYinYangController) => void;
20
+ /** Appelé quand la souris entre/sort d'un satellite (null = sortie). */
21
+ onChildHover?: (oddId: string | null, cuttingIndex?: number) => void;
22
+ };
23
+ export declare class ChildInteractionController {
24
+ private deps;
25
+ constructor(deps: ChildInteractionControllerDeps);
26
+ setChildrenHiddenState(childControllers: IYinYangController[], hidden: boolean): void;
27
+ attachChildToggleHandler(childController: IYinYangController, childOddMap: Map<string, ChildOddMeta>): void;
28
+ attachChildHoverHandler(childController: IYinYangController, childOddMap: Map<string, ChildOddMeta>): void;
29
+ attachBackgroundClickHandler(): void;
30
+ enterOddView(oddId: string): void;
31
+ exitOddView(): void;
32
+ setActiveChild(childControllers: IYinYangController[], childKey: string): void;
33
+ clearActiveChild(childControllers: IYinYangController[]): void;
34
+ animateChildren(childControllers: IYinYangController[], show: boolean, onBeforeShow?: () => void, onBeforeHide?: () => void): Promise<void>;
35
+ private applyChildActiveStyle;
36
+ private getChildOffset;
37
+ }
38
+ export {};
@@ -0,0 +1,14 @@
1
+ import { IYinYangController } from "../../../interfaces/IYinYangController";
2
+ import { ChildrenOrbitEntry } from "../utils/childrenOrbit";
3
+ export declare class ChildrenOrbitService {
4
+ buildOrbitData(params: {
5
+ parentX: number;
6
+ parentY: number;
7
+ childControllers: IYinYangController[];
8
+ radiusScale: number;
9
+ }): ChildrenOrbitEntry[];
10
+ computeTranslation(orbitEntry: ChildrenOrbitEntry, angleOffset: number): {
11
+ x: number;
12
+ y: number;
13
+ };
14
+ }
@@ -0,0 +1,104 @@
1
+ import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
2
+ import { IYinYangController } from "../../../interfaces/IYinYangController";
3
+ import { ActionStackItem } from "../action-stack/ActionStackController";
4
+ import { KeyEventDefinition } from "../../../data/yasuni_key_events";
5
+ import { EventFieldRenderer, Point } from "./EventFieldRenderer";
6
+ import { YinYangControllerState } from "../state/YinYangControllerState";
7
+ type ChildOddMeta = {
8
+ oddId: string;
9
+ score: number;
10
+ cuttingIndex: number;
11
+ };
12
+ type EventFieldLifecycleControllerDeps = {
13
+ state: YinYangControllerState;
14
+ childOddMap: Map<string, ChildOddMeta>;
15
+ eventFieldRenderer: EventFieldRenderer;
16
+ /** Événements Clés qui pilotent le champ pour la frame courante — aperçu
17
+ * RECHERCHE (un seul) ou items cochés de la pile en mode STACK (plusieurs
18
+ * possibles), déjà réduits par l'appelant à un jeu qui partage UNE source
19
+ * (voir YinYangController.getEventFieldKeyEvents). */
20
+ getActiveKeyEvents: () => KeyEventDefinition[];
21
+ getCurrentComponentOrUndefined: () => YinYangInternalModel["components"][number] | undefined;
22
+ getActiveItems: () => ActionStackItem[];
23
+ getChildControllers: () => IYinYangController[];
24
+ getGlobalGroupNode: () => SVGGElement | null;
25
+ getChildRenderedCenter: (childController: IYinYangController) => Point | null;
26
+ getComponentBorderTargetPoint: (componentId: string) => Point | null;
27
+ getEventFieldSource: () => {
28
+ center: Point | null;
29
+ radius: number;
30
+ };
31
+ };
32
+ /**
33
+ * Cycle de vie du "Champ d'événement" (souffle cœur → satellites de
34
+ * l'Événement Clé en aperçu ou validé) : calcul des cibles, boucle rAF,
35
+ * délégation au dessin. Miroir de ForceFieldLifecycleController pour le
36
+ * champ de force d'action — même découpage état (YinYangControllerState) /
37
+ * orchestration (ici) / dessin (EventFieldRenderer).
38
+ */
39
+ export declare class EventFieldLifecycleController {
40
+ private deps;
41
+ private _targetsCacheKey;
42
+ private _targetsCacheResult;
43
+ constructor(deps: EventFieldLifecycleControllerDeps);
44
+ /**
45
+ * Recalcule les cibles du Champ d'événement et démarre/arrête son animation
46
+ * en conséquence. À appeler à chaque aperçu/validation/ouverture-fermeture
47
+ * du menu radial, à chaque toggle de visibilité de la pile ET à chaque
48
+ * bascule d'onglet RECHERCHE/STACK : la portée elle-même ne dépend pas de la
49
+ * pile, mais l'épaisseur du souffle distingue les satellites réellement
50
+ * amplifiés, qui en dépendent — et en mode STACK ce sont les événements
51
+ * cochés de la pile qui décident du champ tout entier.
52
+ */
53
+ updateEventField(): void;
54
+ /**
55
+ * Recalcule state.eventField.targets pour la frame courante (cache-key
56
+ * gated via getTargets — quasi gratuit quand rien n'a changé) et, s'il n'y
57
+ * a plus rien à montrer, cache le champ. Retourne false dans ce cas (rien
58
+ * à rendre cette frame), true sinon.
59
+ *
60
+ * Appelée aussi bien par updateEventField (points d'entrée explicites :
61
+ * aperçu, toggle pile, fin de switch) que par la boucle rAF de
62
+ * startEventFieldAnimation. Cette dernière en a besoin car
63
+ * currentComponentId peut changer de façon SYNCHRONE bien avant que
64
+ * updateEventField() ne soit rappelé : un clic sur l'arc de facette flippe
65
+ * ViewInitializer.currentComponentIndex dès animateToComponent(), alors
66
+ * que refreshChildrenForComponentSwitch() (qui, lui, rappelle
67
+ * updateEventField pour de vrai) n'arrive qu'~componentSwitchDurationMs
68
+ * plus tard, une fois les satellites de la nouvelle facette créés.
69
+ * getEventFieldSource() (le point de départ) réagit à ce flip dès la frame
70
+ * suivante puisqu'il est relu à chaque frame — sans ce recalcul ici, les
71
+ * CIBLES (donc les points d'arrivée) restaient l'ancien tableau pendant
72
+ * tout le switch : un cordon arc-à-arc incohérent (source déjà "entrant",
73
+ * cible encore l'ancien point distant) jusqu'à la correction tardive.
74
+ */
75
+ private refreshTargets;
76
+ /** Mémoïse buildEventFieldTargets : coûteux à parcourir (yasuniCause en
77
+ * entier) mais dont les entrées pertinentes ne changent qu'avec la
78
+ * facette, les Événements Clés actifs, ou les items de pile qui les
79
+ * concernent — voir buildTargetsCacheKey, concaténée sur tout le jeu. */
80
+ private getTargets;
81
+ /**
82
+ * Union des portées de plusieurs Événements Clés actifs (pile STACK avec
83
+ * plusieurs événements cochés) : un satellite touché par deux d'entre eux
84
+ * ne reçoit qu'UN cordon — le renderer n'a qu'une source, deux cordons
85
+ * superposés se liraient comme un seul, en plus lumineux. On garde le
86
+ * coefficient le plus fort, celui qui décrit l'amplification réellement
87
+ * subie par ce satellite.
88
+ */
89
+ private mergeTargets;
90
+ /** Ne dépend que de ce qui peut réellement changer le résultat : la
91
+ * facette courante, la taille du mapping ODD (proxy de changement de
92
+ * facette, même convention que ForceFieldLifecycleController), et — parmi
93
+ * les items de la pile — seulement ceux liés à CET Événement Clé (son
94
+ * propre item, pour la règle de slotIndex, et les actions qu'il cible).
95
+ * Les autres items de la pile n'influencent pas le champ d'événement. */
96
+ private buildTargetsCacheKey;
97
+ /** Résout les cibles courantes en points écran et pousse la frame au
98
+ * renderer — appelé une fois par updateEventField (état) et à chaque
99
+ * frame par startEventFieldAnimation (orbite + pulsation). */
100
+ private updateEventFieldVisual;
101
+ private startEventFieldAnimation;
102
+ private stopEventFieldAnimation;
103
+ }
104
+ export {};
@@ -0,0 +1,86 @@
1
+ export type Point = {
2
+ x: number;
3
+ y: number;
4
+ };
5
+ /** Segment cœur → cible déjà résolu en points écran — la résolution
6
+ * logique (quel satellite, quelle facette) vit dans eventFieldTargets.ts,
7
+ * ce renderer ne fait que dessiner. */
8
+ export type EventFieldRenderSegment = {
9
+ end: Point;
10
+ /** Bonus de score de l'Événement Clé — pilote l'épaisseur du souffle. */
11
+ scoreBonus: number;
12
+ };
13
+ /**
14
+ * Chorégraphie du "Champ d'événement" : un souffle annulaire. Le cœur du
15
+ * yin/yang (le disque central du lobe de l'orientation courante, pas le
16
+ * centre géométrique du symbole) émet des anneaux concentriques ; chaque
17
+ * anneau allume au passage le segment de cordon qu'il traverse, puis éclate
18
+ * en onde de choc en atteignant le satellite amplifié.
19
+ *
20
+ * Les anneaux sont partagés par toutes les cibles — un seul souffle,
21
+ * plusieurs impacts — ce qui dit visuellement ce que fait un Événement Clé :
22
+ * quelque chose se produit au niveau de la facette et irradie vers les
23
+ * satellites concernés.
24
+ *
25
+ * Volontairement distinct du beam laser rectiligne et continu des champs de
26
+ * force d'action (ForceFieldRenderer) : la distinction porte sur la
27
+ * grammaire temporelle (émission rythmée vs flux continu) autant que sur la
28
+ * teinte, elle survit donc à un changement de palette.
29
+ */
30
+ export declare class EventFieldRenderer {
31
+ private group;
32
+ private rings;
33
+ private layers;
34
+ private coreHalo;
35
+ /** setTimeout id du fondu de sortie en cours, ou null — voir hide(). */
36
+ private _hideTimeoutId;
37
+ /** requestAnimationFrame id de la rétraction géométrique en cours — voir retractGeometry(). */
38
+ private _retractFrameId;
39
+ hasActiveGroup(): boolean;
40
+ ensureElements(params: {
41
+ globalGroupNode: SVGGElement;
42
+ requiredCount: number;
43
+ }): void;
44
+ /**
45
+ * Fondu de sortie : au lieu de retirer le groupe instantanément (clear()),
46
+ * ramène son opacité à 0 sur fieldFadeOutDurationMs puis le retire
47
+ * réellement — même grammaire de disparition que ForceFieldRenderer.hide().
48
+ * Idempotent : un appel alors qu'un fondu est déjà en cours ne fait rien de
49
+ * plus (le clear() en attente suffit).
50
+ */
51
+ hide(): void;
52
+ /**
53
+ * Rétraction géométrique : ramène chaque cordon vers le cœur (ou l'arc de
54
+ * bordure en mode entrant), inverse exact de la croissance animatedEnd de
55
+ * updateVisual (même courbe lue à l'envers : d3.easeCubicOut(1 - t)).
56
+ * Repart de _prevCord — la géométrie du dernier frame réellement rendu —
57
+ * donc indépendante de tout state externe : continue même si la cible
58
+ * (satellite, Événement Clé actif) a déjà disparu entre temps. headAura/
59
+ * headCore suivent la pointe rétractée : leur opacité a un plancher non
60
+ * nul (0.3 / 0.6 × revealEase dans updateVisual, visibles même hors flash
61
+ * d'impact) — les laisser figés à l'ancienne cible les détacherait
62
+ * visiblement du cordon en train de se rétracter sous eux. Seuls les
63
+ * anneaux / capsules / onde de choc restent figés sur leur dernière pose
64
+ * et s'effacent avec le fondu d'opacité du groupe posé par hide().
65
+ */
66
+ private retractGeometry;
67
+ clear(): void;
68
+ updateVisual(params: {
69
+ /** Centre du cœur — disque central du lobe yin OU yang selon
70
+ * l'orientation de la facette courante, jamais le centre du symbole. */
71
+ sourceCenter: Point | null;
72
+ /** Rayon de ce disque : les cordons partent de son bord et les anneaux
73
+ * naissent à sa circonférence, pas d'un point. */
74
+ sourceRadius: number;
75
+ segments: EventFieldRenderSegment[];
76
+ /** Temps écoulé en secondes depuis le début de l'animation. */
77
+ phase: number;
78
+ /** performance.now() du dernier souffle qui a (ré)apparu — undefined ou
79
+ * déjà écoulé = plein régime immédiat. Pilote la croissance cœur → cible
80
+ * à l'apparition, même convention que ForceFieldRenderer (revealStartedAt
81
+ * / revealEase) : sans elle le cordon surgissait déjà à pleine longueur
82
+ * et pleine opacité, contrastant avec le beam d'action qui se dessine
83
+ * progressivement. */
84
+ revealStartedAt?: number;
85
+ }): void;
86
+ }
@@ -0,0 +1,50 @@
1
+ import { type PropagatedActionEdge } from "../../../utils/actionPropagation";
2
+ import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
3
+ import { CauseGraph } from "../../../interfaces/ICauseGraph";
4
+ import { PropagationLayersConfig } from "../../../interfaces/IPropagationLayers";
5
+ import { ChildrenOrbitEntry } from "../utils/childrenOrbit";
6
+ import { ForceFieldLink, ForceFieldLinkDetail } from "./types";
7
+ /** Single source of truth for turning a raw propagation edge into the
8
+ * enriched ForceFieldLinkDetail the UI consumes (rationale, layer
9
+ * summaries…) — shared by build(), buildFromSource(), and any other caller
10
+ * that needs to map edges from computeActionPropagationDetails/
11
+ * propagateForceFieldActionInComponent without re-deriving this metadata. */
12
+ export declare function mapPropagatedEdgeToDetail(edge: PropagatedActionEdge, componentId: string): ForceFieldLinkDetail;
13
+ export declare class ForceFieldComputationService {
14
+ build(params: {
15
+ orbitData: ChildrenOrbitEntry[];
16
+ sourceChildIndex: number;
17
+ childOddMap: Map<string, {
18
+ oddId: string;
19
+ score: number;
20
+ cuttingIndex: number;
21
+ }>;
22
+ component: YinYangInternalModel["components"][number] | null;
23
+ allComponents: YinYangInternalModel["components"] | undefined;
24
+ actionDelta: number;
25
+ actionPropagationFactor: number;
26
+ interconnectionGraph: CauseGraph;
27
+ propagationLayers: PropagationLayersConfig;
28
+ }): {
29
+ links: ForceFieldLink[];
30
+ allLinkDetails: ForceFieldLinkDetail[];
31
+ } | null;
32
+ /**
33
+ * Variante de build() qui accepte directement sourceOddId + sourceCuttingId,
34
+ * sans avoir besoin de orbitData/childOddMap. Utilisée pour les actions
35
+ * cross-composantes où ces structures ne sont pas disponibles.
36
+ */
37
+ buildFromSource(params: {
38
+ sourceOddId: string;
39
+ sourceCuttingId: number | undefined;
40
+ component: YinYangInternalModel["components"][number];
41
+ allComponents: YinYangInternalModel["components"] | undefined;
42
+ actionDelta: number;
43
+ actionPropagationFactor: number;
44
+ interconnectionGraph: CauseGraph;
45
+ propagationLayers: PropagationLayersConfig;
46
+ }): {
47
+ links: ForceFieldLink[];
48
+ allLinkDetails: ForceFieldLinkDetail[];
49
+ } | null;
50
+ }