@memberjunction/ng-artifacts 5.43.0 → 5.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +6 -0
  2. package/dist/lib/artifacts.module.d.ts +28 -25
  3. package/dist/lib/artifacts.module.d.ts.map +1 -1
  4. package/dist/lib/artifacts.module.js +22 -2
  5. package/dist/lib/artifacts.module.js.map +1 -1
  6. package/dist/lib/components/artifact-version-history.component.d.ts +1 -0
  7. package/dist/lib/components/artifact-version-history.component.d.ts.map +1 -1
  8. package/dist/lib/components/artifact-version-history.component.js +30 -31
  9. package/dist/lib/components/artifact-version-history.component.js.map +1 -1
  10. package/dist/lib/components/artifact-viewer-panel.component.d.ts +3 -0
  11. package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
  12. package/dist/lib/components/artifact-viewer-panel.component.js +198 -112
  13. package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
  14. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts +11 -0
  15. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts.map +1 -1
  16. package/dist/lib/components/plugins/audio-artifact-viewer.component.js +37 -12
  17. package/dist/lib/components/plugins/audio-artifact-viewer.component.js.map +1 -1
  18. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.d.ts.map +1 -1
  19. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js +40 -51
  20. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js.map +1 -1
  21. package/dist/lib/components/plugins/data-artifact-viewer.component.js +27 -47
  22. package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
  23. package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js +89 -95
  24. package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js.map +1 -1
  25. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts +227 -0
  26. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts.map +1 -0
  27. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js +630 -0
  28. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js.map +1 -0
  29. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts +11 -0
  30. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts.map +1 -1
  31. package/dist/lib/components/plugins/video-artifact-viewer.component.js +37 -12
  32. package/dist/lib/components/plugins/video-artifact-viewer.component.js.map +1 -1
  33. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts +18 -4
  34. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts.map +1 -1
  35. package/dist/lib/components/previews/audio-artifact-preview.component.js +54 -21
  36. package/dist/lib/components/previews/audio-artifact-preview.component.js.map +1 -1
  37. package/dist/lib/components/previews/video-artifact-preview.component.d.ts +19 -5
  38. package/dist/lib/components/previews/video-artifact-preview.component.d.ts.map +1 -1
  39. package/dist/lib/components/previews/video-artifact-preview.component.js +54 -22
  40. package/dist/lib/components/previews/video-artifact-preview.component.js.map +1 -1
  41. package/dist/public-api.d.ts +1 -0
  42. package/dist/public-api.d.ts.map +1 -1
  43. package/dist/public-api.js +1 -0
  44. package/dist/public-api.js.map +1 -1
  45. package/package.json +21 -18
  46. package/dist/__tests__/exports.test.d.ts +0 -2
  47. package/dist/__tests__/exports.test.d.ts.map +0 -1
  48. package/dist/__tests__/exports.test.js +0 -17
  49. package/dist/__tests__/exports.test.js.map +0 -1
  50. package/dist/__tests__/image-artifact-viewer.test.d.ts +0 -2
  51. package/dist/__tests__/image-artifact-viewer.test.d.ts.map +0 -1
  52. package/dist/__tests__/image-artifact-viewer.test.js +0 -62
  53. package/dist/__tests__/image-artifact-viewer.test.js.map +0 -1
  54. package/dist/__tests__/index.test.d.ts +0 -2
  55. package/dist/__tests__/index.test.d.ts.map +0 -1
  56. package/dist/__tests__/index.test.js +0 -7
  57. package/dist/__tests__/index.test.js.map +0 -1
  58. package/dist/lib/__tests__/artifact-snapshots.test.d.ts +0 -2
  59. package/dist/lib/__tests__/artifact-snapshots.test.d.ts.map +0 -1
  60. package/dist/lib/__tests__/artifact-snapshots.test.js +0 -155
  61. package/dist/lib/__tests__/artifact-snapshots.test.js.map +0 -1
  62. package/dist/lib/__tests__/interactive-form-apply.service.test.d.ts +0 -2
  63. package/dist/lib/__tests__/interactive-form-apply.service.test.d.ts.map +0 -1
  64. package/dist/lib/__tests__/interactive-form-apply.service.test.js +0 -211
  65. package/dist/lib/__tests__/interactive-form-apply.service.test.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"component-feedback-panel.component.js","sourceRoot":"","sources":["../../../../../src/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.ts","../../../../../src/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnDestroy, ChangeDetectorRef, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { Metadata } from '@memberjunction/core';\nimport { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { MarkdownModule } from '@memberjunction/ng-markdown';\n\n/**\n * Flattened tree item for rendering the component hierarchy\n */\ninterface TreeItem {\n spec: ComponentSpec;\n depth: number;\n hasChildren: boolean;\n}\n\n/**\n * Component feedback panel that displays component hierarchy tree\n * and allows users to select components and provide star ratings with comments.\n * Registry-agnostic: works with any component registry (Skip, MJ Central, etc.)\n */\n@Component({\n selector: 'mj-component-feedback-panel',\n standalone: true,\n imports: [CommonModule, FormsModule, MarkdownModule],\n templateUrl: './component-feedback-panel.component.html',\n styleUrls: ['./component-feedback-panel.component.css']\n})\nexport class ComponentFeedbackPanelComponent extends BaseAngularComponent implements OnDestroy {\n @Input() ComponentSpec: ComponentSpec | null = null;\n @Input() ReactContainerElement: HTMLElement | null = null;\n @Input() ConversationId: string | null = null;\n @Input() ConversationDetailId: string | null = null;\n\n @Output() Closed = new EventEmitter<void>();\n\n // Feedback form state\n public SelectedSpec: ComponentSpec | null = null;\n public StarRating = 0;\n public HoverRating = 0;\n public FeedbackComments = '';\n public IsSubmitting = false;\n public SubmitSuccess = false;\n public SubmitError = '';\n\n // Tree state\n public ExpandedNodes = new Set<string>();\n\n // Highlight overlays\n private highlightOverlay: HTMLElement | null = null;\n private hoverOverlay: HTMLElement | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private containerClickHandler: ((e: MouseEvent) => void) | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n\n // --- Tree Methods ---\n\n GetTreeItems(): TreeItem[] {\n if (!this.ComponentSpec) return [];\n if (!this.ExpandedNodes.has(this.ComponentSpec.name)) {\n this.ExpandedNodes.add(this.ComponentSpec.name);\n }\n return this.buildTree(this.ComponentSpec, 0);\n }\n\n private buildTree(spec: ComponentSpec, depth: number): TreeItem[] {\n const items: TreeItem[] = [{\n spec,\n depth,\n hasChildren: !!(spec.dependencies && spec.dependencies.length > 0)\n }];\n if (this.ExpandedNodes.has(spec.name) && spec.dependencies) {\n for (const child of spec.dependencies) {\n items.push(...this.buildTree(child, depth + 1));\n }\n }\n return items;\n }\n\n ToggleNode(spec: ComponentSpec, event: MouseEvent): void {\n event.stopPropagation();\n if (this.ExpandedNodes.has(spec.name)) {\n this.ExpandedNodes.delete(spec.name);\n } else {\n this.ExpandedNodes.add(spec.name);\n }\n }\n\n IsNodeExpanded(spec: ComponentSpec): boolean {\n return this.ExpandedNodes.has(spec.name);\n }\n\n SelectComponent(spec: ComponentSpec): void {\n this.SelectedSpec = spec;\n this.ResetForm();\n this.HighlightComponent(spec.name);\n }\n\n IsSelected(spec: ComponentSpec): boolean {\n return this.SelectedSpec?.name === spec.name;\n }\n\n GetIndentation(depth: number): string {\n return `${depth * 20}px`;\n }\n\n // --- Star Rating (supports half-star increments) ---\n\n SetRating(value: number): void {\n this.StarRating = value;\n }\n\n SetHoverRating(value: number): void {\n this.HoverRating = value;\n }\n\n ClearHoverRating(): void {\n this.HoverRating = 0;\n }\n\n GetDisplayRating(): number {\n return this.HoverRating || this.StarRating;\n }\n\n /** Returns 'full' | 'half' | 'empty' for the given 1-based star index */\n GetStarState(index: number): 'full' | 'half' | 'empty' {\n const rating = this.GetDisplayRating();\n if (index <= rating) return 'full';\n if (index - 0.5 <= rating) return 'half';\n return 'empty';\n }\n\n IsStarFilled(index: number): boolean {\n return index <= this.GetDisplayRating();\n }\n\n // --- Form ---\n\n ResetForm(): void {\n this.StarRating = 0;\n this.HoverRating = 0;\n this.FeedbackComments = '';\n this.SubmitSuccess = false;\n this.SubmitError = '';\n }\n\n CanSubmit(): boolean {\n return !!this.SelectedSpec && this.StarRating > 0 && !this.IsSubmitting;\n }\n\n async SubmitFeedback(): Promise<void> {\n if (!this.CanSubmit() || !this.SelectedSpec) return;\n\n this.IsSubmitting = true;\n this.SubmitError = '';\n this.SubmitSuccess = false;\n this.cdr.detectChanges();\n\n try {\n // Dynamic import to avoid adding graphql-dataprovider as a package dependency.\n // At runtime in the browser, this.ProviderToUse is always a GraphQLDataProvider.\n const { GraphQLComponentRegistryClient } = await import('@memberjunction/graphql-dataprovider');\n const provider = this.ProviderToUse;\n const client = new GraphQLComponentRegistryClient(provider as ConstructorParameters<typeof GraphQLComponentRegistryClient>[0]);\n\n const response = await client.SendComponentFeedback({\n componentName: this.SelectedSpec.name,\n componentNamespace: this.SelectedSpec.namespace || this.SelectedSpec.registry || '',\n componentVersion: this.SelectedSpec.version,\n registryName: this.SelectedSpec.registry,\n rating: this.StarRating * 20, // 0-5 -> 0-100\n comments: this.FeedbackComments.trim() || undefined,\n conversationID: this.ConversationId || undefined,\n conversationDetailID: this.ConversationDetailId || undefined\n });\n\n if (response.success) {\n this.SubmitSuccess = true;\n setTimeout(() => {\n this.ResetForm();\n this.cdr.detectChanges();\n }, 2000);\n } else {\n this.SubmitError = response.error || 'Failed to submit feedback.';\n }\n } catch (error) {\n this.SubmitError = 'Failed to submit feedback. Please try again.';\n console.error('Error submitting component feedback:', error);\n } finally {\n this.IsSubmitting = false;\n this.cdr.detectChanges();\n }\n }\n\n // --- Hover Highlight (light preview on tree hover) ---\n\n HoverTreeItem(spec: ComponentSpec): void {\n if (!this.ReactContainerElement || this.IsSelected(spec)) return;\n this.showHoverOverlay(spec.name);\n }\n\n ClearTreeItemHover(): void {\n this.removeHoverOverlay();\n }\n\n private showHoverOverlay(componentName: string): void {\n if (!this.ReactContainerElement) return;\n\n const targetEl = this.ReactContainerElement.querySelector(\n `[data-mj-component=\"${componentName}\"]`\n );\n if (!targetEl) {\n this.removeHoverOverlay();\n return;\n }\n\n this.ensureContainerPositioned();\n\n if (!this.hoverOverlay) {\n this.hoverOverlay = document.createElement('div');\n this.ReactContainerElement.appendChild(this.hoverOverlay);\n }\n\n this.positionOverlay(this.hoverOverlay, targetEl);\n this.hoverOverlay.style.border = '2px dashed var(--mj-brand-primary, #3B82F6)';\n this.hoverOverlay.style.background = 'color-mix(in srgb, var(--mj-brand-primary, #3B82F6) 5%, transparent)';\n }\n\n private removeHoverOverlay(): void {\n if (this.hoverOverlay?.parentNode) {\n this.hoverOverlay.parentNode.removeChild(this.hoverOverlay);\n }\n this.hoverOverlay = null;\n }\n\n // --- Region Highlighting (solid selection) ---\n\n private HighlightComponent(componentName: string): void {\n if (!this.ReactContainerElement) {\n this.ClearHighlight();\n return;\n }\n\n const targetEl = this.ReactContainerElement.querySelector(\n `[data-mj-component=\"${componentName}\"]`\n );\n if (!targetEl) {\n this.ClearHighlight();\n return;\n }\n\n this.ensureContainerPositioned();\n\n if (!this.highlightOverlay) {\n this.highlightOverlay = document.createElement('div');\n this.ReactContainerElement.appendChild(this.highlightOverlay);\n }\n\n this.positionOverlay(this.highlightOverlay, targetEl);\n this.highlightOverlay.style.border = '2px solid var(--mj-brand-primary, #3B82F6)';\n this.highlightOverlay.style.background = 'color-mix(in srgb, var(--mj-brand-primary, #3B82F6) 10%, transparent)';\n\n // Watch for resize/layout changes\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => {\n if (this.highlightOverlay) {\n this.positionOverlay(this.highlightOverlay, targetEl);\n }\n });\n this.resizeObserver.observe(targetEl);\n this.resizeObserver.observe(this.ReactContainerElement);\n\n // Set up bidirectional click selection on container\n this.installContainerClickListener();\n }\n\n private ensureContainerPositioned(): void {\n if (!this.ReactContainerElement) return;\n const containerStyle = getComputedStyle(this.ReactContainerElement);\n if (containerStyle.position === 'static') {\n this.ReactContainerElement.style.position = 'relative';\n }\n }\n\n /**\n * Gets the effective bounding rect for a component marker element.\n * display:contents elements return zero-size rects, so we compute\n * the union bounding box of their children instead.\n */\n private getEffectiveRect(el: Element): DOMRect {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) return rect;\n\n // display:contents wrapper — union the bounding rects of all children\n const children = el.children;\n if (children.length === 0) return rect;\n\n let top = Infinity, left = Infinity, bottom = -Infinity, right = -Infinity;\n for (let i = 0; i < children.length; i++) {\n const childRect = children[i].getBoundingClientRect();\n if (childRect.width === 0 && childRect.height === 0) continue;\n top = Math.min(top, childRect.top);\n left = Math.min(left, childRect.left);\n bottom = Math.max(bottom, childRect.bottom);\n right = Math.max(right, childRect.right);\n }\n\n if (top === Infinity) return rect; // no visible children\n return new DOMRect(left, top, right - left, bottom - top);\n }\n\n private positionOverlay(overlay: HTMLElement, targetEl: Element): void {\n if (!this.ReactContainerElement) return;\n\n const targetRect = this.getEffectiveRect(targetEl);\n const containerRect = this.ReactContainerElement.getBoundingClientRect();\n\n overlay.style.position = 'absolute';\n overlay.style.top = `${targetRect.top - containerRect.top + this.ReactContainerElement.scrollTop}px`;\n overlay.style.left = `${targetRect.left - containerRect.left + this.ReactContainerElement.scrollLeft}px`;\n overlay.style.width = `${targetRect.width}px`;\n overlay.style.height = `${targetRect.height}px`;\n overlay.style.pointerEvents = 'none';\n overlay.style.zIndex = '10';\n overlay.style.borderRadius = '4px';\n overlay.style.transition = 'all 0.2s ease';\n }\n\n // --- Bidirectional click: clicking a component region selects it in the tree ---\n\n private installContainerClickListener(): void {\n if (this.containerClickHandler || !this.ReactContainerElement) return;\n\n this.containerClickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up from click target to find nearest [data-mj-component]\n const componentEl = target.closest('[data-mj-component]');\n if (!componentEl) return;\n\n const componentName = componentEl.getAttribute('data-mj-component');\n if (!componentName) return;\n\n // Find matching spec in the tree\n const matchingSpec = this.findSpecByName(componentName);\n if (matchingSpec && matchingSpec.name !== this.SelectedSpec?.name) {\n this.SelectComponent(matchingSpec);\n this.cdr.detectChanges();\n }\n };\n\n // Use capture phase so we observe the click without interfering with component handlers\n this.ReactContainerElement.addEventListener('click', this.containerClickHandler, true);\n }\n\n private removeContainerClickListener(): void {\n if (this.containerClickHandler && this.ReactContainerElement) {\n this.ReactContainerElement.removeEventListener('click', this.containerClickHandler, true);\n }\n this.containerClickHandler = null;\n }\n\n private findSpecByName(name: string): ComponentSpec | null {\n if (!this.ComponentSpec) return null;\n return this.walkSpecTree(this.ComponentSpec, name);\n }\n\n private walkSpecTree(spec: ComponentSpec, name: string): ComponentSpec | null {\n if (spec.name === name) return spec;\n if (spec.dependencies) {\n for (const child of spec.dependencies) {\n const found = this.walkSpecTree(child, name);\n if (found) return found;\n }\n }\n return null;\n }\n\n private ClearHighlight(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n if (this.highlightOverlay?.parentNode) {\n this.highlightOverlay.parentNode.removeChild(this.highlightOverlay);\n }\n this.highlightOverlay = null;\n this.removeHoverOverlay();\n this.removeContainerClickListener();\n }\n\n ClosePanel(): void {\n this.ClearHighlight();\n this.Closed.emit();\n }\n\n ngOnDestroy(): void {\n this.ClearHighlight();\n }\n}\n","<div class=\"feedback-panel-overlay\" (click)=\"ClosePanel()\">\n <div class=\"feedback-panel\" (click)=\"$event.stopPropagation()\">\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-title\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>Inspect & Rate</span>\n </div>\n <button class=\"close-button\" (click)=\"ClosePanel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"panel-content\">\n <!-- Component Tree Section -->\n <div class=\"tree-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-sitemap\"></i>\n <span>Select Component</span>\n </div>\n\n @if (ComponentSpec) {\n <div class=\"component-tree\">\n @for (item of GetTreeItems(); track item.spec.name) {\n <div\n class=\"tree-item\"\n [class.selected]=\"IsSelected(item.spec)\"\n [style.padding-left]=\"GetIndentation(item.depth)\"\n (click)=\"SelectComponent(item.spec)\"\n (mouseenter)=\"HoverTreeItem(item.spec)\"\n (mouseleave)=\"ClearTreeItemHover()\">\n\n @if (item.hasChildren) {\n <i\n class=\"tree-toggle fa-solid\"\n [class.fa-chevron-right]=\"!IsNodeExpanded(item.spec)\"\n [class.fa-chevron-down]=\"IsNodeExpanded(item.spec)\"\n (click)=\"ToggleNode(item.spec, $event)\">\n </i>\n } @else {\n <span class=\"tree-spacer\"></span>\n }\n\n <i class=\"fa-solid fa-cube component-icon\"></i>\n\n <div class=\"component-info\">\n <span class=\"component-name\">{{ item.spec.title || item.spec.name }}</span>\n @if (item.spec.location === 'registry') {\n <span class=\"component-badge registry\">Registry</span>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No component hierarchy available</span>\n </div>\n }\n </div>\n\n <!-- Feedback Form Section -->\n <div class=\"feedback-section\">\n @if (SelectedSpec) {\n <div class=\"selected-component-info\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-star\"></i>\n <span>Rate: {{ SelectedSpec.title || SelectedSpec.name }}</span>\n </div>\n\n @if (SelectedSpec.description) {\n <mj-markdown [data]=\"SelectedSpec.description\" class=\"component-description\"></mj-markdown>\n }\n\n <!-- Star Rating (half-star support) -->\n <div class=\"star-rating-container\">\n <label class=\"rating-label\">Your Rating</label>\n <div class=\"star-rating\" (mouseleave)=\"ClearHoverRating()\">\n @for (star of [1, 2, 3, 4, 5]; track star) {\n <span class=\"star-wrapper\">\n <!-- Left half (sets X.5 for previous half) -->\n <span class=\"star-zone star-zone-left\"\n (click)=\"SetRating(star - 0.5)\"\n (mouseenter)=\"SetHoverRating(star - 0.5)\"></span>\n <!-- Right half (sets full star) -->\n <span class=\"star-zone star-zone-right\"\n (click)=\"SetRating(star)\"\n (mouseenter)=\"SetHoverRating(star)\"></span>\n <!-- Star icon -->\n @switch (GetStarState(star)) {\n @case ('full') {\n <i class=\"star fa-solid fa-star\"></i>\n }\n @case ('half') {\n <i class=\"star fa-solid fa-star-half-stroke\"></i>\n }\n @default {\n <i class=\"star fa-regular fa-star\"></i>\n }\n }\n </span>\n }\n </div>\n @if (StarRating === 0) {\n <span class=\"rating-text placeholder\">Click to rate</span>\n }\n </div>\n\n <!-- Comment Input -->\n <div class=\"comment-container\">\n <label class=\"comment-label\">\n Comments\n <span class=\"optional-label\">(optional)</span>\n </label>\n <textarea\n class=\"comment-input\"\n [(ngModel)]=\"FeedbackComments\"\n placeholder=\"Share your thoughts about this component...\"\n rows=\"4\"\n maxlength=\"1000\">\n </textarea>\n @if (FeedbackComments.length > 0) {\n <span class=\"char-count\">{{ FeedbackComments.length }}/1000</span>\n }\n </div>\n\n <!-- Submit Button -->\n <div class=\"submit-container\">\n <button\n class=\"submit-button\"\n [disabled]=\"!CanSubmit()\"\n (click)=\"SubmitFeedback()\">\n @if (IsSubmitting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Submitting...</span>\n } @else {\n <i class=\"fa-solid fa-paper-plane\"></i>\n <span>Submit Feedback</span>\n }\n </button>\n </div>\n\n <!-- Success/Error Messages -->\n @if (SubmitSuccess) {\n <div class=\"message success-message\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span>Feedback submitted successfully!</span>\n </div>\n }\n @if (SubmitError) {\n <div class=\"message error-message\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ SubmitError }}</span>\n </div>\n }\n </div>\n } @else {\n <div class=\"no-selection-message\">\n <i class=\"fa-solid fa-hand-pointer\"></i>\n <p>Select a component from the tree to provide feedback</p>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n"]}
1
+ {"version":3,"file":"component-feedback-panel.component.js","sourceRoot":"","sources":["../../../../../src/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.ts","../../../../../src/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnDestroy, ChangeDetectorRef, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { Metadata } from '@memberjunction/core';\nimport { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { MarkdownModule } from '@memberjunction/ng-markdown';\nimport { MJEmptyStateComponent, MJAlertComponent } from '@memberjunction/ng-ui-components';\n\n/**\n * Flattened tree item for rendering the component hierarchy\n */\ninterface TreeItem {\n spec: ComponentSpec;\n depth: number;\n hasChildren: boolean;\n}\n\n/**\n * Component feedback panel that displays component hierarchy tree\n * and allows users to select components and provide star ratings with comments.\n * Registry-agnostic: works with any component registry (Skip, MJ Central, etc.)\n */\n@Component({\n selector: 'mj-component-feedback-panel',\n standalone: true,\n imports: [CommonModule, FormsModule, MarkdownModule, MJEmptyStateComponent, MJAlertComponent],\n templateUrl: './component-feedback-panel.component.html',\n styleUrls: ['./component-feedback-panel.component.css']\n})\nexport class ComponentFeedbackPanelComponent extends BaseAngularComponent implements OnDestroy {\n @Input() ComponentSpec: ComponentSpec | null = null;\n @Input() ReactContainerElement: HTMLElement | null = null;\n @Input() ConversationId: string | null = null;\n @Input() ConversationDetailId: string | null = null;\n\n @Output() Closed = new EventEmitter<void>();\n\n // Feedback form state\n public SelectedSpec: ComponentSpec | null = null;\n public StarRating = 0;\n public HoverRating = 0;\n public FeedbackComments = '';\n public IsSubmitting = false;\n public SubmitSuccess = false;\n public SubmitError = '';\n\n // Tree state\n public ExpandedNodes = new Set<string>();\n\n // Highlight overlays\n private highlightOverlay: HTMLElement | null = null;\n private hoverOverlay: HTMLElement | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private containerClickHandler: ((e: MouseEvent) => void) | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n\n // --- Tree Methods ---\n\n GetTreeItems(): TreeItem[] {\n if (!this.ComponentSpec) return [];\n if (!this.ExpandedNodes.has(this.ComponentSpec.name)) {\n this.ExpandedNodes.add(this.ComponentSpec.name);\n }\n return this.buildTree(this.ComponentSpec, 0);\n }\n\n private buildTree(spec: ComponentSpec, depth: number): TreeItem[] {\n const items: TreeItem[] = [{\n spec,\n depth,\n hasChildren: !!(spec.dependencies && spec.dependencies.length > 0)\n }];\n if (this.ExpandedNodes.has(spec.name) && spec.dependencies) {\n for (const child of spec.dependencies) {\n items.push(...this.buildTree(child, depth + 1));\n }\n }\n return items;\n }\n\n ToggleNode(spec: ComponentSpec, event: MouseEvent): void {\n event.stopPropagation();\n if (this.ExpandedNodes.has(spec.name)) {\n this.ExpandedNodes.delete(spec.name);\n } else {\n this.ExpandedNodes.add(spec.name);\n }\n }\n\n IsNodeExpanded(spec: ComponentSpec): boolean {\n return this.ExpandedNodes.has(spec.name);\n }\n\n SelectComponent(spec: ComponentSpec): void {\n this.SelectedSpec = spec;\n this.ResetForm();\n this.HighlightComponent(spec.name);\n }\n\n IsSelected(spec: ComponentSpec): boolean {\n return this.SelectedSpec?.name === spec.name;\n }\n\n GetIndentation(depth: number): string {\n return `${depth * 20}px`;\n }\n\n // --- Star Rating (supports half-star increments) ---\n\n SetRating(value: number): void {\n this.StarRating = value;\n }\n\n SetHoverRating(value: number): void {\n this.HoverRating = value;\n }\n\n ClearHoverRating(): void {\n this.HoverRating = 0;\n }\n\n GetDisplayRating(): number {\n return this.HoverRating || this.StarRating;\n }\n\n /** Returns 'full' | 'half' | 'empty' for the given 1-based star index */\n GetStarState(index: number): 'full' | 'half' | 'empty' {\n const rating = this.GetDisplayRating();\n if (index <= rating) return 'full';\n if (index - 0.5 <= rating) return 'half';\n return 'empty';\n }\n\n IsStarFilled(index: number): boolean {\n return index <= this.GetDisplayRating();\n }\n\n // --- Form ---\n\n ResetForm(): void {\n this.StarRating = 0;\n this.HoverRating = 0;\n this.FeedbackComments = '';\n this.SubmitSuccess = false;\n this.SubmitError = '';\n }\n\n CanSubmit(): boolean {\n return !!this.SelectedSpec && this.StarRating > 0 && !this.IsSubmitting;\n }\n\n async SubmitFeedback(): Promise<void> {\n if (!this.CanSubmit() || !this.SelectedSpec) return;\n\n this.IsSubmitting = true;\n this.SubmitError = '';\n this.SubmitSuccess = false;\n this.cdr.detectChanges();\n\n try {\n // Dynamic import to avoid adding graphql-dataprovider as a package dependency.\n // At runtime in the browser, this.ProviderToUse is always a GraphQLDataProvider.\n const { GraphQLComponentRegistryClient } = await import('@memberjunction/graphql-dataprovider');\n const provider = this.ProviderToUse;\n const client = new GraphQLComponentRegistryClient(provider as ConstructorParameters<typeof GraphQLComponentRegistryClient>[0]);\n\n const response = await client.SendComponentFeedback({\n componentName: this.SelectedSpec.name,\n componentNamespace: this.SelectedSpec.namespace || this.SelectedSpec.registry || '',\n componentVersion: this.SelectedSpec.version,\n registryName: this.SelectedSpec.registry,\n rating: this.StarRating * 20, // 0-5 -> 0-100\n comments: this.FeedbackComments.trim() || undefined,\n conversationID: this.ConversationId || undefined,\n conversationDetailID: this.ConversationDetailId || undefined\n });\n\n if (response.success) {\n this.SubmitSuccess = true;\n setTimeout(() => {\n this.ResetForm();\n this.cdr.detectChanges();\n }, 2000);\n } else {\n this.SubmitError = response.error || 'Failed to submit feedback.';\n }\n } catch (error) {\n this.SubmitError = 'Failed to submit feedback. Please try again.';\n console.error('Error submitting component feedback:', error);\n } finally {\n this.IsSubmitting = false;\n this.cdr.detectChanges();\n }\n }\n\n // --- Hover Highlight (light preview on tree hover) ---\n\n HoverTreeItem(spec: ComponentSpec): void {\n if (!this.ReactContainerElement || this.IsSelected(spec)) return;\n this.showHoverOverlay(spec.name);\n }\n\n ClearTreeItemHover(): void {\n this.removeHoverOverlay();\n }\n\n private showHoverOverlay(componentName: string): void {\n if (!this.ReactContainerElement) return;\n\n const targetEl = this.ReactContainerElement.querySelector(\n `[data-mj-component=\"${componentName}\"]`\n );\n if (!targetEl) {\n this.removeHoverOverlay();\n return;\n }\n\n this.ensureContainerPositioned();\n\n if (!this.hoverOverlay) {\n this.hoverOverlay = document.createElement('div');\n this.ReactContainerElement.appendChild(this.hoverOverlay);\n }\n\n this.positionOverlay(this.hoverOverlay, targetEl);\n this.hoverOverlay.style.border = '2px dashed var(--mj-brand-primary, #3B82F6)';\n this.hoverOverlay.style.background = 'color-mix(in srgb, var(--mj-brand-primary, #3B82F6) 5%, transparent)';\n }\n\n private removeHoverOverlay(): void {\n if (this.hoverOverlay?.parentNode) {\n this.hoverOverlay.parentNode.removeChild(this.hoverOverlay);\n }\n this.hoverOverlay = null;\n }\n\n // --- Region Highlighting (solid selection) ---\n\n private HighlightComponent(componentName: string): void {\n if (!this.ReactContainerElement) {\n this.ClearHighlight();\n return;\n }\n\n const targetEl = this.ReactContainerElement.querySelector(\n `[data-mj-component=\"${componentName}\"]`\n );\n if (!targetEl) {\n this.ClearHighlight();\n return;\n }\n\n this.ensureContainerPositioned();\n\n if (!this.highlightOverlay) {\n this.highlightOverlay = document.createElement('div');\n this.ReactContainerElement.appendChild(this.highlightOverlay);\n }\n\n this.positionOverlay(this.highlightOverlay, targetEl);\n this.highlightOverlay.style.border = '2px solid var(--mj-brand-primary, #3B82F6)';\n this.highlightOverlay.style.background = 'color-mix(in srgb, var(--mj-brand-primary, #3B82F6) 10%, transparent)';\n\n // Watch for resize/layout changes\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => {\n if (this.highlightOverlay) {\n this.positionOverlay(this.highlightOverlay, targetEl);\n }\n });\n this.resizeObserver.observe(targetEl);\n this.resizeObserver.observe(this.ReactContainerElement);\n\n // Set up bidirectional click selection on container\n this.installContainerClickListener();\n }\n\n private ensureContainerPositioned(): void {\n if (!this.ReactContainerElement) return;\n const containerStyle = getComputedStyle(this.ReactContainerElement);\n if (containerStyle.position === 'static') {\n this.ReactContainerElement.style.position = 'relative';\n }\n }\n\n /**\n * Gets the effective bounding rect for a component marker element.\n * display:contents elements return zero-size rects, so we compute\n * the union bounding box of their children instead.\n */\n private getEffectiveRect(el: Element): DOMRect {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) return rect;\n\n // display:contents wrapper — union the bounding rects of all children\n const children = el.children;\n if (children.length === 0) return rect;\n\n let top = Infinity, left = Infinity, bottom = -Infinity, right = -Infinity;\n for (let i = 0; i < children.length; i++) {\n const childRect = children[i].getBoundingClientRect();\n if (childRect.width === 0 && childRect.height === 0) continue;\n top = Math.min(top, childRect.top);\n left = Math.min(left, childRect.left);\n bottom = Math.max(bottom, childRect.bottom);\n right = Math.max(right, childRect.right);\n }\n\n if (top === Infinity) return rect; // no visible children\n return new DOMRect(left, top, right - left, bottom - top);\n }\n\n private positionOverlay(overlay: HTMLElement, targetEl: Element): void {\n if (!this.ReactContainerElement) return;\n\n const targetRect = this.getEffectiveRect(targetEl);\n const containerRect = this.ReactContainerElement.getBoundingClientRect();\n\n overlay.style.position = 'absolute';\n overlay.style.top = `${targetRect.top - containerRect.top + this.ReactContainerElement.scrollTop}px`;\n overlay.style.left = `${targetRect.left - containerRect.left + this.ReactContainerElement.scrollLeft}px`;\n overlay.style.width = `${targetRect.width}px`;\n overlay.style.height = `${targetRect.height}px`;\n overlay.style.pointerEvents = 'none';\n overlay.style.zIndex = '10';\n overlay.style.borderRadius = '4px';\n overlay.style.transition = 'all 0.2s ease';\n }\n\n // --- Bidirectional click: clicking a component region selects it in the tree ---\n\n private installContainerClickListener(): void {\n if (this.containerClickHandler || !this.ReactContainerElement) return;\n\n this.containerClickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n\n // Walk up from click target to find nearest [data-mj-component]\n const componentEl = target.closest('[data-mj-component]');\n if (!componentEl) return;\n\n const componentName = componentEl.getAttribute('data-mj-component');\n if (!componentName) return;\n\n // Find matching spec in the tree\n const matchingSpec = this.findSpecByName(componentName);\n if (matchingSpec && matchingSpec.name !== this.SelectedSpec?.name) {\n this.SelectComponent(matchingSpec);\n this.cdr.detectChanges();\n }\n };\n\n // Use capture phase so we observe the click without interfering with component handlers\n this.ReactContainerElement.addEventListener('click', this.containerClickHandler, true);\n }\n\n private removeContainerClickListener(): void {\n if (this.containerClickHandler && this.ReactContainerElement) {\n this.ReactContainerElement.removeEventListener('click', this.containerClickHandler, true);\n }\n this.containerClickHandler = null;\n }\n\n private findSpecByName(name: string): ComponentSpec | null {\n if (!this.ComponentSpec) return null;\n return this.walkSpecTree(this.ComponentSpec, name);\n }\n\n private walkSpecTree(spec: ComponentSpec, name: string): ComponentSpec | null {\n if (spec.name === name) return spec;\n if (spec.dependencies) {\n for (const child of spec.dependencies) {\n const found = this.walkSpecTree(child, name);\n if (found) return found;\n }\n }\n return null;\n }\n\n private ClearHighlight(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n if (this.highlightOverlay?.parentNode) {\n this.highlightOverlay.parentNode.removeChild(this.highlightOverlay);\n }\n this.highlightOverlay = null;\n this.removeHoverOverlay();\n this.removeContainerClickListener();\n }\n\n ClosePanel(): void {\n this.ClearHighlight();\n this.Closed.emit();\n }\n\n ngOnDestroy(): void {\n this.ClearHighlight();\n }\n}\n","<div class=\"feedback-panel-overlay\" (click)=\"ClosePanel()\">\n <div class=\"feedback-panel\" (click)=\"$event.stopPropagation()\">\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-title\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>Inspect & Rate</span>\n </div>\n <button class=\"close-button\" (click)=\"ClosePanel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"panel-content\">\n <!-- Component Tree Section -->\n <div class=\"tree-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-sitemap\"></i>\n <span>Select Component</span>\n </div>\n\n @if (ComponentSpec) {\n <div class=\"component-tree\">\n @for (item of GetTreeItems(); track item.spec.name) {\n <div\n class=\"tree-item\"\n [class.selected]=\"IsSelected(item.spec)\"\n [style.padding-left]=\"GetIndentation(item.depth)\"\n (click)=\"SelectComponent(item.spec)\"\n (mouseenter)=\"HoverTreeItem(item.spec)\"\n (mouseleave)=\"ClearTreeItemHover()\">\n\n @if (item.hasChildren) {\n <i\n class=\"tree-toggle fa-solid\"\n [class.fa-chevron-right]=\"!IsNodeExpanded(item.spec)\"\n [class.fa-chevron-down]=\"IsNodeExpanded(item.spec)\"\n (click)=\"ToggleNode(item.spec, $event)\">\n </i>\n } @else {\n <span class=\"tree-spacer\"></span>\n }\n\n <i class=\"fa-solid fa-cube component-icon\"></i>\n\n <div class=\"component-info\">\n <span class=\"component-name\">{{ item.spec.title || item.spec.name }}</span>\n @if (item.spec.location === 'registry') {\n <span class=\"component-badge registry\">Registry</span>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <mj-empty-state\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No component hierarchy available\"\n Size=\"compact\" />\n }\n </div>\n\n <!-- Feedback Form Section -->\n <div class=\"feedback-section\">\n @if (SelectedSpec) {\n <div class=\"selected-component-info\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-star\"></i>\n <span>Rate: {{ SelectedSpec.title || SelectedSpec.name }}</span>\n </div>\n\n @if (SelectedSpec.description) {\n <mj-markdown [data]=\"SelectedSpec.description\" class=\"component-description\"></mj-markdown>\n }\n\n <!-- Star Rating (half-star support) -->\n <div class=\"star-rating-container\">\n <label class=\"rating-label\">Your Rating</label>\n <div class=\"star-rating\" (mouseleave)=\"ClearHoverRating()\">\n @for (star of [1, 2, 3, 4, 5]; track star) {\n <span class=\"star-wrapper\">\n <!-- Left half (sets X.5 for previous half) -->\n <span class=\"star-zone star-zone-left\"\n (click)=\"SetRating(star - 0.5)\"\n (mouseenter)=\"SetHoverRating(star - 0.5)\"></span>\n <!-- Right half (sets full star) -->\n <span class=\"star-zone star-zone-right\"\n (click)=\"SetRating(star)\"\n (mouseenter)=\"SetHoverRating(star)\"></span>\n <!-- Star icon -->\n @switch (GetStarState(star)) {\n @case ('full') {\n <i class=\"star fa-solid fa-star\"></i>\n }\n @case ('half') {\n <i class=\"star fa-solid fa-star-half-stroke\"></i>\n }\n @default {\n <i class=\"star fa-regular fa-star\"></i>\n }\n }\n </span>\n }\n </div>\n @if (StarRating === 0) {\n <span class=\"rating-text placeholder\">Click to rate</span>\n }\n </div>\n\n <!-- Comment Input -->\n <div class=\"comment-container\">\n <label class=\"comment-label\">\n Comments\n <span class=\"optional-label\">(optional)</span>\n </label>\n <textarea\n class=\"comment-input\"\n [(ngModel)]=\"FeedbackComments\"\n placeholder=\"Share your thoughts about this component...\"\n rows=\"4\"\n maxlength=\"1000\">\n </textarea>\n @if (FeedbackComments.length > 0) {\n <span class=\"char-count\">{{ FeedbackComments.length }}/1000</span>\n }\n </div>\n\n <!-- Submit Button -->\n <div class=\"submit-container\">\n <button\n class=\"submit-button\"\n [disabled]=\"!CanSubmit()\"\n (click)=\"SubmitFeedback()\">\n @if (IsSubmitting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Submitting...</span>\n } @else {\n <i class=\"fa-solid fa-paper-plane\"></i>\n <span>Submit Feedback</span>\n }\n </button>\n </div>\n\n <!-- Success/Error Messages -->\n @if (SubmitSuccess) {\n <mj-alert Variant=\"success\">Feedback submitted successfully!</mj-alert>\n }\n @if (SubmitError) {\n <mj-alert Variant=\"error\">{{ SubmitError }}</mj-alert>\n }\n </div>\n } @else {\n <mj-empty-state\n Icon=\"fa-solid fa-hand-pointer\"\n Title=\"Select a component from the tree to provide feedback\"\n Size=\"compact\" />\n }\n </div>\n </div>\n </div>\n</div>\n"]}
@@ -16,7 +16,8 @@ import * as i1 from "@angular/common";
16
16
  import * as i2 from "@memberjunction/ng-markdown";
17
17
  import * as i3 from "@memberjunction/ng-query-viewer";
18
18
  import * as i4 from "@memberjunction/ng-shared-generic";
19
- import * as i5 from "./save-query-dialog.component";
19
+ import * as i5 from "@memberjunction/ng-ui-components";
20
+ import * as i6 from "./save-query-dialog.component";
20
21
  const _forTrack0 = ($index, $item) => $item.name;
21
22
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
22
23
  i0.ɵɵelementStart(0, "span", 7);
@@ -253,38 +254,30 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_
253
254
  } }
254
255
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template(rf, ctx) { if (rf & 1) {
255
256
  const _r13 = i0.ɵɵgetCurrentView();
256
- i0.ɵɵelementStart(0, "div", 51);
257
- i0.ɵɵelement(1, "i", 52);
258
- i0.ɵɵelementStart(2, "span");
259
- i0.ɵɵtext(3);
260
- i0.ɵɵelementEnd();
261
- i0.ɵɵelementStart(4, "span", 53);
262
- i0.ɵɵtext(5, "(Showing cached data)");
257
+ i0.ɵɵelementStart(0, "mj-alert", 51);
258
+ i0.ɵɵtext(1);
259
+ i0.ɵɵelementStart(2, "span", 52);
260
+ i0.ɵɵtext(3, "(Showing cached data)");
263
261
  i0.ɵɵelementEnd()();
264
- i0.ɵɵelementStart(6, "mj-query-data-grid", 54);
265
- i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_EntityLinkClick_6_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_PageChange_6_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
262
+ i0.ɵɵelementStart(4, "mj-query-data-grid", 53);
263
+ i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_EntityLinkClick_4_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_PageChange_4_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
266
264
  i0.ɵɵelementEnd();
267
265
  } if (rf & 2) {
268
266
  const ctx_r0 = i0.ɵɵnextContext(3);
269
- i0.ɵɵadvance(3);
270
- i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
267
+ i0.ɵɵadvance();
268
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.ErrorMessage, " ");
271
269
  i0.ɵɵadvance(3);
272
270
  i0.ɵɵproperty("ColumnConfigs", ctx_r0.GridColumnConfigs)("Data", ctx_r0.GridData)("TotalRowCount", ctx_r0.PagerTotalRowCount)("PageNumber", ctx_r0.PagerPageNumber)("PageSize", ctx_r0.PagerPageSize)("ShowToolbar", false)("ShowRefresh", false)("PersistState", false)("SelectionMode", "none");
273
271
  } }
274
272
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_22_Template(rf, ctx) { if (rf & 1) {
275
- i0.ɵɵelementStart(0, "div", 1);
276
- i0.ɵɵelement(1, "i", 52);
277
- i0.ɵɵelementStart(2, "p");
278
- i0.ɵɵtext(3);
279
- i0.ɵɵelementEnd()();
273
+ i0.ɵɵelement(0, "mj-empty-state", 1);
280
274
  } if (rf & 2) {
281
275
  const ctx_r0 = i0.ɵɵnextContext(3);
282
- i0.ɵɵadvance(3);
283
- i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
276
+ i0.ɵɵproperty("Message", ctx_r0.ErrorMessage);
284
277
  } }
285
278
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template(rf, ctx) { if (rf & 1) {
286
279
  const _r14 = i0.ɵɵgetCurrentView();
287
- i0.ɵɵelementStart(0, "mj-query-data-grid", 54);
280
+ i0.ɵɵelementStart(0, "mj-query-data-grid", 53);
288
281
  i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template_mj_query_data_grid_EntityLinkClick_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template_mj_query_data_grid_PageChange_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
289
282
  i0.ɵɵelementEnd();
290
283
  } if (rf & 2) {
@@ -309,7 +302,7 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
309
302
  i0.ɵɵconditionalCreate(17, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template, 3, 0, "div", 14);
310
303
  i0.ɵɵelementStart(18, "div", 15);
311
304
  i0.ɵɵconditionalCreate(19, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template, 2, 1, "div", 16);
312
- i0.ɵɵconditionalCreate(20, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template, 1, 0, "mj-loading", 17)(21, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template, 7, 10)(22, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_22_Template, 4, 1, "div", 1)(23, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template, 1, 9, "mj-query-data-grid", 18);
305
+ i0.ɵɵconditionalCreate(20, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template, 1, 0, "mj-loading", 17)(21, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template, 5, 10)(22, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_22_Template, 1, 1, "mj-empty-state", 1)(23, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template, 1, 9, "mj-query-data-grid", 18);
313
306
  i0.ɵɵelementEnd();
314
307
  } if (rf & 2) {
315
308
  let tmp_7_0;
@@ -337,12 +330,12 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
337
330
  } }
338
331
  function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
339
332
  i0.ɵɵelementStart(0, "div", 4)(1, "div", 5);
340
- i0.ɵɵelement(2, "i", 55);
333
+ i0.ɵɵelement(2, "i", 54);
341
334
  i0.ɵɵelementStart(3, "span");
342
335
  i0.ɵɵtext(4);
343
336
  i0.ɵɵelementEnd()()();
344
- i0.ɵɵelementStart(5, "div", 56);
345
- i0.ɵɵelement(6, "mj-markdown", 57);
337
+ i0.ɵɵelementStart(5, "div", 55);
338
+ i0.ɵɵelement(6, "mj-markdown", 56);
346
339
  i0.ɵɵelementEnd();
347
340
  } if (rf & 2) {
348
341
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -352,39 +345,26 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ct
352
345
  i0.ɵɵproperty("data", ctx_r0.spec.plan)("enableMermaid", true)("enableHighlight", true)("enableCollapsibleHeadings", false)("enableSmartypants", true);
353
346
  } }
354
347
  function DataArtifactViewerComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
355
- i0.ɵɵelementStart(0, "div", 2);
356
- i0.ɵɵelement(1, "i", 58);
357
- i0.ɵɵelementStart(2, "p");
358
- i0.ɵɵtext(3, "No data to display");
359
- i0.ɵɵelementEnd()();
348
+ i0.ɵɵelement(0, "mj-empty-state", 2);
360
349
  } }
361
350
  function DataArtifactViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
362
- i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template, 24, 10)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 4, 0, "div", 2);
351
+ i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template, 24, 10)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 1, 0, "mj-empty-state", 2);
363
352
  } if (rf & 2) {
364
353
  const ctx_r0 = i0.ɵɵnextContext();
365
354
  i0.ɵɵconditional(ctx_r0.HasData || ctx_r0.IsLoading || ctx_r0.ResolvedTables.length > 0 ? 0 : ctx_r0.spec.plan ? 1 : 2);
366
355
  } }
367
356
  function DataArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
368
- i0.ɵɵelementStart(0, "div", 1);
369
- i0.ɵɵelement(1, "i", 52);
370
- i0.ɵɵelementStart(2, "p");
371
- i0.ɵɵtext(3);
372
- i0.ɵɵelementEnd()();
357
+ i0.ɵɵelement(0, "mj-empty-state", 1);
373
358
  } if (rf & 2) {
374
359
  const ctx_r0 = i0.ɵɵnextContext();
375
- i0.ɵɵadvance(3);
376
- i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
360
+ i0.ɵɵproperty("Message", ctx_r0.ErrorMessage);
377
361
  } }
378
362
  function DataArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
379
- i0.ɵɵelementStart(0, "div", 2);
380
- i0.ɵɵelement(1, "i", 58);
381
- i0.ɵɵelementStart(2, "p");
382
- i0.ɵɵtext(3, "No data to display");
383
- i0.ɵɵelementEnd()();
363
+ i0.ɵɵelement(0, "mj-empty-state", 2);
384
364
  } }
385
365
  function DataArtifactViewerComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
386
366
  const _r15 = i0.ɵɵgetCurrentView();
387
- i0.ɵɵelementStart(0, "mj-save-query-panel", 59);
367
+ i0.ɵɵelementStart(0, "mj-save-query-panel", 57);
388
368
  i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnQuerySaved($event)); })("Cancelled", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Cancelled_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = false); });
389
369
  i0.ɵɵelementEnd();
390
370
  } if (rf & 2) {
@@ -1031,9 +1011,9 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
1031
1011
  return tabs;
1032
1012
  }
1033
1013
  static ɵfac = function DataArtifactViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DataArtifactViewerComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
1034
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], [1, "error-state"], [1, "empty-state"], [3, "QueryName", "QueryDescription", "SQL", "Plan"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [1, "table-tabs"], [1, "computations-bar"], [1, "grid-container"], [1, "saving-overlay"], ["text", "Loading data..."], ["Height", "100%", 3, "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], ["title", "Refresh data", 1, "btn-icon", 3, "click", "disabled"], [1, "fas", "fa-sync-alt"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save", 3, "click"], [1, "fas", "fa-save"], [1, "query-badge", "query-badge-synced"], [1, "fas", "fa-check"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "query-badge", "query-badge-outdated"], [1, "fas", "fa-circle-exclamation"], [1, "dropdown-wrapper"], ["title", "Update or save query", 1, "btn-icon", "btn-warning", 3, "click"], [1, "fas", "fa-caret-down"], [1, "query-dropdown"], [1, "query-dropdown", 3, "click"], [1, "query-dropdown-item", 3, "click"], [1, "fas", "fa-arrow-up-from-bracket", 2, "color", "#d97706"], [1, "dropdown-label"], [1, "dropdown-desc"], [1, "fas", "fa-plus", 2, "color", "#16a34a"], [1, "fas", "fa-external-link-alt", 2, "color", "var(--mj-text-muted)"], [1, "query-badge", "query-badge-muted"], [1, "fas", "fa-clock-rotate-left"], ["title", "Open saved query record", 1, "btn-icon", "btn-muted", 3, "click"], [1, "table-tab", 3, "active", "title"], [1, "table-tab", 3, "click", "title"], [1, "table-tab-name"], [1, "table-tab-count"], [1, "computation-chip", 3, "title"], [1, "computation-label"], [1, "computation-value"], [3, "text"], [1, "error-banner"], [1, "fas", "fa-exclamation-triangle"], [1, "fallback-note"], ["Height", "100%", 3, "EntityLinkClick", "PageChange", "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], [1, "fas", "fa-diagram-project"], [1, "plan-content"], [3, "data", "enableMermaid", "enableHighlight", "enableCollapsibleHeadings", "enableSmartypants"], [1, "fas", "fa-inbox"], [3, "Saved", "Cancelled", "QueryName", "QueryDescription", "SQL", "Plan"]], template: function DataArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
1014
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load data", "Size", "compact", 3, "Message"], ["Icon", "fa-solid fa-inbox", "Title", "No data to display", "Size", "compact"], [3, "QueryName", "QueryDescription", "SQL", "Plan"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [1, "table-tabs"], [1, "computations-bar"], [1, "grid-container"], [1, "saving-overlay"], ["text", "Loading data..."], ["Height", "100%", 3, "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], ["title", "Refresh data", 1, "btn-icon", 3, "click", "disabled"], [1, "fas", "fa-sync-alt"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save", 3, "click"], [1, "fas", "fa-save"], [1, "query-badge", "query-badge-synced"], [1, "fas", "fa-check"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "query-badge", "query-badge-outdated"], [1, "fas", "fa-circle-exclamation"], [1, "dropdown-wrapper"], ["title", "Update or save query", 1, "btn-icon", "btn-warning", 3, "click"], [1, "fas", "fa-caret-down"], [1, "query-dropdown"], [1, "query-dropdown", 3, "click"], [1, "query-dropdown-item", 3, "click"], [1, "fas", "fa-arrow-up-from-bracket", 2, "color", "#d97706"], [1, "dropdown-label"], [1, "dropdown-desc"], [1, "fas", "fa-plus", 2, "color", "#16a34a"], [1, "fas", "fa-external-link-alt", 2, "color", "var(--mj-text-muted)"], [1, "query-badge", "query-badge-muted"], [1, "fas", "fa-clock-rotate-left"], ["title", "Open saved query record", 1, "btn-icon", "btn-muted", 3, "click"], [1, "table-tab", 3, "active", "title"], [1, "table-tab", 3, "click", "title"], [1, "table-tab-name"], [1, "table-tab-count"], [1, "computation-chip", 3, "title"], [1, "computation-label"], [1, "computation-value"], [3, "text"], ["Variant", "warning", "Size", "sm", 1, "data-artifact-alert-pos"], [1, "fallback-note"], ["Height", "100%", 3, "EntityLinkClick", "PageChange", "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], [1, "fas", "fa-diagram-project"], [1, "plan-content"], [3, "data", "enableMermaid", "enableHighlight", "enableCollapsibleHeadings", "enableSmartypants"], [3, "Saved", "Cancelled", "QueryName", "QueryDescription", "SQL", "Plan"]], template: function DataArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
1035
1015
  i0.ɵɵelementStart(0, "div", 0);
1036
- i0.ɵɵconditionalCreate(1, DataArtifactViewerComponent_Conditional_1_Template, 3, 1)(2, DataArtifactViewerComponent_Conditional_2_Template, 4, 1, "div", 1)(3, DataArtifactViewerComponent_Conditional_3_Template, 4, 0, "div", 2);
1016
+ i0.ɵɵconditionalCreate(1, DataArtifactViewerComponent_Conditional_1_Template, 3, 1)(2, DataArtifactViewerComponent_Conditional_2_Template, 1, 1, "mj-empty-state", 1)(3, DataArtifactViewerComponent_Conditional_3_Template, 1, 0, "mj-empty-state", 2);
1037
1017
  i0.ɵɵconditionalCreate(4, DataArtifactViewerComponent_Conditional_4_Template, 1, 4, "mj-save-query-panel", 3);
1038
1018
  i0.ɵɵelementEnd();
1039
1019
  } if (rf & 2) {
@@ -1042,7 +1022,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
1042
1022
  i0.ɵɵconditional(ctx.spec ? 1 : ctx.HasError ? 2 : 3);
1043
1023
  i0.ɵɵadvance(3);
1044
1024
  i0.ɵɵconditional(ctx.ShowSaveDialog ? 4 : -1);
1045
- } }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.row-count[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon[_ngcontent-%COMP%] {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%]: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.grid-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note[_ngcontent-%COMP%] {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.plan-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n\n\n.query-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n\n\n.dropdown-wrapper[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n\n\n.query-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.dropdown-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n\n.table-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name[_ngcontent-%COMP%] {\n text-transform: capitalize;\n}\n\n.table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active[_ngcontent-%COMP%] .table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n\n\n\n.computations-bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}"] });
1025
+ } }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.MJEmptyStateComponent, i5.MJAlertComponent, i6.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.row-count[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon[_ngcontent-%COMP%] {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%]: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.grid-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n\n\n.data-artifact-alert-pos[_ngcontent-%COMP%] {\n margin: 0 0 8px;\n}\n\n.fallback-note[_ngcontent-%COMP%] {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.plan-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n\n\n.query-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n\n\n.dropdown-wrapper[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n\n\n.query-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.dropdown-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n\n.table-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name[_ngcontent-%COMP%] {\n text-transform: capitalize;\n}\n\n.table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active[_ngcontent-%COMP%] .table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n\n\n\n.computations-bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}"] });
1046
1026
  };
1047
1027
  DataArtifactViewerComponent = __decorate([
1048
1028
  RegisterClass(BaseArtifactViewerPluginComponent, 'DataArtifactViewerPlugin')
@@ -1050,7 +1030,7 @@ DataArtifactViewerComponent = __decorate([
1050
1030
  export { DataArtifactViewerComponent };
1051
1031
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DataArtifactViewerComponent, [{
1052
1032
  type: Component,
1053
- args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: "<div class=\"data-artifact-viewer\" [ngClass]=\"cssClass\">\n @if (spec) {\n @if (HasData || IsLoading || ResolvedTables.length > 0) {\n <!-- Toolbar -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-table\"></i>\n <span>{{ spec.title || \"Data Results\" }}</span>\n @if (IsLive) {\n <span class=\"live-badge\">Live</span>\n }\n @if (DisplayRowCount != null) {\n <span class=\"row-count\">{{ DisplayRowCount }} rows</span>\n }\n @if (DisplayExecutionTime != null) {\n <span class=\"exec-time\">{{ DisplayExecutionTime }}ms</span>\n }\n </div>\n <div class=\"data-actions\">\n @if (IsLive) {\n <button class=\"btn-icon\" title=\"Refresh data\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n </button>\n }\n\n @switch (QuerySyncState) {\n <!-- S1: No saved query, latest version \u2192 Save Query -->\n @case (\"no-query-latest\") {\n <button class=\"btn-icon btn-save\" title=\"Save as reusable query\" (click)=\"OnShowSaveDialog()\"><i class=\"fas fa-save\"></i> Save Query</button>\n }\n\n <!-- S2: SQL matches \u2192 green badge + Open -->\n @case (\"synced\") {\n <span class=\"query-badge query-badge-synced\"> <i class=\"fas fa-check\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-open\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S3: SQL differs, latest version \u2192 amber badge + dropdown -->\n @case (\"outdated-latest\") {\n <span class=\"query-badge query-badge-outdated\"> <i class=\"fas fa-circle-exclamation\"></i> Saved at v{{ SavedAtVersion }} </span>\n <div class=\"dropdown-wrapper\">\n <button class=\"btn-icon btn-warning\" title=\"Update or save query\" (click)=\"OnToggleUpdateDropdown()\">\n <i class=\"fas fa-save\"></i> Update Query <i class=\"fas fa-caret-down\"></i>\n </button>\n @if (ShowUpdateDropdown) {\n <div class=\"query-dropdown\" (click)=\"$event.stopPropagation()\">\n <div class=\"query-dropdown-item\" (click)=\"OnUpdateExistingQuery()\">\n <i class=\"fas fa-arrow-up-from-bracket\" style=\"color: #d97706\"></i>\n <div>\n <div class=\"dropdown-label\">Update \"{{ SavedQueryDisplayName }}\"</div>\n <div class=\"dropdown-desc\">Replace saved query SQL with this version's SQL</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnSaveAsNewQuery()\">\n <i class=\"fas fa-plus\" style=\"color: #16a34a\"></i>\n <div>\n <div class=\"dropdown-label\">Save as New Query</div>\n <div class=\"dropdown-desc\">Create a new query, keep the original unchanged</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnOpenSavedQuery(); OnCloseDropdown()\">\n <i class=\"fas fa-external-link-alt\" style=\"color: var(--mj-text-muted)\"></i>\n <div>\n <div class=\"dropdown-label\">Open Saved Query</div>\n <div class=\"dropdown-desc\">View \"{{ SavedQueryDisplayName }}\" (saved at v{{ SavedAtVersion }})</div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- S4: Older version, query was updated ahead -->\n @case (\"query-ahead\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Query updated at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S5: Middle version, saved at older, not latest -->\n @case (\"query-behind\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S6: No saved query, older version \u2192 no actions -->\n @case (\"no-query-older\") {\n <!-- No query actions available on older versions -->\n }\n }\n </div>\n </div>\n\n <!-- Multi-table tab strip -->\n @if (IsMultiTable) {\n <div class=\"table-tabs\">\n @for (table of ResolvedTables; track table.name; let i = $index) {\n <button class=\"table-tab\" [class.active]=\"i === ActiveTableIndex\" [title]=\"table.description || table.name\" (click)=\"OnTableTabClick(i)\">\n <span class=\"table-tab-name\">{{ table.name }}</span>\n @if (table.metadata?.rowCount != null || table.rows.length) {\n <span class=\"table-tab-count\"> ({{ table.metadata?.rowCount ?? table.rows.length }} rows) </span>\n }\n </button>\n }\n </div>\n }\n\n <!-- Computations summary bar -->\n @if (spec.computations?.length) {\n <div class=\"computations-bar\">\n @for (comp of spec.computations; track comp.name) {\n @if (!IsMultiTable || !comp.table || comp.table === ActiveTable?.name) {\n <div class=\"computation-chip\" [title]=\"comp.description || comp.name\">\n <span class=\"computation-label\">{{ comp.name }}</span>\n <span class=\"computation-value\">\n {{ comp.formattedValue || comp.value }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n <!-- Grid -->\n <div class=\"grid-container\">\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading [text]=\"SavingMessage\"></mj-loading>\n </div>\n }\n @if (IsLoading) {\n <mj-loading text=\"Loading data...\"></mj-loading>\n } @else if (HasError && HasData) {\n <div class=\"error-banner\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n <span class=\"fallback-note\">(Showing cached data)</span>\n </div>\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n }\n </div>\n } @else if (spec.plan) {\n <!-- Plan-only view (no results yet) -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-diagram-project\"></i>\n <span>{{ spec.title || \"Query Plan\" }}</span>\n </div>\n </div>\n <div class=\"plan-content\">\n <mj-markdown [data]=\"spec.plan\" [enableMermaid]=\"true\" [enableHighlight]=\"true\" [enableCollapsibleHeadings]=\"false\" [enableSmartypants]=\"true\">\n </mj-markdown>\n </div>\n } @else {\n <!-- No data and no plan -->\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n\n <!-- Save Query Panel (slide-in) -->\n @if (ShowSaveDialog) {\n <mj-save-query-panel\n [QueryName]=\"spec?.title || 'Untitled Query'\"\n [QueryDescription]=\"''\"\n [SQL]=\"spec?.metadata?.sql || ''\"\n [Plan]=\"spec?.plan || ''\"\n (Saved)=\"OnQuerySaved($event)\"\n (Cancelled)=\"ShowSaveDialog = false\"\n >\n </mj-save-query-panel>\n }\n</div>\n", styles: [".data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title i {\n color: var(--mj-text-muted);\n}\n\n.row-count {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open: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.grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n/* Saving overlay \u2014 semi-transparent panel over the grid */\n.saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state, .error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state i, .error-state i {\n font-size: 32px;\n}\n\n.error-state {\n color: var(--mj-status-error);\n}\n\n.plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n/* Query sync badges */\n.query-badge {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* Dropdown wrapper */\n.dropdown-wrapper {\n position: relative;\n}\n\n.btn-icon.btn-warning {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n/* Query actions dropdown */\n.query-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item i {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label {\n font-weight: 500;\n}\n\n.dropdown-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500\u2500 Table Tab Strip \u2500\u2500\u2500 */\n\n.table-tabs {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name {\n text-transform: capitalize;\n}\n\n.table-tab-count {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active .table-tab-count {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n/* \u2500\u2500\u2500 Computations Summary Bar \u2500\u2500\u2500 */\n\n.computations-bar {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n"] }]
1033
+ args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: "<div class=\"data-artifact-viewer\" [ngClass]=\"cssClass\">\n @if (spec) {\n @if (HasData || IsLoading || ResolvedTables.length > 0) {\n <!-- Toolbar -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-table\"></i>\n <span>{{ spec.title || \"Data Results\" }}</span>\n @if (IsLive) {\n <span class=\"live-badge\">Live</span>\n }\n @if (DisplayRowCount != null) {\n <span class=\"row-count\">{{ DisplayRowCount }} rows</span>\n }\n @if (DisplayExecutionTime != null) {\n <span class=\"exec-time\">{{ DisplayExecutionTime }}ms</span>\n }\n </div>\n <div class=\"data-actions\">\n @if (IsLive) {\n <button class=\"btn-icon\" title=\"Refresh data\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n </button>\n }\n\n @switch (QuerySyncState) {\n <!-- S1: No saved query, latest version \u2192 Save Query -->\n @case (\"no-query-latest\") {\n <button class=\"btn-icon btn-save\" title=\"Save as reusable query\" (click)=\"OnShowSaveDialog()\"><i class=\"fas fa-save\"></i> Save Query</button>\n }\n\n <!-- S2: SQL matches \u2192 green badge + Open -->\n @case (\"synced\") {\n <span class=\"query-badge query-badge-synced\"> <i class=\"fas fa-check\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-open\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S3: SQL differs, latest version \u2192 amber badge + dropdown -->\n @case (\"outdated-latest\") {\n <span class=\"query-badge query-badge-outdated\"> <i class=\"fas fa-circle-exclamation\"></i> Saved at v{{ SavedAtVersion }} </span>\n <div class=\"dropdown-wrapper\">\n <button class=\"btn-icon btn-warning\" title=\"Update or save query\" (click)=\"OnToggleUpdateDropdown()\">\n <i class=\"fas fa-save\"></i> Update Query <i class=\"fas fa-caret-down\"></i>\n </button>\n @if (ShowUpdateDropdown) {\n <div class=\"query-dropdown\" (click)=\"$event.stopPropagation()\">\n <div class=\"query-dropdown-item\" (click)=\"OnUpdateExistingQuery()\">\n <i class=\"fas fa-arrow-up-from-bracket\" style=\"color: #d97706\"></i>\n <div>\n <div class=\"dropdown-label\">Update \"{{ SavedQueryDisplayName }}\"</div>\n <div class=\"dropdown-desc\">Replace saved query SQL with this version's SQL</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnSaveAsNewQuery()\">\n <i class=\"fas fa-plus\" style=\"color: #16a34a\"></i>\n <div>\n <div class=\"dropdown-label\">Save as New Query</div>\n <div class=\"dropdown-desc\">Create a new query, keep the original unchanged</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnOpenSavedQuery(); OnCloseDropdown()\">\n <i class=\"fas fa-external-link-alt\" style=\"color: var(--mj-text-muted)\"></i>\n <div>\n <div class=\"dropdown-label\">Open Saved Query</div>\n <div class=\"dropdown-desc\">View \"{{ SavedQueryDisplayName }}\" (saved at v{{ SavedAtVersion }})</div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- S4: Older version, query was updated ahead -->\n @case (\"query-ahead\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Query updated at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S5: Middle version, saved at older, not latest -->\n @case (\"query-behind\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S6: No saved query, older version \u2192 no actions -->\n @case (\"no-query-older\") {\n <!-- No query actions available on older versions -->\n }\n }\n </div>\n </div>\n\n <!-- Multi-table tab strip -->\n @if (IsMultiTable) {\n <div class=\"table-tabs\">\n @for (table of ResolvedTables; track table.name; let i = $index) {\n <button class=\"table-tab\" [class.active]=\"i === ActiveTableIndex\" [title]=\"table.description || table.name\" (click)=\"OnTableTabClick(i)\">\n <span class=\"table-tab-name\">{{ table.name }}</span>\n @if (table.metadata?.rowCount != null || table.rows.length) {\n <span class=\"table-tab-count\"> ({{ table.metadata?.rowCount ?? table.rows.length }} rows) </span>\n }\n </button>\n }\n </div>\n }\n\n <!-- Computations summary bar -->\n @if (spec.computations?.length) {\n <div class=\"computations-bar\">\n @for (comp of spec.computations; track comp.name) {\n @if (!IsMultiTable || !comp.table || comp.table === ActiveTable?.name) {\n <div class=\"computation-chip\" [title]=\"comp.description || comp.name\">\n <span class=\"computation-label\">{{ comp.name }}</span>\n <span class=\"computation-value\">\n {{ comp.formattedValue || comp.value }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n <!-- Grid -->\n <div class=\"grid-container\">\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading [text]=\"SavingMessage\"></mj-loading>\n </div>\n }\n @if (IsLoading) {\n <mj-loading text=\"Loading data...\"></mj-loading>\n } @else if (HasError && HasData) {\n <mj-alert Variant=\"warning\" Size=\"sm\" class=\"data-artifact-alert-pos\">\n {{ ErrorMessage }}\n <span class=\"fallback-note\">(Showing cached data)</span>\n </mj-alert>\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n } @else if (HasError) {\n <mj-empty-state\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load data\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\" />\n } @else {\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n }\n </div>\n } @else if (spec.plan) {\n <!-- Plan-only view (no results yet) -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-diagram-project\"></i>\n <span>{{ spec.title || \"Query Plan\" }}</span>\n </div>\n </div>\n <div class=\"plan-content\">\n <mj-markdown [data]=\"spec.plan\" [enableMermaid]=\"true\" [enableHighlight]=\"true\" [enableCollapsibleHeadings]=\"false\" [enableSmartypants]=\"true\">\n </mj-markdown>\n </div>\n } @else {\n <!-- No data and no plan -->\n <mj-empty-state\n Icon=\"fa-solid fa-inbox\"\n Title=\"No data to display\"\n Size=\"compact\" />\n }\n } @else if (HasError) {\n <mj-empty-state\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load data\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\" />\n } @else {\n <mj-empty-state\n Icon=\"fa-solid fa-inbox\"\n Title=\"No data to display\"\n Size=\"compact\" />\n }\n\n <!-- Save Query Panel (slide-in) -->\n @if (ShowSaveDialog) {\n <mj-save-query-panel\n [QueryName]=\"spec?.title || 'Untitled Query'\"\n [QueryDescription]=\"''\"\n [SQL]=\"spec?.metadata?.sql || ''\"\n [Plan]=\"spec?.plan || ''\"\n (Saved)=\"OnQuerySaved($event)\"\n (Cancelled)=\"ShowSaveDialog = false\"\n >\n </mj-save-query-panel>\n }\n</div>\n", styles: [".data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title i {\n color: var(--mj-text-muted);\n}\n\n.row-count {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open: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.grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n/* Saving overlay \u2014 semi-transparent panel over the grid */\n.saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n/* Positioning only \u2014 mj-alert owns its own box (bg/border/radius/padding/icon). */\n.data-artifact-alert-pos {\n margin: 0 0 8px;\n}\n\n.fallback-note {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n/* Query sync badges */\n.query-badge {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* Dropdown wrapper */\n.dropdown-wrapper {\n position: relative;\n}\n\n.btn-icon.btn-warning {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n/* Query actions dropdown */\n.query-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item i {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label {\n font-weight: 500;\n}\n\n.dropdown-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500\u2500 Table Tab Strip \u2500\u2500\u2500 */\n\n.table-tabs {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name {\n text-transform: capitalize;\n}\n\n.table-tab-count {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active .table-tab-count {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n/* \u2500\u2500\u2500 Computations Summary Bar \u2500\u2500\u2500 */\n\n.computations-bar {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n"] }]
1054
1034
  }], () => [{ type: i0.ChangeDetectorRef }], { openEntityRecord: [{
1055
1035
  type: Output
1056
1036
  }], navigationRequest: [{