@memberjunction/ng-dashboards 5.45.1 → 5.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/PredictiveStudio/at-risk.view-models.d.ts +33 -0
  2. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
  3. package/dist/PredictiveStudio/at-risk.view-models.js +69 -2
  4. package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
  5. package/dist/PredictiveStudio/components/ps-catalog.component.js +2 -2
  6. package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
  7. package/dist/PredictiveStudio/components/ps-compare.component.js +2 -2
  8. package/dist/PredictiveStudio/components/ps-compare.component.js.map +1 -1
  9. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +2 -2
  10. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +1 -1
  11. package/dist/PredictiveStudio/components/ps-experiments.component.js +2 -2
  12. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  13. package/dist/PredictiveStudio/components/ps-home.component.js +2 -2
  14. package/dist/PredictiveStudio/components/ps-home.component.js.map +1 -1
  15. package/dist/PredictiveStudio/components/ps-operate-dialog.component.js +2 -2
  16. package/dist/PredictiveStudio/components/ps-operate-dialog.component.js.map +1 -1
  17. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  18. package/dist/PredictiveStudio/components/ps-pipelines.component.js +306 -297
  19. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  20. package/dist/PredictiveStudio/components/ps-production.component.js +2 -2
  21. package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
  22. package/dist/PredictiveStudio/components/ps-registry.component.d.ts +1 -1
  23. package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
  24. package/dist/PredictiveStudio/components/ps-registry.component.js +15 -9
  25. package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
  26. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +115 -0
  27. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -0
  28. package/dist/PredictiveStudio/predictive-studio-agent-context.js +119 -0
  29. package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -0
  30. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts +36 -0
  31. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts.map +1 -0
  32. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js +52 -0
  33. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js.map +1 -0
  34. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
  35. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
  36. package/dist/PredictiveStudio/resources/ps-models-resource.component.js +133 -56
  37. package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
  38. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +50 -1
  39. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  40. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +591 -165
  41. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  42. package/dist/PredictiveStudio/resources/ps-resource-base.d.ts +17 -0
  43. package/dist/PredictiveStudio/resources/ps-resource-base.d.ts.map +1 -1
  44. package/dist/PredictiveStudio/resources/ps-resource-base.js +27 -1
  45. package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
  46. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +11 -1
  47. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
  48. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +199 -99
  49. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  50. package/package.json +59 -59
@@ -7,6 +7,7 @@ import { PredictiveStudioPromoteModelOperation, } from '@memberjunction/core-ent
7
7
  import { PS_LIFECYCLE_STEPS } from '../predictive-studio.types';
8
8
  import { PS_FEATURE_DOMINANCE_THRESHOLD, maxFeatureImportance, metricsToDisplay, overfitGap, parseFeatureImportance, parseMetrics, primaryAuc, } from '../predictive-studio.view-models';
9
9
  import { PSConfirmModalComponent } from './ps-confirm-modal.component';
10
+ import { humanizeFeatureName } from '../at-risk.view-models';
10
11
  import * as i0 from "@angular/core";
11
12
  const _forTrack0 = ($index, $item) => $item.id;
12
13
  const _forTrack1 = ($index, $item) => $item.key;
@@ -145,7 +146,9 @@ function PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template(rf, ctx
145
146
  i0.ɵɵelementEnd()();
146
147
  } if (rf & 2) {
147
148
  const f_r9 = ctx.$implicit;
148
- i0.ɵɵadvance(2);
149
+ i0.ɵɵadvance();
150
+ i0.ɵɵproperty("title", f_r9.name);
151
+ i0.ɵɵadvance();
149
152
  i0.ɵɵtextInterpolate(f_r9.name);
150
153
  i0.ɵɵadvance(2);
151
154
  i0.ɵɵstyleProp("width", f_r9.pct, "%");
@@ -154,7 +157,7 @@ function PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template(rf, ctx
154
157
  i0.ɵɵtextInterpolate(f_r9.value);
155
158
  } }
156
159
  function PSRegistryComponent_Conditional_2_Conditional_65_Template(rf, ctx) { if (rf & 1) {
157
- i0.ɵɵrepeaterCreate(0, PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template, 7, 6, "div", 51, _forTrack2);
160
+ i0.ɵɵrepeaterCreate(0, PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template, 7, 7, "div", 51, _forTrack2);
158
161
  } if (rf & 2) {
159
162
  const ctx_r2 = i0.ɵɵnextContext(2);
160
163
  i0.ɵɵrepeater(ctx_r2.importance);
@@ -425,9 +428,12 @@ export class PSRegistryComponent {
425
428
  return this.engine?.Models.find((m) => UUIDsEqual(m.ID, this.selectedId));
426
429
  }
427
430
  // ---- live detail: feature importance + metrics ----
428
- /** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON. */
431
+ /** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON, with humanized display names. */
429
432
  get importance() {
430
- return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6);
433
+ return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6).map((b) => ({
434
+ ...b,
435
+ name: humanizeFeatureName(b.name),
436
+ }));
431
437
  }
432
438
  get importanceCaption() {
433
439
  const n = this.importance.length;
@@ -630,7 +636,7 @@ export class PSRegistryComponent {
630
636
  return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', status: 'Draft', iconClass: 'xgb' };
631
637
  }
632
638
  static ɵfac = function PSRegistryComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSRegistryComponent)(); };
633
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRegistryComponent, selectors: [["ps-registry"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser" }, decls: 4, vars: 2, consts: [["data-testid", "ps-registry-panel", 1, "ps-panel", "ps-registry"], ["data-testid", "ps-registry-empty", 1, "ps-empty"], [1, "md-layout"], [3, "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-cubes"], ["data-testid", "ps-registry-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [1, "ps-badge", "gray"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "sel", "arc"], ["data-testid", "ps-registry-detail", 1, "ps-col", "detail"], [1, "ps-card"], [1, "ps-card-body", "dh"], [1, "big-ico"], [1, "fa-solid", "fa-cube"], [2, "flex", "1"], ["data-testid", "ps-registry-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], [1, "ps-badge"], [1, "ps-card-body"], [1, "ps-section-title"], [1, "ps-stepper"], [1, "ps-small", "ps-muted", 2, "margin-top", "10px"], [1, "ps-muted", "ps-small"], [1, "metric-pair"], [1, "mtile"], [1, "v"], [1, "mtile", "honest"], [1, "stat-bar"], [1, "ps-small", "ps-muted", 2, "margin-top", "6px"], [1, "ps-small", "ps-muted"], [1, "ps-callout", "gate", 3, "success", "warn"], [1, "ps-card-body", "ps-row", 2, "align-items", "center"], [1, "ps-muted", "ps-small", 2, "flex", "1"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "auc"], [1, "st"], [1, "ps-step"], [1, "pip"], [1, "fa-solid", "fa-check"], [1, "ln"], [1, "b"], [1, "ps-fbar"], [1, "name", "ps-small"], [1, "track"], [1, "ps-mono"], [1, "ps-callout", "gate"], [1, "ps-small", 2, "margin-top", "6px"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate", 3, "click"], [1, "fa-solid", "fa-circle-check"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote", 3, "click"], [1, "fa-solid", "fa-arrow-up"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive", 3, "click"], [1, "fa-solid", "fa-box-archive"], [3, "confirmed", "cancelled", "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [3, "innerHTML"]], template: function PSRegistryComponent_Template(rf, ctx) { if (rf & 1) {
639
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRegistryComponent, selectors: [["ps-registry"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser" }, decls: 4, vars: 2, consts: [["data-testid", "ps-registry-panel", 1, "ps-panel", "ps-registry"], ["data-testid", "ps-registry-empty", 1, "ps-empty"], [1, "md-layout"], [3, "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-cubes"], ["data-testid", "ps-registry-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [1, "ps-badge", "gray"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "sel", "arc"], ["data-testid", "ps-registry-detail", 1, "ps-col", "detail"], [1, "ps-card"], [1, "ps-card-body", "dh"], [1, "big-ico"], [1, "fa-solid", "fa-cube"], [2, "flex", "1"], ["data-testid", "ps-registry-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], [1, "ps-badge"], [1, "ps-card-body"], [1, "ps-section-title"], [1, "ps-stepper"], [1, "ps-small", "ps-muted", 2, "margin-top", "10px"], [1, "ps-muted", "ps-small"], [1, "metric-pair"], [1, "mtile"], [1, "v"], [1, "mtile", "honest"], [1, "stat-bar"], [1, "ps-small", "ps-muted", 2, "margin-top", "6px"], [1, "ps-small", "ps-muted"], [1, "ps-callout", "gate", 3, "success", "warn"], [1, "ps-card-body", "ps-row", 2, "align-items", "center"], [1, "ps-muted", "ps-small", 2, "flex", "1"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "auc"], [1, "st"], [1, "ps-step"], [1, "pip"], [1, "fa-solid", "fa-check"], [1, "ln"], [1, "b"], [1, "ps-fbar"], [1, "name", "ps-small", 3, "title"], [1, "track"], [1, "ps-mono"], [1, "ps-callout", "gate"], [1, "ps-small", 2, "margin-top", "6px"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate", 3, "click"], [1, "fa-solid", "fa-circle-check"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote", 3, "click"], [1, "fa-solid", "fa-arrow-up"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive", 3, "click"], [1, "fa-solid", "fa-box-archive"], [3, "confirmed", "cancelled", "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [3, "innerHTML"]], template: function PSRegistryComponent_Template(rf, ctx) { if (rf & 1) {
634
640
  i0.ɵɵelementStart(0, "div", 0);
635
641
  i0.ɵɵconditionalCreate(1, PSRegistryComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSRegistryComponent_Conditional_2_Template, 75, 19, "div", 2);
636
642
  i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_3_Template, 2, 11, "ps-confirm-modal", 3);
@@ -640,7 +646,7 @@ export class PSRegistryComponent {
640
646
  i0.ɵɵconditional(ctx.models.length === 0 ? 1 : 2);
641
647
  i0.ɵɵadvance(2);
642
648
  i0.ɵɵconditional(ctx.pending ? 3 : -1);
643
- } }, 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: 10px;\n box-shadow: var(--mj-shadow-sm);\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: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\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: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\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: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-registry .md-layout { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }\n@media (max-width: 1080px) { .ps-registry .md-layout { grid-template-columns: 1fr; } }\n\n.ps-registry .mlist { position: sticky; top: 0; }\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 });
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 });
644
650
  }
645
651
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSRegistryComponent, [{
646
652
  type: Component,
@@ -738,7 +744,7 @@ export class PSRegistryComponent {
738
744
  @if (importance.length > 0) {
739
745
  @for (f of importance; track f.name) {
740
746
  <div class="ps-fbar">
741
- <span class="name ps-small">{{ f.name }}</span>
747
+ <span class="name ps-small" [title]="f.name">{{ f.name }}</span>
742
748
  <div class="track"><span [class.warn]="f.warning" [style.width.%]="f.pct"></span></div>
743
749
  <span class="ps-mono">{{ f.value }}</span>
744
750
  </div>
@@ -806,7 +812,7 @@ export class PSRegistryComponent {
806
812
  </ps-confirm-modal>
807
813
  }
808
814
  </div>
809
- `, 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: 10px;\n box-shadow: var(--mj-shadow-sm);\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: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\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: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\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: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-registry .md-layout { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }\n@media (max-width: 1080px) { .ps-registry .md-layout { grid-template-columns: 1fr; } }\n\n.ps-registry .mlist { position: sticky; top: 0; }\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"] }]
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"] }]
810
816
  }], null, { engine: [{
811
817
  type: Input
812
818
  }], provider: [{
@@ -814,7 +820,7 @@ export class PSRegistryComponent {
814
820
  }], currentUser: [{
815
821
  type: Input
816
822
  }] }); })();
817
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRegistryComponent, { className: "PSRegistryComponent", filePath: "src/PredictiveStudio/components/ps-registry.component.ts", lineNumber: 229 }); })();
823
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRegistryComponent, { className: "PSRegistryComponent", filePath: "src/PredictiveStudio/components/ps-registry.component.ts", lineNumber: 230 }); })();
818
824
  /** Minimal HTML-escape for interpolating model names into the modal's innerHTML message. */
819
825
  function escapeHtml(value) {
820
826
  return value
@@ -1 +1 @@
1
- {"version":3,"file":"ps-registry.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-registry.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport {\n MJMLModelEntity,\n PredictiveStudioPromoteModelOperation,\n PredictiveStudioModelTargetStatus,\n} from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport { PSFeatureBar, PS_LIFECYCLE_STEPS, PSLifecycleStep } from '../predictive-studio.types';\nimport {\n PS_FEATURE_DOMINANCE_THRESHOLD,\n PSMetricDisplay,\n maxFeatureImportance,\n metricsToDisplay,\n overfitGap,\n parseFeatureImportance,\n parseMetrics,\n primaryAuc,\n} from '../predictive-studio.view-models';\nimport { PSConfirmModalComponent } from './ps-confirm-modal.component';\n\ninterface ModelRowVM {\n id: string;\n name: string;\n version: number;\n algorithm: string;\n holdoutAuc: string;\n status: string;\n iconClass: string;\n}\n\n/** A pending promote/archive action awaiting user confirmation. */\ninterface PendingPromotion {\n modelId: string;\n modelName: string;\n targetStatus: PredictiveStudioModelTargetStatus;\n /** When true, the selected model trips the single-feature dominance leakage flag → sign-off required. */\n leakageFlagged: boolean;\n}\n\n/**\n * Model Registry panel: master list of trained ML Models + a rich detail pane with a lifecycle stepper\n * (Draft → Validated → Published → Archived), train-vs-holdout metric comparison, feature-importance\n * bars, a leakage sign-off gate, and lifecycle actions. Fully live against `MJ: ML Models`:\n *\n * - **Feature importance** is parsed from `model.FeatureImportance` (sorted by |value|, top 6, bars).\n * - **Performance metrics** come from `model.Metrics` (train) + `model.HoldoutMetrics` (holdout) —\n * only the metrics actually recorded are shown.\n * - **Promote / Archive** call the {@link PredictiveStudioPromoteModelOperation} Remote Op behind a\n * confirmation modal. On a leakage-flagged model, the modal surfaces the sign-off requirement and\n * captures a required reason; on success the engine refreshes and the list re-renders reactively.\n *\n * 100% entity-agnostic — model names derive from the producing pipeline, never any business entity.\n */\n@Component({\n standalone: true,\n selector: 'ps-registry',\n imports: [CommonModule, MJButtonDirective, PSConfirmModalComponent],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.scss', './ps-registry.component.scss'],\n template: `\n <div class=\"ps-panel ps-registry\" data-testid=\"ps-registry-panel\">\n @if (models.length === 0) {\n <div class=\"ps-empty\" data-testid=\"ps-registry-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-cubes\"></i></span>\n <h3>No trained models yet</h3>\n <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>\n </div>\n } @else {\n <div class=\"md-layout\">\n <!-- master list -->\n <div class=\"ps-card mlist\" data-testid=\"ps-registry-list\">\n <div class=\"ps-card-head\"><h3>ML Models</h3><span class=\"ps-badge gray\">{{ models.length }}</span></div>\n <div class=\"ps-card-body\" style=\"padding:8px\">\n @for (m of models; track m.id) {\n <div class=\"mrow\" data-testid=\"ps-registry-row\" [class.sel]=\"m.id === selectedId\" [class.arc]=\"m.status === 'Archived'\" (click)=\"select(m.id)\">\n <div class=\"ico\" [class]=\"m.iconClass\"><i class=\"fa-solid fa-cube\"></i></div>\n <div style=\"flex:1;min-width:0\">\n <div class=\"nm\">{{ m.name }}</div>\n <div class=\"ln2 ps-muted ps-small\">v{{ m.version }} · {{ m.algorithm }}</div>\n </div>\n <div class=\"auc\">\n <div class=\"v\">{{ m.holdoutAuc }}</div>\n <div class=\"st\" [class]=\"statusClass(m.status)\">{{ m.status }}</div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- detail -->\n <div class=\"ps-col detail\" data-testid=\"ps-registry-detail\">\n <div class=\"ps-card\">\n <div class=\"ps-card-body dh\">\n <div class=\"big-ico\"><i class=\"fa-solid fa-cube\"></i></div>\n <div style=\"flex:1\">\n <h2 data-testid=\"ps-registry-detail-name\">{{ selected.name }} <span class=\"ps-tag ps-mono\">v{{ selected.version }}</span></h2>\n <div class=\"ps-muted ps-small sub\">{{ selected.algorithm }}{{ targetSuffix }} · immutable snapshot</div>\n </div>\n <span class=\"ps-badge\" [class]=\"statusBadgeClass(selected.status)\">{{ selected.status }}</span>\n </div>\n </div>\n\n <!-- lifecycle -->\n <div class=\"ps-card\">\n <div class=\"ps-card-body\">\n <div class=\"ps-section-title\">Lifecycle</div>\n <div class=\"ps-stepper\">\n @for (step of lifecycleSteps; track step; let last = $last) {\n <div class=\"ps-step\" [class.done]=\"stepState(step) === 'done'\" [class.curr]=\"stepState(step) === 'curr'\">\n <span class=\"pip\">@if (stepState(step) === 'done') { <i class=\"fa-solid fa-check\"></i> } @else { {{ $index + 1 }} }</span>\n {{ step }}\n </div>\n @if (!last) { <span class=\"ln\"></span> }\n }\n </div>\n <div class=\"ps-small ps-muted\" style=\"margin-top:10px\">\n Models are immutable once registered; promotion only changes lifecycle state, never weights.\n </div>\n </div>\n </div>\n\n <!-- performance -->\n <div class=\"ps-card\">\n <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>\n <div class=\"ps-card-body\">\n <div class=\"metric-pair\">\n <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>\n <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>\n </div>\n @if (secondaryMetrics.length > 0) {\n <div class=\"stat-bar\">\n @for (s of secondaryMetrics; track s.key) {\n <div class=\"b\"><span class=\"ps-muted ps-small\">{{ s.label }}</span><strong>{{ s.value }}</strong></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">No secondary metrics recorded for this model.</div>\n }\n @if (gapText) {\n <div class=\"ps-small ps-muted\" style=\"margin-top:10px\">\n Train–holdout gap of <strong>{{ gapText }}</strong> {{ gapVerdict }}.\n </div>\n }\n </div>\n </div>\n\n <!-- feature importance -->\n <div class=\"ps-card\">\n <div class=\"ps-card-head\"><h3>Feature Importance</h3><span class=\"ps-muted ps-small\">{{ importanceCaption }}</span></div>\n <div class=\"ps-card-body\">\n @if (importance.length > 0) {\n @for (f of importance; track f.name) {\n <div class=\"ps-fbar\">\n <span class=\"name ps-small\">{{ f.name }}</span>\n <div class=\"track\"><span [class.warn]=\"f.warning\" [style.width.%]=\"f.pct\"></span></div>\n <span class=\"ps-mono\">{{ f.value }}</span>\n </div>\n }\n } @else {\n <div class=\"ps-small ps-muted\">No feature-importance data was recorded for this model.</div>\n }\n </div>\n </div>\n\n <!-- leakage sign-off gate -->\n @if (importance.length > 0) {\n <div class=\"ps-callout gate\" [class.success]=\"!leakageFlagged\" [class.warn]=\"leakageFlagged\">\n <i [class]=\"leakageFlagged ? 'fa-solid fa-triangle-exclamation' : 'fa-solid fa-shield-halved'\"></i>\n <div>\n @if (leakageFlagged) {\n <strong>Leakage flag raised.</strong> A single feature dominates — top importance is\n <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, at/above the\n <strong>{{ dominanceThreshold }}</strong> dominance threshold.\n <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>\n } @else {\n <strong>Leakage gate clear.</strong> No single feature dominates — top importance is\n <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, below the\n <strong>{{ dominanceThreshold }}</strong> dominance threshold.\n <div class=\"ps-small\" style=\"margin-top:6px\"><i class=\"fa-solid fa-check\"></i> Sign-off required before any Validated → Published promotion.</div>\n }\n </div>\n </div>\n }\n\n <!-- actions -->\n <div class=\"ps-card\">\n <div class=\"ps-card-body ps-row\" style=\"align-items:center\">\n <span class=\"ps-muted ps-small\" style=\"flex:1\">{{ actionHint }}</span>\n @if (canValidate) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-registry-validate\" (click)=\"requestPromote('Validated')\"><i class=\"fa-solid fa-circle-check\"></i> Mark Validated</button>\n }\n @if (canPublish) {\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-registry-promote\" (click)=\"requestPromote('Published')\"><i class=\"fa-solid fa-arrow-up\"></i> Promote to Published</button>\n }\n @if (canArchive) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-registry-archive\" (click)=\"requestPromote('Archived')\"><i class=\"fa-solid fa-box-archive\"></i> Archive</button>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n @if (pending) {\n <ps-confirm-modal\n [title]=\"pendingTitle\"\n [icon]=\"pendingIcon\"\n [confirmIcon]=\"pendingIcon\"\n [confirmLabel]=\"pendingConfirmLabel\"\n [variant]=\"pendingVariant\"\n [showReason]=\"pending.leakageFlagged || pending.targetStatus === 'Archived'\"\n [reasonRequired]=\"pending.leakageFlagged\"\n [reasonLabel]=\"pending.leakageFlagged ? 'Leakage sign-off reason' : 'Reason (optional)'\"\n [reasonPlaceholder]=\"pending.leakageFlagged ? 'Explain why this model is safe to publish despite the leakage flag…' : 'Add an optional note for the record…'\"\n [busy]=\"busy\"\n (confirmed)=\"confirmPromote($event)\"\n (cancelled)=\"cancelPromote()\">\n <div [innerHTML]=\"pendingMessage\"></div>\n </ps-confirm-modal>\n }\n </div>\n `,\n})\nexport class PSRegistryComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n /** Provider to route the promote Remote Op + engine refresh through (multi-provider correctness). */\n @Input() provider: IMetadataProvider | null = null;\n /** Acting user for the engine refresh after a mutation. */\n @Input() currentUser: UserInfo | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n private notifications = inject(MJNotificationService);\n\n public models: ModelRowVM[] = [];\n public selectedId = '';\n public lifecycleSteps = PS_LIFECYCLE_STEPS;\n public readonly dominanceThreshold = PS_FEATURE_DOMINANCE_THRESHOLD.toFixed(2);\n\n /** Pending confirmation (null when no modal is open). */\n public pending: PendingPromotion | null = null;\n /** Remote Op in flight — drives the modal spinner + disables the buttons. */\n public busy = false;\n\n ngOnInit(): void {\n this.buildModels();\n this.selectedId = this.models[0]?.id ?? '';\n }\n\n // ---- selection + master list ----\n\n public select(id: string): void {\n this.selectedId = id;\n }\n\n public get selected(): ModelRowVM {\n return this.models.find((m) => m.id === this.selectedId) ?? this.models[0] ?? this.placeholder();\n }\n\n private get selectedEntity(): MJMLModelEntity | undefined {\n return this.engine?.Models.find((m) => UUIDsEqual(m.ID, this.selectedId));\n }\n\n // ---- live detail: feature importance + metrics ----\n\n /** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON. */\n public get importance(): PSFeatureBar[] {\n return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6);\n }\n\n public get importanceCaption(): string {\n const n = this.importance.length;\n return n === 0 ? 'no data' : `normalized · top ${n}`;\n }\n\n public get topFeatureName(): string {\n return this.importance[0]?.name ?? '—';\n }\n public get topFeatureValue(): string {\n return this.importance[0]?.value ?? '—';\n }\n\n /** True when a single feature's |importance| meets/exceeds the dominance threshold. */\n public get leakageFlagged(): boolean {\n const max = maxFeatureImportance(this.selectedEntity?.FeatureImportance);\n return max != null && max >= PS_FEATURE_DOMINANCE_THRESHOLD;\n }\n\n /** Holdout AUC hero number, formatted, or '—'. */\n public get trainAuc(): string {\n const train = parseMetrics(this.selectedEntity?.Metrics).AUC;\n return train != null ? train.toFixed(3) : '—';\n }\n\n /** Secondary metric tiles (precision/recall/F1/log-loss/…) — only those actually recorded. */\n public get secondaryMetrics(): PSMetricDisplay[] {\n const e = this.selectedEntity;\n if (!e) return [];\n // Prefer holdout metrics for the honest secondary numbers; fall back to training metrics.\n const holdout = metricsToDisplay(parseMetrics(e.HoldoutMetrics));\n return holdout.length > 0 ? holdout : metricsToDisplay(parseMetrics(e.Metrics));\n }\n\n /** The formatted train–holdout overfit gap, or '' when not computable. */\n public get gapText(): string {\n const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });\n return gap == null ? '' : Math.abs(gap).toFixed(3);\n }\n\n public get gapVerdict(): string {\n const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });\n if (gap == null) return '';\n return gap > 0.1 ? 'is wide — watch for overfitting' : 'is within tolerance — no overfitting flag';\n }\n\n public get targetSuffix(): string {\n const target = this.selectedEntity?.TargetVariable;\n return target ? ` · predicts ${target}` : '';\n }\n\n // ---- lifecycle stepper ----\n\n public statusClass(status: string): string {\n switch (status) {\n case 'Published': return 'pub';\n case 'Validated': return 'val';\n case 'Draft': return 'dr';\n case 'Archived': return 'arc';\n default: return 'dr';\n }\n }\n\n public statusBadgeClass(status: string): string {\n switch (status) {\n case 'Published': return 'green';\n case 'Validated': return 'blue';\n case 'Archived': return 'gray';\n default: return 'amber';\n }\n }\n\n /** Stepper state for a lifecycle step relative to the selected model's status. */\n public stepState(step: PSLifecycleStep): 'done' | 'curr' | 'todo' {\n const order = PS_LIFECYCLE_STEPS.indexOf(this.selected.status as PSLifecycleStep);\n const idx = PS_LIFECYCLE_STEPS.indexOf(step);\n if (order < 0) return 'todo';\n if (idx < order) return 'done';\n if (idx === order) return 'curr';\n return 'todo';\n }\n\n // ---- promote/archive availability ----\n\n public get canValidate(): boolean {\n return this.selected.status === 'Draft';\n }\n public get canPublish(): boolean {\n return this.selected.status === 'Validated' || this.selected.status === 'Draft';\n }\n public get canArchive(): boolean {\n return this.selected.status === 'Published' || this.selected.status === 'Validated';\n }\n\n public get actionHint(): string {\n switch (this.selected.status) {\n case 'Published': return 'This model is live. Archiving detaches its scoring bindings.';\n case 'Validated': return 'Validated and ready to publish — publishing makes it available for scoring.';\n case 'Draft': return 'Newly registered. Validate it before publishing to production.';\n case 'Archived': return 'Archived — superseded by a newer model.';\n default: return '';\n }\n }\n\n // ---- promote/archive flow (Remote Op) ----\n\n /** Open the confirmation modal for a lifecycle transition. */\n public requestPromote(targetStatus: PredictiveStudioModelTargetStatus): void {\n const entity = this.selectedEntity;\n if (!entity) return;\n this.pending = {\n modelId: entity.ID,\n modelName: this.selected.name,\n targetStatus,\n leakageFlagged: targetStatus === 'Published' && this.leakageFlagged,\n };\n }\n\n public cancelPromote(): void {\n if (this.busy) return;\n this.pending = null;\n }\n\n public get pendingTitle(): string {\n if (!this.pending) return '';\n return this.pending.targetStatus === 'Archived' ? 'Archive model' : `Promote to ${this.pending.targetStatus}`;\n }\n public get pendingIcon(): string {\n if (!this.pending) return 'fa-solid fa-check';\n return this.pending.targetStatus === 'Archived' ? 'fa-solid fa-box-archive' : 'fa-solid fa-arrow-up';\n }\n public get pendingConfirmLabel(): string {\n if (!this.pending) return 'Confirm';\n return this.pending.targetStatus === 'Archived' ? 'Archive' : `Promote to ${this.pending.targetStatus}`;\n }\n public get pendingVariant(): 'info' | 'warn' | 'danger' {\n if (!this.pending) return 'info';\n if (this.pending.targetStatus === 'Archived') return 'warn';\n return this.pending.leakageFlagged ? 'warn' : 'info';\n }\n public get pendingMessage(): string {\n if (!this.pending) return '';\n const name = escapeHtml(this.pending.modelName);\n if (this.pending.targetStatus === 'Archived') {\n return `Archive <strong>${name}</strong>? This detaches any active scoring bindings and removes it from production scoring. The model artifact remains immutable and recoverable.`;\n }\n if (this.pending.leakageFlagged) {\n 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.`;\n }\n return `Promote <strong>${name}</strong> to <strong>${this.pending.targetStatus}</strong>? This changes only the lifecycle state — the trained weights never change.`;\n }\n\n /** Run the promote Remote Op, then refresh the engine + close on success. */\n public async confirmPromote(reason: string): Promise<void> {\n if (!this.pending || this.busy) return;\n this.busy = true;\n const { modelId, modelName, targetStatus, leakageFlagged } = this.pending;\n try {\n const op = new PredictiveStudioPromoteModelOperation();\n const result = await op.Execute(\n { modelId, targetStatus, signOff: leakageFlagged ? true : undefined, reason: reason || undefined },\n { provider: this.provider ?? undefined, user: this.currentUser ?? undefined },\n );\n if (result.Success && result.Output?.promoted) {\n this.notifications.CreateSimpleNotification(\n `${modelName} → ${result.Output.status}`,\n 'success',\n 3500,\n );\n await this.refreshAfterMutation();\n this.pending = null;\n } else {\n this.notifications.CreateSimpleNotification(\n result.ErrorMessage || `Could not promote ${modelName} (status: ${result.Output?.status ?? 'unchanged'}).`,\n 'error',\n 5000,\n );\n }\n } catch (e) {\n this.notifications.CreateSimpleNotification(\n `Promotion failed: ${e instanceof Error ? e.message : String(e)}`,\n 'error',\n 5000,\n );\n } finally {\n this.busy = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Force-refresh the engine's cached models, then rebuild the master list. */\n private async refreshAfterMutation(): Promise<void> {\n const provider = this.provider ?? undefined;\n await this.engine.Config(true, this.currentUser ?? undefined, provider);\n this.buildModels();\n if (!this.models.some((m) => m.id === this.selectedId)) {\n this.selectedId = this.models[0]?.id ?? '';\n }\n this.cdr.detectChanges();\n }\n\n // ---- master-list view-models ----\n\n private buildModels(): void {\n this.models = (this.engine?.Models ?? []).map((m) => this.toVM(m));\n }\n\n private toVM(m: MJMLModelEntity): ModelRowVM {\n const holdout = primaryAuc(m);\n return {\n id: m.ID,\n name: this.engine.ModelDisplayName(m),\n version: m.Version,\n algorithm: this.engine.AlgorithmName(m.AlgorithmID),\n holdoutAuc: holdout != null ? holdout.toFixed(3) : '—',\n status: m.Status,\n iconClass: 'xgb',\n };\n }\n\n private placeholder(): ModelRowVM {\n return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', status: 'Draft', iconClass: 'xgb' };\n }\n}\n\n/** Minimal HTML-escape for interpolating model names into the modal's innerHTML message. */\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n"]}
1
+ {"version":3,"file":"ps-registry.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-registry.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport {\n MJMLModelEntity,\n PredictiveStudioPromoteModelOperation,\n PredictiveStudioModelTargetStatus,\n} from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport { PSFeatureBar, PS_LIFECYCLE_STEPS, PSLifecycleStep } from '../predictive-studio.types';\nimport {\n PS_FEATURE_DOMINANCE_THRESHOLD,\n PSMetricDisplay,\n maxFeatureImportance,\n metricsToDisplay,\n overfitGap,\n parseFeatureImportance,\n parseMetrics,\n primaryAuc,\n} from '../predictive-studio.view-models';\nimport { PSConfirmModalComponent } from './ps-confirm-modal.component';\nimport { humanizeFeatureName } from '../at-risk.view-models';\n\ninterface ModelRowVM {\n id: string;\n name: string;\n version: number;\n algorithm: string;\n holdoutAuc: string;\n status: string;\n iconClass: string;\n}\n\n/** A pending promote/archive action awaiting user confirmation. */\ninterface PendingPromotion {\n modelId: string;\n modelName: string;\n targetStatus: PredictiveStudioModelTargetStatus;\n /** When true, the selected model trips the single-feature dominance leakage flag → sign-off required. */\n leakageFlagged: boolean;\n}\n\n/**\n * Model Registry panel: master list of trained ML Models + a rich detail pane with a lifecycle stepper\n * (Draft → Validated → Published → Archived), train-vs-holdout metric comparison, feature-importance\n * bars, a leakage sign-off gate, and lifecycle actions. Fully live against `MJ: ML Models`:\n *\n * - **Feature importance** is parsed from `model.FeatureImportance` (sorted by |value|, top 6, bars).\n * - **Performance metrics** come from `model.Metrics` (train) + `model.HoldoutMetrics` (holdout) —\n * only the metrics actually recorded are shown.\n * - **Promote / Archive** call the {@link PredictiveStudioPromoteModelOperation} Remote Op behind a\n * confirmation modal. On a leakage-flagged model, the modal surfaces the sign-off requirement and\n * captures a required reason; on success the engine refreshes and the list re-renders reactively.\n *\n * 100% entity-agnostic — model names derive from the producing pipeline, never any business entity.\n */\n@Component({\n standalone: true,\n selector: 'ps-registry',\n imports: [CommonModule, MJButtonDirective, PSConfirmModalComponent],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-registry.component.css'],\n template: `\n <div class=\"ps-panel ps-registry\" data-testid=\"ps-registry-panel\">\n @if (models.length === 0) {\n <div class=\"ps-empty\" data-testid=\"ps-registry-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-cubes\"></i></span>\n <h3>No trained models yet</h3>\n <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>\n </div>\n } @else {\n <div class=\"md-layout\">\n <!-- master list -->\n <div class=\"ps-card mlist\" data-testid=\"ps-registry-list\">\n <div class=\"ps-card-head\"><h3>ML Models</h3><span class=\"ps-badge gray\">{{ models.length }}</span></div>\n <div class=\"ps-card-body\" style=\"padding:8px\">\n @for (m of models; track m.id) {\n <div class=\"mrow\" data-testid=\"ps-registry-row\" [class.sel]=\"m.id === selectedId\" [class.arc]=\"m.status === 'Archived'\" (click)=\"select(m.id)\">\n <div class=\"ico\" [class]=\"m.iconClass\"><i class=\"fa-solid fa-cube\"></i></div>\n <div style=\"flex:1;min-width:0\">\n <div class=\"nm\">{{ m.name }}</div>\n <div class=\"ln2 ps-muted ps-small\">v{{ m.version }} · {{ m.algorithm }}</div>\n </div>\n <div class=\"auc\">\n <div class=\"v\">{{ m.holdoutAuc }}</div>\n <div class=\"st\" [class]=\"statusClass(m.status)\">{{ m.status }}</div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- detail -->\n <div class=\"ps-col detail\" data-testid=\"ps-registry-detail\">\n <div class=\"ps-card\">\n <div class=\"ps-card-body dh\">\n <div class=\"big-ico\"><i class=\"fa-solid fa-cube\"></i></div>\n <div style=\"flex:1\">\n <h2 data-testid=\"ps-registry-detail-name\">{{ selected.name }} <span class=\"ps-tag ps-mono\">v{{ selected.version }}</span></h2>\n <div class=\"ps-muted ps-small sub\">{{ selected.algorithm }}{{ targetSuffix }} · immutable snapshot</div>\n </div>\n <span class=\"ps-badge\" [class]=\"statusBadgeClass(selected.status)\">{{ selected.status }}</span>\n </div>\n </div>\n\n <!-- lifecycle -->\n <div class=\"ps-card\">\n <div class=\"ps-card-body\">\n <div class=\"ps-section-title\">Lifecycle</div>\n <div class=\"ps-stepper\">\n @for (step of lifecycleSteps; track step; let last = $last) {\n <div class=\"ps-step\" [class.done]=\"stepState(step) === 'done'\" [class.curr]=\"stepState(step) === 'curr'\">\n <span class=\"pip\">@if (stepState(step) === 'done') { <i class=\"fa-solid fa-check\"></i> } @else { {{ $index + 1 }} }</span>\n {{ step }}\n </div>\n @if (!last) { <span class=\"ln\"></span> }\n }\n </div>\n <div class=\"ps-small ps-muted\" style=\"margin-top:10px\">\n Models are immutable once registered; promotion only changes lifecycle state, never weights.\n </div>\n </div>\n </div>\n\n <!-- performance -->\n <div class=\"ps-card\">\n <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>\n <div class=\"ps-card-body\">\n <div class=\"metric-pair\">\n <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>\n <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>\n </div>\n @if (secondaryMetrics.length > 0) {\n <div class=\"stat-bar\">\n @for (s of secondaryMetrics; track s.key) {\n <div class=\"b\"><span class=\"ps-muted ps-small\">{{ s.label }}</span><strong>{{ s.value }}</strong></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">No secondary metrics recorded for this model.</div>\n }\n @if (gapText) {\n <div class=\"ps-small ps-muted\" style=\"margin-top:10px\">\n Train–holdout gap of <strong>{{ gapText }}</strong> {{ gapVerdict }}.\n </div>\n }\n </div>\n </div>\n\n <!-- feature importance -->\n <div class=\"ps-card\">\n <div class=\"ps-card-head\"><h3>Feature Importance</h3><span class=\"ps-muted ps-small\">{{ importanceCaption }}</span></div>\n <div class=\"ps-card-body\">\n @if (importance.length > 0) {\n @for (f of importance; track f.name) {\n <div class=\"ps-fbar\">\n <span class=\"name ps-small\" [title]=\"f.name\">{{ f.name }}</span>\n <div class=\"track\"><span [class.warn]=\"f.warning\" [style.width.%]=\"f.pct\"></span></div>\n <span class=\"ps-mono\">{{ f.value }}</span>\n </div>\n }\n } @else {\n <div class=\"ps-small ps-muted\">No feature-importance data was recorded for this model.</div>\n }\n </div>\n </div>\n\n <!-- leakage sign-off gate -->\n @if (importance.length > 0) {\n <div class=\"ps-callout gate\" [class.success]=\"!leakageFlagged\" [class.warn]=\"leakageFlagged\">\n <i [class]=\"leakageFlagged ? 'fa-solid fa-triangle-exclamation' : 'fa-solid fa-shield-halved'\"></i>\n <div>\n @if (leakageFlagged) {\n <strong>Leakage flag raised.</strong> A single feature dominates — top importance is\n <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, at/above the\n <strong>{{ dominanceThreshold }}</strong> dominance threshold.\n <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>\n } @else {\n <strong>Leakage gate clear.</strong> No single feature dominates — top importance is\n <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, below the\n <strong>{{ dominanceThreshold }}</strong> dominance threshold.\n <div class=\"ps-small\" style=\"margin-top:6px\"><i class=\"fa-solid fa-check\"></i> Sign-off required before any Validated → Published promotion.</div>\n }\n </div>\n </div>\n }\n\n <!-- actions -->\n <div class=\"ps-card\">\n <div class=\"ps-card-body ps-row\" style=\"align-items:center\">\n <span class=\"ps-muted ps-small\" style=\"flex:1\">{{ actionHint }}</span>\n @if (canValidate) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-registry-validate\" (click)=\"requestPromote('Validated')\"><i class=\"fa-solid fa-circle-check\"></i> Mark Validated</button>\n }\n @if (canPublish) {\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-registry-promote\" (click)=\"requestPromote('Published')\"><i class=\"fa-solid fa-arrow-up\"></i> Promote to Published</button>\n }\n @if (canArchive) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-registry-archive\" (click)=\"requestPromote('Archived')\"><i class=\"fa-solid fa-box-archive\"></i> Archive</button>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n @if (pending) {\n <ps-confirm-modal\n [title]=\"pendingTitle\"\n [icon]=\"pendingIcon\"\n [confirmIcon]=\"pendingIcon\"\n [confirmLabel]=\"pendingConfirmLabel\"\n [variant]=\"pendingVariant\"\n [showReason]=\"pending.leakageFlagged || pending.targetStatus === 'Archived'\"\n [reasonRequired]=\"pending.leakageFlagged\"\n [reasonLabel]=\"pending.leakageFlagged ? 'Leakage sign-off reason' : 'Reason (optional)'\"\n [reasonPlaceholder]=\"pending.leakageFlagged ? 'Explain why this model is safe to publish despite the leakage flag…' : 'Add an optional note for the record…'\"\n [busy]=\"busy\"\n (confirmed)=\"confirmPromote($event)\"\n (cancelled)=\"cancelPromote()\">\n <div [innerHTML]=\"pendingMessage\"></div>\n </ps-confirm-modal>\n }\n </div>\n `,\n})\nexport class PSRegistryComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n /** Provider to route the promote Remote Op + engine refresh through (multi-provider correctness). */\n @Input() provider: IMetadataProvider | null = null;\n /** Acting user for the engine refresh after a mutation. */\n @Input() currentUser: UserInfo | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n private notifications = inject(MJNotificationService);\n\n public models: ModelRowVM[] = [];\n public selectedId = '';\n public lifecycleSteps = PS_LIFECYCLE_STEPS;\n public readonly dominanceThreshold = PS_FEATURE_DOMINANCE_THRESHOLD.toFixed(2);\n\n /** Pending confirmation (null when no modal is open). */\n public pending: PendingPromotion | null = null;\n /** Remote Op in flight — drives the modal spinner + disables the buttons. */\n public busy = false;\n\n ngOnInit(): void {\n this.buildModels();\n this.selectedId = this.models[0]?.id ?? '';\n }\n\n // ---- selection + master list ----\n\n public select(id: string): void {\n this.selectedId = id;\n }\n\n public get selected(): ModelRowVM {\n return this.models.find((m) => m.id === this.selectedId) ?? this.models[0] ?? this.placeholder();\n }\n\n private get selectedEntity(): MJMLModelEntity | undefined {\n return this.engine?.Models.find((m) => UUIDsEqual(m.ID, this.selectedId));\n }\n\n // ---- live detail: feature importance + metrics ----\n\n /** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON, with humanized display names. */\n public get importance(): PSFeatureBar[] {\n return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6).map((b) => ({\n ...b,\n name: humanizeFeatureName(b.name),\n }));\n }\n\n public get importanceCaption(): string {\n const n = this.importance.length;\n return n === 0 ? 'no data' : `normalized · top ${n}`;\n }\n\n public get topFeatureName(): string {\n return this.importance[0]?.name ?? '—';\n }\n public get topFeatureValue(): string {\n return this.importance[0]?.value ?? '—';\n }\n\n /** True when a single feature's |importance| meets/exceeds the dominance threshold. */\n public get leakageFlagged(): boolean {\n const max = maxFeatureImportance(this.selectedEntity?.FeatureImportance);\n return max != null && max >= PS_FEATURE_DOMINANCE_THRESHOLD;\n }\n\n /** Holdout AUC hero number, formatted, or '—'. */\n public get trainAuc(): string {\n const train = parseMetrics(this.selectedEntity?.Metrics).AUC;\n return train != null ? train.toFixed(3) : '—';\n }\n\n /** Secondary metric tiles (precision/recall/F1/log-loss/…) — only those actually recorded. */\n public get secondaryMetrics(): PSMetricDisplay[] {\n const e = this.selectedEntity;\n if (!e) return [];\n // Prefer holdout metrics for the honest secondary numbers; fall back to training metrics.\n const holdout = metricsToDisplay(parseMetrics(e.HoldoutMetrics));\n return holdout.length > 0 ? holdout : metricsToDisplay(parseMetrics(e.Metrics));\n }\n\n /** The formatted train–holdout overfit gap, or '' when not computable. */\n public get gapText(): string {\n const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });\n return gap == null ? '' : Math.abs(gap).toFixed(3);\n }\n\n public get gapVerdict(): string {\n const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });\n if (gap == null) return '';\n return gap > 0.1 ? 'is wide — watch for overfitting' : 'is within tolerance — no overfitting flag';\n }\n\n public get targetSuffix(): string {\n const target = this.selectedEntity?.TargetVariable;\n return target ? ` · predicts ${target}` : '';\n }\n\n // ---- lifecycle stepper ----\n\n public statusClass(status: string): string {\n switch (status) {\n case 'Published': return 'pub';\n case 'Validated': return 'val';\n case 'Draft': return 'dr';\n case 'Archived': return 'arc';\n default: return 'dr';\n }\n }\n\n public statusBadgeClass(status: string): string {\n switch (status) {\n case 'Published': return 'green';\n case 'Validated': return 'blue';\n case 'Archived': return 'gray';\n default: return 'amber';\n }\n }\n\n /** Stepper state for a lifecycle step relative to the selected model's status. */\n public stepState(step: PSLifecycleStep): 'done' | 'curr' | 'todo' {\n const order = PS_LIFECYCLE_STEPS.indexOf(this.selected.status as PSLifecycleStep);\n const idx = PS_LIFECYCLE_STEPS.indexOf(step);\n if (order < 0) return 'todo';\n if (idx < order) return 'done';\n if (idx === order) return 'curr';\n return 'todo';\n }\n\n // ---- promote/archive availability ----\n\n public get canValidate(): boolean {\n return this.selected.status === 'Draft';\n }\n public get canPublish(): boolean {\n return this.selected.status === 'Validated' || this.selected.status === 'Draft';\n }\n public get canArchive(): boolean {\n return this.selected.status === 'Published' || this.selected.status === 'Validated';\n }\n\n public get actionHint(): string {\n switch (this.selected.status) {\n case 'Published': return 'This model is live. Archiving detaches its scoring bindings.';\n case 'Validated': return 'Validated and ready to publish — publishing makes it available for scoring.';\n case 'Draft': return 'Newly registered. Validate it before publishing to production.';\n case 'Archived': return 'Archived — superseded by a newer model.';\n default: return '';\n }\n }\n\n // ---- promote/archive flow (Remote Op) ----\n\n /** Open the confirmation modal for a lifecycle transition. */\n public requestPromote(targetStatus: PredictiveStudioModelTargetStatus): void {\n const entity = this.selectedEntity;\n if (!entity) return;\n this.pending = {\n modelId: entity.ID,\n modelName: this.selected.name,\n targetStatus,\n leakageFlagged: targetStatus === 'Published' && this.leakageFlagged,\n };\n }\n\n public cancelPromote(): void {\n if (this.busy) return;\n this.pending = null;\n }\n\n public get pendingTitle(): string {\n if (!this.pending) return '';\n return this.pending.targetStatus === 'Archived' ? 'Archive model' : `Promote to ${this.pending.targetStatus}`;\n }\n public get pendingIcon(): string {\n if (!this.pending) return 'fa-solid fa-check';\n return this.pending.targetStatus === 'Archived' ? 'fa-solid fa-box-archive' : 'fa-solid fa-arrow-up';\n }\n public get pendingConfirmLabel(): string {\n if (!this.pending) return 'Confirm';\n return this.pending.targetStatus === 'Archived' ? 'Archive' : `Promote to ${this.pending.targetStatus}`;\n }\n public get pendingVariant(): 'info' | 'warn' | 'danger' {\n if (!this.pending) return 'info';\n if (this.pending.targetStatus === 'Archived') return 'warn';\n return this.pending.leakageFlagged ? 'warn' : 'info';\n }\n public get pendingMessage(): string {\n if (!this.pending) return '';\n const name = escapeHtml(this.pending.modelName);\n if (this.pending.targetStatus === 'Archived') {\n return `Archive <strong>${name}</strong>? This detaches any active scoring bindings and removes it from production scoring. The model artifact remains immutable and recoverable.`;\n }\n if (this.pending.leakageFlagged) {\n 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.`;\n }\n return `Promote <strong>${name}</strong> to <strong>${this.pending.targetStatus}</strong>? This changes only the lifecycle state — the trained weights never change.`;\n }\n\n /** Run the promote Remote Op, then refresh the engine + close on success. */\n public async confirmPromote(reason: string): Promise<void> {\n if (!this.pending || this.busy) return;\n this.busy = true;\n const { modelId, modelName, targetStatus, leakageFlagged } = this.pending;\n try {\n const op = new PredictiveStudioPromoteModelOperation();\n const result = await op.Execute(\n { modelId, targetStatus, signOff: leakageFlagged ? true : undefined, reason: reason || undefined },\n { provider: this.provider ?? undefined, user: this.currentUser ?? undefined },\n );\n if (result.Success && result.Output?.promoted) {\n this.notifications.CreateSimpleNotification(\n `${modelName} → ${result.Output.status}`,\n 'success',\n 3500,\n );\n await this.refreshAfterMutation();\n this.pending = null;\n } else {\n this.notifications.CreateSimpleNotification(\n result.ErrorMessage || `Could not promote ${modelName} (status: ${result.Output?.status ?? 'unchanged'}).`,\n 'error',\n 5000,\n );\n }\n } catch (e) {\n this.notifications.CreateSimpleNotification(\n `Promotion failed: ${e instanceof Error ? e.message : String(e)}`,\n 'error',\n 5000,\n );\n } finally {\n this.busy = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Force-refresh the engine's cached models, then rebuild the master list. */\n private async refreshAfterMutation(): Promise<void> {\n const provider = this.provider ?? undefined;\n await this.engine.Config(true, this.currentUser ?? undefined, provider);\n this.buildModels();\n if (!this.models.some((m) => m.id === this.selectedId)) {\n this.selectedId = this.models[0]?.id ?? '';\n }\n this.cdr.detectChanges();\n }\n\n // ---- master-list view-models ----\n\n private buildModels(): void {\n this.models = (this.engine?.Models ?? []).map((m) => this.toVM(m));\n }\n\n private toVM(m: MJMLModelEntity): ModelRowVM {\n const holdout = primaryAuc(m);\n return {\n id: m.ID,\n name: this.engine.ModelDisplayName(m),\n version: m.Version,\n algorithm: this.engine.AlgorithmName(m.AlgorithmID),\n holdoutAuc: holdout != null ? holdout.toFixed(3) : '—',\n status: m.Status,\n iconClass: 'xgb',\n };\n }\n\n private placeholder(): ModelRowVM {\n return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', status: 'Draft', iconClass: 'xgb' };\n }\n}\n\n/** Minimal HTML-escape for interpolating model names into the modal's innerHTML message. */\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n"]}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @fileoverview Pure, framework-agnostic helpers for Predictive Studio's AI-agent integration across its
3
+ * three doors — **Predictions** (business front door), **Studio** (analyst workbench), and **Models**
4
+ * (registry + production). Each door component supplies a plain snapshot of its current state and these
5
+ * helpers shape it into the key-value context object that flows to the async chat agent AND the realtime
6
+ * co-agent via `NavigationService.SetAgentContext`, plus resolve an agent-supplied id/name/partial to a
7
+ * concrete record for the doors' client tools.
8
+ *
9
+ * Kept Angular-free so it's unit-testable in isolation (see `src/__tests__/predictive-studio-agent-context.test.ts`).
10
+ * The shared validation/cap helpers (`validateStringParam`, `boundNameList`) come from
11
+ * `src/shared/agent-tool-validation.ts`; the PS-specific resolver + builders live LOCAL here (same
12
+ * convention as every other MJ surface — e.g. Lists' `lists-agent-context.ts`).
13
+ *
14
+ * 🔒 SAFETY BOUNDARY: every function here is read-only and side-effect-free — it only shapes context and
15
+ * matches strings. Predictive Studio surfaces are **read / navigate-only to the agent**: the doors expose
16
+ * ONLY section-switch / open-prediction / review / export / open-copilot tools. Model **training,
17
+ * promotion/publish, deletion, scoring-run, "save scores to records", and "send to a list" (which creates
18
+ * `MJ: Lists` + `MJ: List Details` rows) are DELIBERATELY NOT exposed** — those write/irreversible actions
19
+ * stay behind the user's own clicks and the Model Development Agent's approve-gated builder. The
20
+ * read-only boundary is restated on each door component; this module never mutates anything.
21
+ */
22
+ /** Upper bound on names published in any PS context list field; a companion `*Count` is surfaced when truncated. */
23
+ export declare const PS_AGENT_CONTEXT_NAME_LIST_CAP = 25;
24
+ /** Cap a name list to {@link PS_AGENT_CONTEXT_NAME_LIST_CAP}. Pure; never mutates the input. */
25
+ export declare function capPSNames(names: readonly string[]): string[];
26
+ /** Minimal id+name descriptor the doors hand the resolver so it can match an agent reference. */
27
+ export interface PSNamedRecord {
28
+ ID: string;
29
+ Name: string;
30
+ }
31
+ /**
32
+ * Resolve an agent-supplied reference the way a user names things: exact ID (case-insensitive, to tolerate
33
+ * SQL-Server-upper vs PG-lower UUID casing) → exact name (trimmed, case-insensitive) → case-insensitive
34
+ * *contains* match on the name, but ONLY when it's unambiguous — a needle contained in several candidate
35
+ * names returns null (silently opening the first would be silently opening the wrong one; the paired
36
+ * {@link buildPSNotFoundError} then lists the contenders so the agent can disambiguate). Pure +
37
+ * deterministic over the candidate list.
38
+ */
39
+ export declare function resolvePSRecord<T extends PSNamedRecord>(input: string, candidates: readonly T[]): T | null;
40
+ /**
41
+ * Tolerant "not found" message so the agent can self-correct: when the input partially matches SEVERAL
42
+ * candidates it becomes a "did you mean…" listing exactly those; otherwise it samples available names. Pure.
43
+ */
44
+ export declare function buildPSNotFoundError(input: string, candidates: readonly PSNamedRecord[], noun: string): string;
45
+ /** The open prediction's salient slice, published when the workspace is showing a selection. */
46
+ export interface SelectedPredictionSummary {
47
+ Name: string;
48
+ /** Trust grade (Poor/Fair/Good/Excellent). */
49
+ TrustGrade: string;
50
+ /** Whether the trust gate lets the user act on this prediction. */
51
+ CanOpen: boolean;
52
+ }
53
+ /** Component-supplied snapshot for the Predictions door. */
54
+ export interface PredictionsAgentContextInput {
55
+ /** catalog (the home grid) ↔ workspace (a selected prediction). */
56
+ View: 'catalog' | 'workspace';
57
+ /** Total published predictions in the catalog. */
58
+ PredictionCount: number;
59
+ /** How many clear the trust gate (openable). */
60
+ ReadyPredictionCount: number;
61
+ /** Names of the catalog predictions, in display order (bounded). */
62
+ VisiblePredictionNames: string[];
63
+ /** Whether the "+ New prediction" co-pilot is open. */
64
+ ChatOpen: boolean;
65
+ /** The open prediction (workspace view only), else null. */
66
+ Selected?: SelectedPredictionSummary | null;
67
+ /** Whether the at-risk list has finished loading for the selection. */
68
+ AtRiskLoaded?: boolean;
69
+ /** Ranked at-risk row count (when loaded). */
70
+ AtRiskCount?: number;
71
+ /** Band breakdown of the at-risk rows (when loaded). */
72
+ HighRiskCount?: number;
73
+ MediumRiskCount?: number;
74
+ LowRiskCount?: number;
75
+ /** Plain-language "what's driving this" drivers for the selection (bounded). */
76
+ Drivers?: string[];
77
+ }
78
+ /** Build the Predictions door context: catalog counts + bounded names, and (in workspace) the selection + at-risk breakdown. */
79
+ export declare function buildPredictionsAgentContext(input: PredictionsAgentContextInput): Record<string, unknown>;
80
+ /** Component-supplied snapshot for the Studio door. */
81
+ export interface StudioAgentContextInput {
82
+ /** Active section key (home/pipelines/catalog/experiments/compare). */
83
+ ActiveSection: string;
84
+ /** Active section display label. */
85
+ ActiveSectionLabel: string;
86
+ /** Labels of the door's sections, in nav order. */
87
+ SectionLabels: string[];
88
+ PublishedModelCount: number;
89
+ RunningSessionCount: number;
90
+ PipelineCount: number;
91
+ AlgorithmCount: number;
92
+ ExperimentCount: number;
93
+ TrainingRunCount: number;
94
+ /** Whether the docked Model Dev Agent co-pilot is open. */
95
+ ChatOpen: boolean;
96
+ }
97
+ /** Build the Studio door context: active section + section labels + the workbench's headline counts. */
98
+ export declare function buildStudioAgentContext(input: StudioAgentContextInput): Record<string, unknown>;
99
+ /** Component-supplied snapshot for the Models door. */
100
+ export interface ModelsAgentContextInput {
101
+ /** Active section key (registry/production). */
102
+ ActiveSection: string;
103
+ /** Active section display label. */
104
+ ActiveSectionLabel: string;
105
+ /** Labels of the door's sections, in nav order. */
106
+ SectionLabels: string[];
107
+ TotalModelCount: number;
108
+ PublishedModelCount: number;
109
+ DraftModelCount: number;
110
+ /** Models with at least one scoring Record Process (live in production). */
111
+ ProductionModelCount: number;
112
+ }
113
+ /** Build the Models door context: active section + section labels + the lifecycle counts. */
114
+ export declare function buildModelsAgentContext(input: ModelsAgentContextInput): Record<string, unknown>;
115
+ //# sourceMappingURL=predictive-studio-agent-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predictive-studio-agent-context.d.ts","sourceRoot":"","sources":["../../src/PredictiveStudio/predictive-studio-agent-context.ts"],"names":[],"mappings":"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"}