@memberjunction/ng-clustering 6.1.0-edge.5 → 6.1.0-edge.6

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.
@@ -863,7 +863,7 @@ export class ClusterConfigPanelComponent {
863
863
  } }, inputs: { IsRunning: "IsRunning", Metrics: "Metrics", EntityOptions: "EntityOptions", EntityDocOptions: "EntityDocOptions", AllEntityDocOptions: "AllEntityDocOptions", ShowDimensionToggle: "ShowDimensionToggle", ShowMultiEntity: "ShowMultiEntity", ShowSaveButton: "ShowSaveButton", ShowAlgorithmPicker: "ShowAlgorithmPicker", ShowMetricPicker: "ShowMetricPicker", DefaultAlgorithm: "DefaultAlgorithm", AvailableEntities: "AvailableEntities", Collapsed: "Collapsed" }, outputs: { RunClustering: "RunClustering", SaveVisualization: "SaveVisualization", BeforeRunClustering: "BeforeRunClustering", BeforeSave: "BeforeSave", ConfigChanged: "ConfigChanged", AlgorithmChanged: "AlgorithmChanged", DimensionsChanged: "DimensionsChanged" }, standalone: false, decls: 9, vars: 9, consts: [["panelElement", ""], [1, "config-panel"], [1, "panel-header", 3, "mousedown"], [1, "fa-solid", "fa-sliders"], [1, "header-actions"], ["title", "Toggle panel", 1, "fa-solid", 3, "click"], [1, "fa-solid", "fa-grip-dots-vertical", "drag-handle"], [1, "form-group"], [3, "ngModelChange", "ngModel"], [3, "value"], [1, "form-group", "multi-source"], [1, "form-row", "toggle-row"], ["role", "group", 1, "seg-toggle"], ["type", "button", 1, "seg-btn", 3, "click"], ["type", "text", "placeholder", "e.g. Status = 'Active'", 3, "ngModelChange", "ngModel"], [1, "form-row"], ["type", "number", 3, "ngModelChange", "ngModel", "min", "max"], [1, "action-row"], [1, "run-btn", 3, "click", "disabled"], ["title", "Save this visualization", 1, "save-btn"], [1, "metrics-section"], ["Size", "sm", 3, "ExpandedChange", "FlushBody", "Expanded"], ["mjAccordionTitle", ""], ["mjAccordionBody", ""], [1, "mj-accordion-badge", "mj-accordion-badge--right"], [1, "multi-source-list"], [1, "multi-source-hint"], [1, "multi-source-item"], ["type", "checkbox", 3, "change", "checked"], [1, "multi-source-name"], [1, "multi-source-entity"], ["type", "range", 3, "ngModelChange", "ngModel", "min", "max", "step"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"], ["title", "Save this visualization", 1, "save-btn", 3, "click"], [1, "fa-solid", "fa-bookmark"], [1, "metric-item"], [1, "metric-label"], [1, "metric-value"]], template: function ClusterConfigPanelComponent_Template(rf, ctx) { if (rf & 1) {
864
864
  const _r1 = i0.ɵɵgetCurrentView();
865
865
  i0.ɵɵelementStart(0, "div", 1, 0)(2, "h3", 2);
866
- i0.ɵɵlistener("mousedown", function ClusterConfigPanelComponent_Template_h3_mousedown_2_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnHeaderMouseDown($event)); });
866
+ i0.ɵɵlistener("mousedown", function ClusterConfigPanelComponent_Template_h3_mousedown_2_listener($event) { return ctx.OnHeaderMouseDown($event); });
867
867
  i0.ɵɵelement(3, "i", 3);
868
868
  i0.ɵɵtext(4, " Configuration ");
869
869
  i0.ɵɵelementStart(5, "span", 4)(6, "i", 5);
@@ -879,7 +879,7 @@ export class ClusterConfigPanelComponent {
879
879
  i0.ɵɵclassProp("fa-chevron-up", ctx.IsExpanded)("fa-chevron-down", !ctx.IsExpanded);
880
880
  i0.ɵɵadvance(2);
881
881
  i0.ɵɵconditional(ctx.IsExpanded ? 8 : -1);
882
- } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.NumberValueAccessor, i1.RangeValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.MinValidator, i1.MaxValidator, i1.NgModel, i2.MJAccordionPanelComponent, i2.MJAccordionTitleDirective, i2.MJAccordionBodyDirective], styles: [".config-panel[_ngcontent-%COMP%] {\n position: absolute;\n width: 300px;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 95%, transparent);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--mj-border-default);\n border-radius: 14px;\n padding: 20px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n z-index: 10;\n}\n\n.panel-header[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n font-weight: 600;\n margin: 0 0 16px 0;\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-primary);\n cursor: move;\n user-select: none;\n}\n\n.panel-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child {\n color: var(--mj-brand-primary);\n}\n\n.header-actions[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.header-actions[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n cursor: pointer;\n transition: color 0.15s;\n}\n\n.header-actions[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n\n.drag-handle[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n\n\n.form-group[_ngcontent-%COMP%] {\n margin-bottom: 14px;\n}\n\n.form-group[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n font-size: 0.7rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin-bottom: 5px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] input[type=\"number\"][_ngcontent-%COMP%] {\n width: 100%;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n transition: border 0.15s;\n}\n\n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] input[type=\"number\"][_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.form-group[_ngcontent-%COMP%] input[type=\"range\"][_ngcontent-%COMP%] {\n width: 100%;\n accent-color: var(--mj-brand-primary);\n}\n\n.form-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 10px;\n}\n\n.form-row[_ngcontent-%COMP%] .form-group[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.action-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-top: 4px;\n}\n\n.run-btn[_ngcontent-%COMP%] {\n flex: 1;\n padding: 10px;\n background: var(--mj-brand-primary);\n border: none;\n border-radius: 8px;\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: background 0.15s;\n}\n\n.run-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.run-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-btn[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s;\n}\n\n.save-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n\n\n.toggle-row[_ngcontent-%COMP%] .form-group[_ngcontent-%COMP%] { min-width: 0; }\n\n.seg-toggle[_ngcontent-%COMP%] {\n display: inline-flex;\n gap: 2px;\n padding: 3px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n width: 100%;\n}\n\n.seg-btn[_ngcontent-%COMP%] {\n flex: 1;\n padding: 5px 8px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.75rem;\n font-weight: 600;\n border-radius: 6px;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n\n.seg-btn[_ngcontent-%COMP%]:hover { color: var(--mj-text-primary); }\n\n.seg-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 12%, transparent);\n}\n\n\n\n\n\n\n\n\n.multi-source-list[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 8px;\n max-height: 180px;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n}\n\n.multi-source-hint[_ngcontent-%COMP%] {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n line-height: 1.3;\n}\n\n.multi-source-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 4px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n}\n\n.multi-source-item[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); border-radius: 6px; }\n\n.multi-source-entity[_ngcontent-%COMP%] {\n margin-left: auto;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n\n\n.metrics-section[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n margin-top: 14px;\n padding-top: 14px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.metric-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.metric-value.good[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}"] }); }
882
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.NumberValueAccessor, i1.RangeValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.MinValidator, i1.MaxValidator, i1.NgModel, i2.MJAccordionPanelComponent, i2.MJAccordionTitleDirective, i2.MJAccordionBodyDirective], styles: [".config-panel[_ngcontent-%COMP%] {\n position: absolute;\n width: 300px;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 95%, transparent);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--mj-border-default);\n border-radius: 14px;\n padding: 20px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-bg-page) 40%, transparent);\n z-index: 10;\n}\n\n.panel-header[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n font-weight: 600;\n margin: 0 0 16px 0;\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-primary);\n cursor: move;\n user-select: none;\n}\n\n.panel-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child {\n color: var(--mj-brand-primary);\n}\n\n.header-actions[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.header-actions[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n cursor: pointer;\n transition: color 0.15s;\n}\n\n.header-actions[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n\n.drag-handle[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n\n.form-group[_ngcontent-%COMP%] {\n margin-bottom: 14px;\n}\n\n.form-group[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n font-size: 0.7rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin-bottom: 5px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] input[type=\"number\"][_ngcontent-%COMP%] {\n width: 100%;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n transition: border 0.15s;\n}\n\n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] input[type=\"number\"][_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.form-group[_ngcontent-%COMP%] input[type=\"range\"][_ngcontent-%COMP%] {\n width: 100%;\n accent-color: var(--mj-brand-primary);\n}\n\n.form-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 10px;\n}\n\n.form-row[_ngcontent-%COMP%] .form-group[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n.action-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-top: 4px;\n}\n\n.run-btn[_ngcontent-%COMP%] {\n flex: 1;\n padding: 10px;\n background: var(--mj-brand-primary);\n border: none;\n border-radius: 8px;\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: background 0.15s;\n}\n\n.run-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.run-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-btn[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s;\n}\n\n.save-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n\n.toggle-row[_ngcontent-%COMP%] .form-group[_ngcontent-%COMP%] { min-width: 0; }\n\n.seg-toggle[_ngcontent-%COMP%] {\n display: inline-flex;\n gap: 2px;\n padding: 3px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n width: 100%;\n}\n\n.seg-btn[_ngcontent-%COMP%] {\n flex: 1;\n padding: 5px 8px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.75rem;\n font-weight: 600;\n border-radius: 6px;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n\n.seg-btn[_ngcontent-%COMP%]:hover { color: var(--mj-text-primary); }\n\n.seg-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 12%, transparent);\n}\n\n\n\n\n\n\n.multi-source-list[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 8px;\n max-height: 180px;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n}\n\n.multi-source-hint[_ngcontent-%COMP%] {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n line-height: 1.3;\n}\n\n.multi-source-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 4px;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n}\n\n.multi-source-item[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); border-radius: 6px; }\n\n.multi-source-entity[_ngcontent-%COMP%] {\n margin-left: auto;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n\n.metrics-section[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n margin-top: 14px;\n padding-top: 14px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.metric-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.metric-value.good[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}"] }); }
883
883
  }
884
884
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClusterConfigPanelComponent, [{
885
885
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"cluster-config-panel.component.js","sourceRoot":"","sources":["../../src/lib/cluster-config-panel.component.ts","../../src/lib/cluster-config-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Floating glass-morphism configuration panel for clustering.\n *\n * `ClusterConfigPanelComponent` provides a draggable floating panel with\n * entity picker, algorithm picker, parameter sliders, distance metric\n * selector, max records input, run button, and result metrics display.\n *\n * It emits cancelable `BeforeXXX` events so consumers can intercept and\n * validate before clustering starts or a visualization is saved.\n *\n * @example\n * ```html\n * <mj-cluster-config-panel\n * [IsRunning]=\"isRunning\"\n * [Metrics]=\"result?.Metrics ?? null\"\n * [EntityOptions]=\"myEntityOptions\"\n * [ShowSaveButton]=\"true\"\n * [DefaultAlgorithm]=\"'dbscan'\"\n * [Collapsed]=\"false\"\n * (BeforeRunClustering)=\"onBeforeRun($event)\"\n * (RunClustering)=\"onRunClustering($event)\"\n * (ConfigChanged)=\"onConfigChanged($event)\"\n * (SaveVisualization)=\"onSave()\">\n * </mj-cluster-config-panel>\n * ```\n */\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n ChangeDetectorRef,\n OnInit,\n OnDestroy,\n ViewChild,\n inject,\n} from '@angular/core';\nimport {\n ClusterConfig,\n ClusterConfigPanelEntityOption,\n ClusterConfigPanelEntityDocOption,\n ClusterMetrics,\n ClusterAlgorithm,\n ClusterDistanceMetric,\n CancelableEvent,\n DefaultClusterConfig,\n} from './clustering.types';\n\n@Component({\n standalone: false,\n selector: 'mj-cluster-config-panel',\n templateUrl: './cluster-config-panel.component.html',\n styleUrls: ['./cluster-config-panel.component.css'],\n})\nexport class ClusterConfigPanelComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * Whether a clustering run is currently in progress.\n *\n * When `true`, the Run button is disabled and shows a spinner.\n *\n * @default false\n */\n @Input() IsRunning = false;\n\n /**\n * Result metrics from the most recent clustering run.\n *\n * When non-null, the metrics section is displayed at the bottom\n * of the panel showing silhouette score, cluster count, etc.\n */\n @Input() Metrics: ClusterMetrics | null = null;\n\n /**\n * Entity options for the dropdown.\n *\n * The parent component is responsible for populating this list,\n * typically from MJ Metadata or a subset filtered by\n * `AvailableEntities`.\n */\n private _entityOptions: ClusterConfigPanelEntityOption[] = [];\n\n @Input()\n set EntityOptions(value: ClusterConfigPanelEntityOption[]) {\n this._entityOptions = value || [];\n this.rebuildFilteredEntities();\n }\n get EntityOptions(): ClusterConfigPanelEntityOption[] {\n return this._entityOptions;\n }\n\n /** Entity Document options for the selected entity. Only shown when 2+ docs exist. */\n @Input() EntityDocOptions: ClusterConfigPanelEntityDocOption[] = [];\n\n /**\n * All Entity Documents across entities (each with `EntityName`), used to power\n * the optional multi-entity source selector. When two or more are selected the\n * run becomes a multi-entity analysis (their vectors are merged server-side).\n */\n @Input() AllEntityDocOptions: ClusterConfigPanelEntityDocOption[] = [];\n\n /** Whether to show the 2D/3D projection toggle. @default true */\n @Input() ShowDimensionToggle = true;\n\n /** Whether to show the multi-entity source selector. @default true */\n @Input() ShowMultiEntity = true;\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Whether to display the \"Save this visualization\" link.\n *\n * @default true\n */\n @Input() ShowSaveButton = true;\n\n /**\n * Whether to display the algorithm picker dropdown.\n *\n * When `false`, the algorithm is fixed to `DefaultAlgorithm`.\n *\n * @default true\n */\n @Input() ShowAlgorithmPicker = true;\n\n /**\n * Whether to display the distance metric picker dropdown.\n *\n * @default true\n */\n @Input() ShowMetricPicker = true;\n\n /**\n * Default algorithm selection when the panel initializes.\n *\n * @default 'kmeans'\n */\n @Input() DefaultAlgorithm: ClusterAlgorithm = 'kmeans';\n\n /**\n * Restrict the entity picker to a specific list of entity names.\n *\n * When empty (the default), all entity options from the\n * `EntityOptions` input are shown. When non-empty, only entities\n * whose `Name` appears in this array are shown.\n *\n * @default []\n *\n * @example\n * ```html\n * <mj-cluster-config-panel\n * [AvailableEntities]=\"['Companies', 'Contacts', 'Deals']\">\n * </mj-cluster-config-panel>\n * ```\n */\n @Input() AvailableEntities: string[] = [];\n\n /**\n * Whether the panel starts in a collapsed (minimized) state.\n *\n * The user can toggle between collapsed and expanded by clicking\n * the collapse chevron in the panel header.\n *\n * @default false\n */\n @Input() Collapsed = false;\n\n // ================================================================\n // Standard Outputs\n // ================================================================\n\n /**\n * Emitted when the user clicks Run and `BeforeRunClustering` was not\n * canceled. Payload is a snapshot of the current config.\n */\n @Output() RunClustering = new EventEmitter<ClusterConfig>();\n\n /**\n * Emitted when the user clicks the Save link and `BeforeSave` was\n * not canceled.\n */\n @Output() SaveVisualization = new EventEmitter<void>();\n\n // ================================================================\n // Cancelable Outputs\n // ================================================================\n\n /**\n * Fires **before** a clustering run starts. Set `event.Cancel = true`\n * to prevent the run (e.g., for validation).\n *\n * @example\n * ```typescript\n * onBeforeRun(event: CancelableEvent<ClusterConfig>): void {\n * if (!event.Data.EntityName) {\n * event.Cancel = true;\n * alert('Please select an entity first.');\n * }\n * }\n * ```\n */\n @Output() BeforeRunClustering = new EventEmitter<CancelableEvent<ClusterConfig>>();\n\n /**\n * Fires **before** a visualization is saved. Set `event.Cancel = true`\n * to prevent the save (e.g., to prompt for a name first).\n */\n @Output() BeforeSave = new EventEmitter<CancelableEvent<ClusterConfig>>();\n\n // ================================================================\n // Informational Outputs\n // ================================================================\n\n /**\n * Fires whenever any configuration value changes (entity, algorithm,\n * K, epsilon, distance metric, max records, filter).\n *\n * Payload is a snapshot of the entire config at the time of change.\n */\n @Output() ConfigChanged = new EventEmitter<ClusterConfig>();\n\n /**\n * Fires specifically when the algorithm selection changes.\n *\n * Useful for showing/hiding algorithm-specific UI in the parent.\n */\n @Output() AlgorithmChanged = new EventEmitter<ClusterAlgorithm>();\n\n /**\n * Fires when the 2D/3D projection toggle changes. Hosts should re-run (or\n * re-project) so the change is reflected immediately — a 3D projection needs\n * a Z coordinate that only a fresh run produces.\n */\n @Output() DimensionsChanged = new EventEmitter<2 | 3>();\n\n // ================================================================\n // Internal State\n // ================================================================\n\n /** The working configuration object. */\n public Config: ClusterConfig = DefaultClusterConfig();\n\n /** Filtered entity list for display (filtered by AvailableEntities). */\n public FilteredEntityOptions: ClusterConfigPanelEntityOption[] = [];\n\n /** Whether the panel body is currently visible. */\n public IsExpanded = true;\n\n /** Whether the multi-entity source selector is expanded. */\n public ShowMultiSource = false;\n\n /** Algorithm options for the dropdown. */\n public AlgorithmOptions: Array<{ Value: ClusterAlgorithm; Label: string }> = [\n { Value: 'kmeans', Label: 'K-Means' },\n { Value: 'dbscan', Label: 'DBSCAN' },\n ];\n\n /** Distance metric options for the dropdown. */\n public MetricOptions: Array<{ Value: ClusterDistanceMetric; Label: string }> = [\n { Value: 'cosine', Label: 'Cosine' },\n { Value: 'euclidean', Label: 'Euclidean' },\n { Value: 'dotproduct', Label: 'Dot Product' },\n ];\n\n // Dragging state\n /** Current panel top offset in pixels. */\n public PanelTop = 12;\n /** Current panel left offset in pixels. */\n public PanelLeft = 12;\n private isDragging = false;\n private dragStartX = 0;\n private dragStartY = 0;\n private dragStartTop = 0;\n private dragStartLeft = 0;\n private boundOnMouseMove: ((e: MouseEvent) => void) | null = null;\n private boundOnMouseUp: ((e: MouseEvent) => void) | null = null;\n\n @ViewChild('panelElement', { static: false }) private panelRef!: ElementRef<HTMLDivElement>;\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnInit(): void {\n // Apply defaults\n this.Config.Algorithm = this.DefaultAlgorithm;\n this.IsExpanded = !this.Collapsed;\n\n this.rebuildFilteredEntities();\n\n this.boundOnMouseMove = this.onMouseMove.bind(this);\n this.boundOnMouseUp = this.onMouseUp.bind(this);\n document.addEventListener('mousemove', this.boundOnMouseMove);\n document.addEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnDestroy(): void {\n if (this.boundOnMouseMove) document.removeEventListener('mousemove', this.boundOnMouseMove);\n if (this.boundOnMouseUp) document.removeEventListener('mouseup', this.boundOnMouseUp);\n }\n\n // ================================================================\n // Public Template Methods\n // ================================================================\n\n /**\n * Whether the current algorithm is K-Means (controls which\n * parameter sliders are shown).\n */\n public get IsKMeans(): boolean {\n return this.Config.Algorithm === 'kmeans';\n }\n\n /**\n * Handle algorithm dropdown change.\n *\n * @param value New algorithm value string.\n */\n public OnAlgorithmChanged(value: string): void {\n const newAlg = value as ClusterAlgorithm;\n this.Config.Algorithm = newAlg;\n this.AlgorithmChanged.emit(newAlg);\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /**\n * Handle distance metric dropdown change.\n *\n * @param value New metric value string.\n */\n public OnMetricChanged(value: string): void {\n this.Config.DistanceMetric = value as ClusterDistanceMetric;\n this.emitConfigChanged();\n }\n\n /**\n * Handle entity dropdown change.\n *\n * @param value New entity name.\n */\n public OnEntityChanged(value: string): void {\n this.Config.EntityName = value;\n this.emitConfigChanged();\n }\n\n /**\n * Handle changes to any numeric/text config field (K, Epsilon,\n * MinPoints, MaxRecords, Filter).\n */\n public OnFieldChanged(): void {\n this.emitConfigChanged();\n }\n\n // ----- Multi-entity / dimensions / color-by -----\n\n /** The currently selected entity-document IDs for multi-entity clustering. */\n public get SelectedDocIDs(): string[] {\n return this.Config.EntityDocumentIDs ?? [];\n }\n\n /** True when two or more source documents are selected (multi-entity mode). */\n public get IsMultiEntity(): boolean {\n return this.SelectedDocIDs.length >= 2;\n }\n\n /** Whether there are enough documents to offer multi-entity selection. */\n public get HasMultiSourceOptions(): boolean {\n return this.ShowMultiEntity && this.AllEntityDocOptions.length > 1;\n }\n\n /** Whether a given document is currently selected. */\n public IsDocSelected(id: string): boolean {\n return this.SelectedDocIDs.includes(id);\n }\n\n /** Toggle a document in/out of the multi-entity selection. */\n public ToggleDoc(id: string): void {\n const set = new Set(this.Config.EntityDocumentIDs ?? []);\n if (set.has(id)) {\n set.delete(id);\n } else {\n set.add(id);\n }\n this.Config.EntityDocumentIDs = [...set];\n // Once a multi-entity selection exists, color-by-entity is the useful default.\n if (this.IsMultiEntity && this.Config.ColorBy !== 'entity') {\n this.Config.ColorBy = 'entity';\n }\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /** Handle the multi-source accordion's expand/collapse. */\n public OnMultiSourceExpandedChange(expanded: boolean): void {\n this.ShowMultiSource = expanded;\n this.cdr.detectChanges();\n }\n\n /** Set the projection dimensionality (2 or 3). */\n public SetDimensions(d: 2 | 3): void {\n if (this.Config.Dimensions === d) return;\n this.Config.Dimensions = d;\n this.emitConfigChanged();\n this.DimensionsChanged.emit(d);\n this.cdr.detectChanges();\n }\n\n /** Set the legend/color mode. */\n public SetColorBy(mode: 'cluster' | 'entity'): void {\n this.Config.ColorBy = mode;\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /** Whether the Run button can be clicked. */\n public get CanRun(): boolean {\n return !this.IsRunning && (!!this.Config.EntityName || this.SelectedDocIDs.length > 0);\n }\n\n /** Whether the Save button should be offered (there are results to save). */\n public get CanSave(): boolean {\n return this.ShowSaveButton && !!this.Metrics && this.Metrics.RecordCount > 0;\n }\n\n /**\n * Handle Run button click. Fires `BeforeRunClustering`, and if not\n * canceled, emits `RunClustering`.\n */\n public OnRun(): void {\n if (!this.CanRun) return;\n\n const snapshot = { ...this.Config };\n const cancelable: CancelableEvent<ClusterConfig> = { Data: snapshot, Cancel: false };\n this.BeforeRunClustering.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.RunClustering.emit(snapshot);\n }\n\n /**\n * Handle Save link click. Fires `BeforeSave`, and if not canceled,\n * emits `SaveVisualization`.\n */\n public OnSave(): void {\n const cancelable: CancelableEvent<ClusterConfig> = { Data: { ...this.Config }, Cancel: false };\n this.BeforeSave.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.SaveVisualization.emit();\n }\n\n /**\n * Toggle the panel between expanded and collapsed states.\n */\n public ToggleCollapse(): void {\n this.IsExpanded = !this.IsExpanded;\n this.cdr.detectChanges();\n }\n\n /**\n * Format a silhouette score to 2 decimal places.\n *\n * @param score The raw silhouette score.\n * @returns Formatted string (e.g., `\"0.72\"`).\n */\n public FormatSilhouette(score: number): string {\n return score.toFixed(2);\n }\n\n /**\n * Whether a silhouette score indicates good clustering quality.\n *\n * @param score The silhouette score to evaluate.\n * @returns `true` if score >= 0.5.\n */\n public IsSilhouetteGood(score: number): boolean {\n return score >= 0.5;\n }\n\n /**\n * Format a computation time in milliseconds to a human-readable string.\n *\n * @param ms Milliseconds.\n * @returns e.g., `\"1.2s\"` or `\"450ms\"`.\n */\n public FormatTime(ms: number): string {\n return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;\n }\n\n /**\n * Start dragging the panel via header mousedown.\n *\n * @param event The native mouse event.\n */\n public OnHeaderMouseDown(event: MouseEvent): void {\n event.preventDefault();\n this.isDragging = true;\n this.dragStartX = event.clientX;\n this.dragStartY = event.clientY;\n this.dragStartTop = this.PanelTop;\n this.dragStartLeft = this.PanelLeft;\n }\n\n /** @internal TrackBy for algorithm options. */\n public TrackAlgorithmBy(_index: number, item: { Value: string }): string {\n return item.Value;\n }\n\n /** @internal TrackBy for metric options. */\n public TrackMetricBy(_index: number, item: { Value: string }): string {\n return item.Value;\n }\n\n /** @internal TrackBy for entity options. */\n public TrackEntityBy(_index: number, item: ClusterConfigPanelEntityOption): string {\n return item.Name;\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n /**\n * Rebuild the filtered entity list based on `AvailableEntities`.\n */\n private rebuildFilteredEntities(): void {\n if (this.AvailableEntities.length > 0) {\n const allowedSet = new Set(this.AvailableEntities);\n this.FilteredEntityOptions = this.EntityOptions.filter(e => allowedSet.has(e.Name));\n } else {\n this.FilteredEntityOptions = this.EntityOptions;\n }\n\n if (this.FilteredEntityOptions.length > 0 && !this.Config.EntityName) {\n this.Config.EntityName = this.FilteredEntityOptions[0].Name;\n }\n }\n\n /** Emit `ConfigChanged` with a snapshot of the current config. */\n private emitConfigChanged(): void {\n this.ConfigChanged.emit({ ...this.Config });\n }\n\n private onMouseMove(event: MouseEvent): void {\n if (!this.isDragging) return;\n const dx = event.clientX - this.dragStartX;\n const dy = event.clientY - this.dragStartY;\n this.PanelTop = Math.max(0, this.dragStartTop + dy);\n this.PanelLeft = Math.max(0, this.dragStartLeft + dx);\n this.cdr.detectChanges();\n }\n\n private onMouseUp(_event: MouseEvent): void {\n this.isDragging = false;\n }\n}\n","<div #panelElement\n class=\"config-panel\"\n [style.top.px]=\"PanelTop\"\n [style.left.px]=\"PanelLeft\">\n\n <!-- Draggable header -->\n <h3 class=\"panel-header\"\n (mousedown)=\"OnHeaderMouseDown($event)\">\n <i class=\"fa-solid fa-sliders\"></i>\n Configuration\n <span class=\"header-actions\">\n <i class=\"fa-solid\"\n [class.fa-chevron-up]=\"IsExpanded\"\n [class.fa-chevron-down]=\"!IsExpanded\"\n (click)=\"ToggleCollapse(); $event.stopPropagation()\"\n title=\"Toggle panel\"></i>\n <i class=\"fa-solid fa-grip-dots-vertical drag-handle\"></i>\n </span>\n </h3>\n\n @if (IsExpanded) {\n <!-- Entity picker -->\n <div class=\"form-group\">\n <label>Entity</label>\n <select [ngModel]=\"Config.EntityName\"\n (ngModelChange)=\"OnEntityChanged($event)\">\n @for (entity of FilteredEntityOptions; track TrackEntityBy($index, entity)) {\n <option [value]=\"entity.Name\">{{ entity.Name }}</option>\n }\n </select>\n </div>\n\n <!-- Entity Document selector (only shown when 2+ docs for selected entity) -->\n @if (EntityDocOptions.length > 1) {\n <div class=\"form-group\">\n <label>Document Template</label>\n <select [ngModel]=\"Config.EntityDocumentID\"\n (ngModelChange)=\"Config.EntityDocumentID = $event; OnFieldChanged()\">\n @for (doc of EntityDocOptions; track doc.ID) {\n <option [value]=\"doc.ID\">{{ doc.Name }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Multi-entity source selector -->\n @if (HasMultiSourceOptions) {\n <div class=\"form-group multi-source\">\n <mj-accordion-panel\n Size=\"sm\"\n [FlushBody]=\"true\"\n [Expanded]=\"ShowMultiSource\"\n (ExpandedChange)=\"OnMultiSourceExpandedChange($event)\">\n <ng-template mjAccordionTitle>\n Multiple sources\n @if (IsMultiEntity) {\n <span class=\"mj-accordion-badge mj-accordion-badge--right\">{{ SelectedDocIDs.length }}</span>\n }\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"multi-source-list\">\n <div class=\"multi-source-hint\">Select 2+ documents to cluster them together. All must use the same embedding model.</div>\n @for (doc of AllEntityDocOptions; track doc.ID) {\n <label class=\"multi-source-item\">\n <input type=\"checkbox\" [checked]=\"IsDocSelected(doc.ID)\" (change)=\"ToggleDoc(doc.ID)\">\n <span class=\"multi-source-name\">{{ doc.Name }}</span>\n @if (doc.EntityName) { <span class=\"multi-source-entity\">{{ doc.EntityName }}</span> }\n </label>\n }\n </div>\n </ng-template>\n </mj-accordion-panel>\n </div>\n }\n\n <!-- Projection (2D / 3D) + Color-by toggles -->\n <div class=\"form-row toggle-row\">\n @if (ShowDimensionToggle) {\n <div class=\"form-group\">\n <label>Projection</label>\n <div class=\"seg-toggle\" role=\"group\">\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"(Config.Dimensions ?? 2) === 2\" (click)=\"SetDimensions(2)\">2D</button>\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"Config.Dimensions === 3\" (click)=\"SetDimensions(3)\">3D</button>\n </div>\n </div>\n }\n <div class=\"form-group\">\n <label>Color by</label>\n <div class=\"seg-toggle\" role=\"group\">\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"(Config.ColorBy ?? 'cluster') === 'cluster'\" (click)=\"SetColorBy('cluster')\">Cluster</button>\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"Config.ColorBy === 'entity'\" (click)=\"SetColorBy('entity')\">Entity</button>\n </div>\n </div>\n </div>\n\n <!-- Filter -->\n <div class=\"form-group\">\n <label>Filter (optional)</label>\n <input type=\"text\"\n [(ngModel)]=\"Config.Filter\"\n (ngModelChange)=\"OnFieldChanged()\"\n placeholder=\"e.g. Status = 'Active'\">\n </div>\n\n <!-- Algorithm + K/Epsilon row -->\n <div class=\"form-row\">\n @if (ShowAlgorithmPicker) {\n <div class=\"form-group\">\n <label>Algorithm</label>\n <select [ngModel]=\"Config.Algorithm\"\n (ngModelChange)=\"OnAlgorithmChanged($event)\">\n @for (alg of AlgorithmOptions; track TrackAlgorithmBy($index, alg)) {\n <option [value]=\"alg.Value\">{{ alg.Label }}</option>\n }\n </select>\n </div>\n }\n\n @if (IsKMeans) {\n <div class=\"form-group\">\n <label>Clusters (K): {{ Config.K }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.K\"\n (ngModelChange)=\"Config.K = $event; OnFieldChanged()\"\n [min]=\"2\"\n [max]=\"20\"\n [step]=\"1\">\n </div>\n } @else {\n <div class=\"form-group\">\n <label>Epsilon: {{ Config.Epsilon }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.Epsilon\"\n (ngModelChange)=\"Config.Epsilon = $event; OnFieldChanged()\"\n [min]=\"0.05\"\n [max]=\"0.95\"\n [step]=\"0.05\">\n </div>\n }\n </div>\n\n <!-- DBSCAN MinPoints row (only visible for DBSCAN) -->\n @if (!IsKMeans) {\n <div class=\"form-group\">\n <label>Min Points: {{ Config.MinPoints }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.MinPoints\"\n (ngModelChange)=\"Config.MinPoints = $event; OnFieldChanged()\"\n [min]=\"2\"\n [max]=\"20\"\n [step]=\"1\">\n </div>\n }\n\n <!-- Distance metric + Max records row -->\n <div class=\"form-row\">\n @if (ShowMetricPicker) {\n <div class=\"form-group\">\n <label>Distance Metric</label>\n <select [ngModel]=\"Config.DistanceMetric\"\n (ngModelChange)=\"OnMetricChanged($event)\">\n @for (metric of MetricOptions; track TrackMetricBy($index, metric)) {\n <option [value]=\"metric.Value\">{{ metric.Label }}</option>\n }\n </select>\n </div>\n }\n <div class=\"form-group\">\n <label>Max Records</label>\n <input type=\"number\"\n [(ngModel)]=\"Config.MaxRecords\"\n (ngModelChange)=\"OnFieldChanged()\"\n [min]=\"10\"\n [max]=\"5000\">\n </div>\n </div>\n\n <!-- Run + Save action row -->\n <div class=\"action-row\">\n <button class=\"run-btn\"\n [disabled]=\"!CanRun\"\n (click)=\"OnRun()\">\n @if (IsRunning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run Clustering\n }\n </button>\n @if (CanSave) {\n <button class=\"save-btn\" (click)=\"OnSave()\" title=\"Save this visualization\">\n <i class=\"fa-solid fa-bookmark\"></i> Save\n </button>\n }\n </div>\n\n <!-- Metrics display -->\n @if (Metrics && Metrics.RecordCount > 0) {\n <div class=\"metrics-section\">\n <div class=\"metric-item\">\n <span class=\"metric-label\">Silhouette</span>\n <span class=\"metric-value\"\n [class.good]=\"IsSilhouetteGood(Metrics.SilhouetteScore)\">\n {{ FormatSilhouette(Metrics.SilhouetteScore) }}\n </span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Clusters</span>\n <span class=\"metric-value\">{{ Metrics.ClusterCount }}</span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Records</span>\n <span class=\"metric-value\">{{ Metrics.RecordCount }}</span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Time</span>\n <span class=\"metric-value\">{{ FormatTime(Metrics.ComputationTimeMs) }}</span>\n </div>\n @if (Metrics.OutlierCount > 0) {\n <div class=\"metric-item\">\n <span class=\"metric-label\">Outliers</span>\n <span class=\"metric-value\">{{ Metrics.OutlierCount }}</span>\n </div>\n }\n </div>\n }\n\n }\n</div>\n"]}
1
+ {"version":3,"file":"cluster-config-panel.component.js","sourceRoot":"","sources":["../../src/lib/cluster-config-panel.component.ts","../../src/lib/cluster-config-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Floating glass-morphism configuration panel for clustering.\n *\n * `ClusterConfigPanelComponent` provides a draggable floating panel with\n * entity picker, algorithm picker, parameter sliders, distance metric\n * selector, max records input, run button, and result metrics display.\n *\n * It emits cancelable `BeforeXXX` events so consumers can intercept and\n * validate before clustering starts or a visualization is saved.\n *\n * @example\n * ```html\n * <mj-cluster-config-panel\n * [IsRunning]=\"isRunning\"\n * [Metrics]=\"result?.Metrics ?? null\"\n * [EntityOptions]=\"myEntityOptions\"\n * [ShowSaveButton]=\"true\"\n * [DefaultAlgorithm]=\"'dbscan'\"\n * [Collapsed]=\"false\"\n * (BeforeRunClustering)=\"onBeforeRun($event)\"\n * (RunClustering)=\"onRunClustering($event)\"\n * (ConfigChanged)=\"onConfigChanged($event)\"\n * (SaveVisualization)=\"onSave()\">\n * </mj-cluster-config-panel>\n * ```\n */\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n ChangeDetectorRef,\n OnInit,\n OnDestroy,\n ViewChild,\n inject,\n} from '@angular/core';\nimport {\n ClusterConfig,\n ClusterConfigPanelEntityOption,\n ClusterConfigPanelEntityDocOption,\n ClusterMetrics,\n ClusterAlgorithm,\n ClusterDistanceMetric,\n CancelableEvent,\n DefaultClusterConfig,\n} from './clustering.types';\n\n@Component({\n standalone: false,\n selector: 'mj-cluster-config-panel',\n templateUrl: './cluster-config-panel.component.html',\n styleUrls: ['./cluster-config-panel.component.css'],\n})\nexport class ClusterConfigPanelComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * Whether a clustering run is currently in progress.\n *\n * When `true`, the Run button is disabled and shows a spinner.\n *\n * @default false\n */\n @Input() IsRunning = false;\n\n /**\n * Result metrics from the most recent clustering run.\n *\n * When non-null, the metrics section is displayed at the bottom\n * of the panel showing silhouette score, cluster count, etc.\n */\n @Input() Metrics: ClusterMetrics | null = null;\n\n /**\n * Entity options for the dropdown.\n *\n * The parent component is responsible for populating this list,\n * typically from MJ Metadata or a subset filtered by\n * `AvailableEntities`.\n */\n private _entityOptions: ClusterConfigPanelEntityOption[] = [];\n\n @Input()\n set EntityOptions(value: ClusterConfigPanelEntityOption[]) {\n this._entityOptions = value || [];\n this.rebuildFilteredEntities();\n }\n get EntityOptions(): ClusterConfigPanelEntityOption[] {\n return this._entityOptions;\n }\n\n /** Entity Document options for the selected entity. Only shown when 2+ docs exist. */\n @Input() EntityDocOptions: ClusterConfigPanelEntityDocOption[] = [];\n\n /**\n * All Entity Documents across entities (each with `EntityName`), used to power\n * the optional multi-entity source selector. When two or more are selected the\n * run becomes a multi-entity analysis (their vectors are merged server-side).\n */\n @Input() AllEntityDocOptions: ClusterConfigPanelEntityDocOption[] = [];\n\n /** Whether to show the 2D/3D projection toggle. @default true */\n @Input() ShowDimensionToggle = true;\n\n /** Whether to show the multi-entity source selector. @default true */\n @Input() ShowMultiEntity = true;\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Whether to display the \"Save this visualization\" link.\n *\n * @default true\n */\n @Input() ShowSaveButton = true;\n\n /**\n * Whether to display the algorithm picker dropdown.\n *\n * When `false`, the algorithm is fixed to `DefaultAlgorithm`.\n *\n * @default true\n */\n @Input() ShowAlgorithmPicker = true;\n\n /**\n * Whether to display the distance metric picker dropdown.\n *\n * @default true\n */\n @Input() ShowMetricPicker = true;\n\n /**\n * Default algorithm selection when the panel initializes.\n *\n * @default 'kmeans'\n */\n @Input() DefaultAlgorithm: ClusterAlgorithm = 'kmeans';\n\n /**\n * Restrict the entity picker to a specific list of entity names.\n *\n * When empty (the default), all entity options from the\n * `EntityOptions` input are shown. When non-empty, only entities\n * whose `Name` appears in this array are shown.\n *\n * @default []\n *\n * @example\n * ```html\n * <mj-cluster-config-panel\n * [AvailableEntities]=\"['Companies', 'Contacts', 'Deals']\">\n * </mj-cluster-config-panel>\n * ```\n */\n @Input() AvailableEntities: string[] = [];\n\n /**\n * Whether the panel starts in a collapsed (minimized) state.\n *\n * The user can toggle between collapsed and expanded by clicking\n * the collapse chevron in the panel header.\n *\n * @default false\n */\n @Input() Collapsed = false;\n\n // ================================================================\n // Standard Outputs\n // ================================================================\n\n /**\n * Emitted when the user clicks Run and `BeforeRunClustering` was not\n * canceled. Payload is a snapshot of the current config.\n */\n @Output() RunClustering = new EventEmitter<ClusterConfig>();\n\n /**\n * Emitted when the user clicks the Save link and `BeforeSave` was\n * not canceled.\n */\n @Output() SaveVisualization = new EventEmitter<void>();\n\n // ================================================================\n // Cancelable Outputs\n // ================================================================\n\n /**\n * Fires **before** a clustering run starts. Set `event.Cancel = true`\n * to prevent the run (e.g., for validation).\n *\n * @example\n * ```typescript\n * onBeforeRun(event: CancelableEvent<ClusterConfig>): void {\n * if (!event.Data.EntityName) {\n * event.Cancel = true;\n * alert('Please select an entity first.');\n * }\n * }\n * ```\n */\n @Output() BeforeRunClustering = new EventEmitter<CancelableEvent<ClusterConfig>>();\n\n /**\n * Fires **before** a visualization is saved. Set `event.Cancel = true`\n * to prevent the save (e.g., to prompt for a name first).\n */\n @Output() BeforeSave = new EventEmitter<CancelableEvent<ClusterConfig>>();\n\n // ================================================================\n // Informational Outputs\n // ================================================================\n\n /**\n * Fires whenever any configuration value changes (entity, algorithm,\n * K, epsilon, distance metric, max records, filter).\n *\n * Payload is a snapshot of the entire config at the time of change.\n */\n @Output() ConfigChanged = new EventEmitter<ClusterConfig>();\n\n /**\n * Fires specifically when the algorithm selection changes.\n *\n * Useful for showing/hiding algorithm-specific UI in the parent.\n */\n @Output() AlgorithmChanged = new EventEmitter<ClusterAlgorithm>();\n\n /**\n * Fires when the 2D/3D projection toggle changes. Hosts should re-run (or\n * re-project) so the change is reflected immediately — a 3D projection needs\n * a Z coordinate that only a fresh run produces.\n */\n @Output() DimensionsChanged = new EventEmitter<2 | 3>();\n\n // ================================================================\n // Internal State\n // ================================================================\n\n /** The working configuration object. */\n public Config: ClusterConfig = DefaultClusterConfig();\n\n /** Filtered entity list for display (filtered by AvailableEntities). */\n public FilteredEntityOptions: ClusterConfigPanelEntityOption[] = [];\n\n /** Whether the panel body is currently visible. */\n public IsExpanded = true;\n\n /** Whether the multi-entity source selector is expanded. */\n public ShowMultiSource = false;\n\n /** Algorithm options for the dropdown. */\n public AlgorithmOptions: Array<{ Value: ClusterAlgorithm; Label: string }> = [\n { Value: 'kmeans', Label: 'K-Means' },\n { Value: 'dbscan', Label: 'DBSCAN' },\n ];\n\n /** Distance metric options for the dropdown. */\n public MetricOptions: Array<{ Value: ClusterDistanceMetric; Label: string }> = [\n { Value: 'cosine', Label: 'Cosine' },\n { Value: 'euclidean', Label: 'Euclidean' },\n { Value: 'dotproduct', Label: 'Dot Product' },\n ];\n\n // Dragging state\n /** Current panel top offset in pixels. */\n public PanelTop = 12;\n /** Current panel left offset in pixels. */\n public PanelLeft = 12;\n private isDragging = false;\n private dragStartX = 0;\n private dragStartY = 0;\n private dragStartTop = 0;\n private dragStartLeft = 0;\n private boundOnMouseMove: ((e: MouseEvent) => void) | null = null;\n private boundOnMouseUp: ((e: MouseEvent) => void) | null = null;\n\n @ViewChild('panelElement', { static: false }) private panelRef!: ElementRef<HTMLDivElement>;\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnInit(): void {\n // Apply defaults\n this.Config.Algorithm = this.DefaultAlgorithm;\n this.IsExpanded = !this.Collapsed;\n\n this.rebuildFilteredEntities();\n\n this.boundOnMouseMove = this.onMouseMove.bind(this);\n this.boundOnMouseUp = this.onMouseUp.bind(this);\n document.addEventListener('mousemove', this.boundOnMouseMove);\n document.addEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnDestroy(): void {\n if (this.boundOnMouseMove) document.removeEventListener('mousemove', this.boundOnMouseMove);\n if (this.boundOnMouseUp) document.removeEventListener('mouseup', this.boundOnMouseUp);\n }\n\n // ================================================================\n // Public Template Methods\n // ================================================================\n\n /**\n * Whether the current algorithm is K-Means (controls which\n * parameter sliders are shown).\n */\n public get IsKMeans(): boolean {\n return this.Config.Algorithm === 'kmeans';\n }\n\n /**\n * Handle algorithm dropdown change.\n *\n * @param value New algorithm value string.\n */\n public OnAlgorithmChanged(value: string): void {\n const newAlg = value as ClusterAlgorithm;\n this.Config.Algorithm = newAlg;\n this.AlgorithmChanged.emit(newAlg);\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /**\n * Handle distance metric dropdown change.\n *\n * @param value New metric value string.\n */\n public OnMetricChanged(value: string): void {\n this.Config.DistanceMetric = value as ClusterDistanceMetric;\n this.emitConfigChanged();\n }\n\n /**\n * Handle entity dropdown change.\n *\n * @param value New entity name.\n */\n public OnEntityChanged(value: string): void {\n this.Config.EntityName = value;\n this.emitConfigChanged();\n }\n\n /**\n * Handle changes to any numeric/text config field (K, Epsilon,\n * MinPoints, MaxRecords, Filter).\n */\n public OnFieldChanged(): void {\n this.emitConfigChanged();\n }\n\n // ----- Multi-entity / dimensions / color-by -----\n\n /** The currently selected entity-document IDs for multi-entity clustering. */\n public get SelectedDocIDs(): string[] {\n return this.Config.EntityDocumentIDs ?? [];\n }\n\n /** True when two or more source documents are selected (multi-entity mode). */\n public get IsMultiEntity(): boolean {\n return this.SelectedDocIDs.length >= 2;\n }\n\n /** Whether there are enough documents to offer multi-entity selection. */\n public get HasMultiSourceOptions(): boolean {\n return this.ShowMultiEntity && this.AllEntityDocOptions.length > 1;\n }\n\n /** Whether a given document is currently selected. */\n public IsDocSelected(id: string): boolean {\n return this.SelectedDocIDs.includes(id);\n }\n\n /** Toggle a document in/out of the multi-entity selection. */\n public ToggleDoc(id: string): void {\n const set = new Set(this.Config.EntityDocumentIDs ?? []);\n if (set.has(id)) {\n set.delete(id);\n } else {\n set.add(id);\n }\n this.Config.EntityDocumentIDs = [...set];\n // Once a multi-entity selection exists, color-by-entity is the useful default.\n if (this.IsMultiEntity && this.Config.ColorBy !== 'entity') {\n this.Config.ColorBy = 'entity';\n }\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /** Handle the multi-source accordion's expand/collapse. */\n public OnMultiSourceExpandedChange(expanded: boolean): void {\n this.ShowMultiSource = expanded;\n this.cdr.detectChanges();\n }\n\n /** Set the projection dimensionality (2 or 3). */\n public SetDimensions(d: 2 | 3): void {\n if (this.Config.Dimensions === d) return;\n this.Config.Dimensions = d;\n this.emitConfigChanged();\n this.DimensionsChanged.emit(d);\n this.cdr.detectChanges();\n }\n\n /** Set the legend/color mode. */\n public SetColorBy(mode: 'cluster' | 'entity'): void {\n this.Config.ColorBy = mode;\n this.emitConfigChanged();\n this.cdr.detectChanges();\n }\n\n /** Whether the Run button can be clicked. */\n public get CanRun(): boolean {\n return !this.IsRunning && (!!this.Config.EntityName || this.SelectedDocIDs.length > 0);\n }\n\n /** Whether the Save button should be offered (there are results to save). */\n public get CanSave(): boolean {\n return this.ShowSaveButton && !!this.Metrics && this.Metrics.RecordCount > 0;\n }\n\n /**\n * Handle Run button click. Fires `BeforeRunClustering`, and if not\n * canceled, emits `RunClustering`.\n */\n public OnRun(): void {\n if (!this.CanRun) return;\n\n const snapshot = { ...this.Config };\n const cancelable: CancelableEvent<ClusterConfig> = { Data: snapshot, Cancel: false };\n this.BeforeRunClustering.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.RunClustering.emit(snapshot);\n }\n\n /**\n * Handle Save link click. Fires `BeforeSave`, and if not canceled,\n * emits `SaveVisualization`.\n */\n public OnSave(): void {\n const cancelable: CancelableEvent<ClusterConfig> = { Data: { ...this.Config }, Cancel: false };\n this.BeforeSave.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.SaveVisualization.emit();\n }\n\n /**\n * Toggle the panel between expanded and collapsed states.\n */\n public ToggleCollapse(): void {\n this.IsExpanded = !this.IsExpanded;\n this.cdr.detectChanges();\n }\n\n /**\n * Format a silhouette score to 2 decimal places.\n *\n * @param score The raw silhouette score.\n * @returns Formatted string (e.g., `\"0.72\"`).\n */\n public FormatSilhouette(score: number): string {\n return score.toFixed(2);\n }\n\n /**\n * Whether a silhouette score indicates good clustering quality.\n *\n * @param score The silhouette score to evaluate.\n * @returns `true` if score >= 0.5.\n */\n public IsSilhouetteGood(score: number): boolean {\n return score >= 0.5;\n }\n\n /**\n * Format a computation time in milliseconds to a human-readable string.\n *\n * @param ms Milliseconds.\n * @returns e.g., `\"1.2s\"` or `\"450ms\"`.\n */\n public FormatTime(ms: number): string {\n return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;\n }\n\n /**\n * Start dragging the panel via header mousedown.\n *\n * @param event The native mouse event.\n */\n public OnHeaderMouseDown(event: MouseEvent): void {\n event.preventDefault();\n this.isDragging = true;\n this.dragStartX = event.clientX;\n this.dragStartY = event.clientY;\n this.dragStartTop = this.PanelTop;\n this.dragStartLeft = this.PanelLeft;\n }\n\n /** @internal TrackBy for algorithm options. */\n public TrackAlgorithmBy(_index: number, item: { Value: string }): string {\n return item.Value;\n }\n\n /** @internal TrackBy for metric options. */\n public TrackMetricBy(_index: number, item: { Value: string }): string {\n return item.Value;\n }\n\n /** @internal TrackBy for entity options. */\n public TrackEntityBy(_index: number, item: ClusterConfigPanelEntityOption): string {\n return item.Name;\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n /**\n * Rebuild the filtered entity list based on `AvailableEntities`.\n */\n private rebuildFilteredEntities(): void {\n if (this.AvailableEntities.length > 0) {\n const allowedSet = new Set(this.AvailableEntities);\n this.FilteredEntityOptions = this.EntityOptions.filter(e => allowedSet.has(e.Name));\n } else {\n this.FilteredEntityOptions = this.EntityOptions;\n }\n\n if (this.FilteredEntityOptions.length > 0 && !this.Config.EntityName) {\n this.Config.EntityName = this.FilteredEntityOptions[0].Name;\n }\n }\n\n /** Emit `ConfigChanged` with a snapshot of the current config. */\n private emitConfigChanged(): void {\n this.ConfigChanged.emit({ ...this.Config });\n }\n\n private onMouseMove(event: MouseEvent): void {\n if (!this.isDragging) return;\n const dx = event.clientX - this.dragStartX;\n const dy = event.clientY - this.dragStartY;\n this.PanelTop = Math.max(0, this.dragStartTop + dy);\n this.PanelLeft = Math.max(0, this.dragStartLeft + dx);\n this.cdr.detectChanges();\n }\n\n private onMouseUp(_event: MouseEvent): void {\n this.isDragging = false;\n }\n}\n","<div #panelElement\n class=\"config-panel\"\n [style.top.px]=\"PanelTop\"\n [style.left.px]=\"PanelLeft\">\n\n <!-- Draggable header -->\n <h3 class=\"panel-header\"\n (mousedown)=\"OnHeaderMouseDown($event)\">\n <i class=\"fa-solid fa-sliders\"></i>\n Configuration\n <span class=\"header-actions\">\n <i class=\"fa-solid\"\n [class.fa-chevron-up]=\"IsExpanded\"\n [class.fa-chevron-down]=\"!IsExpanded\"\n (click)=\"ToggleCollapse(); $event.stopPropagation()\"\n title=\"Toggle panel\"></i>\n <i class=\"fa-solid fa-grip-dots-vertical drag-handle\"></i>\n </span>\n </h3>\n\n @if (IsExpanded) {\n <!-- Entity picker -->\n <div class=\"form-group\">\n <label>Entity</label>\n <select [ngModel]=\"Config.EntityName\"\n (ngModelChange)=\"OnEntityChanged($event)\">\n @for (entity of FilteredEntityOptions; track TrackEntityBy($index, entity)) {\n <option [value]=\"entity.Name\">{{ entity.Name }}</option>\n }\n </select>\n </div>\n\n <!-- Entity Document selector (only shown when 2+ docs for selected entity) -->\n @if (EntityDocOptions.length > 1) {\n <div class=\"form-group\">\n <label>Document Template</label>\n <select [ngModel]=\"Config.EntityDocumentID\"\n (ngModelChange)=\"Config.EntityDocumentID = $event; OnFieldChanged()\">\n @for (doc of EntityDocOptions; track doc.ID) {\n <option [value]=\"doc.ID\">{{ doc.Name }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Multi-entity source selector -->\n @if (HasMultiSourceOptions) {\n <div class=\"form-group multi-source\">\n <mj-accordion-panel\n Size=\"sm\"\n [FlushBody]=\"true\"\n [Expanded]=\"ShowMultiSource\"\n (ExpandedChange)=\"OnMultiSourceExpandedChange($event)\">\n <ng-template mjAccordionTitle>\n Multiple sources\n @if (IsMultiEntity) {\n <span class=\"mj-accordion-badge mj-accordion-badge--right\">{{ SelectedDocIDs.length }}</span>\n }\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"multi-source-list\">\n <div class=\"multi-source-hint\">Select 2+ documents to cluster them together. All must use the same embedding model.</div>\n @for (doc of AllEntityDocOptions; track doc.ID) {\n <label class=\"multi-source-item\">\n <input type=\"checkbox\" [checked]=\"IsDocSelected(doc.ID)\" (change)=\"ToggleDoc(doc.ID)\">\n <span class=\"multi-source-name\">{{ doc.Name }}</span>\n @if (doc.EntityName) { <span class=\"multi-source-entity\">{{ doc.EntityName }}</span> }\n </label>\n }\n </div>\n </ng-template>\n </mj-accordion-panel>\n </div>\n }\n\n <!-- Projection (2D / 3D) + Color-by toggles -->\n <div class=\"form-row toggle-row\">\n @if (ShowDimensionToggle) {\n <div class=\"form-group\">\n <label>Projection</label>\n <div class=\"seg-toggle\" role=\"group\">\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"(Config.Dimensions ?? 2) === 2\" (click)=\"SetDimensions(2)\">2D</button>\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"Config.Dimensions === 3\" (click)=\"SetDimensions(3)\">3D</button>\n </div>\n </div>\n }\n <div class=\"form-group\">\n <label>Color by</label>\n <div class=\"seg-toggle\" role=\"group\">\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"(Config.ColorBy ?? 'cluster') === 'cluster'\" (click)=\"SetColorBy('cluster')\">Cluster</button>\n <button type=\"button\" class=\"seg-btn\" [class.active]=\"Config.ColorBy === 'entity'\" (click)=\"SetColorBy('entity')\">Entity</button>\n </div>\n </div>\n </div>\n\n <!-- Filter -->\n <div class=\"form-group\">\n <label>Filter (optional)</label>\n <input type=\"text\"\n [(ngModel)]=\"Config.Filter\"\n (ngModelChange)=\"OnFieldChanged()\"\n placeholder=\"e.g. Status = 'Active'\">\n </div>\n\n <!-- Algorithm + K/Epsilon row -->\n <div class=\"form-row\">\n @if (ShowAlgorithmPicker) {\n <div class=\"form-group\">\n <label>Algorithm</label>\n <select [ngModel]=\"Config.Algorithm\"\n (ngModelChange)=\"OnAlgorithmChanged($event)\">\n @for (alg of AlgorithmOptions; track TrackAlgorithmBy($index, alg)) {\n <option [value]=\"alg.Value\">{{ alg.Label }}</option>\n }\n </select>\n </div>\n }\n\n @if (IsKMeans) {\n <div class=\"form-group\">\n <label>Clusters (K): {{ Config.K }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.K\"\n (ngModelChange)=\"Config.K = $event; OnFieldChanged()\"\n [min]=\"2\"\n [max]=\"20\"\n [step]=\"1\">\n </div>\n } @else {\n <div class=\"form-group\">\n <label>Epsilon: {{ Config.Epsilon }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.Epsilon\"\n (ngModelChange)=\"Config.Epsilon = $event; OnFieldChanged()\"\n [min]=\"0.05\"\n [max]=\"0.95\"\n [step]=\"0.05\">\n </div>\n }\n </div>\n\n <!-- DBSCAN MinPoints row (only visible for DBSCAN) -->\n @if (!IsKMeans) {\n <div class=\"form-group\">\n <label>Min Points: {{ Config.MinPoints }}</label>\n <input type=\"range\"\n [ngModel]=\"Config.MinPoints\"\n (ngModelChange)=\"Config.MinPoints = $event; OnFieldChanged()\"\n [min]=\"2\"\n [max]=\"20\"\n [step]=\"1\">\n </div>\n }\n\n <!-- Distance metric + Max records row -->\n <div class=\"form-row\">\n @if (ShowMetricPicker) {\n <div class=\"form-group\">\n <label>Distance Metric</label>\n <select [ngModel]=\"Config.DistanceMetric\"\n (ngModelChange)=\"OnMetricChanged($event)\">\n @for (metric of MetricOptions; track TrackMetricBy($index, metric)) {\n <option [value]=\"metric.Value\">{{ metric.Label }}</option>\n }\n </select>\n </div>\n }\n <div class=\"form-group\">\n <label>Max Records</label>\n <input type=\"number\"\n [(ngModel)]=\"Config.MaxRecords\"\n (ngModelChange)=\"OnFieldChanged()\"\n [min]=\"10\"\n [max]=\"5000\">\n </div>\n </div>\n\n <!-- Run + Save action row -->\n <div class=\"action-row\">\n <button class=\"run-btn\"\n [disabled]=\"!CanRun\"\n (click)=\"OnRun()\">\n @if (IsRunning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run Clustering\n }\n </button>\n @if (CanSave) {\n <button class=\"save-btn\" (click)=\"OnSave()\" title=\"Save this visualization\">\n <i class=\"fa-solid fa-bookmark\"></i> Save\n </button>\n }\n </div>\n\n <!-- Metrics display -->\n @if (Metrics && Metrics.RecordCount > 0) {\n <div class=\"metrics-section\">\n <div class=\"metric-item\">\n <span class=\"metric-label\">Silhouette</span>\n <span class=\"metric-value\"\n [class.good]=\"IsSilhouetteGood(Metrics.SilhouetteScore)\">\n {{ FormatSilhouette(Metrics.SilhouetteScore) }}\n </span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Clusters</span>\n <span class=\"metric-value\">{{ Metrics.ClusterCount }}</span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Records</span>\n <span class=\"metric-value\">{{ Metrics.RecordCount }}</span>\n </div>\n <div class=\"metric-item\">\n <span class=\"metric-label\">Time</span>\n <span class=\"metric-value\">{{ FormatTime(Metrics.ComputationTimeMs) }}</span>\n </div>\n @if (Metrics.OutlierCount > 0) {\n <div class=\"metric-item\">\n <span class=\"metric-label\">Outliers</span>\n <span class=\"metric-value\">{{ Metrics.OutlierCount }}</span>\n </div>\n }\n </div>\n }\n\n }\n</div>\n"]}
@@ -264,7 +264,7 @@ function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template(rf,
264
264
  const _r17 = i0.ɵɵgetCurrentView();
265
265
  i0.ɵɵelementStart(0, "input", 32);
266
266
  i0.ɵɵtwoWayListener("ngModelChange", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.EditingLabelDraft, $event) || (ctx_r1.EditingLabelDraft = $event); return i0.ɵɵresetView($event); });
267
- i0.ɵɵlistener("keydown", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_keydown_0_listener($event) { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnLabelEditKeydown($event, cluster_r16)); })("blur", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_blur_0_listener() { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.CommitLabelEdit(cluster_r16)); })("click", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_click_0_listener($event) { i0.ɵɵrestoreView(_r17); return i0.ɵɵresetView($event.stopPropagation()); });
267
+ i0.ɵɵlistener("keydown", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_keydown_0_listener($event) { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnLabelEditKeydown($event, cluster_r16)); })("blur", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_blur_0_listener() { i0.ɵɵrestoreView(_r17); const cluster_r16 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.CommitLabelEdit(cluster_r16)); })("click", function ClusterScatterComponent_Conditional_6_For_4_Conditional_2_Template_input_click_0_listener($event) { return $event.stopPropagation(); });
268
268
  i0.ɵɵelementEnd();
269
269
  } if (rf & 2) {
270
270
  const ctx_r1 = i0.ɵɵnextContext(3);
@@ -1 +1 @@
1
- {"version":3,"file":"cluster-scatter.component.js","sourceRoot":"","sources":["../../src/lib/cluster-scatter.component.ts","../../src/lib/cluster-scatter.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview SVG scatter plot component for cluster visualization.\n *\n * `ClusterScatterComponent` renders an interactive 2D scatter plot where each\n * dot represents a clustered entity record. It supports:\n *\n * - **Zoom / pan** via mouse wheel and drag (SVG viewBox manipulation)\n * - **Tooltips** on hover showing point metadata\n * - **Legend** overlay in the bottom-left corner\n * - **Radial gradient glow** per cluster centroid\n * - **Cancelable `BeforeXXX` events** so consumers can intercept interactions\n * - **Programmatic selection and zoom** via public methods\n * - **Full customization** of dot radius, opacity, color palette, and more\n *\n * @example\n * ```html\n * <mj-cluster-scatter\n * [Points]=\"result.Points\"\n * [Clusters]=\"result.Clusters\"\n * [DotRadius]=\"6\"\n * [ShowTooltip]=\"true\"\n * [ColorPalette]=\"['#ff0000','#00ff00','#0000ff']\"\n * (BeforePointClick)=\"onBeforeClick($event)\"\n * (PointClicked)=\"onPointClicked($event)\"\n * (AfterClusteringComplete)=\"onClustered($event)\"\n * (SelectionChanged)=\"onSelectionChanged($event)\">\n * </mj-cluster-scatter>\n * ```\n */\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n ChangeDetectorRef,\n AfterViewInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n inject,\n} from '@angular/core';\nimport {\n ClusterPoint,\n ClusterInfo,\n ClusterVisualizationResult,\n CancelableEvent,\n ViewportRect,\n ViewportTransform,\n ClusterSelectedEvent,\n CLUSTER_COLORS,\n} from './clustering.types';\n\n@Component({\n standalone: false,\n selector: 'mj-cluster-scatter',\n templateUrl: './cluster-scatter.component.html',\n styleUrls: ['./cluster-scatter.component.css'],\n encapsulation: ViewEncapsulation.None,\n})\nexport class ClusterScatterComponent implements AfterViewInit, OnDestroy, OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * Array of 2D-projected points to render.\n *\n * Each point carries its cluster assignment, label, and metadata.\n * Typically produced by `ClusteringService.RunPipeline()`.\n */\n @Input() Points: ClusterPoint[] = [];\n\n /**\n * Cluster summary information used for the legend and color mapping.\n */\n @Input() Clusters: ClusterInfo[] = [];\n\n /**\n * Legend / color mode. `'cluster'` (default) colors each point by its assigned\n * cluster; `'entity'` colors by the point's source entity (`Metadata.EntityName`),\n * which is the useful mode for multi-entity analyses.\n */\n @Input() ColorBy: 'cluster' | 'entity' = 'cluster';\n\n /**\n * When `true`, a semi-transparent loading overlay is displayed\n * on top of the scatter plot.\n */\n @Input() IsLoading = false;\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Base radius (in SVG units) for each data point dot.\n *\n * @default 5\n */\n @Input() DotRadius = 5;\n\n /**\n * Base opacity for data point dots (0 to 1).\n *\n * When a point is hovered, non-hovered points are dimmed below this value.\n *\n * @default 0.75\n */\n @Input() DotOpacity = 0.75;\n\n /**\n * Radius used for the highlight ring on hovered or selected points.\n *\n * @default 8\n */\n @Input() HighlightRadius = 8;\n\n /**\n * Whether to show the color-coded cluster legend overlay.\n *\n * @default true\n */\n @Input() ShowLegend = true;\n\n /**\n * Whether to show the tooltip popup on point hover.\n *\n * When `false`, `BeforePointHover` and `PointHovered` still fire but\n * the built-in tooltip DOM is not rendered.\n *\n * @default true\n */\n @Input() ShowTooltip = true;\n\n /**\n * Which metadata keys to display inside the tooltip.\n *\n * When empty (the default), **all** metadata keys are shown.\n *\n * @default []\n *\n * @example\n * ```html\n * <mj-cluster-scatter [TooltipFields]=\"['EntityRecordID', 'Score']\">\n * </mj-cluster-scatter>\n * ```\n */\n @Input() TooltipFields: string[] = [];\n\n /**\n * Ordered list of metadata field keys for prioritized display in tooltips\n * and the detail panel. When set, fields are displayed in this order instead\n * of raw metadata key order. Fields not in this list appear after the\n * prioritized fields.\n *\n * Typically computed from entity metadata (DefaultInView, Sequence) by the\n * consuming component and passed in.\n *\n * @default [] (no prioritization — original key order)\n */\n @Input() FieldPriority: string[] = [];\n\n /**\n * Map of field names to human-readable display names.\n * Used to show \"First Name\" instead of \"FirstName\" in tooltips and detail panels.\n * If a field is not in this map, the raw key is shown.\n *\n * @default {} (use raw key names)\n */\n @Input() FieldDisplayNames: Record<string, string> = {};\n\n /**\n * The entity name for metadata-driven field display in tooltips and\n * detail panels. When set, the mj-entity-card component uses EntityInfo\n * to prioritize fields by IsNameField and DefaultInView.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Override the default cluster color palette.\n *\n * Colors are assigned to clusters by index: cluster 0 gets\n * `ColorPalette[0]`, cluster 1 gets `ColorPalette[1]`, etc.\n * Wraps around when there are more clusters than colors.\n *\n * @default CLUSTER_COLORS (10 accessible colors)\n */\n @Input() ColorPalette: string[] = [];\n\n /**\n * Enable or disable zoom via mouse wheel.\n *\n * @default true\n */\n @Input() EnableZoom = true;\n\n /**\n * Enable or disable panning via click-and-drag.\n *\n * @default true\n */\n @Input() EnablePan = true;\n\n /**\n * Whether scatter plot transitions (zoom, highlight) should be animated.\n *\n * @default true\n */\n @Input() AnimateTransitions = true;\n\n /**\n * Externally controlled set of selected point IDs (by `VectorKey`).\n *\n * When provided, the component renders selection rings for matching\n * points and emits `SelectionChanged` when the user modifies the set\n * interactively.\n *\n * @default (empty Set)\n *\n * @example\n * ```html\n * <mj-cluster-scatter\n * [SelectedPointIds]=\"mySelectedIds\"\n * (SelectionChanged)=\"mySelectedIds = $event\">\n * </mj-cluster-scatter>\n * ```\n */\n @Input() SelectedPointIds: Set<string> = new Set<string>();\n\n /**\n * Minimum zoom level (smallest allowed viewBox multiplier).\n *\n * A value of `0.5` means the user can zoom in until the viewBox is\n * half of the default 1000x700 size.\n *\n * @default 0.5\n */\n @Input() MinZoom = 0.5;\n\n /**\n * Maximum zoom level (largest allowed viewBox multiplier).\n *\n * A value of `10` means the user can zoom out until the viewBox is\n * 10x the default 1000x700 size.\n *\n * @default 10\n */\n @Input() MaxZoom = 10;\n\n // ================================================================\n // Outputs (standard events)\n // ================================================================\n\n /**\n * Emitted when a point is clicked (after `BeforePointClick` completes\n * without cancellation).\n */\n @Output() PointClicked = new EventEmitter<ClusterPoint>();\n\n /**\n * Emitted when the user clicks \"Open Record\" in the detail panel.\n * The parent should wire this to `NavigationService.OpenEntityRecord()`.\n */\n @Output() OpenRecordRequested = new EventEmitter<ClusterPoint>();\n\n /**\n * Emitted when the mouse enters or leaves a data point.\n * Emits `null` on mouse-leave.\n */\n @Output() PointHovered = new EventEmitter<ClusterPoint | null>();\n\n // ================================================================\n // Outputs (cancelable \"Before\" events)\n // ================================================================\n\n /**\n * Fires **before** `PointClicked`. Set `event.Cancel = true` to\n * suppress the click entirely.\n *\n * @example\n * ```typescript\n * onBeforeClick(event: CancelableEvent<ClusterPoint>): void {\n * if (event.Data.ClusterId === -1) {\n * event.Cancel = true; // ignore outlier clicks\n * }\n * }\n * ```\n */\n @Output() BeforePointClick = new EventEmitter<CancelableEvent<ClusterPoint>>();\n\n /**\n * Fires **before** the tooltip is shown on hover. Set\n * `event.Cancel = true` to suppress the tooltip for this point.\n */\n @Output() BeforePointHover = new EventEmitter<CancelableEvent<ClusterPoint>>();\n\n /**\n * Fires **before** a zoom or pan operation is applied. Set\n * `event.Cancel = true` to reject the viewport change.\n *\n * The payload contains the **proposed** viewport rectangle.\n */\n @Output() BeforeZoom = new EventEmitter<CancelableEvent<ViewportRect>>();\n\n // ================================================================\n // Outputs (informational events)\n // ================================================================\n\n /**\n * Fires after new `Points` and `Clusters` are received and centroids\n * have been computed, delivering the full visualization result.\n */\n @Output() AfterClusteringComplete = new EventEmitter<ClusterVisualizationResult>();\n\n /**\n * Fires when the user clicks a cluster label or legend swatch.\n */\n @Output() ClusterSelected = new EventEmitter<ClusterSelectedEvent>();\n\n /**\n * Fires whenever the set of selected point IDs changes\n * (via user click or programmatic `SelectPoints` / `ClearSelection`).\n */\n @Output() SelectionChanged = new EventEmitter<Set<string>>();\n\n /**\n * Fires after every zoom or pan that changes the visible viewport.\n */\n @Output() ViewportChanged = new EventEmitter<ViewportRect>();\n\n /**\n * Fires when the user edits a cluster label inline.\n * Payload: `{ ClusterId, OldLabel, NewLabel }`.\n */\n @Output() LabelEdited = new EventEmitter<{ ClusterId: number; OldLabel: string; NewLabel: string }>();\n\n // ================================================================\n // Label Editing State\n // ================================================================\n\n /** The cluster ID currently being edited, or null if none. */\n public EditingClusterId: number | null = null;\n /** The draft label text during editing. */\n public EditingLabelDraft = '';\n\n /** Start inline editing for a cluster label. */\n public StartLabelEdit(cluster: ClusterInfo, event: MouseEvent): void {\n event.stopPropagation();\n this.EditingClusterId = cluster.Id;\n this.EditingLabelDraft = cluster.Label;\n this.cdr.detectChanges();\n }\n\n /** Commit the edited label. */\n public CommitLabelEdit(cluster: ClusterInfo): void {\n const oldLabel = cluster.Label;\n const newLabel = this.EditingLabelDraft.trim();\n if (newLabel.length > 0 && newLabel !== oldLabel) {\n cluster.Label = newLabel;\n this.LabelEdited.emit({ ClusterId: cluster.Id, OldLabel: oldLabel, NewLabel: newLabel });\n }\n this.EditingClusterId = null;\n this.cdr.detectChanges();\n }\n\n /** Cancel editing. */\n public CancelLabelEdit(): void {\n this.EditingClusterId = null;\n this.cdr.detectChanges();\n }\n\n /** Handle keydown in the edit input. */\n public OnLabelEditKeydown(event: KeyboardEvent, cluster: ClusterInfo): void {\n if (event.key === 'Enter') {\n this.CommitLabelEdit(cluster);\n } else if (event.key === 'Escape') {\n this.CancelLabelEdit();\n }\n }\n\n // ================================================================\n // Internal State\n // ================================================================\n\n /** SVG viewBox parameters: [minX, minY, width, height]. */\n private _viewBox = [0, 0, 1000, 700];\n\n /** Cached viewBox string for template binding (avoids NG0100 on recalc). */\n public ViewBoxString = '0 0 1000 700';\n\n public get ViewBox(): number[] {\n return this._viewBox;\n }\n\n public set ViewBox(value: number[]) {\n this._viewBox = value;\n this.ViewBoxString = value.join(' ');\n }\n\n /** Get the current viewport transform for saving/restoring. */\n public GetViewportTransform(): ViewportTransform {\n return {\n TranslateX: this.ViewBox[0],\n TranslateY: this.ViewBox[1],\n Scale: this.defaultWidth / this.ViewBox[2],\n };\n }\n\n /** Restore a previously saved viewport transform. */\n public SetViewportTransform(vt: ViewportTransform): void {\n const width = this.defaultWidth / vt.Scale;\n const height = this.defaultHeight / vt.Scale;\n this.ViewBox = [vt.TranslateX, vt.TranslateY, width, height];\n }\n\n /** Currently hovered point (null when idle). */\n public HoveredPoint: ClusterPoint | null = null;\n /** Screen-space tooltip X position. */\n public TooltipX = 0;\n /** Screen-space tooltip Y position. */\n public TooltipY = 0;\n /** Currently highlighted (single-clicked) point key. */\n public HighlightedKey: string | null = null;\n\n /** Background grid lines for visual orientation. */\n public HorizontalGridLines: number[] = [175, 350, 525];\n /** Background vertical grid lines. */\n public VerticalGridLines: number[] = [250, 500, 750];\n\n /** Computed cluster centroids used for radial glow effects. */\n public ClusterCentroids: Array<{ ClusterId: number; Cx: number; Cy: number; R: number; Color: string }> = [];\n\n /** Filtered metadata entries for the tooltip (computed on hover). */\n public TooltipEntries: Array<{ Key: string; Value: unknown }> = [];\n\n // ================================================================\n // Detail Panel State\n // ================================================================\n\n /** The currently selected point shown in the detail panel. */\n public SelectedPoint: ClusterPoint | null = null;\n\n /** Whether the detail panel is visible. */\n public ShowDetailPanel = false;\n\n /** Metadata entries for the detail panel (computed on selection). */\n public DetailEntries: Array<{ Key: string; Value: unknown }> = [];\n\n /** Cluster members for the selected point's cluster. */\n public ClusterMembers: ClusterPoint[] = [];\n\n /** Whether the cluster members list is expanded. */\n public ClusterMembersExpanded = true;\n\n @ViewChild('svgElement', { static: false }) private svgRef!: ElementRef<SVGSVGElement>;\n\n // Pan/zoom state\n private isPanning = false;\n private panStartX = 0;\n private panStartY = 0;\n private panStartViewBox = [0, 0, 1000, 700];\n // 3D orbit-drag state\n private isRotating = false;\n private rotateStartX = 0;\n private rotateStartY = 0;\n private rotateStartYaw = 0;\n private rotateStartPitch = 0;\n private boundOnMouseMove: ((e: MouseEvent) => void) | null = null;\n private boundOnMouseUp: ((e: MouseEvent) => void) | null = null;\n\n /** Default viewBox dimensions used for zoom-limit calculations. */\n private readonly defaultWidth = 1000;\n private readonly defaultHeight = 700;\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngAfterViewInit(): void {\n this.boundOnMouseMove = this.onMouseMove.bind(this);\n this.boundOnMouseUp = this.onMouseUp.bind(this);\n document.addEventListener('mousemove', this.boundOnMouseMove);\n document.addEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnDestroy(): void {\n if (this.boundOnMouseMove) document.removeEventListener('mousemove', this.boundOnMouseMove);\n if (this.boundOnMouseUp) document.removeEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n this.computeCentroids();\n\n // Emit AfterClusteringComplete when new data arrives\n if ((changes['Points'] || changes['Clusters']) && this.Points.length > 0 && this.Clusters.length > 0) {\n this.AfterClusteringComplete.emit({\n Points: this.Points,\n Clusters: this.Clusters,\n Metrics: {\n SilhouetteScore: 0,\n ClusterCount: this.Clusters.length,\n ComputationTimeMs: 0,\n RecordCount: this.Points.length,\n OutlierCount: this.Points.filter(p => p.ClusterId < 0).length,\n },\n Config: {\n EntityName: '',\n EntityDocumentID: '',\n Algorithm: 'kmeans',\n K: this.Clusters.length,\n Epsilon: 0,\n MinPoints: 0,\n DistanceMetric: 'cosine',\n MaxRecords: this.Points.length,\n Filter: '',\n },\n });\n }\n }\n\n // ================================================================\n // Template Helpers\n // ================================================================\n\n /**\n * Returns the current SVG `viewBox` as a space-separated string.\n *\n * @returns e.g. `\"0 0 1000 700\"`\n */\n public GetViewBoxString(): string {\n return this.ViewBox.join(' ');\n }\n\n /**\n * Resolve the display color for a given point based on its cluster\n * assignment and the active `ColorPalette`.\n *\n * @param point The point to colorize.\n * @returns CSS color string.\n */\n public GetPointColor(point: ClusterPoint): string {\n if (this.ColorBy === 'entity') {\n return this.getEntityColor(point);\n }\n if (point.ClusterId < 0) return '#64748b'; // outlier gray\n const palette = this.getActivePalette();\n const cluster = this.Clusters.find(c => c.Id === point.ClusterId);\n return cluster?.Color ?? palette[point.ClusterId % palette.length];\n }\n\n // ================================================================\n // Color-by-entity (multi-entity analyses)\n // ================================================================\n\n /** Stable entity-name → color map, assigned in first-seen order. */\n private entityColorMap = new Map<string, string>();\n\n /** Resolve a color for a point keyed by its source entity name. */\n private getEntityColor(point: ClusterPoint): string {\n const name = (point.Metadata?.['EntityName'] as string) || (point.Metadata?.['Entity'] as string) || 'Unknown';\n let color = this.entityColorMap.get(name);\n if (!color) {\n const palette = this.getActivePalette();\n color = palette[this.entityColorMap.size % palette.length];\n this.entityColorMap.set(name, color);\n }\n return color;\n }\n\n /** Distinct source entities present in the data (for the entity legend). */\n public get EntityLegend(): Array<{ Name: string; Color: string; Count: number }> {\n if (this.ColorBy !== 'entity') return [];\n const counts = new Map<string, number>();\n for (const p of this.Points) {\n const name = (p.Metadata?.['EntityName'] as string) || (p.Metadata?.['Entity'] as string) || 'Unknown';\n counts.set(name, (counts.get(name) ?? 0) + 1);\n }\n return [...counts.entries()].map(([Name, Count]) => ({\n Name,\n Count,\n Color: this.getEntityColor({ Metadata: { EntityName: Name } } as unknown as ClusterPoint),\n }));\n }\n\n // ================================================================\n // 3D projection (static isometric, depth-cued)\n // ================================================================\n\n /** True when any point carries a Z coordinate (a 3D projection). */\n public get Is3D(): boolean {\n return this.Points.some(p => p.Z != null);\n }\n\n /** Orbit angles (radians) for the interactive 3D projection. Updated by drag. */\n public Yaw = 0.6;\n public Pitch = 0.45;\n\n /** Rotate a centered (x,y,z) by the current yaw (around Y) then pitch (around X). */\n private rotate(point: ClusterPoint): { x: number; y: number; z: number } {\n const x0 = point.X - 500;\n const y0 = point.Y - 350;\n const z0 = (point.Z ?? 500) - 500;\n const cy = Math.cos(this.Yaw), sy = Math.sin(this.Yaw);\n // yaw around vertical (Y) axis\n const x1 = x0 * cy + z0 * sy;\n const z1 = -x0 * sy + z0 * cy;\n const y1 = y0;\n // pitch around horizontal (X) axis\n const cp = Math.cos(this.Pitch), sp = Math.sin(this.Pitch);\n const y2 = y1 * cp - z1 * sp;\n const z2 = y1 * sp + z1 * cp;\n return { x: x1, y: y2, z: z2 };\n }\n\n /** Project a point's (X,Y,Z) to a screen X in the SVG coordinate space. */\n public PX(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return point.X;\n return 500 + this.rotate(point).x;\n }\n\n /** Project a point's (X,Y,Z) to a screen Y in the SVG coordinate space. */\n public PY(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return point.Y;\n return 350 + this.rotate(point).y;\n }\n\n /** Normalized depth [0=far, 1=near] for a point, used for size/opacity cues. */\n private depthNorm(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return 1;\n const depth = this.rotate(point).z;\n // centered coords span roughly ±440 per axis; combined depth ~[-760, 760]\n return Math.max(0, Math.min(1, (depth + 600) / 1200));\n }\n\n /** Reset the 3D orbit to the default viewing angle. */\n public ResetRotation(): void {\n this.Yaw = 0.6;\n this.Pitch = 0.45;\n this.cdr.detectChanges();\n }\n\n /**\n * Cluster labels positioned at each cluster's centroid in *projected* space.\n * Used to label clusters in 3D (where the 2D centroid-glow path is disabled).\n * Recomputes as the user orbits because it depends on PX/PY (yaw/pitch).\n */\n public get ProjectedClusterLabels(): Array<{ X: number; Y: number; ClusterId: number; Color: string }> {\n if (!this.Is3D) return [];\n const groups = new Map<number, ClusterPoint[]>();\n for (const p of this.Points) {\n if (p.ClusterId < 0) continue;\n const arr = groups.get(p.ClusterId);\n if (arr) arr.push(p); else groups.set(p.ClusterId, [p]);\n }\n const palette = this.getActivePalette();\n const out: Array<{ X: number; Y: number; ClusterId: number; Color: string }> = [];\n for (const [cid, pts] of groups) {\n const sx = pts.reduce((s, p) => s + this.PX(p), 0) / pts.length;\n const sy = pts.reduce((s, p) => s + this.PY(p), 0) / pts.length;\n const cluster = this.Clusters.find(c => c.Id === cid);\n out.push({ X: sx, Y: sy, ClusterId: cid, Color: cluster?.Color ?? palette[cid % palette.length] });\n }\n return out;\n }\n\n /**\n * Points in render order. In 3D, far points are drawn first so nearer points\n * paint on top (SVG has no z-index); in 2D the original order is preserved.\n */\n public get RenderPoints(): ClusterPoint[] {\n if (!this.Is3D) return this.Points;\n return [...this.Points].sort((a, b) => this.depthNorm(a) - this.depthNorm(b));\n }\n\n /**\n * Compute the SVG radius for a data point.\n *\n * Selected points use the larger `HighlightRadius`; all others use `DotRadius`.\n *\n * @param point The point to size.\n * @returns Radius in SVG units.\n */\n public GetPointRadius(point: ClusterPoint): number {\n if (this.SelectedPointIds.has(point.VectorKey)) {\n return this.HighlightRadius;\n }\n if (this.Is3D) {\n // Nearer points (depth→1) render larger to convey depth.\n return this.DotRadius * (0.6 + 0.7 * this.depthNorm(point));\n }\n return this.DotRadius;\n }\n\n /**\n * Compute the fill opacity for a data point.\n *\n * Non-hovered points are dimmed when another point is hovered.\n *\n * @param point The point to evaluate.\n * @returns Opacity value between 0 and 1.\n */\n public GetPointOpacity(point: ClusterPoint): number {\n if (this.HoveredPoint && this.HoveredPoint.VectorKey !== point.VectorKey) {\n return this.DotOpacity * 0.5;\n }\n if (this.Is3D) {\n // Fade far points slightly for depth perception.\n return this.DotOpacity * (0.55 + 0.45 * this.depthNorm(point));\n }\n return this.DotOpacity;\n }\n\n /**\n * Handle mouseenter on a data point: show tooltip, fire hover events.\n *\n * @param point The hovered point.\n * @param event The native mouse event (used for tooltip positioning).\n */\n public OnPointMouseEnter(point: ClusterPoint, event: MouseEvent): void {\n const cancelable: CancelableEvent<ClusterPoint> = { Data: point, Cancel: false };\n this.BeforePointHover.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.HoveredPoint = point;\n this.TooltipX = event.clientX + 16;\n this.TooltipY = event.clientY - 10;\n this.computeTooltipEntries(point);\n this.PointHovered.emit(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Handle mouseleave on a data point: hide tooltip, emit null hover.\n */\n public OnPointMouseLeave(): void {\n this.HoveredPoint = null;\n this.TooltipEntries = [];\n this.PointHovered.emit(null);\n this.cdr.detectChanges();\n }\n\n /**\n * Handle click on a data point: fires `BeforePointClick`, toggles\n * selection, opens the detail panel, then fires `PointClicked` and\n * `SelectionChanged`.\n *\n * @param point The clicked point.\n */\n public OnPointClick(point: ClusterPoint): void {\n const cancelable: CancelableEvent<ClusterPoint> = { Data: point, Cancel: false };\n this.BeforePointClick.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.HighlightedKey = point.VectorKey;\n\n // Toggle selection\n const newSelection = new Set(this.SelectedPointIds);\n if (newSelection.has(point.VectorKey)) {\n newSelection.delete(point.VectorKey);\n } else {\n newSelection.add(point.VectorKey);\n }\n this.SelectedPointIds = newSelection;\n this.SelectionChanged.emit(newSelection);\n\n // Open detail panel for the clicked point\n this.selectPointForDetail(point);\n\n this.PointClicked.emit(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Whether the given point is the currently highlighted (last-clicked) point.\n *\n * @param point The point to check.\n * @returns `true` if highlighted.\n */\n public IsHighlighted(point: ClusterPoint): boolean {\n return this.HighlightedKey === point.VectorKey;\n }\n\n /**\n * Whether the given point is in the current selection set.\n *\n * @param point The point to check.\n * @returns `true` if selected.\n */\n public IsSelected(point: ClusterPoint): boolean {\n return this.SelectedPointIds.has(point.VectorKey);\n }\n\n /**\n * Handle mouse wheel events for zooming.\n *\n * Fires `BeforeZoom` and `ViewportChanged`. Respects `EnableZoom`,\n * `MinZoom`, and `MaxZoom` constraints.\n *\n * @param event The native wheel event.\n */\n public OnWheel(event: WheelEvent): void {\n event.preventDefault();\n if (!this.EnableZoom) return;\n\n const zoomFactor = event.deltaY > 0 ? 1.1 : 0.9;\n const [minX, minY, width, height] = this.ViewBox;\n\n const minWidth = this.defaultWidth * this.MinZoom;\n const maxWidth = this.defaultWidth * this.MaxZoom;\n const minHeight = this.defaultHeight * this.MinZoom;\n const maxHeight = this.defaultHeight * this.MaxZoom;\n\n const newWidth = Math.max(minWidth, Math.min(maxWidth, width * zoomFactor));\n const newHeight = Math.max(minHeight, Math.min(maxHeight, height * zoomFactor));\n\n const centerX = minX + width / 2;\n const centerY = minY + height / 2;\n\n const proposed: ViewportRect = {\n MinX: centerX - newWidth / 2,\n MinY: centerY - newHeight / 2,\n Width: newWidth,\n Height: newHeight,\n };\n\n const cancelable: CancelableEvent<ViewportRect> = { Data: proposed, Cancel: false };\n this.BeforeZoom.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Start panning on left-mousedown.\n *\n * @param event The native mouse event.\n */\n public OnMouseDown(event: MouseEvent): void {\n if (event.button !== 0) return;\n // In 3D mode a left-drag orbits the camera (so depth is perceivable);\n // in 2D it pans the viewBox.\n if (this.Is3D) {\n this.isRotating = true;\n this.rotateStartX = event.clientX;\n this.rotateStartY = event.clientY;\n this.rotateStartYaw = this.Yaw;\n this.rotateStartPitch = this.Pitch;\n return;\n }\n if (!this.EnablePan) return;\n this.isPanning = true;\n this.panStartX = event.clientX;\n this.panStartY = event.clientY;\n this.panStartViewBox = [...this.ViewBox];\n }\n\n /**\n * Handle a click on a cluster legend item.\n *\n * @param cluster The cluster that was clicked.\n */\n public OnClusterLegendClick(cluster: ClusterInfo): void {\n this.ClusterSelected.emit({\n ClusterId: cluster.Id,\n Label: cluster.Label,\n Color: cluster.Color,\n MemberCount: cluster.MemberCount,\n });\n }\n\n /**\n * Resolve the human-readable label for a point's cluster.\n *\n * @param point The point to look up.\n * @returns Cluster label string, or `\"Outlier\"` for noise points.\n */\n public GetClusterLabel(point: ClusterPoint): string {\n if (point.ClusterId < 0) return 'Outlier';\n const cluster = this.Clusters.find(c => c.Id === point.ClusterId);\n return cluster?.Label ?? `Cluster ${point.ClusterId + 1}`;\n }\n\n /** Get the label for a cluster by ID (used for SVG text labels on the chart). */\n public GetClusterLabelById(clusterId: number): string {\n const cluster = this.Clusters.find(c => c.Id === clusterId);\n return cluster?.Label ?? '';\n }\n\n /** @internal TrackBy function for point loops. */\n public TrackPointBy(_index: number, point: ClusterPoint): string {\n return point.VectorKey;\n }\n\n /** @internal TrackBy function for cluster loops. */\n public TrackClusterBy(_index: number, cluster: ClusterInfo): number {\n return cluster.Id;\n }\n\n /** @internal TrackBy function for centroid loops. */\n public TrackCentroidBy(_index: number, centroid: { ClusterId: number }): number {\n return centroid.ClusterId;\n }\n\n // ================================================================\n // Public API Methods\n // ================================================================\n\n /**\n * Animate the viewport to center on a specific cluster's centroid.\n *\n * The zoom level is adjusted so that all cluster members are visible\n * with some padding.\n *\n * @param clusterId The numeric ID of the cluster to zoom into.\n *\n * @example\n * ```typescript\n * @ViewChild(ClusterScatterComponent) scatter!: ClusterScatterComponent;\n * this.scatter.ZoomToCluster(2);\n * ```\n */\n public ZoomToCluster(clusterId: number): void {\n const members = this.Points.filter(p => p.ClusterId === clusterId);\n if (members.length === 0) return;\n\n const bounds = this.computeBounds(members);\n const padding = 80;\n const proposed: ViewportRect = {\n MinX: bounds.minX - padding,\n MinY: bounds.minY - padding,\n Width: (bounds.maxX - bounds.minX) + padding * 2,\n Height: (bounds.maxY - bounds.minY) + padding * 2,\n };\n\n // Ensure minimum dimensions\n if (proposed.Width < 200) {\n const cx = proposed.MinX + proposed.Width / 2;\n proposed.Width = 200;\n proposed.MinX = cx - 100;\n }\n if (proposed.Height < 140) {\n const cy = proposed.MinY + proposed.Height / 2;\n proposed.Height = 140;\n proposed.MinY = cy - 70;\n }\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Reset the viewport to the default zoom level (full view).\n *\n * @example\n * ```typescript\n * this.scatter.ResetZoom();\n * ```\n */\n public ResetZoom(): void {\n const proposed: ViewportRect = {\n MinX: 0,\n MinY: 0,\n Width: this.defaultWidth,\n Height: this.defaultHeight,\n };\n this.ViewBox = [0, 0, this.defaultWidth, this.defaultHeight];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Return all points currently visible within the viewport bounds.\n *\n * @returns Array of `ClusterPoint` objects whose X/Y fall inside the\n * current viewBox.\n */\n public GetVisiblePoints(): ClusterPoint[] {\n const [minX, minY, width, height] = this.ViewBox;\n const maxX = minX + width;\n const maxY = minY + height;\n return this.Points.filter(p =>\n p.X >= minX && p.X <= maxX && p.Y >= minY && p.Y <= maxY\n );\n }\n\n /**\n * Programmatically select points by their `VectorKey` IDs.\n *\n * Merges with the existing selection. Use `ClearSelection()` first\n * to replace the selection entirely.\n *\n * @param ids Array of `VectorKey` strings to select.\n *\n * @example\n * ```typescript\n * this.scatter.SelectPoints(['abc-123', 'def-456']);\n * ```\n */\n public SelectPoints(ids: string[]): void {\n const newSelection = new Set(this.SelectedPointIds);\n for (const id of ids) {\n newSelection.add(id);\n }\n this.SelectedPointIds = newSelection;\n this.SelectionChanged.emit(newSelection);\n this.cdr.detectChanges();\n }\n\n /**\n * Clear all selected points.\n */\n public ClearSelection(): void {\n this.SelectedPointIds = new Set<string>();\n this.HighlightedKey = null;\n this.SelectionChanged.emit(this.SelectedPointIds);\n this.cdr.detectChanges();\n }\n\n /**\n * Export the current scatter plot as an SVG string.\n *\n * The returned string is a self-contained `<svg>` element that can be\n * saved to a file or embedded in HTML.\n *\n * @returns SVG markup string, or empty string if the SVG element is\n * not yet rendered.\n */\n public ExportSVG(): string {\n if (!this.svgRef?.nativeElement) return '';\n const svgElement = this.svgRef.nativeElement;\n const serializer = new XMLSerializer();\n return serializer.serializeToString(svgElement);\n }\n\n /**\n * Visually emphasize all points belonging to a cluster by increasing\n * their radius and dimming non-members.\n *\n * Call `ResetZoom()` or re-assign `Points` to clear the highlight.\n *\n * @param clusterId The numeric cluster ID to emphasize.\n */\n public HighlightCluster(clusterId: number): void {\n const memberKeys = this.Points\n .filter(p => p.ClusterId === clusterId)\n .map(p => p.VectorKey);\n this.SelectedPointIds = new Set(memberKeys);\n this.SelectionChanged.emit(this.SelectedPointIds);\n this.cdr.detectChanges();\n }\n\n // ================================================================\n // Detail Panel Methods\n // ================================================================\n\n /**\n * Close the detail panel.\n */\n public CloseDetailPanel(): void {\n this.ShowDetailPanel = false;\n this.SelectedPoint = null;\n this.DetailEntries = [];\n this.ClusterMembers = [];\n this.cdr.detectChanges();\n }\n\n /**\n * Emit the `OpenRecordRequested` event for the currently selected point\n * (triggered by the \"Open Record\" button in the detail panel).\n */\n public OnOpenRecordClick(): void {\n if (this.SelectedPoint) {\n this.OpenRecordRequested.emit(this.SelectedPoint);\n }\n }\n\n /**\n * Select a point from the cluster members list, updating the detail\n * panel and highlighting the point on the chart.\n *\n * @param point The cluster member point to select.\n */\n public OnClusterMemberClick(point: ClusterPoint): void {\n this.HighlightedKey = point.VectorKey;\n this.selectPointForDetail(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Toggle the cluster members list expansion state.\n */\n public ToggleClusterMembers(): void {\n this.ClusterMembersExpanded = !this.ClusterMembersExpanded;\n }\n\n /**\n * Get the color for a cluster by its ID, used in the detail panel.\n *\n * @param clusterId The cluster ID.\n * @returns CSS color string.\n */\n public GetClusterColor(clusterId: number): string {\n if (clusterId < 0) return '#64748b';\n const cluster = this.Clusters.find(c => c.Id === clusterId);\n if (cluster?.Color) return cluster.Color;\n const palette = this.getActivePalette();\n return palette[clusterId % palette.length];\n }\n\n /**\n * Return the metadata keys that should be hidden in the detail panel.\n */\n private get internalMetadataKeys(): Set<string> {\n return new Set([\n 'Name', 'Entity', 'EntityIcon', 'RecordID',\n 'TemplateID', '__mj_UpdatedAt', '__mj_CreatedAt',\n ]);\n }\n\n /**\n * Select a point and populate the detail panel state.\n */\n private selectPointForDetail(point: ClusterPoint): void {\n this.SelectedPoint = point;\n this.ShowDetailPanel = true;\n this.computeDetailEntries(point);\n this.computeClusterMembers(point);\n }\n\n /**\n * Compute the metadata entries for the detail panel, excluding internal fields.\n * Uses FieldPriority for ordering when available.\n */\n private computeDetailEntries(point: ClusterPoint): void {\n const hidden = this.internalMetadataKeys;\n const raw = Object.entries(point.Metadata)\n .filter(([key]) => !hidden.has(key))\n .filter(([, value]) => value != null && String(value).trim() !== '')\n .map(([Key, Value]) => ({ Key, Value }));\n\n this.DetailEntries = this.sortEntriesByPriority(raw);\n }\n\n /**\n * Compute the list of cluster members for the selected point's cluster.\n */\n private computeClusterMembers(point: ClusterPoint): void {\n this.ClusterMembers = this.Points\n .filter(p => p.ClusterId === point.ClusterId)\n .sort((a, b) => a.Label.localeCompare(b.Label));\n }\n\n /** @internal TrackBy function for cluster member loops. */\n public TrackMemberBy(_index: number, point: ClusterPoint): string {\n return point.VectorKey;\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private onMouseMove(event: MouseEvent): void {\n if (this.isRotating) {\n const dx = event.clientX - this.rotateStartX;\n const dy = event.clientY - this.rotateStartY;\n this.Yaw = this.rotateStartYaw + dx * 0.01;\n // clamp pitch so the scene never flips upside down\n this.Pitch = Math.max(-1.3, Math.min(1.3, this.rotateStartPitch + dy * 0.01));\n this.cdr.detectChanges();\n return;\n }\n if (!this.isPanning || !this.svgRef) return;\n const svg = this.svgRef.nativeElement;\n const rect = svg.getBoundingClientRect();\n const scaleX = this.panStartViewBox[2] / rect.width;\n const scaleY = this.panStartViewBox[3] / rect.height;\n\n const dx = (event.clientX - this.panStartX) * scaleX;\n const dy = (event.clientY - this.panStartY) * scaleY;\n\n const proposed: ViewportRect = {\n MinX: this.panStartViewBox[0] - dx,\n MinY: this.panStartViewBox[1] - dy,\n Width: this.panStartViewBox[2],\n Height: this.panStartViewBox[3],\n };\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n private onMouseUp(_event: MouseEvent): void {\n this.isPanning = false;\n this.isRotating = false;\n }\n\n private computeCentroids(): void {\n if (!this.Points.length || !this.Clusters.length) {\n this.ClusterCentroids = [];\n return;\n }\n\n this.ClusterCentroids = this.Clusters.map(cluster => {\n const members = this.Points.filter(p => p.ClusterId === cluster.Id);\n if (members.length === 0) {\n return { ClusterId: cluster.Id, Cx: 500, Cy: 350, R: 80, Color: cluster.Color };\n }\n const cx = members.reduce((s, p) => s + p.X, 0) / members.length;\n const cy = members.reduce((s, p) => s + p.Y, 0) / members.length;\n const maxDist = Math.max(\n ...members.map(p => Math.sqrt((p.X - cx) ** 2 + (p.Y - cy) ** 2)),\n 40\n );\n return {\n ClusterId: cluster.Id,\n Cx: cx,\n Cy: cy,\n R: Math.min(maxDist + 30, 200),\n Color: cluster.Color,\n };\n });\n }\n\n /**\n * Compute the bounding box for a set of points.\n */\n private computeBounds(points: ClusterPoint[]): { minX: number; minY: number; maxX: number; maxY: number } {\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\n for (const p of points) {\n if (p.X < minX) minX = p.X;\n if (p.X > maxX) maxX = p.X;\n if (p.Y < minY) minY = p.Y;\n if (p.Y > maxY) maxY = p.Y;\n }\n return { minX, minY, maxX, maxY };\n }\n\n /**\n * Resolve the effective color palette (custom override or default).\n */\n private getActivePalette(): string[] {\n return this.ColorPalette.length > 0 ? this.ColorPalette : CLUSTER_COLORS;\n }\n\n /**\n * Compute the tooltip entries based on TooltipFields filter.\n * Uses FieldPriority for ordering when available.\n */\n private computeTooltipEntries(point: ClusterPoint): void {\n const entries = Object.entries(point.Metadata);\n let mapped: { Key: string; Value: unknown }[];\n if (this.TooltipFields.length > 0) {\n mapped = entries\n .filter(([key]) => this.TooltipFields.includes(key))\n .map(([Key, Value]) => ({ Key, Value }));\n } else {\n mapped = entries.map(([Key, Value]) => ({ Key, Value }));\n }\n this.TooltipEntries = this.sortEntriesByPriority(mapped);\n }\n\n /**\n * Sort entries using the FieldPriority ordering.\n * Fields in FieldPriority come first (in priority order), then remaining fields.\n */\n private sortEntriesByPriority(entries: { Key: string; Value: unknown }[]): { Key: string; Value: unknown }[] {\n if (this.FieldPriority.length === 0) return entries;\n\n const priorityIndex = new Map(this.FieldPriority.map((key, idx) => [key, idx]));\n return [...entries].sort((a, b) => {\n const aIdx = priorityIndex.get(a.Key) ?? 99999;\n const bIdx = priorityIndex.get(b.Key) ?? 99999;\n return aIdx - bIdx;\n });\n }\n}\n","<div class=\"scatter-container\">\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\">\n <div class=\"loading-spinner\">\n <i class=\"fa-solid fa-circle-nodes fa-spin\"></i>\n <span>Computing clusters...</span>\n </div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && Points.length === 0) {\n <mj-empty-state\n class=\"empty-state-fill\"\n Icon=\"fa-solid fa-chart-simple\"\n Title=\"No data to display\"\n Message=\"Configure and run clustering to see results\" />\n }\n\n <!-- SVG scatter plot -->\n @if (Points.length > 0) {\n <svg #svgElement\n class=\"scatter-svg\"\n [class.no-transitions]=\"!AnimateTransitions\"\n [attr.viewBox]=\"ViewBoxString\"\n preserveAspectRatio=\"xMidYMid meet\"\n (wheel)=\"OnWheel($event)\"\n (mousedown)=\"OnMouseDown($event)\">\n\n <!-- Gradient definitions for cluster glow -->\n <defs>\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <radialGradient [attr.id]=\"'cluster-glow-' + centroid.ClusterId\"\n cx=\"50%\" cy=\"50%\" r=\"50%\">\n <stop offset=\"0%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0.12\"/>\n <stop offset=\"100%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0\"/>\n </radialGradient>\n }\n </defs>\n\n <!-- Background grid lines -->\n <g class=\"grid-lines\">\n @for (y of HorizontalGridLines; track y) {\n <line [attr.x1]=\"0\" [attr.y1]=\"y\" [attr.x2]=\"1000\" [attr.y2]=\"y\"/>\n }\n @for (x of VerticalGridLines; track x) {\n <line [attr.x1]=\"x\" [attr.y1]=\"0\" [attr.x2]=\"x\" [attr.y2]=\"700\"/>\n }\n </g>\n\n <!-- Cluster glow + labels (2D only; centroids are 2D aesthetics) -->\n @if (!Is3D) {\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <circle [attr.cx]=\"centroid.Cx\"\n [attr.cy]=\"centroid.Cy\"\n [attr.r]=\"centroid.R\"\n [attr.fill]=\"'url(#cluster-glow-' + centroid.ClusterId + ')'\"/>\n }\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n @if (GetClusterLabelById(centroid.ClusterId); as label) {\n <text class=\"cluster-label\"\n [attr.x]=\"centroid.Cx\"\n [attr.y]=\"centroid.Cy - centroid.R - 8\"\n text-anchor=\"middle\"\n [attr.fill]=\"centroid.Color\">{{ label }}</text>\n }\n }\n }\n\n <!-- Data points (RenderPoints is depth-sorted in 3D) -->\n @for (point of RenderPoints; track TrackPointBy($index, point)) {\n <circle class=\"data-point\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"GetPointRadius(point)\"\n [attr.fill]=\"GetPointColor(point)\"\n [attr.opacity]=\"GetPointOpacity(point)\"\n (mouseenter)=\"OnPointMouseEnter(point, $event)\"\n (mouseleave)=\"OnPointMouseLeave()\"\n (click)=\"OnPointClick(point); $event.stopPropagation()\"/>\n }\n\n <!-- 3D cluster labels (at projected centroids; follow rotation) -->\n @if (Is3D && ColorBy !== 'entity') {\n @for (lbl of ProjectedClusterLabels; track lbl.ClusterId) {\n @if (GetClusterLabelById(lbl.ClusterId); as text) {\n <text class=\"cluster-label cluster-label-3d\"\n [attr.x]=\"lbl.X\"\n [attr.y]=\"lbl.Y\"\n text-anchor=\"middle\"\n [attr.fill]=\"lbl.Color\">{{ text }}</text>\n }\n }\n }\n\n <!-- Selection rings for selected points -->\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsSelected(point)) {\n <circle class=\"selection-ring\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 2\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2\"\n opacity=\"0.8\"/>\n }\n }\n\n <!-- Highlight ring for last-clicked point -->\n @if (HighlightedKey) {\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsHighlighted(point)) {\n <circle [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 4\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2.5\"\n opacity=\"0.9\"/>\n }\n }\n }\n </svg>\n }\n\n <!-- 3D orbit hint + reset -->\n @if (Points.length > 0 && Is3D) {\n <div class=\"threed-hint\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>3D · drag to rotate</span>\n <button class=\"threed-reset\" (click)=\"ResetRotation()\" title=\"Reset view\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n </div>\n }\n\n <!-- Entity legend (multi-entity color mode) -->\n @if (ShowLegend && ColorBy === 'entity' && EntityLegend.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Entities</div>\n @for (ent of EntityLegend; track ent.Name) {\n <div class=\"legend-item\">\n <div class=\"legend-dot\" [style.background]=\"ent.Color\"></div>\n <span class=\"legend-label\">{{ ent.Name }}</span>\n <span class=\"legend-count\">{{ ent.Count }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Legend overlay (cluster color mode) -->\n @if (ShowLegend && ColorBy !== 'entity' && Clusters.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Clusters</div>\n @for (cluster of Clusters; track TrackClusterBy($index, cluster)) {\n <div class=\"legend-item\" (click)=\"OnClusterLegendClick(cluster)\">\n <div class=\"legend-dot\" [style.background]=\"cluster.Color\"></div>\n @if (EditingClusterId === cluster.Id) {\n <input class=\"legend-label-input\"\n [(ngModel)]=\"EditingLabelDraft\"\n (keydown)=\"OnLabelEditKeydown($event, cluster)\"\n (blur)=\"CommitLabelEdit(cluster)\"\n (click)=\"$event.stopPropagation()\"\n autofocus />\n } @else {\n <span class=\"legend-label\">{{ cluster.Label }}</span>\n <button class=\"legend-edit-btn\"\n title=\"Edit label\"\n (click)=\"StartLabelEdit(cluster, $event)\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n }\n <span class=\"legend-count\">{{ cluster.MemberCount }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Tooltip -->\n @if (ShowTooltip && HoveredPoint) {\n <div class=\"tooltip\"\n [style.left.px]=\"TooltipX\"\n [style.top.px]=\"TooltipY\">\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"HoveredPoint.Metadata\"\n Variant=\"compact\">\n </mj-entity-card>\n <div class=\"tt-cluster\">\n <i class=\"fa-solid fa-circle\" style=\"font-size: 7px; vertical-align: middle\"></i>\n {{ GetClusterLabel(HoveredPoint) }}\n </div>\n <div class=\"tt-action\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Click to view details\n </div>\n </div>\n }\n\n <!-- Detail Panel (slide-in from right) -->\n <div class=\"detail-panel\" [class.detail-panel-open]=\"ShowDetailPanel && SelectedPoint\">\n @if (SelectedPoint) {\n <!-- Panel Header -->\n <div class=\"detail-header\">\n <div class=\"detail-header-content\">\n @if (SelectedPoint.Metadata?.['EntityIcon']) {\n <div class=\"detail-icon\">\n <i [class]=\"SelectedPoint.Metadata['EntityIcon']\"></i>\n </div>\n }\n <div class=\"detail-header-text\">\n <div class=\"detail-name\">{{ SelectedPoint.Label }}</div>\n @if (SelectedPoint.Metadata?.['Entity']) {\n <span class=\"detail-entity-badge\">{{ SelectedPoint.Metadata['Entity'] }}</span>\n }\n </div>\n </div>\n <button class=\"detail-close\" (click)=\"CloseDetailPanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Cluster badge -->\n <div class=\"detail-cluster\">\n <span class=\"detail-cluster-dot\"\n [style.background]=\"GetClusterColor(SelectedPoint.ClusterId)\"></span>\n {{ GetClusterLabel(SelectedPoint) }}\n </div>\n\n <!-- Metadata fields via entity card -->\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"SelectedPoint.Metadata\"\n Variant=\"detail\"\n CssClass=\"detail-entity-card\">\n </mj-entity-card>\n\n <!-- Cluster Members -->\n <div class=\"detail-members-section\">\n <mj-accordion-panel\n Size=\"sm\"\n [Fill]=\"true\"\n [FlushBody]=\"true\"\n [(Expanded)]=\"ClusterMembersExpanded\">\n <ng-template mjAccordionTitle>\n Cluster Members\n <span class=\"mj-accordion-badge mj-accordion-badge--right\">{{ ClusterMembers.length }}</span>\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"detail-members-list\">\n @for (member of ClusterMembers; track TrackMemberBy($index, member)) {\n <div class=\"detail-member-row\"\n [class.active]=\"member.VectorKey === SelectedPoint?.VectorKey\"\n (click)=\"OnClusterMemberClick(member)\">\n @if (member.Metadata?.['EntityIcon']) {\n <i [class]=\"member.Metadata['EntityIcon']\" class=\"member-icon\"></i>\n }\n <span class=\"member-label\">{{ member.Label }}</span>\n </div>\n }\n </div>\n </ng-template>\n </mj-accordion-panel>\n </div>\n\n <!-- Open Record button -->\n <div class=\"detail-footer\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecordClick()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Record\n </button>\n </div>\n }\n </div>\n</div>\n"]}
1
+ {"version":3,"file":"cluster-scatter.component.js","sourceRoot":"","sources":["../../src/lib/cluster-scatter.component.ts","../../src/lib/cluster-scatter.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview SVG scatter plot component for cluster visualization.\n *\n * `ClusterScatterComponent` renders an interactive 2D scatter plot where each\n * dot represents a clustered entity record. It supports:\n *\n * - **Zoom / pan** via mouse wheel and drag (SVG viewBox manipulation)\n * - **Tooltips** on hover showing point metadata\n * - **Legend** overlay in the bottom-left corner\n * - **Radial gradient glow** per cluster centroid\n * - **Cancelable `BeforeXXX` events** so consumers can intercept interactions\n * - **Programmatic selection and zoom** via public methods\n * - **Full customization** of dot radius, opacity, color palette, and more\n *\n * @example\n * ```html\n * <mj-cluster-scatter\n * [Points]=\"result.Points\"\n * [Clusters]=\"result.Clusters\"\n * [DotRadius]=\"6\"\n * [ShowTooltip]=\"true\"\n * [ColorPalette]=\"['#ff0000','#00ff00','#0000ff']\"\n * (BeforePointClick)=\"onBeforeClick($event)\"\n * (PointClicked)=\"onPointClicked($event)\"\n * (AfterClusteringComplete)=\"onClustered($event)\"\n * (SelectionChanged)=\"onSelectionChanged($event)\">\n * </mj-cluster-scatter>\n * ```\n */\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n ChangeDetectorRef,\n AfterViewInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n inject,\n} from '@angular/core';\nimport {\n ClusterPoint,\n ClusterInfo,\n ClusterVisualizationResult,\n CancelableEvent,\n ViewportRect,\n ViewportTransform,\n ClusterSelectedEvent,\n CLUSTER_COLORS,\n} from './clustering.types';\n\n@Component({\n standalone: false,\n selector: 'mj-cluster-scatter',\n templateUrl: './cluster-scatter.component.html',\n styleUrls: ['./cluster-scatter.component.css'],\n encapsulation: ViewEncapsulation.None,\n})\nexport class ClusterScatterComponent implements AfterViewInit, OnDestroy, OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * Array of 2D-projected points to render.\n *\n * Each point carries its cluster assignment, label, and metadata.\n * Typically produced by `ClusteringService.RunPipeline()`.\n */\n @Input() Points: ClusterPoint[] = [];\n\n /**\n * Cluster summary information used for the legend and color mapping.\n */\n @Input() Clusters: ClusterInfo[] = [];\n\n /**\n * Legend / color mode. `'cluster'` (default) colors each point by its assigned\n * cluster; `'entity'` colors by the point's source entity (`Metadata.EntityName`),\n * which is the useful mode for multi-entity analyses.\n */\n @Input() ColorBy: 'cluster' | 'entity' = 'cluster';\n\n /**\n * When `true`, a semi-transparent loading overlay is displayed\n * on top of the scatter plot.\n */\n @Input() IsLoading = false;\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Base radius (in SVG units) for each data point dot.\n *\n * @default 5\n */\n @Input() DotRadius = 5;\n\n /**\n * Base opacity for data point dots (0 to 1).\n *\n * When a point is hovered, non-hovered points are dimmed below this value.\n *\n * @default 0.75\n */\n @Input() DotOpacity = 0.75;\n\n /**\n * Radius used for the highlight ring on hovered or selected points.\n *\n * @default 8\n */\n @Input() HighlightRadius = 8;\n\n /**\n * Whether to show the color-coded cluster legend overlay.\n *\n * @default true\n */\n @Input() ShowLegend = true;\n\n /**\n * Whether to show the tooltip popup on point hover.\n *\n * When `false`, `BeforePointHover` and `PointHovered` still fire but\n * the built-in tooltip DOM is not rendered.\n *\n * @default true\n */\n @Input() ShowTooltip = true;\n\n /**\n * Which metadata keys to display inside the tooltip.\n *\n * When empty (the default), **all** metadata keys are shown.\n *\n * @default []\n *\n * @example\n * ```html\n * <mj-cluster-scatter [TooltipFields]=\"['EntityRecordID', 'Score']\">\n * </mj-cluster-scatter>\n * ```\n */\n @Input() TooltipFields: string[] = [];\n\n /**\n * Ordered list of metadata field keys for prioritized display in tooltips\n * and the detail panel. When set, fields are displayed in this order instead\n * of raw metadata key order. Fields not in this list appear after the\n * prioritized fields.\n *\n * Typically computed from entity metadata (DefaultInView, Sequence) by the\n * consuming component and passed in.\n *\n * @default [] (no prioritization — original key order)\n */\n @Input() FieldPriority: string[] = [];\n\n /**\n * Map of field names to human-readable display names.\n * Used to show \"First Name\" instead of \"FirstName\" in tooltips and detail panels.\n * If a field is not in this map, the raw key is shown.\n *\n * @default {} (use raw key names)\n */\n @Input() FieldDisplayNames: Record<string, string> = {};\n\n /**\n * The entity name for metadata-driven field display in tooltips and\n * detail panels. When set, the mj-entity-card component uses EntityInfo\n * to prioritize fields by IsNameField and DefaultInView.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Override the default cluster color palette.\n *\n * Colors are assigned to clusters by index: cluster 0 gets\n * `ColorPalette[0]`, cluster 1 gets `ColorPalette[1]`, etc.\n * Wraps around when there are more clusters than colors.\n *\n * @default CLUSTER_COLORS (10 accessible colors)\n */\n @Input() ColorPalette: string[] = [];\n\n /**\n * Enable or disable zoom via mouse wheel.\n *\n * @default true\n */\n @Input() EnableZoom = true;\n\n /**\n * Enable or disable panning via click-and-drag.\n *\n * @default true\n */\n @Input() EnablePan = true;\n\n /**\n * Whether scatter plot transitions (zoom, highlight) should be animated.\n *\n * @default true\n */\n @Input() AnimateTransitions = true;\n\n /**\n * Externally controlled set of selected point IDs (by `VectorKey`).\n *\n * When provided, the component renders selection rings for matching\n * points and emits `SelectionChanged` when the user modifies the set\n * interactively.\n *\n * @default (empty Set)\n *\n * @example\n * ```html\n * <mj-cluster-scatter\n * [SelectedPointIds]=\"mySelectedIds\"\n * (SelectionChanged)=\"mySelectedIds = $event\">\n * </mj-cluster-scatter>\n * ```\n */\n @Input() SelectedPointIds: Set<string> = new Set<string>();\n\n /**\n * Minimum zoom level (smallest allowed viewBox multiplier).\n *\n * A value of `0.5` means the user can zoom in until the viewBox is\n * half of the default 1000x700 size.\n *\n * @default 0.5\n */\n @Input() MinZoom = 0.5;\n\n /**\n * Maximum zoom level (largest allowed viewBox multiplier).\n *\n * A value of `10` means the user can zoom out until the viewBox is\n * 10x the default 1000x700 size.\n *\n * @default 10\n */\n @Input() MaxZoom = 10;\n\n // ================================================================\n // Outputs (standard events)\n // ================================================================\n\n /**\n * Emitted when a point is clicked (after `BeforePointClick` completes\n * without cancellation).\n */\n @Output() PointClicked = new EventEmitter<ClusterPoint>();\n\n /**\n * Emitted when the user clicks \"Open Record\" in the detail panel.\n * The parent should wire this to `NavigationService.OpenEntityRecord()`.\n */\n @Output() OpenRecordRequested = new EventEmitter<ClusterPoint>();\n\n /**\n * Emitted when the mouse enters or leaves a data point.\n * Emits `null` on mouse-leave.\n */\n @Output() PointHovered = new EventEmitter<ClusterPoint | null>();\n\n // ================================================================\n // Outputs (cancelable \"Before\" events)\n // ================================================================\n\n /**\n * Fires **before** `PointClicked`. Set `event.Cancel = true` to\n * suppress the click entirely.\n *\n * @example\n * ```typescript\n * onBeforeClick(event: CancelableEvent<ClusterPoint>): void {\n * if (event.Data.ClusterId === -1) {\n * event.Cancel = true; // ignore outlier clicks\n * }\n * }\n * ```\n */\n @Output() BeforePointClick = new EventEmitter<CancelableEvent<ClusterPoint>>();\n\n /**\n * Fires **before** the tooltip is shown on hover. Set\n * `event.Cancel = true` to suppress the tooltip for this point.\n */\n @Output() BeforePointHover = new EventEmitter<CancelableEvent<ClusterPoint>>();\n\n /**\n * Fires **before** a zoom or pan operation is applied. Set\n * `event.Cancel = true` to reject the viewport change.\n *\n * The payload contains the **proposed** viewport rectangle.\n */\n @Output() BeforeZoom = new EventEmitter<CancelableEvent<ViewportRect>>();\n\n // ================================================================\n // Outputs (informational events)\n // ================================================================\n\n /**\n * Fires after new `Points` and `Clusters` are received and centroids\n * have been computed, delivering the full visualization result.\n */\n @Output() AfterClusteringComplete = new EventEmitter<ClusterVisualizationResult>();\n\n /**\n * Fires when the user clicks a cluster label or legend swatch.\n */\n @Output() ClusterSelected = new EventEmitter<ClusterSelectedEvent>();\n\n /**\n * Fires whenever the set of selected point IDs changes\n * (via user click or programmatic `SelectPoints` / `ClearSelection`).\n */\n @Output() SelectionChanged = new EventEmitter<Set<string>>();\n\n /**\n * Fires after every zoom or pan that changes the visible viewport.\n */\n @Output() ViewportChanged = new EventEmitter<ViewportRect>();\n\n /**\n * Fires when the user edits a cluster label inline.\n * Payload: `{ ClusterId, OldLabel, NewLabel }`.\n */\n @Output() LabelEdited = new EventEmitter<{ ClusterId: number; OldLabel: string; NewLabel: string }>();\n\n // ================================================================\n // Label Editing State\n // ================================================================\n\n /** The cluster ID currently being edited, or null if none. */\n public EditingClusterId: number | null = null;\n /** The draft label text during editing. */\n public EditingLabelDraft = '';\n\n /** Start inline editing for a cluster label. */\n public StartLabelEdit(cluster: ClusterInfo, event: MouseEvent): void {\n event.stopPropagation();\n this.EditingClusterId = cluster.Id;\n this.EditingLabelDraft = cluster.Label;\n this.cdr.detectChanges();\n }\n\n /** Commit the edited label. */\n public CommitLabelEdit(cluster: ClusterInfo): void {\n const oldLabel = cluster.Label;\n const newLabel = this.EditingLabelDraft.trim();\n if (newLabel.length > 0 && newLabel !== oldLabel) {\n cluster.Label = newLabel;\n this.LabelEdited.emit({ ClusterId: cluster.Id, OldLabel: oldLabel, NewLabel: newLabel });\n }\n this.EditingClusterId = null;\n this.cdr.detectChanges();\n }\n\n /** Cancel editing. */\n public CancelLabelEdit(): void {\n this.EditingClusterId = null;\n this.cdr.detectChanges();\n }\n\n /** Handle keydown in the edit input. */\n public OnLabelEditKeydown(event: KeyboardEvent, cluster: ClusterInfo): void {\n if (event.key === 'Enter') {\n this.CommitLabelEdit(cluster);\n } else if (event.key === 'Escape') {\n this.CancelLabelEdit();\n }\n }\n\n // ================================================================\n // Internal State\n // ================================================================\n\n /** SVG viewBox parameters: [minX, minY, width, height]. */\n private _viewBox = [0, 0, 1000, 700];\n\n /** Cached viewBox string for template binding (avoids NG0100 on recalc). */\n public ViewBoxString = '0 0 1000 700';\n\n public get ViewBox(): number[] {\n return this._viewBox;\n }\n\n public set ViewBox(value: number[]) {\n this._viewBox = value;\n this.ViewBoxString = value.join(' ');\n }\n\n /** Get the current viewport transform for saving/restoring. */\n public GetViewportTransform(): ViewportTransform {\n return {\n TranslateX: this.ViewBox[0],\n TranslateY: this.ViewBox[1],\n Scale: this.defaultWidth / this.ViewBox[2],\n };\n }\n\n /** Restore a previously saved viewport transform. */\n public SetViewportTransform(vt: ViewportTransform): void {\n const width = this.defaultWidth / vt.Scale;\n const height = this.defaultHeight / vt.Scale;\n this.ViewBox = [vt.TranslateX, vt.TranslateY, width, height];\n }\n\n /** Currently hovered point (null when idle). */\n public HoveredPoint: ClusterPoint | null = null;\n /** Screen-space tooltip X position. */\n public TooltipX = 0;\n /** Screen-space tooltip Y position. */\n public TooltipY = 0;\n /** Currently highlighted (single-clicked) point key. */\n public HighlightedKey: string | null = null;\n\n /** Background grid lines for visual orientation. */\n public HorizontalGridLines: number[] = [175, 350, 525];\n /** Background vertical grid lines. */\n public VerticalGridLines: number[] = [250, 500, 750];\n\n /** Computed cluster centroids used for radial glow effects. */\n public ClusterCentroids: Array<{ ClusterId: number; Cx: number; Cy: number; R: number; Color: string }> = [];\n\n /** Filtered metadata entries for the tooltip (computed on hover). */\n public TooltipEntries: Array<{ Key: string; Value: unknown }> = [];\n\n // ================================================================\n // Detail Panel State\n // ================================================================\n\n /** The currently selected point shown in the detail panel. */\n public SelectedPoint: ClusterPoint | null = null;\n\n /** Whether the detail panel is visible. */\n public ShowDetailPanel = false;\n\n /** Metadata entries for the detail panel (computed on selection). */\n public DetailEntries: Array<{ Key: string; Value: unknown }> = [];\n\n /** Cluster members for the selected point's cluster. */\n public ClusterMembers: ClusterPoint[] = [];\n\n /** Whether the cluster members list is expanded. */\n public ClusterMembersExpanded = true;\n\n @ViewChild('svgElement', { static: false }) private svgRef!: ElementRef<SVGSVGElement>;\n\n // Pan/zoom state\n private isPanning = false;\n private panStartX = 0;\n private panStartY = 0;\n private panStartViewBox = [0, 0, 1000, 700];\n // 3D orbit-drag state\n private isRotating = false;\n private rotateStartX = 0;\n private rotateStartY = 0;\n private rotateStartYaw = 0;\n private rotateStartPitch = 0;\n private boundOnMouseMove: ((e: MouseEvent) => void) | null = null;\n private boundOnMouseUp: ((e: MouseEvent) => void) | null = null;\n\n /** Default viewBox dimensions used for zoom-limit calculations. */\n private readonly defaultWidth = 1000;\n private readonly defaultHeight = 700;\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngAfterViewInit(): void {\n this.boundOnMouseMove = this.onMouseMove.bind(this);\n this.boundOnMouseUp = this.onMouseUp.bind(this);\n document.addEventListener('mousemove', this.boundOnMouseMove);\n document.addEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnDestroy(): void {\n if (this.boundOnMouseMove) document.removeEventListener('mousemove', this.boundOnMouseMove);\n if (this.boundOnMouseUp) document.removeEventListener('mouseup', this.boundOnMouseUp);\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n this.computeCentroids();\n\n // Emit AfterClusteringComplete when new data arrives\n if ((changes['Points'] || changes['Clusters']) && this.Points.length > 0 && this.Clusters.length > 0) {\n this.AfterClusteringComplete.emit({\n Points: this.Points,\n Clusters: this.Clusters,\n Metrics: {\n SilhouetteScore: 0,\n ClusterCount: this.Clusters.length,\n ComputationTimeMs: 0,\n RecordCount: this.Points.length,\n OutlierCount: this.Points.filter(p => p.ClusterId < 0).length,\n },\n Config: {\n EntityName: '',\n EntityDocumentID: '',\n Algorithm: 'kmeans',\n K: this.Clusters.length,\n Epsilon: 0,\n MinPoints: 0,\n DistanceMetric: 'cosine',\n MaxRecords: this.Points.length,\n Filter: '',\n },\n });\n }\n }\n\n // ================================================================\n // Template Helpers\n // ================================================================\n\n /**\n * Returns the current SVG `viewBox` as a space-separated string.\n *\n * @returns e.g. `\"0 0 1000 700\"`\n */\n public GetViewBoxString(): string {\n return this.ViewBox.join(' ');\n }\n\n /**\n * Resolve the display color for a given point based on its cluster\n * assignment and the active `ColorPalette`.\n *\n * @param point The point to colorize.\n * @returns CSS color string.\n */\n public GetPointColor(point: ClusterPoint): string {\n if (this.ColorBy === 'entity') {\n return this.getEntityColor(point);\n }\n if (point.ClusterId < 0) return '#64748b'; // outlier gray\n const palette = this.getActivePalette();\n const cluster = this.Clusters.find(c => c.Id === point.ClusterId);\n return cluster?.Color ?? palette[point.ClusterId % palette.length];\n }\n\n // ================================================================\n // Color-by-entity (multi-entity analyses)\n // ================================================================\n\n /** Stable entity-name → color map, assigned in first-seen order. */\n private entityColorMap = new Map<string, string>();\n\n /** Resolve a color for a point keyed by its source entity name. */\n private getEntityColor(point: ClusterPoint): string {\n const name = (point.Metadata?.['EntityName'] as string) || (point.Metadata?.['Entity'] as string) || 'Unknown';\n let color = this.entityColorMap.get(name);\n if (!color) {\n const palette = this.getActivePalette();\n color = palette[this.entityColorMap.size % palette.length];\n this.entityColorMap.set(name, color);\n }\n return color;\n }\n\n /** Distinct source entities present in the data (for the entity legend). */\n public get EntityLegend(): Array<{ Name: string; Color: string; Count: number }> {\n if (this.ColorBy !== 'entity') return [];\n const counts = new Map<string, number>();\n for (const p of this.Points) {\n const name = (p.Metadata?.['EntityName'] as string) || (p.Metadata?.['Entity'] as string) || 'Unknown';\n counts.set(name, (counts.get(name) ?? 0) + 1);\n }\n return [...counts.entries()].map(([Name, Count]) => ({\n Name,\n Count,\n Color: this.getEntityColor({ Metadata: { EntityName: Name } } as unknown as ClusterPoint),\n }));\n }\n\n // ================================================================\n // 3D projection (static isometric, depth-cued)\n // ================================================================\n\n /** True when any point carries a Z coordinate (a 3D projection). */\n public get Is3D(): boolean {\n return this.Points.some(p => p.Z != null);\n }\n\n /** Orbit angles (radians) for the interactive 3D projection. Updated by drag. */\n public Yaw = 0.6;\n public Pitch = 0.45;\n\n /** Rotate a centered (x,y,z) by the current yaw (around Y) then pitch (around X). */\n private rotate(point: ClusterPoint): { x: number; y: number; z: number } {\n const x0 = point.X - 500;\n const y0 = point.Y - 350;\n const z0 = (point.Z ?? 500) - 500;\n const cy = Math.cos(this.Yaw), sy = Math.sin(this.Yaw);\n // yaw around vertical (Y) axis\n const x1 = x0 * cy + z0 * sy;\n const z1 = -x0 * sy + z0 * cy;\n const y1 = y0;\n // pitch around horizontal (X) axis\n const cp = Math.cos(this.Pitch), sp = Math.sin(this.Pitch);\n const y2 = y1 * cp - z1 * sp;\n const z2 = y1 * sp + z1 * cp;\n return { x: x1, y: y2, z: z2 };\n }\n\n /** Project a point's (X,Y,Z) to a screen X in the SVG coordinate space. */\n public PX(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return point.X;\n return 500 + this.rotate(point).x;\n }\n\n /** Project a point's (X,Y,Z) to a screen Y in the SVG coordinate space. */\n public PY(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return point.Y;\n return 350 + this.rotate(point).y;\n }\n\n /** Normalized depth [0=far, 1=near] for a point, used for size/opacity cues. */\n private depthNorm(point: ClusterPoint): number {\n if (!this.Is3D || point.Z == null) return 1;\n const depth = this.rotate(point).z;\n // centered coords span roughly ±440 per axis; combined depth ~[-760, 760]\n return Math.max(0, Math.min(1, (depth + 600) / 1200));\n }\n\n /** Reset the 3D orbit to the default viewing angle. */\n public ResetRotation(): void {\n this.Yaw = 0.6;\n this.Pitch = 0.45;\n this.cdr.detectChanges();\n }\n\n /**\n * Cluster labels positioned at each cluster's centroid in *projected* space.\n * Used to label clusters in 3D (where the 2D centroid-glow path is disabled).\n * Recomputes as the user orbits because it depends on PX/PY (yaw/pitch).\n */\n public get ProjectedClusterLabels(): Array<{ X: number; Y: number; ClusterId: number; Color: string }> {\n if (!this.Is3D) return [];\n const groups = new Map<number, ClusterPoint[]>();\n for (const p of this.Points) {\n if (p.ClusterId < 0) continue;\n const arr = groups.get(p.ClusterId);\n if (arr) arr.push(p); else groups.set(p.ClusterId, [p]);\n }\n const palette = this.getActivePalette();\n const out: Array<{ X: number; Y: number; ClusterId: number; Color: string }> = [];\n for (const [cid, pts] of groups) {\n const sx = pts.reduce((s, p) => s + this.PX(p), 0) / pts.length;\n const sy = pts.reduce((s, p) => s + this.PY(p), 0) / pts.length;\n const cluster = this.Clusters.find(c => c.Id === cid);\n out.push({ X: sx, Y: sy, ClusterId: cid, Color: cluster?.Color ?? palette[cid % palette.length] });\n }\n return out;\n }\n\n /**\n * Points in render order. In 3D, far points are drawn first so nearer points\n * paint on top (SVG has no z-index); in 2D the original order is preserved.\n */\n public get RenderPoints(): ClusterPoint[] {\n if (!this.Is3D) return this.Points;\n return [...this.Points].sort((a, b) => this.depthNorm(a) - this.depthNorm(b));\n }\n\n /**\n * Compute the SVG radius for a data point.\n *\n * Selected points use the larger `HighlightRadius`; all others use `DotRadius`.\n *\n * @param point The point to size.\n * @returns Radius in SVG units.\n */\n public GetPointRadius(point: ClusterPoint): number {\n if (this.SelectedPointIds.has(point.VectorKey)) {\n return this.HighlightRadius;\n }\n if (this.Is3D) {\n // Nearer points (depth→1) render larger to convey depth.\n return this.DotRadius * (0.6 + 0.7 * this.depthNorm(point));\n }\n return this.DotRadius;\n }\n\n /**\n * Compute the fill opacity for a data point.\n *\n * Non-hovered points are dimmed when another point is hovered.\n *\n * @param point The point to evaluate.\n * @returns Opacity value between 0 and 1.\n */\n public GetPointOpacity(point: ClusterPoint): number {\n if (this.HoveredPoint && this.HoveredPoint.VectorKey !== point.VectorKey) {\n return this.DotOpacity * 0.5;\n }\n if (this.Is3D) {\n // Fade far points slightly for depth perception.\n return this.DotOpacity * (0.55 + 0.45 * this.depthNorm(point));\n }\n return this.DotOpacity;\n }\n\n /**\n * Handle mouseenter on a data point: show tooltip, fire hover events.\n *\n * @param point The hovered point.\n * @param event The native mouse event (used for tooltip positioning).\n */\n public OnPointMouseEnter(point: ClusterPoint, event: MouseEvent): void {\n const cancelable: CancelableEvent<ClusterPoint> = { Data: point, Cancel: false };\n this.BeforePointHover.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.HoveredPoint = point;\n this.TooltipX = event.clientX + 16;\n this.TooltipY = event.clientY - 10;\n this.computeTooltipEntries(point);\n this.PointHovered.emit(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Handle mouseleave on a data point: hide tooltip, emit null hover.\n */\n public OnPointMouseLeave(): void {\n this.HoveredPoint = null;\n this.TooltipEntries = [];\n this.PointHovered.emit(null);\n this.cdr.detectChanges();\n }\n\n /**\n * Handle click on a data point: fires `BeforePointClick`, toggles\n * selection, opens the detail panel, then fires `PointClicked` and\n * `SelectionChanged`.\n *\n * @param point The clicked point.\n */\n public OnPointClick(point: ClusterPoint): void {\n const cancelable: CancelableEvent<ClusterPoint> = { Data: point, Cancel: false };\n this.BeforePointClick.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.HighlightedKey = point.VectorKey;\n\n // Toggle selection\n const newSelection = new Set(this.SelectedPointIds);\n if (newSelection.has(point.VectorKey)) {\n newSelection.delete(point.VectorKey);\n } else {\n newSelection.add(point.VectorKey);\n }\n this.SelectedPointIds = newSelection;\n this.SelectionChanged.emit(newSelection);\n\n // Open detail panel for the clicked point\n this.selectPointForDetail(point);\n\n this.PointClicked.emit(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Whether the given point is the currently highlighted (last-clicked) point.\n *\n * @param point The point to check.\n * @returns `true` if highlighted.\n */\n public IsHighlighted(point: ClusterPoint): boolean {\n return this.HighlightedKey === point.VectorKey;\n }\n\n /**\n * Whether the given point is in the current selection set.\n *\n * @param point The point to check.\n * @returns `true` if selected.\n */\n public IsSelected(point: ClusterPoint): boolean {\n return this.SelectedPointIds.has(point.VectorKey);\n }\n\n /**\n * Handle mouse wheel events for zooming.\n *\n * Fires `BeforeZoom` and `ViewportChanged`. Respects `EnableZoom`,\n * `MinZoom`, and `MaxZoom` constraints.\n *\n * @param event The native wheel event.\n */\n public OnWheel(event: WheelEvent): void {\n event.preventDefault();\n if (!this.EnableZoom) return;\n\n const zoomFactor = event.deltaY > 0 ? 1.1 : 0.9;\n const [minX, minY, width, height] = this.ViewBox;\n\n const minWidth = this.defaultWidth * this.MinZoom;\n const maxWidth = this.defaultWidth * this.MaxZoom;\n const minHeight = this.defaultHeight * this.MinZoom;\n const maxHeight = this.defaultHeight * this.MaxZoom;\n\n const newWidth = Math.max(minWidth, Math.min(maxWidth, width * zoomFactor));\n const newHeight = Math.max(minHeight, Math.min(maxHeight, height * zoomFactor));\n\n const centerX = minX + width / 2;\n const centerY = minY + height / 2;\n\n const proposed: ViewportRect = {\n MinX: centerX - newWidth / 2,\n MinY: centerY - newHeight / 2,\n Width: newWidth,\n Height: newHeight,\n };\n\n const cancelable: CancelableEvent<ViewportRect> = { Data: proposed, Cancel: false };\n this.BeforeZoom.emit(cancelable);\n if (cancelable.Cancel) return;\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Start panning on left-mousedown.\n *\n * @param event The native mouse event.\n */\n public OnMouseDown(event: MouseEvent): void {\n if (event.button !== 0) return;\n // In 3D mode a left-drag orbits the camera (so depth is perceivable);\n // in 2D it pans the viewBox.\n if (this.Is3D) {\n this.isRotating = true;\n this.rotateStartX = event.clientX;\n this.rotateStartY = event.clientY;\n this.rotateStartYaw = this.Yaw;\n this.rotateStartPitch = this.Pitch;\n return;\n }\n if (!this.EnablePan) return;\n this.isPanning = true;\n this.panStartX = event.clientX;\n this.panStartY = event.clientY;\n this.panStartViewBox = [...this.ViewBox];\n }\n\n /**\n * Handle a click on a cluster legend item.\n *\n * @param cluster The cluster that was clicked.\n */\n public OnClusterLegendClick(cluster: ClusterInfo): void {\n this.ClusterSelected.emit({\n ClusterId: cluster.Id,\n Label: cluster.Label,\n Color: cluster.Color,\n MemberCount: cluster.MemberCount,\n });\n }\n\n /**\n * Resolve the human-readable label for a point's cluster.\n *\n * @param point The point to look up.\n * @returns Cluster label string, or `\"Outlier\"` for noise points.\n */\n public GetClusterLabel(point: ClusterPoint): string {\n if (point.ClusterId < 0) return 'Outlier';\n const cluster = this.Clusters.find(c => c.Id === point.ClusterId);\n return cluster?.Label ?? `Cluster ${point.ClusterId + 1}`;\n }\n\n /** Get the label for a cluster by ID (used for SVG text labels on the chart). */\n public GetClusterLabelById(clusterId: number): string {\n const cluster = this.Clusters.find(c => c.Id === clusterId);\n return cluster?.Label ?? '';\n }\n\n /** @internal TrackBy function for point loops. */\n public TrackPointBy(_index: number, point: ClusterPoint): string {\n return point.VectorKey;\n }\n\n /** @internal TrackBy function for cluster loops. */\n public TrackClusterBy(_index: number, cluster: ClusterInfo): number {\n return cluster.Id;\n }\n\n /** @internal TrackBy function for centroid loops. */\n public TrackCentroidBy(_index: number, centroid: { ClusterId: number }): number {\n return centroid.ClusterId;\n }\n\n // ================================================================\n // Public API Methods\n // ================================================================\n\n /**\n * Animate the viewport to center on a specific cluster's centroid.\n *\n * The zoom level is adjusted so that all cluster members are visible\n * with some padding.\n *\n * @param clusterId The numeric ID of the cluster to zoom into.\n *\n * @example\n * ```typescript\n * @ViewChild(ClusterScatterComponent) scatter!: ClusterScatterComponent;\n * this.scatter.ZoomToCluster(2);\n * ```\n */\n public ZoomToCluster(clusterId: number): void {\n const members = this.Points.filter(p => p.ClusterId === clusterId);\n if (members.length === 0) return;\n\n const bounds = this.computeBounds(members);\n const padding = 80;\n const proposed: ViewportRect = {\n MinX: bounds.minX - padding,\n MinY: bounds.minY - padding,\n Width: (bounds.maxX - bounds.minX) + padding * 2,\n Height: (bounds.maxY - bounds.minY) + padding * 2,\n };\n\n // Ensure minimum dimensions\n if (proposed.Width < 200) {\n const cx = proposed.MinX + proposed.Width / 2;\n proposed.Width = 200;\n proposed.MinX = cx - 100;\n }\n if (proposed.Height < 140) {\n const cy = proposed.MinY + proposed.Height / 2;\n proposed.Height = 140;\n proposed.MinY = cy - 70;\n }\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Reset the viewport to the default zoom level (full view).\n *\n * @example\n * ```typescript\n * this.scatter.ResetZoom();\n * ```\n */\n public ResetZoom(): void {\n const proposed: ViewportRect = {\n MinX: 0,\n MinY: 0,\n Width: this.defaultWidth,\n Height: this.defaultHeight,\n };\n this.ViewBox = [0, 0, this.defaultWidth, this.defaultHeight];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n /**\n * Return all points currently visible within the viewport bounds.\n *\n * @returns Array of `ClusterPoint` objects whose X/Y fall inside the\n * current viewBox.\n */\n public GetVisiblePoints(): ClusterPoint[] {\n const [minX, minY, width, height] = this.ViewBox;\n const maxX = minX + width;\n const maxY = minY + height;\n return this.Points.filter(p =>\n p.X >= minX && p.X <= maxX && p.Y >= minY && p.Y <= maxY\n );\n }\n\n /**\n * Programmatically select points by their `VectorKey` IDs.\n *\n * Merges with the existing selection. Use `ClearSelection()` first\n * to replace the selection entirely.\n *\n * @param ids Array of `VectorKey` strings to select.\n *\n * @example\n * ```typescript\n * this.scatter.SelectPoints(['abc-123', 'def-456']);\n * ```\n */\n public SelectPoints(ids: string[]): void {\n const newSelection = new Set(this.SelectedPointIds);\n for (const id of ids) {\n newSelection.add(id);\n }\n this.SelectedPointIds = newSelection;\n this.SelectionChanged.emit(newSelection);\n this.cdr.detectChanges();\n }\n\n /**\n * Clear all selected points.\n */\n public ClearSelection(): void {\n this.SelectedPointIds = new Set<string>();\n this.HighlightedKey = null;\n this.SelectionChanged.emit(this.SelectedPointIds);\n this.cdr.detectChanges();\n }\n\n /**\n * Export the current scatter plot as an SVG string.\n *\n * The returned string is a self-contained `<svg>` element that can be\n * saved to a file or embedded in HTML.\n *\n * @returns SVG markup string, or empty string if the SVG element is\n * not yet rendered.\n */\n public ExportSVG(): string {\n if (!this.svgRef?.nativeElement) return '';\n const svgElement = this.svgRef.nativeElement;\n const serializer = new XMLSerializer();\n return serializer.serializeToString(svgElement);\n }\n\n /**\n * Visually emphasize all points belonging to a cluster by increasing\n * their radius and dimming non-members.\n *\n * Call `ResetZoom()` or re-assign `Points` to clear the highlight.\n *\n * @param clusterId The numeric cluster ID to emphasize.\n */\n public HighlightCluster(clusterId: number): void {\n const memberKeys = this.Points\n .filter(p => p.ClusterId === clusterId)\n .map(p => p.VectorKey);\n this.SelectedPointIds = new Set(memberKeys);\n this.SelectionChanged.emit(this.SelectedPointIds);\n this.cdr.detectChanges();\n }\n\n // ================================================================\n // Detail Panel Methods\n // ================================================================\n\n /**\n * Close the detail panel.\n */\n public CloseDetailPanel(): void {\n this.ShowDetailPanel = false;\n this.SelectedPoint = null;\n this.DetailEntries = [];\n this.ClusterMembers = [];\n this.cdr.detectChanges();\n }\n\n /**\n * Emit the `OpenRecordRequested` event for the currently selected point\n * (triggered by the \"Open Record\" button in the detail panel).\n */\n public OnOpenRecordClick(): void {\n if (this.SelectedPoint) {\n this.OpenRecordRequested.emit(this.SelectedPoint);\n }\n }\n\n /**\n * Select a point from the cluster members list, updating the detail\n * panel and highlighting the point on the chart.\n *\n * @param point The cluster member point to select.\n */\n public OnClusterMemberClick(point: ClusterPoint): void {\n this.HighlightedKey = point.VectorKey;\n this.selectPointForDetail(point);\n this.cdr.detectChanges();\n }\n\n /**\n * Toggle the cluster members list expansion state.\n */\n public ToggleClusterMembers(): void {\n this.ClusterMembersExpanded = !this.ClusterMembersExpanded;\n }\n\n /**\n * Get the color for a cluster by its ID, used in the detail panel.\n *\n * @param clusterId The cluster ID.\n * @returns CSS color string.\n */\n public GetClusterColor(clusterId: number): string {\n if (clusterId < 0) return '#64748b';\n const cluster = this.Clusters.find(c => c.Id === clusterId);\n if (cluster?.Color) return cluster.Color;\n const palette = this.getActivePalette();\n return palette[clusterId % palette.length];\n }\n\n /**\n * Return the metadata keys that should be hidden in the detail panel.\n */\n private get internalMetadataKeys(): Set<string> {\n return new Set([\n 'Name', 'Entity', 'EntityIcon', 'RecordID',\n 'TemplateID', '__mj_UpdatedAt', '__mj_CreatedAt',\n ]);\n }\n\n /**\n * Select a point and populate the detail panel state.\n */\n private selectPointForDetail(point: ClusterPoint): void {\n this.SelectedPoint = point;\n this.ShowDetailPanel = true;\n this.computeDetailEntries(point);\n this.computeClusterMembers(point);\n }\n\n /**\n * Compute the metadata entries for the detail panel, excluding internal fields.\n * Uses FieldPriority for ordering when available.\n */\n private computeDetailEntries(point: ClusterPoint): void {\n const hidden = this.internalMetadataKeys;\n const raw = Object.entries(point.Metadata)\n .filter(([key]) => !hidden.has(key))\n .filter(([, value]) => value != null && String(value).trim() !== '')\n .map(([Key, Value]) => ({ Key, Value }));\n\n this.DetailEntries = this.sortEntriesByPriority(raw);\n }\n\n /**\n * Compute the list of cluster members for the selected point's cluster.\n */\n private computeClusterMembers(point: ClusterPoint): void {\n this.ClusterMembers = this.Points\n .filter(p => p.ClusterId === point.ClusterId)\n .sort((a, b) => a.Label.localeCompare(b.Label));\n }\n\n /** @internal TrackBy function for cluster member loops. */\n public TrackMemberBy(_index: number, point: ClusterPoint): string {\n return point.VectorKey;\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private onMouseMove(event: MouseEvent): void {\n if (this.isRotating) {\n const dx = event.clientX - this.rotateStartX;\n const dy = event.clientY - this.rotateStartY;\n this.Yaw = this.rotateStartYaw + dx * 0.01;\n // clamp pitch so the scene never flips upside down\n this.Pitch = Math.max(-1.3, Math.min(1.3, this.rotateStartPitch + dy * 0.01));\n this.cdr.detectChanges();\n return;\n }\n if (!this.isPanning || !this.svgRef) return;\n const svg = this.svgRef.nativeElement;\n const rect = svg.getBoundingClientRect();\n const scaleX = this.panStartViewBox[2] / rect.width;\n const scaleY = this.panStartViewBox[3] / rect.height;\n\n const dx = (event.clientX - this.panStartX) * scaleX;\n const dy = (event.clientY - this.panStartY) * scaleY;\n\n const proposed: ViewportRect = {\n MinX: this.panStartViewBox[0] - dx,\n MinY: this.panStartViewBox[1] - dy,\n Width: this.panStartViewBox[2],\n Height: this.panStartViewBox[3],\n };\n\n this.ViewBox = [proposed.MinX, proposed.MinY, proposed.Width, proposed.Height];\n this.ViewportChanged.emit(proposed);\n this.cdr.detectChanges();\n }\n\n private onMouseUp(_event: MouseEvent): void {\n this.isPanning = false;\n this.isRotating = false;\n }\n\n private computeCentroids(): void {\n if (!this.Points.length || !this.Clusters.length) {\n this.ClusterCentroids = [];\n return;\n }\n\n this.ClusterCentroids = this.Clusters.map(cluster => {\n const members = this.Points.filter(p => p.ClusterId === cluster.Id);\n if (members.length === 0) {\n return { ClusterId: cluster.Id, Cx: 500, Cy: 350, R: 80, Color: cluster.Color };\n }\n const cx = members.reduce((s, p) => s + p.X, 0) / members.length;\n const cy = members.reduce((s, p) => s + p.Y, 0) / members.length;\n const maxDist = Math.max(\n ...members.map(p => Math.sqrt((p.X - cx) ** 2 + (p.Y - cy) ** 2)),\n 40\n );\n return {\n ClusterId: cluster.Id,\n Cx: cx,\n Cy: cy,\n R: Math.min(maxDist + 30, 200),\n Color: cluster.Color,\n };\n });\n }\n\n /**\n * Compute the bounding box for a set of points.\n */\n private computeBounds(points: ClusterPoint[]): { minX: number; minY: number; maxX: number; maxY: number } {\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\n for (const p of points) {\n if (p.X < minX) minX = p.X;\n if (p.X > maxX) maxX = p.X;\n if (p.Y < minY) minY = p.Y;\n if (p.Y > maxY) maxY = p.Y;\n }\n return { minX, minY, maxX, maxY };\n }\n\n /**\n * Resolve the effective color palette (custom override or default).\n */\n private getActivePalette(): string[] {\n return this.ColorPalette.length > 0 ? this.ColorPalette : CLUSTER_COLORS;\n }\n\n /**\n * Compute the tooltip entries based on TooltipFields filter.\n * Uses FieldPriority for ordering when available.\n */\n private computeTooltipEntries(point: ClusterPoint): void {\n const entries = Object.entries(point.Metadata);\n let mapped: { Key: string; Value: unknown }[];\n if (this.TooltipFields.length > 0) {\n mapped = entries\n .filter(([key]) => this.TooltipFields.includes(key))\n .map(([Key, Value]) => ({ Key, Value }));\n } else {\n mapped = entries.map(([Key, Value]) => ({ Key, Value }));\n }\n this.TooltipEntries = this.sortEntriesByPriority(mapped);\n }\n\n /**\n * Sort entries using the FieldPriority ordering.\n * Fields in FieldPriority come first (in priority order), then remaining fields.\n */\n private sortEntriesByPriority(entries: { Key: string; Value: unknown }[]): { Key: string; Value: unknown }[] {\n if (this.FieldPriority.length === 0) return entries;\n\n const priorityIndex = new Map(this.FieldPriority.map((key, idx) => [key, idx]));\n return [...entries].sort((a, b) => {\n const aIdx = priorityIndex.get(a.Key) ?? 99999;\n const bIdx = priorityIndex.get(b.Key) ?? 99999;\n return aIdx - bIdx;\n });\n }\n}\n","<div class=\"scatter-container\">\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\">\n <div class=\"loading-spinner\">\n <i class=\"fa-solid fa-circle-nodes fa-spin\"></i>\n <span>Computing clusters...</span>\n </div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && Points.length === 0) {\n <mj-empty-state\n class=\"empty-state-fill\"\n Icon=\"fa-solid fa-chart-simple\"\n Title=\"No data to display\"\n Message=\"Configure and run clustering to see results\" />\n }\n\n <!-- SVG scatter plot -->\n @if (Points.length > 0) {\n <svg #svgElement\n class=\"scatter-svg\"\n [class.no-transitions]=\"!AnimateTransitions\"\n [attr.viewBox]=\"ViewBoxString\"\n preserveAspectRatio=\"xMidYMid meet\"\n (wheel)=\"OnWheel($event)\"\n (mousedown)=\"OnMouseDown($event)\">\n\n <!-- Gradient definitions for cluster glow -->\n <defs>\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <radialGradient [attr.id]=\"'cluster-glow-' + centroid.ClusterId\"\n cx=\"50%\" cy=\"50%\" r=\"50%\">\n <stop offset=\"0%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0.12\"/>\n <stop offset=\"100%\" [attr.stop-color]=\"centroid.Color\" stop-opacity=\"0\"/>\n </radialGradient>\n }\n </defs>\n\n <!-- Background grid lines -->\n <g class=\"grid-lines\">\n @for (y of HorizontalGridLines; track y) {\n <line [attr.x1]=\"0\" [attr.y1]=\"y\" [attr.x2]=\"1000\" [attr.y2]=\"y\"/>\n }\n @for (x of VerticalGridLines; track x) {\n <line [attr.x1]=\"x\" [attr.y1]=\"0\" [attr.x2]=\"x\" [attr.y2]=\"700\"/>\n }\n </g>\n\n <!-- Cluster glow + labels (2D only; centroids are 2D aesthetics) -->\n @if (!Is3D) {\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n <circle [attr.cx]=\"centroid.Cx\"\n [attr.cy]=\"centroid.Cy\"\n [attr.r]=\"centroid.R\"\n [attr.fill]=\"'url(#cluster-glow-' + centroid.ClusterId + ')'\"/>\n }\n @for (centroid of ClusterCentroids; track TrackCentroidBy($index, centroid)) {\n @if (GetClusterLabelById(centroid.ClusterId); as label) {\n <text class=\"cluster-label\"\n [attr.x]=\"centroid.Cx\"\n [attr.y]=\"centroid.Cy - centroid.R - 8\"\n text-anchor=\"middle\"\n [attr.fill]=\"centroid.Color\">{{ label }}</text>\n }\n }\n }\n\n <!-- Data points (RenderPoints is depth-sorted in 3D) -->\n @for (point of RenderPoints; track TrackPointBy($index, point)) {\n <circle class=\"data-point\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"GetPointRadius(point)\"\n [attr.fill]=\"GetPointColor(point)\"\n [attr.opacity]=\"GetPointOpacity(point)\"\n (mouseenter)=\"OnPointMouseEnter(point, $event)\"\n (mouseleave)=\"OnPointMouseLeave()\"\n (click)=\"OnPointClick(point); $event.stopPropagation()\"/>\n }\n\n <!-- 3D cluster labels (at projected centroids; follow rotation) -->\n @if (Is3D && ColorBy !== 'entity') {\n @for (lbl of ProjectedClusterLabels; track lbl.ClusterId) {\n @if (GetClusterLabelById(lbl.ClusterId); as text) {\n <text class=\"cluster-label cluster-label-3d\"\n [attr.x]=\"lbl.X\"\n [attr.y]=\"lbl.Y\"\n text-anchor=\"middle\"\n [attr.fill]=\"lbl.Color\">{{ text }}</text>\n }\n }\n }\n\n <!-- Selection rings for selected points -->\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsSelected(point)) {\n <circle class=\"selection-ring\"\n [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 2\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2\"\n opacity=\"0.8\"/>\n }\n }\n\n <!-- Highlight ring for last-clicked point -->\n @if (HighlightedKey) {\n @for (point of Points; track TrackPointBy($index, point)) {\n @if (IsHighlighted(point)) {\n <circle [attr.cx]=\"PX(point)\"\n [attr.cy]=\"PY(point)\"\n [attr.r]=\"HighlightRadius + 4\"\n fill=\"none\"\n [attr.stroke]=\"GetPointColor(point)\"\n stroke-width=\"2.5\"\n opacity=\"0.9\"/>\n }\n }\n }\n </svg>\n }\n\n <!-- 3D orbit hint + reset -->\n @if (Points.length > 0 && Is3D) {\n <div class=\"threed-hint\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>3D · drag to rotate</span>\n <button class=\"threed-reset\" (click)=\"ResetRotation()\" title=\"Reset view\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n </div>\n }\n\n <!-- Entity legend (multi-entity color mode) -->\n @if (ShowLegend && ColorBy === 'entity' && EntityLegend.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Entities</div>\n @for (ent of EntityLegend; track ent.Name) {\n <div class=\"legend-item\">\n <div class=\"legend-dot\" [style.background]=\"ent.Color\"></div>\n <span class=\"legend-label\">{{ ent.Name }}</span>\n <span class=\"legend-count\">{{ ent.Count }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Legend overlay (cluster color mode) -->\n @if (ShowLegend && ColorBy !== 'entity' && Clusters.length > 0) {\n <div class=\"legend\">\n <div class=\"legend-title\">Clusters</div>\n @for (cluster of Clusters; track TrackClusterBy($index, cluster)) {\n <div class=\"legend-item\" (click)=\"OnClusterLegendClick(cluster)\">\n <div class=\"legend-dot\" [style.background]=\"cluster.Color\"></div>\n @if (EditingClusterId === cluster.Id) {\n <input class=\"legend-label-input\"\n [(ngModel)]=\"EditingLabelDraft\"\n (keydown)=\"OnLabelEditKeydown($event, cluster)\"\n (blur)=\"CommitLabelEdit(cluster)\"\n (click)=\"$event.stopPropagation()\"\n autofocus />\n } @else {\n <span class=\"legend-label\">{{ cluster.Label }}</span>\n <button class=\"legend-edit-btn\"\n title=\"Edit label\"\n (click)=\"StartLabelEdit(cluster, $event)\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n }\n <span class=\"legend-count\">{{ cluster.MemberCount }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Tooltip -->\n @if (ShowTooltip && HoveredPoint) {\n <div class=\"tooltip\"\n [style.left.px]=\"TooltipX\"\n [style.top.px]=\"TooltipY\">\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"HoveredPoint.Metadata\"\n Variant=\"compact\">\n </mj-entity-card>\n <div class=\"tt-cluster\">\n <i class=\"fa-solid fa-circle\" style=\"font-size: 7px; vertical-align: middle\"></i>\n {{ GetClusterLabel(HoveredPoint) }}\n </div>\n <div class=\"tt-action\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Click to view details\n </div>\n </div>\n }\n\n <!-- Detail Panel (slide-in from right) -->\n <div class=\"detail-panel\" [class.detail-panel-open]=\"ShowDetailPanel && SelectedPoint\">\n @if (SelectedPoint) {\n <!-- Panel Header -->\n <div class=\"detail-header\">\n <div class=\"detail-header-content\">\n @if (SelectedPoint.Metadata?.['EntityIcon']) {\n <div class=\"detail-icon\">\n <i [class]=\"SelectedPoint.Metadata['EntityIcon']\"></i>\n </div>\n }\n <div class=\"detail-header-text\">\n <div class=\"detail-name\">{{ SelectedPoint.Label }}</div>\n @if (SelectedPoint.Metadata?.['Entity']) {\n <span class=\"detail-entity-badge\">{{ SelectedPoint.Metadata['Entity'] }}</span>\n }\n </div>\n </div>\n <button class=\"detail-close\" (click)=\"CloseDetailPanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Cluster badge -->\n <div class=\"detail-cluster\">\n <span class=\"detail-cluster-dot\"\n [style.background]=\"GetClusterColor(SelectedPoint.ClusterId)\"></span>\n {{ GetClusterLabel(SelectedPoint) }}\n </div>\n\n <!-- Metadata fields via entity card -->\n <mj-entity-card\n [EntityName]=\"EntityName\"\n [Record]=\"SelectedPoint.Metadata\"\n Variant=\"detail\"\n CssClass=\"detail-entity-card\">\n </mj-entity-card>\n\n <!-- Cluster Members -->\n <div class=\"detail-members-section\">\n <mj-accordion-panel\n Size=\"sm\"\n [Fill]=\"true\"\n [FlushBody]=\"true\"\n [(Expanded)]=\"ClusterMembersExpanded\">\n <ng-template mjAccordionTitle>\n Cluster Members\n <span class=\"mj-accordion-badge mj-accordion-badge--right\">{{ ClusterMembers.length }}</span>\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"detail-members-list\">\n @for (member of ClusterMembers; track TrackMemberBy($index, member)) {\n <div class=\"detail-member-row\"\n [class.active]=\"member.VectorKey === SelectedPoint?.VectorKey\"\n (click)=\"OnClusterMemberClick(member)\">\n @if (member.Metadata?.['EntityIcon']) {\n <i [class]=\"member.Metadata['EntityIcon']\" class=\"member-icon\"></i>\n }\n <span class=\"member-label\">{{ member.Label }}</span>\n </div>\n }\n </div>\n </ng-template>\n </mj-accordion-panel>\n </div>\n\n <!-- Open Record button -->\n <div class=\"detail-footer\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecordClick()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Record\n </button>\n </div>\n }\n </div>\n</div>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-clustering",
3
- "version": "6.1.0-edge.5",
3
+ "version": "6.1.0-edge.6",
4
4
  "description": "MemberJunction: Reusable Angular clustering visualization components with scatter plot, config panel, and clustering service",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -17,27 +17,27 @@
17
17
  "author": "MemberJunction",
18
18
  "license": "BUSL-1.1",
19
19
  "devDependencies": {
20
- "@angular/compiler": "21.1.3",
21
- "@angular/compiler-cli": "21.1.3",
22
- "@memberjunction/ng-test-utils": "6.1.0-edge.5",
20
+ "@angular/compiler": "21.2.22",
21
+ "@angular/compiler-cli": "21.2.22",
22
+ "@memberjunction/ng-test-utils": "6.1.0-edge.6",
23
23
  "vitest": "^4.0.18"
24
24
  },
25
25
  "peerDependencies": {
26
- "@angular/common": "^21.1.3",
27
- "@angular/core": "^21.1.3",
28
- "@angular/forms": "^21.1.3"
26
+ "@angular/common": "^21.2.22",
27
+ "@angular/core": "^21.2.22",
28
+ "@angular/forms": "^21.2.22"
29
29
  },
30
30
  "dependencies": {
31
- "@memberjunction/ng-ui-components": "6.1.0-edge.5",
32
- "@memberjunction/ai-engine-base": "6.1.0-edge.5",
33
- "@memberjunction/ai-vectors-memory": "6.1.0-edge.5",
34
- "@memberjunction/core": "6.1.0-edge.5",
35
- "@memberjunction/core-entities": "6.1.0-edge.5",
36
- "@memberjunction/global": "6.1.0-edge.5",
37
- "@memberjunction/graphql-dataprovider": "6.1.0-edge.5",
38
- "@memberjunction/ng-base-types": "6.1.0-edge.5",
39
- "@memberjunction/ng-entity-card": "6.1.0-edge.5",
40
- "@memberjunction/ng-entity-viewer": "6.1.0-edge.5",
31
+ "@memberjunction/ng-ui-components": "6.1.0-edge.6",
32
+ "@memberjunction/ai-engine-base": "6.1.0-edge.6",
33
+ "@memberjunction/ai-vectors-memory": "6.1.0-edge.6",
34
+ "@memberjunction/core": "6.1.0-edge.6",
35
+ "@memberjunction/core-entities": "6.1.0-edge.6",
36
+ "@memberjunction/global": "6.1.0-edge.6",
37
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.6",
38
+ "@memberjunction/ng-base-types": "6.1.0-edge.6",
39
+ "@memberjunction/ng-entity-card": "6.1.0-edge.6",
40
+ "@memberjunction/ng-entity-viewer": "6.1.0-edge.6",
41
41
  "rxjs": "^7.8.2",
42
42
  "tslib": "^2.8.1",
43
43
  "umap-js": "^1.4.0"