@memberjunction/ng-search 6.1.4 → 6.2.0-edge.1

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.
@@ -373,7 +373,7 @@ function SearchResultsComponent_ng_template_3_Conditional_14_Template(rf, ctx) {
373
373
  i0.ɵɵadvance();
374
374
  i0.ɵɵconditional(result_r12.Snippet ? 1 : -1);
375
375
  i0.ɵɵadvance(5);
376
- i0.ɵɵtextInterpolate(result_r12.EntityName);
376
+ i0.ɵɵtextInterpolate(result_r12.EntityDisplayName || result_r12.EntityName);
377
377
  i0.ɵɵadvance(4);
378
378
  i0.ɵɵtextInterpolate(result_r12.RecordID);
379
379
  i0.ɵɵadvance(4);
@@ -418,7 +418,7 @@ function SearchResultsComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) {
418
418
  i0.ɵɵadvance(2);
419
419
  i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.RecordName || result_r12.Title), i0.ɵɵsanitizeHtml);
420
420
  i0.ɵɵadvance();
421
- i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.EntityName), i0.ɵɵsanitizeHtml);
421
+ i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.EntityDisplayName || result_r12.EntityName), i0.ɵɵsanitizeHtml);
422
422
  i0.ɵɵadvance();
423
423
  i0.ɵɵconditional(result_r12.Tags && result_r12.Tags.length > 0 ? 7 : -1);
424
424
  i0.ɵɵadvance();
@@ -687,7 +687,7 @@ export class SearchResultsComponent extends BaseAngularComponent {
687
687
  }
688
688
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchResultsComponent, [{
689
689
  type: Component,
690
- args: [{ standalone: false, selector: 'mj-search-results', template: "@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 FLAT BLENDED LIST (sorted by score) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 GROUPED BY SOURCE TYPE \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- \u2550\u2550\u2550 Shared result card template \u2550\u2550\u2550 -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: ["/* Search Results Component */\n\n:host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n}\n\n.results-scroll-area {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n}\n\n.results-loading {\n display: flex;\n justify-content: center;\n padding: 3rem 1rem;\n}\n\n.results-summary {\n padding: 0.75rem 1rem;\n color: var(--mj-text-secondary);\n font-size: 0.85rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-summary-time {\n color: var(--mj-text-muted);\n}\n\n/* Group Header */\n.results-group {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.results-group-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.results-group-icon {\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.results-group-label {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n flex: 1;\n}\n\n.results-group-count {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.results-group-chevron {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n transition: transform 0.2s ease;\n}\n\n/* Result Card \u2014 expandable */\n.result-card {\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin: 0 0 0.35rem 1.5rem;\n overflow: hidden;\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.result-card:hover {\n border-color: var(--mj-border-default);\n}\n\n.result-card-expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.result-card-main {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.85rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-card-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-card-icon-wrap {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.8rem;\n flex-shrink: 0;\n}\n\n.result-card-body {\n flex: 1;\n min-width: 0;\n}\n\n.result-card-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.85rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-card-entity {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n}\n\n.result-tag-chip {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n}\n\n.result-tag-more {\n display: inline-block;\n padding: 1px 6px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.result-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.result-open-btn,\n.result-expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.result-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.result-expand-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Expanded detail panel */\n.result-card-detail {\n padding: 0.75rem 1rem 0.85rem 3.5rem;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.detail-section {\n margin-bottom: 0.65rem;\n}\n\n.detail-label {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 0.25rem;\n display: block;\n}\n\n.detail-snippet {\n margin: 0;\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n margin-bottom: 0.65rem;\n}\n\n.detail-meta-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.detail-meta-item i {\n font-size: 0.65rem;\n}\n\n.detail-record-id {\n font-family: monospace;\n font-size: 0.7rem;\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 0.65rem;\n}\n\n.detail-links {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 8px;\n}\n\n.detail-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border-radius: 6px;\n font-size: 0.72rem;\n color: var(--mj-text-link);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-border-default));\n text-decoration: none;\n transition: background 0.15s ease;\n}\n\n.detail-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n text-decoration: underline;\n}\n\n.detail-link i {\n font-size: 0.6rem;\n}\n\n.detail-actions {\n display: flex;\n gap: 8px;\n}\n\n.detail-open-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-brand-primary);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.detail-similar-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-similar-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.result-card-snippet {\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.35rem;\n}\n\n.result-card-tag {\n display: inline-block;\n padding: 0.1rem 0.4rem;\n border-radius: 4px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tag-more {\n color: var(--mj-brand-primary);\n}\n\n.result-card-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Default: neutral gray */\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border: 2px solid color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n.score-circle.score-high {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-low {\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n\n.score-value {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n}\n.score-high .score-value {\n color: var(--mj-status-success-text, var(--mj-status-success));\n}\n.score-medium .score-value {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n}\n.score-low .score-value {\n color: var(--mj-text-muted);\n}\n\n/* Show More Button */\n.results-show-more {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 1rem;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n cursor: pointer;\n justify-content: center;\n transition: background 0.1s ease;\n}\n\n.results-show-more:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Google-style Pagination \u2014 pinned at bottom */\n.results-pager {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0.75rem 0;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.pager-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0 6px;\n}\n\n.pager-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.pager-btn:disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.pager-active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.pager-active:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n.pager-ellipsis {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n}\n\n.pager-prev i,\n.pager-next i {\n font-size: 0.7rem;\n}\n\n/* Search text highlighting */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n}\n"] }]
690
+ args: [{ standalone: false, selector: 'mj-search-results', template: "@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 FLAT BLENDED LIST (sorted by score) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 GROUPED BY SOURCE TYPE \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- \u2550\u2550\u2550 Shared result card template \u2550\u2550\u2550 -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityDisplayName || result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityDisplayName || result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: ["/* Search Results Component */\n\n:host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n}\n\n.results-scroll-area {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n}\n\n.results-loading {\n display: flex;\n justify-content: center;\n padding: 3rem 1rem;\n}\n\n.results-summary {\n padding: 0.75rem 1rem;\n color: var(--mj-text-secondary);\n font-size: 0.85rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-summary-time {\n color: var(--mj-text-muted);\n}\n\n/* Group Header */\n.results-group {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.results-group-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.results-group-icon {\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.results-group-label {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n flex: 1;\n}\n\n.results-group-count {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.results-group-chevron {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n transition: transform 0.2s ease;\n}\n\n/* Result Card \u2014 expandable */\n.result-card {\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin: 0 0 0.35rem 1.5rem;\n overflow: hidden;\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.result-card:hover {\n border-color: var(--mj-border-default);\n}\n\n.result-card-expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.result-card-main {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.85rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-card-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-card-icon-wrap {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.8rem;\n flex-shrink: 0;\n}\n\n.result-card-body {\n flex: 1;\n min-width: 0;\n}\n\n.result-card-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.85rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-card-entity {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n}\n\n.result-tag-chip {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n}\n\n.result-tag-more {\n display: inline-block;\n padding: 1px 6px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.result-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.result-open-btn,\n.result-expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.result-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.result-expand-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Expanded detail panel */\n.result-card-detail {\n padding: 0.75rem 1rem 0.85rem 3.5rem;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.detail-section {\n margin-bottom: 0.65rem;\n}\n\n.detail-label {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 0.25rem;\n display: block;\n}\n\n.detail-snippet {\n margin: 0;\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n margin-bottom: 0.65rem;\n}\n\n.detail-meta-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.detail-meta-item i {\n font-size: 0.65rem;\n}\n\n.detail-record-id {\n font-family: monospace;\n font-size: 0.7rem;\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 0.65rem;\n}\n\n.detail-links {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 8px;\n}\n\n.detail-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border-radius: 6px;\n font-size: 0.72rem;\n color: var(--mj-text-link);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-border-default));\n text-decoration: none;\n transition: background 0.15s ease;\n}\n\n.detail-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n text-decoration: underline;\n}\n\n.detail-link i {\n font-size: 0.6rem;\n}\n\n.detail-actions {\n display: flex;\n gap: 8px;\n}\n\n.detail-open-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-brand-primary);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.detail-similar-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-similar-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.result-card-snippet {\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.35rem;\n}\n\n.result-card-tag {\n display: inline-block;\n padding: 0.1rem 0.4rem;\n border-radius: 4px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tag-more {\n color: var(--mj-brand-primary);\n}\n\n.result-card-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Default: neutral gray */\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border: 2px solid color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n.score-circle.score-high {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-low {\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n\n.score-value {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n}\n.score-high .score-value {\n color: var(--mj-status-success-text, var(--mj-status-success));\n}\n.score-medium .score-value {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n}\n.score-low .score-value {\n color: var(--mj-text-muted);\n}\n\n/* Show More Button */\n.results-show-more {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 1rem;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n cursor: pointer;\n justify-content: center;\n transition: background 0.1s ease;\n}\n\n.results-show-more:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Google-style Pagination \u2014 pinned at bottom */\n.results-pager {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0.75rem 0;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.pager-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0 6px;\n}\n\n.pager-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.pager-btn:disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.pager-active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.pager-active:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n.pager-ellipsis {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n}\n\n.pager-prev i,\n.pager-next i {\n font-size: 0.7rem;\n}\n\n/* Search text highlighting */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n}\n"] }]
691
691
  }], null, { ResultGroups: [{
692
692
  type: Input
693
693
  }], FlatResults: [{
@@ -1 +1 @@
1
- {"version":3,"file":"search-results.component.js","sourceRoot":"","sources":["../../src/lib/search-results.component.ts","../../src/lib/search-results.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Results Component\n *\n * Displays search results grouped by source type with expandable sections.\n * Used both within the search overlay and as a standalone results view\n * in the Knowledge Hub search dashboard.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchResultItem, SearchResultGroup, SearchResultSelectedEvent } from './search-types';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-results',\n templateUrl: './search-results.component.html',\n styleUrls: ['./search-results.component.css']\n})\nexport class SearchResultsComponent extends BaseAngularComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Grouped search results to display (used when DisplayMode is 'grouped') */\n @Input() ResultGroups: SearchResultGroup[] = [];\n\n /** Flat list of all results sorted by score (used when DisplayMode is 'flat') */\n @Input() FlatResults: SearchResultItem[] = [];\n\n /** Display mode: 'flat' for blended list sorted by score, 'grouped' for source-type groups */\n @Input() DisplayMode: 'flat' | 'grouped' = 'flat';\n\n /** Whether results are currently loading */\n @Input() IsLoading = false;\n\n /** Total result count */\n @Input() TotalCount = 0;\n\n /** Elapsed search time in ms */\n @Input() ElapsedMs = 0;\n\n /** Maximum results per group before \"Show more\" (grouped mode only) */\n @Input() MaxPerGroup = 5;\n\n /** Results per page in flat mode */\n @Input() PageSize = 10;\n\n /** Whether to show the results summary line (count + time). Set false when parent provides its own header. */\n @Input() ShowSummary = true;\n\n /** Whether to show score badges */\n @Input() ShowScores = true;\n\n /** Whether to show tags */\n @Input() ShowTags = true;\n\n /** Whether to show source type icons */\n @Input() ShowSourceIcons = true;\n\n /** Text to highlight within result cards (e.g. from a client-side filter) */\n @Input() HighlightText = '';\n\n @Output() ResultSelected = new EventEmitter<SearchResultSelectedEvent>();\n\n /** Emitted when user clicks \"Open Record\" — parent handles navigation */\n @Output() OpenRecordRequested = new EventEmitter<SearchResultItem>();\n\n /** Emitted when user clicks \"See Similar Items\" — parent runs a \"more like this\" search */\n @Output() MoreLikeThisRequested = new EventEmitter<SearchResultItem>();\n\n /** Current page (1-based) for flat mode pagination */\n public CurrentPage = 1;\n\n /** Tracks which groups are expanded */\n public ExpandedGroups = new Set<string>();\n\n /** Set of expanded result card IDs — multiple can be open simultaneously */\n public ExpandedResultIDs = new Set<string>();\n\n // ── Flat mode pagination ──\n\n /** Get results for the current page in flat mode */\n public get PagedResults(): SearchResultItem[] {\n const start = (this.CurrentPage - 1) * this.PageSize;\n return this.FlatResults.slice(start, start + this.PageSize);\n }\n\n /** Total number of pages */\n public get TotalPages(): number {\n return Math.ceil(this.FlatResults.length / this.PageSize);\n }\n\n /** Page numbers to display in the pager */\n public get PageNumbers(): number[] {\n const total = this.TotalPages;\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n\n const pages: number[] = [1];\n const start = Math.max(2, this.CurrentPage - 2);\n const end = Math.min(total - 1, this.CurrentPage + 2);\n if (start > 2) pages.push(-1); // ellipsis marker\n for (let i = start; i <= end; i++) pages.push(i);\n if (end < total - 1) pages.push(-1); // ellipsis marker\n pages.push(total);\n return pages;\n }\n\n /** Navigate to a specific page */\n public GoToPage(page: number): void {\n if (page < 1 || page > this.TotalPages) return;\n this.CurrentPage = page;\n this.cdr.detectChanges();\n }\n\n // ── Grouped mode ──\n\n /** Toggle group expansion */\n public ToggleGroup(sourceType: string): void {\n if (this.ExpandedGroups.has(sourceType)) {\n this.ExpandedGroups.delete(sourceType);\n } else {\n this.ExpandedGroups.add(sourceType);\n }\n this.cdr.detectChanges();\n }\n\n /** Whether a group is expanded */\n public IsGroupExpanded(sourceType: string): boolean {\n return this.ExpandedGroups.has(sourceType);\n }\n\n /** Get visible results for a group (respecting max per group unless expanded) */\n public GetVisibleResults(group: SearchResultGroup): SearchResultItem[] {\n if (this.IsGroupExpanded(group.SourceType)) {\n return group.Results;\n }\n return group.Results.slice(0, this.MaxPerGroup);\n }\n\n /** Whether a group has more results than shown */\n public HasMoreResults(group: SearchResultGroup): boolean {\n return group.Results.length > this.MaxPerGroup && !this.IsGroupExpanded(group.SourceType);\n }\n\n /** Get the count of hidden results */\n public GetHiddenCount(group: SearchResultGroup): number {\n return group.Results.length - this.MaxPerGroup;\n }\n\n /** Handle clicking a result */\n public OnResultClick(result: SearchResultItem, event: MouseEvent): void {\n this.ResultSelected.emit({\n Result: result,\n OpenInNewTab: event.metaKey || event.ctrlKey\n });\n }\n\n /** Toggle expand/collapse of a result card — multiple can be open */\n public ToggleExpand(result: SearchResultItem, event?: MouseEvent): void {\n if (event) event.stopPropagation();\n if (this.ExpandedResultIDs.has(result.ID)) {\n this.ExpandedResultIDs.delete(result.ID);\n } else {\n this.ExpandedResultIDs.add(result.ID);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a result card is expanded */\n public IsExpanded(resultID: string): boolean {\n return this.ExpandedResultIDs.has(resultID);\n }\n\n /** Handle \"Open Record\" button click */\n public OnOpenRecord(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.OpenRecordRequested.emit(result);\n }\n\n /** Handle \"See Similar Items\" button click — emits the result for a \"more like this\" search */\n public OnMoreLikeThis(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.MoreLikeThisRequested.emit(result);\n }\n\n /**\n * Wrap substrings matching HighlightText in <mark> tags for visual emphasis.\n * The input is regex-escaped so user text is treated as a literal string.\n */\n public HighlightMatch(text: string): string {\n return HighlightSearchMatches(text, this.HighlightText, 'search-highlight');\n }\n\n /** Format a score as a percentage */\n public FormatScore(score: number): string {\n return `${Math.round(score * 100)}%`;\n }\n\n /** Format elapsed time */\n public FormatTime(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n }\n\n /** Resolved link cache per result ID */\n private resolvedLinksCache = new Map<string, { Value: string; Label: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Resolve URL and Email fields from the result's RawMetadata using entity ExtendedType metadata.\n * Returns clickable link entries with proper type classification.\n */\n public GetResolvedLinks(result: SearchResultItem): { Value: string; Label: string; Type: 'URL' | 'Email' }[] {\n if (this.resolvedLinksCache.has(result.ID)) {\n return this.resolvedLinksCache.get(result.ID)!;\n }\n\n const links: { Value: string; Label: string; Type: 'URL' | 'Email' }[] = [];\n\n if (result.RawMetadata) {\n try {\n const meta = JSON.parse(result.RawMetadata) as Record<string, unknown>;\n const linkFields = this.GetLinkFields(result.EntityName);\n\n for (const field of linkFields) {\n const value = meta[field.Name];\n if (value && typeof value === 'string' && value.trim().length > 0) {\n links.push({\n Value: value,\n Label: field.DisplayName,\n Type: field.Type\n });\n }\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Fallback: extract URLs from snippet if no ExtendedType fields found\n if (links.length === 0) {\n const urls = this.ExtractUrls(result.Snippet);\n for (const url of urls) {\n links.push({ Value: url, Label: '', Type: 'URL' });\n }\n }\n\n this.resolvedLinksCache.set(result.ID, links);\n return links;\n }\n\n /** Extract URLs from snippet text */\n public ExtractUrls(snippet: string): string[] {\n if (!snippet) return [];\n const urlRegex = /https?:\\/\\/[^\\s,;)>\"']+/g;\n const matches = snippet.match(urlRegex);\n return matches ? [...new Set(matches)] : [];\n }\n\n /** Truncate a URL for display */\n public TruncateUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.length > 30\n ? parsed.pathname.substring(0, 30) + '...'\n : parsed.pathname;\n return parsed.hostname + path;\n } catch {\n return url.length > 50 ? url.substring(0, 50) + '...' : url;\n }\n }\n\n /** Cache of URL/Email field info per entity name */\n private linkFieldsCache = new Map<string, { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Get URL and Email fields for an entity using ExtendedType from entity metadata.\n * Returns fields with ExtendedType='URL' or ExtendedType='Email'.\n */\n public GetLinkFields(entityName: string): { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[] {\n if (this.linkFieldsCache.has(entityName)) {\n return this.linkFieldsCache.get(entityName)!;\n }\n\n try {\n const md = this.ProviderToUse;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n\n const linkFields = entityInfo.Fields\n .filter(f => f.ExtendedType === 'URL' || f.ExtendedType === 'Email')\n .map(f => ({\n Name: f.Name,\n DisplayName: f.DisplayNameOrName,\n Type: f.ExtendedType as 'URL' | 'Email'\n }));\n\n this.linkFieldsCache.set(entityName, linkFields);\n return linkFields;\n } catch {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n }\n}\n","@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- ══════ FLAT BLENDED LIST (sorted by score) ══════ -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- ══════ GROUPED BY SOURCE TYPE ══════ -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- ═══ Shared result card template ═══ -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n"]}
1
+ {"version":3,"file":"search-results.component.js","sourceRoot":"","sources":["../../src/lib/search-results.component.ts","../../src/lib/search-results.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Results Component\n *\n * Displays search results grouped by source type with expandable sections.\n * Used both within the search overlay and as a standalone results view\n * in the Knowledge Hub search dashboard.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchResultItem, SearchResultGroup, SearchResultSelectedEvent } from './search-types';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-results',\n templateUrl: './search-results.component.html',\n styleUrls: ['./search-results.component.css']\n})\nexport class SearchResultsComponent extends BaseAngularComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Grouped search results to display (used when DisplayMode is 'grouped') */\n @Input() ResultGroups: SearchResultGroup[] = [];\n\n /** Flat list of all results sorted by score (used when DisplayMode is 'flat') */\n @Input() FlatResults: SearchResultItem[] = [];\n\n /** Display mode: 'flat' for blended list sorted by score, 'grouped' for source-type groups */\n @Input() DisplayMode: 'flat' | 'grouped' = 'flat';\n\n /** Whether results are currently loading */\n @Input() IsLoading = false;\n\n /** Total result count */\n @Input() TotalCount = 0;\n\n /** Elapsed search time in ms */\n @Input() ElapsedMs = 0;\n\n /** Maximum results per group before \"Show more\" (grouped mode only) */\n @Input() MaxPerGroup = 5;\n\n /** Results per page in flat mode */\n @Input() PageSize = 10;\n\n /** Whether to show the results summary line (count + time). Set false when parent provides its own header. */\n @Input() ShowSummary = true;\n\n /** Whether to show score badges */\n @Input() ShowScores = true;\n\n /** Whether to show tags */\n @Input() ShowTags = true;\n\n /** Whether to show source type icons */\n @Input() ShowSourceIcons = true;\n\n /** Text to highlight within result cards (e.g. from a client-side filter) */\n @Input() HighlightText = '';\n\n @Output() ResultSelected = new EventEmitter<SearchResultSelectedEvent>();\n\n /** Emitted when user clicks \"Open Record\" — parent handles navigation */\n @Output() OpenRecordRequested = new EventEmitter<SearchResultItem>();\n\n /** Emitted when user clicks \"See Similar Items\" — parent runs a \"more like this\" search */\n @Output() MoreLikeThisRequested = new EventEmitter<SearchResultItem>();\n\n /** Current page (1-based) for flat mode pagination */\n public CurrentPage = 1;\n\n /** Tracks which groups are expanded */\n public ExpandedGroups = new Set<string>();\n\n /** Set of expanded result card IDs — multiple can be open simultaneously */\n public ExpandedResultIDs = new Set<string>();\n\n // ── Flat mode pagination ──\n\n /** Get results for the current page in flat mode */\n public get PagedResults(): SearchResultItem[] {\n const start = (this.CurrentPage - 1) * this.PageSize;\n return this.FlatResults.slice(start, start + this.PageSize);\n }\n\n /** Total number of pages */\n public get TotalPages(): number {\n return Math.ceil(this.FlatResults.length / this.PageSize);\n }\n\n /** Page numbers to display in the pager */\n public get PageNumbers(): number[] {\n const total = this.TotalPages;\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n\n const pages: number[] = [1];\n const start = Math.max(2, this.CurrentPage - 2);\n const end = Math.min(total - 1, this.CurrentPage + 2);\n if (start > 2) pages.push(-1); // ellipsis marker\n for (let i = start; i <= end; i++) pages.push(i);\n if (end < total - 1) pages.push(-1); // ellipsis marker\n pages.push(total);\n return pages;\n }\n\n /** Navigate to a specific page */\n public GoToPage(page: number): void {\n if (page < 1 || page > this.TotalPages) return;\n this.CurrentPage = page;\n this.cdr.detectChanges();\n }\n\n // ── Grouped mode ──\n\n /** Toggle group expansion */\n public ToggleGroup(sourceType: string): void {\n if (this.ExpandedGroups.has(sourceType)) {\n this.ExpandedGroups.delete(sourceType);\n } else {\n this.ExpandedGroups.add(sourceType);\n }\n this.cdr.detectChanges();\n }\n\n /** Whether a group is expanded */\n public IsGroupExpanded(sourceType: string): boolean {\n return this.ExpandedGroups.has(sourceType);\n }\n\n /** Get visible results for a group (respecting max per group unless expanded) */\n public GetVisibleResults(group: SearchResultGroup): SearchResultItem[] {\n if (this.IsGroupExpanded(group.SourceType)) {\n return group.Results;\n }\n return group.Results.slice(0, this.MaxPerGroup);\n }\n\n /** Whether a group has more results than shown */\n public HasMoreResults(group: SearchResultGroup): boolean {\n return group.Results.length > this.MaxPerGroup && !this.IsGroupExpanded(group.SourceType);\n }\n\n /** Get the count of hidden results */\n public GetHiddenCount(group: SearchResultGroup): number {\n return group.Results.length - this.MaxPerGroup;\n }\n\n /** Handle clicking a result */\n public OnResultClick(result: SearchResultItem, event: MouseEvent): void {\n this.ResultSelected.emit({\n Result: result,\n OpenInNewTab: event.metaKey || event.ctrlKey\n });\n }\n\n /** Toggle expand/collapse of a result card — multiple can be open */\n public ToggleExpand(result: SearchResultItem, event?: MouseEvent): void {\n if (event) event.stopPropagation();\n if (this.ExpandedResultIDs.has(result.ID)) {\n this.ExpandedResultIDs.delete(result.ID);\n } else {\n this.ExpandedResultIDs.add(result.ID);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a result card is expanded */\n public IsExpanded(resultID: string): boolean {\n return this.ExpandedResultIDs.has(resultID);\n }\n\n /** Handle \"Open Record\" button click */\n public OnOpenRecord(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.OpenRecordRequested.emit(result);\n }\n\n /** Handle \"See Similar Items\" button click — emits the result for a \"more like this\" search */\n public OnMoreLikeThis(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.MoreLikeThisRequested.emit(result);\n }\n\n /**\n * Wrap substrings matching HighlightText in <mark> tags for visual emphasis.\n * The input is regex-escaped so user text is treated as a literal string.\n */\n public HighlightMatch(text: string): string {\n return HighlightSearchMatches(text, this.HighlightText, 'search-highlight');\n }\n\n /** Format a score as a percentage */\n public FormatScore(score: number): string {\n return `${Math.round(score * 100)}%`;\n }\n\n /** Format elapsed time */\n public FormatTime(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n }\n\n /** Resolved link cache per result ID */\n private resolvedLinksCache = new Map<string, { Value: string; Label: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Resolve URL and Email fields from the result's RawMetadata using entity ExtendedType metadata.\n * Returns clickable link entries with proper type classification.\n */\n public GetResolvedLinks(result: SearchResultItem): { Value: string; Label: string; Type: 'URL' | 'Email' }[] {\n if (this.resolvedLinksCache.has(result.ID)) {\n return this.resolvedLinksCache.get(result.ID)!;\n }\n\n const links: { Value: string; Label: string; Type: 'URL' | 'Email' }[] = [];\n\n if (result.RawMetadata) {\n try {\n const meta = JSON.parse(result.RawMetadata) as Record<string, unknown>;\n const linkFields = this.GetLinkFields(result.EntityName);\n\n for (const field of linkFields) {\n const value = meta[field.Name];\n if (value && typeof value === 'string' && value.trim().length > 0) {\n links.push({\n Value: value,\n Label: field.DisplayName,\n Type: field.Type\n });\n }\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Fallback: extract URLs from snippet if no ExtendedType fields found\n if (links.length === 0) {\n const urls = this.ExtractUrls(result.Snippet);\n for (const url of urls) {\n links.push({ Value: url, Label: '', Type: 'URL' });\n }\n }\n\n this.resolvedLinksCache.set(result.ID, links);\n return links;\n }\n\n /** Extract URLs from snippet text */\n public ExtractUrls(snippet: string): string[] {\n if (!snippet) return [];\n const urlRegex = /https?:\\/\\/[^\\s,;)>\"']+/g;\n const matches = snippet.match(urlRegex);\n return matches ? [...new Set(matches)] : [];\n }\n\n /** Truncate a URL for display */\n public TruncateUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.length > 30\n ? parsed.pathname.substring(0, 30) + '...'\n : parsed.pathname;\n return parsed.hostname + path;\n } catch {\n return url.length > 50 ? url.substring(0, 50) + '...' : url;\n }\n }\n\n /** Cache of URL/Email field info per entity name */\n private linkFieldsCache = new Map<string, { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Get URL and Email fields for an entity using ExtendedType from entity metadata.\n * Returns fields with ExtendedType='URL' or ExtendedType='Email'.\n */\n public GetLinkFields(entityName: string): { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[] {\n if (this.linkFieldsCache.has(entityName)) {\n return this.linkFieldsCache.get(entityName)!;\n }\n\n try {\n const md = this.ProviderToUse;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n\n const linkFields = entityInfo.Fields\n .filter(f => f.ExtendedType === 'URL' || f.ExtendedType === 'Email')\n .map(f => ({\n Name: f.Name,\n DisplayName: f.DisplayNameOrName,\n Type: f.ExtendedType as 'URL' | 'Email'\n }));\n\n this.linkFieldsCache.set(entityName, linkFields);\n return linkFields;\n } catch {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n }\n}\n","@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- ══════ FLAT BLENDED LIST (sorted by score) ══════ -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- ══════ GROUPED BY SOURCE TYPE ══════ -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- ═══ Shared result card template ═══ -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityDisplayName || result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityDisplayName || result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n"]}
@@ -83,14 +83,22 @@ export declare class SearchSuggestComponent implements OnInit {
83
83
  handling knows when the highlight has walked off the end of the list. */
84
84
  get NavigableItemCount(): number;
85
85
  /** Whether to show recent searches section */
86
+ get ShowRecentSection(): boolean;
87
+ /** @deprecated Use {@link ShowRecentSection}. */
86
88
  get showRecentSection(): boolean;
87
89
  /** Whether to show preview results section */
90
+ get ShowPreviewSection(): boolean;
91
+ /** @deprecated Use {@link ShowPreviewSection}. */
88
92
  get showPreviewSection(): boolean;
89
93
  /** Whether to show the recently-opened-records section (idle state only) */
94
+ get ShowRecentRecordsSection(): boolean;
95
+ /** @deprecated Use {@link ShowRecentRecordsSection}. */
90
96
  get showRecentRecordsSection(): boolean;
91
97
  /** Visible recently opened records (capped at 3) */
92
98
  get VisibleRecentRecords(): RecentRecordItem[];
93
99
  /** Whether to show the "See all N results" footer */
100
+ get ShowSeeAllFooter(): boolean;
101
+ /** @deprecated Use {@link ShowSeeAllFooter}. */
94
102
  get showSeeAllFooter(): boolean;
95
103
  /** Visible recent searches (capped at 5) */
96
104
  get VisibleRecentSearches(): RecentSearch[];
@@ -1 +1 @@
1
- {"version":3,"file":"search-suggest.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-suggest.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-suggest.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-suggest.component.ts"],"names":[],"mappings":"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"}
@@ -78,7 +78,7 @@ function SearchSuggestComponent_Conditional_0_Conditional_3_For_6_Template(rf, c
78
78
  i0.ɵɵadvance(3);
79
79
  i0.ɵɵtextInterpolate(record_r7.RecordName);
80
80
  i0.ɵɵadvance(2);
81
- i0.ɵɵtextInterpolate(record_r7.EntityName);
81
+ i0.ɵɵtextInterpolate(record_r7.EntityDisplayName || record_r7.EntityName);
82
82
  } }
83
83
  function SearchSuggestComponent_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
84
84
  i0.ɵɵelementStart(0, "div", 2)(1, "div", 7);
@@ -119,7 +119,7 @@ function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_0_For_2_
119
119
  i0.ɵɵadvance(3);
120
120
  i0.ɵɵtextInterpolate(result_r10.RecordName || result_r10.Title);
121
121
  i0.ɵɵadvance(2);
122
- i0.ɵɵtextInterpolate(result_r10.EntityName);
122
+ i0.ɵɵtextInterpolate(result_r10.EntityDisplayName || result_r10.EntityName);
123
123
  i0.ɵɵadvance();
124
124
  i0.ɵɵclassMap("suggest-result-score " + ctx_r1.GetScoreClass(result_r10.Score));
125
125
  i0.ɵɵadvance();
@@ -380,7 +380,7 @@ export class SearchSuggestComponent {
380
380
  SelectHighlighted() {
381
381
  if (this.HighlightedIndex < 0)
382
382
  return;
383
- if (this.showRecentSection) {
383
+ if (this.ShowRecentSection) {
384
384
  // Navigating recent searches
385
385
  const visibleRecent = this.VisibleRecentSearches;
386
386
  if (this.HighlightedIndex < visibleRecent.length) {
@@ -388,9 +388,9 @@ export class SearchSuggestComponent {
388
388
  return;
389
389
  }
390
390
  }
391
- if (this.showRecentRecordsSection) {
391
+ if (this.ShowRecentRecordsSection) {
392
392
  // Navigating recently opened records (indexes continue after recents)
393
- const offset = this.showRecentSection ? this.VisibleRecentSearches.length : 0;
393
+ const offset = this.ShowRecentSection ? this.VisibleRecentSearches.length : 0;
394
394
  const recordIndex = this.HighlightedIndex - offset;
395
395
  const visibleRecords = this.VisibleRecentRecords;
396
396
  if (recordIndex >= 0 && recordIndex < visibleRecords.length) {
@@ -398,9 +398,9 @@ export class SearchSuggestComponent {
398
398
  return;
399
399
  }
400
400
  }
401
- if (this.showPreviewSection) {
401
+ if (this.ShowPreviewSection) {
402
402
  // Navigating preview results
403
- const offset = this.showRecentSection ? this.VisibleRecentSearches.length : 0;
403
+ const offset = this.ShowRecentSection ? this.VisibleRecentSearches.length : 0;
404
404
  const resultIndex = this.HighlightedIndex - offset;
405
405
  const visibleResults = this.VisiblePreviewResults;
406
406
  if (resultIndex >= 0 && resultIndex < visibleResults.length) {
@@ -408,7 +408,7 @@ export class SearchSuggestComponent {
408
408
  return;
409
409
  }
410
410
  // "See all" footer
411
- if (resultIndex === visibleResults.length && this.showSeeAllFooter) {
411
+ if (resultIndex === visibleResults.length && this.ShowSeeAllFooter) {
412
412
  this.OnSeeAllClick();
413
413
  }
414
414
  }
@@ -425,24 +425,40 @@ export class SearchSuggestComponent {
425
425
  }
426
426
  // --- Template getters ---
427
427
  /** Whether to show recent searches section */
428
- get showRecentSection() {
428
+ get ShowRecentSection() {
429
429
  return this.ShowRecent && this.Query.length < this.MinQueryLength && this.RecentSearches.length > 0;
430
430
  }
431
+ /** @deprecated Use {@link ShowRecentSection}. */
432
+ get showRecentSection() {
433
+ return this.ShowRecentSection;
434
+ }
431
435
  /** Whether to show preview results section */
432
- get showPreviewSection() {
436
+ get ShowPreviewSection() {
433
437
  return this.Query.length >= this.MinQueryLength;
434
438
  }
439
+ /** @deprecated Use {@link ShowPreviewSection}. */
440
+ get showPreviewSection() {
441
+ return this.ShowPreviewSection;
442
+ }
435
443
  /** Whether to show the recently-opened-records section (idle state only) */
436
- get showRecentRecordsSection() {
444
+ get ShowRecentRecordsSection() {
437
445
  return this.Query.length < this.MinQueryLength && this.RecentRecords.length > 0;
438
446
  }
447
+ /** @deprecated Use {@link ShowRecentRecordsSection}. */
448
+ get showRecentRecordsSection() {
449
+ return this.ShowRecentRecordsSection;
450
+ }
439
451
  /** Visible recently opened records (capped at 3) */
440
452
  get VisibleRecentRecords() {
441
453
  return this.RecentRecords.slice(0, 3);
442
454
  }
443
455
  /** Whether to show the "See all N results" footer */
456
+ get ShowSeeAllFooter() {
457
+ return this.ShowPreviewSection && this.TotalCount > 0;
458
+ }
459
+ /** @deprecated Use {@link ShowSeeAllFooter}. */
444
460
  get showSeeAllFooter() {
445
- return this.showPreviewSection && this.TotalCount > 0;
461
+ return this.ShowSeeAllFooter;
446
462
  }
447
463
  /** Visible recent searches (capped at 5) */
448
464
  get VisibleRecentSearches() {
@@ -531,15 +547,15 @@ export class SearchSuggestComponent {
531
547
  // --- Private ---
532
548
  getNavigableItemCount() {
533
549
  let count = 0;
534
- if (this.showRecentSection) {
550
+ if (this.ShowRecentSection) {
535
551
  count += this.VisibleRecentSearches.length;
536
552
  }
537
- if (this.showRecentRecordsSection) {
553
+ if (this.ShowRecentRecordsSection) {
538
554
  count += this.VisibleRecentRecords.length;
539
555
  }
540
- if (this.showPreviewSection) {
556
+ if (this.ShowPreviewSection) {
541
557
  count += this.VisiblePreviewResults.length;
542
- if (this.showSeeAllFooter) {
558
+ if (this.ShowSeeAllFooter) {
543
559
  count += 1; // "See all" item
544
560
  }
545
561
  }
@@ -586,7 +602,7 @@ export class SearchSuggestComponent {
586
602
  }
587
603
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchSuggestComponent, [{
588
604
  type: Component,
589
- args: [{ standalone: false, selector: 'mj-search-suggest', template: "@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <button type=\"button\" class=\"suggest-clear-link\" aria-label=\"Clear recent searches\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnClearRecentClick($event)\">Clear</button>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Recently opened records section (idle state) -->\n @if (showRecentRecordsSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock-rotate-left suggest-section-icon\"></i>\n <span>Recently Opened</span>\n </div>\n @for (record of VisibleRecentRecords; track record.EntityName + record.RecordID; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnRecentRecordClick(record); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <i class=\"fa-solid fa-file-lines suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ record.RecordName }}</span>\n <span class=\"suggest-item-meta\">{{ record.EntityName }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recents of either kind and query too short -->\n @if (!showRecentSection && !showRecentRecordsSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n\n <!-- Host-supplied promo row (e.g. command palette nudge) -->\n @if (ShowPromo && PromoText) {\n <div class=\"suggest-promo\">\n <i class=\"fa-solid fa-wand-magic-sparkles suggest-promo-icon\"></i>\n <span class=\"suggest-promo-text\">{{ PromoText }}</span>\n <button type=\"button\" class=\"suggest-promo-action\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoAccept()\">{{ PromoActionLabel }}</button>\n <button type=\"button\" class=\"suggest-promo-dismiss\" aria-label=\"Dismiss\" title=\"Dismiss\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoDismiss(); $event.stopPropagation()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n </div>\n}\n", styles: ["/* Search Suggest Dropdown */\n\n.suggest-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: suggestSlideIn 0.15s ease;\n}\n\n@keyframes suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Sections */\n.suggest-section {\n padding: 0.25rem 0;\n}\n\n.suggest-section + .suggest-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link {\n margin-left: auto;\n padding: 0;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: 0.68rem;\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link:hover {\n text-decoration: underline;\n}\n\n.suggest-clear-link:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n border-radius: var(--mj-radius-sm);\n}\n\n/* Items */\n.suggest-item {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item:hover,\n.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n/* Result items */\n.suggest-result-item {\n gap: 0.65rem;\n}\n\n.suggest-result-icon {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* Score badge \u2014 base styles */\n.suggest-result-score {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n /* Default: neutral gray */\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 high relevance (green, \u226540%) */\n.suggest-score-high {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 medium relevance (amber, 15\u201339%) */\n.suggest-score-medium {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 low relevance (gray, <15%) */\n.suggest-score-low {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Loading */\n.suggest-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.suggest-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* See all footer */\n.suggest-see-all {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all:hover,\n.suggest-see-all.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon {\n font-size: 0.72rem;\n}\n\n/* --- Min Relevance Filter Bar --- */\n.suggest-filter-bar {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label {\n flex: 1;\n}\n\n.suggest-filter-chevron {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open {\n transform: rotate(180deg);\n}\n\n/* Filter popover */\n.suggest-filter-popover {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}\n\n/* Host-supplied promo row \u2014 a quiet whisper pinned to the dropdown's bottom\n edge, never a banner. */\n.suggest-promo {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.suggest-promo-icon {\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.suggest-promo-text {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-promo-action {\n flex-shrink: 0;\n padding: 0.2rem 0.6rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n font-weight: var(--mj-font-medium);\n cursor: pointer;\n transition: var(--mj-transition-colors);\n}\n\n.suggest-promo-action:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.suggest-promo-dismiss {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 0.75rem;\n cursor: pointer;\n transition: var(--mj-transition-colors);\n flex-shrink: 0;\n}\n\n.suggest-promo-dismiss:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n"] }]
605
+ args: [{ standalone: false, selector: 'mj-search-suggest', template: "@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <button type=\"button\" class=\"suggest-clear-link\" aria-label=\"Clear recent searches\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnClearRecentClick($event)\">Clear</button>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Recently opened records section (idle state) -->\n @if (showRecentRecordsSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock-rotate-left suggest-section-icon\"></i>\n <span>Recently Opened</span>\n </div>\n @for (record of VisibleRecentRecords; track record.EntityName + record.RecordID; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnRecentRecordClick(record); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <i class=\"fa-solid fa-file-lines suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ record.RecordName }}</span>\n <span class=\"suggest-item-meta\">{{ record.EntityDisplayName || record.EntityName }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityDisplayName || result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recents of either kind and query too short -->\n @if (!showRecentSection && !showRecentRecordsSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n\n <!-- Host-supplied promo row (e.g. command palette nudge) -->\n @if (ShowPromo && PromoText) {\n <div class=\"suggest-promo\">\n <i class=\"fa-solid fa-wand-magic-sparkles suggest-promo-icon\"></i>\n <span class=\"suggest-promo-text\">{{ PromoText }}</span>\n <button type=\"button\" class=\"suggest-promo-action\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoAccept()\">{{ PromoActionLabel }}</button>\n <button type=\"button\" class=\"suggest-promo-dismiss\" aria-label=\"Dismiss\" title=\"Dismiss\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoDismiss(); $event.stopPropagation()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n </div>\n}\n", styles: ["/* Search Suggest Dropdown */\n\n.suggest-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: suggestSlideIn 0.15s ease;\n}\n\n@keyframes suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Sections */\n.suggest-section {\n padding: 0.25rem 0;\n}\n\n.suggest-section + .suggest-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link {\n margin-left: auto;\n padding: 0;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: 0.68rem;\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link:hover {\n text-decoration: underline;\n}\n\n.suggest-clear-link:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n border-radius: var(--mj-radius-sm);\n}\n\n/* Items */\n.suggest-item {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item:hover,\n.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n/* Result items */\n.suggest-result-item {\n gap: 0.65rem;\n}\n\n.suggest-result-icon {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* Score badge \u2014 base styles */\n.suggest-result-score {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n /* Default: neutral gray */\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 high relevance (green, \u226540%) */\n.suggest-score-high {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 medium relevance (amber, 15\u201339%) */\n.suggest-score-medium {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 low relevance (gray, <15%) */\n.suggest-score-low {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Loading */\n.suggest-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.suggest-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* See all footer */\n.suggest-see-all {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all:hover,\n.suggest-see-all.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon {\n font-size: 0.72rem;\n}\n\n/* --- Min Relevance Filter Bar --- */\n.suggest-filter-bar {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label {\n flex: 1;\n}\n\n.suggest-filter-chevron {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open {\n transform: rotate(180deg);\n}\n\n/* Filter popover */\n.suggest-filter-popover {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}\n\n/* Host-supplied promo row \u2014 a quiet whisper pinned to the dropdown's bottom\n edge, never a banner. */\n.suggest-promo {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.suggest-promo-icon {\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.suggest-promo-text {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-promo-action {\n flex-shrink: 0;\n padding: 0.2rem 0.6rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n font-weight: var(--mj-font-medium);\n cursor: pointer;\n transition: var(--mj-transition-colors);\n}\n\n.suggest-promo-action:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.suggest-promo-dismiss {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 0.75rem;\n cursor: pointer;\n transition: var(--mj-transition-colors);\n flex-shrink: 0;\n}\n\n.suggest-promo-dismiss:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n"] }]
590
606
  }], null, { IsOpen: [{
591
607
  type: Input
592
608
  }], PreviewResults: [{