@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.
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +6 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +99 -45
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +4 -0
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts.map +1 -1
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +16 -6
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +14 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/services/explorer-state.service.js +2 -2
- package/dist/DataExplorer/services/explorer-state.service.js.map +1 -1
- package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
- package/dist/DevTools/graphql-console.component.js +2 -0
- package/dist/DevTools/graphql-console.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +2 -0
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +4 -12
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +39 -78
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts +3 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +7 -6
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts +3 -0
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +7 -6
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.d.ts +41 -3
- package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.js +99 -6
- package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-catalog.component.d.ts +4 -1
- package/dist/PredictiveStudio/components/ps-catalog.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-catalog.component.js +61 -37
- package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +12 -0
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.js +426 -184
- package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +84 -0
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -0
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +1397 -0
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -0
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +66 -34
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +2456 -743
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +194 -0
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -0
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +1902 -0
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -0
- package/dist/PredictiveStudio/components/ps-production.component.d.ts +17 -1
- package/dist/PredictiveStudio/components/ps-production.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-production.component.js +653 -372
- package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts +21 -61
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.js +214 -681
- package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +49 -0
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +1 -0
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +658 -0
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +42 -0
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js +90 -0
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -0
- package/dist/PredictiveStudio/predictive-studio.view-models.d.ts +14 -2
- package/dist/PredictiveStudio/predictive-studio.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/predictive-studio.view-models.js +41 -5
- package/dist/PredictiveStudio/predictive-studio.view-models.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js +178 -65
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +9 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +307 -369
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +17 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +309 -138
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
- package/dist/VersionHistory/components/graph-resource.component.d.ts +1 -0
- package/dist/VersionHistory/components/graph-resource.component.d.ts.map +1 -1
- package/dist/VersionHistory/components/graph-resource.component.js +6 -5
- package/dist/VersionHistory/components/graph-resource.component.js.map +1 -1
- package/dist/predictive-studio-dashboards.module.d.ts +10 -8
- package/dist/predictive-studio-dashboards.module.d.ts.map +1 -1
- package/dist/predictive-studio-dashboards.module.js +13 -3
- package/dist/predictive-studio-dashboards.module.js.map +1 -1
- package/package.json +64 -63
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { ChangeDetectorRef, Component, Input, ViewEncapsulation, inject } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
|
-
import {
|
|
3
|
+
import { AngularSplitModule } from 'angular-split';
|
|
4
4
|
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
5
5
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { PSConfirmModalComponent } from './ps-confirm-modal.component';
|
|
10
|
-
import { humanizeFeatureName } from '../at-risk.view-models';
|
|
6
|
+
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
7
|
+
import { PSModelDetailComponent } from '@memberjunction/ng-core-entity-forms';
|
|
8
|
+
import { primaryAuc, primaryModelScore, formatMetricValue } from '../predictive-studio.view-models';
|
|
11
9
|
import * as i0 from "@angular/core";
|
|
10
|
+
import * as i1 from "angular-split";
|
|
12
11
|
const _forTrack0 = ($index, $item) => $item.id;
|
|
13
|
-
const _forTrack1 = ($index, $item) => $item.key;
|
|
14
|
-
const _forTrack2 = ($index, $item) => $item.name;
|
|
15
12
|
function PSRegistryComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
-
i0.ɵɵelementStart(0, "div", 1)(1, "span",
|
|
17
|
-
i0.ɵɵelement(2, "i",
|
|
13
|
+
i0.ɵɵelementStart(0, "div", 1)(1, "span", 3);
|
|
14
|
+
i0.ɵɵelement(2, "i", 4);
|
|
18
15
|
i0.ɵɵelementEnd();
|
|
19
16
|
i0.ɵɵelementStart(3, "h3");
|
|
20
17
|
i0.ɵɵtext(4, "No trained models yet");
|
|
@@ -23,377 +20,117 @@ function PSRegistryComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
|
23
20
|
i0.ɵɵtext(6, "Train a pipeline to register your first immutable model. Each successful training run produces a versioned model you can validate, publish, and score with here.");
|
|
24
21
|
i0.ɵɵelementEnd()();
|
|
25
22
|
} }
|
|
26
|
-
function
|
|
27
|
-
const
|
|
28
|
-
i0.ɵɵelementStart(0, "div",
|
|
29
|
-
i0.ɵɵlistener("click", function
|
|
30
|
-
i0.ɵɵ
|
|
31
|
-
i0.ɵɵ
|
|
23
|
+
function PSRegistryComponent_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
25
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "button", 13);
|
|
26
|
+
i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_1_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.toggleList()); });
|
|
27
|
+
i0.ɵɵelement(2, "i", 14);
|
|
28
|
+
i0.ɵɵelementEnd();
|
|
29
|
+
i0.ɵɵelementStart(3, "div", 15);
|
|
30
|
+
i0.ɵɵelement(4, "i", 4);
|
|
31
|
+
i0.ɵɵelementEnd()();
|
|
32
|
+
} }
|
|
33
|
+
function PSRegistryComponent_Conditional_2_Conditional_3_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
35
|
+
i0.ɵɵelementStart(0, "div", 24);
|
|
36
|
+
i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_3_For_12_Template_div_click_0_listener() { const m_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.select(m_r6.id)); });
|
|
37
|
+
i0.ɵɵelementStart(1, "div", 25);
|
|
38
|
+
i0.ɵɵelement(2, "i", 26);
|
|
32
39
|
i0.ɵɵelementEnd();
|
|
33
|
-
i0.ɵɵelementStart(3, "div",
|
|
40
|
+
i0.ɵɵelementStart(3, "div", 27)(4, "div", 28);
|
|
34
41
|
i0.ɵɵtext(5);
|
|
35
42
|
i0.ɵɵelementEnd();
|
|
36
|
-
i0.ɵɵelementStart(6, "div",
|
|
43
|
+
i0.ɵɵelementStart(6, "div", 29);
|
|
37
44
|
i0.ɵɵtext(7);
|
|
38
45
|
i0.ɵɵelementEnd()();
|
|
39
|
-
i0.ɵɵelementStart(8, "div",
|
|
46
|
+
i0.ɵɵelementStart(8, "div", 30)(9, "div", 31);
|
|
40
47
|
i0.ɵɵtext(10);
|
|
41
48
|
i0.ɵɵelementEnd();
|
|
42
|
-
i0.ɵɵelementStart(11, "div",
|
|
49
|
+
i0.ɵɵelementStart(11, "div", 32);
|
|
43
50
|
i0.ɵɵtext(12);
|
|
44
51
|
i0.ɵɵelementEnd()()();
|
|
45
52
|
} if (rf & 2) {
|
|
46
|
-
const
|
|
47
|
-
const ctx_r2 = i0.ɵɵnextContext(
|
|
48
|
-
i0.ɵɵclassProp("sel",
|
|
53
|
+
const m_r6 = ctx.$implicit;
|
|
54
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
55
|
+
i0.ɵɵclassProp("sel", m_r6.id === ctx_r2.selectedId)("arc", m_r6.status === "Archived");
|
|
49
56
|
i0.ɵɵadvance();
|
|
50
|
-
i0.ɵɵclassMap(
|
|
57
|
+
i0.ɵɵclassMap(m_r6.iconClass);
|
|
51
58
|
i0.ɵɵadvance(4);
|
|
52
|
-
i0.ɵɵtextInterpolate(
|
|
53
|
-
i0.ɵɵadvance(2);
|
|
54
|
-
i0.ɵɵtextInterpolate2("v", m_r2.version, " \u00B7 ", m_r2.algorithm);
|
|
55
|
-
i0.ɵɵadvance(3);
|
|
56
|
-
i0.ɵɵtextInterpolate(m_r2.holdoutAuc);
|
|
57
|
-
i0.ɵɵadvance();
|
|
58
|
-
i0.ɵɵclassMap(ctx_r2.statusClass(m_r2.status));
|
|
59
|
-
i0.ɵɵadvance();
|
|
60
|
-
i0.ɵɵtextInterpolate(m_r2.status);
|
|
61
|
-
} }
|
|
62
|
-
function PSRegistryComponent_Conditional_2_For_30_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
63
|
-
i0.ɵɵelement(0, "i", 48);
|
|
64
|
-
} }
|
|
65
|
-
function PSRegistryComponent_Conditional_2_For_30_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
-
i0.ɵɵtext(0);
|
|
67
|
-
} if (rf & 2) {
|
|
68
|
-
const $index_r4 = i0.ɵɵnextContext().$index;
|
|
69
|
-
i0.ɵɵtextInterpolate1(" ", $index_r4 + 1, " ");
|
|
70
|
-
} }
|
|
71
|
-
function PSRegistryComponent_Conditional_2_For_30_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
72
|
-
i0.ɵɵelement(0, "span", 49);
|
|
73
|
-
} }
|
|
74
|
-
function PSRegistryComponent_Conditional_2_For_30_Template(rf, ctx) { if (rf & 1) {
|
|
75
|
-
i0.ɵɵelementStart(0, "div", 46)(1, "span", 47);
|
|
76
|
-
i0.ɵɵconditionalCreate(2, PSRegistryComponent_Conditional_2_For_30_Conditional_2_Template, 1, 0, "i", 48)(3, PSRegistryComponent_Conditional_2_For_30_Conditional_3_Template, 1, 1);
|
|
77
|
-
i0.ɵɵelementEnd();
|
|
78
|
-
i0.ɵɵtext(4);
|
|
79
|
-
i0.ɵɵelementEnd();
|
|
80
|
-
i0.ɵɵconditionalCreate(5, PSRegistryComponent_Conditional_2_For_30_Conditional_5_Template, 1, 0, "span", 49);
|
|
81
|
-
} if (rf & 2) {
|
|
82
|
-
const step_r5 = ctx.$implicit;
|
|
83
|
-
const ɵ$index_87_r6 = ctx.$index;
|
|
84
|
-
const ɵ$count_87_r7 = ctx.$count;
|
|
85
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
86
|
-
i0.ɵɵclassProp("done", ctx_r2.stepState(step_r5) === "done")("curr", ctx_r2.stepState(step_r5) === "curr");
|
|
87
|
-
i0.ɵɵadvance(2);
|
|
88
|
-
i0.ɵɵconditional(ctx_r2.stepState(step_r5) === "done" ? 2 : 3);
|
|
89
|
-
i0.ɵɵadvance(2);
|
|
90
|
-
i0.ɵɵtextInterpolate1(" ", step_r5, " ");
|
|
91
|
-
i0.ɵɵadvance();
|
|
92
|
-
i0.ɵɵconditional(!(ɵ$index_87_r6 === ɵ$count_87_r7 - 1) ? 5 : -1);
|
|
93
|
-
} }
|
|
94
|
-
function PSRegistryComponent_Conditional_2_Conditional_55_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
95
|
-
i0.ɵɵelementStart(0, "div", 50)(1, "span", 25);
|
|
96
|
-
i0.ɵɵtext(2);
|
|
97
|
-
i0.ɵɵelementEnd();
|
|
98
|
-
i0.ɵɵelementStart(3, "strong");
|
|
99
|
-
i0.ɵɵtext(4);
|
|
100
|
-
i0.ɵɵelementEnd()();
|
|
101
|
-
} if (rf & 2) {
|
|
102
|
-
const s_r8 = ctx.$implicit;
|
|
103
|
-
i0.ɵɵadvance(2);
|
|
104
|
-
i0.ɵɵtextInterpolate(s_r8.label);
|
|
59
|
+
i0.ɵɵtextInterpolate(m_r6.name);
|
|
105
60
|
i0.ɵɵadvance(2);
|
|
106
|
-
i0.ɵɵ
|
|
107
|
-
} }
|
|
108
|
-
function PSRegistryComponent_Conditional_2_Conditional_55_Template(rf, ctx) { if (rf & 1) {
|
|
109
|
-
i0.ɵɵelementStart(0, "div", 30);
|
|
110
|
-
i0.ɵɵrepeaterCreate(1, PSRegistryComponent_Conditional_2_Conditional_55_For_2_Template, 5, 2, "div", 50, _forTrack1);
|
|
111
|
-
i0.ɵɵelementEnd();
|
|
112
|
-
} if (rf & 2) {
|
|
113
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
114
|
-
i0.ɵɵadvance();
|
|
115
|
-
i0.ɵɵrepeater(ctx_r2.secondaryMetrics);
|
|
116
|
-
} }
|
|
117
|
-
function PSRegistryComponent_Conditional_2_Conditional_56_Template(rf, ctx) { if (rf & 1) {
|
|
118
|
-
i0.ɵɵelementStart(0, "div", 31);
|
|
119
|
-
i0.ɵɵtext(1, "No secondary metrics recorded for this model.");
|
|
120
|
-
i0.ɵɵelementEnd();
|
|
121
|
-
} }
|
|
122
|
-
function PSRegistryComponent_Conditional_2_Conditional_57_Template(rf, ctx) { if (rf & 1) {
|
|
123
|
-
i0.ɵɵelementStart(0, "div", 24);
|
|
124
|
-
i0.ɵɵtext(1, " Train\u2013holdout gap of ");
|
|
125
|
-
i0.ɵɵelementStart(2, "strong");
|
|
126
|
-
i0.ɵɵtext(3);
|
|
127
|
-
i0.ɵɵelementEnd();
|
|
128
|
-
i0.ɵɵtext(4);
|
|
129
|
-
i0.ɵɵelementEnd();
|
|
130
|
-
} if (rf & 2) {
|
|
131
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
61
|
+
i0.ɵɵtextInterpolate2("v", m_r6.version, " \u2022 ", m_r6.algorithm);
|
|
132
62
|
i0.ɵɵadvance(3);
|
|
133
|
-
i0.ɵɵtextInterpolate(
|
|
63
|
+
i0.ɵɵtextInterpolate(m_r6.holdoutScore);
|
|
134
64
|
i0.ɵɵadvance();
|
|
135
|
-
i0.ɵɵ
|
|
136
|
-
} }
|
|
137
|
-
function PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template(rf, ctx) { if (rf & 1) {
|
|
138
|
-
i0.ɵɵelementStart(0, "div", 51)(1, "span", 52);
|
|
139
|
-
i0.ɵɵtext(2);
|
|
140
|
-
i0.ɵɵelementEnd();
|
|
141
|
-
i0.ɵɵelementStart(3, "div", 53);
|
|
142
|
-
i0.ɵɵelement(4, "span");
|
|
143
|
-
i0.ɵɵelementEnd();
|
|
144
|
-
i0.ɵɵelementStart(5, "span", 54);
|
|
145
|
-
i0.ɵɵtext(6);
|
|
146
|
-
i0.ɵɵelementEnd()();
|
|
147
|
-
} if (rf & 2) {
|
|
148
|
-
const f_r9 = ctx.$implicit;
|
|
65
|
+
i0.ɵɵclassMap(ctx_r2.statusClass(m_r6.status));
|
|
149
66
|
i0.ɵɵadvance();
|
|
150
|
-
i0.ɵɵ
|
|
151
|
-
i0.ɵɵadvance();
|
|
152
|
-
i0.ɵɵtextInterpolate(f_r9.name);
|
|
153
|
-
i0.ɵɵadvance(2);
|
|
154
|
-
i0.ɵɵstyleProp("width", f_r9.pct, "%");
|
|
155
|
-
i0.ɵɵclassProp("warn", f_r9.warning);
|
|
156
|
-
i0.ɵɵadvance(2);
|
|
157
|
-
i0.ɵɵtextInterpolate(f_r9.value);
|
|
67
|
+
i0.ɵɵtextInterpolate(m_r6.status);
|
|
158
68
|
} }
|
|
159
|
-
function
|
|
160
|
-
i0.ɵɵ
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
i0.ɵɵrepeater(ctx_r2.importance);
|
|
164
|
-
} }
|
|
165
|
-
function PSRegistryComponent_Conditional_2_Conditional_66_Template(rf, ctx) { if (rf & 1) {
|
|
166
|
-
i0.ɵɵelementStart(0, "div", 32);
|
|
167
|
-
i0.ɵɵtext(1, "No feature-importance data was recorded for this model.");
|
|
168
|
-
i0.ɵɵelementEnd();
|
|
169
|
-
} }
|
|
170
|
-
function PSRegistryComponent_Conditional_2_Conditional_67_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
171
|
-
i0.ɵɵelementStart(0, "strong");
|
|
172
|
-
i0.ɵɵtext(1, "Leakage flag raised.");
|
|
173
|
-
i0.ɵɵelementEnd();
|
|
174
|
-
i0.ɵɵtext(2, " A single feature dominates \u2014 top importance is ");
|
|
175
|
-
i0.ɵɵelementStart(3, "strong");
|
|
176
|
-
i0.ɵɵtext(4);
|
|
69
|
+
function PSRegistryComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
70
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
71
|
+
i0.ɵɵelementStart(0, "as-split-area", 8)(1, "div", 16)(2, "div", 17)(3, "h3");
|
|
72
|
+
i0.ɵɵtext(4, "ML Models");
|
|
177
73
|
i0.ɵɵelementEnd();
|
|
178
|
-
i0.ɵɵ
|
|
179
|
-
i0.ɵɵelementStart(6, "strong");
|
|
74
|
+
i0.ɵɵelementStart(5, "div", 18)(6, "span", 19);
|
|
180
75
|
i0.ɵɵtext(7);
|
|
181
76
|
i0.ɵɵelementEnd();
|
|
182
|
-
i0.ɵɵ
|
|
183
|
-
i0.ɵɵ
|
|
184
|
-
i0.ɵɵelement(
|
|
185
|
-
i0.ɵɵ
|
|
186
|
-
i0.ɵɵ
|
|
77
|
+
i0.ɵɵelementStart(8, "button", 20);
|
|
78
|
+
i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_3_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.toggleList()); });
|
|
79
|
+
i0.ɵɵelement(9, "i", 21);
|
|
80
|
+
i0.ɵɵelementEnd()()();
|
|
81
|
+
i0.ɵɵelementStart(10, "div", 22);
|
|
82
|
+
i0.ɵɵrepeaterCreate(11, PSRegistryComponent_Conditional_2_Conditional_3_For_12_Template, 13, 13, "div", 23, _forTrack0);
|
|
83
|
+
i0.ɵɵelementEnd()()();
|
|
187
84
|
} if (rf & 2) {
|
|
188
|
-
const ctx_r2 = i0.ɵɵnextContext(
|
|
85
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
86
|
+
i0.ɵɵproperty("size", ctx_r2.listSizePct)("minSize", 18)("maxSize", 50);
|
|
87
|
+
i0.ɵɵadvance(7);
|
|
88
|
+
i0.ɵɵtextInterpolate(ctx_r2.models.length);
|
|
189
89
|
i0.ɵɵadvance(4);
|
|
190
|
-
i0.ɵɵ
|
|
191
|
-
i0.ɵɵadvance(3);
|
|
192
|
-
i0.ɵɵtextInterpolate(ctx_r2.dominanceThreshold);
|
|
90
|
+
i0.ɵɵrepeater(ctx_r2.models);
|
|
193
91
|
} }
|
|
194
|
-
function
|
|
195
|
-
i0.ɵɵ
|
|
196
|
-
i0.ɵɵ
|
|
197
|
-
i0.ɵɵ
|
|
198
|
-
i0.ɵɵtext(2, " No single feature dominates \u2014 top importance is ");
|
|
199
|
-
i0.ɵɵelementStart(3, "strong");
|
|
200
|
-
i0.ɵɵtext(4);
|
|
201
|
-
i0.ɵɵelementEnd();
|
|
202
|
-
i0.ɵɵtext(5, ", below the ");
|
|
203
|
-
i0.ɵɵelementStart(6, "strong");
|
|
204
|
-
i0.ɵɵtext(7);
|
|
205
|
-
i0.ɵɵelementEnd();
|
|
206
|
-
i0.ɵɵtext(8, " dominance threshold. ");
|
|
207
|
-
i0.ɵɵelementStart(9, "div", 56);
|
|
208
|
-
i0.ɵɵelement(10, "i", 48);
|
|
209
|
-
i0.ɵɵtext(11, " Sign-off required before any Validated \u2192 Published promotion.");
|
|
92
|
+
function PSRegistryComponent_Conditional_2_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
93
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
94
|
+
i0.ɵɵelementStart(0, "ps-model-detail", 33);
|
|
95
|
+
i0.ɵɵlistener("statusChanged", function PSRegistryComponent_Conditional_2_Conditional_6_Template_ps_model_detail_statusChanged_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onModelStatusChanged($event)); });
|
|
210
96
|
i0.ɵɵelementEnd();
|
|
211
|
-
} if (rf & 2) {
|
|
212
|
-
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
213
|
-
i0.ɵɵadvance(4);
|
|
214
|
-
i0.ɵɵtextInterpolate2("", ctx_r2.topFeatureName, " at ", ctx_r2.topFeatureValue);
|
|
215
|
-
i0.ɵɵadvance(3);
|
|
216
|
-
i0.ɵɵtextInterpolate(ctx_r2.dominanceThreshold);
|
|
217
|
-
} }
|
|
218
|
-
function PSRegistryComponent_Conditional_2_Conditional_67_Template(rf, ctx) { if (rf & 1) {
|
|
219
|
-
i0.ɵɵelementStart(0, "div", 55);
|
|
220
|
-
i0.ɵɵelement(1, "i");
|
|
221
|
-
i0.ɵɵelementStart(2, "div");
|
|
222
|
-
i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_2_Conditional_67_Conditional_3_Template, 12, 3)(4, PSRegistryComponent_Conditional_2_Conditional_67_Conditional_4_Template, 12, 3);
|
|
223
|
-
i0.ɵɵelementEnd()();
|
|
224
97
|
} if (rf & 2) {
|
|
225
98
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
226
|
-
i0.ɵɵ
|
|
227
|
-
i0.ɵɵadvance();
|
|
228
|
-
i0.ɵɵclassMap(ctx_r2.leakageFlagged ? "fa-solid fa-triangle-exclamation" : "fa-solid fa-shield-halved");
|
|
229
|
-
i0.ɵɵadvance(2);
|
|
230
|
-
i0.ɵɵconditional(ctx_r2.leakageFlagged ? 3 : 4);
|
|
231
|
-
} }
|
|
232
|
-
function PSRegistryComponent_Conditional_2_Conditional_72_Template(rf, ctx) { if (rf & 1) {
|
|
233
|
-
const _r10 = i0.ɵɵgetCurrentView();
|
|
234
|
-
i0.ɵɵelementStart(0, "button", 58);
|
|
235
|
-
i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_72_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.requestPromote("Validated")); });
|
|
236
|
-
i0.ɵɵelement(1, "i", 59);
|
|
237
|
-
i0.ɵɵtext(2, " Mark Validated");
|
|
238
|
-
i0.ɵɵelementEnd();
|
|
99
|
+
i0.ɵɵproperty("model", ctx_r2.selectedEntity)("displayName", ctx_r2.engine ? ctx_r2.engine.ModelDisplayName(ctx_r2.selectedEntity) : undefined)("provider", ctx_r2.provider)("currentUser", ctx_r2.currentUser);
|
|
239
100
|
} }
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
i0.ɵɵ
|
|
243
|
-
i0.ɵɵ
|
|
244
|
-
i0.ɵɵelement(1, "i", 61);
|
|
245
|
-
i0.ɵɵtext(2, " Promote to Published");
|
|
246
|
-
i0.ɵɵelementEnd();
|
|
247
|
-
} }
|
|
248
|
-
function PSRegistryComponent_Conditional_2_Conditional_74_Template(rf, ctx) { if (rf & 1) {
|
|
249
|
-
const _r12 = i0.ɵɵgetCurrentView();
|
|
250
|
-
i0.ɵɵelementStart(0, "button", 62);
|
|
251
|
-
i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_74_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.requestPromote("Archived")); });
|
|
252
|
-
i0.ɵɵelement(1, "i", 63);
|
|
253
|
-
i0.ɵɵtext(2, " Archive");
|
|
254
|
-
i0.ɵɵelementEnd();
|
|
101
|
+
function PSRegistryComponent_Conditional_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
102
|
+
i0.ɵɵelementStart(0, "div", 12)(1, "p", 34);
|
|
103
|
+
i0.ɵɵtext(2, "Select a model to view details.");
|
|
104
|
+
i0.ɵɵelementEnd()();
|
|
255
105
|
} }
|
|
256
106
|
function PSRegistryComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
257
|
-
|
|
258
|
-
i0.ɵɵ
|
|
259
|
-
i0.ɵɵ
|
|
260
|
-
i0.ɵɵelementStart(
|
|
261
|
-
i0.ɵɵ
|
|
262
|
-
i0.ɵɵ
|
|
263
|
-
i0.ɵɵelementStart(
|
|
264
|
-
i0.ɵɵ
|
|
265
|
-
i0.ɵɵelementEnd()();
|
|
266
|
-
i0.ɵɵelementStart(10, "div", 11)(11, "div", 12)(12, "div", 13)(13, "div", 14);
|
|
267
|
-
i0.ɵɵelement(14, "i", 15);
|
|
268
|
-
i0.ɵɵelementEnd();
|
|
269
|
-
i0.ɵɵelementStart(15, "div", 16)(16, "h2", 17);
|
|
270
|
-
i0.ɵɵtext(17);
|
|
271
|
-
i0.ɵɵelementStart(18, "span", 18);
|
|
272
|
-
i0.ɵɵtext(19);
|
|
273
|
-
i0.ɵɵelementEnd()();
|
|
274
|
-
i0.ɵɵelementStart(20, "div", 19);
|
|
275
|
-
i0.ɵɵtext(21);
|
|
276
|
-
i0.ɵɵelementEnd()();
|
|
277
|
-
i0.ɵɵelementStart(22, "span", 20);
|
|
278
|
-
i0.ɵɵtext(23);
|
|
279
|
-
i0.ɵɵelementEnd()()();
|
|
280
|
-
i0.ɵɵelementStart(24, "div", 12)(25, "div", 21)(26, "div", 22);
|
|
281
|
-
i0.ɵɵtext(27, "Lifecycle");
|
|
282
|
-
i0.ɵɵelementEnd();
|
|
283
|
-
i0.ɵɵelementStart(28, "div", 23);
|
|
284
|
-
i0.ɵɵrepeaterCreate(29, PSRegistryComponent_Conditional_2_For_30_Template, 6, 7, null, null, i0.ɵɵrepeaterTrackByIdentity);
|
|
285
|
-
i0.ɵɵelementEnd();
|
|
286
|
-
i0.ɵɵelementStart(31, "div", 24);
|
|
287
|
-
i0.ɵɵtext(32, " Models are immutable once registered; promotion only changes lifecycle state, never weights. ");
|
|
288
|
-
i0.ɵɵelementEnd()()();
|
|
289
|
-
i0.ɵɵelementStart(33, "div", 12)(34, "div", 7)(35, "h3");
|
|
290
|
-
i0.ɵɵtext(36, "Performance");
|
|
291
|
-
i0.ɵɵelementEnd();
|
|
292
|
-
i0.ɵɵelementStart(37, "span", 25);
|
|
293
|
-
i0.ɵɵtext(38, "Holdout = held-out test fold, never seen in training");
|
|
294
|
-
i0.ɵɵelementEnd()();
|
|
295
|
-
i0.ɵɵelementStart(39, "div", 21)(40, "div", 26)(41, "div", 27)(42, "div", 22);
|
|
296
|
-
i0.ɵɵtext(43, "Train AUC");
|
|
297
|
-
i0.ɵɵelementEnd();
|
|
298
|
-
i0.ɵɵelementStart(44, "div", 28);
|
|
299
|
-
i0.ɵɵtext(45);
|
|
300
|
-
i0.ɵɵelementEnd();
|
|
301
|
-
i0.ɵɵelementStart(46, "div", 25);
|
|
302
|
-
i0.ɵɵtext(47, "in-sample \u00B7 optimistic");
|
|
303
|
-
i0.ɵɵelementEnd()();
|
|
304
|
-
i0.ɵɵelementStart(48, "div", 29)(49, "div", 22);
|
|
305
|
-
i0.ɵɵtext(50, "Holdout AUC");
|
|
306
|
-
i0.ɵɵelementEnd();
|
|
307
|
-
i0.ɵɵelementStart(51, "div", 28);
|
|
308
|
-
i0.ɵɵtext(52);
|
|
309
|
-
i0.ɵɵelementEnd();
|
|
310
|
-
i0.ɵɵelementStart(53, "div", 25);
|
|
311
|
-
i0.ɵɵtext(54, "out-of-sample \u00B7 the honest number");
|
|
312
|
-
i0.ɵɵelementEnd()()();
|
|
313
|
-
i0.ɵɵconditionalCreate(55, PSRegistryComponent_Conditional_2_Conditional_55_Template, 3, 0, "div", 30)(56, PSRegistryComponent_Conditional_2_Conditional_56_Template, 2, 0, "div", 31);
|
|
314
|
-
i0.ɵɵconditionalCreate(57, PSRegistryComponent_Conditional_2_Conditional_57_Template, 5, 2, "div", 24);
|
|
315
|
-
i0.ɵɵelementEnd()();
|
|
316
|
-
i0.ɵɵelementStart(58, "div", 12)(59, "div", 7)(60, "h3");
|
|
317
|
-
i0.ɵɵtext(61, "Feature Importance");
|
|
318
|
-
i0.ɵɵelementEnd();
|
|
319
|
-
i0.ɵɵelementStart(62, "span", 25);
|
|
320
|
-
i0.ɵɵtext(63);
|
|
321
|
-
i0.ɵɵelementEnd()();
|
|
322
|
-
i0.ɵɵelementStart(64, "div", 21);
|
|
323
|
-
i0.ɵɵconditionalCreate(65, PSRegistryComponent_Conditional_2_Conditional_65_Template, 2, 0)(66, PSRegistryComponent_Conditional_2_Conditional_66_Template, 2, 0, "div", 32);
|
|
324
|
-
i0.ɵɵelementEnd()();
|
|
325
|
-
i0.ɵɵconditionalCreate(67, PSRegistryComponent_Conditional_2_Conditional_67_Template, 5, 7, "div", 33);
|
|
326
|
-
i0.ɵɵelementStart(68, "div", 12)(69, "div", 34)(70, "span", 35);
|
|
327
|
-
i0.ɵɵtext(71);
|
|
328
|
-
i0.ɵɵelementEnd();
|
|
329
|
-
i0.ɵɵconditionalCreate(72, PSRegistryComponent_Conditional_2_Conditional_72_Template, 3, 0, "button", 36);
|
|
330
|
-
i0.ɵɵconditionalCreate(73, PSRegistryComponent_Conditional_2_Conditional_73_Template, 3, 0, "button", 37);
|
|
331
|
-
i0.ɵɵconditionalCreate(74, PSRegistryComponent_Conditional_2_Conditional_74_Template, 3, 0, "button", 38);
|
|
107
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
108
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
109
|
+
i0.ɵɵconditionalCreate(1, PSRegistryComponent_Conditional_2_Conditional_1_Template, 5, 0, "div", 6);
|
|
110
|
+
i0.ɵɵelementStart(2, "as-split", 7);
|
|
111
|
+
i0.ɵɵlistener("dragEnd", function PSRegistryComponent_Conditional_2_Template_as_split_dragEnd_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onSplitDragEnd($event.sizes)); });
|
|
112
|
+
i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_2_Conditional_3_Template, 13, 4, "as-split-area", 8);
|
|
113
|
+
i0.ɵɵelementStart(4, "as-split-area", 9)(5, "div", 10);
|
|
114
|
+
i0.ɵɵconditionalCreate(6, PSRegistryComponent_Conditional_2_Conditional_6_Template, 1, 4, "ps-model-detail", 11)(7, PSRegistryComponent_Conditional_2_Conditional_7_Template, 3, 0, "div", 12);
|
|
332
115
|
i0.ɵɵelementEnd()()()();
|
|
333
116
|
} if (rf & 2) {
|
|
334
117
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
335
|
-
i0.ɵɵ
|
|
336
|
-
i0.ɵɵtextInterpolate(ctx_r2.models.length);
|
|
337
|
-
i0.ɵɵadvance(2);
|
|
338
|
-
i0.ɵɵrepeater(ctx_r2.models);
|
|
339
|
-
i0.ɵɵadvance(9);
|
|
340
|
-
i0.ɵɵtextInterpolate1("", ctx_r2.selected.name, " ");
|
|
341
|
-
i0.ɵɵadvance(2);
|
|
342
|
-
i0.ɵɵtextInterpolate1("v", ctx_r2.selected.version);
|
|
343
|
-
i0.ɵɵadvance(2);
|
|
344
|
-
i0.ɵɵtextInterpolate2("", ctx_r2.selected.algorithm, "", ctx_r2.targetSuffix, " \u00B7 immutable snapshot");
|
|
118
|
+
i0.ɵɵclassProp("list-collapsed", ctx_r2.isListCollapsed);
|
|
345
119
|
i0.ɵɵadvance();
|
|
346
|
-
i0.ɵɵ
|
|
347
|
-
i0.ɵɵadvance();
|
|
348
|
-
i0.ɵɵtextInterpolate(ctx_r2.selected.status);
|
|
349
|
-
i0.ɵɵadvance(6);
|
|
350
|
-
i0.ɵɵrepeater(ctx_r2.lifecycleSteps);
|
|
351
|
-
i0.ɵɵadvance(16);
|
|
352
|
-
i0.ɵɵtextInterpolate(ctx_r2.trainAuc);
|
|
353
|
-
i0.ɵɵadvance(7);
|
|
354
|
-
i0.ɵɵtextInterpolate(ctx_r2.selected.holdoutAuc);
|
|
355
|
-
i0.ɵɵadvance(3);
|
|
356
|
-
i0.ɵɵconditional(ctx_r2.secondaryMetrics.length > 0 ? 55 : 56);
|
|
357
|
-
i0.ɵɵadvance(2);
|
|
358
|
-
i0.ɵɵconditional(ctx_r2.gapText ? 57 : -1);
|
|
359
|
-
i0.ɵɵadvance(6);
|
|
360
|
-
i0.ɵɵtextInterpolate(ctx_r2.importanceCaption);
|
|
361
|
-
i0.ɵɵadvance(2);
|
|
362
|
-
i0.ɵɵconditional(ctx_r2.importance.length > 0 ? 65 : 66);
|
|
363
|
-
i0.ɵɵadvance(2);
|
|
364
|
-
i0.ɵɵconditional(ctx_r2.importance.length > 0 ? 67 : -1);
|
|
365
|
-
i0.ɵɵadvance(4);
|
|
366
|
-
i0.ɵɵtextInterpolate(ctx_r2.actionHint);
|
|
120
|
+
i0.ɵɵconditional(ctx_r2.isListCollapsed ? 1 : -1);
|
|
367
121
|
i0.ɵɵadvance();
|
|
368
|
-
i0.ɵɵ
|
|
122
|
+
i0.ɵɵproperty("gutterSize", 6);
|
|
369
123
|
i0.ɵɵadvance();
|
|
370
|
-
i0.ɵɵconditional(ctx_r2.
|
|
124
|
+
i0.ɵɵconditional(!ctx_r2.isListCollapsed ? 3 : -1);
|
|
371
125
|
i0.ɵɵadvance();
|
|
372
|
-
i0.ɵɵ
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const _r13 = i0.ɵɵgetCurrentView();
|
|
376
|
-
i0.ɵɵelementStart(0, "ps-confirm-modal", 64);
|
|
377
|
-
i0.ɵɵlistener("confirmed", function PSRegistryComponent_Conditional_3_Template_ps_confirm_modal_confirmed_0_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.confirmPromote($event)); })("cancelled", function PSRegistryComponent_Conditional_3_Template_ps_confirm_modal_cancelled_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.cancelPromote()); });
|
|
378
|
-
i0.ɵɵelement(1, "div", 65);
|
|
379
|
-
i0.ɵɵelementEnd();
|
|
380
|
-
} if (rf & 2) {
|
|
381
|
-
const ctx_r2 = i0.ɵɵnextContext();
|
|
382
|
-
i0.ɵɵproperty("title", ctx_r2.pendingTitle)("icon", ctx_r2.pendingIcon)("confirmIcon", ctx_r2.pendingIcon)("confirmLabel", ctx_r2.pendingConfirmLabel)("variant", ctx_r2.pendingVariant)("showReason", ctx_r2.pending.leakageFlagged || ctx_r2.pending.targetStatus === "Archived")("reasonRequired", ctx_r2.pending.leakageFlagged)("reasonLabel", ctx_r2.pending.leakageFlagged ? "Leakage sign-off reason" : "Reason (optional)")("reasonPlaceholder", ctx_r2.pending.leakageFlagged ? "Explain why this model is safe to publish despite the leakage flag\u2026" : "Add an optional note for the record\u2026")("busy", ctx_r2.busy);
|
|
383
|
-
i0.ɵɵadvance();
|
|
384
|
-
i0.ɵɵproperty("innerHTML", ctx_r2.pendingMessage, i0.ɵɵsanitizeHtml);
|
|
126
|
+
i0.ɵɵproperty("size", ctx_r2.isListCollapsed ? 100 : ctx_r2.detailSizePct)("minSize", 50);
|
|
127
|
+
i0.ɵɵadvance(2);
|
|
128
|
+
i0.ɵɵconditional(ctx_r2.selectedEntity ? 6 : 7);
|
|
385
129
|
} }
|
|
386
130
|
/**
|
|
387
|
-
* Model Registry panel: master list of trained ML Models + a rich detail pane
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* - **Feature importance** is parsed from `model.FeatureImportance` (sorted by |value|, top 6, bars).
|
|
392
|
-
* - **Performance metrics** come from `model.Metrics` (train) + `model.HoldoutMetrics` (holdout) —
|
|
393
|
-
* only the metrics actually recorded are shown.
|
|
394
|
-
* - **Promote / Archive** call the {@link PredictiveStudioPromoteModelOperation} Remote Op behind a
|
|
395
|
-
* confirmation modal. On a leakage-flagged model, the modal surfaces the sign-off requirement and
|
|
396
|
-
* captures a required reason; on success the engine refreshes and the list re-renders reactively.
|
|
131
|
+
* Model Registry panel: master list of trained ML Models + a rich detail pane rendered via the unified
|
|
132
|
+
* {@link PSModelDetailComponent}. Includes resizable split layout with user preference persistence via
|
|
133
|
+
* UserInfoEngine and collapse/expand support.
|
|
397
134
|
*
|
|
398
135
|
* 100% entity-agnostic — model names derive from the producing pipeline, never any business entity.
|
|
399
136
|
*/
|
|
@@ -407,15 +144,70 @@ export class PSRegistryComponent {
|
|
|
407
144
|
notifications = inject(MJNotificationService);
|
|
408
145
|
models = [];
|
|
409
146
|
selectedId = '';
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
147
|
+
// Resizable split state
|
|
148
|
+
listSizePct = 28;
|
|
149
|
+
detailSizePct = 72;
|
|
150
|
+
isListCollapsed = false;
|
|
151
|
+
_initialModelId;
|
|
152
|
+
set initialModelId(id) {
|
|
153
|
+
this._initialModelId = id;
|
|
154
|
+
if (id) {
|
|
155
|
+
if (this.models.length === 0 || !this.models.some((m) => UUIDsEqual(m.id, id))) {
|
|
156
|
+
this.buildModels();
|
|
157
|
+
}
|
|
158
|
+
if (this.models.some((m) => UUIDsEqual(m.id, id))) {
|
|
159
|
+
this.selectedId = id;
|
|
160
|
+
}
|
|
161
|
+
this.cdr.detectChanges();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
get initialModelId() {
|
|
165
|
+
return this._initialModelId;
|
|
166
|
+
}
|
|
416
167
|
ngOnInit() {
|
|
168
|
+
const saved = UserInfoEngine.Instance.GetSetting('mj.predictiveStudio.registry.layout');
|
|
169
|
+
if (saved) {
|
|
170
|
+
try {
|
|
171
|
+
const prefs = JSON.parse(saved);
|
|
172
|
+
if (typeof prefs.listSizePct === 'number')
|
|
173
|
+
this.listSizePct = prefs.listSizePct;
|
|
174
|
+
if (typeof prefs.detailSizePct === 'number')
|
|
175
|
+
this.detailSizePct = prefs.detailSizePct;
|
|
176
|
+
if (typeof prefs.isListCollapsed === 'boolean')
|
|
177
|
+
this.isListCollapsed = prefs.isListCollapsed;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// ignore malformed pref
|
|
181
|
+
}
|
|
182
|
+
}
|
|
417
183
|
this.buildModels();
|
|
418
|
-
this.
|
|
184
|
+
if (this._initialModelId && this.models.some((m) => UUIDsEqual(m.id, this._initialModelId))) {
|
|
185
|
+
this.selectedId = this._initialModelId;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
this.selectedId = this.models[0]?.id ?? '';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// ---- splitter resizing & persistence ----
|
|
192
|
+
onSplitDragEnd(sizes) {
|
|
193
|
+
if (Array.isArray(sizes) && sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') {
|
|
194
|
+
this.listSizePct = Math.round(sizes[0]);
|
|
195
|
+
this.detailSizePct = Math.round(sizes[1]);
|
|
196
|
+
this.saveLayoutPrefs();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
toggleList() {
|
|
200
|
+
this.isListCollapsed = !this.isListCollapsed;
|
|
201
|
+
this.saveLayoutPrefs();
|
|
202
|
+
this.cdr.markForCheck();
|
|
203
|
+
}
|
|
204
|
+
saveLayoutPrefs() {
|
|
205
|
+
const prefs = {
|
|
206
|
+
listSizePct: this.listSizePct,
|
|
207
|
+
detailSizePct: this.detailSizePct,
|
|
208
|
+
isListCollapsed: this.isListCollapsed,
|
|
209
|
+
};
|
|
210
|
+
UserInfoEngine.Instance.SetSettingDebounced('mj.predictiveStudio.registry.layout', JSON.stringify(prefs));
|
|
419
211
|
}
|
|
420
212
|
// ---- selection + master list ----
|
|
421
213
|
select(id) {
|
|
@@ -425,61 +217,8 @@ export class PSRegistryComponent {
|
|
|
425
217
|
return this.models.find((m) => m.id === this.selectedId) ?? this.models[0] ?? this.placeholder();
|
|
426
218
|
}
|
|
427
219
|
get selectedEntity() {
|
|
428
|
-
return this.engine?.Models
|
|
429
|
-
}
|
|
430
|
-
// ---- live detail: feature importance + metrics ----
|
|
431
|
-
/** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON, with humanized display names. */
|
|
432
|
-
get importance() {
|
|
433
|
-
return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6).map((b) => ({
|
|
434
|
-
...b,
|
|
435
|
-
name: humanizeFeatureName(b.name),
|
|
436
|
-
}));
|
|
437
|
-
}
|
|
438
|
-
get importanceCaption() {
|
|
439
|
-
const n = this.importance.length;
|
|
440
|
-
return n === 0 ? 'no data' : `normalized · top ${n}`;
|
|
441
|
-
}
|
|
442
|
-
get topFeatureName() {
|
|
443
|
-
return this.importance[0]?.name ?? '—';
|
|
444
|
-
}
|
|
445
|
-
get topFeatureValue() {
|
|
446
|
-
return this.importance[0]?.value ?? '—';
|
|
447
|
-
}
|
|
448
|
-
/** True when a single feature's |importance| meets/exceeds the dominance threshold. */
|
|
449
|
-
get leakageFlagged() {
|
|
450
|
-
const max = maxFeatureImportance(this.selectedEntity?.FeatureImportance);
|
|
451
|
-
return max != null && max >= PS_FEATURE_DOMINANCE_THRESHOLD;
|
|
452
|
-
}
|
|
453
|
-
/** Holdout AUC hero number, formatted, or '—'. */
|
|
454
|
-
get trainAuc() {
|
|
455
|
-
const train = parseMetrics(this.selectedEntity?.Metrics).AUC;
|
|
456
|
-
return train != null ? train.toFixed(3) : '—';
|
|
220
|
+
return this.engine?.Models?.find((m) => UUIDsEqual(m.ID, this.selectedId));
|
|
457
221
|
}
|
|
458
|
-
/** Secondary metric tiles (precision/recall/F1/log-loss/…) — only those actually recorded. */
|
|
459
|
-
get secondaryMetrics() {
|
|
460
|
-
const e = this.selectedEntity;
|
|
461
|
-
if (!e)
|
|
462
|
-
return [];
|
|
463
|
-
// Prefer holdout metrics for the honest secondary numbers; fall back to training metrics.
|
|
464
|
-
const holdout = metricsToDisplay(parseMetrics(e.HoldoutMetrics));
|
|
465
|
-
return holdout.length > 0 ? holdout : metricsToDisplay(parseMetrics(e.Metrics));
|
|
466
|
-
}
|
|
467
|
-
/** The formatted train–holdout overfit gap, or '' when not computable. */
|
|
468
|
-
get gapText() {
|
|
469
|
-
const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });
|
|
470
|
-
return gap == null ? '' : Math.abs(gap).toFixed(3);
|
|
471
|
-
}
|
|
472
|
-
get gapVerdict() {
|
|
473
|
-
const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });
|
|
474
|
-
if (gap == null)
|
|
475
|
-
return '';
|
|
476
|
-
return gap > 0.1 ? 'is wide — watch for overfitting' : 'is within tolerance — no overfitting flag';
|
|
477
|
-
}
|
|
478
|
-
get targetSuffix() {
|
|
479
|
-
const target = this.selectedEntity?.TargetVariable;
|
|
480
|
-
return target ? ` · predicts ${target}` : '';
|
|
481
|
-
}
|
|
482
|
-
// ---- lifecycle stepper ----
|
|
483
222
|
statusClass(status) {
|
|
484
223
|
switch (status) {
|
|
485
224
|
case 'Published': return 'pub';
|
|
@@ -489,122 +228,9 @@ export class PSRegistryComponent {
|
|
|
489
228
|
default: return 'dr';
|
|
490
229
|
}
|
|
491
230
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
case 'Validated': return 'blue';
|
|
496
|
-
case 'Archived': return 'gray';
|
|
497
|
-
default: return 'amber';
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
/** Stepper state for a lifecycle step relative to the selected model's status. */
|
|
501
|
-
stepState(step) {
|
|
502
|
-
const order = PS_LIFECYCLE_STEPS.indexOf(this.selected.status);
|
|
503
|
-
const idx = PS_LIFECYCLE_STEPS.indexOf(step);
|
|
504
|
-
if (order < 0)
|
|
505
|
-
return 'todo';
|
|
506
|
-
if (idx < order)
|
|
507
|
-
return 'done';
|
|
508
|
-
if (idx === order)
|
|
509
|
-
return 'curr';
|
|
510
|
-
return 'todo';
|
|
511
|
-
}
|
|
512
|
-
// ---- promote/archive availability ----
|
|
513
|
-
get canValidate() {
|
|
514
|
-
return this.selected.status === 'Draft';
|
|
515
|
-
}
|
|
516
|
-
get canPublish() {
|
|
517
|
-
return this.selected.status === 'Validated' || this.selected.status === 'Draft';
|
|
518
|
-
}
|
|
519
|
-
get canArchive() {
|
|
520
|
-
return this.selected.status === 'Published' || this.selected.status === 'Validated';
|
|
521
|
-
}
|
|
522
|
-
get actionHint() {
|
|
523
|
-
switch (this.selected.status) {
|
|
524
|
-
case 'Published': return 'This model is live. Archiving detaches its scoring bindings.';
|
|
525
|
-
case 'Validated': return 'Validated and ready to publish — publishing makes it available for scoring.';
|
|
526
|
-
case 'Draft': return 'Newly registered. Validate it before publishing to production.';
|
|
527
|
-
case 'Archived': return 'Archived — superseded by a newer model.';
|
|
528
|
-
default: return '';
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
// ---- promote/archive flow (Remote Op) ----
|
|
532
|
-
/** Open the confirmation modal for a lifecycle transition. */
|
|
533
|
-
requestPromote(targetStatus) {
|
|
534
|
-
const entity = this.selectedEntity;
|
|
535
|
-
if (!entity)
|
|
536
|
-
return;
|
|
537
|
-
this.pending = {
|
|
538
|
-
modelId: entity.ID,
|
|
539
|
-
modelName: this.selected.name,
|
|
540
|
-
targetStatus,
|
|
541
|
-
leakageFlagged: targetStatus === 'Published' && this.leakageFlagged,
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
cancelPromote() {
|
|
545
|
-
if (this.busy)
|
|
546
|
-
return;
|
|
547
|
-
this.pending = null;
|
|
548
|
-
}
|
|
549
|
-
get pendingTitle() {
|
|
550
|
-
if (!this.pending)
|
|
551
|
-
return '';
|
|
552
|
-
return this.pending.targetStatus === 'Archived' ? 'Archive model' : `Promote to ${this.pending.targetStatus}`;
|
|
553
|
-
}
|
|
554
|
-
get pendingIcon() {
|
|
555
|
-
if (!this.pending)
|
|
556
|
-
return 'fa-solid fa-check';
|
|
557
|
-
return this.pending.targetStatus === 'Archived' ? 'fa-solid fa-box-archive' : 'fa-solid fa-arrow-up';
|
|
558
|
-
}
|
|
559
|
-
get pendingConfirmLabel() {
|
|
560
|
-
if (!this.pending)
|
|
561
|
-
return 'Confirm';
|
|
562
|
-
return this.pending.targetStatus === 'Archived' ? 'Archive' : `Promote to ${this.pending.targetStatus}`;
|
|
563
|
-
}
|
|
564
|
-
get pendingVariant() {
|
|
565
|
-
if (!this.pending)
|
|
566
|
-
return 'info';
|
|
567
|
-
if (this.pending.targetStatus === 'Archived')
|
|
568
|
-
return 'warn';
|
|
569
|
-
return this.pending.leakageFlagged ? 'warn' : 'info';
|
|
570
|
-
}
|
|
571
|
-
get pendingMessage() {
|
|
572
|
-
if (!this.pending)
|
|
573
|
-
return '';
|
|
574
|
-
const name = escapeHtml(this.pending.modelName);
|
|
575
|
-
if (this.pending.targetStatus === 'Archived') {
|
|
576
|
-
return `Archive <strong>${name}</strong>? This detaches any active scoring bindings and removes it from production scoring. The model artifact remains immutable and recoverable.`;
|
|
577
|
-
}
|
|
578
|
-
if (this.pending.leakageFlagged) {
|
|
579
|
-
return `<strong>${name}</strong> is flagged for possible target leakage. Publishing requires an explicit sign-off — confirm you understand the risk and provide a reason below.`;
|
|
580
|
-
}
|
|
581
|
-
return `Promote <strong>${name}</strong> to <strong>${this.pending.targetStatus}</strong>? This changes only the lifecycle state — the trained weights never change.`;
|
|
582
|
-
}
|
|
583
|
-
/** Run the promote Remote Op, then refresh the engine + close on success. */
|
|
584
|
-
async confirmPromote(reason) {
|
|
585
|
-
if (!this.pending || this.busy)
|
|
586
|
-
return;
|
|
587
|
-
this.busy = true;
|
|
588
|
-
const { modelId, modelName, targetStatus, leakageFlagged } = this.pending;
|
|
589
|
-
try {
|
|
590
|
-
const op = new PredictiveStudioPromoteModelOperation();
|
|
591
|
-
const result = await op.Execute({ modelId, targetStatus, signOff: leakageFlagged ? true : undefined, reason: reason || undefined }, { provider: this.provider ?? undefined, user: this.currentUser ?? undefined });
|
|
592
|
-
if (result.Success && result.Output?.promoted) {
|
|
593
|
-
this.notifications.CreateSimpleNotification(`${modelName} → ${result.Output.status}`, 'success', 3500);
|
|
594
|
-
await this.refreshAfterMutation();
|
|
595
|
-
this.pending = null;
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
this.notifications.CreateSimpleNotification(result.ErrorMessage || `Could not promote ${modelName} (status: ${result.Output?.status ?? 'unchanged'}).`, 'error', 5000);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
catch (e) {
|
|
602
|
-
this.notifications.CreateSimpleNotification(`Promotion failed: ${e instanceof Error ? e.message : String(e)}`, 'error', 5000);
|
|
603
|
-
}
|
|
604
|
-
finally {
|
|
605
|
-
this.busy = false;
|
|
606
|
-
this.cdr.detectChanges();
|
|
607
|
-
}
|
|
231
|
+
// ---- lifecycle change handling ----
|
|
232
|
+
async onModelStatusChanged(evt) {
|
|
233
|
+
await this.refreshAfterMutation();
|
|
608
234
|
}
|
|
609
235
|
/** Force-refresh the engine's cached models, then rebuild the master list. */
|
|
610
236
|
async refreshAfterMutation() {
|
|
@@ -622,35 +248,36 @@ export class PSRegistryComponent {
|
|
|
622
248
|
}
|
|
623
249
|
toVM(m) {
|
|
624
250
|
const holdout = primaryAuc(m);
|
|
251
|
+
const score = primaryModelScore(m);
|
|
252
|
+
const formattedScore = score != null ? formatMetricValue(score.key, score.value) : (holdout != null ? holdout.toFixed(3) : '—');
|
|
625
253
|
return {
|
|
626
254
|
id: m.ID,
|
|
627
|
-
name: this.engine.ModelDisplayName(m),
|
|
255
|
+
name: this.engine?.ModelDisplayName ? this.engine.ModelDisplayName(m) : (m.Pipeline || `Model v${m.Version}`),
|
|
628
256
|
version: m.Version,
|
|
629
|
-
algorithm: this.engine.AlgorithmName(m.AlgorithmID),
|
|
630
|
-
holdoutAuc: holdout != null ? holdout.toFixed(3) :
|
|
257
|
+
algorithm: this.engine?.AlgorithmName ? this.engine.AlgorithmName(m.AlgorithmID) : (m.Algorithm || 'Algorithm'),
|
|
258
|
+
holdoutAuc: holdout != null ? holdout.toFixed(3) : formattedScore,
|
|
259
|
+
holdoutScore: formattedScore,
|
|
260
|
+
scoreLabel: score?.label ?? 'AUC',
|
|
631
261
|
status: m.Status,
|
|
632
262
|
iconClass: 'xgb',
|
|
633
263
|
};
|
|
634
264
|
}
|
|
635
265
|
placeholder() {
|
|
636
|
-
return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', status: 'Draft', iconClass: 'xgb' };
|
|
266
|
+
return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', holdoutScore: '—', scoreLabel: 'AUC', status: 'Draft', iconClass: 'xgb' };
|
|
637
267
|
}
|
|
638
268
|
static ɵfac = function PSRegistryComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSRegistryComponent)(); };
|
|
639
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRegistryComponent, selectors: [["ps-registry"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser" }, decls:
|
|
269
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRegistryComponent, selectors: [["ps-registry"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser", initialModelId: "initialModelId" }, decls: 3, vars: 1, consts: [["data-testid", "ps-registry-panel", 1, "ps-panel", "ps-registry"], ["data-testid", "ps-registry-empty", 1, "ps-empty"], [1, "reg-layout", 3, "list-collapsed"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-cubes"], [1, "reg-layout"], ["role", "region", "aria-label", "Model registry list (collapsed)", 1, "reg-collapsed-strip"], ["direction", "horizontal", "unit", "percent", 1, "reg-splitter", 3, "dragEnd", "gutterSize"], [3, "size", "minSize", "maxSize"], [3, "size", "minSize"], ["data-testid", "ps-registry-detail", 1, "ps-col", "detail"], [3, "model", "displayName", "provider", "currentUser"], [1, "ps-empty", 2, "padding", "40px 20px"], ["type", "button", "aria-label", "Expand models list", "title", "Expand list", 1, "reg-rail-collapse", 3, "click"], [1, "fa-solid", "fa-chevron-right"], [1, "reg-collapsed-strip-label"], ["data-testid", "ps-registry-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [2, "display", "flex", "gap", "6px", "align-items", "center"], [1, "ps-badge", "gray"], ["type", "button", "title", "Collapse list", "aria-label", "Collapse list", 1, "reg-collapse-btn", 3, "click"], [1, "fa-solid", "fa-chevron-left"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "sel", "arc"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "click"], [1, "ico"], [1, "fa-solid", "fa-cube"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "auc"], [1, "v"], [1, "st"], [3, "statusChanged", "model", "displayName", "provider", "currentUser"], [1, "ps-muted"]], template: function PSRegistryComponent_Template(rf, ctx) { if (rf & 1) {
|
|
640
270
|
i0.ɵɵelementStart(0, "div", 0);
|
|
641
|
-
i0.ɵɵconditionalCreate(1, PSRegistryComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSRegistryComponent_Conditional_2_Template,
|
|
642
|
-
i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_3_Template, 2, 11, "ps-confirm-modal", 3);
|
|
271
|
+
i0.ɵɵconditionalCreate(1, PSRegistryComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSRegistryComponent_Conditional_2_Template, 8, 8, "div", 2);
|
|
643
272
|
i0.ɵɵelementEnd();
|
|
644
273
|
} if (rf & 2) {
|
|
645
274
|
i0.ɵɵadvance();
|
|
646
275
|
i0.ɵɵconditional(ctx.models.length === 0 ? 1 : 2);
|
|
647
|
-
i0.ɵɵadvance(2);
|
|
648
|
-
i0.ɵɵconditional(ctx.pending ? 3 : -1);
|
|
649
|
-
} }, dependencies: [CommonModule, MJButtonDirective, PSConfirmModalComponent], 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", "/* The panel fills its section; the list and detail columns each scroll on their own. */\n.ps-registry { display: flex; flex-direction: column; height: 100%; min-height: 0; }\n.ps-registry .md-layout {\n flex: 1; min-height: 0;\n display: grid; grid-template-columns: 300px 1fr; grid-template-rows: minmax(0, 1fr); gap: 18px;\n}\n@media (max-width: 1080px) {\n .ps-registry .md-layout { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1.4fr); }\n}\n\n/* model list \u2014 its own scroll column */\n.ps-registry .mlist { min-height: 0; overflow-y: auto; align-self: start; max-height: 100%; }\n\n/* detail \u2014 its own scroll column, independent of the list. NOTE: .detail is also a\n .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. The child rule is load-bearing, not decorative. */\n.ps-registry .detail { min-height: 0; overflow-y: auto; padding-right: 2px; }\n.ps-registry .detail > * { flex: 0 0 auto; }\n.ps-registry .mrow {\n display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 8px; cursor: pointer; transition: background .12s;\n}\n.ps-registry .mrow:hover { background: var(--mj-bg-surface-hover); }\n.ps-registry .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); box-shadow: inset 3px 0 0 var(--mj-brand-primary); }\n.ps-registry .mrow.arc { opacity: .72; }\n.ps-registry .mrow .ico {\n width: 30px; height: 30px; border-radius: 7px; display: grid; place-items: center; color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb { background: var(--ps-node-feature); }\n.ps-registry .mrow .ico.lgbm { background: var(--ps-node-algorithm); }\n.ps-registry .mrow .ico.rf { background: var(--ps-node-source); }\n.ps-registry .mrow .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.ps-registry .mrow .auc { text-align: right; flex: 0 0 auto; }\n.ps-registry .mrow .auc .v { font-weight: 700; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-registry .mrow .auc .st { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }\n.ps-registry .mrow .auc .st.pub { color: var(--mj-status-success-text); }\n.ps-registry .mrow .auc .st.val { color: var(--mj-status-info-text); }\n.ps-registry .mrow .auc .st.dr { color: var(--mj-text-muted); }\n.ps-registry .mrow .auc .st.arc { color: var(--mj-text-disabled); }\n\n/* detail */\n.ps-registry .dh { display: flex; align-items: center; gap: 14px; }\n.ps-registry .big-ico {\n width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;\n background: var(--ps-node-feature); color: var(--mj-text-inverse); font-size: 20px; flex: 0 0 auto;\n}\n.ps-registry .dh h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 8px; }\n.ps-registry .dh .sub { margin-top: 2px; }\n\n.ps-registry .metric-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ps-registry .mtile { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 14px; }\n.ps-registry .mtile .v { font-size: 28px; font-weight: 720; color: var(--mj-text-primary); letter-spacing: -.02em; }\n.ps-registry .mtile.honest { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .mtile.honest .v { color: var(--mj-brand-primary); }\n.ps-registry .stat-bar { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }\n.ps-registry .stat-bar .b { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border: 1px solid var(--mj-border-subtle); border-radius: 8px; flex: 1; min-width: 90px; }\n.ps-registry .stat-bar .b strong { color: var(--mj-text-primary); font-size: 15px; }\n\n.ps-registry .gate { align-items: flex-start; }\n.ps-registry .gate i { font-size: 20px; }\n\n.ps-registry .lineage { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.ps-registry .ln-card { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 10px 12px; min-width: 120px; }\n.ps-registry .ln-card.cur { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .ln-card .t { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-registry .ln-card .n { font-weight: 650; color: var(--mj-text-primary); font-size: 13px; margin: 2px 0; }\n.ps-registry .ln-arrow { color: var(--mj-text-disabled); }\n"], encapsulation: 2 });
|
|
276
|
+
} }, dependencies: [CommonModule, AngularSplitModule, i1.SplitComponent, i1.SplitAreaComponent, PSModelDetailComponent], 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", "/* The panel fills its section; the list and detail columns each scroll on their own. */\n.ps-registry {\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 Resizable Splitter Layout */\n.ps-registry .reg-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n.ps-registry .reg-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.ps-registry .reg-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-registry .reg-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-registry .reg-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-registry .reg-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.ps-registry .reg-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-registry .reg-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/* model list \u2014 its own scroll column */\n.ps-registry .mlist {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n border-radius: 0;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n}\n\n/* detail \u2014 its own scroll column */\n.ps-registry .detail {\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 20px;\n}\n\n.ps-registry .mrow {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.12s;\n}\n.ps-registry .mrow:hover {\n background: var(--mj-bg-surface-hover);\n}\n.ps-registry .mrow.sel {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: inset 3px 0 0 var(--mj-brand-primary);\n}\n.ps-registry .mrow.arc {\n opacity: 0.72;\n}\n.ps-registry .mrow .ico {\n width: 30px;\n height: 30px;\n border-radius: 7px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 13px;\n flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb {\n background: var(--ps-node-feature);\n}\n.ps-registry .mrow .ico.lgbm {\n background: var(--ps-node-algorithm);\n}\n.ps-registry .mrow .ico.rf {\n background: var(--ps-node-source);\n}\n.ps-registry .mrow .nm {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ps-registry .mrow .auc {\n text-align: right;\n flex: 0 0 auto;\n}\n.ps-registry .mrow .auc .v {\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n.ps-registry .mrow .auc .st {\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n.ps-registry .mrow .auc .st.pub {\n color: var(--mj-status-success-text);\n}\n.ps-registry .mrow .auc .st.val {\n color: var(--mj-status-info-text);\n}\n.ps-registry .mrow .auc .st.dr {\n color: var(--mj-text-muted);\n}\n.ps-registry .mrow .auc .st.arc {\n color: var(--mj-text-disabled);\n}\n"], encapsulation: 2 });
|
|
650
277
|
}
|
|
651
278
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSRegistryComponent, [{
|
|
652
279
|
type: Component,
|
|
653
|
-
args: [{ standalone: true, selector: 'ps-registry', imports: [CommonModule,
|
|
280
|
+
args: [{ standalone: true, selector: 'ps-registry', imports: [CommonModule, AngularSplitModule, PSModelDetailComponent], encapsulation: ViewEncapsulation.None, template: `
|
|
654
281
|
<div class="ps-panel ps-registry" data-testid="ps-registry-panel">
|
|
655
282
|
@if (models.length === 0) {
|
|
656
283
|
<div class="ps-empty" data-testid="ps-registry-empty">
|
|
@@ -659,174 +286,80 @@ export class PSRegistryComponent {
|
|
|
659
286
|
<p>Train a pipeline to register your first immutable model. Each successful training run produces a versioned model you can validate, publish, and score with here.</p>
|
|
660
287
|
</div>
|
|
661
288
|
} @else {
|
|
662
|
-
<div class="
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
<div class="ico" [class]="m.iconClass"><i class="fa-solid fa-cube"></i></div>
|
|
670
|
-
<div style="flex:1;min-width:0">
|
|
671
|
-
<div class="nm">{{ m.name }}</div>
|
|
672
|
-
<div class="ln2 ps-muted ps-small">v{{ m.version }} · {{ m.algorithm }}</div>
|
|
673
|
-
</div>
|
|
674
|
-
<div class="auc">
|
|
675
|
-
<div class="v">{{ m.holdoutAuc }}</div>
|
|
676
|
-
<div class="st" [class]="statusClass(m.status)">{{ m.status }}</div>
|
|
677
|
-
</div>
|
|
678
|
-
</div>
|
|
679
|
-
}
|
|
289
|
+
<div class="reg-layout" [class.list-collapsed]="isListCollapsed">
|
|
290
|
+
@if (isListCollapsed) {
|
|
291
|
+
<div class="reg-collapsed-strip" role="region" aria-label="Model registry list (collapsed)">
|
|
292
|
+
<button class="reg-rail-collapse" type="button" (click)="toggleList()" aria-label="Expand models list" title="Expand list">
|
|
293
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
294
|
+
</button>
|
|
295
|
+
<div class="reg-collapsed-strip-label"><i class="fa-solid fa-cubes"></i></div>
|
|
680
296
|
</div>
|
|
681
|
-
|
|
297
|
+
}
|
|
682
298
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
<div class="
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
<!-- lifecycle -->
|
|
697
|
-
<div class="ps-card">
|
|
698
|
-
<div class="ps-card-body">
|
|
699
|
-
<div class="ps-section-title">Lifecycle</div>
|
|
700
|
-
<div class="ps-stepper">
|
|
701
|
-
@for (step of lifecycleSteps; track step; let last = $last) {
|
|
702
|
-
<div class="ps-step" [class.done]="stepState(step) === 'done'" [class.curr]="stepState(step) === 'curr'">
|
|
703
|
-
<span class="pip">@if (stepState(step) === 'done') { <i class="fa-solid fa-check"></i> } @else { {{ $index + 1 }} }</span>
|
|
704
|
-
{{ step }}
|
|
299
|
+
<as-split direction="horizontal" class="reg-splitter" unit="percent" [gutterSize]="6" (dragEnd)="onSplitDragEnd($event.sizes)">
|
|
300
|
+
@if (!isListCollapsed) {
|
|
301
|
+
<as-split-area [size]="listSizePct" [minSize]="18" [maxSize]="50">
|
|
302
|
+
<!-- master list -->
|
|
303
|
+
<div class="ps-card mlist" data-testid="ps-registry-list">
|
|
304
|
+
<div class="ps-card-head">
|
|
305
|
+
<h3>ML Models</h3>
|
|
306
|
+
<div style="display:flex;gap:6px;align-items:center">
|
|
307
|
+
<span class="ps-badge gray">{{ models.length }}</span>
|
|
308
|
+
<button class="reg-collapse-btn" type="button" (click)="toggleList()" title="Collapse list" aria-label="Collapse list">
|
|
309
|
+
<i class="fa-solid fa-chevron-left"></i>
|
|
310
|
+
</button>
|
|
705
311
|
</div>
|
|
706
|
-
@if (!last) { <span class="ln"></span> }
|
|
707
|
-
}
|
|
708
|
-
</div>
|
|
709
|
-
<div class="ps-small ps-muted" style="margin-top:10px">
|
|
710
|
-
Models are immutable once registered; promotion only changes lifecycle state, never weights.
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
</div>
|
|
714
|
-
|
|
715
|
-
<!-- performance -->
|
|
716
|
-
<div class="ps-card">
|
|
717
|
-
<div class="ps-card-head"><h3>Performance</h3><span class="ps-muted ps-small">Holdout = held-out test fold, never seen in training</span></div>
|
|
718
|
-
<div class="ps-card-body">
|
|
719
|
-
<div class="metric-pair">
|
|
720
|
-
<div class="mtile"><div class="ps-section-title">Train AUC</div><div class="v">{{ trainAuc }}</div><div class="ps-muted ps-small">in-sample · optimistic</div></div>
|
|
721
|
-
<div class="mtile honest"><div class="ps-section-title">Holdout AUC</div><div class="v">{{ selected.holdoutAuc }}</div><div class="ps-muted ps-small">out-of-sample · the honest number</div></div>
|
|
722
|
-
</div>
|
|
723
|
-
@if (secondaryMetrics.length > 0) {
|
|
724
|
-
<div class="stat-bar">
|
|
725
|
-
@for (s of secondaryMetrics; track s.key) {
|
|
726
|
-
<div class="b"><span class="ps-muted ps-small">{{ s.label }}</span><strong>{{ s.value }}</strong></div>
|
|
727
|
-
}
|
|
728
312
|
</div>
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
313
|
+
<div class="ps-card-body" style="padding:8px">
|
|
314
|
+
@for (m of models; track m.id) {
|
|
315
|
+
<div class="mrow" data-testid="ps-registry-row" [class.sel]="m.id === selectedId" [class.arc]="m.status === 'Archived'" (click)="select(m.id)">
|
|
316
|
+
<div class="ico" [class]="m.iconClass"><i class="fa-solid fa-cube"></i></div>
|
|
317
|
+
<div style="flex:1;min-width:0">
|
|
318
|
+
<div class="nm">{{ m.name }}</div>
|
|
319
|
+
<div class="ln2 ps-muted ps-small">v{{ m.version }} • {{ m.algorithm }}</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="auc">
|
|
322
|
+
<div class="v">{{ m.holdoutScore }}</div>
|
|
323
|
+
<div class="st" [class]="statusClass(m.status)">{{ m.status }}</div>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
}
|
|
735
327
|
</div>
|
|
736
|
-
}
|
|
737
|
-
</div>
|
|
738
|
-
</div>
|
|
739
|
-
|
|
740
|
-
<!-- feature importance -->
|
|
741
|
-
<div class="ps-card">
|
|
742
|
-
<div class="ps-card-head"><h3>Feature Importance</h3><span class="ps-muted ps-small">{{ importanceCaption }}</span></div>
|
|
743
|
-
<div class="ps-card-body">
|
|
744
|
-
@if (importance.length > 0) {
|
|
745
|
-
@for (f of importance; track f.name) {
|
|
746
|
-
<div class="ps-fbar">
|
|
747
|
-
<span class="name ps-small" [title]="f.name">{{ f.name }}</span>
|
|
748
|
-
<div class="track"><span [class.warn]="f.warning" [style.width.%]="f.pct"></span></div>
|
|
749
|
-
<span class="ps-mono">{{ f.value }}</span>
|
|
750
|
-
</div>
|
|
751
|
-
}
|
|
752
|
-
} @else {
|
|
753
|
-
<div class="ps-small ps-muted">No feature-importance data was recorded for this model.</div>
|
|
754
|
-
}
|
|
755
|
-
</div>
|
|
756
|
-
</div>
|
|
757
|
-
|
|
758
|
-
<!-- leakage sign-off gate -->
|
|
759
|
-
@if (importance.length > 0) {
|
|
760
|
-
<div class="ps-callout gate" [class.success]="!leakageFlagged" [class.warn]="leakageFlagged">
|
|
761
|
-
<i [class]="leakageFlagged ? 'fa-solid fa-triangle-exclamation' : 'fa-solid fa-shield-halved'"></i>
|
|
762
|
-
<div>
|
|
763
|
-
@if (leakageFlagged) {
|
|
764
|
-
<strong>Leakage flag raised.</strong> A single feature dominates — top importance is
|
|
765
|
-
<strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, at/above the
|
|
766
|
-
<strong>{{ dominanceThreshold }}</strong> dominance threshold.
|
|
767
|
-
<div class="ps-small" style="margin-top:6px"><i class="fa-solid fa-pen"></i> Sign-off with a reason is required to publish this model.</div>
|
|
768
|
-
} @else {
|
|
769
|
-
<strong>Leakage gate clear.</strong> No single feature dominates — top importance is
|
|
770
|
-
<strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, below the
|
|
771
|
-
<strong>{{ dominanceThreshold }}</strong> dominance threshold.
|
|
772
|
-
<div class="ps-small" style="margin-top:6px"><i class="fa-solid fa-check"></i> Sign-off required before any Validated → Published promotion.</div>
|
|
773
|
-
}
|
|
774
328
|
</div>
|
|
775
|
-
</
|
|
329
|
+
</as-split-area>
|
|
776
330
|
}
|
|
777
331
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
<div class="ps-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
332
|
+
<as-split-area [size]="isListCollapsed ? 100 : detailSizePct" [minSize]="50">
|
|
333
|
+
<!-- detail via unified PSModelDetailComponent -->
|
|
334
|
+
<div class="ps-col detail" data-testid="ps-registry-detail">
|
|
335
|
+
@if (selectedEntity) {
|
|
336
|
+
<ps-model-detail
|
|
337
|
+
[model]="selectedEntity"
|
|
338
|
+
[displayName]="engine ? engine.ModelDisplayName(selectedEntity) : undefined"
|
|
339
|
+
[provider]="provider"
|
|
340
|
+
[currentUser]="currentUser"
|
|
341
|
+
(statusChanged)="onModelStatusChanged($event)">
|
|
342
|
+
</ps-model-detail>
|
|
343
|
+
} @else {
|
|
344
|
+
<div class="ps-empty" style="padding: 40px 20px;">
|
|
345
|
+
<p class="ps-muted">Select a model to view details.</p>
|
|
346
|
+
</div>
|
|
790
347
|
}
|
|
791
348
|
</div>
|
|
792
|
-
</
|
|
793
|
-
</
|
|
349
|
+
</as-split-area>
|
|
350
|
+
</as-split>
|
|
794
351
|
</div>
|
|
795
352
|
}
|
|
796
|
-
|
|
797
|
-
@if (pending) {
|
|
798
|
-
<ps-confirm-modal
|
|
799
|
-
[title]="pendingTitle"
|
|
800
|
-
[icon]="pendingIcon"
|
|
801
|
-
[confirmIcon]="pendingIcon"
|
|
802
|
-
[confirmLabel]="pendingConfirmLabel"
|
|
803
|
-
[variant]="pendingVariant"
|
|
804
|
-
[showReason]="pending.leakageFlagged || pending.targetStatus === 'Archived'"
|
|
805
|
-
[reasonRequired]="pending.leakageFlagged"
|
|
806
|
-
[reasonLabel]="pending.leakageFlagged ? 'Leakage sign-off reason' : 'Reason (optional)'"
|
|
807
|
-
[reasonPlaceholder]="pending.leakageFlagged ? 'Explain why this model is safe to publish despite the leakage flag…' : 'Add an optional note for the record…'"
|
|
808
|
-
[busy]="busy"
|
|
809
|
-
(confirmed)="confirmPromote($event)"
|
|
810
|
-
(cancelled)="cancelPromote()">
|
|
811
|
-
<div [innerHTML]="pendingMessage"></div>
|
|
812
|
-
</ps-confirm-modal>
|
|
813
|
-
}
|
|
814
353
|
</div>
|
|
815
|
-
`, 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", "/* The panel fills its section; the list and detail columns each scroll on their own. */\n.ps-registry { display: flex; flex-direction: column; height: 100%; min-height: 0; }\n.ps-registry .md-layout {\n flex: 1; min-height: 0;\n display: grid; grid-template-columns: 300px 1fr; grid-template-rows: minmax(0, 1fr); gap: 18px;\n}\n@media (max-width: 1080px) {\n .ps-registry .md-layout { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1.4fr); }\n}\n\n/* model list \u2014 its own scroll column */\n.ps-registry .mlist { min-height: 0; overflow-y: auto; align-self: start; max-height: 100%; }\n\n/* detail \u2014 its own scroll column, independent of the list. NOTE: .detail is also a\n .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. The child rule is load-bearing, not decorative. */\n.ps-registry .detail { min-height: 0; overflow-y: auto; padding-right: 2px; }\n.ps-registry .detail > * { flex: 0 0 auto; }\n.ps-registry .mrow {\n display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 8px; cursor: pointer; transition: background .12s;\n}\n.ps-registry .mrow:hover { background: var(--mj-bg-surface-hover); }\n.ps-registry .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); box-shadow: inset 3px 0 0 var(--mj-brand-primary); }\n.ps-registry .mrow.arc { opacity: .72; }\n.ps-registry .mrow .ico {\n width: 30px; height: 30px; border-radius: 7px; display: grid; place-items: center; color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb { background: var(--ps-node-feature); }\n.ps-registry .mrow .ico.lgbm { background: var(--ps-node-algorithm); }\n.ps-registry .mrow .ico.rf { background: var(--ps-node-source); }\n.ps-registry .mrow .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.ps-registry .mrow .auc { text-align: right; flex: 0 0 auto; }\n.ps-registry .mrow .auc .v { font-weight: 700; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-registry .mrow .auc .st { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }\n.ps-registry .mrow .auc .st.pub { color: var(--mj-status-success-text); }\n.ps-registry .mrow .auc .st.val { color: var(--mj-status-info-text); }\n.ps-registry .mrow .auc .st.dr { color: var(--mj-text-muted); }\n.ps-registry .mrow .auc .st.arc { color: var(--mj-text-disabled); }\n\n/* detail */\n.ps-registry .dh { display: flex; align-items: center; gap: 14px; }\n.ps-registry .big-ico {\n width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;\n background: var(--ps-node-feature); color: var(--mj-text-inverse); font-size: 20px; flex: 0 0 auto;\n}\n.ps-registry .dh h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 8px; }\n.ps-registry .dh .sub { margin-top: 2px; }\n\n.ps-registry .metric-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ps-registry .mtile { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 14px; }\n.ps-registry .mtile .v { font-size: 28px; font-weight: 720; color: var(--mj-text-primary); letter-spacing: -.02em; }\n.ps-registry .mtile.honest { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .mtile.honest .v { color: var(--mj-brand-primary); }\n.ps-registry .stat-bar { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }\n.ps-registry .stat-bar .b { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border: 1px solid var(--mj-border-subtle); border-radius: 8px; flex: 1; min-width: 90px; }\n.ps-registry .stat-bar .b strong { color: var(--mj-text-primary); font-size: 15px; }\n\n.ps-registry .gate { align-items: flex-start; }\n.ps-registry .gate i { font-size: 20px; }\n\n.ps-registry .lineage { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.ps-registry .ln-card { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 10px 12px; min-width: 120px; }\n.ps-registry .ln-card.cur { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .ln-card .t { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-registry .ln-card .n { font-weight: 650; color: var(--mj-text-primary); font-size: 13px; margin: 2px 0; }\n.ps-registry .ln-arrow { color: var(--mj-text-disabled); }\n"] }]
|
|
354
|
+
`, 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", "/* The panel fills its section; the list and detail columns each scroll on their own. */\n.ps-registry {\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 Resizable Splitter Layout */\n.ps-registry .reg-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-page);\n}\n\n.ps-registry .reg-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.ps-registry .reg-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-registry .reg-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-registry .reg-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-registry .reg-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.ps-registry .reg-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-registry .reg-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/* model list \u2014 its own scroll column */\n.ps-registry .mlist {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n border-radius: 0;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n}\n\n/* detail \u2014 its own scroll column */\n.ps-registry .detail {\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 20px;\n}\n\n.ps-registry .mrow {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.12s;\n}\n.ps-registry .mrow:hover {\n background: var(--mj-bg-surface-hover);\n}\n.ps-registry .mrow.sel {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: inset 3px 0 0 var(--mj-brand-primary);\n}\n.ps-registry .mrow.arc {\n opacity: 0.72;\n}\n.ps-registry .mrow .ico {\n width: 30px;\n height: 30px;\n border-radius: 7px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 13px;\n flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb {\n background: var(--ps-node-feature);\n}\n.ps-registry .mrow .ico.lgbm {\n background: var(--ps-node-algorithm);\n}\n.ps-registry .mrow .ico.rf {\n background: var(--ps-node-source);\n}\n.ps-registry .mrow .nm {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ps-registry .mrow .auc {\n text-align: right;\n flex: 0 0 auto;\n}\n.ps-registry .mrow .auc .v {\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n.ps-registry .mrow .auc .st {\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n.ps-registry .mrow .auc .st.pub {\n color: var(--mj-status-success-text);\n}\n.ps-registry .mrow .auc .st.val {\n color: var(--mj-status-info-text);\n}\n.ps-registry .mrow .auc .st.dr {\n color: var(--mj-text-muted);\n}\n.ps-registry .mrow .auc .st.arc {\n color: var(--mj-text-disabled);\n}\n"] }]
|
|
816
355
|
}], null, { engine: [{
|
|
817
356
|
type: Input
|
|
818
357
|
}], provider: [{
|
|
819
358
|
type: Input
|
|
820
359
|
}], currentUser: [{
|
|
821
360
|
type: Input
|
|
361
|
+
}], initialModelId: [{
|
|
362
|
+
type: Input
|
|
822
363
|
}] }); })();
|
|
823
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRegistryComponent, { className: "PSRegistryComponent", filePath: "src/PredictiveStudio/components/ps-registry.component.ts", lineNumber:
|
|
824
|
-
/** Minimal HTML-escape for interpolating model names into the modal's innerHTML message. */
|
|
825
|
-
function escapeHtml(value) {
|
|
826
|
-
return value
|
|
827
|
-
.replace(/&/g, '&')
|
|
828
|
-
.replace(/</g, '<')
|
|
829
|
-
.replace(/>/g, '>')
|
|
830
|
-
.replace(/"/g, '"');
|
|
831
|
-
}
|
|
364
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRegistryComponent, { className: "PSRegistryComponent", filePath: "src/PredictiveStudio/components/ps-registry.component.ts", lineNumber: 114 }); })();
|
|
832
365
|
//# sourceMappingURL=ps-registry.component.js.map
|