@memberjunction/ng-artifacts 5.39.0 → 5.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/lib/artifacts.module.d.ts +26 -13
  2. package/dist/lib/artifacts.module.d.ts.map +1 -1
  3. package/dist/lib/artifacts.module.js +65 -2
  4. package/dist/lib/artifacts.module.js.map +1 -1
  5. package/dist/lib/components/artifact-message-card.component.d.ts +17 -1
  6. package/dist/lib/components/artifact-message-card.component.d.ts.map +1 -1
  7. package/dist/lib/components/artifact-message-card.component.js +118 -27
  8. package/dist/lib/components/artifact-message-card.component.js.map +1 -1
  9. package/dist/lib/components/base-artifact-viewer.component.d.ts +11 -0
  10. package/dist/lib/components/base-artifact-viewer.component.d.ts.map +1 -1
  11. package/dist/lib/components/base-artifact-viewer.component.js +11 -0
  12. package/dist/lib/components/base-artifact-viewer.component.js.map +1 -1
  13. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts +32 -0
  14. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts.map +1 -0
  15. package/dist/lib/components/plugins/audio-artifact-viewer.component.js +146 -0
  16. package/dist/lib/components/plugins/audio-artifact-viewer.component.js.map +1 -0
  17. package/dist/lib/components/plugins/image-artifact-viewer.component.d.ts +11 -1
  18. package/dist/lib/components/plugins/image-artifact-viewer.component.d.ts.map +1 -1
  19. package/dist/lib/components/plugins/image-artifact-viewer.component.js +17 -1
  20. package/dist/lib/components/plugins/image-artifact-viewer.component.js.map +1 -1
  21. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts +34 -0
  22. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts.map +1 -0
  23. package/dist/lib/components/plugins/video-artifact-viewer.component.js +148 -0
  24. package/dist/lib/components/plugins/video-artifact-viewer.component.js.map +1 -0
  25. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts +20 -0
  26. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts.map +1 -0
  27. package/dist/lib/components/previews/audio-artifact-preview.component.js +83 -0
  28. package/dist/lib/components/previews/audio-artifact-preview.component.js.map +1 -0
  29. package/dist/lib/components/previews/base-artifact-preview.component.d.ts +33 -0
  30. package/dist/lib/components/previews/base-artifact-preview.component.d.ts.map +1 -0
  31. package/dist/lib/components/previews/base-artifact-preview.component.js +52 -0
  32. package/dist/lib/components/previews/base-artifact-preview.component.js.map +1 -0
  33. package/dist/lib/components/previews/image-artifact-preview.component.d.ts +28 -0
  34. package/dist/lib/components/previews/image-artifact-preview.component.d.ts.map +1 -0
  35. package/dist/lib/components/previews/image-artifact-preview.component.js +91 -0
  36. package/dist/lib/components/previews/image-artifact-preview.component.js.map +1 -0
  37. package/dist/lib/components/previews/video-artifact-preview.component.d.ts +21 -0
  38. package/dist/lib/components/previews/video-artifact-preview.component.d.ts.map +1 -0
  39. package/dist/lib/components/previews/video-artifact-preview.component.js +84 -0
  40. package/dist/lib/components/previews/video-artifact-preview.component.js.map +1 -0
  41. package/dist/lib/interfaces/artifact-viewer-plugin.interface.d.ts +53 -10
  42. package/dist/lib/interfaces/artifact-viewer-plugin.interface.d.ts.map +1 -1
  43. package/dist/lib/interfaces/artifact-viewer-plugin.interface.js.map +1 -1
  44. package/dist/lib/services/artifact-preview-resolver.service.d.ts +39 -0
  45. package/dist/lib/services/artifact-preview-resolver.service.d.ts.map +1 -0
  46. package/dist/lib/services/artifact-preview-resolver.service.js +78 -0
  47. package/dist/lib/services/artifact-preview-resolver.service.js.map +1 -0
  48. package/dist/public-api.d.ts +7 -0
  49. package/dist/public-api.d.ts.map +1 -1
  50. package/dist/public-api.js +8 -0
  51. package/dist/public-api.js.map +1 -1
  52. package/package.json +18 -18
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-message-card.component.js","sourceRoot":"","sources":["../../../src/lib/components/artifact-message-card.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MJArtifactEntity, MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { UserInfo, RunView } from '@memberjunction/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { ArtifactIconService } from '../services/artifact-icon.service';\n\n/**\n * Artifact message card component - displays a simple info bar for artifacts in conversation messages.\n * Shows artifact icon, name, type badge, and version. Click to open full artifact viewer.\n */\n@Component({\n standalone: false,\n selector: 'mj-artifact-message-card',\n template: `\n <div class=\"artifact-message-card\" [class.loading]=\"loading\" [class.error]=\"error\">\n @if (loading) {\n <div class=\"artifact-skeleton\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-text\"></div>\n </div>\n } @else if (error) {\n <div class=\"artifact-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>Failed to load artifact</span>\n </div>\n } @else if (artifact) {\n <div class=\"artifact-info-bar\" (click)=\"openFullView()\">\n <div class=\"artifact-icon\">\n <i class=\"fa-solid\" [ngClass]=\"getArtifactIcon()\"></i>\n </div>\n <div class=\"artifact-info\">\n <span class=\"artifact-name\">{{ displayName }}</span>\n <div class=\"artifact-meta\">\n <span class=\"artifact-type-badge\" [style.background]=\"getTypeBadgeColor()\">\n {{ artifact.Type }}\n </span>\n <span class=\"artifact-version\">v{{ currentVersion?.VersionNumber || 1 }}</span>\n </div>\n </div>\n <div class=\"open-icon\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n .artifact-message-card {\n margin: 12px 0;\n }\n\n .artifact-skeleton {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n }\n\n .skeleton-icon {\n width: 32px;\n height: 32px;\n background: var(--mj-border-default);\n border-radius: 6px;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n .skeleton-text {\n flex: 1;\n height: 32px;\n background: var(--mj-border-default);\n border-radius: 4px;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n .artifact-error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n border-radius: 6px;\n color: var(--mj-status-error);\n font-size: 14px;\n }\n\n .artifact-error i {\n font-size: 16px;\n }\n\n .artifact-info-bar {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n transition: all 200ms ease;\n }\n\n .artifact-info-bar:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .artifact-icon {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n font-size: 16px;\n }\n\n .artifact-info {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n }\n\n .artifact-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .artifact-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .artifact-type-badge {\n display: inline-block;\n padding: 2px 8px;\n color: var(--mj-text-inverse);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.5px;\n border-radius: 3px;\n text-transform: uppercase;\n }\n\n .artifact-version {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 500;\n }\n\n .open-icon {\n flex-shrink: 0;\n color: var(--mj-text-disabled);\n font-size: 14px;\n transition: color 200ms ease;\n }\n\n .artifact-info-bar:hover .open-icon {\n color: var(--mj-brand-primary);\n }\n `]\n})\nexport class ArtifactMessageCardComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n @Input() artifactId!: string;\n @Input() versionNumber?: number;\n @Input() currentUser!: UserInfo;\n @Input() artifact?: MJArtifactEntity; // Optional - if provided, skips loading\n @Input() artifactVersion?: MJArtifactVersionEntity; // Optional - if provided, skips loading\n @Output() actionPerformed = new EventEmitter<{action: string; artifact: MJArtifactEntity; version?: MJArtifactVersionEntity}>();\n\n public _artifact: MJArtifactEntity | null = null;\n public _currentVersion: MJArtifactVersionEntity | null = null;\n public loading = true;\n public error = false;\n\n private destroy$ = new Subject<void>();\n\n constructor(private artifactIconService: ArtifactIconService) {\n super();}\n\n async ngOnInit(): Promise<void> {\n // If entities are provided, use them directly\n if (this.artifact && this.artifactVersion) {\n this._artifact = this.artifact;\n this._currentVersion = this.artifactVersion;\n this.loading = false;\n } else {\n // Otherwise load from database\n await this.loadArtifact();\n }\n }\n\n // Getters to access the internal properties\n public get artifactEntity(): MJArtifactEntity | null {\n return this._artifact;\n }\n\n public get currentVersion(): MJArtifactVersionEntity | null {\n return this._currentVersion;\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private async loadArtifact(): Promise<void> {\n if (!this.artifactId) {\n this.error = true;\n this.loading = false;\n return;\n }\n\n try {\n this.loading = true;\n this.error = false;\n\n // Load artifact directly\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJArtifactEntity>({\n EntityName: 'MJ: Conversation Artifacts',\n ExtraFilter: `ID='${this.artifactId}'`,\n MaxRows: 1,\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success && result.Results && result.Results.length > 0) {\n this._artifact = result.Results[0];\n // Load version content\n await this.loadVersionContent();\n } else {\n this.error = true;\n }\n\n } catch (err) {\n console.error('Error loading artifact:', err);\n this.error = true;\n } finally {\n this.loading = false;\n }\n }\n\n private async loadVersionContent(): Promise<void> {\n if (!this._artifact) return;\n\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const filter = this.versionNumber\n ? `ArtifactID='${this._artifact.ID}' AND VersionNumber=${this.versionNumber}`\n : `ArtifactID='${this._artifact.ID}'`;\n\n const result = await rv.RunView<MJArtifactVersionEntity>({\n EntityName: 'MJ: Artifact Versions',\n ExtraFilter: filter,\n OrderBy: 'VersionNumber DESC',\n MaxRows: 1,\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success && result.Results && result.Results.length > 0) {\n this._currentVersion = result.Results[0];\n }\n } catch (err) {\n console.error('Error loading version content:', err);\n }\n }\n\n /**\n * Get the display name - prefer version-specific name if available, otherwise use artifact name\n */\n public get displayName(): string {\n if (this._currentVersion?.Name) {\n return this._currentVersion.Name;\n }\n return this._artifact?.Name || 'Untitled';\n }\n\n /**\n * Get the display description - prefer version-specific description if available, otherwise use artifact description\n */\n public get displayDescription(): string | null {\n if (this._currentVersion?.Description) {\n return this._currentVersion.Description;\n }\n return this._artifact?.Description || null;\n }\n\n public get isCodeArtifact(): boolean {\n if (!this._artifact) return false;\n const name = this._artifact.Name?.toLowerCase() || '';\n const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs', '.cpp', '.c', '.go', '.rs', '.sql', '.html', '.css', '.scss'];\n return codeExtensions.some(ext => name.endsWith(ext));\n }\n\n /**\n * Get the icon for this artifact using the centralized icon service.\n * Fallback priority: Plugin icon > Metadata icon > Hardcoded mapping > Generic icon\n */\n public getArtifactIcon(): string {\n if (!this._artifact) return 'fa-file';\n return this.artifactIconService.getArtifactIcon(this._artifact);\n }\n\n public getTypeBadgeColor(): string {\n if (!this._artifact) return '#6B7280';\n\n const type = this._artifact.Type?.toLowerCase() || '';\n\n if (type.includes('code')) return '#8B5CF6'; // Purple\n if (type.includes('report')) return '#3B82F6'; // Blue\n if (type.includes('dashboard')) return '#10B981'; // Green\n if (type.includes('document')) return '#F59E0B'; // Orange\n\n return '#6B7280'; // Gray\n }\n\n public openFullView(): void {\n if (this._artifact) {\n this.actionPerformed.emit({ action: 'open', artifact: this._artifact, version: this._currentVersion || undefined });\n }\n }\n}\n"]}
1
+ {"version":3,"file":"artifact-message-card.component.js","sourceRoot":"","sources":["../../../src/lib/components/artifact-message-card.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, Type, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MJArtifactEntity, MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { UserInfo, RunView } from '@memberjunction/core';\nimport { Subject } from 'rxjs';\nimport { ArtifactIconService } from '../services/artifact-icon.service';\nimport { ArtifactPreviewResolverService } from '../services/artifact-preview-resolver.service';\nimport { IArtifactPreviewComponent } from '../interfaces/artifact-viewer-plugin.interface';\n\n/**\n * Artifact message card component - displays a simple info bar for artifacts in conversation messages.\n * Shows artifact icon, name, type badge, and version. Click to open full artifact viewer.\n */\n@Component({\n standalone: false,\n selector: 'mj-artifact-message-card',\n template: `\n <div class=\"artifact-message-card\" [class.loading]=\"loading\" [class.error]=\"error\">\n @if (loading) {\n <div class=\"artifact-skeleton\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-text\"></div>\n </div>\n } @else if (error) {\n <div class=\"artifact-error\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>Failed to load artifact</span>\n </div>\n } @else if (artifact) {\n @if (previewComponentType && currentVersion) {\n <!-- Inline preview: a clickable wrapper still opens the full right-side viewer.\n Media controls inside the preview (video/audio) stop their own click propagation. -->\n <div class=\"artifact-preview-wrapper\" (click)=\"openFullView()\" [title]=\"displayName\">\n <ng-container\n *ngComponentOutlet=\"previewComponentType; inputs: previewInputs\"\n ></ng-container>\n <div class=\"artifact-preview-caption\">\n <i class=\"fa-solid\" [ngClass]=\"getArtifactIcon()\"></i>\n <span class=\"artifact-name\">{{ displayName }}</span>\n <span class=\"artifact-type-badge\" [style.background]=\"getTypeBadgeColor()\">\n {{ artifact.Type }}\n </span>\n <span class=\"artifact-version\">v{{ currentVersion.VersionNumber || 1 }}</span>\n <span class=\"open-icon\"><i class=\"fa-solid fa-arrow-up-right-from-square\"></i></span>\n </div>\n </div>\n } @else {\n <div class=\"artifact-info-bar\" (click)=\"openFullView()\">\n <div class=\"artifact-icon\">\n <i class=\"fa-solid\" [ngClass]=\"getArtifactIcon()\"></i>\n </div>\n <div class=\"artifact-info\">\n <span class=\"artifact-name\">{{ displayName }}</span>\n <div class=\"artifact-meta\">\n <span class=\"artifact-type-badge\" [style.background]=\"getTypeBadgeColor()\">\n {{ artifact.Type }}\n </span>\n <span class=\"artifact-version\">v{{ currentVersion?.VersionNumber || 1 }}</span>\n </div>\n </div>\n <div class=\"open-icon\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </div>\n </div>\n }\n }\n </div>\n `,\n styles: [`\n .artifact-message-card {\n margin: 12px 0;\n }\n\n .artifact-skeleton {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n }\n\n .skeleton-icon {\n width: 32px;\n height: 32px;\n background: var(--mj-border-default);\n border-radius: 6px;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n .skeleton-text {\n flex: 1;\n height: 32px;\n background: var(--mj-border-default);\n border-radius: 4px;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n .artifact-error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n border-radius: 6px;\n color: var(--mj-status-error);\n font-size: 14px;\n }\n\n .artifact-error i {\n font-size: 16px;\n }\n\n .artifact-info-bar {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n transition: all 200ms ease;\n }\n\n .artifact-info-bar:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .artifact-icon {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n font-size: 16px;\n }\n\n .artifact-info {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n }\n\n .artifact-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .artifact-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .artifact-type-badge {\n display: inline-block;\n padding: 2px 8px;\n color: var(--mj-text-inverse);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.5px;\n border-radius: 3px;\n text-transform: uppercase;\n }\n\n .artifact-version {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 500;\n }\n\n .open-icon {\n flex-shrink: 0;\n color: var(--mj-text-disabled);\n font-size: 14px;\n transition: color 200ms ease;\n }\n\n .artifact-info-bar:hover .open-icon {\n color: var(--mj-brand-primary);\n }\n\n .artifact-preview-wrapper {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n transition: all 200ms ease;\n }\n\n .artifact-preview-wrapper:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .artifact-preview-caption {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n color: var(--mj-text-muted);\n font-size: 12px;\n }\n\n .artifact-preview-caption .artifact-name {\n flex: 1;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .artifact-preview-caption .open-icon {\n color: var(--mj-text-disabled);\n transition: color 200ms ease;\n }\n\n .artifact-preview-wrapper:hover .artifact-preview-caption .open-icon {\n color: var(--mj-brand-primary);\n }\n `]\n})\nexport class ArtifactMessageCardComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n @Input() artifactId!: string;\n @Input() versionNumber?: number;\n @Input() currentUser!: UserInfo;\n @Input() artifact?: MJArtifactEntity; // Optional - if provided, skips loading\n @Input() artifactVersion?: MJArtifactVersionEntity; // Optional - if provided, skips loading\n @Output() actionPerformed = new EventEmitter<{action: string; artifact: MJArtifactEntity; version?: MJArtifactVersionEntity}>();\n\n public _artifact: MJArtifactEntity | null = null;\n public _currentVersion: MJArtifactVersionEntity | null = null;\n public loading = true;\n public error = false;\n\n /**\n * Resolved inline-preview component for this artifact's type/contentType, or null when no plugin\n * exposes a matching preview (the card then falls back to its existing info-bar box). Resolved\n * SYNCHRONOUSLY up front (before the box renders) to avoid a flash / ExpressionChanged error.\n */\n public previewComponentType: Type<IArtifactPreviewComponent> | null = null;\n\n /** Inputs forwarded to the dynamically rendered preview component via *ngComponentOutlet. */\n public previewInputs: Record<string, unknown> = {};\n\n private destroy$ = new Subject<void>();\n private readonly previewResolver = inject(ArtifactPreviewResolverService);\n\n constructor(private artifactIconService: ArtifactIconService) {\n super();}\n\n async ngOnInit(): Promise<void> {\n // If entities are provided, use them directly\n if (this.artifact && this.artifactVersion) {\n this._artifact = this.artifact;\n this._currentVersion = this.artifactVersion;\n this.loading = false;\n } else {\n // Otherwise load from database\n await this.loadArtifact();\n }\n // Resolve the inline preview once artifact + version are known. Synchronous so the correct\n // branch (preview vs. box) is decided before the template renders this state.\n this.resolvePreview();\n }\n\n /**\n * Pick the inline-preview component for this artifact, INDEPENDENTLY of the full viewer.\n * Type name comes from `artifact.Type`; content type from `artifactVersion.MimeType`.\n * No match → previewComponentType stays null → existing info-bar box renders unchanged.\n */\n private resolvePreview(): void {\n if (!this._artifact || !this._currentVersion) {\n this.previewComponentType = null;\n return;\n }\n this.previewComponentType = this.previewResolver.resolvePreviewComponent(\n this._artifact.Type,\n this._currentVersion.MimeType,\n );\n if (this.previewComponentType) {\n this.previewInputs = { artifactVersion: this._currentVersion };\n }\n }\n\n // Getters to access the internal properties\n public get artifactEntity(): MJArtifactEntity | null {\n return this._artifact;\n }\n\n public get currentVersion(): MJArtifactVersionEntity | null {\n return this._currentVersion;\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private async loadArtifact(): Promise<void> {\n if (!this.artifactId) {\n this.error = true;\n this.loading = false;\n return;\n }\n\n try {\n this.loading = true;\n this.error = false;\n\n // Load artifact directly\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJArtifactEntity>({\n EntityName: 'MJ: Conversation Artifacts',\n ExtraFilter: `ID='${this.artifactId}'`,\n MaxRows: 1,\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success && result.Results && result.Results.length > 0) {\n this._artifact = result.Results[0];\n // Load version content\n await this.loadVersionContent();\n } else {\n this.error = true;\n }\n\n } catch (err) {\n console.error('Error loading artifact:', err);\n this.error = true;\n } finally {\n this.loading = false;\n }\n }\n\n private async loadVersionContent(): Promise<void> {\n if (!this._artifact) return;\n\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const filter = this.versionNumber\n ? `ArtifactID='${this._artifact.ID}' AND VersionNumber=${this.versionNumber}`\n : `ArtifactID='${this._artifact.ID}'`;\n\n const result = await rv.RunView<MJArtifactVersionEntity>({\n EntityName: 'MJ: Artifact Versions',\n ExtraFilter: filter,\n OrderBy: 'VersionNumber DESC',\n MaxRows: 1,\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success && result.Results && result.Results.length > 0) {\n this._currentVersion = result.Results[0];\n }\n } catch (err) {\n console.error('Error loading version content:', err);\n }\n }\n\n /**\n * Get the display name - prefer version-specific name if available, otherwise use artifact name\n */\n public get displayName(): string {\n if (this._currentVersion?.Name) {\n return this._currentVersion.Name;\n }\n return this._artifact?.Name || 'Untitled';\n }\n\n /**\n * Get the display description - prefer version-specific description if available, otherwise use artifact description\n */\n public get displayDescription(): string | null {\n if (this._currentVersion?.Description) {\n return this._currentVersion.Description;\n }\n return this._artifact?.Description || null;\n }\n\n public get isCodeArtifact(): boolean {\n if (!this._artifact) return false;\n const name = this._artifact.Name?.toLowerCase() || '';\n const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs', '.cpp', '.c', '.go', '.rs', '.sql', '.html', '.css', '.scss'];\n return codeExtensions.some(ext => name.endsWith(ext));\n }\n\n /**\n * Get the icon for this artifact using the centralized icon service.\n * Fallback priority: Plugin icon > Metadata icon > Hardcoded mapping > Generic icon\n */\n public getArtifactIcon(): string {\n if (!this._artifact) return 'fa-file';\n return this.artifactIconService.getArtifactIcon(this._artifact);\n }\n\n public getTypeBadgeColor(): string {\n if (!this._artifact) return '#6B7280';\n\n const type = this._artifact.Type?.toLowerCase() || '';\n\n if (type.includes('code')) return '#8B5CF6'; // Purple\n if (type.includes('report')) return '#3B82F6'; // Blue\n if (type.includes('dashboard')) return '#10B981'; // Green\n if (type.includes('document')) return '#F59E0B'; // Orange\n\n return '#6B7280'; // Gray\n }\n\n public openFullView(): void {\n if (this._artifact) {\n this.actionPerformed.emit({ action: 'open', artifact: this._artifact, version: this._currentVersion || undefined });\n }\n }\n}\n"]}
@@ -75,6 +75,17 @@ export declare abstract class BaseArtifactViewerPluginComponent extends BaseAngu
75
75
  * The artifact version to display
76
76
  */
77
77
  artifactVersion: MJArtifactVersionEntity;
78
+ /**
79
+ * Inline-PREVIEW resolution does NOT live here.
80
+ *
81
+ * Plugins that want to drive an inline preview inside conversation message cards declare the
82
+ * preview contract as STATIC members on their class — `static readonly PreviewComponentType` and
83
+ * `static CanHandlePreview(...)` — described by {@link IArtifactViewerPluginPreviewStatics}. The
84
+ * resolver reads those statics off the registered constructor WITHOUT instantiating the plugin
85
+ * (these are Angular components with DI constructors; a bare `new` outside an injection context
86
+ * throws). There is intentionally NO instance-level preview member on this base class — static is
87
+ * the single source of truth.
88
+ */
78
89
  /**
79
90
  * Optional: Custom height for the viewer (defaults to auto)
80
91
  */
@@ -1 +1 @@
1
- {"version":3,"file":"base-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,IAAI,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;;AAE1F;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IAEpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;IAC/E,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACrE;AAED;;;;GAIG;AACH,8BAIsB,iCAAkC,SAAQ,oBAAqB,YAAW,wBAAwB;IACtH;;OAEG;IACM,eAAe,EAAG,uBAAuB,CAAC;IAEnD;;OAEG;IACM,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACM,QAAQ,EAAE,OAAO,CAAQ;IAElC;;OAEG;IACM,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;OASG;IACH,IAAW,wBAAwB,IAAI,OAAO,CAE7C;IAED;;;;;;;;;;;OAWG;IACH,IAAW,0BAA0B,IAAI,OAAO,CAE/C;IAED;;;;;;;;OAQG;IACH,IAAW,iBAAiB,IAAI,OAAO,CAEtC;IAED;;;OAGG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM;IAa9B;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI;IAU/C;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,MAAM;IAIlC;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,MAAM;IAIlC;;;;;OAKG;IACI,WAAW,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAExC;;;;;;OAMG;IACI,iBAAiB,CAAC,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAE3D;;;;;;OAMG;IACH,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED;;;;;;OAMG;IACI,kBAAkB,IAAI,IAAI;IAIjC;;;;;OAKG;IACI,iBAAiB,CAAC,IAAI,iBAAiB,EAAE;IAEhD;;;;;;;;;OASG;IACI,sBAAsB,CAAC,IAAI,MAAM,EAAE;IAE1C;;;;;;;;;;;OAWG;aACa,uBAAuB,IAAI,YAAY,GAAG,IAAI;IAE9D;;;OAGG;IACH,SAAS,CAAC,aAAa,IAAI,MAAM,GAAG,IAAI;IAIxC;;;OAGG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1C;;;;OAIG;cACa,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;yCA1MhE,iCAAiC;2CAAjC,iCAAiC;CAoNtD"}
1
+ {"version":3,"file":"base-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,17 @@ export class BaseArtifactViewerPluginComponent extends BaseAngularComponent {
11
11
  * The artifact version to display
12
12
  */
13
13
  artifactVersion;
14
+ /**
15
+ * Inline-PREVIEW resolution does NOT live here.
16
+ *
17
+ * Plugins that want to drive an inline preview inside conversation message cards declare the
18
+ * preview contract as STATIC members on their class — `static readonly PreviewComponentType` and
19
+ * `static CanHandlePreview(...)` — described by {@link IArtifactViewerPluginPreviewStatics}. The
20
+ * resolver reads those statics off the registered constructor WITHOUT instantiating the plugin
21
+ * (these are Angular components with DI constructors; a bare `new` outside an injection context
22
+ * throws). There is intentionally NO instance-level preview member on this base class — static is
23
+ * the single source of truth.
24
+ */
14
25
  /**
15
26
  * Optional: Custom height for the viewer (defaults to auto)
16
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"base-artifact-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Type } from '@angular/core';\nimport { DataSnapshot } from '@memberjunction/core';\nimport { MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { IArtifactViewerComponent } from '../interfaces/artifact-viewer-plugin.interface';\n\n/**\n * General-purpose navigation request emitted by artifact viewer plugins.\n * Allows plugins to request app-level navigation (e.g., switch to a specific\n * application, open a nav item, and set query parameters) without depending\n * on NavigationService directly.\n *\n * The event bubbles through ArtifactTypePluginViewer → ArtifactViewerPanel →\n * consumer (conversations, dashboards, etc.) → ultimately the Explorer wrapper\n * where NavigationService lives.\n */\nexport interface NavigationRequest {\n /** Target application name (e.g., 'Data Explorer'). If omitted, stays in current app. */\n appName?: string;\n\n /** Target navigation item label within the app (e.g., 'Queries') */\n navItemName: string;\n\n /** Query parameters to pass to the navigation target (e.g., { queryId: 'xxx' }) */\n queryParams?: Record<string, string>;\n}\n\n/**\n * Represents an additional tab that a plugin can provide to the artifact viewer.\n *\n * Tabs can render content in two ways:\n * 1. **String content** - Use `contentType` with `content` for built-in renderers (markdown, json, code, etc.)\n * 2. **Custom component** - Use `contentType: 'component'` with `component` and optional `componentInputs`\n *\n * @example\n * // String content tab\n * {\n * label: 'Code',\n * icon: 'fa-code',\n * contentType: 'code',\n * content: () => this.getCodeContent(),\n * language: 'typescript'\n * }\n *\n * @example\n * // Custom component tab\n * {\n * label: 'Data',\n * icon: 'fa-database',\n * contentType: 'component',\n * component: DataRequirementsViewerComponent,\n * componentInputs: { dataRequirements: this.spec.dataRequirements }\n * }\n */\nexport interface ArtifactViewerTab {\n /** Display label for the tab */\n label: string;\n /** Font Awesome icon class (without 'fas' prefix) */\n icon?: string;\n /** Type of content to render */\n contentType: 'plaintext' | 'html' | 'markdown' | 'json' | 'code' | 'component';\n /** String content or function returning string (for non-component tabs) */\n content?: string | (() => string);\n /** Language hint for code highlighting (used with 'code' contentType) */\n language?: string;\n /** Angular component class to render (used with 'component' contentType) */\n component?: Type<any>;\n /** Inputs to pass to the custom component */\n componentInputs?: Record<string, any> | (() => Record<string, any>);\n}\n\n/**\n * Abstract base component for all artifact viewer plugins.\n * Provides common functionality and enforces the contract.\n * Note: This is an abstract class and should not be declared in Angular module.\n */\n@Component({\n standalone: false,\n template: ''\n})\nexport abstract class BaseArtifactViewerPluginComponent extends BaseAngularComponent implements IArtifactViewerComponent {\n /**\n * The artifact version to display\n */\n @Input() artifactVersion!: MJArtifactVersionEntity;\n\n /**\n * Optional: Custom height for the viewer (defaults to auto)\n */\n @Input() height?: string;\n\n /**\n * Optional: Whether the viewer is in readonly mode\n */\n @Input() readonly: boolean = true;\n\n /**\n * Optional: Additional CSS classes to apply\n */\n @Input() cssClass?: string;\n\n /**\n * Whether this plugin is showing an \"elevated\" display (e.g., extracted markdown/HTML)\n * vs raw content. When true and content type is JSON, the wrapper will show a JSON tab.\n *\n * Subclasses should override this getter to return the appropriate value based on their\n * current display state. For example, JSON plugin returns true when showing displayMarkdown\n * or displayHtml, false when showing raw JSON editor.\n *\n * Default: false (showing raw content)\n */\n public get isShowingElevatedDisplay(): boolean {\n return false;\n }\n\n /**\n * Whether the parent wrapper should show a raw content tab (e.g., JSON tab for JSON content).\n * This gives plugins fine-grained control over the parent's tab display.\n *\n * Use cases:\n * - JSON plugin with extract rules: true (show JSON tab to view source)\n * - Component plugin: true (show JSON tab even when displaying elevated component)\n * - Code plugin: false (already showing raw content)\n *\n * Subclasses can override this to control parent behavior.\n * Default: true (parent should show raw content tab if applicable)\n */\n public get parentShouldShowRawContent(): boolean {\n return true;\n }\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Used by the parent wrapper to determine if the Display tab should be shown.\n *\n * Subclasses should override this getter to return true when they have\n * meaningful content to display.\n *\n * Default: false (subclasses must opt-in to showing Display tab)\n */\n public get hasDisplayContent(): boolean {\n return false;\n }\n\n /**\n * Get the content from the artifact version.\n * Handles both string content and JSON objects.\n */\n protected getContent(): string {\n if (!this.artifactVersion?.Content) {\n return '';\n }\n\n if (typeof this.artifactVersion.Content === 'string') {\n return this.artifactVersion.Content;\n }\n\n // If Content is an object, stringify it\n return JSON.stringify(this.artifactVersion.Content, null, 2);\n }\n\n /**\n * Parse JSON content safely\n */\n protected parseJsonContent<T = any>(): T | null {\n try {\n const content = this.getContent();\n return JSON.parse(content);\n } catch (error) {\n console.error('Failed to parse artifact content as JSON:', error);\n return null;\n }\n }\n\n /**\n * Get a safe display name for the artifact version\n */\n protected getDisplayName(): string {\n return this.artifactVersion?.Name || 'Untitled Artifact';\n }\n\n /**\n * Get the description\n */\n protected getDescription(): string {\n return this.artifactVersion?.Description || '';\n }\n\n /**\n * Optional: Emitted when the plugin's additional tabs change after async loading.\n * Plugins that load data asynchronously (e.g., from a component registry) should\n * emit this when loading completes and new tabs become available. The parent panel\n * listens for this to re-run change detection and render the updated tab list.\n */\n public tabsChanged?: EventEmitter<void>;\n\n /**\n * Optional: Emitted when the plugin wants to navigate to a different app/nav item.\n * Plugins that need app-level navigation (e.g., \"Open Query\" navigating to the\n * Data Explorer's Queries nav item) should instantiate this emitter and emit a\n * NavigationRequest. The event bubbles up through the artifact viewer chain to the\n * host application where NavigationService handles the actual navigation.\n */\n public navigationRequest?: EventEmitter<NavigationRequest>;\n\n /**\n * Whether this plugin supports user feedback.\n * Plugins that return true will get a feedback button in the artifact viewer header.\n * Override this getter to return true when feedback is available.\n *\n * Default: false\n */\n public get SupportsFeedback(): boolean {\n return false;\n }\n\n /**\n * Ask the user for feedback on the current artifact.\n * Called by the artifact viewer panel when the user clicks the feedback button in the header.\n * Each plugin implements its own feedback UX (dialog, panel, inline form, etc.).\n *\n * Default: no-op. Subclasses should override to implement artifact-type-specific feedback.\n */\n public AskUserForFeedback(): void {\n // Subclasses override to implement feedback UX\n }\n\n /**\n * Get additional tabs that this plugin wants to provide to the artifact viewer.\n * Override this method to provide custom tabs for viewing metadata, code, etc.\n *\n * @returns Array of tab definitions, or undefined if no additional tabs\n */\n public GetAdditionalTabs?(): ArtifactViewerTab[];\n\n /**\n * Get list of standard tabs that this plugin wants to hide/remove.\n * Use this when the plugin provides custom alternatives to standard tabs.\n *\n * Example: Component viewer provides custom \"Resolved JSON\" tab, so removes standard \"JSON\" tab\n *\n * @returns Array of standard tab names to remove (case-insensitive)\n * Valid values: 'JSON', 'Details', 'Links'\n * Note: 'Display' cannot be removed (it's the main view)\n */\n public GetStandardTabRemovals?(): string[];\n\n /**\n * Return a point-in-time snapshot of the data this viewer is currently displaying.\n * Every viewer plugin MUST implement this with type-appropriate content.\n *\n * Guidelines for file-based viewers:\n * - PDF: page count, current page in interpretation, extracted text summary in custom\n * - Excel: each sheet as a DataTable in tables[], active sheet in activeTab\n * - Word: section count in interpretation, extracted text in custom\n * - For all: use getDisplayTitle() for snap.title\n *\n * @returns A DataSnapshot describing the current state, or null if no data is available.\n */\n public abstract GetCurrentStateSnapshot(): DataSnapshot | null;\n\n /**\n * Get the raw text content from the artifact version.\n * Returns null if no content is available.\n */\n protected getRawContent(): string | null {\n return this.artifactVersion?.Content ?? null;\n }\n\n /**\n * Get a display-friendly title for the artifact.\n * Returns null if no name is available.\n */\n protected getDisplayTitle(): string | null {\n return this.artifactVersion?.Name ?? null;\n }\n\n /**\n * Trigger a browser file download from a URL.\n * Shared by all file-backed viewer plugins (PDF, XLSX, DOCX) so the fetch →\n * blob → object-URL → anchor pattern lives in exactly one place.\n */\n protected async triggerBrowserDownload(url: string, fileName: string): Promise<void> {\n const response = await fetch(url);\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.download = fileName;\n anchor.click();\n URL.revokeObjectURL(objectUrl);\n }\n}\n"]}
1
+ {"version":3,"file":"base-artifact-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Type } from '@angular/core';\nimport { DataSnapshot } from '@memberjunction/core';\nimport { MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { IArtifactViewerComponent } from '../interfaces/artifact-viewer-plugin.interface';\n\n/**\n * General-purpose navigation request emitted by artifact viewer plugins.\n * Allows plugins to request app-level navigation (e.g., switch to a specific\n * application, open a nav item, and set query parameters) without depending\n * on NavigationService directly.\n *\n * The event bubbles through ArtifactTypePluginViewer → ArtifactViewerPanel →\n * consumer (conversations, dashboards, etc.) → ultimately the Explorer wrapper\n * where NavigationService lives.\n */\nexport interface NavigationRequest {\n /** Target application name (e.g., 'Data Explorer'). If omitted, stays in current app. */\n appName?: string;\n\n /** Target navigation item label within the app (e.g., 'Queries') */\n navItemName: string;\n\n /** Query parameters to pass to the navigation target (e.g., { queryId: 'xxx' }) */\n queryParams?: Record<string, string>;\n}\n\n/**\n * Represents an additional tab that a plugin can provide to the artifact viewer.\n *\n * Tabs can render content in two ways:\n * 1. **String content** - Use `contentType` with `content` for built-in renderers (markdown, json, code, etc.)\n * 2. **Custom component** - Use `contentType: 'component'` with `component` and optional `componentInputs`\n *\n * @example\n * // String content tab\n * {\n * label: 'Code',\n * icon: 'fa-code',\n * contentType: 'code',\n * content: () => this.getCodeContent(),\n * language: 'typescript'\n * }\n *\n * @example\n * // Custom component tab\n * {\n * label: 'Data',\n * icon: 'fa-database',\n * contentType: 'component',\n * component: DataRequirementsViewerComponent,\n * componentInputs: { dataRequirements: this.spec.dataRequirements }\n * }\n */\nexport interface ArtifactViewerTab {\n /** Display label for the tab */\n label: string;\n /** Font Awesome icon class (without 'fas' prefix) */\n icon?: string;\n /** Type of content to render */\n contentType: 'plaintext' | 'html' | 'markdown' | 'json' | 'code' | 'component';\n /** String content or function returning string (for non-component tabs) */\n content?: string | (() => string);\n /** Language hint for code highlighting (used with 'code' contentType) */\n language?: string;\n /** Angular component class to render (used with 'component' contentType) */\n component?: Type<any>;\n /** Inputs to pass to the custom component */\n componentInputs?: Record<string, any> | (() => Record<string, any>);\n}\n\n/**\n * Abstract base component for all artifact viewer plugins.\n * Provides common functionality and enforces the contract.\n * Note: This is an abstract class and should not be declared in Angular module.\n */\n@Component({\n standalone: false,\n template: ''\n})\nexport abstract class BaseArtifactViewerPluginComponent extends BaseAngularComponent implements IArtifactViewerComponent {\n /**\n * The artifact version to display\n */\n @Input() artifactVersion!: MJArtifactVersionEntity;\n\n /**\n * Inline-PREVIEW resolution does NOT live here.\n *\n * Plugins that want to drive an inline preview inside conversation message cards declare the\n * preview contract as STATIC members on their class — `static readonly PreviewComponentType` and\n * `static CanHandlePreview(...)` — described by {@link IArtifactViewerPluginPreviewStatics}. The\n * resolver reads those statics off the registered constructor WITHOUT instantiating the plugin\n * (these are Angular components with DI constructors; a bare `new` outside an injection context\n * throws). There is intentionally NO instance-level preview member on this base class — static is\n * the single source of truth.\n */\n\n /**\n * Optional: Custom height for the viewer (defaults to auto)\n */\n @Input() height?: string;\n\n /**\n * Optional: Whether the viewer is in readonly mode\n */\n @Input() readonly: boolean = true;\n\n /**\n * Optional: Additional CSS classes to apply\n */\n @Input() cssClass?: string;\n\n /**\n * Whether this plugin is showing an \"elevated\" display (e.g., extracted markdown/HTML)\n * vs raw content. When true and content type is JSON, the wrapper will show a JSON tab.\n *\n * Subclasses should override this getter to return the appropriate value based on their\n * current display state. For example, JSON plugin returns true when showing displayMarkdown\n * or displayHtml, false when showing raw JSON editor.\n *\n * Default: false (showing raw content)\n */\n public get isShowingElevatedDisplay(): boolean {\n return false;\n }\n\n /**\n * Whether the parent wrapper should show a raw content tab (e.g., JSON tab for JSON content).\n * This gives plugins fine-grained control over the parent's tab display.\n *\n * Use cases:\n * - JSON plugin with extract rules: true (show JSON tab to view source)\n * - Component plugin: true (show JSON tab even when displaying elevated component)\n * - Code plugin: false (already showing raw content)\n *\n * Subclasses can override this to control parent behavior.\n * Default: true (parent should show raw content tab if applicable)\n */\n public get parentShouldShowRawContent(): boolean {\n return true;\n }\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Used by the parent wrapper to determine if the Display tab should be shown.\n *\n * Subclasses should override this getter to return true when they have\n * meaningful content to display.\n *\n * Default: false (subclasses must opt-in to showing Display tab)\n */\n public get hasDisplayContent(): boolean {\n return false;\n }\n\n /**\n * Get the content from the artifact version.\n * Handles both string content and JSON objects.\n */\n protected getContent(): string {\n if (!this.artifactVersion?.Content) {\n return '';\n }\n\n if (typeof this.artifactVersion.Content === 'string') {\n return this.artifactVersion.Content;\n }\n\n // If Content is an object, stringify it\n return JSON.stringify(this.artifactVersion.Content, null, 2);\n }\n\n /**\n * Parse JSON content safely\n */\n protected parseJsonContent<T = any>(): T | null {\n try {\n const content = this.getContent();\n return JSON.parse(content);\n } catch (error) {\n console.error('Failed to parse artifact content as JSON:', error);\n return null;\n }\n }\n\n /**\n * Get a safe display name for the artifact version\n */\n protected getDisplayName(): string {\n return this.artifactVersion?.Name || 'Untitled Artifact';\n }\n\n /**\n * Get the description\n */\n protected getDescription(): string {\n return this.artifactVersion?.Description || '';\n }\n\n /**\n * Optional: Emitted when the plugin's additional tabs change after async loading.\n * Plugins that load data asynchronously (e.g., from a component registry) should\n * emit this when loading completes and new tabs become available. The parent panel\n * listens for this to re-run change detection and render the updated tab list.\n */\n public tabsChanged?: EventEmitter<void>;\n\n /**\n * Optional: Emitted when the plugin wants to navigate to a different app/nav item.\n * Plugins that need app-level navigation (e.g., \"Open Query\" navigating to the\n * Data Explorer's Queries nav item) should instantiate this emitter and emit a\n * NavigationRequest. The event bubbles up through the artifact viewer chain to the\n * host application where NavigationService handles the actual navigation.\n */\n public navigationRequest?: EventEmitter<NavigationRequest>;\n\n /**\n * Whether this plugin supports user feedback.\n * Plugins that return true will get a feedback button in the artifact viewer header.\n * Override this getter to return true when feedback is available.\n *\n * Default: false\n */\n public get SupportsFeedback(): boolean {\n return false;\n }\n\n /**\n * Ask the user for feedback on the current artifact.\n * Called by the artifact viewer panel when the user clicks the feedback button in the header.\n * Each plugin implements its own feedback UX (dialog, panel, inline form, etc.).\n *\n * Default: no-op. Subclasses should override to implement artifact-type-specific feedback.\n */\n public AskUserForFeedback(): void {\n // Subclasses override to implement feedback UX\n }\n\n /**\n * Get additional tabs that this plugin wants to provide to the artifact viewer.\n * Override this method to provide custom tabs for viewing metadata, code, etc.\n *\n * @returns Array of tab definitions, or undefined if no additional tabs\n */\n public GetAdditionalTabs?(): ArtifactViewerTab[];\n\n /**\n * Get list of standard tabs that this plugin wants to hide/remove.\n * Use this when the plugin provides custom alternatives to standard tabs.\n *\n * Example: Component viewer provides custom \"Resolved JSON\" tab, so removes standard \"JSON\" tab\n *\n * @returns Array of standard tab names to remove (case-insensitive)\n * Valid values: 'JSON', 'Details', 'Links'\n * Note: 'Display' cannot be removed (it's the main view)\n */\n public GetStandardTabRemovals?(): string[];\n\n /**\n * Return a point-in-time snapshot of the data this viewer is currently displaying.\n * Every viewer plugin MUST implement this with type-appropriate content.\n *\n * Guidelines for file-based viewers:\n * - PDF: page count, current page in interpretation, extracted text summary in custom\n * - Excel: each sheet as a DataTable in tables[], active sheet in activeTab\n * - Word: section count in interpretation, extracted text in custom\n * - For all: use getDisplayTitle() for snap.title\n *\n * @returns A DataSnapshot describing the current state, or null if no data is available.\n */\n public abstract GetCurrentStateSnapshot(): DataSnapshot | null;\n\n /**\n * Get the raw text content from the artifact version.\n * Returns null if no content is available.\n */\n protected getRawContent(): string | null {\n return this.artifactVersion?.Content ?? null;\n }\n\n /**\n * Get a display-friendly title for the artifact.\n * Returns null if no name is available.\n */\n protected getDisplayTitle(): string | null {\n return this.artifactVersion?.Name ?? null;\n }\n\n /**\n * Trigger a browser file download from a URL.\n * Shared by all file-backed viewer plugins (PDF, XLSX, DOCX) so the fetch →\n * blob → object-URL → anchor pattern lives in exactly one place.\n */\n protected async triggerBrowserDownload(url: string, fileName: string): Promise<void> {\n const response = await fetch(url);\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.download = fileName;\n anchor.click();\n URL.revokeObjectURL(objectUrl);\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { OnInit, Type } from '@angular/core';
2
+ import { DataSnapshot } from '@memberjunction/core';
3
+ import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
4
+ import { IArtifactPreviewComponent } from '../../interfaces/artifact-viewer-plugin.interface';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * Minimal full-size viewer plugin for audio artifacts. Renders an `<audio controls>` player.
8
+ * Supports both storage modes (see {@link VideoArtifactViewerComponent} for the same contract).
9
+ *
10
+ * Deliberately minimal — the inline experience lives in {@link AudioArtifactPreviewComponent}
11
+ * (exposed via {@link previewComponentType}); this satisfies the required `componentType`.
12
+ */
13
+ export declare class AudioArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {
14
+ private readonly fileService;
15
+ private readonly cdr;
16
+ audioUrl: string;
17
+ errorMessage: string;
18
+ /**
19
+ * Inline-preview component for audio artifacts ({@link IArtifactViewerPluginPreviewStatics}).
20
+ * STATIC so the resolver reads it off the constructor without instantiating this DI-using component.
21
+ */
22
+ static readonly PreviewComponentType: Type<IArtifactPreviewComponent>;
23
+ /** Matches audio artifacts by type name or `audio/*` MIME. STATIC — drives preview resolution. */
24
+ static CanHandlePreview(artifactTypeName: string, contentType?: string): boolean;
25
+ get hasDisplayContent(): boolean;
26
+ ngOnInit(): Promise<void>;
27
+ onMediaError(): void;
28
+ GetCurrentStateSnapshot(): DataSnapshot | null;
29
+ static ɵfac: i0.ɵɵFactoryDeclaration<AudioArtifactViewerComponent, never>;
30
+ static ɵcmp: i0.ɵɵComponentDeclaration<AudioArtifactViewerComponent, "mj-audio-artifact-viewer", never, {}, {}, never, never, false, never>;
31
+ }
32
+ //# sourceMappingURL=audio-artifact-viewer.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/audio-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,MAAM,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AAEtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,mDAAmD,CAAC;;AAG9F;;;;;;GAMG;AACH,qBAyDa,4BAA6B,SAAQ,iCAAkC,YAAW,MAAM;IACjG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAE1C,QAAQ,SAAM;IACd,YAAY,SAAM;IAEzB;;;OAGG;IACH,gBAAuB,oBAAoB,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAiC;IAE7G,kGAAkG;WACpF,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO;IAQvF,IAAoB,iBAAiB,IAAI,OAAO,CAE/C;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBxB,YAAY,IAAI,IAAI;IAMX,uBAAuB,IAAI,YAAY,GAAG,IAAI;yCAnDrD,4BAA4B;2CAA5B,4BAA4B;CAiExC"}
@@ -0,0 +1,146 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { ChangeDetectorRef, Component, inject } from '@angular/core';
8
+ import { DataSnapshot } from '@memberjunction/core';
9
+ import { RegisterClass } from '@memberjunction/global';
10
+ import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
11
+ import { ArtifactFileService } from '../../services/artifact-file.service';
12
+ import { AudioArtifactPreviewComponent } from '../previews/audio-artifact-preview.component';
13
+ import * as i0 from "@angular/core";
14
+ import * as i1 from "@angular/common";
15
+ function AudioArtifactViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
16
+ i0.ɵɵelementStart(0, "div", 1);
17
+ i0.ɵɵelement(1, "i", 4);
18
+ i0.ɵɵelementStart(2, "span");
19
+ i0.ɵɵtext(3);
20
+ i0.ɵɵelementEnd()();
21
+ } if (rf & 2) {
22
+ const ctx_r0 = i0.ɵɵnextContext();
23
+ i0.ɵɵadvance(3);
24
+ i0.ɵɵtextInterpolate(ctx_r0.errorMessage);
25
+ } }
26
+ function AudioArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
27
+ const _r2 = i0.ɵɵgetCurrentView();
28
+ i0.ɵɵelementStart(0, "audio", 5);
29
+ i0.ɵɵlistener("error", function AudioArtifactViewerComponent_Conditional_2_Template_audio_error_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onMediaError()); });
30
+ i0.ɵɵelementEnd();
31
+ } if (rf & 2) {
32
+ const ctx_r0 = i0.ɵɵnextContext();
33
+ i0.ɵɵproperty("src", ctx_r0.audioUrl);
34
+ } }
35
+ function AudioArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
36
+ i0.ɵɵelementStart(0, "div", 3);
37
+ i0.ɵɵelement(1, "i", 6);
38
+ i0.ɵɵelementStart(2, "span");
39
+ i0.ɵɵtext(3, "Loading audio\u2026");
40
+ i0.ɵɵelementEnd()();
41
+ } }
42
+ /**
43
+ * Minimal full-size viewer plugin for audio artifacts. Renders an `<audio controls>` player.
44
+ * Supports both storage modes (see {@link VideoArtifactViewerComponent} for the same contract).
45
+ *
46
+ * Deliberately minimal — the inline experience lives in {@link AudioArtifactPreviewComponent}
47
+ * (exposed via {@link previewComponentType}); this satisfies the required `componentType`.
48
+ */
49
+ let AudioArtifactViewerComponent = class AudioArtifactViewerComponent extends BaseArtifactViewerPluginComponent {
50
+ fileService = inject(ArtifactFileService);
51
+ cdr = inject(ChangeDetectorRef);
52
+ audioUrl = '';
53
+ errorMessage = '';
54
+ /**
55
+ * Inline-preview component for audio artifacts ({@link IArtifactViewerPluginPreviewStatics}).
56
+ * STATIC so the resolver reads it off the constructor without instantiating this DI-using component.
57
+ */
58
+ static PreviewComponentType = AudioArtifactPreviewComponent;
59
+ /** Matches audio artifacts by type name or `audio/*` MIME. STATIC — drives preview resolution. */
60
+ static CanHandlePreview(artifactTypeName, contentType) {
61
+ const mime = (contentType ?? '').toLowerCase();
62
+ if (mime.startsWith('audio/')) {
63
+ return true;
64
+ }
65
+ return (artifactTypeName ?? '').trim().toLowerCase() === 'audio';
66
+ }
67
+ get hasDisplayContent() {
68
+ return true;
69
+ }
70
+ async ngOnInit() {
71
+ try {
72
+ if (this.artifactVersion?.ContentMode === 'File' && this.artifactVersion.ID) {
73
+ this.audioUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);
74
+ }
75
+ else {
76
+ const content = this.artifactVersion?.Content;
77
+ if (content) {
78
+ this.audioUrl = content;
79
+ }
80
+ else {
81
+ this.errorMessage = 'This artifact has no audio content.';
82
+ }
83
+ }
84
+ }
85
+ catch (err) {
86
+ const detail = err instanceof Error ? err.message : String(err);
87
+ this.errorMessage = `Could not load audio: ${detail}`;
88
+ }
89
+ this.cdr.markForCheck();
90
+ }
91
+ onMediaError() {
92
+ this.errorMessage = 'The audio could not be played. It may be corrupt or in an unsupported format.';
93
+ this.audioUrl = '';
94
+ this.cdr.markForCheck();
95
+ }
96
+ GetCurrentStateSnapshot() {
97
+ if (!this.artifactVersion) {
98
+ return null;
99
+ }
100
+ const snap = new DataSnapshot();
101
+ snap.title = this.getDisplayTitle() ?? undefined;
102
+ snap.interpretation = `Audio (${this.artifactVersion.MimeType ?? 'audio'}).`;
103
+ snap.custom = {
104
+ mimeType: this.artifactVersion.MimeType ?? undefined,
105
+ fileName: this.artifactVersion.FileName ?? undefined,
106
+ contentMode: this.artifactVersion.ContentMode,
107
+ };
108
+ return snap;
109
+ }
110
+ static ɵfac = /*@__PURE__*/ (() => { let ɵAudioArtifactViewerComponent_BaseFactory; return function AudioArtifactViewerComponent_Factory(__ngFactoryType__) { return (ɵAudioArtifactViewerComponent_BaseFactory || (ɵAudioArtifactViewerComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AudioArtifactViewerComponent)))(__ngFactoryType__ || AudioArtifactViewerComponent); }; })();
111
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AudioArtifactViewerComponent, selectors: [["mj-audio-artifact-viewer"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 4, vars: 2, consts: [[1, "audio-viewer", 3, "ngClass"], [1, "audio-viewer__state", "audio-viewer__state--error"], ["controls", "", "preload", "metadata", 1, "audio-viewer__audio", 3, "src"], [1, "audio-viewer__state"], [1, "fa-solid", "fa-exclamation-triangle"], ["controls", "", "preload", "metadata", 1, "audio-viewer__audio", 3, "error", "src"], [1, "fa-solid", "fa-spinner", "fa-spin"]], template: function AudioArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
112
+ i0.ɵɵelementStart(0, "div", 0);
113
+ i0.ɵɵconditionalCreate(1, AudioArtifactViewerComponent_Conditional_1_Template, 4, 1, "div", 1)(2, AudioArtifactViewerComponent_Conditional_2_Template, 1, 1, "audio", 2)(3, AudioArtifactViewerComponent_Conditional_3_Template, 4, 0, "div", 3);
114
+ i0.ɵɵelementEnd();
115
+ } if (rf & 2) {
116
+ i0.ɵɵproperty("ngClass", ctx.cssClass);
117
+ i0.ɵɵadvance();
118
+ i0.ɵɵconditional(ctx.errorMessage ? 1 : ctx.audioUrl ? 2 : 3);
119
+ } }, dependencies: [i1.NgClass], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n }\n\n .audio-viewer[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n background: var(--mj-bg-surface-sunken);\n }\n\n .audio-viewer__audio[_ngcontent-%COMP%] {\n width: 100%;\n max-width: 480px;\n }\n\n .audio-viewer__state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .audio-viewer__state--error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n }"] });
120
+ };
121
+ AudioArtifactViewerComponent = __decorate([
122
+ RegisterClass(BaseArtifactViewerPluginComponent, 'AudioArtifactViewerPlugin')
123
+ ], AudioArtifactViewerComponent);
124
+ export { AudioArtifactViewerComponent };
125
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AudioArtifactViewerComponent, [{
126
+ type: Component,
127
+ args: [{ standalone: false, selector: 'mj-audio-artifact-viewer', template: `
128
+ <div class="audio-viewer" [ngClass]="cssClass">
129
+ @if (errorMessage) {
130
+ <div class="audio-viewer__state audio-viewer__state--error">
131
+ <i class="fa-solid fa-exclamation-triangle"></i>
132
+ <span>{{ errorMessage }}</span>
133
+ </div>
134
+ } @else if (audioUrl) {
135
+ <audio class="audio-viewer__audio" controls preload="metadata" [src]="audioUrl" (error)="onMediaError()"></audio>
136
+ } @else {
137
+ <div class="audio-viewer__state">
138
+ <i class="fa-solid fa-spinner fa-spin"></i>
139
+ <span>Loading audio…</span>
140
+ </div>
141
+ }
142
+ </div>
143
+ `, styles: ["\n :host {\n display: block;\n height: 100%;\n }\n\n .audio-viewer {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n background: var(--mj-bg-surface-sunken);\n }\n\n .audio-viewer__audio {\n width: 100%;\n max-width: 480px;\n }\n\n .audio-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .audio-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n "] }]
144
+ }], null, null); })();
145
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AudioArtifactViewerComponent, { className: "AudioArtifactViewerComponent", filePath: "src/lib/components/plugins/audio-artifact-viewer.component.ts", lineNumber: 73 }); })();
146
+ //# sourceMappingURL=audio-artifact-viewer.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/audio-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, inject, OnInit, Type } from '@angular/core';\nimport { DataSnapshot } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';\nimport { ArtifactFileService } from '../../services/artifact-file.service';\nimport { IArtifactPreviewComponent } from '../../interfaces/artifact-viewer-plugin.interface';\nimport { AudioArtifactPreviewComponent } from '../previews/audio-artifact-preview.component';\n\n/**\n * Minimal full-size viewer plugin for audio artifacts. Renders an `<audio controls>` player.\n * Supports both storage modes (see {@link VideoArtifactViewerComponent} for the same contract).\n *\n * Deliberately minimal — the inline experience lives in {@link AudioArtifactPreviewComponent}\n * (exposed via {@link previewComponentType}); this satisfies the required `componentType`.\n */\n@Component({\n standalone: false,\n selector: 'mj-audio-artifact-viewer',\n template: `\n <div class=\"audio-viewer\" [ngClass]=\"cssClass\">\n @if (errorMessage) {\n <div class=\"audio-viewer__state audio-viewer__state--error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n </div>\n } @else if (audioUrl) {\n <audio class=\"audio-viewer__audio\" controls preload=\"metadata\" [src]=\"audioUrl\" (error)=\"onMediaError()\"></audio>\n } @else {\n <div class=\"audio-viewer__state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading audio…</span>\n </div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n\n .audio-viewer {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n background: var(--mj-bg-surface-sunken);\n }\n\n .audio-viewer__audio {\n width: 100%;\n max-width: 480px;\n }\n\n .audio-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .audio-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n `,\n ],\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'AudioArtifactViewerPlugin')\nexport class AudioArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {\n private readonly fileService = inject(ArtifactFileService);\n private readonly cdr = inject(ChangeDetectorRef);\n\n public audioUrl = '';\n public errorMessage = '';\n\n /**\n * Inline-preview component for audio artifacts ({@link IArtifactViewerPluginPreviewStatics}).\n * STATIC so the resolver reads it off the constructor without instantiating this DI-using component.\n */\n public static readonly PreviewComponentType: Type<IArtifactPreviewComponent> = AudioArtifactPreviewComponent;\n\n /** Matches audio artifacts by type name or `audio/*` MIME. STATIC — drives preview resolution. */\n public static CanHandlePreview(artifactTypeName: string, contentType?: string): boolean {\n const mime = (contentType ?? '').toLowerCase();\n if (mime.startsWith('audio/')) {\n return true;\n }\n return (artifactTypeName ?? '').trim().toLowerCase() === 'audio';\n }\n\n public override get hasDisplayContent(): boolean {\n return true;\n }\n\n async ngOnInit(): Promise<void> {\n try {\n if (this.artifactVersion?.ContentMode === 'File' && this.artifactVersion.ID) {\n this.audioUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);\n } else {\n const content = this.artifactVersion?.Content;\n if (content) {\n this.audioUrl = content;\n } else {\n this.errorMessage = 'This artifact has no audio content.';\n }\n }\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n this.errorMessage = `Could not load audio: ${detail}`;\n }\n this.cdr.markForCheck();\n }\n\n public onMediaError(): void {\n this.errorMessage = 'The audio could not be played. It may be corrupt or in an unsupported format.';\n this.audioUrl = '';\n this.cdr.markForCheck();\n }\n\n public override GetCurrentStateSnapshot(): DataSnapshot | null {\n if (!this.artifactVersion) {\n return null;\n }\n const snap = new DataSnapshot();\n snap.title = this.getDisplayTitle() ?? undefined;\n snap.interpretation = `Audio (${this.artifactVersion.MimeType ?? 'audio'}).`;\n snap.custom = {\n mimeType: this.artifactVersion.MimeType ?? undefined,\n fileName: this.artifactVersion.FileName ?? undefined,\n contentMode: this.artifactVersion.ContentMode,\n };\n return snap;\n }\n}\n"]}
@@ -1,7 +1,8 @@
1
- import { ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';
1
+ import { ChangeDetectorRef, OnDestroy, OnInit, Type } from '@angular/core';
2
2
  import { DataSnapshot } from '@memberjunction/core';
3
3
  import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
4
4
  import { ArtifactFileService } from '../../services/artifact-file.service';
5
+ import { IArtifactPreviewComponent } from '../../interfaces/artifact-viewer-plugin.interface';
5
6
  import * as i0 from "@angular/core";
6
7
  /**
7
8
  * Lookup table for synthesizing a download filename when the artifact version
@@ -79,6 +80,15 @@ export declare class ImageArtifactViewerComponent extends BaseArtifactViewerPlug
79
80
  */
80
81
  private inlineObjectUrl;
81
82
  constructor(fileService: ArtifactFileService, cdr: ChangeDetectorRef);
83
+ /**
84
+ * Inline-preview component for image artifacts ({@link IArtifactViewerPluginPreviewStatics}).
85
+ * STATIC so the resolver can read it off the registered constructor WITHOUT instantiating this
86
+ * Angular component. Surfaces a contained thumbnail inside conversation message cards; the full
87
+ * viewer (this class) remains the required `componentType`.
88
+ */
89
+ static readonly PreviewComponentType: Type<IArtifactPreviewComponent>;
90
+ /** Matches raster image artifacts by type name or `image/*` MIME. STATIC — drives preview resolution. */
91
+ static CanHandlePreview(artifactTypeName: string, contentType?: string): boolean;
82
92
  /** Opt in to the wrapper's "Display" tab — without this the panel only shows Details/JSON. */
83
93
  get hasDisplayContent(): boolean;
84
94
  /** Filename surfaced by the toolbar in the center of the bar. */
@@ -1 +1 @@
1
- {"version":3,"file":"image-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/image-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;;AAE3E;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWpE,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAWnH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBA0Ja,4BAA6B,SAAQ,iCAAkC,YAAW,MAAM,EAAE,SAAS;IA4BxG,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,GAAG;IA5Bf,6GAA6G;IACtG,QAAQ,SAAM;IAErB,yFAAyF;IAClF,SAAS,UAAQ;IAExB,yEAAyE;IAClE,aAAa,UAAS;IAE7B,sFAAsF;IAC/E,YAAY,SAAM;IAEzB,oGAAoG;IAC7F,WAAW,EAAE,KAAK,GAAG,QAAQ,CAAS;IAE7C,iFAAiF;IAC1E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAuB;gBAGlC,WAAW,EAAE,mBAAmB,EAChC,GAAG,EAAE,iBAAiB;IAKlC,8FAA8F;IAC9F,IAAoB,iBAAiB,IAAI,OAAO,CAE/C;IAED,iEAAiE;IACjE,IAAW,eAAe,IAAI,MAAM,CAEnC;IAED,4EAA4E;IAC5E,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,WAAW,IAAI,IAAI;IAWnB;;;;OAIG;YACW,eAAe;IAqCtB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQ/B,YAAY,IAAI,IAAI;IAI3B,OAAO,CAAC,SAAS;IAQV,iBAAiB,IAAI,IAAI;IAShC;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBxC;;;;OAIG;IACI,OAAO,IAAI,IAAI;IAQtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;;;OAIG;IACa,uBAAuB,IAAI,YAAY,GAAG,IAAI;yCAtMrD,4BAA4B;2CAA5B,4BAA4B;CA4NxC"}
1
+ {"version":3,"file":"image-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/image-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,mDAAmD,CAAC;;AAG9F;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWpE,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAWnH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBA0Ja,4BAA6B,SAAQ,iCAAkC,YAAW,MAAM,EAAE,SAAS;IA4BxG,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,GAAG;IA5Bf,6GAA6G;IACtG,QAAQ,SAAM;IAErB,yFAAyF;IAClF,SAAS,UAAQ;IAExB,yEAAyE;IAClE,aAAa,UAAS;IAE7B,sFAAsF;IAC/E,YAAY,SAAM;IAEzB,oGAAoG;IAC7F,WAAW,EAAE,KAAK,GAAG,QAAQ,CAAS;IAE7C,iFAAiF;IAC1E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE3C;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAuB;gBAGlC,WAAW,EAAE,mBAAmB,EAChC,GAAG,EAAE,iBAAiB;IAKlC;;;;;OAKG;IACH,gBAAuB,oBAAoB,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAiC;IAE7G,yGAAyG;WAC3F,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO;IAQvF,8FAA8F;IAC9F,IAAoB,iBAAiB,IAAI,OAAO,CAE/C;IAED,iEAAiE;IACjE,IAAW,eAAe,IAAI,MAAM,CAEnC;IAED,4EAA4E;IAC5E,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,WAAW,IAAI,IAAI;IAWnB;;;;OAIG;YACW,eAAe;IAqCtB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQ/B,YAAY,IAAI,IAAI;IAI3B,OAAO,CAAC,SAAS;IAQV,iBAAiB,IAAI,IAAI;IAShC;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBxC;;;;OAIG;IACI,OAAO,IAAI,IAAI;IAQtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;;;OAIG;IACa,uBAAuB,IAAI,YAAY,GAAG,IAAI;yCAvNrD,4BAA4B;2CAA5B,4BAA4B;CA6OxC"}
@@ -8,6 +8,7 @@ import { Component } from '@angular/core';
8
8
  import { DataSnapshot } from '@memberjunction/core';
9
9
  import { RegisterClass } from '@memberjunction/global';
10
10
  import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
11
+ import { ImageArtifactPreviewComponent } from '../previews/image-artifact-preview.component';
11
12
  import * as i0 from "@angular/core";
12
13
  import * as i1 from "../../services/artifact-file.service";
13
14
  import * as i2 from "@angular/common";
@@ -160,6 +161,21 @@ let ImageArtifactViewerComponent = class ImageArtifactViewerComponent extends Ba
160
161
  this.fileService = fileService;
161
162
  this.cdr = cdr;
162
163
  }
164
+ /**
165
+ * Inline-preview component for image artifacts ({@link IArtifactViewerPluginPreviewStatics}).
166
+ * STATIC so the resolver can read it off the registered constructor WITHOUT instantiating this
167
+ * Angular component. Surfaces a contained thumbnail inside conversation message cards; the full
168
+ * viewer (this class) remains the required `componentType`.
169
+ */
170
+ static PreviewComponentType = ImageArtifactPreviewComponent;
171
+ /** Matches raster image artifacts by type name or `image/*` MIME. STATIC — drives preview resolution. */
172
+ static CanHandlePreview(artifactTypeName, contentType) {
173
+ const mime = (contentType ?? '').toLowerCase();
174
+ if (mime.startsWith('image/')) {
175
+ return true;
176
+ }
177
+ return (artifactTypeName ?? '').trim().toLowerCase() === 'image';
178
+ }
163
179
  /** Opt in to the wrapper's "Display" tab — without this the panel only shows Details/JSON. */
164
180
  get hasDisplayContent() {
165
181
  return true;
@@ -409,5 +425,5 @@ export { ImageArtifactViewerComponent };
409
425
  </div>
410
426
  `, styles: ["\n :host {\n display: block;\n height: 100%;\n }\n\n .image-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n position: relative;\n }\n\n .image-viewer__body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n }\n\n /* \"Actual size\" mode: allow scrolling from the top-left so large images\n don't get re-centered as the user pans. The browser handles overflow. */\n .image-viewer__body--actual {\n align-items: flex-start;\n justify-content: flex-start;\n }\n\n .image-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .image-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .image-viewer__img {\n /* Fit mode (default): never exceeds the panel, preserves aspect ratio. */\n max-width: 100%;\n max-height: 100%;\n width: auto;\n height: auto;\n object-fit: contain;\n cursor: zoom-in;\n background: var(--mj-bg-surface);\n border-radius: 4px;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 12%, transparent);\n transition: opacity 0.18s ease-out;\n }\n\n /* Actual-size mode: render at the image's intrinsic dimensions so the body\n scroll-area handles anything larger than the panel. */\n .image-viewer__img--actual {\n max-width: none;\n max-height: none;\n width: auto;\n height: auto;\n cursor: zoom-out;\n }\n\n /* Pre-load: keep the <img> in the DOM (so the browser decodes it) but\n invisible and out of layout flow so the spinner sits centered. */\n .image-viewer__img--hidden {\n opacity: 0;\n position: absolute;\n pointer-events: none;\n }\n\n .image-viewer__dimensions {\n position: absolute;\n bottom: 8px;\n right: 12px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-bg-surface-elevated) 90%, transparent);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n color: var(--mj-text-muted);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n pointer-events: none;\n /* Don't intercept clicks meant for the image-toggle. */\n }\n "] }]
411
427
  }], () => [{ type: i1.ArtifactFileService }, { type: i0.ChangeDetectorRef }], null); })();
412
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ImageArtifactViewerComponent, { className: "ImageArtifactViewerComponent", filePath: "src/lib/components/plugins/image-artifact-viewer.component.ts", lineNumber: 238 }); })();
428
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ImageArtifactViewerComponent, { className: "ImageArtifactViewerComponent", filePath: "src/lib/components/plugins/image-artifact-viewer.component.ts", lineNumber: 240 }); })();
413
429
  //# sourceMappingURL=image-artifact-viewer.component.js.map