@memberjunction/ng-dashboards 5.46.0 → 5.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +15 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +45 -35
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +7 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts +4 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +39 -30
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +14 -12
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +6 -1
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/Lists/services/list-set-operations.service.d.ts +11 -0
- package/dist/Lists/services/list-set-operations.service.d.ts.map +1 -1
- package/dist/Lists/services/list-set-operations.service.js +27 -2
- package/dist/Lists/services/list-set-operations.service.js.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.d.ts +33 -0
- package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.js +69 -2
- package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-catalog.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-compare.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-compare.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-home.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-home.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-operate-dialog.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-operate-dialog.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +306 -297
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-production.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.js +15 -9
- package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +115 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js +119 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts +36 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js +52 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js.map +1 -0
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js +133 -56
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +50 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +591 -165
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.d.ts +17 -0
- package/dist/PredictiveStudio/resources/ps-resource-base.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js +27 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +11 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +199 -99
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
- package/package.json +59 -59
|
@@ -443,7 +443,7 @@ export class PSCatalogComponent {
|
|
|
443
443
|
i0.ɵɵconditional(ctx.selectedUseCaseIds.length > 0 ? 15 : -1);
|
|
444
444
|
i0.ɵɵadvance();
|
|
445
445
|
i0.ɵɵconditional(ctx.cards.length === 0 ? 16 : 17);
|
|
446
|
-
} }, dependencies: [CommonModule, MJButtonDirective], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 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-catalog { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-catalog .guide-card .guide-head { display: flex; gap: 12px; align-items: flex-start; }\n.ps-catalog .guide-head i { font-size: 18px; margin-top: 2px; }\n.ps-catalog .guide-head h3 { font-size: 14.5px; color: var(--mj-text-primary); margin-bottom: 4px; }\n.ps-catalog .guide-head .ps-muted { max-width: 560px; }\n\n.ps-catalog .chips-row { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }\n.ps-catalog .chips { display: flex; gap: 8px; flex-wrap: wrap; }\n.ps-catalog .chip {\n display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;\n border: 1px solid var(--mj-border-default); background: var(--mj-bg-surface);\n color: var(--mj-text-secondary); font-size: 12.5px; font-weight: 600; cursor: pointer; transition: all .12s;\n}\n.ps-catalog .chip:hover { background: var(--mj-bg-surface-hover); }\n.ps-catalog .chip.on { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n.ps-catalog .chip i { font-size: 12px; }\n.ps-catalog .clearx {\n display: inline-flex; align-items: center; gap: 6px; background: transparent; border: none;\n color: var(--mj-text-muted); font-size: 12.5px; font-weight: 600; cursor: pointer;\n}\n.ps-catalog .clearx:hover { color: var(--mj-status-error-text); }\n.ps-catalog .reco-banner { margin-top: 14px; }\n\n/* gallery */\n.ps-catalog .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }\n.ps-catalog .acard {\n position: relative; display: flex; flex-direction: column;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; box-shadow: var(--mj-shadow-sm); overflow: hidden; transition: opacity .15s, box-shadow .15s, border-color .15s;\n}\n.ps-catalog .acard.hit { border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); }\n.ps-catalog .acard.dim { opacity: .46; }\n.ps-catalog .ribbon { position: absolute; top: 12px; right: 12px; z-index: 1; }\n\n.ps-catalog .ah { display: flex; align-items: center; gap: 12px; padding: 16px 16px 0; }\n.ps-catalog .ah .ic {\n width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; flex: 0 0 auto;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); color: var(--mj-brand-primary);\n}\n.ps-catalog .ah .nm { font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-catalog .ah .key { color: var(--mj-text-muted); }\n\n.ps-catalog .ab { padding: 14px 16px; display: flex; flex-direction: column; gap: 11px; flex: 1; }\n.ps-catalog .kv-title { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-catalog .meta-row { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.ps-catalog .af {\n display: flex; align-items: center; gap: 8px; padding: 12px 16px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n\n/* Inline algorithm detail panel (toggled by the Details button) */\n.ps-catalog .adetail {\n padding: 14px 16px; border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-sunken); display: flex; flex-direction: column; gap: 8px;\n}\n.ps-catalog .adetail-desc { margin: 0; line-height: 1.45; color: var(--mj-text-secondary); }\n.ps-catalog .dkv { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }\n.ps-catalog .dk { color: var(--mj-text-muted); font-weight: 650; }\n.ps-catalog .dv { color: var(--mj-text-primary); text-align: right; }\n.ps-catalog .hp-list { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }\n.ps-catalog .hp { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; }\n.ps-catalog .adetail-foot { display: flex; justify-content: flex-start; margin-top: 6px; }\n"], encapsulation: 2 });
|
|
446
|
+
} }, dependencies: [CommonModule, MJButtonDirective], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-catalog { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-catalog .guide-card .guide-head { display: flex; gap: 12px; align-items: flex-start; }\n.ps-catalog .guide-head i { font-size: 18px; margin-top: 2px; }\n.ps-catalog .guide-head h3 { font-size: 14.5px; color: var(--mj-text-primary); margin-bottom: 4px; }\n.ps-catalog .guide-head .ps-muted { max-width: 560px; }\n\n.ps-catalog .chips-row { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }\n.ps-catalog .chips { display: flex; gap: 8px; flex-wrap: wrap; }\n.ps-catalog .chip {\n display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;\n border: 1px solid var(--mj-border-default); background: var(--mj-bg-surface);\n color: var(--mj-text-secondary); font-size: 12.5px; font-weight: 600; cursor: pointer; transition: all .12s;\n}\n.ps-catalog .chip:hover { background: var(--mj-bg-surface-hover); }\n.ps-catalog .chip.on { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n.ps-catalog .chip i { font-size: 12px; }\n.ps-catalog .clearx {\n display: inline-flex; align-items: center; gap: 6px; background: transparent; border: none;\n color: var(--mj-text-muted); font-size: 12.5px; font-weight: 600; cursor: pointer;\n}\n.ps-catalog .clearx:hover { color: var(--mj-status-error-text); }\n.ps-catalog .reco-banner { margin-top: 14px; }\n\n/* gallery */\n.ps-catalog .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }\n.ps-catalog .acard {\n position: relative; display: flex; flex-direction: column;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; overflow: hidden; transition: opacity .15s, border-color .12s, background .12s;\n}\n.ps-catalog .acard:hover { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface)); }\n.ps-catalog .acard.hit { border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); }\n.ps-catalog .acard.dim { opacity: .46; }\n.ps-catalog .ribbon { position: absolute; top: 12px; right: 12px; z-index: 1; }\n\n.ps-catalog .ah { display: flex; align-items: center; gap: 12px; padding: 16px 16px 0; }\n.ps-catalog .ah .ic {\n width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; flex: 0 0 auto;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); color: var(--mj-brand-primary);\n}\n.ps-catalog .ah .nm { font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-catalog .ah .key { color: var(--mj-text-muted); }\n\n.ps-catalog .ab { padding: 14px 16px; display: flex; flex-direction: column; gap: 11px; flex: 1; }\n.ps-catalog .kv-title { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-catalog .meta-row { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.ps-catalog .af {\n display: flex; align-items: center; gap: 8px; padding: 12px 16px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n\n/* Inline algorithm detail panel (toggled by the Details button) */\n.ps-catalog .adetail {\n padding: 14px 16px; border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-sunken); display: flex; flex-direction: column; gap: 8px;\n}\n.ps-catalog .adetail-desc { margin: 0; line-height: 1.45; color: var(--mj-text-secondary); }\n.ps-catalog .dkv { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }\n.ps-catalog .dk { color: var(--mj-text-muted); font-weight: 650; }\n.ps-catalog .dv { color: var(--mj-text-primary); text-align: right; }\n.ps-catalog .hp-list { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }\n.ps-catalog .hp { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; }\n.ps-catalog .adetail-foot { display: flex; justify-content: flex-start; margin-top: 6px; }\n"], encapsulation: 2 });
|
|
447
447
|
}
|
|
448
448
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSCatalogComponent, [{
|
|
449
449
|
type: Component,
|
|
@@ -569,7 +569,7 @@ export class PSCatalogComponent {
|
|
|
569
569
|
</div>
|
|
570
570
|
}
|
|
571
571
|
</div>
|
|
572
|
-
`, 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-catalog { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-catalog .guide-card .guide-head { display: flex; gap: 12px; align-items: flex-start; }\n.ps-catalog .guide-head i { font-size: 18px; margin-top: 2px; }\n.ps-catalog .guide-head h3 { font-size: 14.5px; color: var(--mj-text-primary); margin-bottom: 4px; }\n.ps-catalog .guide-head .ps-muted { max-width: 560px; }\n\n.ps-catalog .chips-row { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }\n.ps-catalog .chips { display: flex; gap: 8px; flex-wrap: wrap; }\n.ps-catalog .chip {\n display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;\n border: 1px solid var(--mj-border-default); background: var(--mj-bg-surface);\n color: var(--mj-text-secondary); font-size: 12.5px; font-weight: 600; cursor: pointer; transition: all .12s;\n}\n.ps-catalog .chip:hover { background: var(--mj-bg-surface-hover); }\n.ps-catalog .chip.on { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n.ps-catalog .chip i { font-size: 12px; }\n.ps-catalog .clearx {\n display: inline-flex; align-items: center; gap: 6px; background: transparent; border: none;\n color: var(--mj-text-muted); font-size: 12.5px; font-weight: 600; cursor: pointer;\n}\n.ps-catalog .clearx:hover { color: var(--mj-status-error-text); }\n.ps-catalog .reco-banner { margin-top: 14px; }\n\n/* gallery */\n.ps-catalog .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }\n.ps-catalog .acard {\n position: relative; display: flex; flex-direction: column;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; box-shadow: var(--mj-shadow-sm); overflow: hidden; transition: opacity .15s, box-shadow .15s, border-color .15s;\n}\n.ps-catalog .acard.hit { border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); }\n.ps-catalog .acard.dim { opacity: .46; }\n.ps-catalog .ribbon { position: absolute; top: 12px; right: 12px; z-index: 1; }\n\n.ps-catalog .ah { display: flex; align-items: center; gap: 12px; padding: 16px 16px 0; }\n.ps-catalog .ah .ic {\n width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; flex: 0 0 auto;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); color: var(--mj-brand-primary);\n}\n.ps-catalog .ah .nm { font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-catalog .ah .key { color: var(--mj-text-muted); }\n\n.ps-catalog .ab { padding: 14px 16px; display: flex; flex-direction: column; gap: 11px; flex: 1; }\n.ps-catalog .kv-title { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-catalog .meta-row { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.ps-catalog .af {\n display: flex; align-items: center; gap: 8px; padding: 12px 16px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n\n/* Inline algorithm detail panel (toggled by the Details button) */\n.ps-catalog .adetail {\n padding: 14px 16px; border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-sunken); display: flex; flex-direction: column; gap: 8px;\n}\n.ps-catalog .adetail-desc { margin: 0; line-height: 1.45; color: var(--mj-text-secondary); }\n.ps-catalog .dkv { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }\n.ps-catalog .dk { color: var(--mj-text-muted); font-weight: 650; }\n.ps-catalog .dv { color: var(--mj-text-primary); text-align: right; }\n.ps-catalog .hp-list { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }\n.ps-catalog .hp { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; }\n.ps-catalog .adetail-foot { display: flex; justify-content: flex-start; margin-top: 6px; }\n"] }]
|
|
572
|
+
`, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-catalog { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-catalog .guide-card .guide-head { display: flex; gap: 12px; align-items: flex-start; }\n.ps-catalog .guide-head i { font-size: 18px; margin-top: 2px; }\n.ps-catalog .guide-head h3 { font-size: 14.5px; color: var(--mj-text-primary); margin-bottom: 4px; }\n.ps-catalog .guide-head .ps-muted { max-width: 560px; }\n\n.ps-catalog .chips-row { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }\n.ps-catalog .chips { display: flex; gap: 8px; flex-wrap: wrap; }\n.ps-catalog .chip {\n display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;\n border: 1px solid var(--mj-border-default); background: var(--mj-bg-surface);\n color: var(--mj-text-secondary); font-size: 12.5px; font-weight: 600; cursor: pointer; transition: all .12s;\n}\n.ps-catalog .chip:hover { background: var(--mj-bg-surface-hover); }\n.ps-catalog .chip.on { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n.ps-catalog .chip i { font-size: 12px; }\n.ps-catalog .clearx {\n display: inline-flex; align-items: center; gap: 6px; background: transparent; border: none;\n color: var(--mj-text-muted); font-size: 12.5px; font-weight: 600; cursor: pointer;\n}\n.ps-catalog .clearx:hover { color: var(--mj-status-error-text); }\n.ps-catalog .reco-banner { margin-top: 14px; }\n\n/* gallery */\n.ps-catalog .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }\n.ps-catalog .acard {\n position: relative; display: flex; flex-direction: column;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; overflow: hidden; transition: opacity .15s, border-color .12s, background .12s;\n}\n.ps-catalog .acard:hover { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface)); }\n.ps-catalog .acard.hit { border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); }\n.ps-catalog .acard.dim { opacity: .46; }\n.ps-catalog .ribbon { position: absolute; top: 12px; right: 12px; z-index: 1; }\n\n.ps-catalog .ah { display: flex; align-items: center; gap: 12px; padding: 16px 16px 0; }\n.ps-catalog .ah .ic {\n width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; flex: 0 0 auto;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); color: var(--mj-brand-primary);\n}\n.ps-catalog .ah .nm { font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-catalog .ah .key { color: var(--mj-text-muted); }\n\n.ps-catalog .ab { padding: 14px 16px; display: flex; flex-direction: column; gap: 11px; flex: 1; }\n.ps-catalog .kv-title { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-catalog .meta-row { display: flex; flex-wrap: wrap; gap: 6px; }\n\n.ps-catalog .af {\n display: flex; align-items: center; gap: 8px; padding: 12px 16px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n\n/* Inline algorithm detail panel (toggled by the Details button) */\n.ps-catalog .adetail {\n padding: 14px 16px; border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-sunken); display: flex; flex-direction: column; gap: 8px;\n}\n.ps-catalog .adetail-desc { margin: 0; line-height: 1.45; color: var(--mj-text-secondary); }\n.ps-catalog .dkv { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; }\n.ps-catalog .dk { color: var(--mj-text-muted); font-weight: 650; }\n.ps-catalog .dv { color: var(--mj-text-primary); text-align: right; }\n.ps-catalog .hp-list { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }\n.ps-catalog .hp { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; }\n.ps-catalog .adetail-foot { display: flex; justify-content: flex-start; margin-top: 6px; }\n"] }]
|
|
573
573
|
}], null, { engine: [{
|
|
574
574
|
type: Input
|
|
575
575
|
}], askAgent: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ps-catalog.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJMLAlgorithmEntity, MJMLAlgorithmUseCaseEntity } from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine, RecommendationLevel, RECOMMENDATION_RANK } from '../engine/predictive-studio.engine';\n\ninterface AlgoCardVM {\n algo: MJMLAlgorithmEntity;\n icon: string;\n problemTypes: string[];\n bestLevel: RecommendationLevel | null;\n}\n\nconst ALGO_ICONS: Record<string, string> = {\n xgboost: 'fa-solid fa-bolt',\n lightgbm: 'fa-solid fa-feather',\n logistic_regression: 'fa-solid fa-wave-square',\n random_forest: 'fa-solid fa-tree',\n ridge: 'fa-solid fa-ruler',\n linear_regression: 'fa-solid fa-ruler',\n mlp: 'fa-solid fa-network-wired',\n};\n\nconst USE_CASE_ICONS: Record<string, string> = {\n binary: 'fa-solid fa-circle-half-stroke',\n regression: 'fa-solid fa-arrow-trend-up',\n interpret: 'fa-solid fa-magnifying-glass-chart',\n tuning: 'fa-solid fa-sliders',\n large: 'fa-solid fa-gauge-high',\n embedding: 'fa-solid fa-vector-square',\n small: 'fa-solid fa-compress',\n};\n\n/**\n * Algorithm Catalog panel (mockup algorithms-2): a card gallery of the fixed algorithm catalog plus\n * a \"Guide me\" scenario picker. Selecting use-case chips highlights and re-sorts the algorithm cards\n * by each algorithm's best RecommendationLevel across the chosen scenarios — driven entirely by the\n * cached MJ: ML Algorithm Use Case Rankings matrix.\n */\n@Component({\n standalone: true,\n selector: 'ps-catalog',\n imports: [CommonModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.scss', './ps-catalog.component.scss'],\n template: `\n <div class=\"ps-panel ps-catalog\" data-testid=\"ps-catalog-panel\">\n <!-- Guide me scenario picker -->\n <div class=\"ps-card guide-card\">\n <div class=\"ps-card-body\">\n <div class=\"guide-head\">\n <i class=\"fa-solid fa-compass\" style=\"color: var(--mj-brand-primary)\"></i>\n <div>\n <h3>Guide me — what does your problem look like?</h3>\n <div class=\"ps-muted ps-small\">\n Tap the scenarios that describe your data and goals. We'll highlight the algorithms that fit\n and pull the best matches to the front. Pick one or several.\n </div>\n </div>\n </div>\n <div class=\"chips-row\">\n <div class=\"chips\" data-testid=\"ps-catalog-scenarios\">\n @for (uc of useCases; track uc.ID) {\n <button class=\"chip\" [class.on]=\"isSelected(uc.ID)\" data-testid=\"ps-catalog-scenario-chip\" (click)=\"toggleUseCase(uc.ID)\">\n <i [class]=\"useCaseIcon(uc)\"></i> {{ uc.Name }}\n </button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <button class=\"clearx\" (click)=\"clearScenarios()\"><i class=\"fa-solid fa-xmark\"></i> Clear</button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <div class=\"ps-callout info reco-banner\" data-testid=\"ps-catalog-reco-banner\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n @if (recommendation.primaries.length > 0) {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, your top pick(s):\n <strong>{{ recommendation.primaries.join(' & ') }}</strong>. Stronger fits are pulled to the front.</div>\n } @else {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, no single primary stands out —\n review the strong fits at the top.</div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Card gallery -->\n @if (cards.length === 0) {\n <div class=\"ps-card\"><div class=\"ps-card-body ps-muted\">No algorithms in the catalog yet.</div></div>\n } @else {\n <div class=\"gallery\" data-testid=\"ps-catalog-gallery\">\n @for (card of cards; track card.algo.ID) {\n <div class=\"acard\" data-testid=\"ps-catalog-card\"\n [class.hit]=\"card.bestLevel === 'Primary'\"\n [class.dim]=\"card.bestLevel && rank(card.bestLevel) <= 1\">\n @if (card.bestLevel === 'Primary') {\n <div class=\"ribbon\"><span class=\"ps-lvl ps-lvl-Primary\">Primary</span></div>\n }\n <div class=\"ah\">\n <div class=\"ic\"><i [class]=\"card.icon\"></i></div>\n <div class=\"title\">\n <div class=\"nm\">{{ card.algo.Name }}</div>\n <div class=\"ps-mono key\">{{ card.algo.DriverClass }}</div>\n </div>\n </div>\n <div class=\"ab\">\n @if (card.algo.Description) {\n <div class=\"ps-muted ps-small\">{{ card.algo.Description }}</div>\n }\n <div class=\"kv-title\">Problem types</div>\n <div class=\"meta-row\">\n @for (pt of card.problemTypes; track pt) {\n <span class=\"ps-tag\">{{ pt }}</span>\n }\n </div>\n <div class=\"meta-row\">\n @if (card.algo.SupportsFeatureImportance) {\n <span class=\"ps-badge green\"><i class=\"fa-solid fa-chart-column\"></i> Feature importance</span>\n } @else {\n <span class=\"ps-badge red\"><i class=\"fa-solid fa-ban\"></i> No importance</span>\n }\n </div>\n </div>\n <div class=\"af\">\n @if (card.bestLevel) {\n <span class=\"ps-lvl\" [class]=\"'ps-lvl-' + card.bestLevel\">{{ levelLabel(card.bestLevel) }}</span>\n }\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-catalog-details\"\n [class.on]=\"detailId === card.algo.ID\" (click)=\"toggleDetail(card.algo.ID)\">\n <i class=\"fa-solid fa-circle-info\"></i> Details\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-catalog-use\"\n (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use\n </button>\n </div>\n @if (detailId === card.algo.ID) {\n <div class=\"adetail\" data-testid=\"ps-catalog-detail\">\n @if (card.algo.Description) {\n <p class=\"ps-small adetail-desc\">{{ card.algo.Description }}</p>\n }\n <div class=\"dkv\"><span class=\"dk\">Problem types</span><span class=\"dv\">{{ card.problemTypes.join(', ') || '—' }}</span></div>\n <div class=\"dkv\"><span class=\"dk\">Driver class</span><span class=\"dv ps-mono\">{{ card.algo.DriverClass }}</span></div>\n @if (hyperparams(card.algo).length > 0) {\n <div class=\"dk\" style=\"margin-top:8px\">Default hyperparameters</div>\n <div class=\"hp-list\">\n @for (hp of hyperparams(card.algo); track hp.k) {\n <div class=\"hp\"><span class=\"ps-mono\">{{ hp.k }}</span><span class=\"ps-mono ps-muted\">{{ hp.v }}</span></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">Sensible defaults are applied at training time.</div>\n }\n <div class=\"adetail-foot\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use this algorithm\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class PSCatalogComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */\n @Output() askAgent = new EventEmitter<string>();\n\n public selectedUseCaseIds: string[] = [];\n public cards: AlgoCardVM[] = [];\n /** The algorithm whose inline detail panel is expanded (null = none). */\n public detailId: string | null = null;\n\n ngOnInit(): void {\n this.rebuildCards();\n }\n\n public get useCases(): MJMLAlgorithmUseCaseEntity[] {\n return this.engine?.UseCases ?? [];\n }\n\n public useCaseIcon(uc: MJMLAlgorithmUseCaseEntity): string {\n const name = (uc.Name || '').toLowerCase();\n if (name.includes('binary')) return USE_CASE_ICONS['binary'];\n if (name.includes('regression')) return USE_CASE_ICONS['regression'];\n if (name.includes('interpret')) return USE_CASE_ICONS['interpret'];\n if (name.includes('tuning') || name.includes('minimal')) return USE_CASE_ICONS['tuning'];\n if (name.includes('large') || name.includes('wide') || name.includes('speed')) return USE_CASE_ICONS['large'];\n if (name.includes('embedding') || name.includes('llm')) return USE_CASE_ICONS['embedding'];\n if (name.includes('small')) return USE_CASE_ICONS['small'];\n return 'fa-solid fa-circle-dot';\n }\n\n public isSelected(id: string): boolean {\n return this.selectedUseCaseIds.some((s) => UUIDsEqual(s, id));\n }\n\n public toggleUseCase(id: string): void {\n if (this.isSelected(id)) {\n this.selectedUseCaseIds = this.selectedUseCaseIds.filter((s) => !UUIDsEqual(s, id));\n } else {\n this.selectedUseCaseIds = [...this.selectedUseCaseIds, id];\n }\n this.rebuildCards();\n }\n\n public clearScenarios(): void {\n this.selectedUseCaseIds = [];\n this.rebuildCards();\n }\n\n // ---- algorithm card actions ----\n\n /** Toggle the inline detail panel for an algorithm card. */\n public toggleDetail(id: string): void {\n this.detailId = this.detailId === id ? null : id;\n }\n\n /** Seed the Model Development Agent to build a model with the chosen algorithm. */\n public useAlgorithm(algo: MJMLAlgorithmEntity): void {\n this.askAgent.emit(\n `Help me build a predictive model using the ${algo.Name} algorithm. ` +\n `Walk me through choosing the entity, the outcome to predict, and the features to assemble.`,\n );\n }\n\n /** Parse an algorithm's `DefaultHyperparameters` JSON into a small key/value list (≤8 rows). */\n public hyperparams(algo: MJMLAlgorithmEntity): { k: string; v: string }[] {\n const raw = algo.DefaultHyperparameters;\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n return Object.entries(parsed as Record<string, unknown>)\n .map(([k, v]) => ({ k, v: String(v) }))\n .slice(0, 8);\n } catch {\n return [];\n }\n }\n\n public rank(level: RecommendationLevel): number {\n return RECOMMENDATION_RANK[level];\n }\n\n public levelLabel(level: RecommendationLevel): string {\n switch (level) {\n case 'Primary': return 'Primary fit';\n case 'Strong': return 'Strong fit';\n case 'Viable': return 'Viable';\n case 'Weak': return 'Weak fit';\n case 'NotRecommended': return 'N/A';\n }\n }\n\n /**\n * Structured recommendation data for the \"Guide me\" banner — rendered with plain template\n * interpolation + `<strong>` markup (no `[innerHTML]`). `scenarioLabel` is the human-readable\n * scenario name(s); `primaries` is the list of Primary-fit algorithm names.\n */\n public get recommendation(): { scenarioLabel: string; primaries: string[] } {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const primaries = this.engine.Algorithms.filter((a) => levels.get(a.ID) === 'Primary').map((a) => a.Name);\n const scenarioLabel =\n this.selectedUseCaseIds.length === 1\n ? this.useCases.find((u) => UUIDsEqual(u.ID, this.selectedUseCaseIds[0]))?.Name ?? 'your scenario'\n : `${this.selectedUseCaseIds.length} scenarios`;\n return { scenarioLabel, primaries };\n }\n\n private rebuildCards(): void {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const cards: AlgoCardVM[] = this.engine.Algorithms.map((algo) => ({\n algo,\n icon: ALGO_ICONS[algo.DriverClass] ?? 'fa-solid fa-shapes',\n problemTypes: (algo.ProblemTypes || '').split(',').map((p) => p.trim()).filter((p) => p.length > 0),\n bestLevel: levels.get(algo.ID) ?? null,\n }));\n if (this.selectedUseCaseIds.length > 0) {\n cards.sort((a, b) => {\n const ra = a.bestLevel ? RECOMMENDATION_RANK[a.bestLevel] : -1;\n const rb = b.bestLevel ? RECOMMENDATION_RANK[b.bestLevel] : -1;\n return rb - ra;\n });\n }\n this.cards = cards;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ps-catalog.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-catalog.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJMLAlgorithmEntity, MJMLAlgorithmUseCaseEntity } from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine, RecommendationLevel, RECOMMENDATION_RANK } from '../engine/predictive-studio.engine';\n\ninterface AlgoCardVM {\n algo: MJMLAlgorithmEntity;\n icon: string;\n problemTypes: string[];\n bestLevel: RecommendationLevel | null;\n}\n\nconst ALGO_ICONS: Record<string, string> = {\n xgboost: 'fa-solid fa-bolt',\n lightgbm: 'fa-solid fa-feather',\n logistic_regression: 'fa-solid fa-wave-square',\n random_forest: 'fa-solid fa-tree',\n ridge: 'fa-solid fa-ruler',\n linear_regression: 'fa-solid fa-ruler',\n mlp: 'fa-solid fa-network-wired',\n};\n\nconst USE_CASE_ICONS: Record<string, string> = {\n binary: 'fa-solid fa-circle-half-stroke',\n regression: 'fa-solid fa-arrow-trend-up',\n interpret: 'fa-solid fa-magnifying-glass-chart',\n tuning: 'fa-solid fa-sliders',\n large: 'fa-solid fa-gauge-high',\n embedding: 'fa-solid fa-vector-square',\n small: 'fa-solid fa-compress',\n};\n\n/**\n * Algorithm Catalog panel (mockup algorithms-2): a card gallery of the fixed algorithm catalog plus\n * a \"Guide me\" scenario picker. Selecting use-case chips highlights and re-sorts the algorithm cards\n * by each algorithm's best RecommendationLevel across the chosen scenarios — driven entirely by the\n * cached MJ: ML Algorithm Use Case Rankings matrix.\n */\n@Component({\n standalone: true,\n selector: 'ps-catalog',\n imports: [CommonModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-catalog.component.css'],\n template: `\n <div class=\"ps-panel ps-catalog\" data-testid=\"ps-catalog-panel\">\n <!-- Guide me scenario picker -->\n <div class=\"ps-card guide-card\">\n <div class=\"ps-card-body\">\n <div class=\"guide-head\">\n <i class=\"fa-solid fa-compass\" style=\"color: var(--mj-brand-primary)\"></i>\n <div>\n <h3>Guide me — what does your problem look like?</h3>\n <div class=\"ps-muted ps-small\">\n Tap the scenarios that describe your data and goals. We'll highlight the algorithms that fit\n and pull the best matches to the front. Pick one or several.\n </div>\n </div>\n </div>\n <div class=\"chips-row\">\n <div class=\"chips\" data-testid=\"ps-catalog-scenarios\">\n @for (uc of useCases; track uc.ID) {\n <button class=\"chip\" [class.on]=\"isSelected(uc.ID)\" data-testid=\"ps-catalog-scenario-chip\" (click)=\"toggleUseCase(uc.ID)\">\n <i [class]=\"useCaseIcon(uc)\"></i> {{ uc.Name }}\n </button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <button class=\"clearx\" (click)=\"clearScenarios()\"><i class=\"fa-solid fa-xmark\"></i> Clear</button>\n }\n </div>\n @if (selectedUseCaseIds.length > 0) {\n <div class=\"ps-callout info reco-banner\" data-testid=\"ps-catalog-reco-banner\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n @if (recommendation.primaries.length > 0) {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, your top pick(s):\n <strong>{{ recommendation.primaries.join(' & ') }}</strong>. Stronger fits are pulled to the front.</div>\n } @else {\n <div>For <strong>{{ recommendation.scenarioLabel }}</strong>, no single primary stands out —\n review the strong fits at the top.</div>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Card gallery -->\n @if (cards.length === 0) {\n <div class=\"ps-card\"><div class=\"ps-card-body ps-muted\">No algorithms in the catalog yet.</div></div>\n } @else {\n <div class=\"gallery\" data-testid=\"ps-catalog-gallery\">\n @for (card of cards; track card.algo.ID) {\n <div class=\"acard\" data-testid=\"ps-catalog-card\"\n [class.hit]=\"card.bestLevel === 'Primary'\"\n [class.dim]=\"card.bestLevel && rank(card.bestLevel) <= 1\">\n @if (card.bestLevel === 'Primary') {\n <div class=\"ribbon\"><span class=\"ps-lvl ps-lvl-Primary\">Primary</span></div>\n }\n <div class=\"ah\">\n <div class=\"ic\"><i [class]=\"card.icon\"></i></div>\n <div class=\"title\">\n <div class=\"nm\">{{ card.algo.Name }}</div>\n <div class=\"ps-mono key\">{{ card.algo.DriverClass }}</div>\n </div>\n </div>\n <div class=\"ab\">\n @if (card.algo.Description) {\n <div class=\"ps-muted ps-small\">{{ card.algo.Description }}</div>\n }\n <div class=\"kv-title\">Problem types</div>\n <div class=\"meta-row\">\n @for (pt of card.problemTypes; track pt) {\n <span class=\"ps-tag\">{{ pt }}</span>\n }\n </div>\n <div class=\"meta-row\">\n @if (card.algo.SupportsFeatureImportance) {\n <span class=\"ps-badge green\"><i class=\"fa-solid fa-chart-column\"></i> Feature importance</span>\n } @else {\n <span class=\"ps-badge red\"><i class=\"fa-solid fa-ban\"></i> No importance</span>\n }\n </div>\n </div>\n <div class=\"af\">\n @if (card.bestLevel) {\n <span class=\"ps-lvl\" [class]=\"'ps-lvl-' + card.bestLevel\">{{ levelLabel(card.bestLevel) }}</span>\n }\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-catalog-details\"\n [class.on]=\"detailId === card.algo.ID\" (click)=\"toggleDetail(card.algo.ID)\">\n <i class=\"fa-solid fa-circle-info\"></i> Details\n </button>\n <button mjButton variant=\"primary\" size=\"sm\" data-testid=\"ps-catalog-use\"\n (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use\n </button>\n </div>\n @if (detailId === card.algo.ID) {\n <div class=\"adetail\" data-testid=\"ps-catalog-detail\">\n @if (card.algo.Description) {\n <p class=\"ps-small adetail-desc\">{{ card.algo.Description }}</p>\n }\n <div class=\"dkv\"><span class=\"dk\">Problem types</span><span class=\"dv\">{{ card.problemTypes.join(', ') || '—' }}</span></div>\n <div class=\"dkv\"><span class=\"dk\">Driver class</span><span class=\"dv ps-mono\">{{ card.algo.DriverClass }}</span></div>\n @if (hyperparams(card.algo).length > 0) {\n <div class=\"dk\" style=\"margin-top:8px\">Default hyperparameters</div>\n <div class=\"hp-list\">\n @for (hp of hyperparams(card.algo); track hp.k) {\n <div class=\"hp\"><span class=\"ps-mono\">{{ hp.k }}</span><span class=\"ps-mono ps-muted\">{{ hp.v }}</span></div>\n }\n </div>\n } @else {\n <div class=\"ps-small ps-muted\" style=\"margin-top:6px\">Sensible defaults are applied at training time.</div>\n }\n <div class=\"adetail-foot\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"useAlgorithm(card.algo)\">\n <i class=\"fa-solid fa-diagram-project\"></i> Use this algorithm\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class PSCatalogComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Emitted with a starter prompt to open + seed the Model Development Agent chat. */\n @Output() askAgent = new EventEmitter<string>();\n\n public selectedUseCaseIds: string[] = [];\n public cards: AlgoCardVM[] = [];\n /** The algorithm whose inline detail panel is expanded (null = none). */\n public detailId: string | null = null;\n\n ngOnInit(): void {\n this.rebuildCards();\n }\n\n public get useCases(): MJMLAlgorithmUseCaseEntity[] {\n return this.engine?.UseCases ?? [];\n }\n\n public useCaseIcon(uc: MJMLAlgorithmUseCaseEntity): string {\n const name = (uc.Name || '').toLowerCase();\n if (name.includes('binary')) return USE_CASE_ICONS['binary'];\n if (name.includes('regression')) return USE_CASE_ICONS['regression'];\n if (name.includes('interpret')) return USE_CASE_ICONS['interpret'];\n if (name.includes('tuning') || name.includes('minimal')) return USE_CASE_ICONS['tuning'];\n if (name.includes('large') || name.includes('wide') || name.includes('speed')) return USE_CASE_ICONS['large'];\n if (name.includes('embedding') || name.includes('llm')) return USE_CASE_ICONS['embedding'];\n if (name.includes('small')) return USE_CASE_ICONS['small'];\n return 'fa-solid fa-circle-dot';\n }\n\n public isSelected(id: string): boolean {\n return this.selectedUseCaseIds.some((s) => UUIDsEqual(s, id));\n }\n\n public toggleUseCase(id: string): void {\n if (this.isSelected(id)) {\n this.selectedUseCaseIds = this.selectedUseCaseIds.filter((s) => !UUIDsEqual(s, id));\n } else {\n this.selectedUseCaseIds = [...this.selectedUseCaseIds, id];\n }\n this.rebuildCards();\n }\n\n public clearScenarios(): void {\n this.selectedUseCaseIds = [];\n this.rebuildCards();\n }\n\n // ---- algorithm card actions ----\n\n /** Toggle the inline detail panel for an algorithm card. */\n public toggleDetail(id: string): void {\n this.detailId = this.detailId === id ? null : id;\n }\n\n /** Seed the Model Development Agent to build a model with the chosen algorithm. */\n public useAlgorithm(algo: MJMLAlgorithmEntity): void {\n this.askAgent.emit(\n `Help me build a predictive model using the ${algo.Name} algorithm. ` +\n `Walk me through choosing the entity, the outcome to predict, and the features to assemble.`,\n );\n }\n\n /** Parse an algorithm's `DefaultHyperparameters` JSON into a small key/value list (≤8 rows). */\n public hyperparams(algo: MJMLAlgorithmEntity): { k: string; v: string }[] {\n const raw = algo.DefaultHyperparameters;\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n return Object.entries(parsed as Record<string, unknown>)\n .map(([k, v]) => ({ k, v: String(v) }))\n .slice(0, 8);\n } catch {\n return [];\n }\n }\n\n public rank(level: RecommendationLevel): number {\n return RECOMMENDATION_RANK[level];\n }\n\n public levelLabel(level: RecommendationLevel): string {\n switch (level) {\n case 'Primary': return 'Primary fit';\n case 'Strong': return 'Strong fit';\n case 'Viable': return 'Viable';\n case 'Weak': return 'Weak fit';\n case 'NotRecommended': return 'N/A';\n }\n }\n\n /**\n * Structured recommendation data for the \"Guide me\" banner — rendered with plain template\n * interpolation + `<strong>` markup (no `[innerHTML]`). `scenarioLabel` is the human-readable\n * scenario name(s); `primaries` is the list of Primary-fit algorithm names.\n */\n public get recommendation(): { scenarioLabel: string; primaries: string[] } {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const primaries = this.engine.Algorithms.filter((a) => levels.get(a.ID) === 'Primary').map((a) => a.Name);\n const scenarioLabel =\n this.selectedUseCaseIds.length === 1\n ? this.useCases.find((u) => UUIDsEqual(u.ID, this.selectedUseCaseIds[0]))?.Name ?? 'your scenario'\n : `${this.selectedUseCaseIds.length} scenarios`;\n return { scenarioLabel, primaries };\n }\n\n private rebuildCards(): void {\n const levels = this.engine.BestLevelsForScenarios(this.selectedUseCaseIds);\n const cards: AlgoCardVM[] = this.engine.Algorithms.map((algo) => ({\n algo,\n icon: ALGO_ICONS[algo.DriverClass] ?? 'fa-solid fa-shapes',\n problemTypes: (algo.ProblemTypes || '').split(',').map((p) => p.trim()).filter((p) => p.length > 0),\n bestLevel: levels.get(algo.ID) ?? null,\n }));\n if (this.selectedUseCaseIds.length > 0) {\n cards.sort((a, b) => {\n const ra = a.bestLevel ? RECOMMENDATION_RANK[a.bestLevel] : -1;\n const rb = b.bestLevel ? RECOMMENDATION_RANK[b.bestLevel] : -1;\n return rb - ra;\n });\n }\n this.cards = cards;\n }\n}\n"]}
|
|
@@ -213,7 +213,7 @@ export class PSCompareComponent {
|
|
|
213
213
|
} if (rf & 2) {
|
|
214
214
|
i0.ɵɵadvance();
|
|
215
215
|
i0.ɵɵconditional(ctx.sessionOptions.length === 0 ? 1 : 2);
|
|
216
|
-
} }, dependencies: [CommonModule, FormsModule, i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel], 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-compare { display: flex; flex-direction: column; gap: 16px; }\n.ps-compare .cmp-toolbar { display: flex; align-items: center; gap: 12px; }\n\n/* side-by-side grid */\n.ps-compare .cmp-grid { display: grid; grid-template-columns: 200px repeat(3, 1fr); gap: 0; min-width: 720px; }\n.ps-compare .cmp-grid .stub { padding: 14px 12px; font-weight: 700; color: var(--mj-text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; border-bottom: 1px solid var(--mj-border-default); }\n.ps-compare .colhead { padding: 12px; border-top: 3px solid var(--mj-brand-primary); border-bottom: 1px solid var(--mj-border-default); border-left: 1px solid var(--mj-border-subtle); }\n.ps-compare .colhead .rn { font-weight: 700; color: var(--mj-text-primary); }\n.ps-compare .rowhdr { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); display: flex; flex-direction: column; }\n.ps-compare .rowhdr > span:first-child { color: var(--mj-text-primary); font-weight: 600; font-size: 13px; }\n.ps-compare .cell {\n padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); border-left: 1px solid var(--mj-border-subtle);\n display: flex; align-items: center; gap: 8px;\n}\n.ps-compare .cell .v { color: var(--mj-text-primary); font-weight: 600; font-variant-numeric: tabular-nums; }\n.ps-compare .cell.best { background: var(--mj-status-success-bg); }\n.ps-compare .cell.best .v { color: var(--mj-status-success-text); }\n.ps-compare .cell.warn .v { color: var(--mj-status-warning-text); }\n.ps-compare .best-pill { font-size: 9.5px; font-weight: 800; letter-spacing: .05em; color: var(--mj-status-success-text); background: color-mix(in srgb, var(--mj-status-success) 18%, transparent); padding: 1px 6px; border-radius: 999px; }\n\n/* overlay */\n.ps-compare .legend-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }\n.ps-compare .legend-row .li { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--mj-text-secondary); }\n.ps-compare .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-compare .chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-compare .chart-grid { grid-template-columns: 1fr; } }\n.ps-compare .chart-grid .span2 { grid-column: 1 / span 2; }\n@media (max-width: 980px) { .ps-compare .chart-grid .span2 { grid-column: auto; } }\n.ps-compare .chart { width: 100%; height: auto; }\n.ps-compare .chart .axis { stroke: var(--mj-border-strong); stroke-width: 1; }\n.ps-compare .chart .chance { stroke: var(--mj-border-strong); stroke-width: 1; stroke-dasharray: 4 4; }\n.ps-compare .chart .threshold { stroke: var(--mj-status-warning); stroke-width: 1.5; stroke-dasharray: 5 4; }\n.ps-compare .chart .axis-lbl { font-size: 10px; fill: var(--mj-text-muted); }\n.ps-compare .chart .warn-lbl { fill: var(--mj-status-warning-text); font-weight: 700; }\n.ps-compare .roc-legend { display: flex; gap: 14px; margin-top: 8px; }\n.ps-compare .roc-legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* champion / challenger */\n.ps-compare .reco { align-items: center; }\n.ps-compare .vs-head { display: grid; grid-template-columns: 1fr 64px 1fr; gap: 0; align-items: stretch; }\n@media (max-width: 820px) { .ps-compare .vs-head { grid-template-columns: 1fr; } }\n.ps-compare .vs-card { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 16px; background: var(--mj-bg-surface); }\n.ps-compare .vs-card.champ { border-top: 4px solid var(--mj-status-success); }\n.ps-compare .vs-card.chall { border-top: 4px solid var(--ps-node-embedding); }\n.ps-compare .vs-card .role { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700; }\n.ps-compare .vs-card.champ .role { color: var(--mj-status-success-text); }\n.ps-compare .vs-card.chall .role { color: var(--ps-node-embedding); }\n.ps-compare .vs-card .title { font-size: 16px; font-weight: 700; color: var(--mj-text-primary); margin: 8px 0 2px; }\n.ps-compare .vs-stats { display: flex; gap: 18px; margin-top: 14px; }\n.ps-compare .vs-stats strong { font-size: 20px; color: var(--mj-text-primary); }\n.ps-compare .vs-stats .warn-txt { color: var(--mj-status-warning-text); }\n.ps-compare .vs-stats .err-txt { color: var(--mj-status-error-text); }\n.ps-compare .vs-mid { display: grid; place-items: center; font-weight: 800; color: var(--mj-text-muted); background: var(--mj-bg-surface-sunken); border-top: 1px solid var(--mj-border-default); border-bottom: 1px solid var(--mj-border-default); }\n@media (max-width: 820px) { .ps-compare .vs-mid { padding: 8px; } }\n\n.ps-compare .delta-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }\n@media (max-width: 880px) { .ps-compare .delta-row { grid-template-columns: 1fr 1fr; } }\n.ps-compare .delta { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 14px; }\n.ps-compare .delta .lab { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-compare .delta .big { font-size: 20px; font-weight: 720; margin: 4px 0; display: flex; align-items: center; gap: 7px; }\n.ps-compare .delta.up .big { color: var(--mj-status-success-text); }\n.ps-compare .delta.down .big { color: var(--mj-status-error-text); }\n"], encapsulation: 2 });
|
|
216
|
+
} }, dependencies: [CommonModule, FormsModule, i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-compare { display: flex; flex-direction: column; gap: 16px; }\n.ps-compare .cmp-toolbar { display: flex; align-items: center; gap: 12px; }\n\n/* side-by-side grid */\n.ps-compare .cmp-grid { display: grid; grid-template-columns: 200px repeat(3, 1fr); gap: 0; min-width: 720px; }\n.ps-compare .cmp-grid .stub { padding: 14px 12px; font-weight: 700; color: var(--mj-text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; border-bottom: 1px solid var(--mj-border-default); }\n.ps-compare .colhead { padding: 12px; border-top: 3px solid var(--mj-brand-primary); border-bottom: 1px solid var(--mj-border-default); border-left: 1px solid var(--mj-border-subtle); }\n.ps-compare .colhead .rn { font-weight: 700; color: var(--mj-text-primary); }\n.ps-compare .rowhdr { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); display: flex; flex-direction: column; }\n.ps-compare .rowhdr > span:first-child { color: var(--mj-text-primary); font-weight: 600; font-size: 13px; }\n.ps-compare .cell {\n padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); border-left: 1px solid var(--mj-border-subtle);\n display: flex; align-items: center; gap: 8px;\n}\n.ps-compare .cell .v { color: var(--mj-text-primary); font-weight: 600; font-variant-numeric: tabular-nums; }\n.ps-compare .cell.best { background: var(--mj-status-success-bg); }\n.ps-compare .cell.best .v { color: var(--mj-status-success-text); }\n.ps-compare .cell.warn .v { color: var(--mj-status-warning-text); }\n.ps-compare .best-pill { font-size: 9.5px; font-weight: 800; letter-spacing: .05em; color: var(--mj-status-success-text); background: color-mix(in srgb, var(--mj-status-success) 18%, transparent); padding: 1px 6px; border-radius: 999px; }\n\n/* overlay */\n.ps-compare .legend-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }\n.ps-compare .legend-row .li { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--mj-text-secondary); }\n.ps-compare .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-compare .chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-compare .chart-grid { grid-template-columns: 1fr; } }\n.ps-compare .chart-grid .span2 { grid-column: 1 / span 2; }\n@media (max-width: 980px) { .ps-compare .chart-grid .span2 { grid-column: auto; } }\n.ps-compare .chart { width: 100%; height: auto; }\n.ps-compare .chart .axis { stroke: var(--mj-border-strong); stroke-width: 1; }\n.ps-compare .chart .chance { stroke: var(--mj-border-strong); stroke-width: 1; stroke-dasharray: 4 4; }\n.ps-compare .chart .threshold { stroke: var(--mj-status-warning); stroke-width: 1.5; stroke-dasharray: 5 4; }\n.ps-compare .chart .axis-lbl { font-size: 10px; fill: var(--mj-text-muted); }\n.ps-compare .chart .warn-lbl { fill: var(--mj-status-warning-text); font-weight: 700; }\n.ps-compare .roc-legend { display: flex; gap: 14px; margin-top: 8px; }\n.ps-compare .roc-legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* champion / challenger */\n.ps-compare .reco { align-items: center; }\n.ps-compare .vs-head { display: grid; grid-template-columns: 1fr 64px 1fr; gap: 0; align-items: stretch; }\n@media (max-width: 820px) { .ps-compare .vs-head { grid-template-columns: 1fr; } }\n.ps-compare .vs-card { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 16px; background: var(--mj-bg-surface); }\n.ps-compare .vs-card.champ { border-top: 4px solid var(--mj-status-success); }\n.ps-compare .vs-card.chall { border-top: 4px solid var(--ps-node-embedding); }\n.ps-compare .vs-card .role { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700; }\n.ps-compare .vs-card.champ .role { color: var(--mj-status-success-text); }\n.ps-compare .vs-card.chall .role { color: var(--ps-node-embedding); }\n.ps-compare .vs-card .title { font-size: 16px; font-weight: 700; color: var(--mj-text-primary); margin: 8px 0 2px; }\n.ps-compare .vs-stats { display: flex; gap: 18px; margin-top: 14px; }\n.ps-compare .vs-stats strong { font-size: 20px; color: var(--mj-text-primary); }\n.ps-compare .vs-stats .warn-txt { color: var(--mj-status-warning-text); }\n.ps-compare .vs-stats .err-txt { color: var(--mj-status-error-text); }\n.ps-compare .vs-mid { display: grid; place-items: center; font-weight: 800; color: var(--mj-text-muted); background: var(--mj-bg-surface-sunken); border-top: 1px solid var(--mj-border-default); border-bottom: 1px solid var(--mj-border-default); }\n@media (max-width: 820px) { .ps-compare .vs-mid { padding: 8px; } }\n\n.ps-compare .delta-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }\n@media (max-width: 880px) { .ps-compare .delta-row { grid-template-columns: 1fr 1fr; } }\n.ps-compare .delta { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 14px; }\n.ps-compare .delta .lab { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-compare .delta .big { font-size: 20px; font-weight: 720; margin: 4px 0; display: flex; align-items: center; gap: 7px; }\n.ps-compare .delta.up .big { color: var(--mj-status-success-text); }\n.ps-compare .delta.down .big { color: var(--mj-status-error-text); }\n"], encapsulation: 2 });
|
|
217
217
|
}
|
|
218
218
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSCompareComponent, [{
|
|
219
219
|
type: Component,
|
|
@@ -279,7 +279,7 @@ export class PSCompareComponent {
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
</div>
|
|
282
|
-
`, 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-compare { display: flex; flex-direction: column; gap: 16px; }\n.ps-compare .cmp-toolbar { display: flex; align-items: center; gap: 12px; }\n\n/* side-by-side grid */\n.ps-compare .cmp-grid { display: grid; grid-template-columns: 200px repeat(3, 1fr); gap: 0; min-width: 720px; }\n.ps-compare .cmp-grid .stub { padding: 14px 12px; font-weight: 700; color: var(--mj-text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; border-bottom: 1px solid var(--mj-border-default); }\n.ps-compare .colhead { padding: 12px; border-top: 3px solid var(--mj-brand-primary); border-bottom: 1px solid var(--mj-border-default); border-left: 1px solid var(--mj-border-subtle); }\n.ps-compare .colhead .rn { font-weight: 700; color: var(--mj-text-primary); }\n.ps-compare .rowhdr { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); display: flex; flex-direction: column; }\n.ps-compare .rowhdr > span:first-child { color: var(--mj-text-primary); font-weight: 600; font-size: 13px; }\n.ps-compare .cell {\n padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); border-left: 1px solid var(--mj-border-subtle);\n display: flex; align-items: center; gap: 8px;\n}\n.ps-compare .cell .v { color: var(--mj-text-primary); font-weight: 600; font-variant-numeric: tabular-nums; }\n.ps-compare .cell.best { background: var(--mj-status-success-bg); }\n.ps-compare .cell.best .v { color: var(--mj-status-success-text); }\n.ps-compare .cell.warn .v { color: var(--mj-status-warning-text); }\n.ps-compare .best-pill { font-size: 9.5px; font-weight: 800; letter-spacing: .05em; color: var(--mj-status-success-text); background: color-mix(in srgb, var(--mj-status-success) 18%, transparent); padding: 1px 6px; border-radius: 999px; }\n\n/* overlay */\n.ps-compare .legend-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }\n.ps-compare .legend-row .li { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--mj-text-secondary); }\n.ps-compare .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-compare .chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-compare .chart-grid { grid-template-columns: 1fr; } }\n.ps-compare .chart-grid .span2 { grid-column: 1 / span 2; }\n@media (max-width: 980px) { .ps-compare .chart-grid .span2 { grid-column: auto; } }\n.ps-compare .chart { width: 100%; height: auto; }\n.ps-compare .chart .axis { stroke: var(--mj-border-strong); stroke-width: 1; }\n.ps-compare .chart .chance { stroke: var(--mj-border-strong); stroke-width: 1; stroke-dasharray: 4 4; }\n.ps-compare .chart .threshold { stroke: var(--mj-status-warning); stroke-width: 1.5; stroke-dasharray: 5 4; }\n.ps-compare .chart .axis-lbl { font-size: 10px; fill: var(--mj-text-muted); }\n.ps-compare .chart .warn-lbl { fill: var(--mj-status-warning-text); font-weight: 700; }\n.ps-compare .roc-legend { display: flex; gap: 14px; margin-top: 8px; }\n.ps-compare .roc-legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* champion / challenger */\n.ps-compare .reco { align-items: center; }\n.ps-compare .vs-head { display: grid; grid-template-columns: 1fr 64px 1fr; gap: 0; align-items: stretch; }\n@media (max-width: 820px) { .ps-compare .vs-head { grid-template-columns: 1fr; } }\n.ps-compare .vs-card { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 16px; background: var(--mj-bg-surface); }\n.ps-compare .vs-card.champ { border-top: 4px solid var(--mj-status-success); }\n.ps-compare .vs-card.chall { border-top: 4px solid var(--ps-node-embedding); }\n.ps-compare .vs-card .role { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700; }\n.ps-compare .vs-card.champ .role { color: var(--mj-status-success-text); }\n.ps-compare .vs-card.chall .role { color: var(--ps-node-embedding); }\n.ps-compare .vs-card .title { font-size: 16px; font-weight: 700; color: var(--mj-text-primary); margin: 8px 0 2px; }\n.ps-compare .vs-stats { display: flex; gap: 18px; margin-top: 14px; }\n.ps-compare .vs-stats strong { font-size: 20px; color: var(--mj-text-primary); }\n.ps-compare .vs-stats .warn-txt { color: var(--mj-status-warning-text); }\n.ps-compare .vs-stats .err-txt { color: var(--mj-status-error-text); }\n.ps-compare .vs-mid { display: grid; place-items: center; font-weight: 800; color: var(--mj-text-muted); background: var(--mj-bg-surface-sunken); border-top: 1px solid var(--mj-border-default); border-bottom: 1px solid var(--mj-border-default); }\n@media (max-width: 820px) { .ps-compare .vs-mid { padding: 8px; } }\n\n.ps-compare .delta-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }\n@media (max-width: 880px) { .ps-compare .delta-row { grid-template-columns: 1fr 1fr; } }\n.ps-compare .delta { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 14px; }\n.ps-compare .delta .lab { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-compare .delta .big { font-size: 20px; font-weight: 720; margin: 4px 0; display: flex; align-items: center; gap: 7px; }\n.ps-compare .delta.up .big { color: var(--mj-status-success-text); }\n.ps-compare .delta.down .big { color: var(--mj-status-error-text); }\n"] }]
|
|
282
|
+
`, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-compare { display: flex; flex-direction: column; gap: 16px; }\n.ps-compare .cmp-toolbar { display: flex; align-items: center; gap: 12px; }\n\n/* side-by-side grid */\n.ps-compare .cmp-grid { display: grid; grid-template-columns: 200px repeat(3, 1fr); gap: 0; min-width: 720px; }\n.ps-compare .cmp-grid .stub { padding: 14px 12px; font-weight: 700; color: var(--mj-text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; border-bottom: 1px solid var(--mj-border-default); }\n.ps-compare .colhead { padding: 12px; border-top: 3px solid var(--mj-brand-primary); border-bottom: 1px solid var(--mj-border-default); border-left: 1px solid var(--mj-border-subtle); }\n.ps-compare .colhead .rn { font-weight: 700; color: var(--mj-text-primary); }\n.ps-compare .rowhdr { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); display: flex; flex-direction: column; }\n.ps-compare .rowhdr > span:first-child { color: var(--mj-text-primary); font-weight: 600; font-size: 13px; }\n.ps-compare .cell {\n padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); border-left: 1px solid var(--mj-border-subtle);\n display: flex; align-items: center; gap: 8px;\n}\n.ps-compare .cell .v { color: var(--mj-text-primary); font-weight: 600; font-variant-numeric: tabular-nums; }\n.ps-compare .cell.best { background: var(--mj-status-success-bg); }\n.ps-compare .cell.best .v { color: var(--mj-status-success-text); }\n.ps-compare .cell.warn .v { color: var(--mj-status-warning-text); }\n.ps-compare .best-pill { font-size: 9.5px; font-weight: 800; letter-spacing: .05em; color: var(--mj-status-success-text); background: color-mix(in srgb, var(--mj-status-success) 18%, transparent); padding: 1px 6px; border-radius: 999px; }\n\n/* overlay */\n.ps-compare .legend-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }\n.ps-compare .legend-row .li { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--mj-text-secondary); }\n.ps-compare .sw { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }\n.ps-compare .chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-compare .chart-grid { grid-template-columns: 1fr; } }\n.ps-compare .chart-grid .span2 { grid-column: 1 / span 2; }\n@media (max-width: 980px) { .ps-compare .chart-grid .span2 { grid-column: auto; } }\n.ps-compare .chart { width: 100%; height: auto; }\n.ps-compare .chart .axis { stroke: var(--mj-border-strong); stroke-width: 1; }\n.ps-compare .chart .chance { stroke: var(--mj-border-strong); stroke-width: 1; stroke-dasharray: 4 4; }\n.ps-compare .chart .threshold { stroke: var(--mj-status-warning); stroke-width: 1.5; stroke-dasharray: 5 4; }\n.ps-compare .chart .axis-lbl { font-size: 10px; fill: var(--mj-text-muted); }\n.ps-compare .chart .warn-lbl { fill: var(--mj-status-warning-text); font-weight: 700; }\n.ps-compare .roc-legend { display: flex; gap: 14px; margin-top: 8px; }\n.ps-compare .roc-legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* champion / challenger */\n.ps-compare .reco { align-items: center; }\n.ps-compare .vs-head { display: grid; grid-template-columns: 1fr 64px 1fr; gap: 0; align-items: stretch; }\n@media (max-width: 820px) { .ps-compare .vs-head { grid-template-columns: 1fr; } }\n.ps-compare .vs-card { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 16px; background: var(--mj-bg-surface); }\n.ps-compare .vs-card.champ { border-top: 4px solid var(--mj-status-success); }\n.ps-compare .vs-card.chall { border-top: 4px solid var(--ps-node-embedding); }\n.ps-compare .vs-card .role { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700; }\n.ps-compare .vs-card.champ .role { color: var(--mj-status-success-text); }\n.ps-compare .vs-card.chall .role { color: var(--ps-node-embedding); }\n.ps-compare .vs-card .title { font-size: 16px; font-weight: 700; color: var(--mj-text-primary); margin: 8px 0 2px; }\n.ps-compare .vs-stats { display: flex; gap: 18px; margin-top: 14px; }\n.ps-compare .vs-stats strong { font-size: 20px; color: var(--mj-text-primary); }\n.ps-compare .vs-stats .warn-txt { color: var(--mj-status-warning-text); }\n.ps-compare .vs-stats .err-txt { color: var(--mj-status-error-text); }\n.ps-compare .vs-mid { display: grid; place-items: center; font-weight: 800; color: var(--mj-text-muted); background: var(--mj-bg-surface-sunken); border-top: 1px solid var(--mj-border-default); border-bottom: 1px solid var(--mj-border-default); }\n@media (max-width: 820px) { .ps-compare .vs-mid { padding: 8px; } }\n\n.ps-compare .delta-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }\n@media (max-width: 880px) { .ps-compare .delta-row { grid-template-columns: 1fr 1fr; } }\n.ps-compare .delta { border: 1px solid var(--mj-border-default); border-radius: 10px; padding: 14px; }\n.ps-compare .delta .lab { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-compare .delta .big { font-size: 20px; font-weight: 720; margin: 4px 0; display: flex; align-items: center; gap: 7px; }\n.ps-compare .delta.up .big { color: var(--mj-status-success-text); }\n.ps-compare .delta.down .big { color: var(--mj-status-error-text); }\n"] }]
|
|
283
283
|
}], null, { engine: [{
|
|
284
284
|
type: Input
|
|
285
285
|
}] }); })();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ps-compare.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-compare.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport {\n PSCompareColumn,\n PSCompareMetricRow,\n buildCompareMetricRows,\n deriveCompareColumns,\n} from '../predictive-studio.view-models';\n\n/** A selectable session option for the compare picker. */\ninterface SessionOption {\n id: string;\n name: string;\n scoredCount: number;\n}\n\n/**\n * Compare Runs panel — a live side-by-side comparison of the top scored iterations (training runs)\n * within a selected experiment session. Fully live:\n *\n * - A session picker lists every session that has ≥2 scored iterations (only those are comparable).\n * - The top runs of the selected session are derived via {@link deriveCompareColumns} and rendered as\n * side-by-side columns; the real metric rows (holdout score, compute cost) come from\n * {@link buildCompareMetricRows}, with the best column per row highlighted.\n *\n * Empty/prompt state when nothing is comparable. 100% entity-agnostic.\n */\n@Component({\n standalone: true,\n selector: 'ps-compare',\n imports: [CommonModule, FormsModule],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.scss', './ps-compare.component.scss'],\n template: `\n <div class=\"ps-panel ps-compare\" data-testid=\"ps-compare-panel\">\n @if (sessionOptions.length === 0) {\n <div class=\"ps-empty\" data-testid=\"ps-compare-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-chart-column\"></i></span>\n <h3>Nothing to compare yet</h3>\n <p>Run an experiment with at least two scored iterations and you'll be able to line up their metrics side-by-side here to pick a winner.</p>\n </div>\n } @else {\n <!-- toolbar: session picker -->\n <div class=\"cmp-toolbar\">\n <div class=\"ps-field\" style=\"margin:0;min-width:280px\">\n <label>Session to compare</label>\n <select class=\"ps-input\" data-testid=\"ps-compare-session\" [(ngModel)]=\"selectedSessionId\" (ngModelChange)=\"rebuild()\">\n @for (s of sessionOptions; track s.id) {\n <option [value]=\"s.id\">{{ s.name }} · {{ s.scoredCount }} scored</option>\n }\n </select>\n </div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-small ps-muted\">{{ runs.length }} top runs · ranked by score</span>\n </div>\n\n @if (runs.length < 2) {\n <div class=\"ps-callout info\">\n <i class=\"fa-solid fa-circle-info\"></i>\n <div class=\"ps-small\">This session has fewer than two scored iterations — pick another session to compare.</div>\n </div>\n } @else {\n <div class=\"ps-card\" data-testid=\"ps-compare-layout-side\">\n <div class=\"ps-card-head\"><h3>Side-by-side comparison</h3><span class=\"ps-muted ps-small\">Best value in each row highlighted</span></div>\n <div class=\"ps-card-body\" style=\"overflow-x:auto\">\n <div class=\"cmp-grid\" [style.grid-template-columns]=\"gridTemplate\">\n <div class=\"stub\">Run / Metric</div>\n @for (r of runs; track r.key) {\n <div class=\"colhead\" [style.border-top-color]=\"r.color\">\n <div class=\"rn\">{{ r.label }}</div>\n <div class=\"ps-muted ps-small\">{{ r.algorithm }} · {{ r.descriptor }}</div>\n <span class=\"ps-badge\" [class]=\"r.isBest ? 'green' : 'gray'\">{{ r.isBest ? 'Top scorer' : r.status }}</span>\n </div>\n }\n @for (row of metricRows; track row.label) {\n <div class=\"rowhdr\"><span>{{ row.label }}</span><span class=\"h ps-muted ps-small\">{{ row.qualifier }}</span></div>\n @for (v of row.values; track $index) {\n <div class=\"cell\" [class.best]=\"$index === row.bestIndex\">\n <span class=\"v\">{{ v }}</span>\n @if ($index === row.bestIndex) { <span class=\"best-pill\">BEST</span> }\n </div>\n }\n }\n </div>\n </div>\n </div>\n <div class=\"ps-callout info\">\n <i class=\"fa-solid fa-circle-info\"></i>\n <div class=\"ps-small\">\n <strong>{{ verdict }}</strong>\n </div>\n </div>\n }\n }\n </div>\n `,\n})\nexport class PSCompareComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Sessions with ≥2 scored iterations — the comparable set. */\n public sessionOptions: SessionOption[] = [];\n public selectedSessionId = '';\n public runs: PSCompareColumn[] = [];\n public metricRows: PSCompareMetricRow[] = [];\n\n ngOnInit(): void {\n this.buildSessionOptions();\n this.selectedSessionId = this.sessionOptions[0]?.id ?? '';\n this.rebuild();\n }\n\n /** List sessions that have at least two scored iterations (the only ones worth comparing). */\n private buildSessionOptions(): void {\n const sessions = this.engine?.Sessions ?? [];\n this.sessionOptions = sessions\n .map((s) => {\n const scoredCount = this.engine.IterationRowsForSession(s.ID).filter((it) => it.Score != null).length;\n return { id: s.ID, name: s.Name, scoredCount };\n })\n .filter((o) => o.scoredCount >= 2);\n }\n\n /** Derive the compare columns + metric rows for the selected session. */\n public rebuild(): void {\n if (!this.selectedSessionId) {\n this.runs = [];\n this.metricRows = [];\n return;\n }\n const rows = this.engine.IterationRowsForSession(this.selectedSessionId);\n this.runs = deriveCompareColumns(rows, 3);\n this.metricRows = buildCompareMetricRows(this.runs);\n }\n\n /** CSS grid template — stub column + one fraction per run column. */\n public get gridTemplate(): string {\n return `200px repeat(${this.runs.length}, 1fr)`;\n }\n\n /** A plain-language verdict naming the best run by holdout score. */\n public get verdict(): string {\n const best = this.runs.find((r) => r.isBest);\n if (!best || best.holdoutAuc == null) return 'Pick the run that wins on the holdout score — the honest out-of-sample number.';\n return `${best.label} (${best.algorithm}) leads on the holdout score at ${best.holdoutAuc.toFixed(3)} — prefer the holdout number over training metrics when promoting.`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ps-compare.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-compare.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport {\n PSCompareColumn,\n PSCompareMetricRow,\n buildCompareMetricRows,\n deriveCompareColumns,\n} from '../predictive-studio.view-models';\n\n/** A selectable session option for the compare picker. */\ninterface SessionOption {\n id: string;\n name: string;\n scoredCount: number;\n}\n\n/**\n * Compare Runs panel — a live side-by-side comparison of the top scored iterations (training runs)\n * within a selected experiment session. Fully live:\n *\n * - A session picker lists every session that has ≥2 scored iterations (only those are comparable).\n * - The top runs of the selected session are derived via {@link deriveCompareColumns} and rendered as\n * side-by-side columns; the real metric rows (holdout score, compute cost) come from\n * {@link buildCompareMetricRows}, with the best column per row highlighted.\n *\n * Empty/prompt state when nothing is comparable. 100% entity-agnostic.\n */\n@Component({\n standalone: true,\n selector: 'ps-compare',\n imports: [CommonModule, FormsModule],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-compare.component.css'],\n template: `\n <div class=\"ps-panel ps-compare\" data-testid=\"ps-compare-panel\">\n @if (sessionOptions.length === 0) {\n <div class=\"ps-empty\" data-testid=\"ps-compare-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-chart-column\"></i></span>\n <h3>Nothing to compare yet</h3>\n <p>Run an experiment with at least two scored iterations and you'll be able to line up their metrics side-by-side here to pick a winner.</p>\n </div>\n } @else {\n <!-- toolbar: session picker -->\n <div class=\"cmp-toolbar\">\n <div class=\"ps-field\" style=\"margin:0;min-width:280px\">\n <label>Session to compare</label>\n <select class=\"ps-input\" data-testid=\"ps-compare-session\" [(ngModel)]=\"selectedSessionId\" (ngModelChange)=\"rebuild()\">\n @for (s of sessionOptions; track s.id) {\n <option [value]=\"s.id\">{{ s.name }} · {{ s.scoredCount }} scored</option>\n }\n </select>\n </div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-small ps-muted\">{{ runs.length }} top runs · ranked by score</span>\n </div>\n\n @if (runs.length < 2) {\n <div class=\"ps-callout info\">\n <i class=\"fa-solid fa-circle-info\"></i>\n <div class=\"ps-small\">This session has fewer than two scored iterations — pick another session to compare.</div>\n </div>\n } @else {\n <div class=\"ps-card\" data-testid=\"ps-compare-layout-side\">\n <div class=\"ps-card-head\"><h3>Side-by-side comparison</h3><span class=\"ps-muted ps-small\">Best value in each row highlighted</span></div>\n <div class=\"ps-card-body\" style=\"overflow-x:auto\">\n <div class=\"cmp-grid\" [style.grid-template-columns]=\"gridTemplate\">\n <div class=\"stub\">Run / Metric</div>\n @for (r of runs; track r.key) {\n <div class=\"colhead\" [style.border-top-color]=\"r.color\">\n <div class=\"rn\">{{ r.label }}</div>\n <div class=\"ps-muted ps-small\">{{ r.algorithm }} · {{ r.descriptor }}</div>\n <span class=\"ps-badge\" [class]=\"r.isBest ? 'green' : 'gray'\">{{ r.isBest ? 'Top scorer' : r.status }}</span>\n </div>\n }\n @for (row of metricRows; track row.label) {\n <div class=\"rowhdr\"><span>{{ row.label }}</span><span class=\"h ps-muted ps-small\">{{ row.qualifier }}</span></div>\n @for (v of row.values; track $index) {\n <div class=\"cell\" [class.best]=\"$index === row.bestIndex\">\n <span class=\"v\">{{ v }}</span>\n @if ($index === row.bestIndex) { <span class=\"best-pill\">BEST</span> }\n </div>\n }\n }\n </div>\n </div>\n </div>\n <div class=\"ps-callout info\">\n <i class=\"fa-solid fa-circle-info\"></i>\n <div class=\"ps-small\">\n <strong>{{ verdict }}</strong>\n </div>\n </div>\n }\n }\n </div>\n `,\n})\nexport class PSCompareComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n\n /** Sessions with ≥2 scored iterations — the comparable set. */\n public sessionOptions: SessionOption[] = [];\n public selectedSessionId = '';\n public runs: PSCompareColumn[] = [];\n public metricRows: PSCompareMetricRow[] = [];\n\n ngOnInit(): void {\n this.buildSessionOptions();\n this.selectedSessionId = this.sessionOptions[0]?.id ?? '';\n this.rebuild();\n }\n\n /** List sessions that have at least two scored iterations (the only ones worth comparing). */\n private buildSessionOptions(): void {\n const sessions = this.engine?.Sessions ?? [];\n this.sessionOptions = sessions\n .map((s) => {\n const scoredCount = this.engine.IterationRowsForSession(s.ID).filter((it) => it.Score != null).length;\n return { id: s.ID, name: s.Name, scoredCount };\n })\n .filter((o) => o.scoredCount >= 2);\n }\n\n /** Derive the compare columns + metric rows for the selected session. */\n public rebuild(): void {\n if (!this.selectedSessionId) {\n this.runs = [];\n this.metricRows = [];\n return;\n }\n const rows = this.engine.IterationRowsForSession(this.selectedSessionId);\n this.runs = deriveCompareColumns(rows, 3);\n this.metricRows = buildCompareMetricRows(this.runs);\n }\n\n /** CSS grid template — stub column + one fraction per run column. */\n public get gridTemplate(): string {\n return `200px repeat(${this.runs.length}, 1fr)`;\n }\n\n /** A plain-language verdict naming the best run by holdout score. */\n public get verdict(): string {\n const best = this.runs.find((r) => r.isBest);\n if (!best || best.holdoutAuc == null) return 'Pick the run that wins on the holdout score — the honest out-of-sample number.';\n return `${best.label} (${best.algorithm}) leads on the holdout score at ${best.holdoutAuc.toFixed(3)} — prefer the holdout number over training metrics when promoting.`;\n }\n}\n"]}
|