@memberjunction/ng-dashboards 6.1.3 → 6.2.0-edge.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 (99) hide show
  1. package/dist/AI/components/vectors/vector-management-resource.component.d.ts +6 -1
  2. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  3. package/dist/AI/components/vectors/vector-management-resource.component.js +99 -45
  4. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  5. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +4 -0
  6. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts.map +1 -1
  7. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +16 -6
  8. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js.map +1 -1
  9. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
  10. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  11. package/dist/DataExplorer/data-explorer-dashboard.component.js +14 -4
  12. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  13. package/dist/DataExplorer/services/explorer-state.service.js +2 -2
  14. package/dist/DataExplorer/services/explorer-state.service.js.map +1 -1
  15. package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
  16. package/dist/DevTools/graphql-console.component.js +2 -0
  17. package/dist/DevTools/graphql-console.component.js.map +1 -1
  18. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  19. package/dist/Integration/components/connections/connections.component.js +2 -0
  20. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  21. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +4 -12
  22. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
  23. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +39 -78
  24. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  25. package/dist/Lists/components/lists-browse-resource.component.d.ts +3 -0
  26. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  27. package/dist/Lists/components/lists-browse-resource.component.js +7 -6
  28. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  29. package/dist/Lists/components/lists-my-lists-resource.component.d.ts +3 -0
  30. package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
  31. package/dist/Lists/components/lists-my-lists-resource.component.js +7 -6
  32. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  33. package/dist/PredictiveStudio/at-risk.view-models.d.ts +41 -3
  34. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
  35. package/dist/PredictiveStudio/at-risk.view-models.js +99 -6
  36. package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
  37. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts +4 -1
  38. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts.map +1 -1
  39. package/dist/PredictiveStudio/components/ps-catalog.component.js +61 -37
  40. package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
  41. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +12 -0
  42. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
  43. package/dist/PredictiveStudio/components/ps-experiments.component.js +426 -184
  44. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  45. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +84 -0
  46. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -0
  47. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +1397 -0
  48. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -0
  49. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +66 -34
  50. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  51. package/dist/PredictiveStudio/components/ps-pipelines.component.js +2456 -743
  52. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  53. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +194 -0
  54. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -0
  55. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +1902 -0
  56. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -0
  57. package/dist/PredictiveStudio/components/ps-production.component.d.ts +17 -1
  58. package/dist/PredictiveStudio/components/ps-production.component.d.ts.map +1 -1
  59. package/dist/PredictiveStudio/components/ps-production.component.js +653 -372
  60. package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
  61. package/dist/PredictiveStudio/components/ps-registry.component.d.ts +21 -61
  62. package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
  63. package/dist/PredictiveStudio/components/ps-registry.component.js +214 -681
  64. package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
  65. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +49 -0
  66. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +1 -0
  67. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +658 -0
  68. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +1 -0
  69. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +42 -0
  70. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -0
  71. package/dist/PredictiveStudio/predictive-studio-score-history.service.js +90 -0
  72. package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -0
  73. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts +14 -2
  74. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts.map +1 -1
  75. package/dist/PredictiveStudio/predictive-studio.view-models.js +41 -5
  76. package/dist/PredictiveStudio/predictive-studio.view-models.js.map +1 -1
  77. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
  78. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
  79. package/dist/PredictiveStudio/resources/ps-models-resource.component.js +178 -65
  80. package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
  81. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +9 -1
  82. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  83. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +307 -369
  84. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  85. package/dist/PredictiveStudio/resources/ps-resource-base.js +1 -1
  86. package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
  87. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +17 -1
  88. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
  89. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +309 -138
  90. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  91. package/dist/VersionHistory/components/graph-resource.component.d.ts +1 -0
  92. package/dist/VersionHistory/components/graph-resource.component.d.ts.map +1 -1
  93. package/dist/VersionHistory/components/graph-resource.component.js +6 -5
  94. package/dist/VersionHistory/components/graph-resource.component.js.map +1 -1
  95. package/dist/predictive-studio-dashboards.module.d.ts +10 -8
  96. package/dist/predictive-studio-dashboards.module.d.ts.map +1 -1
  97. package/dist/predictive-studio-dashboards.module.js +13 -3
  98. package/dist/predictive-studio-dashboards.module.js.map +1 -1
  99. package/package.json +64 -63
@@ -0,0 +1,658 @@
1
+ import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewEncapsulation, inject, } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { MJButtonDirective } from '@memberjunction/ng-ui-components';
5
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
6
+ import { UUIDsEqual } from '@memberjunction/global';
7
+ import { PredictiveStudioStartExperimentSessionOperation, } from '@memberjunction/core-entities';
8
+ import { DOMINANCE_THRESHOLD_DEFAULT, } from '@memberjunction/predictive-studio-core';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@angular/forms";
11
+ const _forTrack0 = ($index, $item) => $item.ID;
12
+ function PSRunExperimentModalComponent_For_19_Template(rf, ctx) { if (rf & 1) {
13
+ i0.ɵɵelementStart(0, "option", 13);
14
+ i0.ɵɵtext(1);
15
+ i0.ɵɵelementEnd();
16
+ } if (rf & 2) {
17
+ const p_r1 = ctx.$implicit;
18
+ i0.ɵɵproperty("value", p_r1.ID);
19
+ i0.ɵɵadvance();
20
+ i0.ɵɵtextInterpolate2("", p_r1.Name, " (Target: ", p_r1.TargetVariable || "None", ")");
21
+ } }
22
+ function PSRunExperimentModalComponent_Conditional_31_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelementStart(0, "option", 37);
24
+ i0.ɵɵtext(1, "ROC-AUC (Default)");
25
+ i0.ɵɵelementEnd();
26
+ i0.ɵɵelementStart(2, "option", 38);
27
+ i0.ɵɵtext(3, "F1 Score");
28
+ i0.ɵɵelementEnd();
29
+ i0.ɵɵelementStart(4, "option", 39);
30
+ i0.ɵɵtext(5, "Accuracy");
31
+ i0.ɵɵelementEnd();
32
+ } }
33
+ function PSRunExperimentModalComponent_Conditional_32_Template(rf, ctx) { if (rf & 1) {
34
+ i0.ɵɵelementStart(0, "option", 40);
35
+ i0.ɵɵtext(1, "RMSE (Default)");
36
+ i0.ɵɵelementEnd();
37
+ i0.ɵɵelementStart(2, "option", 41);
38
+ i0.ɵɵtext(3, "MAE");
39
+ i0.ɵɵelementEnd();
40
+ i0.ɵɵelementStart(4, "option", 42);
41
+ i0.ɵɵtext(5, "R-Squared");
42
+ i0.ɵɵelementEnd();
43
+ } }
44
+ function PSRunExperimentModalComponent_For_55_Conditional_7_Template(rf, ctx) { if (rf & 1) {
45
+ i0.ɵɵelementStart(0, "div", 47);
46
+ i0.ɵɵtext(1);
47
+ i0.ɵɵelementEnd();
48
+ } if (rf & 2) {
49
+ const algo_r3 = i0.ɵɵnextContext().$implicit;
50
+ i0.ɵɵadvance();
51
+ i0.ɵɵtextInterpolate(algo_r3.Description);
52
+ } }
53
+ function PSRunExperimentModalComponent_For_55_Conditional_9_Template(rf, ctx) { if (rf & 1) {
54
+ i0.ɵɵelementStart(0, "span", 49);
55
+ i0.ɵɵtext(1, "Importance");
56
+ i0.ɵɵelementEnd();
57
+ } }
58
+ function PSRunExperimentModalComponent_For_55_Template(rf, ctx) { if (rf & 1) {
59
+ const _r2 = i0.ɵɵgetCurrentView();
60
+ i0.ɵɵelementStart(0, "div", 43);
61
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_For_55_Template_div_click_0_listener() { const algo_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.toggleAlgo(algo_r3.ID)); });
62
+ i0.ɵɵelementStart(1, "input", 44);
63
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_For_55_Template_input_click_1_listener($event) { return $event.stopPropagation(); })("change", function PSRunExperimentModalComponent_For_55_Template_input_change_1_listener() { const algo_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.toggleAlgo(algo_r3.ID)); });
64
+ i0.ɵɵelementEnd();
65
+ i0.ɵɵelementStart(2, "div", 45)(3, "div", 46);
66
+ i0.ɵɵelement(4, "i");
67
+ i0.ɵɵelementStart(5, "span");
68
+ i0.ɵɵtext(6);
69
+ i0.ɵɵelementEnd()();
70
+ i0.ɵɵconditionalCreate(7, PSRunExperimentModalComponent_For_55_Conditional_7_Template, 2, 1, "div", 47);
71
+ i0.ɵɵelementStart(8, "div", 48);
72
+ i0.ɵɵconditionalCreate(9, PSRunExperimentModalComponent_For_55_Conditional_9_Template, 2, 0, "span", 49);
73
+ i0.ɵɵelementStart(10, "span", 50);
74
+ i0.ɵɵtext(11);
75
+ i0.ɵɵelementEnd()()()();
76
+ } if (rf & 2) {
77
+ const algo_r3 = ctx.$implicit;
78
+ const ctx_r3 = i0.ɵɵnextContext();
79
+ i0.ɵɵclassProp("selected", ctx_r3.isAlgoSelected(algo_r3.ID));
80
+ i0.ɵɵadvance();
81
+ i0.ɵɵproperty("checked", ctx_r3.isAlgoSelected(algo_r3.ID))("disabled", ctx_r3.busy);
82
+ i0.ɵɵadvance(3);
83
+ i0.ɵɵclassMap(ctx_r3.getAlgoIcon(algo_r3.DriverClass));
84
+ i0.ɵɵadvance(2);
85
+ i0.ɵɵtextInterpolate(algo_r3.Name);
86
+ i0.ɵɵadvance();
87
+ i0.ɵɵconditional(algo_r3.Description ? 7 : -1);
88
+ i0.ɵɵadvance(2);
89
+ i0.ɵɵconditional(algo_r3.SupportsFeatureImportance ? 9 : -1);
90
+ i0.ɵɵadvance(2);
91
+ i0.ɵɵtextInterpolate(algo_r3.DriverClass);
92
+ } }
93
+ function PSRunExperimentModalComponent_Conditional_86_Template(rf, ctx) { if (rf & 1) {
94
+ i0.ɵɵelement(0, "i", 51);
95
+ i0.ɵɵtext(1, " Launching Experiment\u2026 ");
96
+ } }
97
+ function PSRunExperimentModalComponent_Conditional_87_Template(rf, ctx) { if (rf & 1) {
98
+ i0.ɵɵelement(0, "i", 52);
99
+ i0.ɵɵtext(1, " Launch Experiment ");
100
+ } }
101
+ const ALGO_ICONS = {
102
+ xgboost: 'fa-solid fa-bolt',
103
+ lightgbm: 'fa-solid fa-feather',
104
+ logistic_regression: 'fa-solid fa-wave-square',
105
+ random_forest: 'fa-solid fa-tree',
106
+ ridge: 'fa-solid fa-ruler',
107
+ linear_regression: 'fa-solid fa-ruler',
108
+ mlp: 'fa-solid fa-network-wired',
109
+ };
110
+ /**
111
+ * Manual Experiment Runner modal — lets data scientists configure and launch a
112
+ * multi-algorithm tournament session on an existing training pipeline.
113
+ */
114
+ export class PSRunExperimentModalComponent {
115
+ engine;
116
+ provider = null;
117
+ currentUser = null;
118
+ preselectedPipelineId;
119
+ closed = new EventEmitter();
120
+ started = new EventEmitter();
121
+ cdr = inject(ChangeDetectorRef);
122
+ notifications = inject(MJNotificationService);
123
+ selectedPipelineId = '';
124
+ sessionName = '';
125
+ goal = '';
126
+ successMetric = 'AUC';
127
+ problemType = 'classification';
128
+ selectedAlgoIds = new Set();
129
+ budgetRuns = 15;
130
+ budgetCost = 25;
131
+ budgetMinutes = 30;
132
+ holdoutPercent = 15;
133
+ busy = false;
134
+ ngOnInit() {
135
+ const pipelines = this.engine?.Pipelines ?? [];
136
+ if (this.preselectedPipelineId && pipelines.some((p) => UUIDsEqual(p.ID, this.preselectedPipelineId))) {
137
+ this.selectedPipelineId = this.preselectedPipelineId;
138
+ }
139
+ else if (pipelines.length > 0) {
140
+ this.selectedPipelineId = pipelines[0].ID;
141
+ }
142
+ this.syncFromSelectedPipeline();
143
+ }
144
+ get availableAlgorithms() {
145
+ return this.engine?.Algorithms ?? [];
146
+ }
147
+ get selectedPipeline() {
148
+ return this.engine?.Pipelines.find((p) => UUIDsEqual(p.ID, this.selectedPipelineId));
149
+ }
150
+ onPipelineChange(newId) {
151
+ this.selectedPipelineId = newId;
152
+ this.syncFromSelectedPipeline();
153
+ }
154
+ syncFromSelectedPipeline() {
155
+ const p = this.selectedPipeline;
156
+ if (!p)
157
+ return;
158
+ this.problemType = p.ProblemType || 'classification';
159
+ this.successMetric = this.problemType === 'classification' ? 'AUC' : 'RMSE';
160
+ this.sessionName = `${p.Name} — Multi-Algorithm Tournament`;
161
+ this.goal = `Evaluate and compare algorithms on ${p.Name} to maximize ${this.successMetric}.`;
162
+ this.selectRecommendedAlgos();
163
+ }
164
+ selectRecommendedAlgos() {
165
+ this.selectedAlgoIds.clear();
166
+ const pt = this.problemType;
167
+ for (const algo of this.availableAlgorithms) {
168
+ const types = (algo.ProblemTypes || '').toLowerCase();
169
+ if (types.includes(pt) || types.length === 0) {
170
+ // Recommend gradient boosting and random forest by default
171
+ const dc = (algo.DriverClass || '').toLowerCase();
172
+ if (dc.includes('xgboost') || dc.includes('lightgbm') || dc.includes('forest') || dc.includes('logistic')) {
173
+ this.selectedAlgoIds.add(algo.ID);
174
+ }
175
+ }
176
+ }
177
+ if (this.selectedAlgoIds.size === 0) {
178
+ // Fallback: select first 3
179
+ this.availableAlgorithms.slice(0, 3).forEach((a) => this.selectedAlgoIds.add(a.ID));
180
+ }
181
+ }
182
+ selectAllAlgos() {
183
+ this.availableAlgorithms.forEach((a) => this.selectedAlgoIds.add(a.ID));
184
+ }
185
+ clearAlgos() {
186
+ this.selectedAlgoIds.clear();
187
+ }
188
+ isAlgoSelected(id) {
189
+ return this.selectedAlgoIds.has(id);
190
+ }
191
+ toggleAlgo(id) {
192
+ if (this.selectedAlgoIds.has(id)) {
193
+ this.selectedAlgoIds.delete(id);
194
+ }
195
+ else {
196
+ this.selectedAlgoIds.add(id);
197
+ }
198
+ }
199
+ getAlgoIcon(driverClass) {
200
+ const key = (driverClass || '').toLowerCase();
201
+ for (const [k, icon] of Object.entries(ALGO_ICONS)) {
202
+ if (key.includes(k))
203
+ return icon;
204
+ }
205
+ return 'fa-solid fa-chart-line';
206
+ }
207
+ get isValid() {
208
+ return Boolean(this.selectedPipelineId &&
209
+ this.sessionName.trim() &&
210
+ this.selectedAlgoIds.size > 0 &&
211
+ this.budgetRuns > 0 &&
212
+ this.budgetCost > 0);
213
+ }
214
+ cancel() {
215
+ if (this.busy)
216
+ return;
217
+ this.closed.emit();
218
+ }
219
+ onBackdropClick(event) {
220
+ if (event.target === event.currentTarget && !this.busy) {
221
+ this.cancel();
222
+ }
223
+ }
224
+ async launch() {
225
+ const pipeline = this.selectedPipeline;
226
+ if (!pipeline || !this.isValid || this.busy)
227
+ return;
228
+ this.busy = true;
229
+ this.notifications.CreateSimpleNotification('Starting experiment session…', 'info', 3000);
230
+ try {
231
+ // Parse pipeline configuration
232
+ const sources = this.parseJson(pipeline.SourceBindings, []);
233
+ const stepGraph = this.parseJson(pipeline.FeatureSteps, { Steps: [] });
234
+ const leakage = this.parseJson(pipeline.LeakageGuard, {
235
+ DenyFields: [],
236
+ SingleFeatureDominanceThreshold: DOMINANCE_THRESHOLD_DEFAULT,
237
+ });
238
+ const asOf = this.parseJson(pipeline.AsOfStrategy, { Mode: 'none' });
239
+ const validation = this.parseJson(pipeline.ValidationStrategy, {
240
+ Strategy: 'train_test_split',
241
+ TestSize: 0.2,
242
+ LockedHoldoutFraction: 0.15,
243
+ });
244
+ const sourceEntity = sources[0]?.Ref || 'Entity';
245
+ const features = [];
246
+ for (const step of stepGraph.Steps ?? []) {
247
+ if ('Columns' in step && Array.isArray(step.Columns)) {
248
+ features.push(...step.Columns);
249
+ }
250
+ else if ('Column' in step && typeof step.Column === 'string') {
251
+ features.push(step.Column);
252
+ }
253
+ }
254
+ const selectedAlgos = this.availableAlgorithms.filter((a) => this.selectedAlgoIds.has(a.ID));
255
+ const planSpec = {
256
+ Goal: this.goal.trim() || `Optimize model for ${pipeline.Name}`,
257
+ TargetDefinition: {
258
+ EntityName: sourceEntity,
259
+ TargetVariable: pipeline.TargetVariable || 'Target',
260
+ ProblemType: this.problemType,
261
+ SuccessMetric: this.successMetric,
262
+ AsOfStrategy: asOf,
263
+ },
264
+ CandidateSources: sources.map((s) => ({
265
+ Kind: s.Kind,
266
+ Ref: s.Ref,
267
+ Why: 'Configured in training pipeline',
268
+ })),
269
+ CandidateFeatures: features.map((f) => ({
270
+ Name: f,
271
+ SourceRef: sourceEntity,
272
+ Kind: 'numeric',
273
+ Why: 'Selected in pipeline feature steps',
274
+ })),
275
+ LeakageNotes: (leakage.DenyFields || []).map((field) => ({
276
+ Field: field,
277
+ Risk: 'Deny-listed leakage risk',
278
+ Action: 'exclude',
279
+ })),
280
+ ProposedExperiments: selectedAlgos.map((algo, index) => ({
281
+ Label: `${algo.Name} (tournament trial)`,
282
+ AlgorithmName: algo.Name,
283
+ FeatureSet: features,
284
+ Rationale: `Evaluate ${algo.Name} across pipeline features`,
285
+ Priority: index + 1,
286
+ })),
287
+ ValidationStrategy: validation,
288
+ ProposedBudget: {
289
+ MaxRuns: this.budgetRuns,
290
+ MaxComputeCost: this.budgetCost,
291
+ MaxWallclockMinutes: this.budgetMinutes,
292
+ },
293
+ Approved: true,
294
+ };
295
+ const budget = {
296
+ MaxRuns: this.budgetRuns,
297
+ MaxComputeCost: this.budgetCost,
298
+ MaxWallclockMinutes: this.budgetMinutes,
299
+ };
300
+ const op = new PredictiveStudioStartExperimentSessionOperation();
301
+ const result = await op.Execute({
302
+ planSpec,
303
+ budget,
304
+ }, { provider: this.provider ?? undefined, user: this.currentUser ?? undefined });
305
+ if (result.Success && result.Output) {
306
+ this.notifications.CreateSimpleNotification(`Experiment session started! Session ID: ${result.Output.sessionId}`, 'success', 5000);
307
+ this.started.emit(result.Output.sessionId);
308
+ this.closed.emit();
309
+ }
310
+ else {
311
+ this.notifications.CreateSimpleNotification(result.ErrorMessage || 'Failed to start experiment session.', 'error', 6000);
312
+ }
313
+ }
314
+ catch (err) {
315
+ this.notifications.CreateSimpleNotification(`Experiment error: ${err instanceof Error ? err.message : String(err)}`, 'error', 6000);
316
+ }
317
+ finally {
318
+ this.busy = false;
319
+ this.cdr.detectChanges();
320
+ }
321
+ }
322
+ parseJson(raw, fallback) {
323
+ if (!raw)
324
+ return fallback;
325
+ try {
326
+ const val = JSON.parse(raw);
327
+ return (val ?? fallback);
328
+ }
329
+ catch {
330
+ return fallback;
331
+ }
332
+ }
333
+ static ɵfac = function PSRunExperimentModalComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSRunExperimentModalComponent)(); };
334
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRunExperimentModalComponent, selectors: [["ps-run-experiment-modal"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser", preselectedPipelineId: "preselectedPipelineId" }, outputs: { closed: "closed", started: "started" }, decls: 88, vars: 24, consts: [[1, "ps-modal-backdrop", 3, "click"], ["role", "dialog", "aria-modal", "true", "data-testid", "ps-run-experiment-modal", 1, "ps-modal-dialog", "ps-run-exp-dialog"], [1, "ps-wizard-header"], [1, "ps-wizard-header-title"], [1, "fa-solid", "fa-flask", 2, "color", "var(--mj-brand-primary)"], [1, "ps-wizard-subtitle"], ["aria-label", "Close dialog", 1, "ps-wizard-close", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "ps-run-exp-body"], [1, "ps-form-group"], [1, "ps-form-label"], [1, "fa-solid", "fa-diagram-project"], [1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], [3, "value"], [1, "ps-form-hint"], [1, "exp-grid-2"], ["type", "text", "placeholder", "e.g. Churn Tournament Q3", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], ["type", "text", "placeholder", "e.g. Find best algorithm on holdout split", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "algo-bar-actions"], [1, "ps-form-label", 2, "margin-bottom", "0"], [1, "fa-solid", "fa-shapes"], [2, "display", "flex", "gap", "8px"], ["type", "button", 1, "ps-link-btn", 3, "click", "disabled"], [1, "ps-muted"], [1, "algo-selection-grid"], [1, "algo-select-card", 3, "selected"], [1, "exp-grid-3"], ["type", "number", "min", "1", "max", "100", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], ["type", "number", "min", "1", "max", "500", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], ["type", "number", "min", "5", "max", "300", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "budget-banner"], [1, "fa-solid", "fa-shield-halved"], [2, "font-size", "12px", "color", "var(--mj-text-secondary)"], [1, "ps-wizard-footer"], ["mjButton", "", "variant", "secondary", "size", "md", 3, "click", "disabled"], [1, "ps-spacer"], ["mjButton", "", "variant", "primary", "size", "md", 3, "click", "disabled"], ["value", "AUC"], ["value", "F1"], ["value", "Accuracy"], ["value", "RMSE"], ["value", "MAE"], ["value", "R2"], [1, "algo-select-card", 3, "click"], ["type", "checkbox", 3, "click", "change", "checked", "disabled"], [1, "algo-select-info"], [1, "algo-select-name"], [1, "algo-select-desc"], [1, "algo-select-tags"], [1, "ps-badge", "green", 2, "font-size", "10px"], [1, "ps-tag", 2, "font-size", "10px"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"]], template: function PSRunExperimentModalComponent_Template(rf, ctx) { if (rf & 1) {
335
+ i0.ɵɵelementStart(0, "div", 0);
336
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_div_click_0_listener($event) { return ctx.onBackdropClick($event); });
337
+ i0.ɵɵelementStart(1, "div", 1)(2, "div", 2)(3, "div", 3);
338
+ i0.ɵɵelement(4, "i", 4);
339
+ i0.ɵɵelementStart(5, "div")(6, "h2");
340
+ i0.ɵɵtext(7, "Run Multi-Algorithm Experiment");
341
+ i0.ɵɵelementEnd();
342
+ i0.ɵɵelementStart(8, "span", 5);
343
+ i0.ɵɵtext(9, "Execute a tournament search across multiple algorithms under bounded budget");
344
+ i0.ɵɵelementEnd()()();
345
+ i0.ɵɵelementStart(10, "button", 6);
346
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_10_listener() { return ctx.cancel(); });
347
+ i0.ɵɵelement(11, "i", 7);
348
+ i0.ɵɵelementEnd()();
349
+ i0.ɵɵelementStart(12, "div", 8)(13, "div", 9)(14, "label", 10);
350
+ i0.ɵɵelement(15, "i", 11);
351
+ i0.ɵɵtext(16, " Training Pipeline ");
352
+ i0.ɵɵelementEnd();
353
+ i0.ɵɵelementStart(17, "select", 12);
354
+ i0.ɵɵlistener("ngModelChange", function PSRunExperimentModalComponent_Template_select_ngModelChange_17_listener($event) { return ctx.onPipelineChange($event); });
355
+ i0.ɵɵrepeaterCreate(18, PSRunExperimentModalComponent_For_19_Template, 2, 3, "option", 13, _forTrack0);
356
+ i0.ɵɵelementEnd();
357
+ i0.ɵɵelementStart(20, "span", 14);
358
+ i0.ɵɵtext(21, " The dataset, feature assembly graph, and leakage deny-list are derived from this pipeline. ");
359
+ i0.ɵɵelementEnd()();
360
+ i0.ɵɵelementStart(22, "div", 15)(23, "div", 9)(24, "label", 10);
361
+ i0.ɵɵtext(25, "Session Name");
362
+ i0.ɵɵelementEnd();
363
+ i0.ɵɵelementStart(26, "input", 16);
364
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_input_ngModelChange_26_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.sessionName, $event) || (ctx.sessionName = $event); return $event; });
365
+ i0.ɵɵelementEnd()();
366
+ i0.ɵɵelementStart(27, "div", 9)(28, "label", 10);
367
+ i0.ɵɵtext(29, "Evaluation Metric");
368
+ i0.ɵɵelementEnd();
369
+ i0.ɵɵelementStart(30, "select", 12);
370
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_select_ngModelChange_30_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.successMetric, $event) || (ctx.successMetric = $event); return $event; });
371
+ i0.ɵɵconditionalCreate(31, PSRunExperimentModalComponent_Conditional_31_Template, 6, 0)(32, PSRunExperimentModalComponent_Conditional_32_Template, 6, 0);
372
+ i0.ɵɵelementEnd()()();
373
+ i0.ɵɵelementStart(33, "div", 9)(34, "label", 10);
374
+ i0.ɵɵtext(35, "Experiment Goal");
375
+ i0.ɵɵelementEnd();
376
+ i0.ɵɵelementStart(36, "input", 17);
377
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_input_ngModelChange_36_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.goal, $event) || (ctx.goal = $event); return $event; });
378
+ i0.ɵɵelementEnd()();
379
+ i0.ɵɵelementStart(37, "div", 9)(38, "div", 18)(39, "label", 19);
380
+ i0.ɵɵelement(40, "i", 20);
381
+ i0.ɵɵtext(41);
382
+ i0.ɵɵelementEnd();
383
+ i0.ɵɵelementStart(42, "div", 21)(43, "button", 22);
384
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_43_listener() { return ctx.selectAllAlgos(); });
385
+ i0.ɵɵtext(44, "Select All");
386
+ i0.ɵɵelementEnd();
387
+ i0.ɵɵelementStart(45, "span", 23);
388
+ i0.ɵɵtext(46, "\u00B7");
389
+ i0.ɵɵelementEnd();
390
+ i0.ɵɵelementStart(47, "button", 22);
391
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_47_listener() { return ctx.selectRecommendedAlgos(); });
392
+ i0.ɵɵtext(48, "Recommended");
393
+ i0.ɵɵelementEnd();
394
+ i0.ɵɵelementStart(49, "span", 23);
395
+ i0.ɵɵtext(50, "\u00B7");
396
+ i0.ɵɵelementEnd();
397
+ i0.ɵɵelementStart(51, "button", 22);
398
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_51_listener() { return ctx.clearAlgos(); });
399
+ i0.ɵɵtext(52, "Clear");
400
+ i0.ɵɵelementEnd()()();
401
+ i0.ɵɵelementStart(53, "div", 24);
402
+ i0.ɵɵrepeaterCreate(54, PSRunExperimentModalComponent_For_55_Template, 12, 10, "div", 25, _forTrack0);
403
+ i0.ɵɵelementEnd()();
404
+ i0.ɵɵelementStart(56, "div", 26)(57, "div", 9)(58, "label", 10);
405
+ i0.ɵɵtext(59, "Max Iterations (Trials)");
406
+ i0.ɵɵelementEnd();
407
+ i0.ɵɵelementStart(60, "input", 27);
408
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_input_ngModelChange_60_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.budgetRuns, $event) || (ctx.budgetRuns = $event); return $event; });
409
+ i0.ɵɵelementEnd();
410
+ i0.ɵɵelementStart(61, "span", 14);
411
+ i0.ɵɵtext(62, "Total model training runs bounded");
412
+ i0.ɵɵelementEnd()();
413
+ i0.ɵɵelementStart(63, "div", 9)(64, "label", 10);
414
+ i0.ɵɵtext(65, "Compute Cost Cap ($)");
415
+ i0.ɵɵelementEnd();
416
+ i0.ɵɵelementStart(66, "input", 28);
417
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_input_ngModelChange_66_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.budgetCost, $event) || (ctx.budgetCost = $event); return $event; });
418
+ i0.ɵɵelementEnd();
419
+ i0.ɵɵelementStart(67, "span", 14);
420
+ i0.ɵɵtext(68, "Max spending for cloud / GPU compute");
421
+ i0.ɵɵelementEnd()();
422
+ i0.ɵɵelementStart(69, "div", 9)(70, "label", 10);
423
+ i0.ɵɵtext(71, "Wall-Clock Cap (Minutes)");
424
+ i0.ɵɵelementEnd();
425
+ i0.ɵɵelementStart(72, "input", 29);
426
+ i0.ɵɵtwoWayListener("ngModelChange", function PSRunExperimentModalComponent_Template_input_ngModelChange_72_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.budgetMinutes, $event) || (ctx.budgetMinutes = $event); return $event; });
427
+ i0.ɵɵelementEnd();
428
+ i0.ɵɵelementStart(73, "span", 14);
429
+ i0.ɵɵtext(74, "Session timeouts if exceeded");
430
+ i0.ɵɵelementEnd()()();
431
+ i0.ɵɵelementStart(75, "div", 30);
432
+ i0.ɵɵelement(76, "i", 31);
433
+ i0.ɵɵelementStart(77, "div", 32)(78, "strong");
434
+ i0.ɵɵtext(79, "Autonomous Guardrails:");
435
+ i0.ɵɵelementEnd();
436
+ i0.ɵɵtext(80);
437
+ i0.ɵɵelementEnd()()();
438
+ i0.ɵɵelementStart(81, "div", 33)(82, "button", 34);
439
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_82_listener() { return ctx.cancel(); });
440
+ i0.ɵɵtext(83, " Cancel ");
441
+ i0.ɵɵelementEnd();
442
+ i0.ɵɵelement(84, "span", 35);
443
+ i0.ɵɵelementStart(85, "button", 36);
444
+ i0.ɵɵlistener("click", function PSRunExperimentModalComponent_Template_button_click_85_listener() { return ctx.launch(); });
445
+ i0.ɵɵconditionalCreate(86, PSRunExperimentModalComponent_Conditional_86_Template, 2, 0)(87, PSRunExperimentModalComponent_Conditional_87_Template, 2, 0);
446
+ i0.ɵɵelementEnd()()()();
447
+ } if (rf & 2) {
448
+ i0.ɵɵadvance(17);
449
+ i0.ɵɵproperty("ngModel", ctx.selectedPipelineId)("disabled", ctx.busy);
450
+ i0.ɵɵadvance();
451
+ i0.ɵɵrepeater(ctx.engine.Pipelines);
452
+ i0.ɵɵadvance(8);
453
+ i0.ɵɵtwoWayProperty("ngModel", ctx.sessionName);
454
+ i0.ɵɵproperty("disabled", ctx.busy);
455
+ i0.ɵɵadvance(4);
456
+ i0.ɵɵtwoWayProperty("ngModel", ctx.successMetric);
457
+ i0.ɵɵproperty("disabled", ctx.busy);
458
+ i0.ɵɵadvance();
459
+ i0.ɵɵconditional(ctx.problemType === "classification" ? 31 : 32);
460
+ i0.ɵɵadvance(5);
461
+ i0.ɵɵtwoWayProperty("ngModel", ctx.goal);
462
+ i0.ɵɵproperty("disabled", ctx.busy);
463
+ i0.ɵɵadvance(5);
464
+ i0.ɵɵtextInterpolate1(" Candidate Algorithms (", ctx.selectedAlgoIds.size, " selected) ");
465
+ i0.ɵɵadvance(2);
466
+ i0.ɵɵproperty("disabled", ctx.busy);
467
+ i0.ɵɵadvance(4);
468
+ i0.ɵɵproperty("disabled", ctx.busy);
469
+ i0.ɵɵadvance(4);
470
+ i0.ɵɵproperty("disabled", ctx.busy);
471
+ i0.ɵɵadvance(3);
472
+ i0.ɵɵrepeater(ctx.availableAlgorithms);
473
+ i0.ɵɵadvance(6);
474
+ i0.ɵɵtwoWayProperty("ngModel", ctx.budgetRuns);
475
+ i0.ɵɵproperty("disabled", ctx.busy);
476
+ i0.ɵɵadvance(6);
477
+ i0.ɵɵtwoWayProperty("ngModel", ctx.budgetCost);
478
+ i0.ɵɵproperty("disabled", ctx.busy);
479
+ i0.ɵɵadvance(6);
480
+ i0.ɵɵtwoWayProperty("ngModel", ctx.budgetMinutes);
481
+ i0.ɵɵproperty("disabled", ctx.busy);
482
+ i0.ɵɵadvance(8);
483
+ i0.ɵɵtextInterpolate2(" The session will run across waves, evaluate each candidate on a locked holdout fraction (", ctx.holdoutPercent, "%), rank them on the leaderboard by ", ctx.successMetric, ", and automatically prune unpromising configurations. ");
484
+ i0.ɵɵadvance(2);
485
+ i0.ɵɵproperty("disabled", ctx.busy);
486
+ i0.ɵɵadvance(3);
487
+ i0.ɵɵproperty("disabled", ctx.busy || !ctx.isValid);
488
+ i0.ɵɵadvance();
489
+ i0.ɵɵconditional(ctx.busy ? 86 : 87);
490
+ } }, dependencies: [CommonModule, FormsModule, i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.NumberValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.MinValidator, i1.MaxValidator, i1.NgModel, MJButtonDirective], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-run-exp-dialog {\n max-width: 860px;\n width: 90vw;\n max-height: 88vh;\n display: flex;\n flex-direction: column;\n}\n\n.ps-run-exp-body {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.exp-grid-2 {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n}\n\n.exp-grid-3 {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n gap: 16px;\n}\n\n.algo-selection-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n gap: 12px;\n margin-top: 10px;\n}\n\n.algo-select-card {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--mj-border);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease-in-out;\n}\n\n.algo-select-card:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-hover, rgba(0, 0, 0, 0.02));\n}\n\n.algo-select-card.selected {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary-light, rgba(59, 130, 246, 0.08));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.algo-select-card input[type=\"checkbox\"] {\n margin-top: 3px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n}\n\n.algo-select-info {\n flex: 1;\n min-width: 0;\n}\n\n.algo-select-name {\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.algo-select-name i {\n color: var(--mj-brand-primary);\n}\n\n.algo-select-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n line-height: 1.3;\n}\n\n.algo-select-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 6px;\n}\n\n.algo-bar-actions {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.budget-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-secondary, rgba(0, 0, 0, 0.03));\n border-radius: 8px;\n border: 1px dashed var(--mj-border);\n}\n\n.budget-banner i {\n font-size: 20px;\n color: var(--mj-status-warning);\n}\n"], encapsulation: 2 });
491
+ }
492
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSRunExperimentModalComponent, [{
493
+ type: Component,
494
+ args: [{ standalone: true, selector: 'ps-run-experiment-modal', imports: [CommonModule, FormsModule, MJButtonDirective], encapsulation: ViewEncapsulation.None, template: `
495
+ <div class="ps-modal-backdrop" (click)="onBackdropClick($event)">
496
+ <div class="ps-modal-dialog ps-run-exp-dialog" role="dialog" aria-modal="true" data-testid="ps-run-experiment-modal">
497
+ <!-- Header -->
498
+ <div class="ps-wizard-header">
499
+ <div class="ps-wizard-header-title">
500
+ <i class="fa-solid fa-flask" style="color: var(--mj-brand-primary)"></i>
501
+ <div>
502
+ <h2>Run Multi-Algorithm Experiment</h2>
503
+ <span class="ps-wizard-subtitle">Execute a tournament search across multiple algorithms under bounded budget</span>
504
+ </div>
505
+ </div>
506
+ <button class="ps-wizard-close" (click)="cancel()" aria-label="Close dialog">
507
+ <i class="fa-solid fa-xmark"></i>
508
+ </button>
509
+ </div>
510
+
511
+ <!-- Body -->
512
+ <div class="ps-run-exp-body">
513
+ <!-- Step 1: Select Pipeline -->
514
+ <div class="ps-form-group">
515
+ <label class="ps-form-label">
516
+ <i class="fa-solid fa-diagram-project"></i> Training Pipeline
517
+ </label>
518
+ <select class="mj-input" [ngModel]="selectedPipelineId" (ngModelChange)="onPipelineChange($event)" [disabled]="busy">
519
+ @for (p of engine.Pipelines; track p.ID) {
520
+ <option [value]="p.ID">{{ p.Name }} (Target: {{ p.TargetVariable || 'None' }})</option>
521
+ }
522
+ </select>
523
+ <span class="ps-form-hint">
524
+ The dataset, feature assembly graph, and leakage deny-list are derived from this pipeline.
525
+ </span>
526
+ </div>
527
+
528
+ <!-- Session Name & Goal -->
529
+ <div class="exp-grid-2">
530
+ <div class="ps-form-group">
531
+ <label class="ps-form-label">Session Name</label>
532
+ <input class="mj-input" type="text" [(ngModel)]="sessionName" placeholder="e.g. Churn Tournament Q3" [disabled]="busy" />
533
+ </div>
534
+ <div class="ps-form-group">
535
+ <label class="ps-form-label">Evaluation Metric</label>
536
+ <select class="mj-input" [(ngModel)]="successMetric" [disabled]="busy">
537
+ @if (problemType === 'classification') {
538
+ <option value="AUC">ROC-AUC (Default)</option>
539
+ <option value="F1">F1 Score</option>
540
+ <option value="Accuracy">Accuracy</option>
541
+ } @else {
542
+ <option value="RMSE">RMSE (Default)</option>
543
+ <option value="MAE">MAE</option>
544
+ <option value="R2">R-Squared</option>
545
+ }
546
+ </select>
547
+ </div>
548
+ </div>
549
+
550
+ <div class="ps-form-group">
551
+ <label class="ps-form-label">Experiment Goal</label>
552
+ <input class="mj-input" type="text" [(ngModel)]="goal" placeholder="e.g. Find best algorithm on holdout split" [disabled]="busy" />
553
+ </div>
554
+
555
+ <!-- Step 2: Algorithm Candidates -->
556
+ <div class="ps-form-group">
557
+ <div class="algo-bar-actions">
558
+ <label class="ps-form-label" style="margin-bottom:0">
559
+ <i class="fa-solid fa-shapes"></i> Candidate Algorithms ({{ selectedAlgoIds.size }} selected)
560
+ </label>
561
+ <div style="display:flex; gap:8px">
562
+ <button type="button" class="ps-link-btn" (click)="selectAllAlgos()" [disabled]="busy">Select All</button>
563
+ <span class="ps-muted">·</span>
564
+ <button type="button" class="ps-link-btn" (click)="selectRecommendedAlgos()" [disabled]="busy">Recommended</button>
565
+ <span class="ps-muted">·</span>
566
+ <button type="button" class="ps-link-btn" (click)="clearAlgos()" [disabled]="busy">Clear</button>
567
+ </div>
568
+ </div>
569
+
570
+ <div class="algo-selection-grid">
571
+ @for (algo of availableAlgorithms; track algo.ID) {
572
+ <div class="algo-select-card"
573
+ [class.selected]="isAlgoSelected(algo.ID)"
574
+ (click)="toggleAlgo(algo.ID)">
575
+ <input type="checkbox"
576
+ [checked]="isAlgoSelected(algo.ID)"
577
+ (click)="$event.stopPropagation()"
578
+ (change)="toggleAlgo(algo.ID)"
579
+ [disabled]="busy" />
580
+ <div class="algo-select-info">
581
+ <div class="algo-select-name">
582
+ <i [class]="getAlgoIcon(algo.DriverClass)"></i>
583
+ <span>{{ algo.Name }}</span>
584
+ </div>
585
+ @if (algo.Description) {
586
+ <div class="algo-select-desc">{{ algo.Description }}</div>
587
+ }
588
+ <div class="algo-select-tags">
589
+ @if (algo.SupportsFeatureImportance) {
590
+ <span class="ps-badge green" style="font-size:10px">Importance</span>
591
+ }
592
+ <span class="ps-tag" style="font-size:10px">{{ algo.DriverClass }}</span>
593
+ </div>
594
+ </div>
595
+ </div>
596
+ }
597
+ </div>
598
+ </div>
599
+
600
+ <!-- Step 3: Validation & Budget -->
601
+ <div class="exp-grid-3">
602
+ <div class="ps-form-group">
603
+ <label class="ps-form-label">Max Iterations (Trials)</label>
604
+ <input class="mj-input" type="number" min="1" max="100" [(ngModel)]="budgetRuns" [disabled]="busy" />
605
+ <span class="ps-form-hint">Total model training runs bounded</span>
606
+ </div>
607
+ <div class="ps-form-group">
608
+ <label class="ps-form-label">Compute Cost Cap ($)</label>
609
+ <input class="mj-input" type="number" min="1" max="500" [(ngModel)]="budgetCost" [disabled]="busy" />
610
+ <span class="ps-form-hint">Max spending for cloud / GPU compute</span>
611
+ </div>
612
+ <div class="ps-form-group">
613
+ <label class="ps-form-label">Wall-Clock Cap (Minutes)</label>
614
+ <input class="mj-input" type="number" min="5" max="300" [(ngModel)]="budgetMinutes" [disabled]="busy" />
615
+ <span class="ps-form-hint">Session timeouts if exceeded</span>
616
+ </div>
617
+ </div>
618
+
619
+ <div class="budget-banner">
620
+ <i class="fa-solid fa-shield-halved"></i>
621
+ <div style="font-size: 12px; color: var(--mj-text-secondary)">
622
+ <strong>Autonomous Guardrails:</strong> The session will run across waves, evaluate each candidate on a locked holdout fraction ({{ holdoutPercent }}%), rank them on the leaderboard by {{ successMetric }}, and automatically prune unpromising configurations.
623
+ </div>
624
+ </div>
625
+ </div>
626
+
627
+ <!-- Footer -->
628
+ <div class="ps-wizard-footer">
629
+ <button mjButton variant="secondary" size="md" (click)="cancel()" [disabled]="busy">
630
+ Cancel
631
+ </button>
632
+ <span class="ps-spacer"></span>
633
+ <button mjButton variant="primary" size="md" (click)="launch()" [disabled]="busy || !isValid">
634
+ @if (busy) {
635
+ <i class="fa-solid fa-spinner fa-spin"></i> Launching Experiment…
636
+ } @else {
637
+ <i class="fa-solid fa-play"></i> Launch Experiment
638
+ }
639
+ </button>
640
+ </div>
641
+ </div>
642
+ </div>
643
+ `, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-run-exp-dialog {\n max-width: 860px;\n width: 90vw;\n max-height: 88vh;\n display: flex;\n flex-direction: column;\n}\n\n.ps-run-exp-body {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.exp-grid-2 {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n}\n\n.exp-grid-3 {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n gap: 16px;\n}\n\n.algo-selection-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n gap: 12px;\n margin-top: 10px;\n}\n\n.algo-select-card {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--mj-border);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease-in-out;\n}\n\n.algo-select-card:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-hover, rgba(0, 0, 0, 0.02));\n}\n\n.algo-select-card.selected {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary-light, rgba(59, 130, 246, 0.08));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.algo-select-card input[type=\"checkbox\"] {\n margin-top: 3px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n}\n\n.algo-select-info {\n flex: 1;\n min-width: 0;\n}\n\n.algo-select-name {\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.algo-select-name i {\n color: var(--mj-brand-primary);\n}\n\n.algo-select-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n line-height: 1.3;\n}\n\n.algo-select-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 6px;\n}\n\n.algo-bar-actions {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.budget-banner {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-secondary, rgba(0, 0, 0, 0.03));\n border-radius: 8px;\n border: 1px dashed var(--mj-border);\n}\n\n.budget-banner i {\n font-size: 20px;\n color: var(--mj-status-warning);\n}\n"] }]
644
+ }], null, { engine: [{
645
+ type: Input
646
+ }], provider: [{
647
+ type: Input
648
+ }], currentUser: [{
649
+ type: Input
650
+ }], preselectedPipelineId: [{
651
+ type: Input
652
+ }], closed: [{
653
+ type: Output
654
+ }], started: [{
655
+ type: Output
656
+ }] }); })();
657
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRunExperimentModalComponent, { className: "PSRunExperimentModalComponent", filePath: "src/PredictiveStudio/components/ps-run-experiment-modal.component.ts", lineNumber: 205 }); })();
658
+ //# sourceMappingURL=ps-run-experiment-modal.component.js.map