@memberjunction/ng-dashboards 6.1.2 → 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
@@ -3,175 +3,764 @@ import { CommonModule } from '@angular/common';
3
3
  import { MJButtonDirective } from '@memberjunction/ng-ui-components';
4
4
  import { MJNotificationService } from '@memberjunction/ng-notifications';
5
5
  import { UUIDsEqual } from '@memberjunction/global';
6
- import { PredictiveStudioTrainModelOperation } from '@memberjunction/core-entities';
6
+ import { CompositeKey, Metadata } from '@memberjunction/core';
7
+ import { PredictiveStudioTrainModelOperation, UserInfoEngine } from '@memberjunction/core-entities';
8
+ import { NavigationService } from '@memberjunction/ng-shared';
9
+ import { AngularSplitModule } from 'angular-split';
10
+ import { PSPipelineWizardComponent } from './ps-pipeline-wizard.component';
7
11
  import { DOMINANCE_THRESHOLD_DEFAULT, parseDenyList, } from '@memberjunction/predictive-studio-core';
8
12
  import * as i0 from "@angular/core";
9
13
  import * as i1 from "@angular/common";
14
+ import * as i2 from "angular-split";
10
15
  const _forTrack0 = ($index, $item) => $item.ID;
11
- const _forTrack1 = ($index, $item) => $item.from + $item.to;
12
- const _forTrack2 = ($index, $item) => $item.id;
13
- const _forTrack3 = ($index, $item) => $item.k;
14
- const _forTrack4 = ($index, $item) => $item.Id;
16
+ const _forTrack1 = ($index, $item) => $item.Id;
17
+ const _forTrack2 = ($index, $item) => $item.from + "->" + $item.to;
18
+ const _forTrack3 = ($index, $item) => $item.id;
19
+ const _forTrack4 = ($index, $item) => $item.k;
15
20
  function PSPipelinesComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
16
21
  const _r1 = i0.ɵɵgetCurrentView();
17
- i0.ɵɵelementStart(0, "div", 1)(1, "span", 2);
18
- i0.ɵɵelement(2, "i", 3);
22
+ i0.ɵɵelementStart(0, "div", 1)(1, "span", 4);
23
+ i0.ɵɵelement(2, "i", 5);
19
24
  i0.ɵɵelementEnd();
20
25
  i0.ɵɵelementStart(3, "h3");
21
26
  i0.ɵɵtext(4, "No training pipelines yet");
22
27
  i0.ɵɵelementEnd();
23
28
  i0.ɵɵelementStart(5, "p");
24
- i0.ɵɵtext(6, " A training pipeline assembles features from your data, picks an algorithm, and trains a versioned model. The fastest way to build one is to describe your goal to the Model Development Agent \u2014 it designs the pipeline, guards against target leakage, and trains for you. ");
29
+ i0.ɵɵtext(6, " A training pipeline assembles features from your data, picks an algorithm, and trains a versioned model. You can build one step-by-step with the manual wizard or ask the Model Development Agent to design one for you. ");
25
30
  i0.ɵɵelementEnd();
26
- i0.ɵɵelementStart(7, "button", 4);
27
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_1_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.askAgent.emit(ctx_r1.starterPrompt)); });
28
- i0.ɵɵelement(8, "i", 5);
29
- i0.ɵɵtext(9, " Ask the agent to build one ");
30
- i0.ɵɵelementEnd()();
31
+ i0.ɵɵelementStart(7, "div", 6)(8, "button", 7);
32
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_1_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openWizard()); });
33
+ i0.ɵɵelement(9, "i", 8);
34
+ i0.ɵɵtext(10, " New Pipeline ");
35
+ i0.ɵɵelementEnd();
36
+ i0.ɵɵelementStart(11, "button", 9);
37
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_1_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.askAgent.emit(ctx_r1.starterPrompt)); });
38
+ i0.ɵɵelement(12, "i", 10);
39
+ i0.ɵɵtext(13, " Ask the agent ");
40
+ i0.ɵɵelementEnd()()();
31
41
  } }
32
- function PSPipelinesComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
42
+ function PSPipelinesComponent_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
33
43
  const _r4 = i0.ɵɵgetCurrentView();
34
- i0.ɵɵelementStart(0, "button", 63);
35
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_For_2_Template_button_click_0_listener() { const p_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.selectPipeline(p_r5.ID)); });
36
- i0.ɵɵelement(1, "i", 3);
37
- i0.ɵɵelementStart(2, "span", 64);
44
+ i0.ɵɵelementStart(0, "div", 12)(1, "button", 38);
45
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_1_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleCatalog()); });
46
+ i0.ɵɵelement(2, "i", 39);
47
+ i0.ɵɵelementEnd();
48
+ i0.ɵɵelementStart(3, "div", 40);
49
+ i0.ɵɵelement(4, "i", 5);
50
+ i0.ɵɵelementEnd()();
51
+ } }
52
+ function PSPipelinesComponent_Conditional_2_Conditional_3_For_26_Conditional_16_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵelementStart(0, "span", 63);
54
+ i0.ɵɵelement(1, "i", 64);
55
+ i0.ɵɵtext(2);
56
+ i0.ɵɵelementEnd();
57
+ } if (rf & 2) {
58
+ i0.ɵɵadvance(2);
59
+ i0.ɵɵtextInterpolate1(" ", ctx);
60
+ } }
61
+ function PSPipelinesComponent_Conditional_2_Conditional_3_For_26_Template(rf, ctx) { if (rf & 1) {
62
+ const _r6 = i0.ɵɵgetCurrentView();
63
+ i0.ɵɵelementStart(0, "button", 55);
64
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_For_26_Template_button_click_0_listener() { const p_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectPipeline(p_r7.ID)); });
65
+ i0.ɵɵelementStart(1, "div", 56)(2, "div", 57);
38
66
  i0.ɵɵtext(3);
39
67
  i0.ɵɵelementEnd();
40
- i0.ɵɵelementStart(4, "span", 65);
68
+ i0.ɵɵelementStart(4, "span", 58);
69
+ i0.ɵɵtext(5);
70
+ i0.ɵɵelementEnd()();
71
+ i0.ɵɵelementStart(6, "div", 59)(7, "span", 60);
72
+ i0.ɵɵtext(8);
73
+ i0.ɵɵelementEnd();
74
+ i0.ɵɵelementStart(9, "span");
75
+ i0.ɵɵtext(10, "\u2022");
76
+ i0.ɵɵelementEnd();
77
+ i0.ɵɵelementStart(11, "span", 61);
78
+ i0.ɵɵtext(12);
79
+ i0.ɵɵelementEnd()();
80
+ i0.ɵɵelementStart(13, "div", 62)(14, "span");
81
+ i0.ɵɵtext(15);
82
+ i0.ɵɵelementEnd();
83
+ i0.ɵɵconditionalCreate(16, PSPipelinesComponent_Conditional_2_Conditional_3_For_26_Conditional_16_Template, 3, 1, "span", 63);
84
+ i0.ɵɵelementEnd()();
85
+ } if (rf & 2) {
86
+ let tmp_20_0;
87
+ const p_r7 = ctx.$implicit;
88
+ const ctx_r1 = i0.ɵɵnextContext(3);
89
+ i0.ɵɵclassProp("on", ctx_r1.isSelectedPipeline(p_r7));
90
+ i0.ɵɵproperty("title", p_r7.Name);
91
+ i0.ɵɵadvance(3);
92
+ i0.ɵɵtextInterpolate(p_r7.Name);
93
+ i0.ɵɵadvance();
94
+ i0.ɵɵclassMap(ctx_r1.statusClass(p_r7.Status));
95
+ i0.ɵɵadvance();
96
+ i0.ɵɵtextInterpolate(p_r7.Status);
97
+ i0.ɵɵadvance(3);
98
+ i0.ɵɵtextInterpolate(ctx_r1.targetEntityName(p_r7));
99
+ i0.ɵɵadvance(4);
100
+ i0.ɵɵtextInterpolate(p_r7.TargetVariable || "Target");
101
+ i0.ɵɵadvance(3);
102
+ i0.ɵɵtextInterpolate(p_r7.ProblemType);
103
+ i0.ɵɵadvance();
104
+ i0.ɵɵconditional((tmp_20_0 = ctx_r1.bestMetricForPipeline(p_r7)) ? 16 : -1, tmp_20_0);
105
+ } }
106
+ function PSPipelinesComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
107
+ const _r5 = i0.ɵɵgetCurrentView();
108
+ i0.ɵɵelementStart(0, "as-split-area", 14)(1, "div", 41)(2, "div", 42)(3, "div", 43)(4, "h2");
41
109
  i0.ɵɵtext(5);
110
+ i0.ɵɵelementEnd();
111
+ i0.ɵɵelementStart(6, "div", 44)(7, "button", 45);
112
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.openWizard()); });
113
+ i0.ɵɵelement(8, "i", 8);
114
+ i0.ɵɵtext(9, " New ");
115
+ i0.ɵɵelementEnd();
116
+ i0.ɵɵelementStart(10, "button", 46);
117
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleCatalog()); });
118
+ i0.ɵɵelement(11, "i", 47);
119
+ i0.ɵɵelementEnd()()();
120
+ i0.ɵɵelementStart(12, "div", 48);
121
+ i0.ɵɵelement(13, "i", 49);
122
+ i0.ɵɵelementStart(14, "input", 50);
123
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_input_input_14_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onSearchInput($event)); });
42
124
  i0.ɵɵelementEnd()();
125
+ i0.ɵɵelementStart(15, "div", 51)(16, "button", 52);
126
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_16_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setFilter("all")); });
127
+ i0.ɵɵtext(17, "All");
128
+ i0.ɵɵelementEnd();
129
+ i0.ɵɵelementStart(18, "button", 52);
130
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setFilter("published")); });
131
+ i0.ɵɵtext(19, "Published");
132
+ i0.ɵɵelementEnd();
133
+ i0.ɵɵelementStart(20, "button", 52);
134
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setFilter("draft")); });
135
+ i0.ɵɵtext(21, "Draft");
136
+ i0.ɵɵelementEnd();
137
+ i0.ɵɵelementStart(22, "button", 52);
138
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_3_Template_button_click_22_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setFilter("core")); });
139
+ i0.ɵɵtext(23, "MJ Core");
140
+ i0.ɵɵelementEnd()()();
141
+ i0.ɵɵelementStart(24, "div", 53);
142
+ i0.ɵɵrepeaterCreate(25, PSPipelinesComponent_Conditional_2_Conditional_3_For_26_Template, 17, 11, "button", 54, _forTrack0);
143
+ i0.ɵɵelementEnd()()();
43
144
  } if (rf & 2) {
44
- const p_r5 = ctx.$implicit;
45
145
  const ctx_r1 = i0.ɵɵnextContext(2);
46
- i0.ɵɵclassProp("on", ctx_r1.isSelectedPipeline(p_r5));
47
- i0.ɵɵproperty("title", p_r5.Name);
146
+ i0.ɵɵproperty("size", ctx_r1.catalogSizePct)("minSize", 18)("maxSize", 50);
147
+ i0.ɵɵadvance(5);
148
+ i0.ɵɵtextInterpolate1("Pipelines (", ctx_r1.filteredPipelines.length, ")");
149
+ i0.ɵɵadvance(9);
150
+ i0.ɵɵproperty("value", ctx_r1.searchQuery);
151
+ i0.ɵɵadvance(2);
152
+ i0.ɵɵclassProp("active", ctx_r1.activeFilter === "all");
153
+ i0.ɵɵadvance(2);
154
+ i0.ɵɵclassProp("active", ctx_r1.activeFilter === "published");
155
+ i0.ɵɵadvance(2);
156
+ i0.ɵɵclassProp("active", ctx_r1.activeFilter === "draft");
157
+ i0.ɵɵadvance(2);
158
+ i0.ɵɵclassProp("active", ctx_r1.activeFilter === "core");
48
159
  i0.ɵɵadvance(3);
49
- i0.ɵɵtextInterpolate(p_r5.Name);
160
+ i0.ɵɵrepeater(ctx_r1.filteredPipelines);
161
+ } }
162
+ function PSPipelinesComponent_Conditional_2_Conditional_20_Conditional_2_Template(rf, ctx) { if (rf & 1) {
163
+ i0.ɵɵelementStart(0, "span", 63);
164
+ i0.ɵɵelement(1, "i", 64);
165
+ i0.ɵɵtext(2);
166
+ i0.ɵɵelementEnd();
167
+ } if (rf & 2) {
168
+ i0.ɵɵadvance(2);
169
+ i0.ɵɵtextInterpolate1(" ", ctx);
170
+ } }
171
+ function PSPipelinesComponent_Conditional_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
172
+ i0.ɵɵelementStart(0, "span", 58);
173
+ i0.ɵɵtext(1);
174
+ i0.ɵɵelementEnd();
175
+ i0.ɵɵconditionalCreate(2, PSPipelinesComponent_Conditional_2_Conditional_20_Conditional_2_Template, 3, 1, "span", 63);
176
+ } if (rf & 2) {
177
+ let tmp_4_0;
178
+ const ctx_r1 = i0.ɵɵnextContext(2);
179
+ i0.ɵɵclassMap(ctx_r1.statusClass(ctx_r1.selectedPipeline.Status));
50
180
  i0.ɵɵadvance();
51
- i0.ɵɵclassMap(ctx_r1.statusClass(p_r5.Status));
181
+ i0.ɵɵtextInterpolate(ctx_r1.selectedPipeline.Status);
52
182
  i0.ɵɵadvance();
53
- i0.ɵɵtextInterpolate(p_r5.Status);
183
+ i0.ɵɵconditional((tmp_4_0 = ctx_r1.bestMetricForPipeline(ctx_r1.selectedPipeline)) ? 2 : -1, tmp_4_0);
54
184
  } }
55
- function PSPipelinesComponent_Conditional_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
56
- i0.ɵɵelementStart(0, "span", 18);
57
- i0.ɵɵelement(1, "i", 66);
185
+ function PSPipelinesComponent_Conditional_2_Conditional_40_Template(rf, ctx) { if (rf & 1) {
186
+ i0.ɵɵelementStart(0, "span", 35);
187
+ i0.ɵɵelement(1, "i", 65);
58
188
  i0.ɵɵtext(2, " Unsaved");
59
189
  i0.ɵɵelementEnd();
60
190
  } }
61
- function PSPipelinesComponent_Conditional_2_For_36_Template(rf, ctx) { if (rf & 1) {
62
- i0.ɵɵnamespaceSVG();
63
- i0.ɵɵelement(0, "path");
191
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_69_Template(rf, ctx) { if (rf & 1) {
192
+ i0.ɵɵelementStart(0, "div", 89)(1, "span", 92);
193
+ i0.ɵɵtext(2);
194
+ i0.ɵɵelementEnd();
195
+ i0.ɵɵelementStart(3, "span", 109);
196
+ i0.ɵɵtext(4);
197
+ i0.ɵɵelementEnd()();
198
+ } if (rf & 2) {
199
+ const s_r9 = ctx.$implicit;
200
+ i0.ɵɵadvance(2);
201
+ i0.ɵɵtextInterpolate(s_r9.Ref || "Unnamed source");
202
+ i0.ɵɵadvance(2);
203
+ i0.ɵɵtextInterpolate(s_r9.Kind);
204
+ } }
205
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
206
+ i0.ɵɵelementStart(0, "span", 113);
207
+ i0.ɵɵtext(1);
208
+ i0.ɵɵelementEnd();
209
+ } if (rf & 2) {
210
+ const col_r12 = ctx.$implicit;
211
+ i0.ɵɵadvance();
212
+ i0.ɵɵtextInterpolate(col_r12);
213
+ } }
214
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Conditional_0_Template(rf, ctx) { if (rf & 1) {
215
+ i0.ɵɵelementStart(0, "div", 112);
216
+ i0.ɵɵrepeaterCreate(1, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Conditional_0_For_2_Template, 2, 1, "span", 113, i0.ɵɵrepeaterTrackByIdentity);
217
+ i0.ɵɵelementEnd();
218
+ } if (rf & 2) {
219
+ const cols_r13 = i0.ɵɵnextContext();
220
+ i0.ɵɵadvance();
221
+ i0.ɵɵrepeater(cols_r13);
222
+ } }
223
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Template(rf, ctx) { if (rf & 1) {
224
+ i0.ɵɵconditionalCreate(0, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Conditional_0_Template, 3, 0, "div", 112);
225
+ } if (rf & 2) {
226
+ i0.ɵɵconditional(ctx.length > 0 ? 0 : -1);
227
+ } }
228
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_7_Conditional_0_Template(rf, ctx) { if (rf & 1) {
229
+ i0.ɵɵelementStart(0, "div", 114);
230
+ i0.ɵɵelement(1, "i", 115);
231
+ i0.ɵɵelementStart(2, "span");
232
+ i0.ɵɵtext(3);
233
+ i0.ɵɵelementEnd()();
234
+ } if (rf & 2) {
235
+ const inputs_r14 = i0.ɵɵnextContext();
236
+ i0.ɵɵadvance(3);
237
+ i0.ɵɵtextInterpolate1("From: ", inputs_r14.join(", "));
238
+ } }
239
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_7_Template(rf, ctx) { if (rf & 1) {
240
+ i0.ɵɵconditionalCreate(0, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_7_Conditional_0_Template, 4, 1, "div", 114);
241
+ } if (rf & 2) {
242
+ i0.ɵɵconditional(ctx.length > 0 ? 0 : -1);
243
+ } }
244
+ function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Template(rf, ctx) { if (rf & 1) {
245
+ const _r10 = i0.ɵɵgetCurrentView();
246
+ i0.ɵɵelementStart(0, "div", 110);
247
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Template_div_click_0_listener() { const st_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectStep(st_r11.Id)); });
248
+ i0.ɵɵelementStart(1, "div", 111)(2, "span", 92);
249
+ i0.ɵɵtext(3);
250
+ i0.ɵɵelementEnd();
251
+ i0.ɵɵelementStart(4, "span", 109);
252
+ i0.ɵɵtext(5);
253
+ i0.ɵɵelementEnd()();
254
+ i0.ɵɵconditionalCreate(6, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_6_Template, 1, 1);
255
+ i0.ɵɵconditionalCreate(7, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Conditional_7_Template, 1, 1);
256
+ i0.ɵɵelementEnd();
257
+ } if (rf & 2) {
258
+ let tmp_14_0;
259
+ let tmp_15_0;
260
+ const st_r11 = ctx.$implicit;
261
+ const ctx_r1 = i0.ɵɵnextContext(3);
262
+ i0.ɵɵadvance(3);
263
+ i0.ɵɵtextInterpolate(st_r11.Label || st_r11.Kind);
264
+ i0.ɵɵadvance(2);
265
+ i0.ɵɵtextInterpolate(st_r11.Kind);
266
+ i0.ɵɵadvance();
267
+ i0.ɵɵconditional((tmp_14_0 = ctx_r1.getStepColumns(st_r11)) ? 6 : -1, tmp_14_0);
268
+ i0.ɵɵadvance();
269
+ i0.ɵɵconditional((tmp_15_0 = ctx_r1.getStepInputs(st_r11)) ? 7 : -1, tmp_15_0);
270
+ } }
271
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_99_Template(rf, ctx) { if (rf & 1) {
272
+ i0.ɵɵelementStart(0, "span", 100);
273
+ i0.ɵɵtext(1, "No deny-list columns configured.");
274
+ i0.ɵɵelementEnd();
275
+ } }
276
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_100_For_1_Template(rf, ctx) { if (rf & 1) {
277
+ i0.ɵɵelementStart(0, "span", 116);
278
+ i0.ɵɵtext(1);
279
+ i0.ɵɵelementEnd();
280
+ } if (rf & 2) {
281
+ const col_r15 = ctx.$implicit;
282
+ i0.ɵɵadvance();
283
+ i0.ɵɵtextInterpolate(col_r15);
284
+ } }
285
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_100_Template(rf, ctx) { if (rf & 1) {
286
+ i0.ɵɵrepeaterCreate(0, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_100_For_1_Template, 2, 1, "span", 116, i0.ɵɵrepeaterTrackByIdentity);
287
+ } if (rf & 2) {
288
+ const ctx_r1 = i0.ɵɵnextContext(3);
289
+ i0.ɵɵrepeater(ctx_r1.editLeakage.DenyFields);
290
+ } }
291
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_151_Template(rf, ctx) { if (rf & 1) {
292
+ i0.ɵɵelementStart(0, "div", 102);
293
+ i0.ɵɵelement(1, "i", 117);
294
+ i0.ɵɵtext(2, " No models trained yet for this pipeline. Click ");
295
+ i0.ɵɵelementStart(3, "strong");
296
+ i0.ɵɵtext(4, "Train");
297
+ i0.ɵɵelementEnd();
298
+ i0.ɵɵtext(5, " to execute the first training run. ");
299
+ i0.ɵɵelementEnd();
300
+ } }
301
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Conditional_6_Template(rf, ctx) { if (rf & 1) {
302
+ i0.ɵɵelementStart(0, "span", 122);
303
+ i0.ɵɵelement(1, "i", 126);
304
+ i0.ɵɵtext(2, " Winner");
305
+ i0.ɵɵelementEnd();
306
+ } }
307
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Template(rf, ctx) { if (rf & 1) {
308
+ const _r16 = i0.ɵɵgetCurrentView();
309
+ i0.ɵɵelementStart(0, "tr", 119);
310
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Template_tr_click_0_listener() { const m_r17 = i0.ɵɵrestoreView(_r16).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.openModelRecord(m_r17.ID)); });
311
+ i0.ɵɵelementStart(1, "td")(2, "div", 120)(3, "a", 121);
312
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Template_a_click_3_listener($event) { const m_r17 = i0.ɵɵrestoreView(_r16).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); $event.stopPropagation(); return i0.ɵɵresetView(ctx_r1.openModelRecord(m_r17.ID)); });
313
+ i0.ɵɵelementStart(4, "strong");
314
+ i0.ɵɵtext(5);
315
+ i0.ɵɵelementEnd()();
316
+ i0.ɵɵconditionalCreate(6, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Conditional_6_Template, 3, 0, "span", 122);
317
+ i0.ɵɵelementEnd()();
318
+ i0.ɵɵelementStart(7, "td");
319
+ i0.ɵɵtext(8);
320
+ i0.ɵɵelementEnd();
321
+ i0.ɵɵelementStart(9, "td")(10, "strong");
322
+ i0.ɵɵtext(11);
323
+ i0.ɵɵelementEnd()();
324
+ i0.ɵɵelementStart(12, "td")(13, "span", 58);
325
+ i0.ɵɵtext(14);
326
+ i0.ɵɵelementEnd()();
327
+ i0.ɵɵelementStart(15, "td");
328
+ i0.ɵɵtext(16);
329
+ i0.ɵɵpipe(17, "date");
330
+ i0.ɵɵelementEnd();
331
+ i0.ɵɵelementStart(18, "td", 123)(19, "button", 124);
332
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Template_button_click_19_listener($event) { const m_r17 = i0.ɵɵrestoreView(_r16).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); $event.stopPropagation(); return i0.ɵɵresetView(ctx_r1.openModelRecord(m_r17.ID)); });
333
+ i0.ɵɵelement(20, "i", 125);
334
+ i0.ɵɵelementEnd()()();
335
+ } if (rf & 2) {
336
+ const m_r17 = ctx.$implicit;
337
+ const ctx_r1 = i0.ɵɵnextContext(4);
338
+ i0.ɵɵclassProp("winner-row", m_r17.Status === "Published");
339
+ i0.ɵɵadvance(5);
340
+ i0.ɵɵtextInterpolate1("v", m_r17.Version);
341
+ i0.ɵɵadvance();
342
+ i0.ɵɵconditional(m_r17.Status === "Published" ? 6 : -1);
343
+ i0.ɵɵadvance(2);
344
+ i0.ɵɵtextInterpolate(m_r17.Algorithm || "Algorithm");
345
+ i0.ɵɵadvance(3);
346
+ i0.ɵɵtextInterpolate(ctx_r1.formatHoldout(m_r17));
347
+ i0.ɵɵadvance(2);
348
+ i0.ɵɵclassMap(ctx_r1.statusClass(m_r17.Status));
349
+ i0.ɵɵadvance();
350
+ i0.ɵɵtextInterpolate(m_r17.Status);
351
+ i0.ɵɵadvance(2);
352
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(17, 10, m_r17.TrainedAt, "short"));
353
+ } }
354
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_Template(rf, ctx) { if (rf & 1) {
355
+ i0.ɵɵelementStart(0, "table", 103)(1, "thead")(2, "tr")(3, "th");
356
+ i0.ɵɵtext(4, "Version");
357
+ i0.ɵɵelementEnd();
358
+ i0.ɵɵelementStart(5, "th");
359
+ i0.ɵɵtext(6, "Algorithm");
360
+ i0.ɵɵelementEnd();
361
+ i0.ɵɵelementStart(7, "th");
362
+ i0.ɵɵtext(8, "Holdout Score");
363
+ i0.ɵɵelementEnd();
364
+ i0.ɵɵelementStart(9, "th");
365
+ i0.ɵɵtext(10, "Status");
366
+ i0.ɵɵelementEnd();
367
+ i0.ɵɵelementStart(11, "th");
368
+ i0.ɵɵtext(12, "Trained At");
369
+ i0.ɵɵelementEnd();
370
+ i0.ɵɵelement(13, "th");
371
+ i0.ɵɵelementEnd()();
372
+ i0.ɵɵelementStart(14, "tbody");
373
+ i0.ɵɵrepeaterCreate(15, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_For_16_Template, 21, 13, "tr", 118, _forTrack0);
374
+ i0.ɵɵelementEnd()();
375
+ } if (rf & 2) {
376
+ const ctx_r1 = i0.ɵɵnextContext(3);
377
+ i0.ɵɵadvance(15);
378
+ i0.ɵɵrepeater(ctx_r1.pipelineModels);
379
+ } }
380
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_164_Template(rf, ctx) { if (rf & 1) {
381
+ const _r18 = i0.ɵɵgetCurrentView();
382
+ i0.ɵɵelementStart(0, "button", 127);
383
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_164_Template_button_click_0_listener() { const pub_r19 = i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.openModelRecord(pub_r19.ID)); });
384
+ i0.ɵɵelement(1, "i", 125);
385
+ i0.ɵɵtext(2, " Open Model Record ");
386
+ i0.ɵɵelementEnd();
387
+ i0.ɵɵelementStart(3, "button", 128);
388
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_164_Template_button_click_3_listener() { const pub_r19 = i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.viewTrainingRuns(pub_r19.ID)); });
389
+ i0.ɵɵelement(4, "i", 129);
390
+ i0.ɵɵtext(5, " View Training Runs ");
391
+ i0.ɵɵelementEnd();
392
+ } }
393
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_170_Template(rf, ctx) { if (rf & 1) {
394
+ i0.ɵɵelementStart(0, "span");
395
+ i0.ɵɵtext(1);
396
+ i0.ɵɵelementEnd();
397
+ } if (rf & 2) {
398
+ const pub_r20 = ctx;
399
+ i0.ɵɵadvance();
400
+ i0.ɵɵtextInterpolate3("v", pub_r20.Version, " (", pub_r20.Algorithm || "Model", ") \u2022 ID: ", pub_r20.ID.slice(0, 8), "\u2026");
401
+ } }
402
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_171_Template(rf, ctx) { if (rf & 1) {
403
+ i0.ɵɵelementStart(0, "span", 106);
404
+ i0.ɵɵtext(1, "No published model serving live predictions yet.");
405
+ i0.ɵɵelementEnd();
406
+ } }
407
+ function PSPipelinesComponent_Conditional_2_Conditional_41_Template(rf, ctx) { if (rf & 1) {
408
+ const _r8 = i0.ɵɵgetCurrentView();
409
+ i0.ɵɵelementStart(0, "div", 36)(1, "div", 66)(2, "div", 67)(3, "div", 68)(4, "span", 69);
410
+ i0.ɵɵtext(5, "1");
411
+ i0.ɵɵelementEnd();
412
+ i0.ɵɵelementStart(6, "div")(7, "h3");
413
+ i0.ɵɵtext(8, "Target Definition & Temporal Decision Boundary");
414
+ i0.ɵɵelementEnd();
415
+ i0.ɵɵelementStart(9, "span", 70);
416
+ i0.ɵɵtext(10, "Defines the training unit entity, target variable, problem type, and point-in-time cutoff.");
417
+ i0.ɵɵelementEnd()()()();
418
+ i0.ɵɵelementStart(11, "div", 71)(12, "div", 72)(13, "label");
419
+ i0.ɵɵtext(14, "Target Entity");
420
+ i0.ɵɵelementEnd();
421
+ i0.ɵɵelementStart(15, "div", 73);
422
+ i0.ɵɵelement(16, "i", 74);
423
+ i0.ɵɵtext(17);
424
+ i0.ɵɵelementEnd();
425
+ i0.ɵɵelementStart(18, "div", 75);
426
+ i0.ɵɵtext(19, "Primary training unit");
427
+ i0.ɵɵelementEnd()();
428
+ i0.ɵɵelementStart(20, "div", 72)(21, "label");
429
+ i0.ɵɵtext(22, "Target Variable");
430
+ i0.ɵɵelementEnd();
431
+ i0.ɵɵelementStart(23, "div", 73);
432
+ i0.ɵɵelement(24, "i", 76);
433
+ i0.ɵɵtext(25);
434
+ i0.ɵɵelementEnd();
435
+ i0.ɵɵelementStart(26, "div", 75);
436
+ i0.ɵɵtext(27);
437
+ i0.ɵɵelementEnd()();
438
+ i0.ɵɵelementStart(28, "div", 72)(29, "label");
439
+ i0.ɵɵtext(30, "Problem Type");
440
+ i0.ɵɵelementEnd();
441
+ i0.ɵɵelementStart(31, "div", 73);
442
+ i0.ɵɵelement(32, "i", 77);
443
+ i0.ɵɵtext(33);
444
+ i0.ɵɵelementEnd();
445
+ i0.ɵɵelementStart(34, "div", 75);
446
+ i0.ɵɵtext(35, "Optimization discipline");
447
+ i0.ɵɵelementEnd()();
448
+ i0.ɵɵelementStart(36, "div", 78)(37, "label");
449
+ i0.ɵɵtext(38, "As-Of Temporal Anchor");
450
+ i0.ɵɵelementEnd();
451
+ i0.ɵɵelementStart(39, "div", 73);
452
+ i0.ɵɵelement(40, "i", 79);
453
+ i0.ɵɵtext(41);
454
+ i0.ɵɵelementEnd();
455
+ i0.ɵɵelementStart(42, "div", 75);
456
+ i0.ɵɵtext(43, "Temporal cutoff to prevent leakage");
457
+ i0.ɵɵelementEnd()()()();
458
+ i0.ɵɵelementStart(44, "div", 66)(45, "div", 67)(46, "div", 68)(47, "span", 69);
459
+ i0.ɵɵtext(48, "2");
460
+ i0.ɵɵelementEnd();
461
+ i0.ɵɵelementStart(49, "div")(50, "h3");
462
+ i0.ɵɵtext(51, "Feature Architecture & Relational Data Sources");
463
+ i0.ɵɵelementEnd();
464
+ i0.ɵɵelementStart(52, "span", 70);
465
+ i0.ɵɵtext(53, "Combines core entity attributes with linked relational features across the database.");
466
+ i0.ɵɵelementEnd()()();
467
+ i0.ɵɵelementStart(54, "div", 80)(55, "button", 81);
468
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Template_button_click_55_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.addSource()); });
469
+ i0.ɵɵelement(56, "i", 8);
470
+ i0.ɵɵtext(57, " Add Source");
471
+ i0.ɵɵelementEnd();
472
+ i0.ɵɵelementStart(58, "button", 82);
473
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_41_Template_button_click_58_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.addStep()); });
474
+ i0.ɵɵelement(59, "i", 83);
475
+ i0.ɵɵtext(60, " Add Step");
476
+ i0.ɵɵelementEnd()()();
477
+ i0.ɵɵelementStart(61, "div", 84)(62, "div", 85)(63, "div", 86)(64, "span");
478
+ i0.ɵɵelement(65, "i", 87);
479
+ i0.ɵɵtext(66);
480
+ i0.ɵɵelementEnd()();
481
+ i0.ɵɵelementStart(67, "div", 88);
482
+ i0.ɵɵrepeaterCreate(68, PSPipelinesComponent_Conditional_2_Conditional_41_For_69_Template, 5, 2, "div", 89, i0.ɵɵrepeaterTrackByIndex);
483
+ i0.ɵɵelementEnd()();
484
+ i0.ɵɵelementStart(70, "div", 85)(71, "div", 86)(72, "span");
485
+ i0.ɵɵelement(73, "i", 83);
486
+ i0.ɵɵtext(74);
487
+ i0.ɵɵelementEnd()();
488
+ i0.ɵɵelementStart(75, "div", 88);
489
+ i0.ɵɵrepeaterCreate(76, PSPipelinesComponent_Conditional_2_Conditional_41_For_77_Template, 8, 4, "div", 90, _forTrack1);
490
+ i0.ɵɵelementEnd()();
491
+ i0.ɵɵelementStart(78, "div", 85)(79, "div", 86)(80, "span");
492
+ i0.ɵɵelement(81, "i", 91);
493
+ i0.ɵɵtext(82, " Selected Algorithm");
494
+ i0.ɵɵelementEnd()();
495
+ i0.ɵɵelementStart(83, "div", 88)(84, "div", 89)(85, "span", 92);
496
+ i0.ɵɵtext(86);
497
+ i0.ɵɵelementEnd();
498
+ i0.ɵɵelementStart(87, "span", 93);
499
+ i0.ɵɵtext(88, "Active");
500
+ i0.ɵɵelementEnd()()()()();
501
+ i0.ɵɵelementStart(89, "div", 94);
502
+ i0.ɵɵelement(90, "i", 95);
503
+ i0.ɵɵelementStart(91, "div", 96)(92, "div", 97);
504
+ i0.ɵɵtext(93, "Leakage Guard Active");
505
+ i0.ɵɵelementEnd();
506
+ i0.ɵɵelementStart(94, "div", 98);
507
+ i0.ɵɵtext(95, " Deny-listed columns are excluded prior to matrix assembly. Dominance threshold: ");
508
+ i0.ɵɵelementStart(96, "strong");
509
+ i0.ɵɵtext(97);
510
+ i0.ɵɵelementEnd()();
511
+ i0.ɵɵelementStart(98, "div", 99);
512
+ i0.ɵɵconditionalCreate(99, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_99_Template, 2, 0, "span", 100)(100, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_100_Template, 2, 0);
513
+ i0.ɵɵelementEnd()()()();
514
+ i0.ɵɵelementStart(101, "div", 66)(102, "div", 67)(103, "div", 68)(104, "span", 69);
515
+ i0.ɵɵtext(105, "3");
516
+ i0.ɵɵelementEnd();
517
+ i0.ɵɵelementStart(106, "div")(107, "h3");
518
+ i0.ɵɵtext(108, "Fitted Preprocessing & Validation Discipline");
519
+ i0.ɵɵelementEnd();
520
+ i0.ɵɵelementStart(109, "span", 70);
521
+ i0.ɵɵtext(110, "Parameters fitted once on training folds and frozen with the model artifact for inference anti-skew.");
522
+ i0.ɵɵelementEnd()()()();
523
+ i0.ɵɵelementStart(111, "div", 71)(112, "div", 72)(113, "label");
524
+ i0.ɵɵtext(114, "Validation Strategy");
525
+ i0.ɵɵelementEnd();
526
+ i0.ɵɵelementStart(115, "div", 73);
527
+ i0.ɵɵtext(116);
528
+ i0.ɵɵelementEnd();
529
+ i0.ɵɵelementStart(117, "div", 75);
530
+ i0.ɵɵtext(118, "Cross-validation split");
531
+ i0.ɵɵelementEnd()();
532
+ i0.ɵɵelementStart(119, "div", 72)(120, "label");
533
+ i0.ɵɵtext(121, "Locked Holdout");
534
+ i0.ɵɵelementEnd();
535
+ i0.ɵɵelementStart(122, "div", 73);
536
+ i0.ɵɵtext(123);
537
+ i0.ɵɵelementEnd();
538
+ i0.ɵɵelementStart(124, "div", 75);
539
+ i0.ɵɵtext(125, "Honest benchmark score");
540
+ i0.ɵɵelementEnd()();
541
+ i0.ɵɵelementStart(126, "div", 72)(127, "label");
542
+ i0.ɵɵtext(128, "Folds / Split");
543
+ i0.ɵɵelementEnd();
544
+ i0.ɵɵelementStart(129, "div", 73);
545
+ i0.ɵɵtext(130);
546
+ i0.ɵɵelementEnd();
547
+ i0.ɵɵelementStart(131, "div", 75);
548
+ i0.ɵɵtext(132, "Training evaluation");
549
+ i0.ɵɵelementEnd()();
550
+ i0.ɵɵelementStart(133, "div", 72)(134, "label");
551
+ i0.ɵɵtext(135, "Anti-Skew Guarantee");
552
+ i0.ɵɵelementEnd();
553
+ i0.ɵɵelementStart(136, "div", 73);
554
+ i0.ɵɵelement(137, "i", 101);
555
+ i0.ɵɵtext(138, " Enforced");
556
+ i0.ɵɵelementEnd();
557
+ i0.ɵɵelementStart(139, "div", 75);
558
+ i0.ɵɵtext(140, "Frozen params travel with model");
559
+ i0.ɵɵelementEnd()()()();
560
+ i0.ɵɵelementStart(141, "div", 66)(142, "div", 67)(143, "div", 68)(144, "span", 69);
561
+ i0.ɵɵtext(145, "4");
562
+ i0.ɵɵelementEnd();
563
+ i0.ɵɵelementStart(146, "div")(147, "h3");
564
+ i0.ɵɵtext(148, "Multi-Algorithm Tournament Leaderboard");
565
+ i0.ɵɵelementEnd();
566
+ i0.ɵɵelementStart(149, "span", 70);
567
+ i0.ɵɵtext(150, "Models trained and scored on locked holdout data for this pipeline.");
568
+ i0.ɵɵelementEnd()()()();
569
+ i0.ɵɵconditionalCreate(151, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_151_Template, 6, 0, "div", 102)(152, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_152_Template, 17, 0, "table", 103);
570
+ i0.ɵɵelementEnd();
571
+ i0.ɵɵelementStart(153, "div", 66)(154, "div", 67)(155, "div", 68)(156, "span", 69);
572
+ i0.ɵɵtext(157, "5");
573
+ i0.ɵɵelementEnd();
574
+ i0.ɵɵelementStart(158, "div")(159, "h3");
575
+ i0.ɵɵtext(160, "Production Serving & Model Registry");
576
+ i0.ɵɵelementEnd();
577
+ i0.ɵɵelementStart(161, "span", 70);
578
+ i0.ɵɵtext(162, "Active inference wiring and model registry status.");
579
+ i0.ɵɵelementEnd()()();
580
+ i0.ɵɵelementStart(163, "div", 104);
581
+ i0.ɵɵconditionalCreate(164, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_164_Template, 6, 0);
582
+ i0.ɵɵelementEnd()();
583
+ i0.ɵɵelementStart(165, "div", 105)(166, "div", 72)(167, "label");
584
+ i0.ɵɵtext(168, "Active Serving Model");
585
+ i0.ɵɵelementEnd();
586
+ i0.ɵɵelementStart(169, "div", 73);
587
+ i0.ɵɵconditionalCreate(170, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_170_Template, 2, 3, "span")(171, PSPipelinesComponent_Conditional_2_Conditional_41_Conditional_171_Template, 2, 0, "span", 106);
588
+ i0.ɵɵelementEnd();
589
+ i0.ɵɵelementStart(172, "div", 75);
590
+ i0.ɵɵtext(173);
591
+ i0.ɵɵelementEnd()();
592
+ i0.ɵɵelementStart(174, "div", 107)(175, "label");
593
+ i0.ɵɵtext(176, "Inference Readiness");
594
+ i0.ɵɵelementEnd();
595
+ i0.ɵɵelementStart(177, "div", 108);
596
+ i0.ɵɵelement(178, "i", 30);
597
+ i0.ɵɵtext(179, " Ready ");
598
+ i0.ɵɵelementEnd();
599
+ i0.ɵɵelementStart(180, "div", 75);
600
+ i0.ɵɵtext(181, "Batch & on-demand scoring enabled");
601
+ i0.ɵɵelementEnd()()()()();
64
602
  } if (rf & 2) {
65
- const edge_r6 = ctx.$implicit;
603
+ let tmp_18_0;
604
+ let tmp_19_0;
66
605
  const ctx_r1 = i0.ɵɵnextContext(2);
67
- i0.ɵɵattribute("d", ctx_r1.edgePath(edge_r6));
606
+ i0.ɵɵadvance(17);
607
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.targetEntityName(ctx_r1.selectedPipeline));
608
+ i0.ɵɵadvance(8);
609
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.editTargetVariable || "Not set");
610
+ i0.ɵɵadvance(2);
611
+ i0.ɵɵtextInterpolate1("", ctx_r1.editProblemType, " outcome");
612
+ i0.ɵɵadvance(6);
613
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.editProblemType);
614
+ i0.ɵɵadvance(8);
615
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.asOfLabel);
616
+ i0.ɵɵadvance(25);
617
+ i0.ɵɵtextInterpolate1(" Feed-In Sources (", ctx_r1.editSources.length, ")");
618
+ i0.ɵɵadvance(2);
619
+ i0.ɵɵrepeater(ctx_r1.editSources);
620
+ i0.ɵɵadvance(6);
621
+ i0.ɵɵtextInterpolate1(" Feature Transforms (", ctx_r1.editSteps.length, ")");
622
+ i0.ɵɵadvance(2);
623
+ i0.ɵɵrepeater(ctx_r1.editSteps);
624
+ i0.ɵɵadvance(10);
625
+ i0.ɵɵtextInterpolate(ctx_r1.selectedAlgorithmName);
626
+ i0.ɵɵadvance(11);
627
+ i0.ɵɵtextInterpolate(ctx_r1.editLeakage.SingleFeatureDominanceThreshold);
628
+ i0.ɵɵadvance(2);
629
+ i0.ɵɵconditional(ctx_r1.editLeakage.DenyFields.length === 0 ? 99 : 100);
630
+ i0.ɵɵadvance(17);
631
+ i0.ɵɵtextInterpolate(ctx_r1.editValidation.Strategy);
632
+ i0.ɵɵadvance(7);
633
+ i0.ɵɵtextInterpolate1("", (ctx_r1.editValidation.LockedHoldoutFraction * 100).toFixed(0), "%");
634
+ i0.ɵɵadvance(7);
635
+ i0.ɵɵtextInterpolate(ctx_r1.editValidation.Strategy === "kfold" ? (ctx_r1.editValidation.K || 5) + " Folds" : ((ctx_r1.editValidation.TestSize || 0.2) * 100).toFixed(0) + "% Test");
636
+ i0.ɵɵadvance(21);
637
+ i0.ɵɵconditional(ctx_r1.pipelineModels.length === 0 ? 151 : 152);
638
+ i0.ɵɵadvance(13);
639
+ i0.ɵɵconditional((tmp_18_0 = ctx_r1.publishedModel) ? 164 : -1, tmp_18_0);
640
+ i0.ɵɵadvance(6);
641
+ i0.ɵɵconditional((tmp_19_0 = ctx_r1.publishedModel) ? 170 : 171, tmp_19_0);
642
+ i0.ɵɵadvance(3);
643
+ i0.ɵɵtextInterpolate2("Model: ", (ctx_r1.publishedModel == null ? null : ctx_r1.publishedModel.Status) || "None", " \u2022 Pipeline: ", ctx_r1.selectedPipeline == null ? null : ctx_r1.selectedPipeline.Status);
68
644
  } }
69
- function PSPipelinesComponent_Conditional_2_For_38_Conditional_5_Template(rf, ctx) { if (rf & 1) {
70
- i0.ɵɵelementStart(0, "span", 69);
645
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_36_Template(rf, ctx) { if (rf & 1) {
646
+ i0.ɵɵnamespaceSVG();
647
+ i0.ɵɵelement(0, "path", 164);
648
+ } if (rf & 2) {
649
+ const edge_r22 = ctx.$implicit;
650
+ const ctx_r1 = i0.ɵɵnextContext(3);
651
+ i0.ɵɵclassProp("active", ctx_r1.isEdgeActive(edge_r22));
652
+ i0.ɵɵattribute("d", ctx_r1.edgePath(edge_r22))("marker-end", ctx_r1.isEdgeActive(edge_r22) ? "url(#ps-arrow-active)" : "url(#ps-arrow)");
653
+ } }
654
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_5_Template(rf, ctx) { if (rf & 1) {
655
+ i0.ɵɵelementStart(0, "span", 109);
71
656
  i0.ɵɵtext(1);
72
657
  i0.ɵɵelementEnd();
73
658
  } if (rf & 2) {
74
- const node_r8 = i0.ɵɵnextContext().$implicit;
659
+ const node_r24 = i0.ɵɵnextContext().$implicit;
75
660
  i0.ɵɵadvance();
76
- i0.ɵɵtextInterpolate(node_r8.tag);
661
+ i0.ɵɵtextInterpolate(node_r24.tag);
77
662
  } }
78
- function PSPipelinesComponent_Conditional_2_For_38_For_8_Template(rf, ctx) { if (rf & 1) {
79
- i0.ɵɵelementStart(0, "div", 71)(1, "span", 74);
663
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_For_8_Template(rf, ctx) { if (rf & 1) {
664
+ i0.ɵɵelementStart(0, "div", 170)(1, "span", 173);
80
665
  i0.ɵɵtext(2);
81
666
  i0.ɵɵelementEnd();
82
- i0.ɵɵelementStart(3, "span");
667
+ i0.ɵɵelementStart(3, "span", 174);
83
668
  i0.ɵɵtext(4);
84
669
  i0.ɵɵelementEnd()();
85
670
  } if (rf & 2) {
86
- const row_r9 = ctx.$implicit;
87
- i0.ɵɵadvance(2);
88
- i0.ɵɵtextInterpolate(row_r9.k);
671
+ const row_r25 = ctx.$implicit;
89
672
  i0.ɵɵadvance(2);
90
- i0.ɵɵtextInterpolate(row_r9.v);
673
+ i0.ɵɵtextInterpolate(row_r25.k);
674
+ i0.ɵɵadvance();
675
+ i0.ɵɵproperty("title", row_r25.v);
676
+ i0.ɵɵadvance();
677
+ i0.ɵɵtextInterpolate(row_r25.v);
91
678
  } }
92
- function PSPipelinesComponent_Conditional_2_For_38_Conditional_9_Template(rf, ctx) { if (rf & 1) {
93
- i0.ɵɵelement(0, "span", 72);
679
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_9_Template(rf, ctx) { if (rf & 1) {
680
+ i0.ɵɵelement(0, "span", 171);
94
681
  } }
95
- function PSPipelinesComponent_Conditional_2_For_38_Conditional_10_Template(rf, ctx) { if (rf & 1) {
96
- i0.ɵɵelement(0, "span", 73);
682
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_10_Template(rf, ctx) { if (rf & 1) {
683
+ i0.ɵɵelement(0, "span", 172);
97
684
  } }
98
- function PSPipelinesComponent_Conditional_2_For_38_Template(rf, ctx) { if (rf & 1) {
99
- const _r7 = i0.ɵɵgetCurrentView();
100
- i0.ɵɵelementStart(0, "div", 67);
101
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_For_38_Template_div_click_0_listener() { const node_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.selectNode(node_r8.id)); });
102
- i0.ɵɵelementStart(1, "div", 68);
103
- i0.ɵɵelement(2, "i", 42);
104
- i0.ɵɵelementStart(3, "span");
685
+ function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Template(rf, ctx) { if (rf & 1) {
686
+ const _r23 = i0.ɵɵgetCurrentView();
687
+ i0.ɵɵelementStart(0, "div", 165);
688
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Template_div_click_0_listener() { const node_r24 = i0.ɵɵrestoreView(_r23).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectNode(node_r24.id)); });
689
+ i0.ɵɵelementStart(1, "div", 166);
690
+ i0.ɵɵelement(2, "i", 167);
691
+ i0.ɵɵelementStart(3, "span", 168);
105
692
  i0.ɵɵtext(4);
106
693
  i0.ɵɵelementEnd();
107
- i0.ɵɵconditionalCreate(5, PSPipelinesComponent_Conditional_2_For_38_Conditional_5_Template, 2, 1, "span", 69);
694
+ i0.ɵɵconditionalCreate(5, PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_5_Template, 2, 1, "span", 109);
108
695
  i0.ɵɵelementEnd();
109
- i0.ɵɵelementStart(6, "div", 70);
110
- i0.ɵɵrepeaterCreate(7, PSPipelinesComponent_Conditional_2_For_38_For_8_Template, 5, 2, "div", 71, _forTrack3);
696
+ i0.ɵɵelementStart(6, "div", 169);
697
+ i0.ɵɵrepeaterCreate(7, PSPipelinesComponent_Conditional_2_Conditional_42_For_38_For_8_Template, 5, 3, "div", 170, _forTrack4);
111
698
  i0.ɵɵelementEnd();
112
- i0.ɵɵconditionalCreate(9, PSPipelinesComponent_Conditional_2_For_38_Conditional_9_Template, 1, 0, "span", 72);
113
- i0.ɵɵconditionalCreate(10, PSPipelinesComponent_Conditional_2_For_38_Conditional_10_Template, 1, 0, "span", 73);
699
+ i0.ɵɵconditionalCreate(9, PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_9_Template, 1, 0, "span", 171);
700
+ i0.ɵɵconditionalCreate(10, PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Conditional_10_Template, 1, 0, "span", 172);
114
701
  i0.ɵɵelementEnd();
115
702
  } if (rf & 2) {
116
- const node_r8 = ctx.$implicit;
117
- const ctx_r1 = i0.ɵɵnextContext(2);
118
- i0.ɵɵstyleProp("left", node_r8.x, "px")("top", node_r8.y, "px");
119
- i0.ɵɵclassProp("selected", node_r8.id === ctx_r1.selectedId);
120
- i0.ɵɵproperty("ngClass", node_r8.type);
121
- i0.ɵɵattribute("data-node-id", node_r8.id);
122
- i0.ɵɵadvance(2);
123
- i0.ɵɵproperty("ngClass", node_r8.icon);
703
+ const node_r24 = ctx.$implicit;
704
+ const ctx_r1 = i0.ɵɵnextContext(3);
705
+ i0.ɵɵstyleProp("left", node_r24.x, "px")("top", node_r24.y, "px")("width", ctx_r1.NODE_W, "px");
706
+ i0.ɵɵclassProp("selected", node_r24.id === ctx_r1.selectedId);
707
+ i0.ɵɵproperty("ngClass", node_r24.type);
708
+ i0.ɵɵattribute("data-node-id", node_r24.id);
124
709
  i0.ɵɵadvance(2);
125
- i0.ɵɵtextInterpolate(node_r8.title);
710
+ i0.ɵɵproperty("ngClass", node_r24.icon);
711
+ i0.ɵɵadvance();
712
+ i0.ɵɵproperty("title", node_r24.title);
126
713
  i0.ɵɵadvance();
127
- i0.ɵɵconditional(node_r8.tag ? 5 : -1);
714
+ i0.ɵɵtextInterpolate(node_r24.title);
715
+ i0.ɵɵadvance();
716
+ i0.ɵɵconditional(node_r24.tag ? 5 : -1);
128
717
  i0.ɵɵadvance(2);
129
- i0.ɵɵrepeater(node_r8.rows);
718
+ i0.ɵɵrepeater(node_r24.rows);
130
719
  i0.ɵɵadvance(2);
131
- i0.ɵɵconditional(node_r8.hasIn ? 9 : -1);
720
+ i0.ɵɵconditional(node_r24.hasIn ? 9 : -1);
132
721
  i0.ɵɵadvance();
133
- i0.ɵɵconditional(node_r8.hasOut ? 10 : -1);
722
+ i0.ɵɵconditional(node_r24.hasOut ? 10 : -1);
134
723
  } }
135
- function PSPipelinesComponent_Conditional_2_Conditional_65_Template(rf, ctx) { if (rf & 1) {
136
- const _r10 = i0.ɵɵgetCurrentView();
137
- i0.ɵɵelementStart(0, "button", 75);
138
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_65_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.deleteSelected()); });
139
- i0.ɵɵelement(1, "i", 76);
724
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_12_Template(rf, ctx) { if (rf & 1) {
725
+ const _r27 = i0.ɵɵgetCurrentView();
726
+ i0.ɵɵelementStart(0, "button", 198);
727
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_12_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r27); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.deleteSelected()); });
728
+ i0.ɵɵelement(1, "i", 199);
140
729
  i0.ɵɵelementEnd();
141
730
  } }
142
- function PSPipelinesComponent_Conditional_2_Conditional_67_For_5_Template(rf, ctx) { if (rf & 1) {
143
- i0.ɵɵelementStart(0, "option", 77);
731
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_For_5_Template(rf, ctx) { if (rf & 1) {
732
+ i0.ɵɵelementStart(0, "option", 200);
144
733
  i0.ɵɵtext(1);
145
734
  i0.ɵɵelementEnd();
146
735
  } if (rf & 2) {
147
- const k_r12 = ctx.$implicit;
148
- i0.ɵɵproperty("value", k_r12);
736
+ const k_r29 = ctx.$implicit;
737
+ i0.ɵɵproperty("value", k_r29);
149
738
  i0.ɵɵadvance();
150
- i0.ɵɵtextInterpolate(k_r12);
739
+ i0.ɵɵtextInterpolate(k_r29);
151
740
  } }
152
- function PSPipelinesComponent_Conditional_2_Conditional_67_Template(rf, ctx) { if (rf & 1) {
153
- const _r11 = i0.ɵɵgetCurrentView();
154
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
741
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_Template(rf, ctx) { if (rf & 1) {
742
+ const _r28 = i0.ɵɵgetCurrentView();
743
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
155
744
  i0.ɵɵtext(2, "Kind");
156
745
  i0.ɵɵelementEnd();
157
- i0.ɵɵelementStart(3, "select", 54);
158
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_67_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setSourceKind($event.target.value)); });
159
- i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_67_For_5_Template, 2, 2, "option", 77, i0.ɵɵrepeaterTrackByIdentity);
746
+ i0.ɵɵelementStart(3, "select", 189);
747
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setSourceKind(ctx_r1.inputVal($event))); });
748
+ i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_For_5_Template, 2, 2, "option", 200, i0.ɵɵrepeaterTrackByIdentity);
160
749
  i0.ɵɵelementEnd()();
161
- i0.ɵɵelementStart(6, "div", 50)(7, "label");
750
+ i0.ɵɵelementStart(6, "div", 186)(7, "label");
162
751
  i0.ɵɵtext(8, "Reference (entity / query / id)");
163
752
  i0.ɵɵelementEnd();
164
- i0.ɵɵelementStart(9, "input", 51);
165
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_67_Template_input_input_9_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setSourceRef($event.target.value)); });
753
+ i0.ɵɵelementStart(9, "input", 187);
754
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_Template_input_input_9_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setSourceRef(ctx_r1.inputVal($event))); });
166
755
  i0.ɵɵelementEnd()();
167
- i0.ɵɵelementStart(10, "div", 50)(11, "label");
756
+ i0.ɵɵelementStart(10, "div", 186)(11, "label");
168
757
  i0.ɵɵtext(12, "Alias (optional)");
169
758
  i0.ɵɵelementEnd();
170
- i0.ɵɵelementStart(13, "input", 51);
171
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_67_Template_input_input_13_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setSourceAlias($event.target.value)); });
759
+ i0.ɵɵelementStart(13, "input", 187);
760
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_Template_input_input_13_listener($event) { i0.ɵɵrestoreView(_r28); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setSourceAlias(ctx_r1.inputVal($event))); });
172
761
  i0.ɵɵelementEnd()();
173
762
  } if (rf & 2) {
174
- const ctx_r1 = i0.ɵɵnextContext(2);
763
+ const ctx_r1 = i0.ɵɵnextContext(4);
175
764
  i0.ɵɵadvance(3);
176
765
  i0.ɵɵproperty("value", ctx_r1.selectedSource.Kind);
177
766
  i0.ɵɵadvance();
@@ -181,69 +770,69 @@ function PSPipelinesComponent_Conditional_2_Conditional_67_Template(rf, ctx) { i
181
770
  i0.ɵɵadvance(4);
182
771
  i0.ɵɵproperty("value", ctx_r1.selectedSource.Alias || "");
183
772
  } }
184
- function PSPipelinesComponent_Conditional_2_Conditional_68_For_5_Template(rf, ctx) { if (rf & 1) {
185
- i0.ɵɵelementStart(0, "option", 77);
773
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_For_5_Template(rf, ctx) { if (rf & 1) {
774
+ i0.ɵɵelementStart(0, "option", 200);
186
775
  i0.ɵɵtext(1);
187
776
  i0.ɵɵelementEnd();
188
777
  } if (rf & 2) {
189
- const k_r14 = ctx.$implicit;
190
- i0.ɵɵproperty("value", k_r14);
778
+ const k_r31 = ctx.$implicit;
779
+ i0.ɵɵproperty("value", k_r31);
191
780
  i0.ɵɵadvance();
192
- i0.ɵɵtextInterpolate(k_r14);
781
+ i0.ɵɵtextInterpolate(k_r31);
193
782
  } }
194
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_10_Template(rf, ctx) { if (rf & 1) {
195
- const _r15 = i0.ɵɵgetCurrentView();
196
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
783
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_10_Template(rf, ctx) { if (rf & 1) {
784
+ const _r32 = i0.ɵɵgetCurrentView();
785
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
197
786
  i0.ɵɵtext(2, "Columns (comma-separated)");
198
787
  i0.ɵɵelementEnd();
199
- i0.ɵɵelementStart(3, "input", 51);
200
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_10_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setColumns($event.target.value)); });
788
+ i0.ɵɵelementStart(3, "input", 187);
789
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_10_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setColumns(ctx_r1.inputVal($event))); });
201
790
  i0.ɵɵelementEnd()();
202
791
  } if (rf & 2) {
203
- const ctx_r1 = i0.ɵɵnextContext(3);
792
+ const ctx_r1 = i0.ɵɵnextContext(5);
204
793
  i0.ɵɵadvance(3);
205
794
  i0.ɵɵproperty("value", ctx_r1.columnsText);
206
795
  } }
207
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_11_Template(rf, ctx) { if (rf & 1) {
208
- const _r16 = i0.ɵɵgetCurrentView();
209
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
796
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_11_Template(rf, ctx) { if (rf & 1) {
797
+ const _r33 = i0.ɵɵgetCurrentView();
798
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
210
799
  i0.ɵɵtext(2, "Columns (comma-separated)");
211
800
  i0.ɵɵelementEnd();
212
- i0.ɵɵelementStart(3, "input", 51);
213
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_11_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setColumns($event.target.value)); });
801
+ i0.ɵɵelementStart(3, "input", 187);
802
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_11_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r33); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setColumns(ctx_r1.inputVal($event))); });
214
803
  i0.ɵɵelementEnd()();
215
804
  } if (rf & 2) {
216
- const ctx_r1 = i0.ɵɵnextContext(3);
805
+ const ctx_r1 = i0.ɵɵnextContext(5);
217
806
  i0.ɵɵadvance(3);
218
807
  i0.ɵɵproperty("value", ctx_r1.columnsText);
219
808
  } }
220
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_For_9_Template(rf, ctx) { if (rf & 1) {
221
- i0.ɵɵelementStart(0, "option", 77);
809
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_For_9_Template(rf, ctx) { if (rf & 1) {
810
+ i0.ɵɵelementStart(0, "option", 200);
222
811
  i0.ɵɵtext(1);
223
812
  i0.ɵɵelementEnd();
224
813
  } if (rf & 2) {
225
- const s_r18 = ctx.$implicit;
226
- i0.ɵɵproperty("value", s_r18);
814
+ const s_r35 = ctx.$implicit;
815
+ i0.ɵɵproperty("value", s_r35);
227
816
  i0.ɵɵadvance();
228
- i0.ɵɵtextInterpolate(s_r18);
817
+ i0.ɵɵtextInterpolate(s_r35);
229
818
  } }
230
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_Template(rf, ctx) { if (rf & 1) {
231
- const _r17 = i0.ɵɵgetCurrentView();
232
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
819
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_Template(rf, ctx) { if (rf & 1) {
820
+ const _r34 = i0.ɵɵgetCurrentView();
821
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
233
822
  i0.ɵɵtext(2, "Column");
234
823
  i0.ɵɵelementEnd();
235
- i0.ɵɵelementStart(3, "input", 51);
236
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", $event.target.value)); });
824
+ i0.ɵɵelementStart(3, "input", 187);
825
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r34); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", ctx_r1.inputVal($event))); });
237
826
  i0.ɵɵelementEnd()();
238
- i0.ɵɵelementStart(4, "div", 50)(5, "label");
827
+ i0.ɵɵelementStart(4, "div", 186)(5, "label");
239
828
  i0.ɵɵtext(6, "Strategy");
240
829
  i0.ɵɵelementEnd();
241
- i0.ɵɵelementStart(7, "select", 54);
242
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_Template_select_change_7_listener($event) { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("Strategy", $event.target.value)); });
243
- i0.ɵɵrepeaterCreate(8, PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_For_9_Template, 2, 2, "option", 77, i0.ɵɵrepeaterTrackByIdentity);
830
+ i0.ɵɵelementStart(7, "select", 189);
831
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_Template_select_change_7_listener($event) { i0.ɵɵrestoreView(_r34); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("Strategy", ctx_r1.inputVal($event))); });
832
+ i0.ɵɵrepeaterCreate(8, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_For_9_Template, 2, 2, "option", 200, i0.ɵɵrepeaterTrackByIdentity);
244
833
  i0.ɵɵelementEnd()();
245
834
  } if (rf & 2) {
246
- const ctx_r1 = i0.ɵɵnextContext(3);
835
+ const ctx_r1 = i0.ɵɵnextContext(5);
247
836
  i0.ɵɵadvance(3);
248
837
  i0.ɵɵproperty("value", ctx_r1.stepField("Column"));
249
838
  i0.ɵɵadvance(4);
@@ -251,62 +840,62 @@ function PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_Template(rf,
251
840
  i0.ɵɵadvance();
252
841
  i0.ɵɵrepeater(ctx_r1.imputeStrategies);
253
842
  } }
254
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_13_Template(rf, ctx) { if (rf & 1) {
255
- const _r19 = i0.ɵɵgetCurrentView();
256
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
843
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_13_Template(rf, ctx) { if (rf & 1) {
844
+ const _r36 = i0.ɵɵgetCurrentView();
845
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
257
846
  i0.ɵɵtext(2, "Column");
258
847
  i0.ɵɵelementEnd();
259
- i0.ɵɵelementStart(3, "input", 51);
260
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_13_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", $event.target.value)); });
848
+ i0.ɵɵelementStart(3, "input", 187);
849
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_13_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r36); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", ctx_r1.inputVal($event))); });
261
850
  i0.ɵɵelementEnd()();
262
851
  } if (rf & 2) {
263
- const ctx_r1 = i0.ɵɵnextContext(3);
852
+ const ctx_r1 = i0.ɵɵnextContext(5);
264
853
  i0.ɵɵadvance(3);
265
854
  i0.ɵɵproperty("value", ctx_r1.stepField("Column"));
266
855
  } }
267
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_14_Template(rf, ctx) { if (rf & 1) {
268
- const _r20 = i0.ɵɵgetCurrentView();
269
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
856
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_14_Template(rf, ctx) { if (rf & 1) {
857
+ const _r37 = i0.ɵɵgetCurrentView();
858
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
270
859
  i0.ɵɵtext(2, "Column");
271
860
  i0.ɵɵelementEnd();
272
- i0.ɵɵelementStart(3, "input", 51);
273
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_14_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", $event.target.value)); });
861
+ i0.ɵɵelementStart(3, "input", 187);
862
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_14_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r37); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("Column", ctx_r1.inputVal($event))); });
274
863
  i0.ɵɵelementEnd()();
275
- i0.ɵɵelementStart(4, "div", 50)(5, "label");
864
+ i0.ɵɵelementStart(4, "div", 186)(5, "label");
276
865
  i0.ɵɵtext(6, "Bins");
277
866
  i0.ɵɵelementEnd();
278
- i0.ɵɵelementStart(7, "input", 78);
279
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_14_Template_input_input_7_listener($event) { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepNum("Bins", $event.target.value)); });
867
+ i0.ɵɵelementStart(7, "input", 201);
868
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_14_Template_input_input_7_listener($event) { i0.ɵɵrestoreView(_r37); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepNum("Bins", ctx_r1.inputVal($event))); });
280
869
  i0.ɵɵelementEnd()();
281
870
  } if (rf & 2) {
282
- const ctx_r1 = i0.ɵɵnextContext(3);
871
+ const ctx_r1 = i0.ɵɵnextContext(5);
283
872
  i0.ɵɵadvance(3);
284
873
  i0.ɵɵproperty("value", ctx_r1.stepField("Column"));
285
874
  i0.ɵɵadvance(4);
286
875
  i0.ɵɵproperty("value", ctx_r1.stepField("Bins"));
287
876
  } }
288
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template(rf, ctx) { if (rf & 1) {
289
- const _r21 = i0.ɵɵgetCurrentView();
290
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
877
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_15_Template(rf, ctx) { if (rf & 1) {
878
+ const _r38 = i0.ɵɵgetCurrentView();
879
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
291
880
  i0.ɵɵtext(2, "Entity");
292
881
  i0.ɵɵelementEnd();
293
- i0.ɵɵelementStart(3, "input", 51);
294
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("Entity", $event.target.value)); });
882
+ i0.ɵɵelementStart(3, "input", 187);
883
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_15_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r38); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("Entity", ctx_r1.inputVal($event))); });
295
884
  i0.ɵɵelementEnd()();
296
- i0.ɵɵelementStart(4, "div", 50)(5, "label");
885
+ i0.ɵɵelementStart(4, "div", 186)(5, "label");
297
886
  i0.ɵɵtext(6, "Embedding model ref");
298
887
  i0.ɵɵelementEnd();
299
- i0.ɵɵelementStart(7, "input", 51);
300
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template_input_input_7_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("EmbeddingModelRef", $event.target.value)); });
888
+ i0.ɵɵelementStart(7, "input", 187);
889
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_15_Template_input_input_7_listener($event) { i0.ɵɵrestoreView(_r38); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("EmbeddingModelRef", ctx_r1.inputVal($event))); });
301
890
  i0.ɵɵelementEnd()();
302
- i0.ɵɵelementStart(8, "div", 50)(9, "label");
891
+ i0.ɵɵelementStart(8, "div", 186)(9, "label");
303
892
  i0.ɵɵtext(10, "Dimensions");
304
893
  i0.ɵɵelementEnd();
305
- i0.ɵɵelementStart(11, "input", 78);
306
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template_input_input_11_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepNum("Dims", $event.target.value)); });
894
+ i0.ɵɵelementStart(11, "input", 201);
895
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_15_Template_input_input_11_listener($event) { i0.ɵɵrestoreView(_r38); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepNum("Dims", ctx_r1.inputVal($event))); });
307
896
  i0.ɵɵelementEnd()();
308
897
  } if (rf & 2) {
309
- const ctx_r1 = i0.ɵɵnextContext(3);
898
+ const ctx_r1 = i0.ɵɵnextContext(5);
310
899
  i0.ɵɵadvance(3);
311
900
  i0.ɵɵproperty("value", ctx_r1.stepField("Entity"));
312
901
  i0.ɵɵadvance(4);
@@ -314,95 +903,64 @@ function PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template(rf,
314
903
  i0.ɵɵadvance(4);
315
904
  i0.ɵɵproperty("value", ctx_r1.stepField("Dims"));
316
905
  } }
317
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_16_Template(rf, ctx) { if (rf & 1) {
318
- const _r22 = i0.ɵɵgetCurrentView();
319
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
906
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_16_Template(rf, ctx) { if (rf & 1) {
907
+ const _r39 = i0.ɵɵgetCurrentView();
908
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
320
909
  i0.ɵɵtext(2, "Feature Pipeline ref");
321
910
  i0.ɵɵelementEnd();
322
- i0.ɵɵelementStart(3, "input", 51);
323
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_16_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("FeaturePipelineRef", $event.target.value)); });
911
+ i0.ɵɵelementStart(3, "input", 187);
912
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_16_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r39); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("FeaturePipelineRef", ctx_r1.inputVal($event))); });
324
913
  i0.ɵɵelementEnd()();
325
914
  } if (rf & 2) {
326
- const ctx_r1 = i0.ɵɵnextContext(3);
915
+ const ctx_r1 = i0.ɵɵnextContext(5);
327
916
  i0.ɵɵadvance(3);
328
917
  i0.ɵɵproperty("value", ctx_r1.stepField("FeaturePipelineRef"));
329
918
  } }
330
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_17_Template(rf, ctx) { if (rf & 1) {
331
- const _r23 = i0.ɵɵgetCurrentView();
332
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
919
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_17_Template(rf, ctx) { if (rf & 1) {
920
+ const _r40 = i0.ɵɵgetCurrentView();
921
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
333
922
  i0.ɵɵtext(2, "Flow Agent ref");
334
923
  i0.ɵɵelementEnd();
335
- i0.ɵɵelementStart(3, "input", 51);
336
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_17_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("FlowAgentRef", $event.target.value)); });
924
+ i0.ɵɵelementStart(3, "input", 187);
925
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_17_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r40); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("FlowAgentRef", ctx_r1.inputVal($event))); });
337
926
  i0.ɵɵelementEnd()();
338
927
  } if (rf & 2) {
339
- const ctx_r1 = i0.ɵɵnextContext(3);
928
+ const ctx_r1 = i0.ɵɵnextContext(5);
340
929
  i0.ɵɵadvance(3);
341
930
  i0.ɵɵproperty("value", ctx_r1.stepField("FlowAgentRef"));
342
931
  } }
343
- function PSPipelinesComponent_Conditional_2_Conditional_68_Case_18_Template(rf, ctx) { if (rf & 1) {
344
- const _r24 = i0.ɵɵgetCurrentView();
345
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
932
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_18_Template(rf, ctx) { if (rf & 1) {
933
+ const _r41 = i0.ɵɵgetCurrentView();
934
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
346
935
  i0.ɵɵtext(2, "Image column");
347
936
  i0.ɵɵelementEnd();
348
- i0.ɵɵelementStart(3, "input", 51);
349
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Case_18_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r24); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setStepStr("ImageColumn", $event.target.value)); });
937
+ i0.ɵɵelementStart(3, "input", 187);
938
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_18_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r41); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.setStepStr("ImageColumn", ctx_r1.inputVal($event))); });
350
939
  i0.ɵɵelementEnd()();
351
940
  } if (rf & 2) {
352
- const ctx_r1 = i0.ɵɵnextContext(3);
941
+ const ctx_r1 = i0.ɵɵnextContext(5);
353
942
  i0.ɵɵadvance(3);
354
943
  i0.ɵɵproperty("value", ctx_r1.stepField("ImageColumn"));
355
944
  } }
356
- function PSPipelinesComponent_Conditional_2_Conditional_68_Conditional_19_For_5_Template(rf, ctx) { if (rf & 1) {
357
- const _r25 = i0.ɵɵgetCurrentView();
358
- i0.ɵɵelementStart(0, "label", 80)(1, "input", 82);
359
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_68_Conditional_19_For_5_Template_input_change_1_listener() { const s_r26 = i0.ɵɵrestoreView(_r25).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.toggleInput(s_r26.Id)); });
360
- i0.ɵɵelementEnd();
361
- i0.ɵɵtext(2);
362
- i0.ɵɵelementEnd();
363
- } if (rf & 2) {
364
- const s_r26 = ctx.$implicit;
365
- const ctx_r1 = i0.ɵɵnextContext(4);
366
- i0.ɵɵadvance();
367
- i0.ɵɵproperty("checked", ctx_r1.hasInput(s_r26.Id));
368
- i0.ɵɵadvance();
369
- i0.ɵɵtextInterpolate1(" ", s_r26.Label || s_r26.Kind);
370
- } }
371
- function PSPipelinesComponent_Conditional_2_Conditional_68_Conditional_19_Template(rf, ctx) { if (rf & 1) {
372
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
373
- i0.ɵɵtext(2, "Inputs (upstream steps)");
374
- i0.ɵɵelementEnd();
375
- i0.ɵɵelementStart(3, "div", 79);
376
- i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_68_Conditional_19_For_5_Template, 3, 2, "label", 80, _forTrack4);
377
- i0.ɵɵelementEnd();
378
- i0.ɵɵelementStart(6, "div", 81);
379
- i0.ɵɵtext(7, "No inputs = a root step fed directly by the sources.");
380
- i0.ɵɵelementEnd()();
381
- } if (rf & 2) {
382
- const ctx_r1 = i0.ɵɵnextContext(3);
383
- i0.ɵɵadvance(4);
384
- i0.ɵɵrepeater(ctx_r1.otherSteps);
385
- } }
386
- function PSPipelinesComponent_Conditional_2_Conditional_68_Template(rf, ctx) { if (rf & 1) {
387
- const _r13 = i0.ɵɵgetCurrentView();
388
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
945
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Template(rf, ctx) { if (rf & 1) {
946
+ const _r30 = i0.ɵɵgetCurrentView();
947
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
389
948
  i0.ɵɵtext(2, "Kind");
390
949
  i0.ɵɵelementEnd();
391
- i0.ɵɵelementStart(3, "select", 54);
392
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_68_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setStepKind($event.target.value)); });
393
- i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_68_For_5_Template, 2, 2, "option", 77, i0.ɵɵrepeaterTrackByIdentity);
950
+ i0.ɵɵelementStart(3, "select", 189);
951
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r30); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setStepKind(ctx_r1.inputVal($event))); });
952
+ i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_For_5_Template, 2, 2, "option", 200, i0.ɵɵrepeaterTrackByIdentity);
394
953
  i0.ɵɵelementEnd()();
395
- i0.ɵɵelementStart(6, "div", 50)(7, "label");
954
+ i0.ɵɵelementStart(6, "div", 186)(7, "label");
396
955
  i0.ɵɵtext(8, "Label");
397
956
  i0.ɵɵelementEnd();
398
- i0.ɵɵelementStart(9, "input", 51);
399
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_68_Template_input_input_9_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setStepLabel($event.target.value)); });
957
+ i0.ɵɵelementStart(9, "input", 187);
958
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Template_input_input_9_listener($event) { i0.ɵɵrestoreView(_r30); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setStepLabel(ctx_r1.inputVal($event))); });
400
959
  i0.ɵɵelementEnd()();
401
- i0.ɵɵconditionalCreate(10, PSPipelinesComponent_Conditional_2_Conditional_68_Case_10_Template, 4, 1, "div", 50)(11, PSPipelinesComponent_Conditional_2_Conditional_68_Case_11_Template, 4, 1, "div", 50)(12, PSPipelinesComponent_Conditional_2_Conditional_68_Case_12_Template, 10, 2)(13, PSPipelinesComponent_Conditional_2_Conditional_68_Case_13_Template, 4, 1, "div", 50)(14, PSPipelinesComponent_Conditional_2_Conditional_68_Case_14_Template, 8, 2)(15, PSPipelinesComponent_Conditional_2_Conditional_68_Case_15_Template, 12, 3)(16, PSPipelinesComponent_Conditional_2_Conditional_68_Case_16_Template, 4, 1, "div", 50)(17, PSPipelinesComponent_Conditional_2_Conditional_68_Case_17_Template, 4, 1, "div", 50)(18, PSPipelinesComponent_Conditional_2_Conditional_68_Case_18_Template, 4, 1, "div", 50);
402
- i0.ɵɵconditionalCreate(19, PSPipelinesComponent_Conditional_2_Conditional_68_Conditional_19_Template, 8, 0, "div", 50);
960
+ i0.ɵɵconditionalCreate(10, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_10_Template, 4, 1, "div", 186)(11, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_11_Template, 4, 1, "div", 186)(12, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_12_Template, 10, 2)(13, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_13_Template, 4, 1, "div", 186)(14, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_14_Template, 8, 2)(15, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_15_Template, 12, 3)(16, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_16_Template, 4, 1, "div", 186)(17, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_17_Template, 4, 1, "div", 186)(18, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Case_18_Template, 4, 1, "div", 186);
403
961
  } if (rf & 2) {
404
- let tmp_5_0;
405
- const ctx_r1 = i0.ɵɵnextContext(2);
962
+ let tmp_7_0;
963
+ const ctx_r1 = i0.ɵɵnextContext(4);
406
964
  i0.ɵɵadvance(3);
407
965
  i0.ɵɵproperty("value", ctx_r1.selectedStep.Kind);
408
966
  i0.ɵɵadvance();
@@ -410,37 +968,35 @@ function PSPipelinesComponent_Conditional_2_Conditional_68_Template(rf, ctx) { i
410
968
  i0.ɵɵadvance(5);
411
969
  i0.ɵɵproperty("value", ctx_r1.selectedStep.Label || "");
412
970
  i0.ɵɵadvance();
413
- i0.ɵɵconditional((tmp_5_0 = ctx_r1.selectedStep.Kind) === "select" ? 10 : tmp_5_0 === "standardize" ? 11 : tmp_5_0 === "impute" ? 12 : tmp_5_0 === "onehot" ? 13 : tmp_5_0 === "bin" ? 14 : tmp_5_0 === "embedding" ? 15 : tmp_5_0 === "llm-derived" ? 16 : tmp_5_0 === "flow-agent" ? 17 : tmp_5_0 === "vision-llm" ? 18 : -1);
414
- i0.ɵɵadvance(9);
415
- i0.ɵɵconditional(ctx_r1.otherSteps.length > 0 ? 19 : -1);
971
+ i0.ɵɵconditional((tmp_7_0 = ctx_r1.selectedStep.Kind) === "select" ? 10 : tmp_7_0 === "standardize" ? 11 : tmp_7_0 === "impute" ? 12 : tmp_7_0 === "onehot" ? 13 : tmp_7_0 === "bin" ? 14 : tmp_7_0 === "embedding" ? 15 : tmp_7_0 === "llm-derived" ? 16 : tmp_7_0 === "flow-agent" ? 17 : tmp_7_0 === "vision-llm" ? 18 : -1);
416
972
  } }
417
- function PSPipelinesComponent_Conditional_2_Conditional_69_For_9_Template(rf, ctx) { if (rf & 1) {
418
- i0.ɵɵelementStart(0, "option", 77);
973
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_For_9_Template(rf, ctx) { if (rf & 1) {
974
+ i0.ɵɵelementStart(0, "option", 200);
419
975
  i0.ɵɵtext(1);
420
976
  i0.ɵɵelementEnd();
421
977
  } if (rf & 2) {
422
- const pt_r28 = ctx.$implicit;
423
- i0.ɵɵproperty("value", pt_r28);
978
+ const pt_r43 = ctx.$implicit;
979
+ i0.ɵɵproperty("value", pt_r43);
424
980
  i0.ɵɵadvance();
425
- i0.ɵɵtextInterpolate(pt_r28);
981
+ i0.ɵɵtextInterpolate(pt_r43);
426
982
  } }
427
- function PSPipelinesComponent_Conditional_2_Conditional_69_Template(rf, ctx) { if (rf & 1) {
428
- const _r27 = i0.ɵɵgetCurrentView();
429
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
983
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_Template(rf, ctx) { if (rf & 1) {
984
+ const _r42 = i0.ɵɵgetCurrentView();
985
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
430
986
  i0.ɵɵtext(2, "Target variable");
431
987
  i0.ɵɵelementEnd();
432
- i0.ɵɵelementStart(3, "input", 51);
433
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_69_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r27); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setTargetVariable($event.target.value)); });
988
+ i0.ɵɵelementStart(3, "input", 187);
989
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r42); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setTargetVariable(ctx_r1.inputVal($event))); });
434
990
  i0.ɵɵelementEnd()();
435
- i0.ɵɵelementStart(4, "div", 50)(5, "label");
991
+ i0.ɵɵelementStart(4, "div", 186)(5, "label");
436
992
  i0.ɵɵtext(6, "Problem type");
437
993
  i0.ɵɵelementEnd();
438
- i0.ɵɵelementStart(7, "select", 54);
439
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_69_Template_select_change_7_listener($event) { i0.ɵɵrestoreView(_r27); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setProblemType($event.target.value)); });
440
- i0.ɵɵrepeaterCreate(8, PSPipelinesComponent_Conditional_2_Conditional_69_For_9_Template, 2, 2, "option", 77, i0.ɵɵrepeaterTrackByIdentity);
994
+ i0.ɵɵelementStart(7, "select", 189);
995
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_Template_select_change_7_listener($event) { i0.ɵɵrestoreView(_r42); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setProblemType(ctx_r1.inputVal($event))); });
996
+ i0.ɵɵrepeaterCreate(8, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_For_9_Template, 2, 2, "option", 200, i0.ɵɵrepeaterTrackByIdentity);
441
997
  i0.ɵɵelementEnd()();
442
998
  } if (rf & 2) {
443
- const ctx_r1 = i0.ɵɵnextContext(2);
999
+ const ctx_r1 = i0.ɵɵnextContext(4);
444
1000
  i0.ɵɵadvance(3);
445
1001
  i0.ɵɵproperty("value", ctx_r1.editTargetVariable);
446
1002
  i0.ɵɵadvance(4);
@@ -448,42 +1004,73 @@ function PSPipelinesComponent_Conditional_2_Conditional_69_Template(rf, ctx) { i
448
1004
  i0.ɵɵadvance();
449
1005
  i0.ɵɵrepeater(ctx_r1.problemTypes);
450
1006
  } }
451
- function PSPipelinesComponent_Conditional_2_Conditional_70_For_5_Template(rf, ctx) { if (rf & 1) {
452
- i0.ɵɵelementStart(0, "option", 77);
1007
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_For_5_Template(rf, ctx) { if (rf & 1) {
1008
+ i0.ɵɵelementStart(0, "option", 200);
453
1009
  i0.ɵɵtext(1);
454
1010
  i0.ɵɵelementEnd();
455
1011
  } if (rf & 2) {
456
- const a_r30 = ctx.$implicit;
457
- i0.ɵɵproperty("value", a_r30.ID);
1012
+ const a_r45 = ctx.$implicit;
1013
+ i0.ɵɵproperty("value", a_r45.ID);
458
1014
  i0.ɵɵadvance();
459
- i0.ɵɵtextInterpolate(a_r30.Name);
1015
+ i0.ɵɵtextInterpolate(a_r45.Name);
460
1016
  } }
461
- function PSPipelinesComponent_Conditional_2_Conditional_70_Template(rf, ctx) { if (rf & 1) {
462
- const _r29 = i0.ɵɵgetCurrentView();
463
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
1017
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Conditional_10_Template(rf, ctx) { if (rf & 1) {
1018
+ const _r46 = i0.ɵɵgetCurrentView();
1019
+ i0.ɵɵelementStart(0, "div", 203)(1, "div", 204);
1020
+ i0.ɵɵtext(2, "Current Serving Winner");
1021
+ i0.ɵɵelementEnd();
1022
+ i0.ɵɵelementStart(3, "div", 205)(4, "span", 206);
1023
+ i0.ɵɵtext(5);
1024
+ i0.ɵɵelementEnd();
1025
+ i0.ɵɵelementStart(6, "span", 122);
1026
+ i0.ɵɵelement(7, "i", 126);
1027
+ i0.ɵɵtext(8, " Active");
1028
+ i0.ɵɵelementEnd()();
1029
+ i0.ɵɵelementStart(9, "div", 80)(10, "button", 207);
1030
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Conditional_10_Template_button_click_10_listener() { const pub_r47 = i0.ɵɵrestoreView(_r46); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.openModelRecord(pub_r47.ID)); });
1031
+ i0.ɵɵelement(11, "i", 125);
1032
+ i0.ɵɵtext(12, " Model ");
1033
+ i0.ɵɵelementEnd();
1034
+ i0.ɵɵelementStart(13, "button", 208);
1035
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Conditional_10_Template_button_click_13_listener() { const pub_r47 = i0.ɵɵrestoreView(_r46); const ctx_r1 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r1.viewTrainingRuns(pub_r47.ID)); });
1036
+ i0.ɵɵelement(14, "i", 129);
1037
+ i0.ɵɵtext(15, " Runs ");
1038
+ i0.ɵɵelementEnd()()();
1039
+ } if (rf & 2) {
1040
+ const pub_r47 = ctx;
1041
+ i0.ɵɵadvance(5);
1042
+ i0.ɵɵtextInterpolate2("v", pub_r47.Version, " (", pub_r47.Algorithm || "Model", ")");
1043
+ } }
1044
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Template(rf, ctx) { if (rf & 1) {
1045
+ const _r44 = i0.ɵɵgetCurrentView();
1046
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
464
1047
  i0.ɵɵtext(2, "Algorithm");
465
1048
  i0.ɵɵelementEnd();
466
- i0.ɵɵelementStart(3, "select", 54);
467
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_70_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setAlgorithm($event.target.value)); });
468
- i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_70_For_5_Template, 2, 2, "option", 77, _forTrack0);
1049
+ i0.ɵɵelementStart(3, "select", 189);
1050
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r44); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setAlgorithm(ctx_r1.inputVal($event))); });
1051
+ i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_For_5_Template, 2, 2, "option", 200, _forTrack0);
469
1052
  i0.ɵɵelementEnd()();
470
- i0.ɵɵelementStart(6, "div", 50)(7, "label");
1053
+ i0.ɵɵelementStart(6, "div", 186)(7, "label");
471
1054
  i0.ɵɵtext(8, "Hyperparameters (JSON)");
472
1055
  i0.ɵɵelementEnd();
473
- i0.ɵɵelementStart(9, "textarea", 83);
474
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_70_Template_textarea_input_9_listener($event) { i0.ɵɵrestoreView(_r29); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setHyperparams($event.target.value)); });
1056
+ i0.ɵɵelementStart(9, "textarea", 202);
1057
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Template_textarea_input_9_listener($event) { i0.ɵɵrestoreView(_r44); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setHyperparams(ctx_r1.inputVal($event))); });
475
1058
  i0.ɵɵelementEnd()();
1059
+ i0.ɵɵconditionalCreate(10, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Conditional_10_Template, 16, 2, "div", 203);
476
1060
  } if (rf & 2) {
477
- const ctx_r1 = i0.ɵɵnextContext(2);
1061
+ let tmp_7_0;
1062
+ const ctx_r1 = i0.ɵɵnextContext(4);
478
1063
  i0.ɵɵadvance(3);
479
1064
  i0.ɵɵproperty("value", ctx_r1.editAlgorithmId);
480
1065
  i0.ɵɵadvance();
481
1066
  i0.ɵɵrepeater(ctx_r1.algorithms);
482
1067
  i0.ɵɵadvance(5);
483
1068
  i0.ɵɵproperty("value", ctx_r1.editHyperparams);
1069
+ i0.ɵɵadvance();
1070
+ i0.ɵɵconditional((tmp_7_0 = ctx_r1.publishedModel) ? 10 : -1, tmp_7_0);
484
1071
  } }
485
- function PSPipelinesComponent_Conditional_2_Conditional_71_Template(rf, ctx) { if (rf & 1) {
486
- i0.ɵɵelementStart(0, "div", 27);
1072
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_0_Template(rf, ctx) { if (rf & 1) {
1073
+ i0.ɵɵelementStart(0, "div", 179);
487
1074
  i0.ɵɵtext(1, "The trained model artifact. Run ");
488
1075
  i0.ɵɵelementStart(2, "strong");
489
1076
  i0.ɵɵtext(3, "Train");
@@ -491,281 +1078,508 @@ function PSPipelinesComponent_Conditional_2_Conditional_71_Template(rf, ctx) { i
491
1078
  i0.ɵɵtext(4, " to produce a new versioned model from this pipeline.");
492
1079
  i0.ɵɵelementEnd();
493
1080
  } }
494
- function PSPipelinesComponent_Conditional_2_Conditional_102_Template(rf, ctx) { if (rf & 1) {
495
- const _r31 = i0.ɵɵgetCurrentView();
496
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
1081
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_1_For_5_Template(rf, ctx) { if (rf & 1) {
1082
+ i0.ɵɵelementStart(0, "option", 200);
1083
+ i0.ɵɵtext(1);
1084
+ i0.ɵɵelementEnd();
1085
+ } if (rf & 2) {
1086
+ const m_r49 = ctx.$implicit;
1087
+ i0.ɵɵproperty("value", m_r49.ID);
1088
+ i0.ɵɵadvance();
1089
+ i0.ɵɵtextInterpolate3("v", m_r49.Version, " \u00B7 ", m_r49.Algorithm || "Algorithm", " (", m_r49.Status, ")");
1090
+ } }
1091
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
1092
+ const _r48 = i0.ɵɵgetCurrentView();
1093
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
1094
+ i0.ɵɵtext(2, "Model Version");
1095
+ i0.ɵɵelementEnd();
1096
+ i0.ɵɵelementStart(3, "select", 189);
1097
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_1_Template_select_change_3_listener($event) { i0.ɵɵrestoreView(_r48); const ctx_r1 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r1.selectModelVersion(ctx_r1.inputVal($event))); });
1098
+ i0.ɵɵrepeaterCreate(4, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_1_For_5_Template, 2, 4, "option", 200, _forTrack0);
1099
+ i0.ɵɵelementEnd()();
1100
+ } if (rf & 2) {
1101
+ const ctx_r1 = i0.ɵɵnextContext(6);
1102
+ i0.ɵɵadvance(3);
1103
+ i0.ɵɵproperty("value", ctx_r1.inspectedModel == null ? null : ctx_r1.inspectedModel.ID);
1104
+ i0.ɵɵadvance();
1105
+ i0.ɵɵrepeater(ctx_r1.pipelineModels);
1106
+ } }
1107
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
1108
+ i0.ɵɵelementStart(0, "span", 122);
1109
+ i0.ɵɵelement(1, "i", 126);
1110
+ i0.ɵɵtext(2, " Winner");
1111
+ i0.ɵɵelementEnd();
1112
+ } }
1113
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
1114
+ const _r50 = i0.ɵɵgetCurrentView();
1115
+ i0.ɵɵelementStart(0, "div", 210)(1, "div", 211)(2, "div", 120)(3, "strong");
1116
+ i0.ɵɵtext(4);
1117
+ i0.ɵɵelementEnd();
1118
+ i0.ɵɵconditionalCreate(5, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Conditional_5_Template, 3, 0, "span", 122);
1119
+ i0.ɵɵelementStart(6, "span", 58);
1120
+ i0.ɵɵtext(7);
1121
+ i0.ɵɵelementEnd()();
1122
+ i0.ɵɵelementStart(8, "span", 212);
1123
+ i0.ɵɵtext(9);
1124
+ i0.ɵɵelementEnd()();
1125
+ i0.ɵɵelementStart(10, "div", 213);
1126
+ i0.ɵɵtext(11, " Algorithm: ");
1127
+ i0.ɵɵelementStart(12, "strong");
1128
+ i0.ɵɵtext(13);
1129
+ i0.ɵɵelementEnd()();
1130
+ i0.ɵɵelementStart(14, "div", 213);
1131
+ i0.ɵɵtext(15, " Holdout Score: ");
1132
+ i0.ɵɵelementStart(16, "strong");
1133
+ i0.ɵɵtext(17);
1134
+ i0.ɵɵelementEnd()();
1135
+ i0.ɵɵelementStart(18, "div", 214)(19, "button", 215);
1136
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Template_button_click_19_listener() { const mdl_r51 = i0.ɵɵrestoreView(_r50); const ctx_r1 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r1.openModelRecord(mdl_r51.ID)); });
1137
+ i0.ɵɵelement(20, "i", 125);
1138
+ i0.ɵɵtext(21, " Open Model Details ");
1139
+ i0.ɵɵelementEnd();
1140
+ i0.ɵɵelementStart(22, "button", 216);
1141
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Template_button_click_22_listener() { const mdl_r51 = i0.ɵɵrestoreView(_r50); const ctx_r1 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r1.viewTrainingRuns(mdl_r51.ID)); });
1142
+ i0.ɵɵelement(23, "i", 129);
1143
+ i0.ɵɵtext(24, " View Training Runs ");
1144
+ i0.ɵɵelementEnd()()();
1145
+ } if (rf & 2) {
1146
+ const mdl_r51 = ctx;
1147
+ const ctx_r1 = i0.ɵɵnextContext(6);
1148
+ i0.ɵɵadvance(4);
1149
+ i0.ɵɵtextInterpolate1("v", mdl_r51.Version);
1150
+ i0.ɵɵadvance();
1151
+ i0.ɵɵconditional(mdl_r51.Status === "Published" ? 5 : -1);
1152
+ i0.ɵɵadvance();
1153
+ i0.ɵɵclassMap(ctx_r1.statusClass(mdl_r51.Status));
1154
+ i0.ɵɵadvance();
1155
+ i0.ɵɵtextInterpolate(mdl_r51.Status);
1156
+ i0.ɵɵadvance();
1157
+ i0.ɵɵproperty("title", mdl_r51.ID);
1158
+ i0.ɵɵadvance();
1159
+ i0.ɵɵtextInterpolate1("", mdl_r51.ID.slice(0, 8), "\u2026");
1160
+ i0.ɵɵadvance(4);
1161
+ i0.ɵɵtextInterpolate(mdl_r51.Algorithm || ctx_r1.selectedAlgorithmName);
1162
+ i0.ɵɵadvance(4);
1163
+ i0.ɵɵtextInterpolate(ctx_r1.formatHoldout(mdl_r51));
1164
+ } }
1165
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Template(rf, ctx) { if (rf & 1) {
1166
+ i0.ɵɵelementStart(0, "div", 209);
1167
+ i0.ɵɵconditionalCreate(1, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_1_Template, 6, 1, "div", 186);
1168
+ i0.ɵɵconditionalCreate(2, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Conditional_2_Template, 25, 9, "div", 210);
1169
+ i0.ɵɵelementEnd();
1170
+ } if (rf & 2) {
1171
+ let tmp_6_0;
1172
+ const ctx_r1 = i0.ɵɵnextContext(5);
1173
+ i0.ɵɵadvance();
1174
+ i0.ɵɵconditional(ctx_r1.pipelineModels.length > 1 ? 1 : -1);
1175
+ i0.ɵɵadvance();
1176
+ i0.ɵɵconditional((tmp_6_0 = ctx_r1.inspectedModel) ? 2 : -1, tmp_6_0);
1177
+ } }
1178
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Template(rf, ctx) { if (rf & 1) {
1179
+ i0.ɵɵconditionalCreate(0, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_0_Template, 5, 0, "div", 179)(1, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Conditional_1_Template, 3, 2, "div", 209);
1180
+ } if (rf & 2) {
1181
+ const ctx_r1 = i0.ɵɵnextContext(4);
1182
+ i0.ɵɵconditional(ctx_r1.pipelineModels.length === 0 ? 0 : 1);
1183
+ } }
1184
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_49_Template(rf, ctx) { if (rf & 1) {
1185
+ const _r52 = i0.ɵɵgetCurrentView();
1186
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
497
1187
  i0.ɵɵtext(2, "Decision-date column");
498
1188
  i0.ɵɵelementEnd();
499
- i0.ɵɵelementStart(3, "input", 51);
500
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_102_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setAsOfColumn($event.target.value)); });
1189
+ i0.ɵɵelementStart(3, "input", 187);
1190
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_49_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r52); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setAsOfColumn(ctx_r1.inputVal($event))); });
501
1191
  i0.ɵɵelementEnd()();
502
1192
  } if (rf & 2) {
503
- const ctx_r1 = i0.ɵɵnextContext(2);
1193
+ const ctx_r1 = i0.ɵɵnextContext(4);
504
1194
  i0.ɵɵadvance(3);
505
1195
  i0.ɵɵproperty("value", ctx_r1.editAsOf.Column || "");
506
1196
  } }
507
- function PSPipelinesComponent_Conditional_2_Conditional_103_Template(rf, ctx) { if (rf & 1) {
508
- const _r32 = i0.ɵɵgetCurrentView();
509
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
1197
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_50_Template(rf, ctx) { if (rf & 1) {
1198
+ const _r53 = i0.ɵɵgetCurrentView();
1199
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
510
1200
  i0.ɵɵtext(2, "Offset days before label");
511
1201
  i0.ɵɵelementEnd();
512
- i0.ɵɵelementStart(3, "input", 78);
513
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_103_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setAsOfOffset($event.target.value)); });
1202
+ i0.ɵɵelementStart(3, "input", 201);
1203
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_50_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r53); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setAsOfOffset(ctx_r1.inputVal($event))); });
514
1204
  i0.ɵɵelementEnd()();
515
1205
  } if (rf & 2) {
516
- const ctx_r1 = i0.ɵɵnextContext(2);
1206
+ const ctx_r1 = i0.ɵɵnextContext(4);
517
1207
  i0.ɵɵadvance(3);
518
1208
  i0.ɵɵproperty("value", ctx_r1.editAsOf.OffsetDays ?? 0);
519
1209
  } }
520
- function PSPipelinesComponent_Conditional_2_Conditional_120_Template(rf, ctx) { if (rf & 1) {
521
- const _r33 = i0.ɵɵgetCurrentView();
522
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
1210
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_67_Template(rf, ctx) { if (rf & 1) {
1211
+ const _r54 = i0.ɵɵgetCurrentView();
1212
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
523
1213
  i0.ɵɵtext(2, "Test size");
524
1214
  i0.ɵɵelementEnd();
525
- i0.ɵɵelementStart(3, "input", 62);
526
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_120_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r33); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setTestSize($event.target.value)); });
1215
+ i0.ɵɵelementStart(3, "input", 197);
1216
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_67_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r54); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setTestSize(ctx_r1.inputVal($event))); });
527
1217
  i0.ɵɵelementEnd()();
528
1218
  } if (rf & 2) {
529
- const ctx_r1 = i0.ɵɵnextContext(2);
1219
+ const ctx_r1 = i0.ɵɵnextContext(4);
530
1220
  i0.ɵɵadvance(3);
531
1221
  i0.ɵɵproperty("value", ctx_r1.editValidation.TestSize ?? 0.2);
532
1222
  } }
533
- function PSPipelinesComponent_Conditional_2_Conditional_121_Template(rf, ctx) { if (rf & 1) {
534
- const _r34 = i0.ɵɵgetCurrentView();
535
- i0.ɵɵelementStart(0, "div", 50)(1, "label");
1223
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_68_Template(rf, ctx) { if (rf & 1) {
1224
+ const _r55 = i0.ɵɵgetCurrentView();
1225
+ i0.ɵɵelementStart(0, "div", 186)(1, "label");
536
1226
  i0.ɵɵtext(2, "Folds (k)");
537
1227
  i0.ɵɵelementEnd();
538
- i0.ɵɵelementStart(3, "input", 78);
539
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_121_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r34); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setK($event.target.value)); });
1228
+ i0.ɵɵelementStart(3, "input", 201);
1229
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_68_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r55); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.setK(ctx_r1.inputVal($event))); });
540
1230
  i0.ɵɵelementEnd()();
541
1231
  } if (rf & 2) {
542
- const ctx_r1 = i0.ɵɵnextContext(2);
1232
+ const ctx_r1 = i0.ɵɵnextContext(4);
543
1233
  i0.ɵɵadvance(3);
544
1234
  i0.ɵɵproperty("value", ctx_r1.editValidation.K ?? 5);
545
1235
  } }
546
- function PSPipelinesComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
547
- const _r3 = i0.ɵɵgetCurrentView();
548
- i0.ɵɵelementStart(0, "div", 6);
549
- i0.ɵɵrepeaterCreate(1, PSPipelinesComponent_Conditional_2_For_2_Template, 6, 7, "button", 7, _forTrack0);
550
- i0.ɵɵelementEnd();
551
- i0.ɵɵelementStart(3, "div", 8)(4, "button", 9);
552
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.save()); });
553
- i0.ɵɵelement(5, "i", 10);
554
- i0.ɵɵtext(6, " Save ");
555
- i0.ɵɵelementEnd();
556
- i0.ɵɵelementStart(7, "button", 11);
557
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.validate()); });
558
- i0.ɵɵelement(8, "i", 12);
559
- i0.ɵɵtext(9, " Validate ");
560
- i0.ɵɵelementEnd();
561
- i0.ɵɵelementStart(10, "button", 13);
562
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.train()); });
563
- i0.ɵɵelement(11, "i", 14);
564
- i0.ɵɵtext(12, " Train ");
565
- i0.ɵɵelementEnd();
566
- i0.ɵɵelementStart(13, "button", 15);
567
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_13_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.refine()); });
568
- i0.ɵɵelement(14, "i", 16);
569
- i0.ɵɵtext(15, " Agent ");
570
- i0.ɵɵelementEnd();
571
- i0.ɵɵelement(16, "span", 17);
572
- i0.ɵɵconditionalCreate(17, PSPipelinesComponent_Conditional_2_Conditional_17_Template, 3, 0, "span", 18);
573
- i0.ɵɵelementEnd();
574
- i0.ɵɵelementStart(18, "div", 19)(19, "div", 20)(20, "div", 21)(21, "span", 22);
575
- i0.ɵɵtext(22, "Add:");
576
- i0.ɵɵelementEnd();
577
- i0.ɵɵelementStart(23, "button", 23);
578
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_23_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.addSource()); });
579
- i0.ɵɵelement(24, "i", 24);
580
- i0.ɵɵtext(25, " Source");
581
- i0.ɵɵelementEnd();
582
- i0.ɵɵelementStart(26, "button", 25);
583
- i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_26_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.addStep()); });
584
- i0.ɵɵelement(27, "i", 26);
585
- i0.ɵɵtext(28, " Feature step");
586
- i0.ɵɵelementEnd();
587
- i0.ɵɵelement(29, "span", 17);
588
- i0.ɵɵelementStart(30, "span", 27);
589
- i0.ɵɵelement(31, "i", 28);
590
- i0.ɵɵtext(32);
591
- i0.ɵɵelementEnd()();
592
- i0.ɵɵelementStart(33, "div", 29);
593
- i0.ɵɵnamespaceSVG();
594
- i0.ɵɵelementStart(34, "svg", 30);
595
- i0.ɵɵrepeaterCreate(35, PSPipelinesComponent_Conditional_2_For_36_Template, 1, 1, ":svg:path", null, _forTrack1);
596
- i0.ɵɵelementEnd();
597
- i0.ɵɵrepeaterCreate(37, PSPipelinesComponent_Conditional_2_For_38_Template, 11, 13, "div", 31, _forTrack2);
598
- i0.ɵɵelementEnd();
599
- i0.ɵɵnamespaceHTML();
600
- i0.ɵɵelementStart(39, "div", 32)(40, "div", 33)(41, "span");
601
- i0.ɵɵelement(42, "i", 34);
602
- i0.ɵɵtext(43, " Source");
603
- i0.ɵɵelementEnd();
604
- i0.ɵɵelementStart(44, "span");
605
- i0.ɵɵelement(45, "i", 35);
606
- i0.ɵɵtext(46, " Feature");
607
- i0.ɵɵelementEnd();
608
- i0.ɵɵelementStart(47, "span");
609
- i0.ɵɵelement(48, "i", 36);
610
- i0.ɵɵtext(49, " Embedding");
611
- i0.ɵɵelementEnd();
612
- i0.ɵɵelementStart(50, "span");
613
- i0.ɵɵelement(51, "i", 37);
614
- i0.ɵɵtext(52, " Target");
615
- i0.ɵɵelementEnd();
616
- i0.ɵɵelementStart(53, "span");
617
- i0.ɵɵelement(54, "i", 38);
618
- i0.ɵɵtext(55, " Algorithm");
619
- i0.ɵɵelementEnd()()()();
620
- i0.ɵɵelementStart(56, "div", 39)(57, "div", 40)(58, "div", 41);
621
- i0.ɵɵelement(59, "i", 42);
622
- i0.ɵɵelementStart(60, "div", 43)(61, "h3", 44);
623
- i0.ɵɵtext(62);
624
- i0.ɵɵelementEnd();
625
- i0.ɵɵelementStart(63, "div", 27);
626
- i0.ɵɵtext(64);
1236
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template(rf, ctx) { if (rf & 1) {
1237
+ const _r26 = i0.ɵɵgetCurrentView();
1238
+ i0.ɵɵelementStart(0, "as-split-area", 14)(1, "div", 175)(2, "div", 176)(3, "div", 177);
1239
+ i0.ɵɵelement(4, "i", 167);
1240
+ i0.ɵɵelementStart(5, "div", 96)(6, "h3", 178);
1241
+ i0.ɵɵtext(7);
1242
+ i0.ɵɵelementEnd();
1243
+ i0.ɵɵelementStart(8, "div", 179);
1244
+ i0.ɵɵtext(9);
627
1245
  i0.ɵɵelementEnd()();
628
- i0.ɵɵconditionalCreate(65, PSPipelinesComponent_Conditional_2_Conditional_65_Template, 2, 0, "button", 45);
629
- i0.ɵɵelementEnd();
630
- i0.ɵɵelementStart(66, "div", 46);
631
- i0.ɵɵconditionalCreate(67, PSPipelinesComponent_Conditional_2_Conditional_67_Template, 14, 3);
632
- i0.ɵɵconditionalCreate(68, PSPipelinesComponent_Conditional_2_Conditional_68_Template, 20, 4);
633
- i0.ɵɵconditionalCreate(69, PSPipelinesComponent_Conditional_2_Conditional_69_Template, 10, 2);
634
- i0.ɵɵconditionalCreate(70, PSPipelinesComponent_Conditional_2_Conditional_70_Template, 10, 2);
635
- i0.ɵɵconditionalCreate(71, PSPipelinesComponent_Conditional_2_Conditional_71_Template, 5, 0, "div", 27);
1246
+ i0.ɵɵelementStart(10, "button", 180);
1247
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleInspector()); });
1248
+ i0.ɵɵelement(11, "i", 39);
1249
+ i0.ɵɵelementEnd();
1250
+ i0.ɵɵconditionalCreate(12, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_12_Template, 2, 0, "button", 181);
1251
+ i0.ɵɵelementEnd();
1252
+ i0.ɵɵelementStart(13, "div", 182);
1253
+ i0.ɵɵconditionalCreate(14, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_14_Template, 14, 3);
1254
+ i0.ɵɵconditionalCreate(15, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_15_Template, 19, 3);
1255
+ i0.ɵɵconditionalCreate(16, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_16_Template, 10, 2);
1256
+ i0.ɵɵconditionalCreate(17, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_17_Template, 11, 3);
1257
+ i0.ɵɵconditionalCreate(18, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_18_Template, 2, 1);
636
1258
  i0.ɵɵelementEnd()();
637
- i0.ɵɵelementStart(72, "div", 47)(73, "div", 48);
638
- i0.ɵɵelement(74, "i", 49);
639
- i0.ɵɵelementStart(75, "h3");
640
- i0.ɵɵtext(76, "Leakage guard");
1259
+ i0.ɵɵelementStart(19, "div", 183)(20, "div", 184);
1260
+ i0.ɵɵelement(21, "i", 185);
1261
+ i0.ɵɵelementStart(22, "h3");
1262
+ i0.ɵɵtext(23, "Leakage guard");
641
1263
  i0.ɵɵelementEnd()();
642
- i0.ɵɵelementStart(77, "div", 46)(78, "div", 50)(79, "label");
643
- i0.ɵɵtext(80, "Deny-list columns (comma-separated)");
1264
+ i0.ɵɵelementStart(24, "div", 182)(25, "div", 186)(26, "label");
1265
+ i0.ɵɵtext(27, "Deny-list columns (comma-separated)");
644
1266
  i0.ɵɵelementEnd();
645
- i0.ɵɵelementStart(81, "input", 51);
646
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Template_input_input_81_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDeny($event.target.value)); });
1267
+ i0.ɵɵelementStart(28, "input", 187);
1268
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_input_input_28_listener($event) { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setDeny(ctx_r1.inputVal($event))); });
647
1269
  i0.ɵɵelementEnd()();
648
- i0.ɵɵelementStart(82, "div", 50)(83, "label");
649
- i0.ɵɵtext(84, "Single-feature dominance threshold");
1270
+ i0.ɵɵelementStart(29, "div", 186)(30, "label");
1271
+ i0.ɵɵtext(31, "Single-feature dominance threshold");
650
1272
  i0.ɵɵelementEnd();
651
- i0.ɵɵelementStart(85, "input", 52);
652
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Template_input_input_85_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setThreshold($event.target.value)); });
1273
+ i0.ɵɵelementStart(32, "input", 188);
1274
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_input_input_32_listener($event) { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setThreshold(ctx_r1.inputVal($event))); });
653
1275
  i0.ɵɵelementEnd()()()();
654
- i0.ɵɵelementStart(86, "div", 47)(87, "div", 48);
655
- i0.ɵɵelement(88, "i", 53);
656
- i0.ɵɵelementStart(89, "h3");
657
- i0.ɵɵtext(90, "As-of strategy");
1276
+ i0.ɵɵelementStart(33, "div", 183)(34, "div", 184);
1277
+ i0.ɵɵelement(35, "i", 79);
1278
+ i0.ɵɵelementStart(36, "h3");
1279
+ i0.ɵɵtext(37, "As-of strategy");
658
1280
  i0.ɵɵelementEnd()();
659
- i0.ɵɵelementStart(91, "div", 46)(92, "div", 50)(93, "label");
660
- i0.ɵɵtext(94, "Mode");
1281
+ i0.ɵɵelementStart(38, "div", 182)(39, "div", 186)(40, "label");
1282
+ i0.ɵɵtext(41, "Mode");
661
1283
  i0.ɵɵelementEnd();
662
- i0.ɵɵelementStart(95, "select", 54);
663
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Template_select_change_95_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setAsOfMode($event.target.value)); });
664
- i0.ɵɵelementStart(96, "option", 55);
665
- i0.ɵɵtext(97, "none");
1284
+ i0.ɵɵelementStart(42, "select", 189);
1285
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_select_change_42_listener($event) { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setAsOfMode(ctx_r1.inputVal($event))); });
1286
+ i0.ɵɵelementStart(43, "option", 190);
1287
+ i0.ɵɵtext(44, "none");
666
1288
  i0.ɵɵelementEnd();
667
- i0.ɵɵelementStart(98, "option", 56);
668
- i0.ɵɵtext(99, "column");
1289
+ i0.ɵɵelementStart(45, "option", 191);
1290
+ i0.ɵɵtext(46, "column");
669
1291
  i0.ɵɵelementEnd();
670
- i0.ɵɵelementStart(100, "option", 57);
671
- i0.ɵɵtext(101, "offset");
1292
+ i0.ɵɵelementStart(47, "option", 192);
1293
+ i0.ɵɵtext(48, "offset");
672
1294
  i0.ɵɵelementEnd()()();
673
- i0.ɵɵconditionalCreate(102, PSPipelinesComponent_Conditional_2_Conditional_102_Template, 4, 1, "div", 50);
674
- i0.ɵɵconditionalCreate(103, PSPipelinesComponent_Conditional_2_Conditional_103_Template, 4, 1, "div", 50);
1295
+ i0.ɵɵconditionalCreate(49, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_49_Template, 4, 1, "div", 186);
1296
+ i0.ɵɵconditionalCreate(50, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_50_Template, 4, 1, "div", 186);
675
1297
  i0.ɵɵelementEnd()();
676
- i0.ɵɵelementStart(104, "div", 47)(105, "div", 48);
677
- i0.ɵɵelement(106, "i", 58);
678
- i0.ɵɵelementStart(107, "h3");
679
- i0.ɵɵtext(108, "Validation");
1298
+ i0.ɵɵelementStart(51, "div", 183)(52, "div", 184);
1299
+ i0.ɵɵelement(53, "i", 193);
1300
+ i0.ɵɵelementStart(54, "h3");
1301
+ i0.ɵɵtext(55, "Validation");
680
1302
  i0.ɵɵelementEnd()();
681
- i0.ɵɵelementStart(109, "div", 46)(110, "div", 50)(111, "label");
682
- i0.ɵɵtext(112, "Strategy");
1303
+ i0.ɵɵelementStart(56, "div", 182)(57, "div", 186)(58, "label");
1304
+ i0.ɵɵtext(59, "Strategy");
683
1305
  i0.ɵɵelementEnd();
684
- i0.ɵɵelementStart(113, "select", 54);
685
- i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Template_select_change_113_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setValStrategy($event.target.value)); });
686
- i0.ɵɵelementStart(114, "option", 59);
687
- i0.ɵɵtext(115, "train_test_split");
1306
+ i0.ɵɵelementStart(60, "select", 189);
1307
+ i0.ɵɵlistener("change", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_select_change_60_listener($event) { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setValStrategy(ctx_r1.inputVal($event))); });
1308
+ i0.ɵɵelementStart(61, "option", 194);
1309
+ i0.ɵɵtext(62, "train_test_split");
688
1310
  i0.ɵɵelementEnd();
689
- i0.ɵɵelementStart(116, "option", 60);
690
- i0.ɵɵtext(117, "kfold");
1311
+ i0.ɵɵelementStart(63, "option", 195);
1312
+ i0.ɵɵtext(64, "kfold");
691
1313
  i0.ɵɵelementEnd();
692
- i0.ɵɵelementStart(118, "option", 61);
693
- i0.ɵɵtext(119, "holdout");
1314
+ i0.ɵɵelementStart(65, "option", 196);
1315
+ i0.ɵɵtext(66, "holdout");
694
1316
  i0.ɵɵelementEnd()()();
695
- i0.ɵɵconditionalCreate(120, PSPipelinesComponent_Conditional_2_Conditional_120_Template, 4, 1, "div", 50);
696
- i0.ɵɵconditionalCreate(121, PSPipelinesComponent_Conditional_2_Conditional_121_Template, 4, 1, "div", 50);
697
- i0.ɵɵelementStart(122, "div", 50)(123, "label");
698
- i0.ɵɵtext(124, "Locked holdout fraction");
1317
+ i0.ɵɵconditionalCreate(67, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_67_Template, 4, 1, "div", 186);
1318
+ i0.ɵɵconditionalCreate(68, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Conditional_68_Template, 4, 1, "div", 186);
1319
+ i0.ɵɵelementStart(69, "div", 186)(70, "label");
1320
+ i0.ɵɵtext(71, "Locked holdout fraction");
699
1321
  i0.ɵɵelementEnd();
700
- i0.ɵɵelementStart(125, "input", 62);
701
- i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Template_input_input_125_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setHoldout($event.target.value)); });
1322
+ i0.ɵɵelementStart(72, "input", 197);
1323
+ i0.ɵɵlistener("input", function PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template_input_input_72_listener($event) { i0.ɵɵrestoreView(_r26); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setHoldout(ctx_r1.inputVal($event))); });
702
1324
  i0.ɵɵelementEnd()()()()()();
703
1325
  } if (rf & 2) {
704
- const ctx_r1 = i0.ɵɵnextContext();
705
- i0.ɵɵadvance();
706
- i0.ɵɵrepeater(ctx_r1.pipelines);
707
- i0.ɵɵadvance(3);
708
- i0.ɵɵproperty("disabled", !ctx_r1.dirty || ctx_r1.busy);
1326
+ const ctx_r1 = i0.ɵɵnextContext(3);
1327
+ i0.ɵɵproperty("size", ctx_r1.inspectorSizePct)("minSize", 20)("maxSize", 55);
1328
+ i0.ɵɵadvance(4);
1329
+ i0.ɵɵproperty("ngClass", ctx_r1.selectedNode.icon);
1330
+ i0.ɵɵattribute("data-type", ctx_r1.selectedNode.type);
709
1331
  i0.ɵɵadvance(3);
710
- i0.ɵɵproperty("disabled", ctx_r1.busy);
1332
+ i0.ɵɵtextInterpolate(ctx_r1.selectedNode.title);
1333
+ i0.ɵɵadvance(2);
1334
+ i0.ɵɵtextInterpolate1("", ctx_r1.nodeTypeLabel(ctx_r1.selectedNode.type), " \u00B7 selected");
711
1335
  i0.ɵɵadvance(3);
712
- i0.ɵɵproperty("disabled", ctx_r1.busy || ctx_r1.dirty);
1336
+ i0.ɵɵconditional(ctx_r1.selectedNode.type === "src" || ctx_r1.selectedNode.type === "feat" || ctx_r1.selectedNode.type === "emb" ? 12 : -1);
1337
+ i0.ɵɵadvance(2);
1338
+ i0.ɵɵconditional(ctx_r1.selectedSource ? 14 : -1);
1339
+ i0.ɵɵadvance();
1340
+ i0.ɵɵconditional(ctx_r1.selectedStep ? 15 : -1);
1341
+ i0.ɵɵadvance();
1342
+ i0.ɵɵconditional(ctx_r1.selectedNode.type === "target" ? 16 : -1);
1343
+ i0.ɵɵadvance();
1344
+ i0.ɵɵconditional(ctx_r1.selectedNode.type === "algo" ? 17 : -1);
1345
+ i0.ɵɵadvance();
1346
+ i0.ɵɵconditional(ctx_r1.selectedNode.type === "output" ? 18 : -1);
1347
+ i0.ɵɵadvance(10);
1348
+ i0.ɵɵproperty("value", ctx_r1.denyText);
1349
+ i0.ɵɵadvance(4);
1350
+ i0.ɵɵproperty("value", ctx_r1.editLeakage.SingleFeatureDominanceThreshold);
1351
+ i0.ɵɵadvance(10);
1352
+ i0.ɵɵproperty("value", ctx_r1.editAsOf.Mode);
713
1353
  i0.ɵɵadvance(7);
714
- i0.ɵɵconditional(ctx_r1.dirty ? 17 : -1);
715
- i0.ɵɵadvance(15);
716
- i0.ɵɵtextInterpolate2(" ", ctx_r1.nodes.length, " nodes \u00B7 ", ctx_r1.edges.length, " edges");
1354
+ i0.ɵɵconditional(ctx_r1.editAsOf.Mode === "column" ? 49 : -1);
1355
+ i0.ɵɵadvance();
1356
+ i0.ɵɵconditional(ctx_r1.editAsOf.Mode === "offset" ? 50 : -1);
1357
+ i0.ɵɵadvance(10);
1358
+ i0.ɵɵproperty("value", ctx_r1.editValidation.Strategy);
1359
+ i0.ɵɵadvance(7);
1360
+ i0.ɵɵconditional(ctx_r1.editValidation.Strategy === "train_test_split" ? 67 : -1);
1361
+ i0.ɵɵadvance();
1362
+ i0.ɵɵconditional(ctx_r1.editValidation.Strategy === "kfold" ? 68 : -1);
1363
+ i0.ɵɵadvance(4);
1364
+ i0.ɵɵproperty("value", ctx_r1.editValidation.LockedHoldoutFraction);
1365
+ } }
1366
+ function PSPipelinesComponent_Conditional_2_Conditional_42_Template(rf, ctx) { if (rf & 1) {
1367
+ const _r21 = i0.ɵɵgetCurrentView();
1368
+ i0.ɵɵelementStart(0, "div", 37)(1, "as-split", 130);
1369
+ i0.ɵɵlistener("dragEnd", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_as_split_dragEnd_1_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onInspectorSplitDragEnd($event.sizes)); });
1370
+ i0.ɵɵelementStart(2, "as-split-area", 15)(3, "div", 131)(4, "div", 132)(5, "div", 133)(6, "span", 134);
1371
+ i0.ɵɵtext(7, "Add to Graph:");
1372
+ i0.ɵɵelementEnd();
1373
+ i0.ɵɵelementStart(8, "button", 135);
1374
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.addSource()); });
1375
+ i0.ɵɵelement(9, "i", 87);
1376
+ i0.ɵɵtext(10, " Source");
1377
+ i0.ɵɵelementEnd();
1378
+ i0.ɵɵelementStart(11, "button", 136);
1379
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.addStep()); });
1380
+ i0.ɵɵelement(12, "i", 83);
1381
+ i0.ɵɵtext(13, " Feature step");
1382
+ i0.ɵɵelementEnd()();
1383
+ i0.ɵɵelementStart(14, "div", 137)(15, "div", 138)(16, "button", 139);
1384
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_button_click_16_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.zoomOut()); });
1385
+ i0.ɵɵelement(17, "i", 140);
1386
+ i0.ɵɵelementEnd();
1387
+ i0.ɵɵelementStart(18, "span", 141);
1388
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_span_click_18_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.resetZoom()); });
1389
+ i0.ɵɵtext(19);
1390
+ i0.ɵɵelementEnd();
1391
+ i0.ɵɵelementStart(20, "button", 142);
1392
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.zoomIn()); });
1393
+ i0.ɵɵelement(21, "i", 8);
1394
+ i0.ɵɵelementEnd()();
1395
+ i0.ɵɵelementStart(22, "button", 143);
1396
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Conditional_42_Template_button_click_22_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleInspector()); });
1397
+ i0.ɵɵelement(23, "i", 144);
1398
+ i0.ɵɵelementEnd();
1399
+ i0.ɵɵelementStart(24, "span", 145);
1400
+ i0.ɵɵelement(25, "i", 146);
1401
+ i0.ɵɵtext(26);
1402
+ i0.ɵɵelementEnd()()();
1403
+ i0.ɵɵelementStart(27, "div", 147)(28, "div", 148);
1404
+ i0.ɵɵnamespaceSVG();
1405
+ i0.ɵɵelementStart(29, "svg", 149)(30, "defs")(31, "marker", 150);
1406
+ i0.ɵɵelement(32, "path", 151);
1407
+ i0.ɵɵelementEnd();
1408
+ i0.ɵɵelementStart(33, "marker", 152);
1409
+ i0.ɵɵelement(34, "path", 153);
1410
+ i0.ɵɵelementEnd()();
1411
+ i0.ɵɵrepeaterCreate(35, PSPipelinesComponent_Conditional_2_Conditional_42_For_36_Template, 1, 4, ":svg:path", 154, _forTrack2);
1412
+ i0.ɵɵelementEnd();
1413
+ i0.ɵɵrepeaterCreate(37, PSPipelinesComponent_Conditional_2_Conditional_42_For_38_Template, 11, 16, "div", 155, _forTrack3);
1414
+ i0.ɵɵelementEnd()();
1415
+ i0.ɵɵnamespaceHTML();
1416
+ i0.ɵɵelementStart(39, "div", 156)(40, "div", 157)(41, "span");
1417
+ i0.ɵɵelement(42, "i", 158);
1418
+ i0.ɵɵtext(43, " Source");
1419
+ i0.ɵɵelementEnd();
1420
+ i0.ɵɵelementStart(44, "span");
1421
+ i0.ɵɵelement(45, "i", 159);
1422
+ i0.ɵɵtext(46, " Feature");
1423
+ i0.ɵɵelementEnd();
1424
+ i0.ɵɵelementStart(47, "span");
1425
+ i0.ɵɵelement(48, "i", 160);
1426
+ i0.ɵɵtext(49, " Embedding");
1427
+ i0.ɵɵelementEnd();
1428
+ i0.ɵɵelementStart(50, "span");
1429
+ i0.ɵɵelement(51, "i", 161);
1430
+ i0.ɵɵtext(52, " Target");
1431
+ i0.ɵɵelementEnd();
1432
+ i0.ɵɵelementStart(53, "span");
1433
+ i0.ɵɵelement(54, "i", 162);
1434
+ i0.ɵɵtext(55, " Algorithm");
1435
+ i0.ɵɵelementEnd();
1436
+ i0.ɵɵelementStart(56, "span");
1437
+ i0.ɵɵelement(57, "i", 163);
1438
+ i0.ɵɵtext(58, " Model");
1439
+ i0.ɵɵelementEnd()()()()();
1440
+ i0.ɵɵconditionalCreate(59, PSPipelinesComponent_Conditional_2_Conditional_42_Conditional_59_Template, 73, 22, "as-split-area", 14);
1441
+ i0.ɵɵelementEnd()();
1442
+ } if (rf & 2) {
1443
+ const ctx_r1 = i0.ɵɵnextContext(2);
1444
+ i0.ɵɵadvance();
1445
+ i0.ɵɵproperty("gutterSize", 6);
717
1446
  i0.ɵɵadvance();
718
- i0.ɵɵstyleProp("width", ctx_r1.canvasW, "px")("height", ctx_r1.canvasH, "px");
1447
+ i0.ɵɵproperty("size", ctx_r1.isInspectorCollapsed ? 100 : ctx_r1.canvasSizePct)("minSize", 40);
1448
+ i0.ɵɵadvance(14);
1449
+ i0.ɵɵproperty("disabled", ctx_r1.zoomLevel <= 0.5);
1450
+ i0.ɵɵadvance(3);
1451
+ i0.ɵɵtextInterpolate1("", ctx_r1.Math.round(ctx_r1.zoomLevel * 100), "%");
719
1452
  i0.ɵɵadvance();
720
- i0.ɵɵattribute("width", ctx_r1.canvasW)("height", ctx_r1.canvasH)("viewBox", "0 0 " + ctx_r1.canvasW + " " + ctx_r1.canvasH);
1453
+ i0.ɵɵproperty("disabled", ctx_r1.zoomLevel >= 2);
1454
+ i0.ɵɵadvance(2);
1455
+ i0.ɵɵclassProp("active", !ctx_r1.isInspectorCollapsed);
1456
+ i0.ɵɵproperty("title", ctx_r1.isInspectorCollapsed ? "Open Inspector" : "Collapse Inspector");
1457
+ i0.ɵɵadvance(4);
1458
+ i0.ɵɵtextInterpolate2(" ", ctx_r1.nodes.length, " nodes \u00B7 ", ctx_r1.edges.length, " edges");
1459
+ i0.ɵɵadvance(2);
1460
+ i0.ɵɵstyleProp("width", ctx_r1.canvasW, "px")("height", ctx_r1.canvasH, "px")("transform", "scale(" + ctx_r1.zoomLevel + ")");
721
1461
  i0.ɵɵadvance();
1462
+ i0.ɵɵattribute("width", ctx_r1.canvasW)("height", ctx_r1.canvasH);
1463
+ i0.ɵɵadvance(6);
722
1464
  i0.ɵɵrepeater(ctx_r1.edges);
723
1465
  i0.ɵɵadvance(2);
724
1466
  i0.ɵɵrepeater(ctx_r1.nodes);
725
1467
  i0.ɵɵadvance(22);
726
- i0.ɵɵproperty("ngClass", ctx_r1.selectedNode.icon);
727
- i0.ɵɵattribute("data-type", ctx_r1.selectedNode.type);
1468
+ i0.ɵɵconditional(!ctx_r1.isInspectorCollapsed ? 59 : -1);
1469
+ } }
1470
+ function PSPipelinesComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
1471
+ const _r3 = i0.ɵɵgetCurrentView();
1472
+ i0.ɵɵelementStart(0, "div", 11);
1473
+ i0.ɵɵconditionalCreate(1, PSPipelinesComponent_Conditional_2_Conditional_1_Template, 5, 0, "div", 12);
1474
+ i0.ɵɵelementStart(2, "as-split", 13);
1475
+ i0.ɵɵlistener("dragEnd", function PSPipelinesComponent_Conditional_2_Template_as_split_dragEnd_2_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSplitDragEnd($event.sizes)); });
1476
+ i0.ɵɵconditionalCreate(3, PSPipelinesComponent_Conditional_2_Conditional_3_Template, 27, 13, "as-split-area", 14);
1477
+ i0.ɵɵelementStart(4, "as-split-area", 15)(5, "div", 16)(6, "div", 17)(7, "div", 18)(8, "div", 19)(9, "span");
1478
+ i0.ɵɵtext(10, "Pipelines");
1479
+ i0.ɵɵelementEnd();
1480
+ i0.ɵɵelement(11, "i", 20);
1481
+ i0.ɵɵelementStart(12, "span");
1482
+ i0.ɵɵtext(13);
1483
+ i0.ɵɵelementEnd();
1484
+ i0.ɵɵelement(14, "i", 20);
1485
+ i0.ɵɵelementStart(15, "strong");
1486
+ i0.ɵɵtext(16);
1487
+ i0.ɵɵelementEnd()();
1488
+ i0.ɵɵelementStart(17, "div", 21)(18, "h1");
1489
+ i0.ɵɵtext(19);
1490
+ i0.ɵɵelementEnd();
1491
+ i0.ɵɵconditionalCreate(20, PSPipelinesComponent_Conditional_2_Conditional_20_Template, 3, 4);
1492
+ i0.ɵɵelementEnd()();
1493
+ i0.ɵɵelementStart(21, "div", 22)(22, "button", 23);
1494
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_22_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleViewMode()); });
1495
+ i0.ɵɵelement(23, "i", 24);
1496
+ i0.ɵɵtext(24);
1497
+ i0.ɵɵelementEnd();
1498
+ i0.ɵɵelementStart(25, "button", 25);
1499
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_25_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cloneSelected()); });
1500
+ i0.ɵɵelement(26, "i", 26);
1501
+ i0.ɵɵtext(27, " Clone ");
1502
+ i0.ɵɵelementEnd();
1503
+ i0.ɵɵelementStart(28, "button", 27);
1504
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_28_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.save()); });
1505
+ i0.ɵɵelement(29, "i", 28);
1506
+ i0.ɵɵtext(30, " Save ");
1507
+ i0.ɵɵelementEnd();
1508
+ i0.ɵɵelementStart(31, "button", 29);
1509
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_31_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.validate()); });
1510
+ i0.ɵɵelement(32, "i", 30);
1511
+ i0.ɵɵtext(33, " Validate ");
1512
+ i0.ɵɵelementEnd();
1513
+ i0.ɵɵelementStart(34, "button", 31);
1514
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_34_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.train()); });
1515
+ i0.ɵɵelement(35, "i", 32);
1516
+ i0.ɵɵtext(36, " Train ");
1517
+ i0.ɵɵelementEnd();
1518
+ i0.ɵɵelementStart(37, "button", 33);
1519
+ i0.ɵɵlistener("click", function PSPipelinesComponent_Conditional_2_Template_button_click_37_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.refine()); });
1520
+ i0.ɵɵelement(38, "i", 34);
1521
+ i0.ɵɵtext(39, " Agent ");
1522
+ i0.ɵɵelementEnd();
1523
+ i0.ɵɵconditionalCreate(40, PSPipelinesComponent_Conditional_2_Conditional_40_Template, 3, 0, "span", 35);
1524
+ i0.ɵɵelementEnd()();
1525
+ i0.ɵɵconditionalCreate(41, PSPipelinesComponent_Conditional_2_Conditional_41_Template, 182, 18, "div", 36);
1526
+ i0.ɵɵconditionalCreate(42, PSPipelinesComponent_Conditional_2_Conditional_42_Template, 60, 20, "div", 37);
1527
+ i0.ɵɵelementEnd()()()();
1528
+ } if (rf & 2) {
1529
+ const ctx_r1 = i0.ɵɵnextContext();
1530
+ i0.ɵɵclassProp("catalog-collapsed", ctx_r1.isCatalogCollapsed);
1531
+ i0.ɵɵadvance();
1532
+ i0.ɵɵconditional(ctx_r1.isCatalogCollapsed ? 1 : -1);
1533
+ i0.ɵɵadvance();
1534
+ i0.ɵɵproperty("gutterSize", 6);
1535
+ i0.ɵɵadvance();
1536
+ i0.ɵɵconditional(!ctx_r1.isCatalogCollapsed ? 3 : -1);
1537
+ i0.ɵɵadvance();
1538
+ i0.ɵɵproperty("size", ctx_r1.isCatalogCollapsed ? 100 : ctx_r1.workspaceSizePct)("minSize", 40);
1539
+ i0.ɵɵadvance(9);
1540
+ i0.ɵɵtextInterpolate(ctx_r1.pipelineDomain(ctx_r1.selectedPipeline));
728
1541
  i0.ɵɵadvance(3);
729
- i0.ɵɵtextInterpolate(ctx_r1.selectedNode.title);
730
- i0.ɵɵadvance(2);
731
- i0.ɵɵtextInterpolate1("", ctx_r1.nodeTypeLabel(ctx_r1.selectedNode.type), " \u00B7 selected");
1542
+ i0.ɵɵtextInterpolate(ctx_r1.selectedPipeline == null ? null : ctx_r1.selectedPipeline.Name);
1543
+ i0.ɵɵadvance(3);
1544
+ i0.ɵɵtextInterpolate(ctx_r1.selectedPipeline == null ? null : ctx_r1.selectedPipeline.Name);
732
1545
  i0.ɵɵadvance();
733
- i0.ɵɵconditional(ctx_r1.selectedNode.type === "src" || ctx_r1.selectedNode.type === "feat" || ctx_r1.selectedNode.type === "emb" ? 65 : -1);
1546
+ i0.ɵɵconditional(ctx_r1.selectedPipeline ? 20 : -1);
734
1547
  i0.ɵɵadvance(2);
735
- i0.ɵɵconditional(ctx_r1.selectedSource ? 67 : -1);
736
- i0.ɵɵadvance();
737
- i0.ɵɵconditional(ctx_r1.selectedStep ? 68 : -1);
1548
+ i0.ɵɵproperty("title", ctx_r1.isGraphView ? "Switch to 5-Stage Progressive Flow" : "Switch to Pipeline Graph View");
738
1549
  i0.ɵɵadvance();
739
- i0.ɵɵconditional(ctx_r1.selectedNode.type === "target" ? 69 : -1);
1550
+ i0.ɵɵclassProp("fa-diagram-project", !ctx_r1.isGraphView)("fa-layer-group", ctx_r1.isGraphView);
740
1551
  i0.ɵɵadvance();
741
- i0.ɵɵconditional(ctx_r1.selectedNode.type === "algo" ? 70 : -1);
1552
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.isGraphView ? "Stages View" : "Graph View", " ");
742
1553
  i0.ɵɵadvance();
743
- i0.ɵɵconditional(ctx_r1.selectedNode.type === "output" ? 71 : -1);
744
- i0.ɵɵadvance(10);
745
- i0.ɵɵproperty("value", ctx_r1.denyText);
746
- i0.ɵɵadvance(4);
747
- i0.ɵɵproperty("value", ctx_r1.editLeakage.SingleFeatureDominanceThreshold);
748
- i0.ɵɵadvance(10);
749
- i0.ɵɵproperty("value", ctx_r1.editAsOf.Mode);
750
- i0.ɵɵadvance(7);
751
- i0.ɵɵconditional(ctx_r1.editAsOf.Mode === "column" ? 102 : -1);
1554
+ i0.ɵɵproperty("disabled", !ctx_r1.selectedPipeline);
1555
+ i0.ɵɵadvance(3);
1556
+ i0.ɵɵproperty("disabled", !ctx_r1.dirty || ctx_r1.busy);
1557
+ i0.ɵɵadvance(3);
1558
+ i0.ɵɵproperty("disabled", ctx_r1.busy);
1559
+ i0.ɵɵadvance(3);
1560
+ i0.ɵɵproperty("disabled", ctx_r1.busy || ctx_r1.dirty);
1561
+ i0.ɵɵadvance(6);
1562
+ i0.ɵɵconditional(ctx_r1.dirty ? 40 : -1);
752
1563
  i0.ɵɵadvance();
753
- i0.ɵɵconditional(ctx_r1.editAsOf.Mode === "offset" ? 103 : -1);
754
- i0.ɵɵadvance(10);
755
- i0.ɵɵproperty("value", ctx_r1.editValidation.Strategy);
756
- i0.ɵɵadvance(7);
757
- i0.ɵɵconditional(ctx_r1.editValidation.Strategy === "train_test_split" ? 120 : -1);
1564
+ i0.ɵɵconditional(ctx_r1.viewMode === "stages" ? 41 : -1);
758
1565
  i0.ɵɵadvance();
759
- i0.ɵɵconditional(ctx_r1.editValidation.Strategy === "kfold" ? 121 : -1);
760
- i0.ɵɵadvance(4);
761
- i0.ɵɵproperty("value", ctx_r1.editValidation.LockedHoldoutFraction);
1566
+ i0.ɵɵconditional(ctx_r1.isGraphView ? 42 : -1);
1567
+ } }
1568
+ function PSPipelinesComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
1569
+ const _r56 = i0.ɵɵgetCurrentView();
1570
+ i0.ɵɵelementStart(0, "ps-pipeline-wizard", 217);
1571
+ i0.ɵɵlistener("saved", function PSPipelinesComponent_Conditional_3_Template_ps_pipeline_wizard_saved_0_listener($event) { i0.ɵɵrestoreView(_r56); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onWizardSaved($event)); })("trainRequested", function PSPipelinesComponent_Conditional_3_Template_ps_pipeline_wizard_trainRequested_0_listener($event) { i0.ɵɵrestoreView(_r56); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onWizardTrainRequested($event)); })("closed", function PSPipelinesComponent_Conditional_3_Template_ps_pipeline_wizard_closed_0_listener() { i0.ɵɵrestoreView(_r56); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeWizard()); });
1572
+ i0.ɵɵelementEnd();
1573
+ } if (rf & 2) {
1574
+ const ctx_r1 = i0.ɵɵnextContext();
1575
+ i0.ɵɵproperty("engine", ctx_r1.engine)("provider", ctx_r1.provider)("currentUser", ctx_r1.currentUser)("cloneFrom", ctx_r1.wizardClonePipeline)("initialAlgorithmId", ctx_r1.initialWizardAlgorithmId);
762
1576
  } }
763
- const NODE_W = 190;
764
- const NODE_H = 86;
765
- const COL_GAP = 86;
766
- const ROW_GAP = 30;
767
- const PAD_X = 28;
768
- const PAD_Y = 28;
1577
+ const NODE_W = 230;
1578
+ const NODE_H = 96;
1579
+ const COL_GAP = 96;
1580
+ const ROW_GAP = 28;
1581
+ const PAD_X = 40;
1582
+ const PAD_Y = 40;
769
1583
  const SOURCE_KINDS = ['Entity', 'Query', 'ExternalEntity', 'VectorSet', 'FeaturePipeline'];
770
1584
  const STEP_KINDS = ['select', 'impute', 'standardize', 'onehot', 'bin', 'embedding', 'llm-derived', 'flow-agent', 'vision-llm'];
771
1585
  const PROBLEM_TYPES = ['classification', 'regression'];
@@ -790,23 +1604,21 @@ const STEP_ICONS = {
790
1604
  };
791
1605
  const PS_PIPELINES_STARTER_PROMPT = 'Help me build a training pipeline. I will tell you what I want to predict and on which entity; ' +
792
1606
  'you assemble the features (guarding against leakage), pick an algorithm, and train a versioned model.';
793
- /**
794
- * Training Pipelines panel — a LIVE, **editable** visual DAG builder over `MJ: ML Training Pipelines`.
795
- * The whole graph is persisted as JSON (`SourceBindings` + `FeatureSteps` + `AlgorithmID` +
796
- * `TargetVariable` + `AsOfStrategy`/`LeakageGuard`/`ValidationStrategy`), so the editor reads/writes the
797
- * REAL spec. The parsed spec is the editable state; nodes/edges are derived from it for rendering, so an
798
- * edit → re-derive → re-layout. Save serializes back to the entity; Train runs the pipeline. Editing is
799
- * inspector-driven (explicit controls, no fiddly port-dragging) — better for a structured, persisted DAG.
800
- * Fully entity-agnostic — every node derives from the pipeline's own refs. See
801
- * plans/predictive-studio-pipeline-builder.md.
802
- */
803
1607
  export class PSPipelinesComponent {
804
1608
  engine;
805
1609
  provider = null;
806
1610
  currentUser = null;
807
1611
  askAgent = new EventEmitter();
1612
+ navigate = new EventEmitter();
808
1613
  cdr = inject(ChangeDetectorRef);
809
1614
  notifications = inject(MJNotificationService);
1615
+ navigationService = inject(NavigationService);
1616
+ isCatalogCollapsed = false;
1617
+ catalogSizePct = 28;
1618
+ workspaceSizePct = 72;
1619
+ isInspectorCollapsed = false;
1620
+ canvasSizePct = 72;
1621
+ inspectorSizePct = 28;
810
1622
  starterPrompt = PS_PIPELINES_STARTER_PROMPT;
811
1623
  sourceKinds = SOURCE_KINDS;
812
1624
  stepKinds = STEP_KINDS;
@@ -814,6 +1626,47 @@ export class PSPipelinesComponent {
814
1626
  imputeStrategies = IMPUTE_STRATEGIES;
815
1627
  pipelines = [];
816
1628
  selectedPipelineId = '';
1629
+ showWizard = false;
1630
+ wizardClonePipeline = null;
1631
+ initialWizardAlgorithmId;
1632
+ viewMode = 'stages';
1633
+ searchQuery = '';
1634
+ activeFilter = 'all';
1635
+ NODE_W = NODE_W;
1636
+ NODE_H = NODE_H;
1637
+ Math = Math;
1638
+ zoomLevel = 1;
1639
+ openWizard(algoId) {
1640
+ this.wizardClonePipeline = null;
1641
+ this.initialWizardAlgorithmId = algoId;
1642
+ this.showWizard = true;
1643
+ }
1644
+ cloneSelected() {
1645
+ if (!this.selectedPipeline)
1646
+ return;
1647
+ this.wizardClonePipeline = this.selectedPipeline;
1648
+ this.initialWizardAlgorithmId = undefined;
1649
+ this.showWizard = true;
1650
+ }
1651
+ closeWizard() {
1652
+ this.showWizard = false;
1653
+ this.wizardClonePipeline = null;
1654
+ this.initialWizardAlgorithmId = undefined;
1655
+ }
1656
+ async onWizardSaved(pipeline) {
1657
+ this.closeWizard();
1658
+ await this.engine.Config(true, this.currentUser ?? undefined, this.provider ?? undefined);
1659
+ this.pipelines = this.engine.Pipelines;
1660
+ this.selectPipeline(pipeline.ID);
1661
+ this.cdr.detectChanges();
1662
+ }
1663
+ async onWizardTrainRequested(pipeline) {
1664
+ this.closeWizard();
1665
+ await this.engine.Config(true, this.currentUser ?? undefined, this.provider ?? undefined);
1666
+ this.pipelines = this.engine.Pipelines;
1667
+ this.selectPipeline(pipeline.ID);
1668
+ await this.train();
1669
+ }
817
1670
  // Editable spec state (the source of truth; nodes/edges are derived).
818
1671
  editSources = [];
819
1672
  editSteps = [];
@@ -833,21 +1686,284 @@ export class PSPipelinesComponent {
833
1686
  busy = false;
834
1687
  nodeById = new Map();
835
1688
  stepSeq = 0;
1689
+ ngOnChanges(_changes) {
1690
+ this.refreshFromEngine();
1691
+ }
836
1692
  ngOnInit() {
1693
+ const saved = UserInfoEngine.Instance.GetSetting('mj.predictiveStudio.pipelines.layout');
1694
+ if (saved) {
1695
+ try {
1696
+ const parsed = JSON.parse(saved);
1697
+ if (typeof parsed.catalogSizePct === 'number' && parsed.catalogSizePct >= 15 && parsed.catalogSizePct <= 60) {
1698
+ this.catalogSizePct = parsed.catalogSizePct;
1699
+ this.workspaceSizePct = 100 - this.catalogSizePct;
1700
+ }
1701
+ if (typeof parsed.isCatalogCollapsed === 'boolean') {
1702
+ this.isCatalogCollapsed = parsed.isCatalogCollapsed;
1703
+ }
1704
+ if (typeof parsed.canvasSizePct === 'number' && parsed.canvasSizePct >= 40 && parsed.canvasSizePct <= 85) {
1705
+ this.canvasSizePct = parsed.canvasSizePct;
1706
+ this.inspectorSizePct = 100 - this.canvasSizePct;
1707
+ }
1708
+ if (typeof parsed.isInspectorCollapsed === 'boolean') {
1709
+ this.isInspectorCollapsed = parsed.isInspectorCollapsed;
1710
+ }
1711
+ }
1712
+ catch { }
1713
+ }
1714
+ this.refreshFromEngine();
1715
+ }
1716
+ onSplitDragEnd(sizes) {
1717
+ if (Array.isArray(sizes) && sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') {
1718
+ this.catalogSizePct = Math.round(sizes[0]);
1719
+ this.workspaceSizePct = Math.round(sizes[1]);
1720
+ this.saveLayoutPrefs();
1721
+ }
1722
+ }
1723
+ toggleCatalog() {
1724
+ this.isCatalogCollapsed = !this.isCatalogCollapsed;
1725
+ this.saveLayoutPrefs();
1726
+ this.cdr.markForCheck();
1727
+ }
1728
+ onInspectorSplitDragEnd(sizes) {
1729
+ if (Array.isArray(sizes) && sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') {
1730
+ this.canvasSizePct = Math.round(sizes[0]);
1731
+ this.inspectorSizePct = Math.round(sizes[1]);
1732
+ this.saveLayoutPrefs();
1733
+ }
1734
+ }
1735
+ toggleInspector() {
1736
+ this.isInspectorCollapsed = !this.isInspectorCollapsed;
1737
+ this.saveLayoutPrefs();
1738
+ this.cdr.markForCheck();
1739
+ }
1740
+ saveLayoutPrefs() {
1741
+ const prefs = {
1742
+ catalogSizePct: this.catalogSizePct,
1743
+ isCatalogCollapsed: this.isCatalogCollapsed,
1744
+ canvasSizePct: this.canvasSizePct,
1745
+ inspectorSizePct: this.inspectorSizePct,
1746
+ isInspectorCollapsed: this.isInspectorCollapsed,
1747
+ };
1748
+ UserInfoEngine.Instance.SetSettingDebounced('mj.predictiveStudio.pipelines.layout', JSON.stringify(prefs));
1749
+ }
1750
+ openModelRecord(modelId) {
1751
+ if (!modelId)
1752
+ return;
1753
+ try {
1754
+ const p = this.provider ?? new Metadata();
1755
+ if (!p)
1756
+ return;
1757
+ const entity = p.EntityByName('MJ: ML Models');
1758
+ if (!entity)
1759
+ return;
1760
+ const ck = CompositeKey.FromURLSegment(entity, modelId);
1761
+ this.navigationService.OpenEntityRecord('MJ: ML Models', ck);
1762
+ }
1763
+ catch (err) {
1764
+ this.notifications.CreateSimpleNotification(`Could not open model: ${err instanceof Error ? err.message : String(err)}`, 'error');
1765
+ }
1766
+ }
1767
+ viewTrainingRuns(modelId) {
1768
+ if (this.navigate.observed) {
1769
+ this.navigate.emit('experiments');
1770
+ return;
1771
+ }
1772
+ try {
1773
+ const p = this.provider ?? new Metadata();
1774
+ if (!p)
1775
+ return;
1776
+ const runs = this.engine?.TrainingRuns ?? [];
1777
+ const match = modelId
1778
+ ? runs.find((r) => UUIDsEqual(r.ResultingModelID, modelId))
1779
+ : runs.find((r) => this.selectedPipelineId && UUIDsEqual(r.PipelineID, this.selectedPipelineId));
1780
+ if (match) {
1781
+ const entity = p.EntityByName('MJ: ML Training Runs');
1782
+ if (entity) {
1783
+ const ck = CompositeKey.FromURLSegment(entity, match.ID);
1784
+ this.navigationService.OpenEntityRecord('MJ: ML Training Runs', ck);
1785
+ return;
1786
+ }
1787
+ }
1788
+ this.navigationService.OpenNavItemByName('Experiments');
1789
+ }
1790
+ catch (err) {
1791
+ this.notifications.CreateSimpleNotification(`Could not open training runs: ${err instanceof Error ? err.message : String(err)}`, 'error');
1792
+ }
1793
+ }
1794
+ refreshFromEngine() {
837
1795
  this.pipelines = this.engine?.Pipelines ?? [];
838
- if (this.pipelines.length > 0) {
1796
+ if (!this.selectedPipelineId && this.pipelines.length > 0) {
839
1797
  this.selectPipeline(this.pipelines[0].ID);
840
1798
  }
1799
+ else if (this.selectedPipelineId && !this.pipelines.some((p) => UUIDsEqual(p.ID, this.selectedPipelineId))) {
1800
+ if (this.pipelines.length > 0) {
1801
+ this.selectPipeline(this.pipelines[0].ID);
1802
+ }
1803
+ }
1804
+ }
1805
+ get isGraphView() {
1806
+ return this.viewMode === 'graph' || this.viewMode === 'dag';
1807
+ }
1808
+ toggleViewMode() {
1809
+ this.viewMode = this.isGraphView ? 'stages' : 'graph';
1810
+ }
1811
+ zoomIn() {
1812
+ this.zoomLevel = Math.min(2.0, Math.round((this.zoomLevel + 0.15) * 100) / 100);
1813
+ }
1814
+ zoomOut() {
1815
+ this.zoomLevel = Math.max(0.5, Math.round((this.zoomLevel - 0.15) * 100) / 100);
1816
+ }
1817
+ resetZoom() {
1818
+ this.zoomLevel = 1;
1819
+ }
1820
+ inputVal(e) {
1821
+ return e.target.value;
1822
+ }
1823
+ isEdgeActive(edge) {
1824
+ return this.selectedId === edge.from || this.selectedId === edge.to;
1825
+ }
1826
+ setFilter(f) {
1827
+ this.activeFilter = f;
1828
+ }
1829
+ onSearchInput(ev) {
1830
+ this.searchQuery = (ev.target?.value ?? '').toLowerCase();
1831
+ }
1832
+ get filteredPipelines() {
1833
+ return (this.pipelines ?? []).filter((p) => {
1834
+ if (this.searchQuery) {
1835
+ const nameMatch = p.Name?.toLowerCase().includes(this.searchQuery);
1836
+ const entityMatch = p.TargetEntity?.toLowerCase().includes(this.searchQuery);
1837
+ const targetMatch = p.TargetVariable?.toLowerCase().includes(this.searchQuery);
1838
+ if (!nameMatch && !entityMatch && !targetMatch)
1839
+ return false;
1840
+ }
1841
+ if (this.activeFilter === 'published')
1842
+ return p.Status === 'Published';
1843
+ if (this.activeFilter === 'draft')
1844
+ return p.Status === 'Draft';
1845
+ if (this.activeFilter === 'core')
1846
+ return p.TargetEntity?.includes('AI') || p.Name?.includes('AI Agent');
1847
+ return true;
1848
+ });
1849
+ }
1850
+ pipelineDomain(p) {
1851
+ if (!p)
1852
+ return 'Platform';
1853
+ const n = (p.Name || '') + (p.TargetEntity || '');
1854
+ if (n.includes('MoreCheese') || n.includes('Membership') || n.includes('Event') || n.includes('Course'))
1855
+ return 'More Cheese Club';
1856
+ if (n.includes('AI') || n.includes('Prompt') || n.includes('Agent'))
1857
+ return 'MemberJunction Platform';
1858
+ return 'BizApps';
1859
+ }
1860
+ targetEntityName(p) {
1861
+ if (!p)
1862
+ return 'Entity';
1863
+ return p.TargetEntity || this.editSources[0]?.Ref || 'Entity';
1864
+ }
1865
+ get pipelineModels() {
1866
+ if (!this.selectedPipelineId || !this.engine?.Models)
1867
+ return [];
1868
+ return this.engine.Models.filter((m) => UUIDsEqual(m.PipelineID, this.selectedPipelineId));
1869
+ }
1870
+ get publishedModel() {
1871
+ return this.pipelineModels.find((m) => m.Status === 'Published') ?? this.pipelineModels[0];
1872
+ }
1873
+ selectedModelVersionId = null;
1874
+ get inspectedModel() {
1875
+ if (this.selectedModelVersionId) {
1876
+ const found = this.pipelineModels.find((m) => UUIDsEqual(m.ID, this.selectedModelVersionId));
1877
+ if (found)
1878
+ return found;
1879
+ }
1880
+ return this.publishedModel;
1881
+ }
1882
+ selectModelVersion(id) {
1883
+ this.selectedModelVersionId = id;
1884
+ this.cdr.markForCheck();
1885
+ }
1886
+ bestMetricForPipeline(p) {
1887
+ if (!p || !this.engine?.Models)
1888
+ return null;
1889
+ const models = this.engine.Models.filter((m) => UUIDsEqual(m.PipelineID, p.ID));
1890
+ if (models.length === 0)
1891
+ return null;
1892
+ const best = models.find((m) => m.Status === 'Published') ?? models[0];
1893
+ return this.formatHoldout(best);
1894
+ }
1895
+ selectStep(id) {
1896
+ this.selectNode(id);
1897
+ }
1898
+ getStepColumns(step) {
1899
+ switch (step.Kind) {
1900
+ case 'select':
1901
+ case 'standardize':
1902
+ return step.Columns ?? [];
1903
+ case 'impute':
1904
+ case 'onehot':
1905
+ case 'bin':
1906
+ return step.Column ? [step.Column] : [];
1907
+ default:
1908
+ return [];
1909
+ }
1910
+ }
1911
+ getStepInputs(step) {
1912
+ return step.Inputs ?? [];
1913
+ }
1914
+ formatHoldout(m) {
1915
+ if (!m.HoldoutMetrics)
1916
+ return m.Metrics ? 'Trained' : 'Pending';
1917
+ try {
1918
+ const hm = typeof m.HoldoutMetrics === 'string' ? JSON.parse(m.HoldoutMetrics) : m.HoldoutMetrics;
1919
+ if (hm.auc != null)
1920
+ return `AUC ${(Number(hm.auc)).toFixed(3)}`;
1921
+ if (hm.r2 != null)
1922
+ return `R² ${(Number(hm.r2)).toFixed(3)}`;
1923
+ if (hm.rmse != null)
1924
+ return `RMSE ${(Number(hm.rmse)).toFixed(2)}`;
1925
+ if (hm.accuracy != null)
1926
+ return `Acc ${(Number(hm.accuracy) * 100).toFixed(1)}%`;
1927
+ return 'Scored';
1928
+ }
1929
+ catch {
1930
+ return 'Scored';
1931
+ }
1932
+ }
1933
+ get asOfLabel() {
1934
+ if (this.editAsOf.Mode === 'column')
1935
+ return `Column: ${this.editAsOf.Column || 'DecisionDate'}`;
1936
+ if (this.editAsOf.Mode === 'offset')
1937
+ return `Offset: ${this.editAsOf.OffsetDays || 0}d prior`;
1938
+ return 'None (Static snapshot)';
1939
+ }
1940
+ get selectedAlgorithmName() {
1941
+ return this.engine?.AlgorithmName(this.editAlgorithmId) || 'Auto-select (Tournament)';
841
1942
  }
842
- // ---- pipeline selection: entity JSON → editable state ----
843
1943
  selectPipeline(id) {
844
1944
  this.selectedPipelineId = id;
1945
+ this.selectedModelVersionId = null;
845
1946
  const p = this.pipelines.find((x) => UUIDsEqual(x.ID, id));
846
1947
  if (!p) {
847
1948
  return;
848
1949
  }
849
- this.editSources = this.parse(p.SourceBindings, []);
850
- this.editSteps = this.parse(p.FeatureSteps, { Steps: [] }).Steps ?? [];
1950
+ // Defensive parsing against non-array JSON schemas
1951
+ const rawSources = this.parse(p.SourceBindings, []);
1952
+ this.editSources = Array.isArray(rawSources)
1953
+ ? rawSources
1954
+ : rawSources && typeof rawSources === 'object'
1955
+ ? [{ Kind: 'Entity', Ref: rawSources.EntityID || '' }]
1956
+ : [];
1957
+ const rawSteps = this.parse(p.FeatureSteps, { Steps: [] });
1958
+ if (Array.isArray(rawSteps)) {
1959
+ this.editSteps = rawSteps;
1960
+ }
1961
+ else if (rawSteps && typeof rawSteps === 'object' && 'Steps' in rawSteps && Array.isArray(rawSteps.Steps)) {
1962
+ this.editSteps = rawSteps.Steps;
1963
+ }
1964
+ else {
1965
+ this.editSteps = [];
1966
+ }
851
1967
  this.editTargetVariable = p.TargetVariable ?? '';
852
1968
  this.editProblemType = p.ProblemType ?? 'classification';
853
1969
  this.editAlgorithmId = p.AlgorithmID ?? '';
@@ -890,8 +2006,32 @@ export class PSPipelinesComponent {
890
2006
  this.editSteps.forEach((step) => nodes.push(this.stepNode(step)));
891
2007
  nodes.push(this.targetNode());
892
2008
  nodes.push(this.algoNode());
893
- nodes.push({ id: '__output', type: 'output', title: 'Model', icon: 'fa-solid fa-cube', x: 0, y: 0, hasIn: false, hasOut: false, rows: [{ k: 'status', v: this.selectedPipeline?.Status ?? 'Draft' }] });
2009
+ nodes.push({
2010
+ id: '__output',
2011
+ type: 'output',
2012
+ title: 'Model Artifact',
2013
+ icon: 'fa-solid fa-cube',
2014
+ tag: this.selectedPipeline?.Status ?? 'Draft',
2015
+ x: 0,
2016
+ y: 0,
2017
+ hasIn: true,
2018
+ hasOut: false,
2019
+ rows: [
2020
+ { k: 'status', v: this.selectedPipeline?.Status ?? 'Draft' },
2021
+ { k: 'serving', v: this.selectedPipeline?.Status === 'Published' ? 'Active' : 'Not Serving' }
2022
+ ]
2023
+ });
894
2024
  const stepIds = new Set(this.editSteps.map((s) => s.Id));
2025
+ const sourceKeyToId = new Map();
2026
+ this.editSources.forEach((sb, i) => {
2027
+ const sid = `src:${i}`;
2028
+ sourceKeyToId.set(sid.toLowerCase(), sid);
2029
+ if (sb.Ref)
2030
+ sourceKeyToId.set(sb.Ref.toLowerCase(), sid);
2031
+ if (sb.Alias)
2032
+ sourceKeyToId.set(sb.Alias.toLowerCase(), sid);
2033
+ });
2034
+ const stepSourcesConnected = new Set();
895
2035
  const referenced = new Set();
896
2036
  for (const step of this.editSteps) {
897
2037
  for (const input of step.Inputs ?? []) {
@@ -899,9 +2039,20 @@ export class PSPipelinesComponent {
899
2039
  edges.push({ from: input, to: step.Id });
900
2040
  referenced.add(input);
901
2041
  }
2042
+ else {
2043
+ const matchedSrc = sourceKeyToId.get(input.toLowerCase());
2044
+ if (matchedSrc) {
2045
+ edges.push({ from: matchedSrc, to: step.Id });
2046
+ stepSourcesConnected.add(step.Id);
2047
+ }
2048
+ }
902
2049
  }
903
2050
  }
904
- const roots = this.editSteps.filter((s) => (s.Inputs ?? []).filter((i) => stepIds.has(i)).length === 0);
2051
+ const roots = this.editSteps.filter((s) => {
2052
+ const hasStepInput = (s.Inputs ?? []).some((i) => stepIds.has(i));
2053
+ const hasSourceInput = stepSourcesConnected.has(s.Id);
2054
+ return !hasStepInput && !hasSourceInput;
2055
+ });
905
2056
  const sourceIds = this.editSources.map((_s, i) => `src:${i}`);
906
2057
  if (this.editSteps.length > 0) {
907
2058
  for (const sid of sourceIds) {
@@ -918,28 +2069,74 @@ export class PSPipelinesComponent {
918
2069
  for (const t of this.editSteps.filter((s) => !referenced.has(s.Id))) {
919
2070
  edges.push({ from: t.Id, to: '__algo' });
920
2071
  }
2072
+ // Connect source to target so target sits cleanly in the feature/target layer
2073
+ if (sourceIds.length > 0) {
2074
+ edges.push({ from: sourceIds[0], to: '__target' });
2075
+ }
921
2076
  edges.push({ from: '__target', to: '__algo' });
922
2077
  edges.push({ from: '__algo', to: '__output' });
923
2078
  this.nodes = nodes;
924
2079
  this.edges = edges;
925
2080
  this.markPorts();
926
- this.layoutDag();
2081
+ this.layoutGraph();
927
2082
  }
928
2083
  sourceNode(sb, i) {
929
- const rows = [{ k: 'kind', v: sb.Kind }, { k: 'ref', v: sb.Ref || '—' }];
2084
+ const md = this.provider ?? new Metadata();
2085
+ let title = sb.Alias;
2086
+ if (!title && sb.Kind === 'Entity' && sb.Ref) {
2087
+ const entity = md.EntityByName(sb.Ref);
2088
+ title = entity?.DisplayName || entity?.Name;
2089
+ }
2090
+ title = title || sb.Ref || 'source';
2091
+ const rows = [{ k: 'kind', v: sb.Kind }];
930
2092
  if (sb.Alias) {
931
2093
  rows.push({ k: 'alias', v: sb.Alias });
932
2094
  }
933
- return { id: `src:${i}`, type: 'src', title: sb.Alias || sb.Ref || 'source', icon: SOURCE_ICONS[sb.Kind] ?? 'fa-solid fa-database', tag: sb.Kind, rows, x: 0, y: 0, hasIn: false, hasOut: true };
2095
+ if (sb.Kind === 'Entity' && title !== sb.Ref) {
2096
+ rows.push({ k: 'entity', v: sb.Ref });
2097
+ }
2098
+ return {
2099
+ id: `src:${i}`,
2100
+ type: 'src',
2101
+ title,
2102
+ icon: SOURCE_ICONS[sb.Kind] ?? 'fa-solid fa-database',
2103
+ tag: sb.Kind,
2104
+ rows,
2105
+ x: 0,
2106
+ y: 0,
2107
+ hasIn: false,
2108
+ hasOut: true
2109
+ };
934
2110
  }
935
2111
  stepNode(step) {
936
- return { id: step.Id, type: step.Kind === 'embedding' ? 'emb' : 'feat', title: step.Label || step.Kind, icon: STEP_ICONS[step.Kind] ?? 'fa-solid fa-sliders', tag: step.Kind, rows: this.stepRows(step), x: 0, y: 0, hasIn: true, hasOut: true };
2112
+ return {
2113
+ id: step.Id,
2114
+ type: step.Kind === 'embedding' ? 'emb' : 'feat',
2115
+ title: step.Label || step.Kind,
2116
+ icon: STEP_ICONS[step.Kind] ?? 'fa-solid fa-sliders',
2117
+ tag: step.Kind,
2118
+ rows: this.stepRows(step),
2119
+ x: 0,
2120
+ y: 0,
2121
+ hasIn: true,
2122
+ hasOut: true
2123
+ };
937
2124
  }
938
2125
  stepRows(step) {
939
2126
  switch (step.Kind) {
940
- case 'select': return [{ k: 'columns', v: `${step.Columns.length} cols` }];
2127
+ case 'select': {
2128
+ const preview = step.Columns.length <= 3
2129
+ ? step.Columns.join(', ')
2130
+ : `${step.Columns.slice(0, 2).join(', ')} +${step.Columns.length - 2} more`;
2131
+ return [{ k: 'columns', v: preview || '0 cols' }];
2132
+ }
941
2133
  case 'impute': return [{ k: 'column', v: step.Column }, { k: 'strategy', v: step.Strategy }];
942
- case 'standardize': return [{ k: 'columns', v: `${step.Columns.length} cols` }, { k: 'scaler', v: 'z-score' }];
2134
+ case 'standardize': {
2135
+ const preview = step.Columns.length <= 3
2136
+ ? step.Columns.join(', ')
2137
+ : `${step.Columns.slice(0, 2).join(', ')} +${step.Columns.length - 2} more`;
2138
+ return [{ k: 'columns', v: preview || '0 cols' }, { k: 'scaler', v: 'z-score' }];
2139
+ }
943
2140
  case 'onehot': return [{ k: 'column', v: step.Column }];
944
2141
  case 'bin': return [{ k: 'column', v: step.Column }, { k: 'bins', v: String(step.Bins) }];
945
2142
  case 'embedding': return [{ k: 'entity', v: step.Entity }, { k: 'dims', v: String(step.Dims) }];
@@ -950,14 +2147,39 @@ export class PSPipelinesComponent {
950
2147
  }
951
2148
  }
952
2149
  targetNode() {
953
- return { id: '__target', type: 'target', title: `Target: ${this.editTargetVariable || '—'}`, icon: 'fa-solid fa-bullseye', rows: [{ k: 'variable', v: this.editTargetVariable || '—' }, { k: 'type', v: this.editProblemType }], x: 0, y: 0, hasIn: false, hasOut: true };
2150
+ return {
2151
+ id: '__target',
2152
+ type: 'target',
2153
+ title: `Target: ${this.editTargetVariable || '—'}`,
2154
+ icon: 'fa-solid fa-bullseye',
2155
+ tag: this.editProblemType,
2156
+ rows: [
2157
+ { k: 'variable', v: this.editTargetVariable || '—' },
2158
+ { k: 'type', v: this.editProblemType }
2159
+ ],
2160
+ x: 0,
2161
+ y: 0,
2162
+ hasIn: true,
2163
+ hasOut: true
2164
+ };
954
2165
  }
955
2166
  algoNode() {
956
- const name = this.engine.AlgorithmName(this.editAlgorithmId);
2167
+ const name = this.engine?.AlgorithmName(this.editAlgorithmId) || 'Algorithm';
957
2168
  const rows = [{ k: 'algorithm', v: name }];
958
2169
  const hp = this.parse(this.editHyperparams, {});
959
- Object.entries(hp).slice(0, 2).forEach(([k, v]) => rows.push({ k, v: String(v) }));
960
- return { id: '__algo', type: 'algo', title: name, icon: 'fa-solid fa-shapes', rows, x: 0, y: 0, hasIn: true, hasOut: true };
2170
+ Object.entries(hp).slice(0, 1).forEach(([k, v]) => rows.push({ k, v: String(v) }));
2171
+ return {
2172
+ id: '__algo',
2173
+ type: 'algo',
2174
+ title: name,
2175
+ icon: 'fa-solid fa-shapes',
2176
+ tag: 'Algorithm',
2177
+ rows,
2178
+ x: 0,
2179
+ y: 0,
2180
+ hasIn: true,
2181
+ hasOut: true
2182
+ };
961
2183
  }
962
2184
  markPorts() {
963
2185
  const hasIn = new Set(this.edges.map((e) => e.to));
@@ -1018,7 +2240,6 @@ export class PSPipelinesComponent {
1018
2240
  this.markDirty();
1019
2241
  }
1020
2242
  }
1021
- /** Read an arbitrary field off the selected step for display (narrowed by template @switch). */
1022
2243
  stepField(field) {
1023
2244
  const s = this.selectedStep;
1024
2245
  const v = s?.[field];
@@ -1043,7 +2264,6 @@ export class PSPipelinesComponent {
1043
2264
  s.Label = label || undefined;
1044
2265
  this.markDirty();
1045
2266
  } }
1046
- /** Change a step's Kind, resetting its config to that Kind's defaults (preserving Id/Label/Inputs). */
1047
2267
  setStepKind(kind) {
1048
2268
  const idx = this.editSteps.findIndex((s) => s.Id === this.selectedId);
1049
2269
  if (idx < 0) {
@@ -1056,7 +2276,6 @@ export class PSPipelinesComponent {
1056
2276
  hasInput(stepId) {
1057
2277
  return (this.selectedStep?.Inputs ?? []).includes(stepId);
1058
2278
  }
1059
- /** Toggle an upstream step input, preventing cycles. */
1060
2279
  toggleInput(stepId) {
1061
2280
  const s = this.selectedStep;
1062
2281
  if (!s) {
@@ -1075,7 +2294,6 @@ export class PSPipelinesComponent {
1075
2294
  }
1076
2295
  this.markDirty();
1077
2296
  }
1078
- /** True if making `target` depend on `from` would create a cycle (from is downstream of target). */
1079
2297
  createsCycle(from, target) {
1080
2298
  const byId = new Map(this.editSteps.map((s) => [s.Id, s]));
1081
2299
  const seen = new Set();
@@ -1100,12 +2318,6 @@ export class PSPipelinesComponent {
1100
2318
  setHyperparams(v) { this.editHyperparams = v; this.markDirty(); }
1101
2319
  // ---- leakage / as-of / validation editing ----
1102
2320
  get denyText() { return this.editLeakage.DenyFields.join(', '); }
1103
- /**
1104
- * Parse the deny-field input. Uses the shared `parseDenyList` so a pasted
1105
- * bracketed list ("[CheckInTime, Status]") yields clean column names rather than
1106
- * "[CheckInTime" / "Status]" — entries which match no column and therefore leave
1107
- * the most dangerous columns silently unguarded. The server re-validates on save.
1108
- */
1109
2321
  setDeny(text) { this.editLeakage.DenyFields = parseDenyList(text); this.dirty = true; }
1110
2322
  setThreshold(v) { const n = Number(v); if (!Number.isNaN(n)) {
1111
2323
  this.editLeakage.SingleFeatureDominanceThreshold = n;
@@ -1153,7 +2365,6 @@ export class PSPipelinesComponent {
1153
2365
  const step = this.selectedStep;
1154
2366
  if (step) {
1155
2367
  this.editSteps = this.editSteps.filter((s) => s.Id !== step.Id);
1156
- // Drop the deleted id from any downstream Inputs.
1157
2368
  for (const s of this.editSteps) {
1158
2369
  if (s.Inputs?.includes(step.Id)) {
1159
2370
  s.Inputs = s.Inputs.filter((i) => i !== step.Id);
@@ -1172,13 +2383,12 @@ export class PSPipelinesComponent {
1172
2383
  case 'onehot': return { ...base, Kind: 'onehot', Column: '' };
1173
2384
  case 'bin': return { ...base, Kind: 'bin', Column: '', Bins: 5 };
1174
2385
  case 'embedding': return { ...base, Kind: 'embedding', Entity: '', EmbeddingModelRef: '', Dims: 384 };
1175
- case 'llm-derived': return { ...base, Kind: 'llm-derived', FeaturePipelineRef: '' };
2386
+ case 'llm-derived': return { ...base, Kind: 'llm-derived', FeaturePipelineRef: '', Columns: [] };
1176
2387
  case 'flow-agent': return { ...base, Kind: 'flow-agent', FlowAgentRef: '', InputMapping: {}, OutputMapping: {} };
1177
2388
  case 'vision-llm': return { ...base, Kind: 'vision-llm', ImageColumn: '', Prompt: { InlinePrompt: '' }, Output: { FeatureName: '', Kind: 'category' } };
1178
2389
  }
1179
2390
  }
1180
2391
  // ---- save / validate / train ----
1181
- /** Serialize the editable state back onto the pipeline entity and Save(). */
1182
2392
  async save() {
1183
2393
  const p = this.selectedPipeline;
1184
2394
  if (!p || this.busy) {
@@ -1209,7 +2419,6 @@ export class PSPipelinesComponent {
1209
2419
  this.cdr.detectChanges();
1210
2420
  }
1211
2421
  }
1212
- /** Client-side spec sanity check before training. */
1213
2422
  validate() {
1214
2423
  const issues = [];
1215
2424
  if (this.editSources.length === 0) {
@@ -1234,7 +2443,6 @@ export class PSPipelinesComponent {
1234
2443
  this.notifications.CreateSimpleNotification('Pipeline looks valid — ready to train.', 'success', 3000);
1235
2444
  }
1236
2445
  }
1237
- /** Run the pipeline via the Train Remote Op, then refresh the engine. */
1238
2446
  async train() {
1239
2447
  const p = this.selectedPipeline;
1240
2448
  if (!p || this.busy || this.dirty) {
@@ -1273,78 +2481,94 @@ export class PSPipelinesComponent {
1273
2481
  }
1274
2482
  statusClass(status) {
1275
2483
  switch (status) {
1276
- case 'Active': return 'green';
1277
- case 'Draft': return 'amber';
1278
- case 'Disabled': return 'gray';
1279
- default: return 'gray';
2484
+ case 'Published': return 'published';
2485
+ case 'Validated': return 'validated';
2486
+ default: return 'draft';
1280
2487
  }
1281
2488
  }
1282
2489
  nodeTypeLabel(type) {
1283
2490
  switch (type) {
1284
2491
  case 'src': return 'Source';
1285
- case 'feat': return 'Feature step';
2492
+ case 'feat': return 'Feature Step';
1286
2493
  case 'emb': return 'Embedding';
1287
2494
  case 'target': return 'Target';
1288
2495
  case 'algo': return 'Algorithm';
1289
- case 'output': return 'Model output';
2496
+ case 'output': return 'Output Model';
1290
2497
  }
1291
2498
  }
1292
- // ---- layered layout (deterministic, topology-driven) ----
1293
- layoutDag() {
1294
- this.nodeById = new Map(this.nodes.map((n) => [n.id, n]));
1295
- const incoming = new Map();
1296
- const outgoing = new Map();
2499
+ // ---- Graph layered layout ----
2500
+ layoutGraph() {
2501
+ this.nodeById.clear();
2502
+ for (const n of this.nodes) {
2503
+ this.nodeById.set(n.id, n);
2504
+ }
2505
+ const inEdges = new Map();
1297
2506
  for (const n of this.nodes) {
1298
- incoming.set(n.id, []);
1299
- outgoing.set(n.id, []);
2507
+ inEdges.set(n.id, []);
1300
2508
  }
1301
2509
  for (const e of this.edges) {
1302
- if (this.nodeById.has(e.from) && this.nodeById.has(e.to)) {
1303
- outgoing.get(e.from).push(e.to);
1304
- incoming.get(e.to).push(e.from);
2510
+ inEdges.get(e.to)?.push(e.from);
2511
+ }
2512
+ const layerOf = new Map();
2513
+ const computeLayer = (id, path) => {
2514
+ if (layerOf.has(id)) {
2515
+ return layerOf.get(id);
2516
+ }
2517
+ if (path.has(id)) {
2518
+ return 0;
1305
2519
  }
2520
+ path.add(id);
2521
+ const preds = inEdges.get(id) ?? [];
2522
+ let l = 0;
2523
+ for (const p of preds) {
2524
+ l = Math.max(l, computeLayer(p, path) + 1);
2525
+ }
2526
+ path.delete(id);
2527
+ layerOf.set(id, l);
2528
+ return l;
2529
+ };
2530
+ for (const n of this.nodes) {
2531
+ computeLayer(n.id, new Set());
1306
2532
  }
1307
- const depth = this.computeLayers(incoming, outgoing);
1308
- const maxDepth = Math.max(0, ...depth.values());
1309
- const columns = Array.from({ length: maxDepth + 1 }, () => []);
2533
+ const algoNode = this.nodes.find((n) => n.id === '__algo');
2534
+ const outNode = this.nodes.find((n) => n.id === '__output');
2535
+ let maxFeatureLayer = 0;
1310
2536
  for (const n of this.nodes) {
1311
- columns[depth.get(n.id) ?? 0].push(n);
1312
- }
1313
- this.positionColumns(columns);
1314
- }
1315
- computeLayers(incoming, outgoing) {
1316
- const depth = new Map(this.nodes.map((n) => [n.id, 0]));
1317
- const indeg = new Map(this.nodes.map((n) => [n.id, incoming.get(n.id).length]));
1318
- const queue = this.nodes.filter((n) => indeg.get(n.id) === 0).map((n) => n.id);
1319
- while (queue.length) {
1320
- const id = queue.shift();
1321
- const d = depth.get(id);
1322
- for (const next of outgoing.get(id)) {
1323
- depth.set(next, Math.max(depth.get(next), d + 1));
1324
- indeg.set(next, indeg.get(next) - 1);
1325
- if (indeg.get(next) === 0) {
1326
- queue.push(next);
1327
- }
2537
+ if (n.id !== '__algo' && n.id !== '__output') {
2538
+ maxFeatureLayer = Math.max(maxFeatureLayer, layerOf.get(n.id) ?? 0);
1328
2539
  }
1329
2540
  }
1330
- return depth;
1331
- }
1332
- positionColumns(columns) {
1333
- const colStep = NODE_W + COL_GAP;
1334
- const rowStep = NODE_H + ROW_GAP;
1335
- const tallest = Math.max(1, ...columns.map((c) => c.length));
1336
- const contentH = tallest * NODE_H + (tallest - 1) * ROW_GAP;
1337
- columns.forEach((col, ci) => {
1338
- const x = PAD_X + ci * colStep;
1339
- const colH = col.length * NODE_H + (col.length - 1) * ROW_GAP;
1340
- const yStart = PAD_Y + (contentH - colH) / 2;
1341
- col.forEach((node, ri) => {
1342
- node.x = x;
1343
- node.y = yStart + ri * rowStep;
2541
+ if (algoNode) {
2542
+ layerOf.set('__algo', maxFeatureLayer + 1);
2543
+ }
2544
+ if (outNode) {
2545
+ layerOf.set('__output', maxFeatureLayer + 2);
2546
+ }
2547
+ const layers = new Map();
2548
+ for (const n of this.nodes) {
2549
+ const l = layerOf.get(n.id) ?? 0;
2550
+ const arr = layers.get(l) ?? [];
2551
+ arr.push(n);
2552
+ layers.set(l, arr);
2553
+ }
2554
+ let maxCol = 0;
2555
+ let maxRow = 0;
2556
+ layers.forEach((nodesInCol, col) => {
2557
+ maxCol = Math.max(maxCol, col);
2558
+ maxRow = Math.max(maxRow, nodesInCol.length);
2559
+ });
2560
+ const maxCanvasHeight = PAD_Y * 2 + maxRow * NODE_H + Math.max(0, maxRow - 1) * ROW_GAP;
2561
+ layers.forEach((nodesInCol, col) => {
2562
+ const colHeight = nodesInCol.length * NODE_H + Math.max(0, nodesInCol.length - 1) * ROW_GAP;
2563
+ // Vertically center this column relative to the tallest column in the graph
2564
+ const colStartY = PAD_Y + Math.max(0, Math.floor((maxCanvasHeight - PAD_Y * 2 - colHeight) / 2));
2565
+ nodesInCol.forEach((n, row) => {
2566
+ n.x = PAD_X + col * (NODE_W + COL_GAP);
2567
+ n.y = colStartY + row * (NODE_H + ROW_GAP);
1344
2568
  });
1345
2569
  });
1346
- this.canvasW = PAD_X * 2 + columns.length * NODE_W + (columns.length - 1) * COL_GAP;
1347
- this.canvasH = PAD_Y * 2 + contentH;
2570
+ this.canvasW = Math.max(920, PAD_X * 2 + (maxCol + 1) * NODE_W + maxCol * COL_GAP);
2571
+ this.canvasH = Math.max(540, maxCanvasHeight);
1348
2572
  }
1349
2573
  edgePath(edge) {
1350
2574
  const from = this.nodeById.get(edge.from);
@@ -1352,28 +2576,30 @@ export class PSPipelinesComponent {
1352
2576
  if (!from || !to) {
1353
2577
  return '';
1354
2578
  }
1355
- const sx = from.x + NODE_W;
1356
- const sy = from.y + NODE_H / 2;
1357
- const tx = to.x;
1358
- const ty = to.y + NODE_H / 2;
1359
- const dx = tx - sx;
1360
- const c1x = sx + dx * 0.5;
1361
- const c2x = tx - dx * 0.5;
1362
- return `M ${sx} ${sy} C ${c1x} ${sy}, ${c2x} ${ty}, ${tx} ${ty}`;
2579
+ const x1 = from.x + NODE_W;
2580
+ const y1 = from.y + NODE_H / 2;
2581
+ // Account for port diameter / marker-end: end 2px before port center
2582
+ const x2 = to.x - 2;
2583
+ const y2 = to.y + NODE_H / 2;
2584
+ const dx = Math.max(36, Math.abs(x2 - x1) * 0.45);
2585
+ return `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`;
1363
2586
  }
1364
2587
  static ɵfac = function PSPipelinesComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSPipelinesComponent)(); };
1365
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSPipelinesComponent, selectors: [["ps-pipelines"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser" }, outputs: { askAgent: "askAgent" }, decls: 3, vars: 1, consts: [["data-testid", "ps-pipelines-panel", 1, "ps-panel", "ps-pipelines"], ["data-testid", "ps-pipelines-empty", 1, "ps-empty"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-diagram-project"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-pipelines-ask-agent", 3, "click"], [1, "fa-solid", "fa-robot"], ["data-testid", "ps-pipelines-picker", 1, "pl-picker"], ["data-testid", "ps-pipelines-pill", 1, "pl-pill", 3, "on", "title"], [1, "pl-toolbar"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-pipelines-save", 3, "click", "disabled"], [1, "fa-solid", "fa-floppy-disk"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-validate", 3, "click", "disabled"], [1, "fa-solid", "fa-circle-check"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-train", 3, "click", "disabled"], [1, "fa-solid", "fa-play"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "ps-spacer"], ["data-testid", "ps-pipelines-dirty", 1, "ps-tag", "amber"], [1, "builder"], [1, "canvas-wrap"], [1, "canvas-bar"], [1, "ps-small", "ps-muted", 2, "font-weight", "600"], ["data-testid", "ps-pipelines-add-source", 1, "ps-pchip", "s", 3, "click"], [1, "fa-solid", "fa-database"], ["data-testid", "ps-pipelines-add-step", 1, "ps-pchip", "f", 3, "click"], [1, "fa-solid", "fa-sliders"], [1, "ps-small", "ps-muted"], [1, "fa-solid", "fa-circle-nodes"], ["data-testid", "ps-pipelines-canvas", 1, "ps-flow", "big"], ["data-testid", "ps-pipelines-edges", 1, "ps-edges"], ["data-testid", "ps-pipelines-node", 1, "ps-node", 3, "ngClass", "selected", "left", "top"], [1, "flow-toolbar"], [1, "legend"], [1, "sw", "src"], [1, "sw", "feat"], [1, "sw", "emb"], [1, "sw", "target"], [1, "sw", "algo"], ["data-testid", "ps-pipelines-inspector", 1, "ps-col", "inspector"], [1, "ps-card", "insp"], [1, "ihead"], [1, "tile", 3, "ngClass"], [2, "flex", "1"], ["data-testid", "ps-pipelines-inspector-title"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-delete"], [1, "ps-card-body"], [1, "ps-card"], [1, "ps-card-head"], [1, "fa-solid", "fa-shield-halved", 2, "color", "var(--mj-status-warning)"], [1, "ps-field"], ["type", "text", 1, "mj-input", 3, "input", "value"], ["type", "number", "step", "0.05", "min", "0", "max", "1", 1, "mj-input", 3, "input", "value"], [1, "fa-solid", "fa-clock-rotate-left", 2, "color", "var(--mj-brand-primary)"], [1, "mj-input", 3, "change", "value"], ["value", "none"], ["value", "column"], ["value", "offset"], [1, "fa-solid", "fa-scissors", 2, "color", "var(--mj-brand-primary)"], ["value", "train_test_split"], ["value", "kfold"], ["value", "holdout"], ["type", "number", "step", "0.05", 1, "mj-input", 3, "input", "value"], ["data-testid", "ps-pipelines-pill", 1, "pl-pill", 3, "click", "title"], [1, "pl-pill-name"], [1, "ps-badge"], [1, "fa-solid", "fa-pen"], ["data-testid", "ps-pipelines-node", 1, "ps-node", 3, "click", "ngClass"], [1, "nh"], [1, "ps-tag"], [1, "nb"], [1, "nb-row"], [1, "port", "in"], [1, "port", "out"], [1, "k"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-delete", 3, "click"], [1, "fa-solid", "fa-trash"], [3, "value"], ["type", "number", 1, "mj-input", 3, "input", "value"], [1, "inputs-list"], [1, "inp-row"], [1, "ps-small", "ps-muted", 2, "margin-top", "4px"], ["type", "checkbox", 3, "change", "checked"], ["rows", "4", 1, "mj-textarea", 3, "input", "value"]], template: function PSPipelinesComponent_Template(rf, ctx) { if (rf & 1) {
2588
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSPipelinesComponent, selectors: [["ps-pipelines"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser", viewMode: "viewMode" }, outputs: { askAgent: "askAgent", navigate: "navigate" }, features: [i0.ɵɵNgOnChangesFeature], decls: 4, vars: 2, consts: [["data-testid", "ps-pipelines-panel", 1, "ps-panel", "ps-pipelines"], ["data-testid", "ps-pipelines-empty", 1, "ps-empty"], [1, "pl-layout", 3, "catalog-collapsed"], [3, "engine", "provider", "currentUser", "cloneFrom", "initialAlgorithmId"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-diagram-project"], [1, "empty-actions", 2, "display", "flex", "gap", "8px", "margin-top", "12px"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-pipelines-new-btn-empty", 3, "click"], [1, "fa-solid", "fa-plus"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-ask-agent", 3, "click"], [1, "fa-solid", "fa-robot"], [1, "pl-layout"], ["role", "region", "aria-label", "Pipelines catalog (collapsed)", 1, "pl-collapsed-strip"], ["direction", "horizontal", "unit", "percent", 1, "pl-splitter", 3, "dragEnd", "gutterSize"], [3, "size", "minSize", "maxSize"], [3, "size", "minSize"], ["data-testid", "ps-pipelines-workspace", 1, "pl-workspace"], [1, "pl-workspace-header"], [1, "pl-workspace-title-area"], [1, "pl-breadcrumbs"], [1, "fa-solid", "fa-chevron-right", 2, "font-size", "10px"], [1, "pl-title-row"], [1, "pl-toolbar"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "title"], [1, "fa-solid"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-clone", 3, "click", "disabled"], [1, "fa-solid", "fa-copy"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-save", 3, "click", "disabled"], [1, "fa-solid", "fa-floppy-disk"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-validate", 3, "click", "disabled"], [1, "fa-solid", "fa-circle-check"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-pipelines-train", 3, "click", "disabled"], [1, "fa-solid", "fa-play"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], ["data-testid", "ps-pipelines-dirty", 1, "ps-tag", "amber"], [1, "pl-stages-body"], [1, "builder"], ["type", "button", "aria-label", "Expand pipelines catalog", "title", "Expand catalog", 1, "pl-rail-collapse", 3, "click"], [1, "fa-solid", "fa-chevron-right"], [1, "pl-collapsed-strip-label"], ["data-testid", "ps-pipelines-catalog", 1, "pl-catalog"], [1, "pl-catalog-header"], [1, "pl-catalog-title-row"], [2, "display", "flex", "gap", "6px", "align-items", "center"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-pipelines-new", 3, "click"], ["type", "button", "title", "Collapse catalog", "aria-label", "Collapse catalog", 1, "pl-collapse-btn", 3, "click"], [1, "fa-solid", "fa-chevron-left"], [1, "pl-search-box"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Filter pipelines...", 3, "input", "value"], [1, "pl-filter-chips"], [1, "pl-filter-chip", 3, "click"], ["data-testid", "ps-pipelines-picker", 1, "pl-picker"], ["data-testid", "ps-pipelines-pill", 1, "pl-pill", "pipeline-card", 3, "on", "title"], ["data-testid", "ps-pipelines-pill", 1, "pl-pill", "pipeline-card", 3, "click", "title"], [1, "pl-card-top"], [1, "pl-card-name"], [1, "ps-badge"], [1, "pl-card-meta"], [1, "pl-entity-tag"], [1, "pl-target-tag"], [1, "pl-card-meta", 2, "justify-content", "space-between", "margin-top", "2px"], [1, "pl-metric-badge"], [1, "fa-solid", "fa-trophy", 2, "color", "#eab308"], [1, "fa-solid", "fa-pen"], [1, "pl-stage-card"], [1, "pl-stage-header"], [1, "pl-stage-title-left"], [1, "pl-stage-num"], [1, "pl-stage-subtitle"], [1, "pl-grid-4"], [1, "pl-metric-box"], [1, "val"], [1, "fa-solid", "fa-table", 2, "color", "var(--mj-brand-primary)"], [1, "sub"], [1, "fa-solid", "fa-bullseye", 2, "color", "var(--mj-status-warning)"], [1, "fa-solid", "fa-chart-pie", 2, "color", "var(--mj-brand-primary)"], [1, "pl-metric-box", 2, "border-left", "3px solid var(--mj-brand-primary)"], [1, "fa-solid", "fa-clock-rotate-left", 2, "color", "var(--mj-brand-primary)"], [2, "display", "flex", "gap", "6px"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-add-source", 3, "click"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-add-step", 3, "click"], [1, "fa-solid", "fa-sliders"], [1, "pl-grid-3"], [1, "pl-feature-group"], [1, "pl-fg-head"], [1, "fa-solid", "fa-database"], [1, "pl-fg-body"], [1, "pl-feature-row"], ["data-testid", "ps-pipelines-step-card", 1, "pl-feature-card"], [1, "fa-solid", "fa-shapes"], [1, "pl-feature-name"], [1, "ps-tag", "primary"], [1, "pl-leakage-box"], [1, "fa-solid", "fa-shield-halved"], [2, "flex", "1"], [2, "font-size", "13px", "font-weight", "700", "color", "var(--mj-text-primary)"], [2, "font-size", "12px", "color", "var(--mj-text-secondary)", "margin-top", "2px"], [2, "margin-top", "6px"], [2, "font-size", "12px", "color", "var(--mj-text-muted)"], [1, "fa-solid", "fa-lock", 2, "color", "var(--mj-status-success)"], [2, "padding", "16px", "text-align", "center", "color", "var(--mj-text-muted)", "font-size", "13px"], [1, "pl-table"], [2, "display", "flex", "gap", "8px", "align-items", "center"], [1, "pl-serving-box"], [2, "color", "var(--mj-text-muted)"], [1, "pl-metric-box", 2, "background", "var(--mj-brand-primary-subtle)", "border-color", "rgba(0,118,182,0.2)"], [1, "val", 2, "color", "var(--mj-brand-primary)"], [1, "ps-tag"], ["data-testid", "ps-pipelines-step-card", 1, "pl-feature-card", 3, "click"], [1, "pl-feature-card-header"], [1, "pl-col-chips"], [1, "pl-col-chip"], [1, "pl-step-inputs"], [1, "fa-solid", "fa-arrow-right-to-bracket"], [1, "pl-deny-chip"], [1, "fa-solid", "fa-flask", 2, "font-size", "24px", "margin-bottom", "8px", "display", "block", "opacity", "0.5"], ["title", "Open model record in Explorer", 1, "pl-model-row", 2, "cursor", "pointer", 3, "winner-row"], ["title", "Open model record in Explorer", 1, "pl-model-row", 2, "cursor", "pointer", 3, "click"], [2, "display", "flex", "align-items", "center", "gap", "6px"], ["href", "javascript:void(0)", 1, "pl-model-link", 3, "click"], [1, "ps-tag", "success"], [2, "text-align", "right"], ["type", "button", "title", "Open model record in Explorer", 1, "pl-action-icon-btn", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "fa-solid", "fa-trophy"], ["mjButton", "", "variant", "secondary", "size", "sm", "type", "button", "title", "Open model record in Explorer", 3, "click"], ["mjButton", "", "variant", "secondary", "size", "sm", "type", "button", "title", "View training runs for this model", 3, "click"], [1, "fa-solid", "fa-flask"], ["direction", "horizontal", "unit", "percent", 1, "builder-splitter", 3, "dragEnd", "gutterSize"], [1, "canvas-wrap"], [1, "canvas-bar"], [1, "canvas-bar-left"], [1, "ps-small", "ps-muted", 2, "font-weight", "600"], ["data-testid", "ps-pipelines-add-source", 1, "ps-pchip", "s", 3, "click"], ["data-testid", "ps-pipelines-add-step", 1, "ps-pchip", "f", 3, "click"], [1, "canvas-bar-right"], [1, "ps-zoom-controls"], ["title", "Zoom Out", 1, "ps-icon-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-minus"], ["title", "Reset to 100%", 1, "ps-zoom-label", 3, "click"], ["title", "Zoom In", 1, "ps-icon-btn", 3, "click", "disabled"], [1, "ps-icon-btn", 3, "click", "title"], [1, "fa-solid", "fa-circle-info"], [1, "ps-small", "ps-muted", "ps-node-count"], [1, "fa-solid", "fa-circle-nodes"], ["data-testid", "ps-pipelines-canvas", 1, "ps-flow", "big"], [1, "ps-graph-viewport"], ["data-testid", "ps-pipelines-edges", 1, "ps-graph-edges"], ["id", "ps-arrow", "viewBox", "0 0 10 10", "refX", "8", "refY", "5", "markerWidth", "6", "markerHeight", "6", "orient", "auto"], ["d", "M 0 1.5 L 8 5 L 0 8.5 z", "fill", "var(--mj-border-strong)"], ["id", "ps-arrow-active", "viewBox", "0 0 10 10", "refX", "8", "refY", "5", "markerWidth", "6", "markerHeight", "6", "orient", "auto"], ["d", "M 0 1.5 L 8 5 L 0 8.5 z", "fill", "var(--mj-brand-primary)"], [1, "ps-edge-path", 3, "active"], ["data-testid", "ps-pipelines-node", 1, "ps-node", 3, "ngClass", "selected", "left", "top", "width"], [1, "flow-toolbar"], [1, "legend"], [1, "sw", "src"], [1, "sw", "feat"], [1, "sw", "emb"], [1, "sw", "target"], [1, "sw", "algo"], [1, "sw", "output"], [1, "ps-edge-path"], ["data-testid", "ps-pipelines-node", 1, "ps-node", 3, "click", "ngClass"], [1, "nh"], [1, "tile", 3, "ngClass"], [1, "ps-node-title", 3, "title"], [1, "nb"], [1, "nb-row"], [1, "port", "in"], [1, "port", "out"], [1, "k"], [1, "v", 3, "title"], ["data-testid", "ps-pipelines-inspector", 1, "ps-col", "inspector"], [1, "ps-card", "insp"], [1, "ihead"], ["data-testid", "ps-pipelines-inspector-title"], [1, "ps-small", "ps-muted"], ["type", "button", "title", "Collapse inspector", "aria-label", "Collapse inspector", 1, "insp-collapse-btn", 3, "click"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-delete"], [1, "ps-card-body"], [1, "ps-card"], [1, "ps-card-head"], [1, "fa-solid", "fa-shield-halved", 2, "color", "var(--mj-status-warning)"], [1, "ps-field"], ["type", "text", 1, "mj-input", 3, "input", "value"], ["type", "number", "step", "0.05", "min", "0", "max", "1", 1, "mj-input", 3, "input", "value"], [1, "mj-input", 3, "change", "value"], ["value", "none"], ["value", "column"], ["value", "offset"], [1, "fa-solid", "fa-scissors", 2, "color", "var(--mj-brand-primary)"], ["value", "train_test_split"], ["value", "kfold"], ["value", "holdout"], ["type", "number", "step", "0.05", 1, "mj-input", 3, "input", "value"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-pipelines-delete", 3, "click"], [1, "fa-solid", "fa-trash"], [3, "value"], ["type", "number", 1, "mj-input", 3, "input", "value"], ["rows", "4", 1, "mj-textarea", 3, "input", "value"], [2, "margin-top", "12px", "padding", "10px", "border-radius", "var(--mj-radius-sm)", "background", "var(--mj-bg-surface-sunken)", "border", "1px solid var(--mj-border-default)"], [1, "ps-small", "ps-muted", 2, "margin-bottom", "6px"], [2, "display", "flex", "justify-content", "space-between", "align-items", "center", "margin-bottom", "8px"], [2, "font-size", "var(--mj-text-xs)", "font-weight", "600"], ["mjButton", "", "variant", "secondary", "size", "sm", "type", "button", "title", "Open model record in Explorer", 2, "flex", "1", "justify-content", "center", 3, "click"], ["mjButton", "", "variant", "secondary", "size", "sm", "type", "button", "title", "View training runs", 2, "flex", "1", "justify-content", "center", 3, "click"], [2, "display", "flex", "flex-direction", "column", "gap", "12px"], [2, "border", "1px solid var(--mj-border-default)", "border-radius", "var(--mj-radius-md)", "padding", "12px", "background", "var(--mj-bg-surface-sunken)", "display", "flex", "flex-direction", "column", "gap", "8px"], [2, "display", "flex", "justify-content", "space-between", "align-items", "center"], [1, "ps-mono", "ps-small", "ps-muted", 3, "title"], [2, "font-size", "var(--mj-text-xs)", "color", "var(--mj-text-secondary)"], [2, "display", "flex", "flex-direction", "column", "gap", "6px", "margin-top", "4px"], ["mjButton", "", "variant", "primary", "size", "sm", "type", "button", 2, "width", "100%", "justify-content", "center", 3, "click"], ["mjButton", "", "variant", "secondary", "size", "sm", "type", "button", 2, "width", "100%", "justify-content", "center", 3, "click"], [3, "saved", "trainRequested", "closed", "engine", "provider", "currentUser", "cloneFrom", "initialAlgorithmId"]], template: function PSPipelinesComponent_Template(rf, ctx) { if (rf & 1) {
1366
2589
  i0.ɵɵelementStart(0, "div", 0);
1367
- i0.ɵɵconditionalCreate(1, PSPipelinesComponent_Conditional_1_Template, 10, 0, "div", 1)(2, PSPipelinesComponent_Conditional_2_Template, 126, 32);
2590
+ i0.ɵɵconditionalCreate(1, PSPipelinesComponent_Conditional_1_Template, 14, 0, "div", 1)(2, PSPipelinesComponent_Conditional_2_Template, 43, 24, "div", 2);
2591
+ i0.ɵɵconditionalCreate(3, PSPipelinesComponent_Conditional_3_Template, 1, 5, "ps-pipeline-wizard", 3);
1368
2592
  i0.ɵɵelementEnd();
1369
2593
  } if (rf & 2) {
1370
2594
  i0.ɵɵadvance();
1371
2595
  i0.ɵɵconditional(ctx.pipelines.length === 0 ? 1 : 2);
1372
- } }, dependencies: [CommonModule, i1.NgClass, 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", "/* Training Pipelines \u2014 real visual DAG builder driven by each pipeline's persisted spec. */\n\n.ps-pipelines { display: flex; flex-direction: column; gap: 14px; height: 100%; min-height: 0; }\n\n/* Pipeline picker bar */\n.ps-pipelines .pl-picker { display: flex; align-items: center; gap: 8px; overflow-x: auto; padding-bottom: 6px; }\n.ps-pipelines .pl-toolbar { display: flex; align-items: center; gap: 8px; margin: 8px 0 12px; padding-bottom: 12px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .pl-pill {\n display: inline-flex; align-items: center; gap: 8px; cursor: pointer; flex: 0 0 auto; max-width: 260px;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary); border-radius: 999px; padding: 6px 12px; font-size: 13px;\n}\n.ps-pipelines .pl-pill .pl-pill-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-pipelines .pl-pill:hover { background: var(--mj-bg-surface-hover); }\n.ps-pipelines .pl-pill.on {\n border-color: var(--mj-brand-primary); color: var(--mj-text-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n/* The builder fills the remaining panel height; each column scrolls on its own. */\n.ps-pipelines .builder {\n flex: 1; min-height: 0;\n display: grid; grid-template-columns: 1fr 320px; grid-template-rows: minmax(0, 1fr); gap: 16px;\n}\n@media (max-width: 1080px) {\n .ps-pipelines .builder { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1.4fr) minmax(0, 1fr); }\n}\n\n.ps-pipelines .canvas-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }\n.ps-pipelines .canvas-bar {\n display: flex; align-items: center; gap: 10px; flex-wrap: wrap;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-bottom: none; border-radius: 10px 10px 0 0; padding: 12px 14px;\n}\n/* The DAG canvas is sized in px by the layered layout (inline width/height) and\n may exceed the column, so it scrolls internally (both axes) while the column\n itself stays put. flex:1 makes it absorb the remaining column height. */\n.ps-pipelines .ps-flow.big {\n border-radius: 0;\n flex: 1 1 auto;\n min-height: 0;\n max-width: 100%;\n overflow: auto;\n}\n/* Pin every node to a fixed height so its port (top:50%) lines up exactly with\n the SVG edge anchor (node.y + NODE_H/2). NODE_H in the component = 86. */\n.ps-pipelines .ps-flow.big .ps-node {\n height: 86px;\n display: flex;\n flex-direction: column;\n}\n.ps-pipelines .ps-flow.big .ps-node .nb { flex: 1; min-height: 0; }\n.ps-pipelines .flow-toolbar {\n display: flex; align-items: center; gap: 8px;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-top: none; border-radius: 0 0 10px 10px; padding: 10px 14px;\n}\n.ps-pipelines .legend { display: flex; align-items: center; gap: 14px; font-size: 12px; color: var(--mj-text-secondary); flex-wrap: wrap; }\n.ps-pipelines .legend span { display: inline-flex; align-items: center; gap: 6px; }\n.ps-pipelines .legend .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-pipelines .legend .sw.src { background: var(--ps-node-source); }\n.ps-pipelines .legend .sw.feat { background: var(--ps-node-feature); }\n.ps-pipelines .legend .sw.emb { background: var(--ps-node-embedding); }\n.ps-pipelines .legend .sw.target { background: var(--ps-node-target); }\n.ps-pipelines .legend .sw.algo { background: var(--ps-node-algorithm); }\n\n/* inspector \u2014 its own scroll column, independent of the canvas. NOTE: .inspector is\n also a .ps-col (display:flex column), so its card children ARE flex items \u2014 without\n flex-shrink:0 they compress to fit (scrollHeight == clientHeight, verified live) and\n the column never scrolls. These child rules are load-bearing, not decorative. */\n.ps-pipelines .inspector {\n min-height: 0;\n overflow-y: auto;\n}\n.ps-pipelines .inspector > * { flex: 0 0 auto; }\n.ps-pipelines .inspector .insp { position: static; }\n.ps-pipelines .ihead { display: flex; align-items: center; gap: 11px; padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .ihead h3 { font-size: 14px; color: var(--mj-text-primary); margin: 0; }\n.ps-pipelines .ihead .tile {\n width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto; background: var(--ps-node-feature);\n}\n.ps-pipelines .ihead .tile[data-type='src'] { background: var(--ps-node-source); }\n.ps-pipelines .ihead .tile[data-type='emb'] { background: var(--ps-node-embedding); }\n.ps-pipelines .ihead .tile[data-type='target'] { background: var(--ps-node-target); }\n.ps-pipelines .ihead .tile[data-type='algo'] { background: var(--ps-node-algorithm); }\n.ps-pipelines .ihead .tile[data-type='output'] { background: var(--mj-status-success); }\n\n/* Palette chips are interactive buttons in the editor */\n.ps-pipelines .canvas-bar .ps-pchip { cursor: pointer; border: none; }\n.ps-pipelines .canvas-bar .ps-pchip:hover { filter: brightness(1.08); }\n\n/* Inspector edit fields */\n.ps-pipelines .ps-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }\n.ps-pipelines .ps-field > label { font-size: 12px; font-weight: 650; color: var(--mj-text-muted); }\n.ps-pipelines .inputs-list { display: flex; flex-direction: column; gap: 4px; }\n.ps-pipelines .inp-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--mj-text-secondary); cursor: pointer; }\n\n/* Empty-state CTA spacing */\n.ps-pipelines .ps-empty button { margin-top: 16px; }\n"], encapsulation: 2 });
2596
+ i0.ɵɵadvance(2);
2597
+ i0.ɵɵconditional(ctx.showWizard ? 3 : -1);
2598
+ } }, dependencies: [CommonModule, i1.NgClass, MJButtonDirective, PSPipelineWizardComponent, AngularSplitModule, i2.SplitComponent, i2.SplitAreaComponent, i1.DatePipe], 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", "/* Training Pipelines \u2014 Human-Centric Master-Detail Redesign with Progressive Stages & DAG toggle */\n\n.ps-pipelines {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n width: 100%;\n overflow: hidden;\n}\n\n/* Master-Detail Layout with Resizable Splitter */\n.ps-pipelines .pl-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n background: var(--mj-bg-page);\n position: relative;\n}\n\n.ps-pipelines .pl-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.ps-pipelines .pl-collapsed-strip {\n width: 44px;\n flex: 0 0 44px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n z-index: 10;\n}\n\n.ps-pipelines .pl-rail-collapse {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-sm);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-rail-collapse:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.ps-pipelines .pl-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.ps-pipelines .pl-collapse-btn {\n background: transparent;\n border: 1px solid transparent;\n color: var(--mj-text-muted);\n border-radius: var(--mj-radius-sm);\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n.ps-pipelines .pl-model-row {\n cursor: pointer;\n transition: background 0.12s ease;\n}\n\n.ps-pipelines .pl-model-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.ps-pipelines .pl-model-link {\n color: var(--mj-brand-primary);\n font-weight: 600;\n text-decoration: none;\n}\n\n.ps-pipelines .pl-model-link:hover {\n text-decoration: underline;\n}\n\n.ps-pipelines .pl-action-icon-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n width: 28px;\n height: 28px;\n border-radius: var(--mj-radius-sm);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: all 0.12s ease;\n}\n\n.ps-pipelines .pl-action-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-brand-primary);\n}\n\n/* Left Column: Pipeline Catalog */\n.ps-pipelines .pl-catalog {\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.ps-pipelines .pl-catalog-header {\n padding: 14px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n gap: 10px;\n flex-shrink: 0;\n}\n\n.ps-pipelines .pl-catalog-title-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.ps-pipelines .pl-catalog-title-row h2 {\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-search-box {\n position: relative;\n width: 100%;\n}\n\n.ps-pipelines .pl-search-box input {\n width: 100%;\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n padding: 7px 10px 7px 30px;\n font-size: 12px;\n color: var(--mj-text-primary);\n outline: none;\n}\n\n.ps-pipelines .pl-search-box i {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.ps-pipelines .pl-filter-chips {\n display: flex;\n gap: 6px;\n overflow-x: auto;\n padding-bottom: 2px;\n}\n\n.ps-pipelines .pl-filter-chip {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n padding: 3px 8px;\n border-radius: 9999px;\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.ps-pipelines .pl-filter-chip.active {\n background: var(--mj-bg-surface-subtle);\n border-color: var(--mj-text-secondary);\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* Catalog Pipeline Cards List (data-testid=\"ps-pipelines-picker\") */\n.ps-pipelines .pl-picker {\n flex: 1;\n overflow-y: auto;\n padding: 10px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Individual Card (data-testid=\"ps-pipelines-pill\") */\n.ps-pipelines .pl-pill.pipeline-card {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 6px;\n width: 100%;\n max-width: none;\n border-radius: 8px;\n padding: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n text-align: left;\n}\n\n.ps-pipelines .pl-pill.pipeline-card:hover {\n border-color: var(--mj-border-subtle);\n background: var(--mj-bg-surface-hover);\n}\n\n.ps-pipelines .pl-pill.pipeline-card.on {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.ps-pipelines .pl-card-top {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n}\n\n.ps-pipelines .pl-card-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: normal;\n}\n\n.ps-pipelines .pl-card-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-entity-tag {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-brand-primary);\n background: var(--mj-brand-primary-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 500;\n}\n\n.ps-pipelines .pl-target-tag {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-metric-badge {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n/* Right Detail Workspace */\n.ps-pipelines .pl-workspace {\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n display: flex;\n flex-direction: column;\n}\n\n.ps-pipelines .pl-workspace-header {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 16px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n position: sticky;\n top: 0;\n z-index: 10;\n flex-shrink: 0;\n}\n\n.ps-pipelines .pl-workspace-title-area {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ps-pipelines .pl-breadcrumbs {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-breadcrumbs span {\n color: var(--mj-text-secondary);\n}\n\n.ps-pipelines .pl-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .pl-title-row h1 {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-toolbar {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 0;\n border: none;\n}\n\n/* Workspace Stages Body */\n.ps-pipelines .pl-stages-body {\n padding: 20px 24px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n max-width: 1400px;\n}\n\n.ps-pipelines .pl-stage-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 18px 20px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.ps-pipelines .pl-stage-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.ps-pipelines .pl-stage-title-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .pl-stage-num {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: var(--mj-brand-primary-subtle);\n color: var(--mj-brand-primary);\n display: grid;\n place-items: center;\n font-size: 12px;\n font-weight: 700;\n}\n\n.ps-pipelines .pl-stage-title-left h3 {\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-stage-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n/* Stage Grids */\n.ps-pipelines .pl-grid-4 {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 12px;\n}\n\n.ps-pipelines .pl-grid-3 {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n@media (max-width: 1200px) {\n .ps-pipelines .pl-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .ps-pipelines .pl-grid-3 { grid-template-columns: 1fr; }\n}\n\n.ps-pipelines .pl-metric-box {\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ps-pipelines .pl-metric-box label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-metric-box .val {\n font-size: 13.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .pl-metric-box .sub {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n/* Feature Group Cards */\n.ps-pipelines .pl-feature-group {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.ps-pipelines .pl-fg-head {\n padding: 10px 12px;\n background: var(--mj-bg-surface-subtle);\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12.5px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-fg-body {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.ps-pipelines .pl-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n padding: 5px 8px;\n background: var(--mj-bg-surface-subtle);\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-feature-name {\n font-family: monospace;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-feature-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n padding: 6px 8px;\n background: var(--mj-bg-surface-subtle);\n border-radius: 4px;\n border: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-feature-card:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-default);\n}\n\n.ps-pipelines .pl-feature-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n}\n\n.ps-pipelines .pl-col-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 2px;\n}\n\n.ps-pipelines .pl-col-chip {\n font-family: monospace;\n font-size: 11px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n padding: 1px 6px;\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-step-inputs {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 4px;\n margin-top: 2px;\n}\n\n/* Leakage Guard Box */\n.ps-pipelines .pl-leakage-box {\n background: var(--mj-status-warning-subtle);\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, transparent);\n border-radius: 8px;\n padding: 12px 14px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.ps-pipelines .pl-leakage-box i {\n color: var(--mj-status-warning);\n font-size: 18px;\n margin-top: 2px;\n}\n\n.ps-pipelines .pl-deny-chip {\n font-family: monospace;\n font-size: 11px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, transparent);\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n padding: 2px 6px;\n border-radius: 4px;\n text-decoration: line-through;\n display: inline-block;\n margin-right: 4px;\n margin-top: 4px;\n}\n\n/* Leaderboard Table */\n.ps-pipelines .pl-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n\n.ps-pipelines .pl-table th {\n text-align: left;\n padding: 8px 12px;\n background: var(--mj-bg-surface-subtle);\n color: var(--mj-text-muted);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.ps-pipelines .pl-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-table tr.winner-row {\n background: var(--mj-brand-primary-subtle);\n font-weight: 600;\n}\n\n/* Serving Box */\n.ps-pipelines .pl-serving-box {\n display: grid;\n grid-template-columns: 2fr 1fr;\n gap: 12px;\n}\n\n@media (max-width: 900px) {\n .ps-pipelines .pl-serving-box { grid-template-columns: 1fr; }\n}\n\n/* Pipeline Graph view & inspector container when isGraphView is active */\n.ps-pipelines .builder {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 16px;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.ps-pipelines .builder-splitter {\n width: 100%;\n height: 100%;\n min-height: 0;\n background: transparent;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter {\n background: transparent;\n transition: background-color 0.15s ease;\n position: relative;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter:hover {\n background-color: var(--mj-brand-primary, #6366f1);\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 24px;\n border-radius: 1px;\n background: var(--mj-border-strong, #cbd5e1);\n transition: background-color 0.15s ease;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter:hover::after {\n background: var(--mj-bg-surface);\n}\n\n.ps-pipelines .insp-collapse-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 6px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0;\n}\n\n.ps-pipelines .insp-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-pipelines .canvas-wrap {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n overflow: hidden;\n}\n\n.ps-pipelines .canvas-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 16px;\n z-index: 5;\n}\n\n.ps-pipelines .canvas-bar-left,\n.ps-pipelines .canvas-bar-right {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .ps-zoom-controls {\n display: flex;\n align-items: center;\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.ps-pipelines .ps-icon-btn {\n background: none;\n border: none;\n padding: 5px 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: grid;\n place-items: center;\n font-size: 11px;\n}\n\n.ps-pipelines .ps-icon-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .ps-icon-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.ps-pipelines .ps-zoom-label {\n font-size: 11px;\n font-weight: 600;\n font-family: monospace;\n padding: 0 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n user-select: none;\n}\n\n.ps-pipelines .ps-node-count {\n font-size: 12px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .ps-flow.big {\n border: none;\n border-radius: 0;\n flex: 1 1 auto;\n min-height: 0;\n max-width: 100%;\n overflow: auto;\n position: relative;\n background:\n radial-gradient(var(--mj-border-default) 1px, transparent 1px) 0 0 / 24px 24px,\n var(--mj-bg-surface-card);\n}\n\n.ps-pipelines .ps-graph-viewport {\n position: relative;\n transform-origin: 0 0;\n transition: transform 0.15s ease-out;\n}\n\n.ps-pipelines .ps-graph-edges {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n overflow: visible;\n}\n\n.ps-pipelines .ps-edge-path {\n stroke: var(--mj-border-strong);\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.ps-pipelines .ps-edge-path.active {\n stroke: var(--mj-brand-primary);\n stroke-width: 2.5;\n}\n\n.ps-pipelines .ps-node {\n position: absolute;\n width: 230px;\n height: 96px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-left-width: 4px;\n border-radius: 8px;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);\n transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;\n display: flex;\n flex-direction: column;\n}\n\n.ps-pipelines .ps-node:hover {\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);\n transform: translateY(-1px);\n}\n\n.ps-pipelines .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) 20%, transparent), 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.ps-pipelines .ps-node .nh {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 600;\n font-size: 12px;\n color: var(--mj-text-primary);\n min-height: 36px;\n}\n\n.ps-pipelines .ps-node .nh i.tile {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 11px;\n flex: 0 0 auto;\n}\n\n.ps-pipelines .ps-node-title {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ps-pipelines .ps-node .nh .ps-tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 5px;\n border-radius: 4px;\n background: var(--mj-bg-surface-subtle);\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.ps-pipelines .ps-node .nb {\n padding: 6px 10px;\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 3px;\n font-size: 11px;\n}\n\n.ps-pipelines .ps-node .nb-row {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n}\n\n.ps-pipelines .ps-node .nb-row .k {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.ps-pipelines .ps-node .nb-row .v {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-family: monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Ports */\n.ps-pipelines .ps-node .port {\n position: absolute;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-border-strong);\n top: 50%;\n transform: translateY(-50%);\n z-index: 2;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .ps-node .port.in { left: -6px; }\n.ps-pipelines .ps-node .port.out { right: -6px; }\n\n.ps-pipelines .ps-node.selected .port {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n}\n\n/* Node Category Accents \u2014 fallback hex values are the intended definitions; hosts may override them externally without a :root block here */\n.ps-pipelines .ps-node.src { border-left-color: var(--ps-node-src, #0284c7); }\n.ps-pipelines .ps-node.src .nh i.tile { background: var(--ps-node-src, #0284c7); }\n\n.ps-pipelines .ps-node.feat { border-left-color: var(--ps-node-feat, #7c3aed); }\n.ps-pipelines .ps-node.feat .nh i.tile { background: var(--ps-node-feat, #7c3aed); }\n\n.ps-pipelines .ps-node.emb { border-left-color: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .ps-node.emb .nh i.tile { background: var(--ps-node-emb, #4f46e5); }\n\n.ps-pipelines .ps-node.target { border-left-color: var(--ps-node-target, #059669); }\n.ps-pipelines .ps-node.target .nh i.tile { background: var(--ps-node-target, #059669); }\n\n.ps-pipelines .ps-node.algo { border-left-color: var(--ps-node-algo, #d97706); }\n.ps-pipelines .ps-node.algo .nh i.tile { background: var(--ps-node-algo, #d97706); }\n\n.ps-pipelines .ps-node.output { border-left-color: var(--ps-node-output, #16a34a); }\n.ps-pipelines .ps-node.output .nh i.tile { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .flow-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n padding: 8px 16px;\n z-index: 5;\n}\n\n.ps-pipelines .legend {\n display: flex;\n align-items: center;\n gap: 16px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n\n.ps-pipelines .legend span {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .legend .sw {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n display: inline-block;\n}\n\n.ps-pipelines .legend .sw.src { background: var(--ps-node-src, #0284c7); }\n.ps-pipelines .legend .sw.feat { background: var(--ps-node-feat, #7c3aed); }\n.ps-pipelines .legend .sw.emb { background: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .legend .sw.target { background: var(--ps-node-target, #059669); }\n.ps-pipelines .legend .sw.algo { background: var(--ps-node-algo, #d97706); }\n.ps-pipelines .legend .sw.output { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .inspector {\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n.ps-pipelines .inspector > * { flex: 0 0 auto; }\n.ps-pipelines .inspector .insp { position: static; border: none; background: transparent; }\n.ps-pipelines .ihead { display: flex; align-items: center; gap: 11px; padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .ihead h3 { font-size: 14px; color: var(--mj-text-primary); margin: 0; }\n.ps-pipelines .ihead .tile {\n width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto; background: var(--ps-node-feat, #7c3aed);\n}\n.ps-pipelines .ihead .tile[data-type='src'] { background: var(--ps-node-src, #0284c7); }\n.ps-pipelines .ihead .tile[data-type='emb'] { background: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .ihead .tile[data-type='target'] { background: var(--ps-node-target, #059669); }\n.ps-pipelines .ihead .tile[data-type='algo'] { background: var(--ps-node-algo, #d97706); }\n.ps-pipelines .ihead .tile[data-type='output'] { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .canvas-bar .ps-pchip { cursor: pointer; border: none; }\n.ps-pipelines .canvas-bar .ps-pchip:hover { filter: brightness(1.08); }\n.ps-pipelines .ps-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }\n.ps-pipelines .ps-field > label { font-size: 12px; font-weight: 650; color: var(--mj-text-muted); }\n.ps-pipelines .inputs-list { display: flex; flex-direction: column; gap: 4px; }\n.ps-pipelines .inp-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--mj-text-secondary); cursor: pointer; }\n.ps-pipelines .ps-empty button { margin-top: 16px; }\n"], encapsulation: 2 });
1373
2599
  }
1374
2600
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSPipelinesComponent, [{
1375
2601
  type: Component,
1376
- args: [{ standalone: true, selector: 'ps-pipelines', imports: [CommonModule, MJButtonDirective], encapsulation: ViewEncapsulation.None, template: `
2602
+ args: [{ standalone: true, selector: 'ps-pipelines', imports: [CommonModule, MJButtonDirective, PSPipelineWizardComponent, AngularSplitModule], encapsulation: ViewEncapsulation.None, template: `
1377
2603
  <div class="ps-panel ps-pipelines" data-testid="ps-pipelines-panel">
1378
2604
  @if (pipelines.length === 0) {
1379
2605
  <div class="ps-empty" data-testid="ps-pipelines-empty">
@@ -1381,216 +2607,699 @@ export class PSPipelinesComponent {
1381
2607
  <h3>No training pipelines yet</h3>
1382
2608
  <p>
1383
2609
  A training pipeline assembles features from your data, picks an algorithm, and trains a
1384
- versioned model. The fastest way to build one is to describe your goal to the Model
1385
- Development Agent — it designs the pipeline, guards against target leakage, and trains for you.
2610
+ versioned model. You can build one step-by-step with the manual wizard or ask the Model
2611
+ Development Agent to design one for you.
1386
2612
  </p>
1387
- <button mjButton variant="primary" size="sm" data-testid="ps-pipelines-ask-agent"
1388
- (click)="askAgent.emit(starterPrompt)">
1389
- <i class="fa-solid fa-robot"></i> Ask the agent to build one
1390
- </button>
1391
- </div>
1392
- } @else {
1393
- <!-- Pipeline picker (its own scrollable row) -->
1394
- <div class="pl-picker" data-testid="ps-pipelines-picker">
1395
- @for (p of pipelines; track p.ID) {
1396
- <button class="pl-pill" [class.on]="isSelectedPipeline(p)"
1397
- data-testid="ps-pipelines-pill" (click)="selectPipeline(p.ID)" [title]="p.Name">
1398
- <i class="fa-solid fa-diagram-project"></i> <span class="pl-pill-name">{{ p.Name }}</span>
1399
- <span class="ps-badge" [class]="statusClass(p.Status)">{{ p.Status }}</span>
2613
+ <div class="empty-actions" style="display: flex; gap: 8px; margin-top: 12px;">
2614
+ <button mjButton variant="primary" size="sm" data-testid="ps-pipelines-new-btn-empty"
2615
+ (click)="openWizard()">
2616
+ <i class="fa-solid fa-plus"></i> New Pipeline
1400
2617
  </button>
1401
- }
1402
- </div>
1403
-
1404
- <!-- Toolbar for the selected pipeline (its own row) -->
1405
- <div class="pl-toolbar">
1406
- <button mjButton variant="primary" size="sm" data-testid="ps-pipelines-save" [disabled]="!dirty || busy" (click)="save()">
1407
- <i class="fa-solid fa-floppy-disk"></i> Save
1408
- </button>
1409
- <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-validate" [disabled]="busy" (click)="validate()">
1410
- <i class="fa-solid fa-circle-check"></i> Validate
1411
- </button>
1412
- <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-train" [disabled]="busy || dirty" (click)="train()">
1413
- <i class="fa-solid fa-play"></i> Train
1414
- </button>
1415
- <button mjButton variant="secondary" size="sm" (click)="refine()">
1416
- <i class="fa-solid fa-wand-magic-sparkles"></i> Agent
1417
- </button>
1418
- <span class="ps-spacer"></span>
1419
- @if (dirty) { <span class="ps-tag amber" data-testid="ps-pipelines-dirty"><i class="fa-solid fa-pen"></i> Unsaved</span> }
2618
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-ask-agent"
2619
+ (click)="askAgent.emit(starterPrompt)">
2620
+ <i class="fa-solid fa-robot"></i> Ask the agent
2621
+ </button>
2622
+ </div>
1420
2623
  </div>
1421
-
1422
- <div class="builder">
1423
- <!-- Canvas -->
1424
- <div class="canvas-wrap">
1425
- <div class="canvas-bar">
1426
- <span class="ps-small ps-muted" style="font-weight:600">Add:</span>
1427
- <button class="ps-pchip s" data-testid="ps-pipelines-add-source" (click)="addSource()"><i class="fa-solid fa-database"></i> Source</button>
1428
- <button class="ps-pchip f" data-testid="ps-pipelines-add-step" (click)="addStep()"><i class="fa-solid fa-sliders"></i> Feature step</button>
1429
- <span class="ps-spacer"></span>
1430
- <span class="ps-small ps-muted"><i class="fa-solid fa-circle-nodes"></i> {{ nodes.length }} nodes · {{ edges.length }} edges</span>
2624
+ } @else {
2625
+ <div class="pl-layout" [class.catalog-collapsed]="isCatalogCollapsed">
2626
+ @if (isCatalogCollapsed) {
2627
+ <div class="pl-collapsed-strip" role="region" aria-label="Pipelines catalog (collapsed)">
2628
+ <button class="pl-rail-collapse" type="button" (click)="toggleCatalog()" aria-label="Expand pipelines catalog" title="Expand catalog">
2629
+ <i class="fa-solid fa-chevron-right"></i>
2630
+ </button>
2631
+ <div class="pl-collapsed-strip-label"><i class="fa-solid fa-diagram-project"></i></div>
1431
2632
  </div>
2633
+ }
1432
2634
 
1433
- <div class="ps-flow big" data-testid="ps-pipelines-canvas"
1434
- [style.width.px]="canvasW" [style.height.px]="canvasH">
1435
- <svg class="ps-edges" data-testid="ps-pipelines-edges"
1436
- [attr.width]="canvasW" [attr.height]="canvasH"
1437
- [attr.viewBox]="'0 0 ' + canvasW + ' ' + canvasH">
1438
- @for (edge of edges; track edge.from + edge.to) {
1439
- <path [attr.d]="edgePath(edge)"></path>
1440
- }
1441
- </svg>
1442
- @for (node of nodes; track node.id) {
1443
- <div class="ps-node" data-testid="ps-pipelines-node" [attr.data-node-id]="node.id"
1444
- [ngClass]="node.type" [class.selected]="node.id === selectedId"
1445
- [style.left.px]="node.x" [style.top.px]="node.y" (click)="selectNode(node.id)">
1446
- <div class="nh">
1447
- <i class="tile" [ngClass]="node.icon"></i>
1448
- <span>{{ node.title }}</span>
1449
- @if (node.tag) { <span class="ps-tag">{{ node.tag }}</span> }
2635
+ <as-split direction="horizontal" class="pl-splitter" unit="percent" [gutterSize]="6" (dragEnd)="onSplitDragEnd($event.sizes)">
2636
+ @if (!isCatalogCollapsed) {
2637
+ <as-split-area [size]="catalogSizePct" [minSize]="18" [maxSize]="50">
2638
+ <!-- Left Column: Master Pipeline Catalog -->
2639
+ <div class="pl-catalog" data-testid="ps-pipelines-catalog">
2640
+ <div class="pl-catalog-header">
2641
+ <div class="pl-catalog-title-row">
2642
+ <h2>Pipelines ({{ filteredPipelines.length }})</h2>
2643
+ <div style="display:flex;gap:6px;align-items:center">
2644
+ <button mjButton variant="primary" size="sm" data-testid="ps-pipelines-new" (click)="openWizard()">
2645
+ <i class="fa-solid fa-plus"></i> New
2646
+ </button>
2647
+ <button class="pl-collapse-btn" type="button" (click)="toggleCatalog()" title="Collapse catalog" aria-label="Collapse catalog">
2648
+ <i class="fa-solid fa-chevron-left"></i>
2649
+ </button>
2650
+ </div>
2651
+ </div>
2652
+ <div class="pl-search-box">
2653
+ <i class="fa-solid fa-magnifying-glass"></i>
2654
+ <input type="text" [value]="searchQuery" (input)="onSearchInput($event)" placeholder="Filter pipelines..." />
2655
+ </div>
2656
+ <div class="pl-filter-chips">
2657
+ <button class="pl-filter-chip" [class.active]="activeFilter === 'all'" (click)="setFilter('all')">All</button>
2658
+ <button class="pl-filter-chip" [class.active]="activeFilter === 'published'" (click)="setFilter('published')">Published</button>
2659
+ <button class="pl-filter-chip" [class.active]="activeFilter === 'draft'" (click)="setFilter('draft')">Draft</button>
2660
+ <button class="pl-filter-chip" [class.active]="activeFilter === 'core'" (click)="setFilter('core')">MJ Core</button>
2661
+ </div>
1450
2662
  </div>
1451
- <div class="nb">
1452
- @for (row of node.rows; track row.k) {
1453
- <div class="nb-row"><span class="k">{{ row.k }}</span><span>{{ row.v }}</span></div>
2663
+
2664
+ <!-- Scrollable Catalog Cards List (preserves data-testid="ps-pipelines-picker" and "ps-pipelines-pill") -->
2665
+ <div class="pl-picker" data-testid="ps-pipelines-picker">
2666
+ @for (p of filteredPipelines; track p.ID) {
2667
+ <button class="pl-pill pipeline-card" [class.on]="isSelectedPipeline(p)"
2668
+ data-testid="ps-pipelines-pill" (click)="selectPipeline(p.ID)" [title]="p.Name">
2669
+ <div class="pl-card-top">
2670
+ <div class="pl-card-name">{{ p.Name }}</div>
2671
+ <span class="ps-badge" [class]="statusClass(p.Status)">{{ p.Status }}</span>
2672
+ </div>
2673
+ <div class="pl-card-meta">
2674
+ <span class="pl-entity-tag">{{ targetEntityName(p) }}</span>
2675
+ <span>&bull;</span>
2676
+ <span class="pl-target-tag">{{ p.TargetVariable || 'Target' }}</span>
2677
+ </div>
2678
+ <div class="pl-card-meta" style="justify-content: space-between; margin-top: 2px;">
2679
+ <span>{{ p.ProblemType }}</span>
2680
+ @if (bestMetricForPipeline(p); as bm) {
2681
+ <span class="pl-metric-badge"><i class="fa-solid fa-trophy" style="color:#eab308"></i> {{ bm }}</span>
2682
+ }
2683
+ </div>
2684
+ </button>
1454
2685
  }
1455
2686
  </div>
1456
- @if (node.hasIn) { <span class="port in"></span> }
1457
- @if (node.hasOut) { <span class="port out"></span> }
1458
2687
  </div>
1459
- }
1460
- </div>
2688
+ </as-split-area>
2689
+ }
2690
+
2691
+ <as-split-area [size]="isCatalogCollapsed ? 100 : workspaceSizePct" [minSize]="40">
2692
+ <!-- Right Column: Human-Centric Workspace (Stages View Default + DAG View Toggle) -->
2693
+ <div class="pl-workspace" data-testid="ps-pipelines-workspace">
2694
+ <div class="pl-workspace-header">
2695
+ <div class="pl-workspace-title-area">
2696
+ <div class="pl-breadcrumbs">
2697
+ <span>Pipelines</span> <i class="fa-solid fa-chevron-right" style="font-size: 10px;"></i>
2698
+ <span>{{ pipelineDomain(selectedPipeline) }}</span> <i class="fa-solid fa-chevron-right" style="font-size: 10px;"></i>
2699
+ <strong>{{ selectedPipeline?.Name }}</strong>
2700
+ </div>
2701
+ <div class="pl-title-row">
2702
+ <h1>{{ selectedPipeline?.Name }}</h1>
2703
+ @if (selectedPipeline) {
2704
+ <span class="ps-badge" [class]="statusClass(selectedPipeline.Status)">{{ selectedPipeline.Status }}</span>
2705
+ @if (bestMetricForPipeline(selectedPipeline); as bm) {
2706
+ <span class="pl-metric-badge"><i class="fa-solid fa-trophy" style="color:#eab308"></i> {{ bm }}</span>
2707
+ }
2708
+ }
2709
+ </div>
2710
+ </div>
1461
2711
 
1462
- <div class="flow-toolbar">
1463
- <div class="legend">
1464
- <span><i class="sw src"></i> Source</span>
1465
- <span><i class="sw feat"></i> Feature</span>
1466
- <span><i class="sw emb"></i> Embedding</span>
1467
- <span><i class="sw target"></i> Target</span>
1468
- <span><i class="sw algo"></i> Algorithm</span>
2712
+ <!-- Action Toolbar -->
2713
+ <div class="pl-toolbar">
2714
+ <button mjButton variant="secondary" size="sm" (click)="toggleViewMode()" [title]="isGraphView ? 'Switch to 5-Stage Progressive Flow' : 'Switch to Pipeline Graph View'">
2715
+ <i class="fa-solid" [class.fa-diagram-project]="!isGraphView" [class.fa-layer-group]="isGraphView"></i>
2716
+ {{ isGraphView ? 'Stages View' : 'Graph View' }}
2717
+ </button>
2718
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-clone" [disabled]="!selectedPipeline" (click)="cloneSelected()">
2719
+ <i class="fa-solid fa-copy"></i> Clone
2720
+ </button>
2721
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-save" [disabled]="!dirty || busy" (click)="save()">
2722
+ <i class="fa-solid fa-floppy-disk"></i> Save
2723
+ </button>
2724
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-validate" [disabled]="busy" (click)="validate()">
2725
+ <i class="fa-solid fa-circle-check"></i> Validate
2726
+ </button>
2727
+ <button mjButton variant="primary" size="sm" data-testid="ps-pipelines-train" [disabled]="busy || dirty" (click)="train()">
2728
+ <i class="fa-solid fa-play"></i> Train
2729
+ </button>
2730
+ <button mjButton variant="secondary" size="sm" (click)="refine()">
2731
+ <i class="fa-solid fa-wand-magic-sparkles"></i> Agent
2732
+ </button>
2733
+ @if (dirty) { <span class="ps-tag amber" data-testid="ps-pipelines-dirty"><i class="fa-solid fa-pen"></i> Unsaved</span> }
1469
2734
  </div>
1470
2735
  </div>
1471
- </div>
1472
2736
 
1473
- <!-- Inspector (editable) -->
1474
- <div class="ps-col inspector" data-testid="ps-pipelines-inspector">
1475
- <div class="ps-card insp">
1476
- <div class="ihead">
1477
- <i class="tile" [ngClass]="selectedNode.icon" [attr.data-type]="selectedNode.type"></i>
1478
- <div style="flex:1">
1479
- <h3 data-testid="ps-pipelines-inspector-title">{{ selectedNode.title }}</h3>
1480
- <div class="ps-small ps-muted">{{ nodeTypeLabel(selectedNode.type) }} · selected</div>
2737
+ <!-- Stages View (Human-Readable Progressive Stages) -->
2738
+ @if (viewMode === 'stages') {
2739
+ <div class="pl-stages-body">
2740
+ <!-- STAGE 1: Target Definition & Decision Boundary -->
2741
+ <div class="pl-stage-card">
2742
+ <div class="pl-stage-header">
2743
+ <div class="pl-stage-title-left">
2744
+ <span class="pl-stage-num">1</span>
2745
+ <div>
2746
+ <h3>Target Definition & Temporal Decision Boundary</h3>
2747
+ <span class="pl-stage-subtitle">Defines the training unit entity, target variable, problem type, and point-in-time cutoff.</span>
2748
+ </div>
2749
+ </div>
2750
+ </div>
2751
+
2752
+ <div class="pl-grid-4">
2753
+ <div class="pl-metric-box">
2754
+ <label>Target Entity</label>
2755
+ <div class="val"><i class="fa-solid fa-table" style="color:var(--mj-brand-primary)"></i> {{ targetEntityName(selectedPipeline) }}</div>
2756
+ <div class="sub">Primary training unit</div>
2757
+ </div>
2758
+ <div class="pl-metric-box">
2759
+ <label>Target Variable</label>
2760
+ <div class="val"><i class="fa-solid fa-bullseye" style="color:var(--mj-status-warning)"></i> {{ editTargetVariable || 'Not set' }}</div>
2761
+ <div class="sub">{{ editProblemType }} outcome</div>
2762
+ </div>
2763
+ <div class="pl-metric-box">
2764
+ <label>Problem Type</label>
2765
+ <div class="val"><i class="fa-solid fa-chart-pie" style="color:var(--mj-brand-primary)"></i> {{ editProblemType }}</div>
2766
+ <div class="sub">Optimization discipline</div>
2767
+ </div>
2768
+ <div class="pl-metric-box" style="border-left: 3px solid var(--mj-brand-primary);">
2769
+ <label>As-Of Temporal Anchor</label>
2770
+ <div class="val"><i class="fa-solid fa-clock-rotate-left" style="color:var(--mj-brand-primary)"></i> {{ asOfLabel }}</div>
2771
+ <div class="sub">Temporal cutoff to prevent leakage</div>
2772
+ </div>
2773
+ </div>
1481
2774
  </div>
1482
- @if (selectedNode.type === 'src' || selectedNode.type === 'feat' || selectedNode.type === 'emb') {
1483
- <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-delete" (click)="deleteSelected()"><i class="fa-solid fa-trash"></i></button>
1484
- }
1485
- </div>
1486
- <div class="ps-card-body">
1487
- <!-- SOURCE editor -->
1488
- @if (selectedSource) {
1489
- <div class="ps-field"><label>Kind</label>
1490
- <select class="mj-input" [value]="selectedSource.Kind" (change)="setSourceKind($any($event.target).value)">
1491
- @for (k of sourceKinds; track k) { <option [value]="k">{{ k }}</option> }
1492
- </select>
2775
+
2776
+ <!-- STAGE 2: Feature Architecture & Relational Sources -->
2777
+ <div class="pl-stage-card">
2778
+ <div class="pl-stage-header">
2779
+ <div class="pl-stage-title-left">
2780
+ <span class="pl-stage-num">2</span>
2781
+ <div>
2782
+ <h3>Feature Architecture & Relational Data Sources</h3>
2783
+ <span class="pl-stage-subtitle">Combines core entity attributes with linked relational features across the database.</span>
2784
+ </div>
2785
+ </div>
2786
+ <div style="display:flex; gap:6px;">
2787
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-add-source" (click)="addSource()"><i class="fa-solid fa-plus"></i> Add Source</button>
2788
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-add-step" (click)="addStep()"><i class="fa-solid fa-sliders"></i> Add Step</button>
2789
+ </div>
1493
2790
  </div>
1494
- <div class="ps-field"><label>Reference (entity / query / id)</label>
1495
- <input class="mj-input" type="text" [value]="selectedSource.Ref" (input)="setSourceRef($any($event.target).value)" />
2791
+
2792
+ <div class="pl-grid-3">
2793
+ <!-- Feature Group: Sources -->
2794
+ <div class="pl-feature-group">
2795
+ <div class="pl-fg-head">
2796
+ <span><i class="fa-solid fa-database"></i> Feed-In Sources ({{ editSources.length }})</span>
2797
+ </div>
2798
+ <div class="pl-fg-body">
2799
+ @for (s of editSources; track $index) {
2800
+ <div class="pl-feature-row">
2801
+ <span class="pl-feature-name">{{ s.Ref || 'Unnamed source' }}</span>
2802
+ <span class="ps-tag">{{ s.Kind }}</span>
2803
+ </div>
2804
+ }
2805
+ </div>
2806
+ </div>
2807
+
2808
+ <!-- Feature Group: Feature Steps -->
2809
+ <div class="pl-feature-group">
2810
+ <div class="pl-fg-head">
2811
+ <span><i class="fa-solid fa-sliders"></i> Feature Transforms ({{ editSteps.length }})</span>
2812
+ </div>
2813
+ <div class="pl-fg-body">
2814
+ @for (st of editSteps; track st.Id) {
2815
+ <div class="pl-feature-card" data-testid="ps-pipelines-step-card" (click)="selectStep(st.Id)">
2816
+ <div class="pl-feature-card-header">
2817
+ <span class="pl-feature-name">{{ st.Label || st.Kind }}</span>
2818
+ <span class="ps-tag">{{ st.Kind }}</span>
2819
+ </div>
2820
+ @if (getStepColumns(st); as cols) {
2821
+ @if (cols.length > 0) {
2822
+ <div class="pl-col-chips">
2823
+ @for (col of cols; track col) {
2824
+ <span class="pl-col-chip">{{ col }}</span>
2825
+ }
2826
+ </div>
2827
+ }
2828
+ }
2829
+ @if (getStepInputs(st); as inputs) {
2830
+ @if (inputs.length > 0) {
2831
+ <div class="pl-step-inputs">
2832
+ <i class="fa-solid fa-arrow-right-to-bracket"></i>
2833
+ <span>From: {{ inputs.join(', ') }}</span>
2834
+ </div>
2835
+ }
2836
+ }
2837
+ </div>
2838
+ }
2839
+ </div>
2840
+ </div>
2841
+
2842
+ <!-- Feature Group: Algorithm Selection -->
2843
+ <div class="pl-feature-group">
2844
+ <div class="pl-fg-head">
2845
+ <span><i class="fa-solid fa-shapes"></i> Selected Algorithm</span>
2846
+ </div>
2847
+ <div class="pl-fg-body">
2848
+ <div class="pl-feature-row">
2849
+ <span class="pl-feature-name">{{ selectedAlgorithmName }}</span>
2850
+ <span class="ps-tag primary">Active</span>
2851
+ </div>
2852
+ </div>
2853
+ </div>
1496
2854
  </div>
1497
- <div class="ps-field"><label>Alias (optional)</label>
1498
- <input class="mj-input" type="text" [value]="selectedSource.Alias || ''" (input)="setSourceAlias($any($event.target).value)" />
2855
+
2856
+ <!-- Leakage Guard Bar -->
2857
+ <div class="pl-leakage-box">
2858
+ <i class="fa-solid fa-shield-halved"></i>
2859
+ <div style="flex:1;">
2860
+ <div style="font-size:13px; font-weight:700; color:var(--mj-text-primary);">Leakage Guard Active</div>
2861
+ <div style="font-size:12px; color:var(--mj-text-secondary); margin-top:2px;">
2862
+ Deny-listed columns are excluded prior to matrix assembly. Dominance threshold: <strong>{{ editLeakage.SingleFeatureDominanceThreshold }}</strong>
2863
+ </div>
2864
+ <div style="margin-top:6px;">
2865
+ @if (editLeakage.DenyFields.length === 0) {
2866
+ <span style="font-size:12px; color:var(--mj-text-muted);">No deny-list columns configured.</span>
2867
+ } @else {
2868
+ @for (col of editLeakage.DenyFields; track col) {
2869
+ <span class="pl-deny-chip">{{ col }}</span>
2870
+ }
2871
+ }
2872
+ </div>
2873
+ </div>
1499
2874
  </div>
1500
- }
1501
- <!-- STEP editor -->
1502
- @if (selectedStep) {
1503
- <div class="ps-field"><label>Kind</label>
1504
- <select class="mj-input" [value]="selectedStep.Kind" (change)="setStepKind($any($event.target).value)">
1505
- @for (k of stepKinds; track k) { <option [value]="k">{{ k }}</option> }
1506
- </select>
2875
+ </div>
2876
+
2877
+ <!-- STAGE 3: Preprocessing & Data Hygiene -->
2878
+ <div class="pl-stage-card">
2879
+ <div class="pl-stage-header">
2880
+ <div class="pl-stage-title-left">
2881
+ <span class="pl-stage-num">3</span>
2882
+ <div>
2883
+ <h3>Fitted Preprocessing & Validation Discipline</h3>
2884
+ <span class="pl-stage-subtitle">Parameters fitted once on training folds and frozen with the model artifact for inference anti-skew.</span>
2885
+ </div>
2886
+ </div>
1507
2887
  </div>
1508
- <div class="ps-field"><label>Label</label>
1509
- <input class="mj-input" type="text" [value]="selectedStep.Label || ''" (input)="setStepLabel($any($event.target).value)" />
2888
+
2889
+ <div class="pl-grid-4">
2890
+ <div class="pl-metric-box">
2891
+ <label>Validation Strategy</label>
2892
+ <div class="val">{{ editValidation.Strategy }}</div>
2893
+ <div class="sub">Cross-validation split</div>
2894
+ </div>
2895
+ <div class="pl-metric-box">
2896
+ <label>Locked Holdout</label>
2897
+ <div class="val">{{ (editValidation.LockedHoldoutFraction * 100).toFixed(0) }}%</div>
2898
+ <div class="sub">Honest benchmark score</div>
2899
+ </div>
2900
+ <div class="pl-metric-box">
2901
+ <label>Folds / Split</label>
2902
+ <div class="val">{{ editValidation.Strategy === 'kfold' ? (editValidation.K || 5) + ' Folds' : ((editValidation.TestSize || 0.2) * 100).toFixed(0) + '% Test' }}</div>
2903
+ <div class="sub">Training evaluation</div>
2904
+ </div>
2905
+ <div class="pl-metric-box">
2906
+ <label>Anti-Skew Guarantee</label>
2907
+ <div class="val"><i class="fa-solid fa-lock" style="color:var(--mj-status-success)"></i> Enforced</div>
2908
+ <div class="sub">Frozen params travel with model</div>
2909
+ </div>
1510
2910
  </div>
1511
- @switch (selectedStep.Kind) {
1512
- @case ('select') { <div class="ps-field"><label>Columns (comma-separated)</label><input class="mj-input" type="text" [value]="columnsText" (input)="setColumns($any($event.target).value)" /></div> }
1513
- @case ('standardize') { <div class="ps-field"><label>Columns (comma-separated)</label><input class="mj-input" type="text" [value]="columnsText" (input)="setColumns($any($event.target).value)" /></div> }
1514
- @case ('impute') {
1515
- <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', $any($event.target).value)" /></div>
1516
- <div class="ps-field"><label>Strategy</label><select class="mj-input" [value]="stepField('Strategy')" (change)="setStepStr('Strategy', $any($event.target).value)">@for (s of imputeStrategies; track s) { <option [value]="s">{{ s }}</option> }</select></div>
1517
- }
1518
- @case ('onehot') { <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', $any($event.target).value)" /></div> }
1519
- @case ('bin') {
1520
- <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', $any($event.target).value)" /></div>
1521
- <div class="ps-field"><label>Bins</label><input class="mj-input" type="number" [value]="stepField('Bins')" (input)="setStepNum('Bins', $any($event.target).value)" /></div>
1522
- }
1523
- @case ('embedding') {
1524
- <div class="ps-field"><label>Entity</label><input class="mj-input" type="text" [value]="stepField('Entity')" (input)="setStepStr('Entity', $any($event.target).value)" /></div>
1525
- <div class="ps-field"><label>Embedding model ref</label><input class="mj-input" type="text" [value]="stepField('EmbeddingModelRef')" (input)="setStepStr('EmbeddingModelRef', $any($event.target).value)" /></div>
1526
- <div class="ps-field"><label>Dimensions</label><input class="mj-input" type="number" [value]="stepField('Dims')" (input)="setStepNum('Dims', $any($event.target).value)" /></div>
1527
- }
1528
- @case ('llm-derived') { <div class="ps-field"><label>Feature Pipeline ref</label><input class="mj-input" type="text" [value]="stepField('FeaturePipelineRef')" (input)="setStepStr('FeaturePipelineRef', $any($event.target).value)" /></div> }
1529
- @case ('flow-agent') { <div class="ps-field"><label>Flow Agent ref</label><input class="mj-input" type="text" [value]="stepField('FlowAgentRef')" (input)="setStepStr('FlowAgentRef', $any($event.target).value)" /></div> }
1530
- @case ('vision-llm') { <div class="ps-field"><label>Image column</label><input class="mj-input" type="text" [value]="stepField('ImageColumn')" (input)="setStepStr('ImageColumn', $any($event.target).value)" /></div> }
2911
+ </div>
2912
+
2913
+ <!-- STAGE 4: Multi-Algorithm Tournament & Leaderboard -->
2914
+ <div class="pl-stage-card">
2915
+ <div class="pl-stage-header">
2916
+ <div class="pl-stage-title-left">
2917
+ <span class="pl-stage-num">4</span>
2918
+ <div>
2919
+ <h3>Multi-Algorithm Tournament Leaderboard</h3>
2920
+ <span class="pl-stage-subtitle">Models trained and scored on locked holdout data for this pipeline.</span>
2921
+ </div>
2922
+ </div>
2923
+ </div>
2924
+
2925
+ @if (pipelineModels.length === 0) {
2926
+ <div style="padding: 16px; text-align: center; color: var(--mj-text-muted); font-size: 13px;">
2927
+ <i class="fa-solid fa-flask" style="font-size: 24px; margin-bottom: 8px; display: block; opacity: 0.5;"></i>
2928
+ No models trained yet for this pipeline. Click <strong>Train</strong> to execute the first training run.
2929
+ </div>
2930
+ } @else {
2931
+ <table class="pl-table">
2932
+ <thead>
2933
+ <tr>
2934
+ <th>Version</th>
2935
+ <th>Algorithm</th>
2936
+ <th>Holdout Score</th>
2937
+ <th>Status</th>
2938
+ <th>Trained At</th>
2939
+ <th></th>
2940
+ </tr>
2941
+ </thead>
2942
+ <tbody>
2943
+ @for (m of pipelineModels; track m.ID) {
2944
+ <tr [class.winner-row]="m.Status === 'Published'" class="pl-model-row" (click)="openModelRecord(m.ID)" style="cursor:pointer;" title="Open model record in Explorer">
2945
+ <td>
2946
+ <div style="display:flex;align-items:center;gap:6px">
2947
+ <a class="pl-model-link" href="javascript:void(0)" (click)="$event.stopPropagation(); openModelRecord(m.ID)">
2948
+ <strong>v{{ m.Version }}</strong>
2949
+ </a>
2950
+ @if (m.Status === 'Published') {
2951
+ <span class="ps-tag success"><i class="fa-solid fa-trophy"></i> Winner</span>
2952
+ }
2953
+ </div>
2954
+ </td>
2955
+ <td>{{ m.Algorithm || 'Algorithm' }}</td>
2956
+ <td><strong>{{ formatHoldout(m) }}</strong></td>
2957
+ <td><span class="ps-badge" [class]="statusClass(m.Status)">{{ m.Status }}</span></td>
2958
+ <td>{{ m.TrainedAt | date:'short' }}</td>
2959
+ <td style="text-align:right">
2960
+ <button type="button" class="pl-action-icon-btn" (click)="$event.stopPropagation(); openModelRecord(m.ID)" title="Open model record in Explorer">
2961
+ <i class="fa-solid fa-arrow-up-right-from-square"></i>
2962
+ </button>
2963
+ </td>
2964
+ </tr>
2965
+ }
2966
+ </tbody>
2967
+ </table>
1531
2968
  }
1532
- <!-- rewire: upstream step inputs -->
1533
- @if (otherSteps.length > 0) {
1534
- <div class="ps-field"><label>Inputs (upstream steps)</label>
1535
- <div class="inputs-list">
1536
- @for (s of otherSteps; track s.Id) {
1537
- <label class="inp-row"><input type="checkbox" [checked]="hasInput(s.Id)" (change)="toggleInput(s.Id)" /> {{ s.Label || s.Kind }}</label>
2969
+ </div>
2970
+
2971
+ <!-- STAGE 5: Production Serving & Deep Link -->
2972
+ <div class="pl-stage-card">
2973
+ <div class="pl-stage-header">
2974
+ <div class="pl-stage-title-left">
2975
+ <span class="pl-stage-num">5</span>
2976
+ <div>
2977
+ <h3>Production Serving & Model Registry</h3>
2978
+ <span class="pl-stage-subtitle">Active inference wiring and model registry status.</span>
2979
+ </div>
2980
+ </div>
2981
+ <div style="display:flex;gap:8px;align-items:center;">
2982
+ @if (publishedModel; as pub) {
2983
+ <button mjButton variant="secondary" size="sm" type="button"
2984
+ (click)="openModelRecord(pub.ID)"
2985
+ title="Open model record in Explorer">
2986
+ <i class="fa-solid fa-arrow-up-right-from-square"></i> Open Model Record
2987
+ </button>
2988
+ <button mjButton variant="secondary" size="sm" type="button"
2989
+ (click)="viewTrainingRuns(pub.ID)"
2990
+ title="View training runs for this model">
2991
+ <i class="fa-solid fa-flask"></i> View Training Runs
2992
+ </button>
2993
+ }
2994
+ </div>
2995
+ </div>
2996
+
2997
+ <div class="pl-serving-box">
2998
+ <div class="pl-metric-box">
2999
+ <label>Active Serving Model</label>
3000
+ <div class="val">
3001
+ @if (publishedModel; as pub) {
3002
+ <span>v{{ pub.Version }} ({{ pub.Algorithm || 'Model' }}) &bull; ID: {{ pub.ID.slice(0, 8) }}…</span>
3003
+ } @else {
3004
+ <span style="color:var(--mj-text-muted);">No published model serving live predictions yet.</span>
1538
3005
  }
1539
3006
  </div>
1540
- <div class="ps-small ps-muted" style="margin-top:4px">No inputs = a root step fed directly by the sources.</div>
3007
+ <div class="sub">Model: {{ publishedModel?.Status || 'None' }} &bull; Pipeline: {{ selectedPipeline?.Status }}</div>
1541
3008
  </div>
1542
- }
1543
- }
1544
- <!-- TARGET editor -->
1545
- @if (selectedNode.type === 'target') {
1546
- <div class="ps-field"><label>Target variable</label><input class="mj-input" type="text" [value]="editTargetVariable" (input)="setTargetVariable($any($event.target).value)" /></div>
1547
- <div class="ps-field"><label>Problem type</label><select class="mj-input" [value]="editProblemType" (change)="setProblemType($any($event.target).value)">@for (pt of problemTypes; track pt) { <option [value]="pt">{{ pt }}</option> }</select></div>
1548
- }
1549
- <!-- ALGORITHM editor -->
1550
- @if (selectedNode.type === 'algo') {
1551
- <div class="ps-field"><label>Algorithm</label><select class="mj-input" [value]="editAlgorithmId" (change)="setAlgorithm($any($event.target).value)">@for (a of algorithms; track a.ID) { <option [value]="a.ID">{{ a.Name }}</option> }</select></div>
1552
- <div class="ps-field"><label>Hyperparameters (JSON)</label><textarea class="mj-textarea" rows="4" [value]="editHyperparams" (input)="setHyperparams($any($event.target).value)"></textarea></div>
1553
- }
1554
- @if (selectedNode.type === 'output') {
1555
- <div class="ps-small ps-muted">The trained model artifact. Run <strong>Train</strong> to produce a new versioned model from this pipeline.</div>
1556
- }
1557
- </div>
1558
- </div>
3009
+ <div class="pl-metric-box" style="background:var(--mj-brand-primary-subtle); border-color:rgba(0,118,182,0.2);">
3010
+ <label>Inference Readiness</label>
3011
+ <div class="val" style="color:var(--mj-brand-primary)">
3012
+ <i class="fa-solid fa-circle-check"></i> Ready
3013
+ </div>
3014
+ <div class="sub">Batch & on-demand scoring enabled</div>
3015
+ </div>
3016
+ </div>
3017
+ </div>
1559
3018
 
1560
- <!-- Leakage guard (editable) -->
1561
- <div class="ps-card">
1562
- <div class="ps-card-head"><i class="fa-solid fa-shield-halved" style="color:var(--mj-status-warning)"></i><h3>Leakage guard</h3></div>
1563
- <div class="ps-card-body">
1564
- <div class="ps-field"><label>Deny-list columns (comma-separated)</label><input class="mj-input" type="text" [value]="denyText" (input)="setDeny($any($event.target).value)" /></div>
1565
- <div class="ps-field"><label>Single-feature dominance threshold</label><input class="mj-input" type="number" step="0.05" min="0" max="1" [value]="editLeakage.SingleFeatureDominanceThreshold" (input)="setThreshold($any($event.target).value)" /></div>
1566
3019
  </div>
1567
- </div>
3020
+ }
1568
3021
 
1569
- <!-- As-of strategy (editable) -->
1570
- <div class="ps-card">
1571
- <div class="ps-card-head"><i class="fa-solid fa-clock-rotate-left" style="color:var(--mj-brand-primary)"></i><h3>As-of strategy</h3></div>
1572
- <div class="ps-card-body">
1573
- <div class="ps-field"><label>Mode</label><select class="mj-input" [value]="editAsOf.Mode" (change)="setAsOfMode($any($event.target).value)"><option value="none">none</option><option value="column">column</option><option value="offset">offset</option></select></div>
1574
- @if (editAsOf.Mode === 'column') { <div class="ps-field"><label>Decision-date column</label><input class="mj-input" type="text" [value]="editAsOf.Column || ''" (input)="setAsOfColumn($any($event.target).value)" /></div> }
1575
- @if (editAsOf.Mode === 'offset') { <div class="ps-field"><label>Offset days before label</label><input class="mj-input" type="number" [value]="editAsOf.OffsetDays ?? 0" (input)="setAsOfOffset($any($event.target).value)" /></div> }
1576
- </div>
1577
- </div>
3022
+ <!-- Graph Canvas & Inspector -->
3023
+ @if (isGraphView) {
3024
+ <div class="builder">
3025
+ <as-split direction="horizontal" class="builder-splitter" unit="percent" [gutterSize]="6" (dragEnd)="onInspectorSplitDragEnd($event.sizes)">
3026
+ <as-split-area [size]="isInspectorCollapsed ? 100 : canvasSizePct" [minSize]="40">
3027
+ <!-- Canvas Wrap -->
3028
+ <div class="canvas-wrap">
3029
+ <div class="canvas-bar">
3030
+ <div class="canvas-bar-left">
3031
+ <span class="ps-small ps-muted" style="font-weight:600">Add to Graph:</span>
3032
+ <button class="ps-pchip s" data-testid="ps-pipelines-add-source" (click)="addSource()"><i class="fa-solid fa-database"></i> Source</button>
3033
+ <button class="ps-pchip f" data-testid="ps-pipelines-add-step" (click)="addStep()"><i class="fa-solid fa-sliders"></i> Feature step</button>
3034
+ </div>
3035
+ <div class="canvas-bar-right">
3036
+ <div class="ps-zoom-controls">
3037
+ <button class="ps-icon-btn" (click)="zoomOut()" [disabled]="zoomLevel <= 0.5" title="Zoom Out"><i class="fa-solid fa-minus"></i></button>
3038
+ <span class="ps-zoom-label" (click)="resetZoom()" title="Reset to 100%">{{ Math.round(zoomLevel * 100) }}%</span>
3039
+ <button class="ps-icon-btn" (click)="zoomIn()" [disabled]="zoomLevel >= 2.0" title="Zoom In"><i class="fa-solid fa-plus"></i></button>
3040
+ </div>
3041
+ <button class="ps-icon-btn" [class.active]="!isInspectorCollapsed" (click)="toggleInspector()" [title]="isInspectorCollapsed ? 'Open Inspector' : 'Collapse Inspector'">
3042
+ <i class="fa-solid fa-circle-info"></i>
3043
+ </button>
3044
+ <span class="ps-small ps-muted ps-node-count"><i class="fa-solid fa-circle-nodes"></i> {{ nodes.length }} nodes · {{ edges.length }} edges</span>
3045
+ </div>
3046
+ </div>
1578
3047
 
1579
- <!-- Validation (editable) -->
1580
- <div class="ps-card">
1581
- <div class="ps-card-head"><i class="fa-solid fa-scissors" style="color:var(--mj-brand-primary)"></i><h3>Validation</h3></div>
1582
- <div class="ps-card-body">
1583
- <div class="ps-field"><label>Strategy</label><select class="mj-input" [value]="editValidation.Strategy" (change)="setValStrategy($any($event.target).value)"><option value="train_test_split">train_test_split</option><option value="kfold">kfold</option><option value="holdout">holdout</option></select></div>
1584
- @if (editValidation.Strategy === 'train_test_split') { <div class="ps-field"><label>Test size</label><input class="mj-input" type="number" step="0.05" [value]="editValidation.TestSize ?? 0.2" (input)="setTestSize($any($event.target).value)" /></div> }
1585
- @if (editValidation.Strategy === 'kfold') { <div class="ps-field"><label>Folds (k)</label><input class="mj-input" type="number" [value]="editValidation.K ?? 5" (input)="setK($any($event.target).value)" /></div> }
1586
- <div class="ps-field"><label>Locked holdout fraction</label><input class="mj-input" type="number" step="0.05" [value]="editValidation.LockedHoldoutFraction" (input)="setHoldout($any($event.target).value)" /></div>
1587
- </div>
1588
- </div>
3048
+ <div class="ps-flow big" data-testid="ps-pipelines-canvas">
3049
+ <div class="ps-graph-viewport"
3050
+ [style.width.px]="canvasW"
3051
+ [style.height.px]="canvasH"
3052
+ [style.transform]="'scale(' + zoomLevel + ')'">
3053
+ <svg class="ps-graph-edges"
3054
+ data-testid="ps-pipelines-edges"
3055
+ [attr.width]="canvasW"
3056
+ [attr.height]="canvasH">
3057
+ <defs>
3058
+ <marker id="ps-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
3059
+ <path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="var(--mj-border-strong)" />
3060
+ </marker>
3061
+ <marker id="ps-arrow-active" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
3062
+ <path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="var(--mj-brand-primary)" />
3063
+ </marker>
3064
+ </defs>
3065
+ @for (edge of edges; track edge.from + '->' + edge.to) {
3066
+ <path class="ps-edge-path"
3067
+ [class.active]="isEdgeActive(edge)"
3068
+ [attr.d]="edgePath(edge)"
3069
+ [attr.marker-end]="isEdgeActive(edge) ? 'url(#ps-arrow-active)' : 'url(#ps-arrow)'"></path>
3070
+ }
3071
+ </svg>
3072
+ @for (node of nodes; track node.id) {
3073
+ <div class="ps-node" data-testid="ps-pipelines-node" [attr.data-node-id]="node.id"
3074
+ [ngClass]="node.type" [class.selected]="node.id === selectedId"
3075
+ [style.left.px]="node.x" [style.top.px]="node.y"
3076
+ [style.width.px]="NODE_W"
3077
+ (click)="selectNode(node.id)">
3078
+ <div class="nh">
3079
+ <i class="tile" [ngClass]="node.icon"></i>
3080
+ <span class="ps-node-title" [title]="node.title">{{ node.title }}</span>
3081
+ @if (node.tag) { <span class="ps-tag">{{ node.tag }}</span> }
3082
+ </div>
3083
+ <div class="nb">
3084
+ @for (row of node.rows; track row.k) {
3085
+ <div class="nb-row">
3086
+ <span class="k">{{ row.k }}</span>
3087
+ <span class="v" [title]="row.v">{{ row.v }}</span>
3088
+ </div>
3089
+ }
3090
+ </div>
3091
+ @if (node.hasIn) { <span class="port in"></span> }
3092
+ @if (node.hasOut) { <span class="port out"></span> }
3093
+ </div>
3094
+ }
3095
+ </div>
3096
+ </div>
3097
+
3098
+ <div class="flow-toolbar">
3099
+ <div class="legend">
3100
+ <span><i class="sw src"></i> Source</span>
3101
+ <span><i class="sw feat"></i> Feature</span>
3102
+ <span><i class="sw emb"></i> Embedding</span>
3103
+ <span><i class="sw target"></i> Target</span>
3104
+ <span><i class="sw algo"></i> Algorithm</span>
3105
+ <span><i class="sw output"></i> Model</span>
3106
+ </div>
3107
+ </div>
3108
+ </div>
3109
+ </as-split-area>
3110
+
3111
+ @if (!isInspectorCollapsed) {
3112
+ <as-split-area [size]="inspectorSizePct" [minSize]="20" [maxSize]="55">
3113
+ <!-- Inspector -->
3114
+ <div class="ps-col inspector" data-testid="ps-pipelines-inspector">
3115
+ <div class="ps-card insp">
3116
+ <div class="ihead">
3117
+ <i class="tile" [ngClass]="selectedNode.icon" [attr.data-type]="selectedNode.type"></i>
3118
+ <div style="flex:1">
3119
+ <h3 data-testid="ps-pipelines-inspector-title">{{ selectedNode.title }}</h3>
3120
+ <div class="ps-small ps-muted">{{ nodeTypeLabel(selectedNode.type) }} · selected</div>
3121
+ </div>
3122
+ <button class="insp-collapse-btn" type="button" (click)="toggleInspector()" title="Collapse inspector" aria-label="Collapse inspector">
3123
+ <i class="fa-solid fa-chevron-right"></i>
3124
+ </button>
3125
+ @if (selectedNode.type === 'src' || selectedNode.type === 'feat' || selectedNode.type === 'emb') {
3126
+ <button mjButton variant="secondary" size="sm" data-testid="ps-pipelines-delete" (click)="deleteSelected()"><i class="fa-solid fa-trash"></i></button>
3127
+ }
3128
+ </div>
3129
+ <div class="ps-card-body">
3130
+ @if (selectedSource) {
3131
+ <div class="ps-field"><label>Kind</label>
3132
+ <select class="mj-input" [value]="selectedSource.Kind" (change)="setSourceKind(inputVal($event))">
3133
+ @for (k of sourceKinds; track k) { <option [value]="k">{{ k }}</option> }
3134
+ </select>
3135
+ </div>
3136
+ <div class="ps-field"><label>Reference (entity / query / id)</label>
3137
+ <input class="mj-input" type="text" [value]="selectedSource.Ref" (input)="setSourceRef(inputVal($event))" />
3138
+ </div>
3139
+ <div class="ps-field"><label>Alias (optional)</label>
3140
+ <input class="mj-input" type="text" [value]="selectedSource.Alias || ''" (input)="setSourceAlias(inputVal($event))" />
3141
+ </div>
3142
+ }
3143
+ @if (selectedStep) {
3144
+ <div class="ps-field"><label>Kind</label>
3145
+ <select class="mj-input" [value]="selectedStep.Kind" (change)="setStepKind(inputVal($event))">
3146
+ @for (k of stepKinds; track k) { <option [value]="k">{{ k }}</option> }
3147
+ </select>
3148
+ </div>
3149
+ <div class="ps-field"><label>Label</label>
3150
+ <input class="mj-input" type="text" [value]="selectedStep.Label || ''" (input)="setStepLabel(inputVal($event))" />
3151
+ </div>
3152
+ @switch (selectedStep.Kind) {
3153
+ @case ('select') { <div class="ps-field"><label>Columns (comma-separated)</label><input class="mj-input" type="text" [value]="columnsText" (input)="setColumns(inputVal($event))" /></div> }
3154
+ @case ('standardize') { <div class="ps-field"><label>Columns (comma-separated)</label><input class="mj-input" type="text" [value]="columnsText" (input)="setColumns(inputVal($event))" /></div> }
3155
+ @case ('impute') {
3156
+ <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', inputVal($event))" /></div>
3157
+ <div class="ps-field"><label>Strategy</label><select class="mj-input" [value]="stepField('Strategy')" (change)="setStepStr('Strategy', inputVal($event))">@for (s of imputeStrategies; track s) { <option [value]="s">{{ s }}</option> }</select></div>
3158
+ }
3159
+ @case ('onehot') { <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', inputVal($event))" /></div> }
3160
+ @case ('bin') {
3161
+ <div class="ps-field"><label>Column</label><input class="mj-input" type="text" [value]="stepField('Column')" (input)="setStepStr('Column', inputVal($event))" /></div>
3162
+ <div class="ps-field"><label>Bins</label><input class="mj-input" type="number" [value]="stepField('Bins')" (input)="setStepNum('Bins', inputVal($event))" /></div>
3163
+ }
3164
+ @case ('embedding') {
3165
+ <div class="ps-field"><label>Entity</label><input class="mj-input" type="text" [value]="stepField('Entity')" (input)="setStepStr('Entity', inputVal($event))" /></div>
3166
+ <div class="ps-field"><label>Embedding model ref</label><input class="mj-input" type="text" [value]="stepField('EmbeddingModelRef')" (input)="setStepStr('EmbeddingModelRef', inputVal($event))" /></div>
3167
+ <div class="ps-field"><label>Dimensions</label><input class="mj-input" type="number" [value]="stepField('Dims')" (input)="setStepNum('Dims', inputVal($event))" /></div>
3168
+ }
3169
+ @case ('llm-derived') { <div class="ps-field"><label>Feature Pipeline ref</label><input class="mj-input" type="text" [value]="stepField('FeaturePipelineRef')" (input)="setStepStr('FeaturePipelineRef', inputVal($event))" /></div> }
3170
+ @case ('flow-agent') { <div class="ps-field"><label>Flow Agent ref</label><input class="mj-input" type="text" [value]="stepField('FlowAgentRef')" (input)="setStepStr('FlowAgentRef', inputVal($event))" /></div> }
3171
+ @case ('vision-llm') { <div class="ps-field"><label>Image column</label><input class="mj-input" type="text" [value]="stepField('ImageColumn')" (input)="setStepStr('ImageColumn', inputVal($event))" /></div> }
3172
+ }
3173
+ }
3174
+ @if (selectedNode.type === 'target') {
3175
+ <div class="ps-field"><label>Target variable</label><input class="mj-input" type="text" [value]="editTargetVariable" (input)="setTargetVariable(inputVal($event))" /></div>
3176
+ <div class="ps-field"><label>Problem type</label><select class="mj-input" [value]="editProblemType" (change)="setProblemType(inputVal($event))">@for (pt of problemTypes; track pt) { <option [value]="pt">{{ pt }}</option> }</select></div>
3177
+ }
3178
+ @if (selectedNode.type === 'algo') {
3179
+ <div class="ps-field"><label>Algorithm</label><select class="mj-input" [value]="editAlgorithmId" (change)="setAlgorithm(inputVal($event))">@for (a of algorithms; track a.ID) { <option [value]="a.ID">{{ a.Name }}</option> }</select></div>
3180
+ <div class="ps-field"><label>Hyperparameters (JSON)</label><textarea class="mj-textarea" rows="4" [value]="editHyperparams" (input)="setHyperparams(inputVal($event))"></textarea></div>
3181
+ @if (publishedModel; as pub) {
3182
+ <div style="margin-top:12px;padding:10px;border-radius:var(--mj-radius-sm);background:var(--mj-bg-surface-sunken);border:1px solid var(--mj-border-default);">
3183
+ <div class="ps-small ps-muted" style="margin-bottom:6px;">Current Serving Winner</div>
3184
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
3185
+ <span style="font-size:var(--mj-text-xs);font-weight:600;">v{{ pub.Version }} ({{ pub.Algorithm || 'Model' }})</span>
3186
+ <span class="ps-tag success"><i class="fa-solid fa-trophy"></i> Active</span>
3187
+ </div>
3188
+ <div style="display:flex;gap:6px;">
3189
+ <button mjButton variant="secondary" size="sm" type="button" (click)="openModelRecord(pub.ID)" style="flex:1;justify-content:center;" title="Open model record in Explorer">
3190
+ <i class="fa-solid fa-arrow-up-right-from-square"></i> Model
3191
+ </button>
3192
+ <button mjButton variant="secondary" size="sm" type="button" (click)="viewTrainingRuns(pub.ID)" style="flex:1;justify-content:center;" title="View training runs">
3193
+ <i class="fa-solid fa-flask"></i> Runs
3194
+ </button>
3195
+ </div>
3196
+ </div>
3197
+ }
3198
+ }
3199
+ @if (selectedNode.type === 'output') {
3200
+ @if (pipelineModels.length === 0) {
3201
+ <div class="ps-small ps-muted">The trained model artifact. Run <strong>Train</strong> to produce a new versioned model from this pipeline.</div>
3202
+ } @else {
3203
+ <div style="display:flex;flex-direction:column;gap:12px;">
3204
+ @if (pipelineModels.length > 1) {
3205
+ <div class="ps-field">
3206
+ <label>Model Version</label>
3207
+ <select class="mj-input" [value]="inspectedModel?.ID" (change)="selectModelVersion(inputVal($event))">
3208
+ @for (m of pipelineModels; track m.ID) {
3209
+ <option [value]="m.ID">v{{ m.Version }} · {{ m.Algorithm || 'Algorithm' }} ({{ m.Status }})</option>
3210
+ }
3211
+ </select>
3212
+ </div>
3213
+ }
3214
+ @if (inspectedModel; as mdl) {
3215
+ <div style="border:1px solid var(--mj-border-default);border-radius:var(--mj-radius-md);padding:12px;background:var(--mj-bg-surface-sunken);display:flex;flex-direction:column;gap:8px;">
3216
+ <div style="display:flex;justify-content:space-between;align-items:center;">
3217
+ <div style="display:flex;align-items:center;gap:6px;">
3218
+ <strong>v{{ mdl.Version }}</strong>
3219
+ @if (mdl.Status === 'Published') {
3220
+ <span class="ps-tag success"><i class="fa-solid fa-trophy"></i> Winner</span>
3221
+ }
3222
+ <span class="ps-badge" [class]="statusClass(mdl.Status)">{{ mdl.Status }}</span>
3223
+ </div>
3224
+ <span class="ps-mono ps-small ps-muted" [title]="mdl.ID">{{ mdl.ID.slice(0, 8) }}…</span>
3225
+ </div>
3226
+ <div style="font-size:var(--mj-text-xs);color:var(--mj-text-secondary);">
3227
+ Algorithm: <strong>{{ mdl.Algorithm || selectedAlgorithmName }}</strong>
3228
+ </div>
3229
+ <div style="font-size:var(--mj-text-xs);color:var(--mj-text-secondary);">
3230
+ Holdout Score: <strong>{{ formatHoldout(mdl) }}</strong>
3231
+ </div>
3232
+ <div style="display:flex;flex-direction:column;gap:6px;margin-top:4px;">
3233
+ <button mjButton variant="primary" size="sm" type="button" (click)="openModelRecord(mdl.ID)" style="width:100%;justify-content:center;">
3234
+ <i class="fa-solid fa-arrow-up-right-from-square"></i> Open Model Details
3235
+ </button>
3236
+ <button mjButton variant="secondary" size="sm" type="button" (click)="viewTrainingRuns(mdl.ID)" style="width:100%;justify-content:center;">
3237
+ <i class="fa-solid fa-flask"></i> View Training Runs
3238
+ </button>
3239
+ </div>
3240
+ </div>
3241
+ }
3242
+ </div>
3243
+ }
3244
+ }
3245
+ </div>
3246
+ </div>
3247
+
3248
+ <!-- Leakage guard (editable) -->
3249
+ <div class="ps-card">
3250
+ <div class="ps-card-head"><i class="fa-solid fa-shield-halved" style="color:var(--mj-status-warning)"></i><h3>Leakage guard</h3></div>
3251
+ <div class="ps-card-body">
3252
+ <div class="ps-field"><label>Deny-list columns (comma-separated)</label><input class="mj-input" type="text" [value]="denyText" (input)="setDeny(inputVal($event))" /></div>
3253
+ <div class="ps-field"><label>Single-feature dominance threshold</label><input class="mj-input" type="number" step="0.05" min="0" max="1" [value]="editLeakage.SingleFeatureDominanceThreshold" (input)="setThreshold(inputVal($event))" /></div>
3254
+ </div>
3255
+ </div>
3256
+
3257
+ <!-- As-of strategy (editable) -->
3258
+ <div class="ps-card">
3259
+ <div class="ps-card-head"><i class="fa-solid fa-clock-rotate-left" style="color:var(--mj-brand-primary)"></i><h3>As-of strategy</h3></div>
3260
+ <div class="ps-card-body">
3261
+ <div class="ps-field"><label>Mode</label><select class="mj-input" [value]="editAsOf.Mode" (change)="setAsOfMode(inputVal($event))"><option value="none">none</option><option value="column">column</option><option value="offset">offset</option></select></div>
3262
+ @if (editAsOf.Mode === 'column') { <div class="ps-field"><label>Decision-date column</label><input class="mj-input" type="text" [value]="editAsOf.Column || ''" (input)="setAsOfColumn(inputVal($event))" /></div> }
3263
+ @if (editAsOf.Mode === 'offset') { <div class="ps-field"><label>Offset days before label</label><input class="mj-input" type="number" [value]="editAsOf.OffsetDays ?? 0" (input)="setAsOfOffset(inputVal($event))" /></div> }
3264
+ </div>
3265
+ </div>
3266
+
3267
+ <!-- Validation (editable) -->
3268
+ <div class="ps-card">
3269
+ <div class="ps-card-head"><i class="fa-solid fa-scissors" style="color:var(--mj-brand-primary)"></i><h3>Validation</h3></div>
3270
+ <div class="ps-card-body">
3271
+ <div class="ps-field"><label>Strategy</label><select class="mj-input" [value]="editValidation.Strategy" (change)="setValStrategy(inputVal($event))"><option value="train_test_split">train_test_split</option><option value="kfold">kfold</option><option value="holdout">holdout</option></select></div>
3272
+ @if (editValidation.Strategy === 'train_test_split') { <div class="ps-field"><label>Test size</label><input class="mj-input" type="number" step="0.05" [value]="editValidation.TestSize ?? 0.2" (input)="setTestSize(inputVal($event))" /></div> }
3273
+ @if (editValidation.Strategy === 'kfold') { <div class="ps-field"><label>Folds (k)</label><input class="mj-input" type="number" [value]="editValidation.K ?? 5" (input)="setK(inputVal($event))" /></div> }
3274
+ <div class="ps-field"><label>Locked holdout fraction</label><input class="mj-input" type="number" step="0.05" [value]="editValidation.LockedHoldoutFraction" (input)="setHoldout(inputVal($event))" /></div>
3275
+ </div>
3276
+ </div>
3277
+ </div>
3278
+ </as-split-area>
3279
+ }
3280
+ </as-split>
1589
3281
  </div>
3282
+ }
3283
+ </div>
3284
+ </as-split-area>
3285
+ </as-split>
1590
3286
  </div>
1591
3287
  }
3288
+
3289
+ @if (showWizard) {
3290
+ <ps-pipeline-wizard
3291
+ [engine]="engine"
3292
+ [provider]="provider"
3293
+ [currentUser]="currentUser"
3294
+ [cloneFrom]="wizardClonePipeline"
3295
+ [initialAlgorithmId]="initialWizardAlgorithmId"
3296
+ (saved)="onWizardSaved($event)"
3297
+ (trainRequested)="onWizardTrainRequested($event)"
3298
+ (closed)="closeWizard()">
3299
+ </ps-pipeline-wizard>
3300
+ }
1592
3301
  </div>
1593
- `, 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", "/* Training Pipelines \u2014 real visual DAG builder driven by each pipeline's persisted spec. */\n\n.ps-pipelines { display: flex; flex-direction: column; gap: 14px; height: 100%; min-height: 0; }\n\n/* Pipeline picker bar */\n.ps-pipelines .pl-picker { display: flex; align-items: center; gap: 8px; overflow-x: auto; padding-bottom: 6px; }\n.ps-pipelines .pl-toolbar { display: flex; align-items: center; gap: 8px; margin: 8px 0 12px; padding-bottom: 12px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .pl-pill {\n display: inline-flex; align-items: center; gap: 8px; cursor: pointer; flex: 0 0 auto; max-width: 260px;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary); border-radius: 999px; padding: 6px 12px; font-size: 13px;\n}\n.ps-pipelines .pl-pill .pl-pill-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-pipelines .pl-pill:hover { background: var(--mj-bg-surface-hover); }\n.ps-pipelines .pl-pill.on {\n border-color: var(--mj-brand-primary); color: var(--mj-text-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n/* The builder fills the remaining panel height; each column scrolls on its own. */\n.ps-pipelines .builder {\n flex: 1; min-height: 0;\n display: grid; grid-template-columns: 1fr 320px; grid-template-rows: minmax(0, 1fr); gap: 16px;\n}\n@media (max-width: 1080px) {\n .ps-pipelines .builder { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1.4fr) minmax(0, 1fr); }\n}\n\n.ps-pipelines .canvas-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }\n.ps-pipelines .canvas-bar {\n display: flex; align-items: center; gap: 10px; flex-wrap: wrap;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-bottom: none; border-radius: 10px 10px 0 0; padding: 12px 14px;\n}\n/* The DAG canvas is sized in px by the layered layout (inline width/height) and\n may exceed the column, so it scrolls internally (both axes) while the column\n itself stays put. flex:1 makes it absorb the remaining column height. */\n.ps-pipelines .ps-flow.big {\n border-radius: 0;\n flex: 1 1 auto;\n min-height: 0;\n max-width: 100%;\n overflow: auto;\n}\n/* Pin every node to a fixed height so its port (top:50%) lines up exactly with\n the SVG edge anchor (node.y + NODE_H/2). NODE_H in the component = 86. */\n.ps-pipelines .ps-flow.big .ps-node {\n height: 86px;\n display: flex;\n flex-direction: column;\n}\n.ps-pipelines .ps-flow.big .ps-node .nb { flex: 1; min-height: 0; }\n.ps-pipelines .flow-toolbar {\n display: flex; align-items: center; gap: 8px;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-top: none; border-radius: 0 0 10px 10px; padding: 10px 14px;\n}\n.ps-pipelines .legend { display: flex; align-items: center; gap: 14px; font-size: 12px; color: var(--mj-text-secondary); flex-wrap: wrap; }\n.ps-pipelines .legend span { display: inline-flex; align-items: center; gap: 6px; }\n.ps-pipelines .legend .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-pipelines .legend .sw.src { background: var(--ps-node-source); }\n.ps-pipelines .legend .sw.feat { background: var(--ps-node-feature); }\n.ps-pipelines .legend .sw.emb { background: var(--ps-node-embedding); }\n.ps-pipelines .legend .sw.target { background: var(--ps-node-target); }\n.ps-pipelines .legend .sw.algo { background: var(--ps-node-algorithm); }\n\n/* inspector \u2014 its own scroll column, independent of the canvas. NOTE: .inspector is\n also a .ps-col (display:flex column), so its card children ARE flex items \u2014 without\n flex-shrink:0 they compress to fit (scrollHeight == clientHeight, verified live) and\n the column never scrolls. These child rules are load-bearing, not decorative. */\n.ps-pipelines .inspector {\n min-height: 0;\n overflow-y: auto;\n}\n.ps-pipelines .inspector > * { flex: 0 0 auto; }\n.ps-pipelines .inspector .insp { position: static; }\n.ps-pipelines .ihead { display: flex; align-items: center; gap: 11px; padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .ihead h3 { font-size: 14px; color: var(--mj-text-primary); margin: 0; }\n.ps-pipelines .ihead .tile {\n width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto; background: var(--ps-node-feature);\n}\n.ps-pipelines .ihead .tile[data-type='src'] { background: var(--ps-node-source); }\n.ps-pipelines .ihead .tile[data-type='emb'] { background: var(--ps-node-embedding); }\n.ps-pipelines .ihead .tile[data-type='target'] { background: var(--ps-node-target); }\n.ps-pipelines .ihead .tile[data-type='algo'] { background: var(--ps-node-algorithm); }\n.ps-pipelines .ihead .tile[data-type='output'] { background: var(--mj-status-success); }\n\n/* Palette chips are interactive buttons in the editor */\n.ps-pipelines .canvas-bar .ps-pchip { cursor: pointer; border: none; }\n.ps-pipelines .canvas-bar .ps-pchip:hover { filter: brightness(1.08); }\n\n/* Inspector edit fields */\n.ps-pipelines .ps-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }\n.ps-pipelines .ps-field > label { font-size: 12px; font-weight: 650; color: var(--mj-text-muted); }\n.ps-pipelines .inputs-list { display: flex; flex-direction: column; gap: 4px; }\n.ps-pipelines .inp-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--mj-text-secondary); cursor: pointer; }\n\n/* Empty-state CTA spacing */\n.ps-pipelines .ps-empty button { margin-top: 16px; }\n"] }]
3302
+ `, 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", "/* Training Pipelines \u2014 Human-Centric Master-Detail Redesign with Progressive Stages & DAG toggle */\n\n.ps-pipelines {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n width: 100%;\n overflow: hidden;\n}\n\n/* Master-Detail Layout with Resizable Splitter */\n.ps-pipelines .pl-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n background: var(--mj-bg-page);\n position: relative;\n}\n\n.ps-pipelines .pl-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.ps-pipelines .pl-collapsed-strip {\n width: 44px;\n flex: 0 0 44px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n z-index: 10;\n}\n\n.ps-pipelines .pl-rail-collapse {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-sm);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-rail-collapse:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.ps-pipelines .pl-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.ps-pipelines .pl-collapse-btn {\n background: transparent;\n border: 1px solid transparent;\n color: var(--mj-text-muted);\n border-radius: var(--mj-radius-sm);\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n.ps-pipelines .pl-model-row {\n cursor: pointer;\n transition: background 0.12s ease;\n}\n\n.ps-pipelines .pl-model-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.ps-pipelines .pl-model-link {\n color: var(--mj-brand-primary);\n font-weight: 600;\n text-decoration: none;\n}\n\n.ps-pipelines .pl-model-link:hover {\n text-decoration: underline;\n}\n\n.ps-pipelines .pl-action-icon-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n width: 28px;\n height: 28px;\n border-radius: var(--mj-radius-sm);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: all 0.12s ease;\n}\n\n.ps-pipelines .pl-action-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-brand-primary);\n}\n\n/* Left Column: Pipeline Catalog */\n.ps-pipelines .pl-catalog {\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.ps-pipelines .pl-catalog-header {\n padding: 14px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n gap: 10px;\n flex-shrink: 0;\n}\n\n.ps-pipelines .pl-catalog-title-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.ps-pipelines .pl-catalog-title-row h2 {\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-search-box {\n position: relative;\n width: 100%;\n}\n\n.ps-pipelines .pl-search-box input {\n width: 100%;\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n padding: 7px 10px 7px 30px;\n font-size: 12px;\n color: var(--mj-text-primary);\n outline: none;\n}\n\n.ps-pipelines .pl-search-box i {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.ps-pipelines .pl-filter-chips {\n display: flex;\n gap: 6px;\n overflow-x: auto;\n padding-bottom: 2px;\n}\n\n.ps-pipelines .pl-filter-chip {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n padding: 3px 8px;\n border-radius: 9999px;\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.ps-pipelines .pl-filter-chip.active {\n background: var(--mj-bg-surface-subtle);\n border-color: var(--mj-text-secondary);\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* Catalog Pipeline Cards List (data-testid=\"ps-pipelines-picker\") */\n.ps-pipelines .pl-picker {\n flex: 1;\n overflow-y: auto;\n padding: 10px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Individual Card (data-testid=\"ps-pipelines-pill\") */\n.ps-pipelines .pl-pill.pipeline-card {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 6px;\n width: 100%;\n max-width: none;\n border-radius: 8px;\n padding: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n text-align: left;\n}\n\n.ps-pipelines .pl-pill.pipeline-card:hover {\n border-color: var(--mj-border-subtle);\n background: var(--mj-bg-surface-hover);\n}\n\n.ps-pipelines .pl-pill.pipeline-card.on {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.ps-pipelines .pl-card-top {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n}\n\n.ps-pipelines .pl-card-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: normal;\n}\n\n.ps-pipelines .pl-card-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-entity-tag {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-brand-primary);\n background: var(--mj-brand-primary-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 500;\n}\n\n.ps-pipelines .pl-target-tag {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-metric-badge {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-subtle);\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n/* Right Detail Workspace */\n.ps-pipelines .pl-workspace {\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n display: flex;\n flex-direction: column;\n}\n\n.ps-pipelines .pl-workspace-header {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 16px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n position: sticky;\n top: 0;\n z-index: 10;\n flex-shrink: 0;\n}\n\n.ps-pipelines .pl-workspace-title-area {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ps-pipelines .pl-breadcrumbs {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-breadcrumbs span {\n color: var(--mj-text-secondary);\n}\n\n.ps-pipelines .pl-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .pl-title-row h1 {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-toolbar {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 0;\n border: none;\n}\n\n/* Workspace Stages Body */\n.ps-pipelines .pl-stages-body {\n padding: 20px 24px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n max-width: 1400px;\n}\n\n.ps-pipelines .pl-stage-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 18px 20px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.ps-pipelines .pl-stage-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.ps-pipelines .pl-stage-title-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .pl-stage-num {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: var(--mj-brand-primary-subtle);\n color: var(--mj-brand-primary);\n display: grid;\n place-items: center;\n font-size: 12px;\n font-weight: 700;\n}\n\n.ps-pipelines .pl-stage-title-left h3 {\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.ps-pipelines .pl-stage-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n/* Stage Grids */\n.ps-pipelines .pl-grid-4 {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 12px;\n}\n\n.ps-pipelines .pl-grid-3 {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n@media (max-width: 1200px) {\n .ps-pipelines .pl-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .ps-pipelines .pl-grid-3 { grid-template-columns: 1fr; }\n}\n\n.ps-pipelines .pl-metric-box {\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ps-pipelines .pl-metric-box label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n}\n\n.ps-pipelines .pl-metric-box .val {\n font-size: 13.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .pl-metric-box .sub {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n/* Feature Group Cards */\n.ps-pipelines .pl-feature-group {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.ps-pipelines .pl-fg-head {\n padding: 10px 12px;\n background: var(--mj-bg-surface-subtle);\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12.5px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-fg-body {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.ps-pipelines .pl-feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n padding: 5px 8px;\n background: var(--mj-bg-surface-subtle);\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-feature-name {\n font-family: monospace;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-feature-card {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n padding: 6px 8px;\n background: var(--mj-bg-surface-subtle);\n border-radius: 4px;\n border: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .pl-feature-card:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-default);\n}\n\n.ps-pipelines .pl-feature-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n}\n\n.ps-pipelines .pl-col-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 2px;\n}\n\n.ps-pipelines .pl-col-chip {\n font-family: monospace;\n font-size: 11px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n padding: 1px 6px;\n border-radius: 4px;\n}\n\n.ps-pipelines .pl-step-inputs {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 4px;\n margin-top: 2px;\n}\n\n/* Leakage Guard Box */\n.ps-pipelines .pl-leakage-box {\n background: var(--mj-status-warning-subtle);\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, transparent);\n border-radius: 8px;\n padding: 12px 14px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.ps-pipelines .pl-leakage-box i {\n color: var(--mj-status-warning);\n font-size: 18px;\n margin-top: 2px;\n}\n\n.ps-pipelines .pl-deny-chip {\n font-family: monospace;\n font-size: 11px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, transparent);\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n padding: 2px 6px;\n border-radius: 4px;\n text-decoration: line-through;\n display: inline-block;\n margin-right: 4px;\n margin-top: 4px;\n}\n\n/* Leaderboard Table */\n.ps-pipelines .pl-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n\n.ps-pipelines .pl-table th {\n text-align: left;\n padding: 8px 12px;\n background: var(--mj-bg-surface-subtle);\n color: var(--mj-text-muted);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.ps-pipelines .pl-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .pl-table tr.winner-row {\n background: var(--mj-brand-primary-subtle);\n font-weight: 600;\n}\n\n/* Serving Box */\n.ps-pipelines .pl-serving-box {\n display: grid;\n grid-template-columns: 2fr 1fr;\n gap: 12px;\n}\n\n@media (max-width: 900px) {\n .ps-pipelines .pl-serving-box { grid-template-columns: 1fr; }\n}\n\n/* Pipeline Graph view & inspector container when isGraphView is active */\n.ps-pipelines .builder {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 16px;\n background: var(--mj-bg-page);\n overflow: hidden;\n}\n\n.ps-pipelines .builder-splitter {\n width: 100%;\n height: 100%;\n min-height: 0;\n background: transparent;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter {\n background: transparent;\n transition: background-color 0.15s ease;\n position: relative;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter:hover {\n background-color: var(--mj-brand-primary, #6366f1);\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 24px;\n border-radius: 1px;\n background: var(--mj-border-strong, #cbd5e1);\n transition: background-color 0.15s ease;\n}\n\n.ps-pipelines .builder-splitter .as-split-gutter:hover::after {\n background: var(--mj-bg-surface);\n}\n\n.ps-pipelines .insp-collapse-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border-radius: 6px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0;\n}\n\n.ps-pipelines .insp-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-pipelines .canvas-wrap {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n overflow: hidden;\n}\n\n.ps-pipelines .canvas-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 16px;\n z-index: 5;\n}\n\n.ps-pipelines .canvas-bar-left,\n.ps-pipelines .canvas-bar-right {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.ps-pipelines .ps-zoom-controls {\n display: flex;\n align-items: center;\n background: var(--mj-bg-surface-subtle);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.ps-pipelines .ps-icon-btn {\n background: none;\n border: none;\n padding: 5px 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: grid;\n place-items: center;\n font-size: 11px;\n}\n\n.ps-pipelines .ps-icon-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.ps-pipelines .ps-icon-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.ps-pipelines .ps-zoom-label {\n font-size: 11px;\n font-weight: 600;\n font-family: monospace;\n padding: 0 6px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n user-select: none;\n}\n\n.ps-pipelines .ps-node-count {\n font-size: 12px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .ps-flow.big {\n border: none;\n border-radius: 0;\n flex: 1 1 auto;\n min-height: 0;\n max-width: 100%;\n overflow: auto;\n position: relative;\n background:\n radial-gradient(var(--mj-border-default) 1px, transparent 1px) 0 0 / 24px 24px,\n var(--mj-bg-surface-card);\n}\n\n.ps-pipelines .ps-graph-viewport {\n position: relative;\n transform-origin: 0 0;\n transition: transform 0.15s ease-out;\n}\n\n.ps-pipelines .ps-graph-edges {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n overflow: visible;\n}\n\n.ps-pipelines .ps-edge-path {\n stroke: var(--mj-border-strong);\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.ps-pipelines .ps-edge-path.active {\n stroke: var(--mj-brand-primary);\n stroke-width: 2.5;\n}\n\n.ps-pipelines .ps-node {\n position: absolute;\n width: 230px;\n height: 96px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-left-width: 4px;\n border-radius: 8px;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);\n transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;\n display: flex;\n flex-direction: column;\n}\n\n.ps-pipelines .ps-node:hover {\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);\n transform: translateY(-1px);\n}\n\n.ps-pipelines .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) 20%, transparent), 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.ps-pipelines .ps-node .nh {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 600;\n font-size: 12px;\n color: var(--mj-text-primary);\n min-height: 36px;\n}\n\n.ps-pipelines .ps-node .nh i.tile {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 11px;\n flex: 0 0 auto;\n}\n\n.ps-pipelines .ps-node-title {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ps-pipelines .ps-node .nh .ps-tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 5px;\n border-radius: 4px;\n background: var(--mj-bg-surface-subtle);\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.ps-pipelines .ps-node .nb {\n padding: 6px 10px;\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 3px;\n font-size: 11px;\n}\n\n.ps-pipelines .ps-node .nb-row {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n}\n\n.ps-pipelines .ps-node .nb-row .k {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.ps-pipelines .ps-node .nb-row .v {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-family: monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Ports */\n.ps-pipelines .ps-node .port {\n position: absolute;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-border-strong);\n top: 50%;\n transform: translateY(-50%);\n z-index: 2;\n transition: all 0.15s ease;\n}\n\n.ps-pipelines .ps-node .port.in { left: -6px; }\n.ps-pipelines .ps-node .port.out { right: -6px; }\n\n.ps-pipelines .ps-node.selected .port {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-primary);\n}\n\n/* Node Category Accents \u2014 fallback hex values are the intended definitions; hosts may override them externally without a :root block here */\n.ps-pipelines .ps-node.src { border-left-color: var(--ps-node-src, #0284c7); }\n.ps-pipelines .ps-node.src .nh i.tile { background: var(--ps-node-src, #0284c7); }\n\n.ps-pipelines .ps-node.feat { border-left-color: var(--ps-node-feat, #7c3aed); }\n.ps-pipelines .ps-node.feat .nh i.tile { background: var(--ps-node-feat, #7c3aed); }\n\n.ps-pipelines .ps-node.emb { border-left-color: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .ps-node.emb .nh i.tile { background: var(--ps-node-emb, #4f46e5); }\n\n.ps-pipelines .ps-node.target { border-left-color: var(--ps-node-target, #059669); }\n.ps-pipelines .ps-node.target .nh i.tile { background: var(--ps-node-target, #059669); }\n\n.ps-pipelines .ps-node.algo { border-left-color: var(--ps-node-algo, #d97706); }\n.ps-pipelines .ps-node.algo .nh i.tile { background: var(--ps-node-algo, #d97706); }\n\n.ps-pipelines .ps-node.output { border-left-color: var(--ps-node-output, #16a34a); }\n.ps-pipelines .ps-node.output .nh i.tile { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .flow-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n padding: 8px 16px;\n z-index: 5;\n}\n\n.ps-pipelines .legend {\n display: flex;\n align-items: center;\n gap: 16px;\n font-size: 11px;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n\n.ps-pipelines .legend span {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.ps-pipelines .legend .sw {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n display: inline-block;\n}\n\n.ps-pipelines .legend .sw.src { background: var(--ps-node-src, #0284c7); }\n.ps-pipelines .legend .sw.feat { background: var(--ps-node-feat, #7c3aed); }\n.ps-pipelines .legend .sw.emb { background: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .legend .sw.target { background: var(--ps-node-target, #059669); }\n.ps-pipelines .legend .sw.algo { background: var(--ps-node-algo, #d97706); }\n.ps-pipelines .legend .sw.output { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .inspector {\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n.ps-pipelines .inspector > * { flex: 0 0 auto; }\n.ps-pipelines .inspector .insp { position: static; border: none; background: transparent; }\n.ps-pipelines .ihead { display: flex; align-items: center; gap: 11px; padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle); }\n.ps-pipelines .ihead h3 { font-size: 14px; color: var(--mj-text-primary); margin: 0; }\n.ps-pipelines .ihead .tile {\n width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto; background: var(--ps-node-feat, #7c3aed);\n}\n.ps-pipelines .ihead .tile[data-type='src'] { background: var(--ps-node-src, #0284c7); }\n.ps-pipelines .ihead .tile[data-type='emb'] { background: var(--ps-node-emb, #4f46e5); }\n.ps-pipelines .ihead .tile[data-type='target'] { background: var(--ps-node-target, #059669); }\n.ps-pipelines .ihead .tile[data-type='algo'] { background: var(--ps-node-algo, #d97706); }\n.ps-pipelines .ihead .tile[data-type='output'] { background: var(--ps-node-output, #16a34a); }\n\n.ps-pipelines .canvas-bar .ps-pchip { cursor: pointer; border: none; }\n.ps-pipelines .canvas-bar .ps-pchip:hover { filter: brightness(1.08); }\n.ps-pipelines .ps-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }\n.ps-pipelines .ps-field > label { font-size: 12px; font-weight: 650; color: var(--mj-text-muted); }\n.ps-pipelines .inputs-list { display: flex; flex-direction: column; gap: 4px; }\n.ps-pipelines .inp-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--mj-text-secondary); cursor: pointer; }\n.ps-pipelines .ps-empty button { margin-top: 16px; }\n"] }]
1594
3303
  }], null, { engine: [{
1595
3304
  type: Input
1596
3305
  }], provider: [{
@@ -1599,6 +3308,10 @@ export class PSPipelinesComponent {
1599
3308
  type: Input
1600
3309
  }], askAgent: [{
1601
3310
  type: Output
3311
+ }], navigate: [{
3312
+ type: Output
3313
+ }], viewMode: [{
3314
+ type: Input
1602
3315
  }] }); })();
1603
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSPipelinesComponent, { className: "PSPipelinesComponent", filePath: "src/PredictiveStudio/components/ps-pipelines.component.ts", lineNumber: 313 }); })();
3316
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSPipelinesComponent, { className: "PSPipelinesComponent", filePath: "src/PredictiveStudio/components/ps-pipelines.component.ts", lineNumber: 790 }); })();
1604
3317
  //# sourceMappingURL=ps-pipelines.component.js.map