@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,149 @@
1
+ import { type ActionStackItem } from "./ActionStackState";
2
+ export type { ActionStackItem };
3
+ type Point = {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ export declare class ActionStackController {
8
+ private readonly state;
9
+ private readonly drag;
10
+ private actionStackGroup;
11
+ private stackLayoutMetrics;
12
+ private actionStackLabelFontScale;
13
+ private historyButtonScale;
14
+ private fallingStackItemIds;
15
+ private keyboardListenersBound;
16
+ private _linkedHoverId;
17
+ private _onBlockHighlightCb?;
18
+ private _onSatelliteHighlightCb?;
19
+ private activeComboHighlight;
20
+ /** Highlight temporaire (tag click) — remplacé par activeComboHighlight si actif. */
21
+ private flashComboHighlight;
22
+ private flashComboTimeout;
23
+ private resizeObserver;
24
+ private _cachedNumVisibleSlots;
25
+ private onLocateInSearch?;
26
+ private onSelectSatellite?;
27
+ setOnLocateInSearch(cb: (componentId: string, oddId: string, cuttingIndex: number, actionId: string, kind: "action" | "keyEvent") => void): void;
28
+ setOnSelectSatellite(cb: (componentId: string, oddId: string, cuttingIndex: number, stackItemId: string) => void): void;
29
+ constructor(onActiveItemsChange: (activeItems: ActionStackItem[]) => void, onClear?: () => void);
30
+ setLabelFontScale(scale: number): void;
31
+ setHistoryButtonScale(scale: number): void;
32
+ /**
33
+ * Surligne dans le stack les actions impliquées dans le combo actuellement affiché.
34
+ * Affiche un crochet coloré sur la droite qui délimite la fenêtre temporelle,
35
+ * met en valeur les actions requises (glow) et estompe les autres dans la fenêtre.
36
+ */
37
+ highlightComboContext(params: {
38
+ requires: string[];
39
+ withinLastN?: number;
40
+ type: string;
41
+ comboName?: string;
42
+ sourceItemId?: string;
43
+ deltaMultiplier?: number;
44
+ maxDeltaMultiplier?: number;
45
+ direction?: "forward" | "backward";
46
+ }): void;
47
+ /** Efface le surlignage combo et restaure l'apparence normale du stack. */
48
+ clearComboHighlight(): void;
49
+ setBlockHighlightCallback(cb: (id: string | null) => void): void;
50
+ setSatelliteHighlightCallback(cb: (id: string | null) => void): void;
51
+ getItemById(id: string): ActionStackItem | undefined;
52
+ highlightByStackItemId(id: string | null): void;
53
+ /**
54
+ * Affiche temporairement le highlight combo (cercles de fenêtre) pendant ~1.8 s.
55
+ * Sans effet si un highlight permanent est déjà actif.
56
+ */
57
+ flashComboContext(params: {
58
+ requires: string[];
59
+ withinLastN?: number;
60
+ type: string;
61
+ comboName?: string;
62
+ sourceItemId?: string;
63
+ deltaMultiplier?: number;
64
+ maxDeltaMultiplier?: number;
65
+ direction?: "forward" | "backward";
66
+ }): void;
67
+ /**
68
+ * Brève mise en valeur (flash) d'une action dans le stack par son actionId.
69
+ * Anime l'intégralité de l'élément (icôn + checkbox + libellé) via un glow
70
+ * drop-shadow, sans toucher au mécanisme de combo highlight.
71
+ */
72
+ flashActionById(actionId: string): void;
73
+ canUndo(): boolean;
74
+ canRedo(): boolean;
75
+ undo(): void;
76
+ redo(): void;
77
+ clearAll(): void;
78
+ clearToolRange(): void;
79
+ /**
80
+ * Clears the active selection in the stack without emitting a change event
81
+ * and without recording history. Called when the diagram state is cleared
82
+ * externally (e.g. the user clicks a different child or closes an action menu)
83
+ * so the stack visual stays in sync. Because the deselection is a side-effect
84
+ * of a diagram action — not a direct user action on the stack — it is not
85
+ * added to the undo history.
86
+ */
87
+ deselectAllItems(): void;
88
+ getActionLabel(actionId: string): string | null;
89
+ clickBookmark(index: number): void;
90
+ addItem(item: Omit<ActionStackItem, "id" | "validatedAt" | "slotIndex">, insertAtVisibleIndex?: number | null, entryOrigin?: Point): void;
91
+ /** Insertion à un slot absolu avec décalage systématique — voir
92
+ * ActionStackState.addItemAtSlot. Même remise à zéro de l'aperçu de
93
+ * glisser qu'addItem, pour la même raison (rendu synchrone dans
94
+ * state.addItemAtSlot). */
95
+ addItemAtSlot(item: Omit<ActionStackItem, "id" | "validatedAt" | "slotIndex">, slotIndex: number): ActionStackItem;
96
+ syncActiveAction(sourceChildKey: string | null, actionId: string | null): void;
97
+ setExternalDragActive(active: boolean): void;
98
+ clearDropPreview(): void;
99
+ updateDropPreview(clientX: number, clientY: number, _localY: number): boolean;
100
+ isDropZoneHit(clientX: number, clientY: number): boolean;
101
+ getInsertionPreviewIndex(): number | null;
102
+ /** True while the user is actively dragging a stack item. */
103
+ get isDragging(): boolean;
104
+ /**
105
+ * Returns all action IDs currently in the stack, in visual stack order
106
+ * (bottom-first, i.e. earliest-played first). This is the authoritative
107
+ * "played history" for combo evaluation: items[0] is the oldest/bottom card,
108
+ * items[last] is the newest/top card. Drag-and-drop reordering is reflected
109
+ * immediately because reorderStackItemsByVisibleIndex mutates this.state.items.
110
+ */
111
+ getOrderedActionIds(): string[];
112
+ getSlotPaddedActionIds(anchorSlot: number, activeItemIds?: Set<string>): string[];
113
+ getNextTopSlot(): number;
114
+ getItemsInStackOrder(): ActionStackItem[];
115
+ getEffectiveDeltaByActionId(): Map<string, number>;
116
+ updateVisual(): void;
117
+ /**
118
+ * Garantit, dans le <defs> du SVG de la pile, le gradient de cœur propre au
119
+ * type de highlight courant, ainsi que le gradient d'impulsion et le filtre
120
+ * de flou partagés par toutes les jauges de fenêtre. Idempotent.
121
+ */
122
+ private _ensureWindowIndicatorDefs;
123
+ private _buildRenderContext;
124
+ /**
125
+ * Pendant un glisser du corps du bracket (voir draggingRangeBlock), calcule une
126
+ * projection en direct des positions — mêmes formules que ActionStackState.
127
+ * moveRangeTo, mais purement pour l'affichage : this.state.items n'est modifié
128
+ * qu'au relâchement (commitRangeMove). Hors glisser, retourne les items tels quels.
129
+ */
130
+ private _computePreviewItems;
131
+ private _computeLayout;
132
+ private _renderPanel;
133
+ private _renderFooterButtons;
134
+ private _renderConfigBookmarks;
135
+ private _renderGhostSlots;
136
+ private _renderTimeline;
137
+ private _renderItems;
138
+ private _renderComboHighlight;
139
+ private _renderUnifiedControl;
140
+ private _renderDragGhost;
141
+ private _bindItemInteractions;
142
+ private ensureActionStackGroup;
143
+ private ensureResizeObserver;
144
+ /** Copies the current tool range to the internal clipboard. */
145
+ copyRangeToClipboard(): void;
146
+ /** Pastes the clipboard at the top of the currently active stack. */
147
+ pasteClipboard(): void;
148
+ private ensureKeyboardListeners;
149
+ }
@@ -0,0 +1,111 @@
1
+ import * as d3 from "d3";
2
+ import type { ActionStackState, ActionStackItem } from "./ActionStackState";
3
+ export interface DragHandlerHost {
4
+ updateVisual(): void;
5
+ removeItem(id: string): void;
6
+ toggleItemSelection(id: string, emitChange: boolean): void;
7
+ toggleRangeActivation(): void;
8
+ setToolRange(minSlot: number, maxSlot: number): void;
9
+ /** Tap (sans glisser) sur la poignée basse : étend la plage jusqu'au slot 0. */
10
+ expandRangeToBottom(): void;
11
+ /** Tap (sans glisser) sur la poignée haute : étend la plage jusqu'au dernier item. */
12
+ expandRangeToTop(): void;
13
+ /** Double-clic sur la gouttière, n'importe où : sélectionne toute la pile d'un coup. */
14
+ selectAllRange(): void;
15
+ /** Déplace effectivement le bloc [originalMinSlot..originalMaxSlot] jusqu'à ce
16
+ * que son début arrive à targetMinSlot — appelé au relâchement d'un glisser sur
17
+ * le corps du bracket, une fois le geste confirmé (pas un simple tap). */
18
+ commitRangeMove(originalMinSlot: number, originalMaxSlot: number, targetMinSlot: number): void;
19
+ highlightByStackItemId(id: string | null): void;
20
+ triggerBlockHighlight(id: string | null): void;
21
+ triggerSatelliteHighlight(id: string | null): void;
22
+ locateInSearch(compId: string, oddId: string, cutIdx: number, actionId: string, kind: "action" | "keyEvent"): void;
23
+ /** Clic sur le libellé d'un item : sélectionne automatiquement son
24
+ * satellite porteur dans le diagramme (action) — ou sa Facette d'origine
25
+ * (Événement Clé, sentinel oddId="" / cutIdx=-1, cf. ActionStackState) —
26
+ * sans changer d'onglet, contrairement à locateInSearch. stackItemId
27
+ * permet ensuite de centrer automatiquement le bloc correspondant dans la
28
+ * liste "Effets cumulés" une fois celle-ci (re)rendue. */
29
+ selectSatelliteForItem(compId: string, oddId: string, cutIdx: number, stackItemId: string): void;
30
+ }
31
+ type Point = {
32
+ x: number;
33
+ y: number;
34
+ };
35
+ type RenderableItem = ActionStackItem & {
36
+ isPlaceholder?: boolean;
37
+ /** Aperçu d'échange lors d'un réordonnancement interne uniquement — voir
38
+ * ActionStackController._computeLayout. */
39
+ isDisplacedPreview?: boolean;
40
+ };
41
+ export declare class ActionStackDragHandler {
42
+ private readonly state;
43
+ private readonly host;
44
+ static readonly ACTION_DRAG_THRESHOLD_PX = 12;
45
+ static readonly DOUBLE_CLICK_MS = 300;
46
+ draggingStackItemId: string | null;
47
+ draggingStackItemPosition: Point | null;
48
+ draggingStackItemGhostWithLabel: boolean;
49
+ stackInsertionPreviewIndex: number | null;
50
+ pendingStackItemEntryOrigins: Map<string, Point>;
51
+ pendingSwapDropY: Map<string, number>;
52
+ stackControlDragInProgress: boolean;
53
+ /** Bloc en cours de glissement via le corps du bracket — permet au rendu de
54
+ * prévisualiser en direct la translation des items concernés, sans encore
55
+ * toucher à l'état persistant (le vrai déplacement n'a lieu qu'au relâchement,
56
+ * voir commitRangeMove). */
57
+ draggingRangeBlock: {
58
+ ids: Set<string>;
59
+ originalMinSlot: number;
60
+ originalMaxSlot: number;
61
+ } | null;
62
+ externalActionDragActive: boolean;
63
+ dropZoneClient: {
64
+ left: number;
65
+ top: number;
66
+ right: number;
67
+ bottom: number;
68
+ } | null;
69
+ dropZoneIsHot: boolean;
70
+ private lastGutterClickTime;
71
+ private layoutMetrics;
72
+ constructor(state: ActionStackState, host: DragHandlerHost);
73
+ get isDragging(): boolean;
74
+ setExternalDragActive(active: boolean): void;
75
+ setRenderGeometry(dropZoneClient: {
76
+ left: number;
77
+ top: number;
78
+ right: number;
79
+ bottom: number;
80
+ }, layoutMetrics: {
81
+ firstSlotCenterY: number;
82
+ slotSpacing: number;
83
+ compactLength: number;
84
+ }): void;
85
+ clearDropPreview(): void;
86
+ updateDropPreview(clientX: number, clientY: number, _localY: number): boolean;
87
+ isDropZoneHit(clientX: number, clientY: number): boolean;
88
+ getInsertionPreviewIndex(): number | null;
89
+ private emitDragPreviewChange;
90
+ syncPosition(actionStackGroup: SVGGElement | null): void;
91
+ bindItemInteractions(mergedItems: d3.Selection<SVGGElement, RenderableItem, SVGGElement, unknown>, bindCtx: {
92
+ x: number;
93
+ leftPad: number;
94
+ labelXOffset: number;
95
+ bottomY: number;
96
+ }, actionStackGroup: SVGGElement | null, _actionStackLabelFontScale: number): void;
97
+ bindUnifiedControlInteractions(ctrlMerged: d3.Selection<SVGGElement, number, SVGGElement, unknown>, ctrlBounds: {
98
+ minSlot: number;
99
+ maxSlot: number;
100
+ }, slotSpacing: number, numVisibleSlots: number): void;
101
+ /**
102
+ * Glisser dans la gouttière temporelle (graduation à gauche de la pile) définit
103
+ * une plage libre — plus d'ancrage forcé au slot 0. Un simple tap sélectionne le
104
+ * slot visé seul (plage de taille 1) ; un glissement l'étend jusqu'au slot courant.
105
+ * Réutilise le même calcul slot↔Y que l'aperçu de dépôt (updateDropPreview) pour
106
+ * rester cohérent avec la conversion clientY → slot déjà éprouvée ailleurs.
107
+ */
108
+ bindGutterSelectInteraction(hitStrip: d3.Selection<SVGRectElement, number, SVGGElement, unknown>): void;
109
+ private _svgLocalPoint;
110
+ }
111
+ export {};
@@ -0,0 +1,118 @@
1
+ import { IYinYangController } from "../../../interfaces/IYinYangController";
2
+ import { IYinYangView } from "../../../interfaces/IYinYangView";
3
+ import { YinYangControllerState } from "../state/YinYangControllerState";
4
+ import { ActionStackController, ActionStackItem } from "./ActionStackController";
5
+ import { ChildrenOrbitEntry } from "../utils/childrenOrbit";
6
+ import { SequentialActionProjectionInput } from "../actions/ActionProjectionService";
7
+ import { type ComboResult } from "../../../data/combo-engine";
8
+ import { ForceFieldLink } from "../force-field/types";
9
+ import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
10
+ type ChildOddMeta = {
11
+ oddId: string;
12
+ score: number;
13
+ cuttingIndex: number;
14
+ };
15
+ type SelectedSourceFocus = {
16
+ oddId: string;
17
+ cuttingId: number | undefined;
18
+ } | null;
19
+ type PersistentActionContext = {
20
+ sourceComponentId: string;
21
+ sourceOddId: string;
22
+ sourceCuttingIndex: number;
23
+ actionId: string;
24
+ actionLabel?: string;
25
+ links: ForceFieldLink[];
26
+ } | null;
27
+ type ActionStackManagerDeps = {
28
+ view: IYinYangView;
29
+ state: YinYangControllerState;
30
+ actionStackController: ActionStackController;
31
+ getChildControllers: () => IYinYangController[];
32
+ getChildOddMap: () => Map<string, ChildOddMeta>;
33
+ getSelectedSourceFocus: () => SelectedSourceFocus;
34
+ setSelectedSourceFocus: (focus: SelectedSourceFocus) => void;
35
+ getPersistentActionContext: () => PersistentActionContext;
36
+ setPersistentActionContext: (ctx: PersistentActionContext) => void;
37
+ getInternalModel: () => YinYangInternalModel;
38
+ getCurrentComponent: () => YinYangInternalModel["components"][number] | null;
39
+ getCurrentComponentOrUndefined: () => YinYangInternalModel["components"][number] | undefined;
40
+ getAllComponents: () => YinYangInternalModel["components"] | undefined;
41
+ prepareForComponentSwitch: (targetComponentId: string) => void;
42
+ animateChildrenHide: () => void;
43
+ recomputeIncomingForceFieldForItems: (items: ActionStackItem[], sourceComponentId: string, currentComponentId: string) => boolean;
44
+ buildProjectionActionsFromStackItems: (items: ActionStackItem[]) => SequentialActionProjectionInput[];
45
+ buildForceFieldSourceIndicesFromStackItems: (items: ActionStackItem[]) => number[];
46
+ clearProjectedActionState: (options?: {
47
+ deselect?: boolean;
48
+ }) => void;
49
+ clearActiveChild: () => void;
50
+ clearForceFieldComputationState: () => void;
51
+ renderOppositeChildrenForceField: () => void;
52
+ renderForceFieldDetails: (linkIndex: number) => void;
53
+ buildChildrenOrbitData: () => ChildrenOrbitEntry[];
54
+ renderChildScoresDeltas: (orbitData: ChildrenOrbitEntry[]) => void;
55
+ syncGhostTensionFromAction: () => void;
56
+ renderGhostDetailsForActiveAction: () => void;
57
+ renderCumulativeDetailsForCutting: (oddId: string, cuttingId: number | undefined) => boolean | undefined;
58
+ renderSourceActionDetailsForCurrentSource: () => void;
59
+ renderActionPropagationTable: () => void;
60
+ clearGhostDetails: () => void;
61
+ /** Réaffiche le descriptif de la Facette courante (avec ses blocs Événement
62
+ * Clé) — utilisé quand la pile ne contient plus que des items sans
63
+ * satellite (Événement Clé) après clearProjectedActionState, qui sinon
64
+ * retombe seul sur la carte Sphère. */
65
+ showComposanteInfo: () => void;
66
+ refreshChildActionMenu: () => void;
67
+ };
68
+ export declare class ActionStackManager {
69
+ private readonly deps;
70
+ private activeStackItemsSnapshot;
71
+ private lastComboResults;
72
+ private lastFocusBeforeAllUnchecked;
73
+ /** weightDelta of the action(s) currently previewed, summed per targeted
74
+ * satellite (a satellite is always its own single target, never
75
+ * propagated) — read by YinYangController to animate/ring each satellite. */
76
+ private currentWeightDeltaByChildKey;
77
+ constructor(deps: ActionStackManagerDeps);
78
+ getActiveItems(): ActionStackItem[];
79
+ getWeightDeltaByChildKey(): Map<string, number>;
80
+ /**
81
+ * Same weightDelta computation as getWeightDeltaByChildKey (baseDelta ×
82
+ * amplificateurs, écrêté aux bornes [WEIGHT_MIN, WEIGHT_MAX] du poids
83
+ * actuel), mais agrégée sur TOUTE la pile active — pas seulement les items
84
+ * de la facette actuellement affichée. La bordure du cercle central montre
85
+ * toutes les facettes à la fois (voir buildBorderSegments), donc son ghost
86
+ * a besoin du poids projeté de chaque facette ayant des items empilés,
87
+ * même celles qu'on ne regarde pas en ce moment. Clé :
88
+ * `${sourceComponentId}::${sourceOddId}::${sourceCuttingIndex}` — un
89
+ * satellite d'une facette non affichée n'a pas de sourceChildKey exploitable
90
+ * ici (childOddMap ne connaît que la facette courante).
91
+ */
92
+ getWeightDeltaByComponentOddCutting(): Map<string, number>;
93
+ clearWeightPreview(): void;
94
+ resetLastComboResults(): void;
95
+ computeComboResultsForAction(actionId: string): ComboResult[] | undefined;
96
+ validateActionSelection(childController: IYinYangController, actionId: string, label: string, insertAtVisibleIndex?: number | null, entryOrigin?: {
97
+ x: number;
98
+ y: number;
99
+ }): void;
100
+ /**
101
+ * Insertion directe à un slot absolu, sans passer par la sélection
102
+ * d'action du satellite (pas de menu, pas de champ de force) : c'est le
103
+ * chemin du placement automatique des actions adverses par le système.
104
+ * Le delta effectif est évalué à la position réelle d'insertion ; il
105
+ * sera de toute façon recalculé pour toute la pile par
106
+ * onStackItemVisibilityToggle à l'émission qui suit.
107
+ */
108
+ insertActionAtSlot(childController: IYinYangController, actionId: string, label: string, slotIndex: number): void;
109
+ /** Item de pile d'une action jouée depuis un satellite — couleur de son
110
+ * ODD, métadonnées de son cutting. Null si le satellite est inconnu. */
111
+ private buildActionStackItem;
112
+ activateActionFromChild(childController: IYinYangController, actionId?: string, label?: string, clearStack?: boolean): void;
113
+ applyProjectedGhostFromStackItems(items: ActionStackItem[]): void;
114
+ onStackItemVisibilityToggle(activeItems: ActionStackItem[]): void;
115
+ private updateProjectedGhostTensions;
116
+ private applyEmptyGhostProjectionState;
117
+ }
118
+ export {};
@@ -0,0 +1,169 @@
1
+ export type ActionStackItem = {
2
+ id: string;
3
+ /**
4
+ * Nature de l'item — "action" (par défaut) pour une action satellite
5
+ * classique, "keyEvent" pour un Événement Clé joué depuis le cercle
6
+ * central d'une facette. Un keyEvent n'a pas de satellite cible (les
7
+ * champs source* ci-dessous sont des sentinels dans ce cas) ni de delta
8
+ * propre (effectiveDelta reste 0) — son seul effet est d'amplifier les
9
+ * actions listées dans sa définition, si elles sont jouées après lui
10
+ * dans la pile (voir amplifier-engine.ts).
11
+ */
12
+ kind: "action" | "keyEvent";
13
+ /** actionId pour kind="action" ; KeyEventDefinition.id pour kind="keyEvent"
14
+ * (préfixe "key_" garantit l'absence de collision avec un actionId). */
15
+ actionId: string;
16
+ label: string;
17
+ /** Sentinel "" pour kind="keyEvent" — non rattaché à un satellite précis. */
18
+ sourceChildKey: string;
19
+ /** Sentinel "" pour kind="keyEvent". */
20
+ sourceOddId: string;
21
+ /** Sentinel -1 pour kind="keyEvent". */
22
+ sourceCuttingIndex: number;
23
+ /** Facette d'origine — pour un keyEvent, c'est la facette de déclenchement. */
24
+ sourceComponentId: string;
25
+ color: string;
26
+ validatedAt: number;
27
+ /** Delta effectif après évaluation du combo (= baseDelta × multiplicateur).
28
+ * Toujours 0 pour kind="keyEvent" (pas d'effet propre). */
29
+ effectiveDelta: number;
30
+ /** Résultats des combos évalués au moment du jeu — un par slot déclenché. */
31
+ comboResults?: import("../../../data/combo-engine").ComboResult[];
32
+ /** Index du slot occupé dans la pile (0 = en bas, croissant vers le haut). */
33
+ slotIndex: number;
34
+ };
35
+ /** Champs persistants d'un item copié — sans id/validatedAt/slotIndex, qui sont
36
+ * réattribués à l'insertion (dans la même pile ou une autre, via un bookmark). */
37
+ export type StackClipboardEntry = Omit<ActionStackItem, "id" | "validatedAt" | "slotIndex">;
38
+ export type BookmarkState = "empty" | "unlocked" | "locked";
39
+ export type ConfigBookmark = {
40
+ state: BookmarkState;
41
+ lockedSnapshot: StackState | null;
42
+ dominantColor: string;
43
+ };
44
+ export type StackState = {
45
+ items: ActionStackItem[];
46
+ activeItemIds: string[];
47
+ };
48
+ export type ActionStackSnapshot = StackState & {
49
+ activeBookmarkIndex: number | null;
50
+ bookmarks: Array<{
51
+ state: BookmarkState;
52
+ lockedSnapshot: StackState | null;
53
+ dominantColor: string;
54
+ }>;
55
+ };
56
+ /**
57
+ * Pure state class for the action stack — no DOM, no D3.
58
+ * Manages items, selection, history, config bookmarks and tool range.
59
+ * The controller wraps mutations with a visual update after each call.
60
+ */
61
+ export declare class ActionStackState {
62
+ private readonly onActiveItemsChange;
63
+ private readonly onClear?;
64
+ readonly ACTION_STACK_MAX_ITEMS = 30;
65
+ readonly HISTORY_LIMIT = 120;
66
+ readonly CONFIG_BOOKMARKS_MAX = 4;
67
+ items: ActionStackItem[];
68
+ activeItemIds: Set<string>;
69
+ historyPast: ActionStackSnapshot[];
70
+ historyFuture: ActionStackSnapshot[];
71
+ isApplyingHistory: boolean;
72
+ toolRangeMin: number;
73
+ toolRangeMax: number | null;
74
+ /** Presse-papiers de plage — survit volontairement au changement de bookmark
75
+ * (copier sur une pile, basculer de bookmark, coller sur une autre). */
76
+ stackClipboard: StackClipboardEntry[] | null;
77
+ configBookmarks: ConfigBookmark[];
78
+ activeBookmarkIndex: number;
79
+ constructor(onActiveItemsChange: (activeItems: ActionStackItem[]) => void, onClear?: (() => void) | undefined);
80
+ canUndo(): boolean;
81
+ canRedo(): boolean;
82
+ getItemById(id: string): ActionStackItem | undefined;
83
+ getActionLabel(actionId: string): string | null;
84
+ getOrderedActionIds(): string[];
85
+ getSlotPaddedActionIds(anchorSlot: number, activeItemIds?: Set<string>): string[];
86
+ getNextTopSlot(): number;
87
+ getItemsInStackOrder(): ActionStackItem[];
88
+ getEffectiveDeltaByActionId(): Map<string, number>;
89
+ /** Bornes de la plage d'outil, qu'elle contienne des items ou non — pour l'affichage
90
+ * du bracket, qui doit rester visible même sur une plage dessinée à vide (voir
91
+ * getToolBlock ci-dessous, qui lui exige au moins un item pour les mutations). */
92
+ getToolRangeBounds(): {
93
+ minSlot: number;
94
+ maxSlot: number;
95
+ } | null;
96
+ getToolBlock(): {
97
+ items: ActionStackItem[];
98
+ minSlot: number;
99
+ maxSlot: number;
100
+ } | null;
101
+ hasClipboard(): boolean;
102
+ /**
103
+ * Adds a new item to the stack and returns the created ActionStackItem so
104
+ * the controller can register animation origins if needed.
105
+ *
106
+ * @param compactLength Current number of visible slots (from render context).
107
+ * Null when called without a specific insertion index.
108
+ */
109
+ addItem(item: Omit<ActionStackItem, "id" | "validatedAt" | "slotIndex">, insertAtVisibleIndex: number | null, compactLength: number | null): ActionStackItem;
110
+ /**
111
+ * Insère à un slot ABSOLU (0 = bas de pile), en décalant d'un cran tout
112
+ * item situé à ce slot ou au-dessus — inconditionnellement, même si le
113
+ * slot est un trou : c'est ce qui rend l'arithmétique d'un plan
114
+ * d'insertions (adverse-action-planner.ts) exacte, insertion après
115
+ * insertion. addItem, lui, n'a pas cette garantie (il ne décale qu'en
116
+ * présence d'un occupant, et raisonne en index visible).
117
+ */
118
+ addItemAtSlot(item: Omit<ActionStackItem, "id" | "validatedAt" | "slotIndex">, slotIndex: number): ActionStackItem;
119
+ private pushItemAtSlot;
120
+ removeItem(itemId: string): void;
121
+ clearAll(): void;
122
+ deselectAllItems(): void;
123
+ syncActiveAction(sourceChildKey: string | null, actionId: string | null): boolean;
124
+ toggleItemSelection(itemId: string, emitChange: boolean): void;
125
+ /** Définit la plage d'outil librement (glisser dans la gouttière temporelle) — plus d'ancrage forcé au slot 0. */
126
+ setToolRange(minSlot: number, maxSlot: number): void;
127
+ /** Fait disparaître le bracket sans toucher aux items — clic sur un emplacement
128
+ * vide de la pile, en dehors de la gouttière et du bracket lui-même. */
129
+ clearToolRange(): void;
130
+ /** Étend la borne basse de la plage jusqu'au tout début de la pile (slot 0) —
131
+ * tap sur la poignée basse du bracket, sans glisser. */
132
+ expandRangeToBottom(): void;
133
+ /** Étend la borne haute de la plage jusqu'au dernier item de la pile — tap sur
134
+ * la poignée haute du bracket, sans glisser. */
135
+ expandRangeToTop(): void;
136
+ /** Sélectionne d'un coup l'ensemble de la pile — double-clic sur la gouttière
137
+ * temporelle, n'importe où. */
138
+ selectAllRange(): void;
139
+ clickBookmark(index: number): void;
140
+ undo(): void;
141
+ redo(): void;
142
+ /**
143
+ * Translate le bloc [originalMinSlot..originalMaxSlot] pour que son début arrive
144
+ * à targetMinSlot — décalage pur des numéros de slot, pas une réinsertion relative
145
+ * aux autres items. Les items "traversés" par le déplacement (ceux qui occupaient
146
+ * l'espace entre l'ancienne et la nouvelle position du bloc) sont repoussés de la
147
+ * taille du bloc pour combler l'espace laissé vacant ; tout le reste ne bouge pas.
148
+ * Généralise l'ancien couple moveRangeUp/moveRangeDown (un cran à la fois, swap
149
+ * avec le seul item adjacent) à un déplacement de plusieurs crans en un glisser.
150
+ */
151
+ moveRangeTo(originalMinSlot: number, originalMaxSlot: number, targetMinSlot: number): void;
152
+ toggleRangeActivation(): void;
153
+ /** Copie la plage sélectionnée dans le presse-papiers interne — n'affecte pas la pile,
154
+ * donc pas d'entrée d'historique. */
155
+ copyRangeToClipboard(): void;
156
+ /** Colle le contenu du presse-papiers au sommet de la pile courante (celle du
157
+ * bookmark actif) et sélectionne la plage collée. */
158
+ pasteClipboard(): void;
159
+ createSnapshot(): ActionStackSnapshot;
160
+ recordHistoryBeforeMutation(): void;
161
+ recordHistoryBeforeMutationAndReturnItems(): ActionStackItem[];
162
+ markActiveBookmarkDirty(): void;
163
+ ensureItemSlotIndices(): void;
164
+ emitActiveItemsChange(): void;
165
+ private createStackState;
166
+ private pushHistory;
167
+ private applyStackState;
168
+ private applySnapshot;
169
+ }
@@ -0,0 +1,73 @@
1
+ import { YinYangInternalModel } from "../../../interfaces/IYinYangInternalModel";
2
+ import { type PropagatedActionEdge } from "../../../utils/actionPropagation";
3
+ import { ForceFieldLink } from "../force-field/types";
4
+ export type ProjectedCuttingState = {
5
+ cuttingId: number;
6
+ originalTension: number;
7
+ projectedTension: number;
8
+ originalState: number;
9
+ projectedState: number;
10
+ };
11
+ export type GhostProjectionResult = {
12
+ projectedGhostTensionByOddId: Map<string, number>;
13
+ projectedGhostStateByOddId: Map<string, number>;
14
+ projectedGhostComponentTension: number;
15
+ projectedGhostCuttingStatesByOddId: Map<string, ProjectedCuttingState[]>;
16
+ };
17
+ export type SequentialActionProjectionInput = {
18
+ sourceOddId: string;
19
+ sourceCuttingIndex: number;
20
+ };
21
+ export type EmptyGhostProjectionState = {
22
+ projectedGhostTensionByOddId: null;
23
+ projectedGhostStateByOddId: null;
24
+ projectedGhostComponentTension: null;
25
+ projectedGhostCuttingStatesByOddId: null;
26
+ };
27
+ export declare function getEmptyGhostProjectionState(): EmptyGhostProjectionState;
28
+ export declare function projectGhostStateForIncomingLinks(params: {
29
+ component: YinYangInternalModel["components"][number] | undefined;
30
+ incomingLinks: ForceFieldLink[];
31
+ }): GhostProjectionResult | null;
32
+ export type ActionSequenceStateSnapshot = Map<string, Map<number, number>>;
33
+ export type ActionSequenceStepInfo = {
34
+ actionIndex: number;
35
+ action: SequentialActionProjectionInput;
36
+ effectiveDelta: number;
37
+ /** Clone of the fractional accumulator just before this step is applied. */
38
+ statesBefore: ActionSequenceStateSnapshot;
39
+ /** Clone of the fractional accumulator just after this step is applied. */
40
+ statesAfter: ActionSequenceStateSnapshot;
41
+ /** This step's own propagation edges — the exact same computation (single
42
+ * pass, computeActionPropagationDetails) that produced statesBefore →
43
+ * statesAfter, against the real rolled-forward state this step saw. Lets
44
+ * callers build a descriptive breakdown (e.g. UI badges) that's guaranteed
45
+ * consistent with the state diff, without re-running propagation. */
46
+ edges: PropagatedActionEdge[];
47
+ };
48
+ export declare function projectGhostStateForActionSequence(params: {
49
+ component: YinYangInternalModel["components"][number] | undefined;
50
+ allComponents: YinYangInternalModel["components"] | undefined;
51
+ actions: SequentialActionProjectionInput[];
52
+ actionDelta: number;
53
+ actionPropagationFactor: number;
54
+ /** Delta effectif par action (index-aligné sur `actions`). Quand fourni,
55
+ * remplace `actionDelta` pour chaque action individuellement. */
56
+ actionDeltas?: number[];
57
+ /** Called once per action, exposing the fractional accumulator state right
58
+ * before and right after that step — lets callers read the exact marginal
59
+ * contribution of a single stacked action within the real sequential
60
+ * simulation (e.g. the STACK panel's per-block delta). No effect on the
61
+ * returned projection. */
62
+ onStep?: (info: ActionSequenceStepInfo) => void;
63
+ }): GhostProjectionResult | null;
64
+ export declare function projectGhostStateForComponent(params: {
65
+ component: YinYangInternalModel["components"][number] | undefined;
66
+ allComponents: YinYangInternalModel["components"] | undefined;
67
+ sourceOddId: string | undefined;
68
+ sourceCuttingIndex: number;
69
+ actionDelta: number;
70
+ actionPropagationFactor: number;
71
+ }): GhostProjectionResult | null;
72
+ export declare function getOriginalComponentTension(component: YinYangInternalModel["components"][number] | undefined): number | null;
73
+ export declare function getOriginalOddTension(component: YinYangInternalModel["components"][number] | undefined, oddId: string): number | null;