@memberjunction/ng-artifacts 6.2.0-edge.0 → 6.2.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/components/artifact-message-card.component.d.ts +60 -8
- package/dist/lib/components/artifact-message-card.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-message-card.component.js +135 -39
- package/dist/lib/components/artifact-message-card.component.js.map +1 -1
- package/dist/lib/components/artifact-type-plugin-viewer.component.d.ts +58 -6
- package/dist/lib/components/artifact-type-plugin-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-type-plugin-viewer.component.js +107 -29
- package/dist/lib/components/artifact-type-plugin-viewer.component.js.map +1 -1
- package/dist/lib/components/artifact-version-download.d.ts +5 -3
- package/dist/lib/components/artifact-version-download.d.ts.map +1 -1
- package/dist/lib/components/artifact-version-download.js +11 -7
- package/dist/lib/components/artifact-version-download.js.map +1 -1
- package/dist/lib/components/artifact-version-history.component.d.ts +74 -8
- package/dist/lib/components/artifact-version-history.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-version-history.component.js +152 -34
- package/dist/lib/components/artifact-version-history.component.js.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.d.ts +328 -35
- package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.js +701 -213
- package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
- package/dist/lib/components/file-artifact-toolbar.component.d.ts +112 -10
- package/dist/lib/components/file-artifact-toolbar.component.d.ts.map +1 -1
- package/dist/lib/components/file-artifact-toolbar.component.js +197 -23
- package/dist/lib/components/file-artifact-toolbar.component.js.map +1 -1
- package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts +9 -1
- package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/audio-artifact-viewer.component.js +20 -4
- package/dist/lib/components/plugins/audio-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts +23 -5
- package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/code-artifact-viewer.component.js +69 -31
- package/dist/lib/components/plugins/code-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts +136 -18
- package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.js +285 -97
- package/dist/lib/components/plugins/component-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.d.ts +2 -2
- package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js +7 -7
- package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js.map +1 -1
- package/dist/lib/components/plugins/component-permission-evaluation.d.ts +2 -0
- package/dist/lib/components/plugins/component-permission-evaluation.d.ts.map +1 -1
- package/dist/lib/components/plugins/component-permission-evaluation.js +6 -2
- package/dist/lib/components/plugins/component-permission-evaluation.js.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts +17 -6
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.js +32 -22
- package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.d.ts +56 -4
- package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js +142 -36
- package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts +14 -2
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js +32 -8
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts +17 -3
- package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/html-artifact-viewer.component.js +36 -8
- package/dist/lib/components/plugins/html-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/image-artifact-viewer.component.d.ts +41 -5
- package/dist/lib/components/plugins/image-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/image-artifact-viewer.component.js +102 -30
- package/dist/lib/components/plugins/image-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts +33 -5
- package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/json-artifact-viewer.component.js +89 -33
- package/dist/lib/components/plugins/json-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts +12 -2
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.js +28 -8
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts +4 -0
- package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js +14 -6
- package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts +47 -5
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js +129 -45
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/save-query-dialog.component.d.ts +5 -1
- package/dist/lib/components/plugins/save-query-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/save-query-dialog.component.js +12 -4
- package/dist/lib/components/plugins/save-query-dialog.component.js.map +1 -1
- package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts +17 -3
- package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/svg-artifact-viewer.component.js +36 -8
- package/dist/lib/components/plugins/svg-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts +9 -1
- package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/video-artifact-viewer.component.js +20 -4
- package/dist/lib/components/plugins/video-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts +28 -4
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js +64 -16
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/previews/audio-artifact-preview.component.d.ts +9 -1
- package/dist/lib/components/previews/audio-artifact-preview.component.d.ts.map +1 -1
- package/dist/lib/components/previews/audio-artifact-preview.component.js +20 -4
- package/dist/lib/components/previews/audio-artifact-preview.component.js.map +1 -1
- package/dist/lib/components/previews/image-artifact-preview.component.d.ts +16 -2
- package/dist/lib/components/previews/image-artifact-preview.component.d.ts.map +1 -1
- package/dist/lib/components/previews/image-artifact-preview.component.js +35 -7
- package/dist/lib/components/previews/image-artifact-preview.component.js.map +1 -1
- package/dist/lib/components/previews/video-artifact-preview.component.d.ts +9 -1
- package/dist/lib/components/previews/video-artifact-preview.component.d.ts.map +1 -1
- package/dist/lib/components/previews/video-artifact-preview.component.js +20 -4
- package/dist/lib/components/previews/video-artifact-preview.component.js.map +1 -1
- package/dist/lib/interfaces/artifact-viewer-plugin.interface.js.map +1 -1
- package/dist/lib/services/artifact-file.service.d.ts +8 -0
- package/dist/lib/services/artifact-file.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-file.service.js +22 -6
- package/dist/lib/services/artifact-file.service.js.map +1 -1
- package/dist/lib/services/artifact-icon.service.d.ts +2 -0
- package/dist/lib/services/artifact-icon.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-icon.service.js +5 -1
- package/dist/lib/services/artifact-icon.service.js.map +1 -1
- package/dist/lib/services/artifact-preview-resolver.service.d.ts +2 -0
- package/dist/lib/services/artifact-preview-resolver.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-preview-resolver.service.js +5 -1
- package/dist/lib/services/artifact-preview-resolver.service.js.map +1 -1
- package/dist/lib/snapshot-helpers.d.ts +10 -0
- package/dist/lib/snapshot-helpers.d.ts.map +1 -1
- package/dist/lib/snapshot-helpers.js +25 -5
- package/dist/lib/snapshot-helpers.js.map +1 -1
- package/package.json +21 -21
|
@@ -606,52 +606,308 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
606
606
|
artifactIconService;
|
|
607
607
|
artifactFileService;
|
|
608
608
|
artifactId;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
609
|
+
CurrentUser;
|
|
610
|
+
/** @deprecated Use {@link CurrentUser}. */
|
|
611
|
+
set currentUser(value) {
|
|
612
|
+
this.CurrentUser = value;
|
|
613
|
+
}
|
|
614
|
+
/** @deprecated Use {@link CurrentUser}. */
|
|
615
|
+
get currentUser() {
|
|
616
|
+
return this.CurrentUser;
|
|
617
|
+
}
|
|
618
|
+
EnvironmentId;
|
|
619
|
+
/** @deprecated Use {@link EnvironmentId}. */
|
|
620
|
+
set environmentId(value) {
|
|
621
|
+
this.EnvironmentId = value;
|
|
622
|
+
}
|
|
623
|
+
/** @deprecated Use {@link EnvironmentId}. */
|
|
624
|
+
get environmentId() {
|
|
625
|
+
return this.EnvironmentId;
|
|
626
|
+
}
|
|
627
|
+
VersionNumber;
|
|
628
|
+
/** @deprecated Use {@link VersionNumber}. */
|
|
629
|
+
set versionNumber(value) {
|
|
630
|
+
this.VersionNumber = value;
|
|
631
|
+
}
|
|
632
|
+
/** @deprecated Use {@link VersionNumber}. */
|
|
633
|
+
get versionNumber() {
|
|
634
|
+
return this.VersionNumber;
|
|
635
|
+
} // Version to display
|
|
636
|
+
ShowSaveToCollection = true;
|
|
637
|
+
/** @deprecated Use {@link ShowSaveToCollection}. */
|
|
638
|
+
set showSaveToCollection(value) {
|
|
639
|
+
this.ShowSaveToCollection = value;
|
|
640
|
+
}
|
|
641
|
+
/** @deprecated Use {@link ShowSaveToCollection}. */
|
|
642
|
+
get showSaveToCollection() {
|
|
643
|
+
return this.ShowSaveToCollection;
|
|
644
|
+
} // Control whether Save to Collection button is shown
|
|
645
|
+
ShowHeader = true;
|
|
646
|
+
/** @deprecated Use {@link ShowHeader}. */
|
|
647
|
+
set showHeader(value) {
|
|
648
|
+
this.ShowHeader = value;
|
|
649
|
+
}
|
|
650
|
+
/** @deprecated Use {@link ShowHeader}. */
|
|
651
|
+
get showHeader() {
|
|
652
|
+
return this.ShowHeader;
|
|
653
|
+
} // Control whether the header section is shown
|
|
654
|
+
ShowTabs = true;
|
|
655
|
+
/** @deprecated Use {@link ShowTabs}. */
|
|
656
|
+
set showTabs(value) {
|
|
657
|
+
this.ShowTabs = value;
|
|
658
|
+
}
|
|
659
|
+
/** @deprecated Use {@link ShowTabs}. */
|
|
660
|
+
get showTabs() {
|
|
661
|
+
return this.ShowTabs;
|
|
662
|
+
} // Control whether the tab navigation is shown (false = show only Display tab content)
|
|
663
|
+
ShowCloseButton = true;
|
|
664
|
+
/** @deprecated Use {@link ShowCloseButton}. */
|
|
665
|
+
set showCloseButton(value) {
|
|
666
|
+
this.ShowCloseButton = value;
|
|
667
|
+
}
|
|
668
|
+
/** @deprecated Use {@link ShowCloseButton}. */
|
|
669
|
+
get showCloseButton() {
|
|
670
|
+
return this.ShowCloseButton;
|
|
671
|
+
} // Control whether the close button is shown in header
|
|
672
|
+
ShowMaximizeButton = true;
|
|
673
|
+
/** @deprecated Use {@link ShowMaximizeButton}. */
|
|
674
|
+
set showMaximizeButton(value) {
|
|
675
|
+
this.ShowMaximizeButton = value;
|
|
676
|
+
}
|
|
677
|
+
/** @deprecated Use {@link ShowMaximizeButton}. */
|
|
678
|
+
get showMaximizeButton() {
|
|
679
|
+
return this.ShowMaximizeButton;
|
|
680
|
+
} // Control whether the maximize/restore button is shown in header
|
|
681
|
+
RefreshTrigger;
|
|
682
|
+
/** @deprecated Use {@link RefreshTrigger}. */
|
|
683
|
+
set refreshTrigger(value) {
|
|
684
|
+
this.RefreshTrigger = value;
|
|
685
|
+
}
|
|
686
|
+
/** @deprecated Use {@link RefreshTrigger}. */
|
|
687
|
+
get refreshTrigger() {
|
|
688
|
+
return this.RefreshTrigger;
|
|
689
|
+
}
|
|
690
|
+
ViewContext = null;
|
|
691
|
+
/** @deprecated Use {@link ViewContext}. */
|
|
692
|
+
set viewContext(value) {
|
|
693
|
+
this.ViewContext = value;
|
|
694
|
+
}
|
|
695
|
+
/** @deprecated Use {@link ViewContext}. */
|
|
696
|
+
get viewContext() {
|
|
697
|
+
return this.ViewContext;
|
|
698
|
+
} // Where artifact is being viewed
|
|
699
|
+
ContextCollectionId;
|
|
700
|
+
/** @deprecated Use {@link ContextCollectionId}. */
|
|
701
|
+
set contextCollectionId(value) {
|
|
702
|
+
this.ContextCollectionId = value;
|
|
703
|
+
}
|
|
704
|
+
/** @deprecated Use {@link ContextCollectionId}. */
|
|
705
|
+
get contextCollectionId() {
|
|
706
|
+
return this.ContextCollectionId;
|
|
707
|
+
} // If viewing in collection, which collection
|
|
620
708
|
canShare; // Whether user can share this artifact
|
|
621
709
|
canEdit; // Whether user can edit this artifact
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
710
|
+
IsMaximized = false;
|
|
711
|
+
/** @deprecated Use {@link IsMaximized}. */
|
|
712
|
+
set isMaximized(value) {
|
|
713
|
+
this.IsMaximized = value;
|
|
714
|
+
}
|
|
715
|
+
/** @deprecated Use {@link IsMaximized}. */
|
|
716
|
+
get isMaximized() {
|
|
717
|
+
return this.IsMaximized;
|
|
718
|
+
} // Whether the panel is currently maximized
|
|
719
|
+
Closed = new EventEmitter();
|
|
720
|
+
/**
|
|
721
|
+
* @deprecated Use {@link Closed}.
|
|
722
|
+
*
|
|
723
|
+
* The same emitter under the old binding name, so a template still binding
|
|
724
|
+
* (closed) keeps working. Must stay AFTER Closed: class fields
|
|
725
|
+
* initialise in order, and the other way round this captures undefined.
|
|
726
|
+
*/
|
|
727
|
+
closed = this.Closed;
|
|
728
|
+
SaveToCollectionRequested = new EventEmitter();
|
|
729
|
+
/**
|
|
730
|
+
* @deprecated Use {@link SaveToCollectionRequested}.
|
|
731
|
+
*
|
|
732
|
+
* The same emitter under the old binding name, so a template still binding
|
|
733
|
+
* (saveToCollectionRequested) keeps working. Must stay AFTER SaveToCollectionRequested: class fields
|
|
734
|
+
* initialise in order, and the other way round this captures undefined.
|
|
735
|
+
*/
|
|
736
|
+
saveToCollectionRequested = this.SaveToCollectionRequested;
|
|
737
|
+
NavigateToLink = new EventEmitter();
|
|
738
|
+
/**
|
|
739
|
+
* @deprecated Use {@link NavigateToLink}.
|
|
740
|
+
*
|
|
741
|
+
* The same emitter under the old binding name, so a template still binding
|
|
742
|
+
* (navigateToLink) keeps working. Must stay AFTER NavigateToLink: class fields
|
|
743
|
+
* initialise in order, and the other way round this captures undefined.
|
|
744
|
+
*/
|
|
745
|
+
navigateToLink = this.NavigateToLink;
|
|
746
|
+
ShareRequested = new EventEmitter();
|
|
747
|
+
/**
|
|
748
|
+
* @deprecated Use {@link ShareRequested}.
|
|
749
|
+
*
|
|
750
|
+
* The same emitter under the old binding name, so a template still binding
|
|
751
|
+
* (shareRequested) keeps working. Must stay AFTER ShareRequested: class fields
|
|
752
|
+
* initialise in order, and the other way round this captures undefined.
|
|
753
|
+
*/
|
|
754
|
+
shareRequested = this.ShareRequested; // Emits artifactId when share is clicked
|
|
755
|
+
MaximizeToggled = new EventEmitter();
|
|
756
|
+
/**
|
|
757
|
+
* @deprecated Use {@link MaximizeToggled}.
|
|
758
|
+
*
|
|
759
|
+
* The same emitter under the old binding name, so a template still binding
|
|
760
|
+
* (maximizeToggled) keeps working. Must stay AFTER MaximizeToggled: class fields
|
|
761
|
+
* initialise in order, and the other way round this captures undefined.
|
|
762
|
+
*/
|
|
763
|
+
maximizeToggled = this.MaximizeToggled; // Emits when user clicks maximize/restore button
|
|
764
|
+
OpenEntityRecord = new EventEmitter();
|
|
765
|
+
/**
|
|
766
|
+
* @deprecated Use {@link OpenEntityRecord}.
|
|
767
|
+
*
|
|
768
|
+
* The same emitter under the old binding name, so a template still binding
|
|
769
|
+
* (openEntityRecord) keeps working. Must stay AFTER OpenEntityRecord: class fields
|
|
770
|
+
* initialise in order, and the other way round this captures undefined.
|
|
771
|
+
*/
|
|
772
|
+
openEntityRecord = this.OpenEntityRecord;
|
|
629
773
|
navigationRequest = new EventEmitter();
|
|
630
|
-
|
|
774
|
+
AnalyzeRequested = new EventEmitter();
|
|
775
|
+
/**
|
|
776
|
+
* @deprecated Use {@link AnalyzeRequested}.
|
|
777
|
+
*
|
|
778
|
+
* The same emitter under the old binding name, so a template still binding
|
|
779
|
+
* (analyzeRequested) keeps working. Must stay AFTER AnalyzeRequested: class fields
|
|
780
|
+
* initialise in order, and the other way round this captures undefined.
|
|
781
|
+
*/
|
|
782
|
+
analyzeRequested = this.AnalyzeRequested;
|
|
631
783
|
/**
|
|
632
784
|
* "Apply to my form" — bubbled from the form-aware component-artifact-viewer
|
|
633
785
|
* branch. Carries the spec + entity. Consumer (chat message card, etc.)
|
|
634
786
|
* confirms and invokes the Create-or-Modify Interactive Form action.
|
|
635
787
|
*/
|
|
636
|
-
|
|
637
|
-
|
|
788
|
+
ApplyFormRequested = new EventEmitter();
|
|
789
|
+
/**
|
|
790
|
+
* @deprecated Use {@link ApplyFormRequested}.
|
|
791
|
+
*
|
|
792
|
+
* The same emitter under the old binding name, so a template still binding
|
|
793
|
+
* (applyFormRequested) keeps working. Must stay AFTER ApplyFormRequested: class fields
|
|
794
|
+
* initialise in order, and the other way round this captures undefined.
|
|
795
|
+
*/
|
|
796
|
+
applyFormRequested = this.ApplyFormRequested;
|
|
797
|
+
PluginViewer;
|
|
798
|
+
/** @deprecated Use {@link PluginViewer}. */
|
|
799
|
+
get pluginViewer() {
|
|
800
|
+
return this.PluginViewer;
|
|
801
|
+
}
|
|
802
|
+
/** @deprecated Use {@link PluginViewer}. */
|
|
803
|
+
set pluginViewer(value) {
|
|
804
|
+
this.PluginViewer = value;
|
|
805
|
+
}
|
|
638
806
|
destroy$ = new Subject();
|
|
639
807
|
artifactLoadToken = 0;
|
|
640
|
-
|
|
808
|
+
Artifact = null;
|
|
809
|
+
/** @deprecated Use {@link Artifact}. */
|
|
810
|
+
get artifact() {
|
|
811
|
+
return this.Artifact;
|
|
812
|
+
}
|
|
813
|
+
/** @deprecated Use {@link Artifact}. */
|
|
814
|
+
set artifact(value) {
|
|
815
|
+
this.Artifact = value;
|
|
816
|
+
}
|
|
641
817
|
artifactVersion = null;
|
|
642
|
-
|
|
643
|
-
|
|
818
|
+
AllVersions = [];
|
|
819
|
+
/** @deprecated Use {@link AllVersions}. */
|
|
820
|
+
get allVersions() {
|
|
821
|
+
return this.AllVersions;
|
|
822
|
+
}
|
|
823
|
+
/** @deprecated Use {@link AllVersions}. */
|
|
824
|
+
set allVersions(value) {
|
|
825
|
+
this.AllVersions = value;
|
|
826
|
+
}
|
|
827
|
+
SelectedVersionNumber = 1;
|
|
828
|
+
/** @deprecated Use {@link SelectedVersionNumber}. */
|
|
829
|
+
get selectedVersionNumber() {
|
|
830
|
+
return this.SelectedVersionNumber;
|
|
831
|
+
}
|
|
832
|
+
/** @deprecated Use {@link SelectedVersionNumber}. */
|
|
833
|
+
set selectedVersionNumber(value) {
|
|
834
|
+
this.SelectedVersionNumber = value;
|
|
835
|
+
}
|
|
644
836
|
isLoading = true;
|
|
645
837
|
error = null;
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
838
|
+
JsonContent = '';
|
|
839
|
+
/** @deprecated Use {@link JsonContent}. */
|
|
840
|
+
get jsonContent() {
|
|
841
|
+
return this.JsonContent;
|
|
842
|
+
}
|
|
843
|
+
/** @deprecated Use {@link JsonContent}. */
|
|
844
|
+
set jsonContent(value) {
|
|
845
|
+
this.JsonContent = value;
|
|
846
|
+
}
|
|
847
|
+
ShowVersionDropdown = false;
|
|
848
|
+
/** @deprecated Use {@link ShowVersionDropdown}. */
|
|
849
|
+
get showVersionDropdown() {
|
|
850
|
+
return this.ShowVersionDropdown;
|
|
851
|
+
}
|
|
852
|
+
/** @deprecated Use {@link ShowVersionDropdown}. */
|
|
853
|
+
set showVersionDropdown(value) {
|
|
854
|
+
this.ShowVersionDropdown = value;
|
|
855
|
+
}
|
|
856
|
+
ArtifactCollections = [];
|
|
857
|
+
/** @deprecated Use {@link ArtifactCollections}. */
|
|
858
|
+
get artifactCollections() {
|
|
859
|
+
return this.ArtifactCollections;
|
|
860
|
+
}
|
|
861
|
+
/** @deprecated Use {@link ArtifactCollections}. */
|
|
862
|
+
set artifactCollections(value) {
|
|
863
|
+
this.ArtifactCollections = value;
|
|
864
|
+
} // All collections for ALL versions
|
|
865
|
+
CurrentVersionCollections = [];
|
|
866
|
+
/** @deprecated Use {@link CurrentVersionCollections}. */
|
|
867
|
+
get currentVersionCollections() {
|
|
868
|
+
return this.CurrentVersionCollections;
|
|
869
|
+
}
|
|
870
|
+
/** @deprecated Use {@link CurrentVersionCollections}. */
|
|
871
|
+
set currentVersionCollections(value) {
|
|
872
|
+
this.CurrentVersionCollections = value;
|
|
873
|
+
} // Collections containing CURRENT version only
|
|
874
|
+
PrimaryCollection = null;
|
|
875
|
+
/** @deprecated Use {@link PrimaryCollection}. */
|
|
876
|
+
get primaryCollection() {
|
|
877
|
+
return this.PrimaryCollection;
|
|
878
|
+
}
|
|
879
|
+
/** @deprecated Use {@link PrimaryCollection}. */
|
|
880
|
+
set primaryCollection(value) {
|
|
881
|
+
this.PrimaryCollection = value;
|
|
882
|
+
}
|
|
651
883
|
// Tabbed interface
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
884
|
+
ActiveTab = 'display';
|
|
885
|
+
/** @deprecated Use {@link ActiveTab}. */
|
|
886
|
+
get activeTab() {
|
|
887
|
+
return this.ActiveTab;
|
|
888
|
+
}
|
|
889
|
+
/** @deprecated Use {@link ActiveTab}. */
|
|
890
|
+
set activeTab(value) {
|
|
891
|
+
this.ActiveTab = value;
|
|
892
|
+
} // Changed to string to support dynamic tabs
|
|
893
|
+
DisplayMarkdown = null;
|
|
894
|
+
/** @deprecated Use {@link DisplayMarkdown}. */
|
|
895
|
+
get displayMarkdown() {
|
|
896
|
+
return this.DisplayMarkdown;
|
|
897
|
+
}
|
|
898
|
+
/** @deprecated Use {@link DisplayMarkdown}. */
|
|
899
|
+
set displayMarkdown(value) {
|
|
900
|
+
this.DisplayMarkdown = value;
|
|
901
|
+
}
|
|
902
|
+
DisplayHtml = null;
|
|
903
|
+
/** @deprecated Use {@link DisplayHtml}. */
|
|
904
|
+
get displayHtml() {
|
|
905
|
+
return this.DisplayHtml;
|
|
906
|
+
}
|
|
907
|
+
/** @deprecated Use {@link DisplayHtml}. */
|
|
908
|
+
set displayHtml(value) {
|
|
909
|
+
this.DisplayHtml = value;
|
|
910
|
+
}
|
|
655
911
|
/**
|
|
656
912
|
* The no-plugin fallback. An artifact type with no viewer plugin (CSV today) used to render an empty
|
|
657
913
|
* pane: the display tab only knew the extracted markdown/HTML attributes, and a file-backed version
|
|
@@ -659,35 +915,99 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
659
915
|
* text-like content (text/*, JSON, CSV) is fetched and shown as plain text — capped, so a large file
|
|
660
916
|
* is downloaded rather than rendered.
|
|
661
917
|
*/
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
918
|
+
FallbackText = null;
|
|
919
|
+
/** @deprecated Use {@link FallbackText}. */
|
|
920
|
+
get fallbackText() {
|
|
921
|
+
return this.FallbackText;
|
|
922
|
+
}
|
|
923
|
+
/** @deprecated Use {@link FallbackText}. */
|
|
924
|
+
set fallbackText(value) {
|
|
925
|
+
this.FallbackText = value;
|
|
926
|
+
}
|
|
927
|
+
FallbackTextTruncated = false;
|
|
928
|
+
/** @deprecated Use {@link FallbackTextTruncated}. */
|
|
929
|
+
get fallbackTextTruncated() {
|
|
930
|
+
return this.FallbackTextTruncated;
|
|
931
|
+
}
|
|
932
|
+
/** @deprecated Use {@link FallbackTextTruncated}. */
|
|
933
|
+
set fallbackTextTruncated(value) {
|
|
934
|
+
this.FallbackTextTruncated = value;
|
|
935
|
+
}
|
|
936
|
+
FallbackBusy = false;
|
|
937
|
+
/** @deprecated Use {@link FallbackBusy}. */
|
|
938
|
+
get fallbackBusy() {
|
|
939
|
+
return this.FallbackBusy;
|
|
940
|
+
}
|
|
941
|
+
/** @deprecated Use {@link FallbackBusy}. */
|
|
942
|
+
set fallbackBusy(value) {
|
|
943
|
+
this.FallbackBusy = value;
|
|
944
|
+
}
|
|
665
945
|
static FALLBACK_TEXT_MAX_CHARS = 200_000;
|
|
666
|
-
|
|
946
|
+
VersionAttributes = [];
|
|
947
|
+
/** @deprecated Use {@link VersionAttributes}. */
|
|
948
|
+
get versionAttributes() {
|
|
949
|
+
return this.VersionAttributes;
|
|
950
|
+
}
|
|
951
|
+
/** @deprecated Use {@link VersionAttributes}. */
|
|
952
|
+
set versionAttributes(value) {
|
|
953
|
+
this.VersionAttributes = value;
|
|
954
|
+
}
|
|
667
955
|
artifactTypeDriverClass = null;
|
|
668
956
|
/** Populated from ArtifactType.ContentCategory. Used to suppress the JSON tab for
|
|
669
957
|
* binary file-type artifacts — driven by metadata, not hardcoded plugin overrides. */
|
|
670
958
|
artifactContentCategory = null;
|
|
671
959
|
// Links tab data
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
960
|
+
OriginConversation = null;
|
|
961
|
+
/** @deprecated Use {@link OriginConversation}. */
|
|
962
|
+
get originConversation() {
|
|
963
|
+
return this.OriginConversation;
|
|
964
|
+
}
|
|
965
|
+
/** @deprecated Use {@link OriginConversation}. */
|
|
966
|
+
set originConversation(value) {
|
|
967
|
+
this.OriginConversation = value;
|
|
968
|
+
}
|
|
969
|
+
AllCollections = [];
|
|
970
|
+
/** @deprecated Use {@link AllCollections}. */
|
|
971
|
+
get allCollections() {
|
|
972
|
+
return this.AllCollections;
|
|
973
|
+
}
|
|
974
|
+
/** @deprecated Use {@link AllCollections}. */
|
|
975
|
+
set allCollections(value) {
|
|
976
|
+
this.AllCollections = value;
|
|
977
|
+
}
|
|
978
|
+
HasAccessToOriginConversation = false;
|
|
979
|
+
/** @deprecated Use {@link HasAccessToOriginConversation}. */
|
|
980
|
+
get hasAccessToOriginConversation() {
|
|
981
|
+
return this.HasAccessToOriginConversation;
|
|
982
|
+
}
|
|
983
|
+
/** @deprecated Use {@link HasAccessToOriginConversation}. */
|
|
984
|
+
set hasAccessToOriginConversation(value) {
|
|
985
|
+
this.HasAccessToOriginConversation = value;
|
|
986
|
+
}
|
|
987
|
+
OriginConversationVersionId = null;
|
|
988
|
+
/** @deprecated Use {@link OriginConversationVersionId}. */
|
|
989
|
+
get originConversationVersionId() {
|
|
990
|
+
return this.OriginConversationVersionId;
|
|
991
|
+
}
|
|
992
|
+
/** @deprecated Use {@link OriginConversationVersionId}. */
|
|
993
|
+
set originConversationVersionId(value) {
|
|
994
|
+
this.OriginConversationVersionId = value;
|
|
995
|
+
} // Version ID that came from origin conversation
|
|
676
996
|
// Dynamic tabs from plugin
|
|
677
|
-
get
|
|
997
|
+
get AllTabs() {
|
|
678
998
|
const tabs = [];
|
|
679
999
|
// Only add Display tab if there's content to display
|
|
680
|
-
if (this.
|
|
1000
|
+
if (this.HasDisplayTab) {
|
|
681
1001
|
tabs.push('Display');
|
|
682
1002
|
}
|
|
683
1003
|
// Get plugin tabs directly from plugin instance (no caching needed - plugin always exists)
|
|
684
|
-
if (this.
|
|
685
|
-
const pluginTabs = this.
|
|
1004
|
+
if (this.PluginViewer?.pluginInstance?.GetAdditionalTabs) {
|
|
1005
|
+
const pluginTabs = this.PluginViewer.pluginInstance.GetAdditionalTabs();
|
|
686
1006
|
const pluginTabLabels = pluginTabs.map((t) => t.label);
|
|
687
1007
|
tabs.push(...pluginTabLabels);
|
|
688
1008
|
}
|
|
689
1009
|
// Get tabs to remove from plugin (case-insensitive)
|
|
690
|
-
const removals = this.
|
|
1010
|
+
const removals = this.PluginViewer?.pluginInstance?.GetStandardTabRemovals?.() || [];
|
|
691
1011
|
const removalsLower = removals.map(r => r.toLowerCase());
|
|
692
1012
|
// File-category artifacts (PDF, Excel, Word) have binary content — the JSON tab
|
|
693
1013
|
// would show a base64 blob or a storage reference, which is meaningless. Suppress it
|
|
@@ -701,19 +1021,23 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
701
1021
|
tabs.push('Details');
|
|
702
1022
|
}
|
|
703
1023
|
// Only add Links tab if there are links to show (unless plugin explicitly removes it)
|
|
704
|
-
if (!removalsLower.includes('links') && this.
|
|
1024
|
+
if (!removalsLower.includes('links') && this.LinksToShow.length > 0) {
|
|
705
1025
|
tabs.push('Links');
|
|
706
1026
|
}
|
|
707
1027
|
return tabs;
|
|
708
1028
|
}
|
|
1029
|
+
/** @deprecated Use {@link AllTabs}. */
|
|
1030
|
+
get allTabs() {
|
|
1031
|
+
return this.AllTabs;
|
|
1032
|
+
}
|
|
709
1033
|
/**
|
|
710
1034
|
* Get the full tab definition for a given tab name.
|
|
711
1035
|
* Returns the ArtifactViewerTab which may include component info for custom component tabs.
|
|
712
1036
|
*/
|
|
713
1037
|
GetTabDefinition(tabName) {
|
|
714
1038
|
// Check if this is a plugin-provided tab
|
|
715
|
-
if (this.
|
|
716
|
-
const pluginTabs = this.
|
|
1039
|
+
if (this.PluginViewer?.pluginInstance?.GetAdditionalTabs) {
|
|
1040
|
+
const pluginTabs = this.PluginViewer.pluginInstance.GetAdditionalTabs();
|
|
717
1041
|
const pluginTab = pluginTabs.find((t) => t.label.toLowerCase() === tabName.toLowerCase());
|
|
718
1042
|
if (pluginTab) {
|
|
719
1043
|
return pluginTab;
|
|
@@ -722,9 +1046,9 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
722
1046
|
// Handle base tabs
|
|
723
1047
|
switch (tabName.toLowerCase()) {
|
|
724
1048
|
case 'json':
|
|
725
|
-
return { label: 'JSON', contentType: 'json', content: this.
|
|
1049
|
+
return { label: 'JSON', contentType: 'json', content: this.JsonContent, language: 'json' };
|
|
726
1050
|
case 'details':
|
|
727
|
-
return { label: 'Details', contentType: 'html', content: this.
|
|
1051
|
+
return { label: 'Details', contentType: 'html', content: this.DisplayMarkdown || this.DisplayHtml || '' };
|
|
728
1052
|
default:
|
|
729
1053
|
return null;
|
|
730
1054
|
}
|
|
@@ -792,8 +1116,8 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
792
1116
|
}
|
|
793
1117
|
async ngOnInit() {
|
|
794
1118
|
// Subscribe to refresh trigger for dynamic version changes
|
|
795
|
-
if (this.
|
|
796
|
-
this.
|
|
1119
|
+
if (this.RefreshTrigger) {
|
|
1120
|
+
this.RefreshTrigger.pipe(takeUntil(this.destroy$)).subscribe(async (data) => {
|
|
797
1121
|
// UUIDsEqual, not ===: SQL Server hands back upper-case UUIDs and PostgreSQL lower-case, so
|
|
798
1122
|
// a raw comparison silently drops a legitimate refresh whenever the emitting side and this
|
|
799
1123
|
// input picked up the id from differently-cased sources.
|
|
@@ -810,9 +1134,9 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
810
1134
|
// Using setTimeout(0) instead of Promise.resolve() because we need a fresh
|
|
811
1135
|
// macrotask boundary — microtasks can still drain inside Angular's CD cycle.
|
|
812
1136
|
setTimeout(async () => {
|
|
813
|
-
await this.loadArtifact(this.
|
|
1137
|
+
await this.loadArtifact(this.VersionNumber);
|
|
814
1138
|
// Track that user viewed this artifact (deferred so it runs after the load)
|
|
815
|
-
if (this.artifactVersion?.ID && this.
|
|
1139
|
+
if (this.artifactVersion?.ID && this.CurrentUser) {
|
|
816
1140
|
this.trackArtifactUsage('Viewed');
|
|
817
1141
|
// Also log to User Record Logs for recents feature (fire-and-forget)
|
|
818
1142
|
this.recentAccessService.logAccess('MJ: Artifacts', this.artifactId, 'artifact');
|
|
@@ -822,7 +1146,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
822
1146
|
async ngOnChanges(changes) {
|
|
823
1147
|
// Reload artifact when artifactId changes
|
|
824
1148
|
if (changes['artifactId'] && !changes['artifactId'].firstChange) {
|
|
825
|
-
await this.loadArtifact(this.
|
|
1149
|
+
await this.loadArtifact(this.VersionNumber);
|
|
826
1150
|
// `loadArtifact` already honored the (possibly also-changed) versionNumber, so the branch
|
|
827
1151
|
// below has nothing left to do. Angular delivers both inputs in ONE SimpleChanges when a
|
|
828
1152
|
// caller switches artifact and version together, and these are two independent `if`s: the
|
|
@@ -837,14 +1161,14 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
837
1161
|
const newVersionNumber = changes['versionNumber'].currentValue;
|
|
838
1162
|
if (newVersionNumber != null) {
|
|
839
1163
|
// Check if we have metadata for this version (allVersions has lightweight metadata)
|
|
840
|
-
const targetVersion = this.
|
|
1164
|
+
const targetVersion = this.AllVersions.find(v => v.VersionNumber === newVersionNumber);
|
|
841
1165
|
if (targetVersion) {
|
|
842
|
-
this.
|
|
1166
|
+
this.SelectedVersionNumber = targetVersion.VersionNumber || 1;
|
|
843
1167
|
// Load full content for the selected version
|
|
844
1168
|
const fullVersion = await this.loadVersionContent(targetVersion.ID);
|
|
845
1169
|
if (fullVersion) {
|
|
846
1170
|
this.artifactVersion = fullVersion;
|
|
847
|
-
this.
|
|
1171
|
+
this.JsonContent = this.formatJSON(fullVersion.Content || '{}');
|
|
848
1172
|
}
|
|
849
1173
|
// Load attributes and collection data in parallel
|
|
850
1174
|
await Promise.all([
|
|
@@ -869,18 +1193,18 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
869
1193
|
return loadToken === this.artifactLoadToken && UUIDsEqual(artifactId, this.artifactId);
|
|
870
1194
|
}
|
|
871
1195
|
clearLoadedArtifactState() {
|
|
872
|
-
this.
|
|
1196
|
+
this.Artifact = null;
|
|
873
1197
|
this.artifactVersion = null;
|
|
874
|
-
this.
|
|
875
|
-
this.
|
|
876
|
-
this.
|
|
877
|
-
this.
|
|
878
|
-
this.
|
|
879
|
-
this.
|
|
880
|
-
this.
|
|
881
|
-
this.
|
|
882
|
-
this.
|
|
883
|
-
this.
|
|
1198
|
+
this.AllVersions = [];
|
|
1199
|
+
this.VersionAttributes = [];
|
|
1200
|
+
this.JsonContent = '';
|
|
1201
|
+
this.DisplayMarkdown = null;
|
|
1202
|
+
this.DisplayHtml = null;
|
|
1203
|
+
this.FallbackText = null;
|
|
1204
|
+
this.FallbackTextTruncated = false;
|
|
1205
|
+
this.ArtifactCollections = [];
|
|
1206
|
+
this.CurrentVersionCollections = [];
|
|
1207
|
+
this.PrimaryCollection = null;
|
|
884
1208
|
this.artifactTypeDriverClass = null;
|
|
885
1209
|
this.artifactContentCategory = null;
|
|
886
1210
|
this.clearLinksData();
|
|
@@ -895,7 +1219,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
895
1219
|
this.clearLoadedArtifactState();
|
|
896
1220
|
const md = this.ProviderToUse;
|
|
897
1221
|
// Load artifact — assign to local first to avoid mid-cycle icon flicker
|
|
898
|
-
const artifactEntity = await md.GetEntityObject('MJ: Artifacts', this.
|
|
1222
|
+
const artifactEntity = await md.GetEntityObject('MJ: Artifacts', this.CurrentUser);
|
|
899
1223
|
const loaded = await artifactEntity.Load(artifactId);
|
|
900
1224
|
if (!isCurrentLoad()) {
|
|
901
1225
|
return;
|
|
@@ -935,7 +1259,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
935
1259
|
MaxRows: 1,
|
|
936
1260
|
ResultType: 'simple'
|
|
937
1261
|
}
|
|
938
|
-
], this.
|
|
1262
|
+
], this.CurrentUser);
|
|
939
1263
|
if (!isCurrentLoad()) {
|
|
940
1264
|
return;
|
|
941
1265
|
}
|
|
@@ -944,8 +1268,8 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
944
1268
|
this.error = 'No artifact version found';
|
|
945
1269
|
return;
|
|
946
1270
|
}
|
|
947
|
-
this.
|
|
948
|
-
this.
|
|
1271
|
+
this.Artifact = artifactEntity;
|
|
1272
|
+
this.AllVersions = versionsResult.Results;
|
|
949
1273
|
// Determine which version to display
|
|
950
1274
|
let selectedVersion;
|
|
951
1275
|
if (targetVersionNumber) {
|
|
@@ -953,7 +1277,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
953
1277
|
}
|
|
954
1278
|
// Fall back to latest version (first in DESC order)
|
|
955
1279
|
const versionToLoad = selectedVersion || versionsResult.Results[0];
|
|
956
|
-
this.
|
|
1280
|
+
this.SelectedVersionNumber = versionToLoad.VersionNumber || 1;
|
|
957
1281
|
// PERF: Start artifact type resolution and selected version content load in parallel
|
|
958
1282
|
const [, selectedVersionEntity] = await Promise.all([
|
|
959
1283
|
this.loadArtifactType(artifactEntity.Type, artifactId, loadToken),
|
|
@@ -964,7 +1288,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
964
1288
|
}
|
|
965
1289
|
if (selectedVersionEntity) {
|
|
966
1290
|
this.artifactVersion = selectedVersionEntity;
|
|
967
|
-
this.
|
|
1291
|
+
this.JsonContent = this.formatJSON(selectedVersionEntity.Content || '{}');
|
|
968
1292
|
}
|
|
969
1293
|
else {
|
|
970
1294
|
this.error = 'Failed to load artifact version content';
|
|
@@ -1004,7 +1328,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1004
1328
|
async loadVersionContent(versionId) {
|
|
1005
1329
|
try {
|
|
1006
1330
|
const md = this.ProviderToUse;
|
|
1007
|
-
const versionEntity = await md.GetEntityObject('MJ: Artifact Versions', this.
|
|
1331
|
+
const versionEntity = await md.GetEntityObject('MJ: Artifact Versions', this.CurrentUser);
|
|
1008
1332
|
const loaded = await versionEntity.Load(versionId);
|
|
1009
1333
|
return loaded ? versionEntity : null;
|
|
1010
1334
|
}
|
|
@@ -1017,12 +1341,12 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1017
1341
|
* Clear all links-related data to prevent stale data when switching artifacts
|
|
1018
1342
|
*/
|
|
1019
1343
|
clearLinksData() {
|
|
1020
|
-
this.
|
|
1021
|
-
this.
|
|
1022
|
-
this.
|
|
1023
|
-
this.
|
|
1344
|
+
this.AllCollections = [];
|
|
1345
|
+
this.OriginConversation = null;
|
|
1346
|
+
this.HasAccessToOriginConversation = false;
|
|
1347
|
+
this.OriginConversationVersionId = null;
|
|
1024
1348
|
}
|
|
1025
|
-
async loadArtifactType(artifactTypeName = this.
|
|
1349
|
+
async loadArtifactType(artifactTypeName = this.Artifact?.Type, artifactId = this.artifactId, loadToken = this.artifactLoadToken) {
|
|
1026
1350
|
const isCurrentLoad = () => this.isCurrentArtifactLoad(artifactId, loadToken);
|
|
1027
1351
|
this.artifactTypeDriverClass = null;
|
|
1028
1352
|
this.artifactContentCategory = null;
|
|
@@ -1030,7 +1354,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1030
1354
|
return;
|
|
1031
1355
|
}
|
|
1032
1356
|
try {
|
|
1033
|
-
await ArtifactMetadataEngine.Instance.Config(false, this.
|
|
1357
|
+
await ArtifactMetadataEngine.Instance.Config(false, this.CurrentUser);
|
|
1034
1358
|
if (!isCurrentLoad()) {
|
|
1035
1359
|
return;
|
|
1036
1360
|
}
|
|
@@ -1060,21 +1384,21 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1060
1384
|
EntityName: 'MJ: Artifact Version Attributes',
|
|
1061
1385
|
ExtraFilter: `ArtifactVersionID='${this.artifactVersion.ID}'`,
|
|
1062
1386
|
ResultType: 'simple'
|
|
1063
|
-
}, this.
|
|
1387
|
+
}, this.CurrentUser);
|
|
1064
1388
|
if (!isCurrentLoad()) {
|
|
1065
1389
|
return;
|
|
1066
1390
|
}
|
|
1067
1391
|
if (result.Success && result.Results) {
|
|
1068
|
-
this.
|
|
1392
|
+
this.VersionAttributes = result.Results;
|
|
1069
1393
|
// Check for displayMarkdown or displayHtml attributes
|
|
1070
|
-
const displayMarkdownAttr = this.
|
|
1071
|
-
const displayHtmlAttr = this.
|
|
1394
|
+
const displayMarkdownAttr = this.VersionAttributes.find(a => a.Name?.toLowerCase() === 'displaymarkdown');
|
|
1395
|
+
const displayHtmlAttr = this.VersionAttributes.find(a => a.Name?.toLowerCase() === 'displayhtml');
|
|
1072
1396
|
// Parse values - they might be JSON-encoded strings
|
|
1073
|
-
this.
|
|
1074
|
-
this.
|
|
1397
|
+
this.DisplayMarkdown = this.parseAttributeValue(displayMarkdownAttr?.Value);
|
|
1398
|
+
this.DisplayHtml = this.parseAttributeValue(displayHtmlAttr?.Value);
|
|
1075
1399
|
// Clean up double-escaped characters in HTML (from LLM generation)
|
|
1076
|
-
if (this.
|
|
1077
|
-
this.
|
|
1400
|
+
if (this.DisplayHtml) {
|
|
1401
|
+
this.DisplayHtml = this.cleanEscapedCharacters(this.DisplayHtml);
|
|
1078
1402
|
}
|
|
1079
1403
|
// Set active tab to the first available tab
|
|
1080
1404
|
this.setActiveTabToFirstAvailable();
|
|
@@ -1097,15 +1421,19 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1097
1421
|
if (this.artifactVersion?.Name) {
|
|
1098
1422
|
return this.artifactVersion.Name;
|
|
1099
1423
|
}
|
|
1100
|
-
return this.
|
|
1424
|
+
return this.Artifact?.Name || 'Artifact';
|
|
1101
1425
|
}
|
|
1102
|
-
get
|
|
1426
|
+
get DisplayDescription() {
|
|
1103
1427
|
if (this.artifactVersion?.Description) {
|
|
1104
1428
|
return this.artifactVersion.Description;
|
|
1105
1429
|
}
|
|
1106
|
-
return this.
|
|
1430
|
+
return this.Artifact?.Description || null;
|
|
1107
1431
|
}
|
|
1108
|
-
|
|
1432
|
+
/** @deprecated Use {@link DisplayDescription}. */
|
|
1433
|
+
get displayDescription() {
|
|
1434
|
+
return this.DisplayDescription;
|
|
1435
|
+
}
|
|
1436
|
+
get HasDisplayTab() {
|
|
1109
1437
|
// Show Display tab if:
|
|
1110
1438
|
// 1. We have a plugin AND it reports having content to display, OR
|
|
1111
1439
|
// 2. We have displayMarkdown or displayHtml attributes from extract rules
|
|
@@ -1113,19 +1441,31 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1113
1441
|
// Note: hasDisplayContent defaults to false in base class, so plugins must
|
|
1114
1442
|
// explicitly opt-in by overriding to return true when they have content.
|
|
1115
1443
|
// This prevents showing Display tab before plugin loads or when plugin has no content.
|
|
1116
|
-
const pluginHasContent = this.
|
|
1117
|
-
return pluginHasContent || !!this.
|
|
1444
|
+
const pluginHasContent = this.PluginViewer?.pluginInstance?.hasDisplayContent ?? false;
|
|
1445
|
+
return pluginHasContent || !!this.DisplayMarkdown || !!this.DisplayHtml;
|
|
1446
|
+
}
|
|
1447
|
+
/** @deprecated Use {@link HasDisplayTab}. */
|
|
1448
|
+
get hasDisplayTab() {
|
|
1449
|
+
return this.HasDisplayTab;
|
|
1118
1450
|
}
|
|
1119
1451
|
/** True when the display tab has nothing better than the file card to show. */
|
|
1452
|
+
get ShowFileFallback() {
|
|
1453
|
+
return !this.HasPlugin && !!this.artifactVersion && !this.DisplayMarkdown && !this.DisplayHtml;
|
|
1454
|
+
}
|
|
1455
|
+
/** @deprecated Use {@link ShowFileFallback}. */
|
|
1120
1456
|
get showFileFallback() {
|
|
1121
|
-
return
|
|
1457
|
+
return this.ShowFileFallback;
|
|
1122
1458
|
}
|
|
1123
1459
|
/** The file name shown on the fallback card. */
|
|
1460
|
+
get FallbackFileName() {
|
|
1461
|
+
return this.artifactVersion?.FileName || this.Artifact?.Name || 'file';
|
|
1462
|
+
}
|
|
1463
|
+
/** @deprecated Use {@link FallbackFileName}. */
|
|
1124
1464
|
get fallbackFileName() {
|
|
1125
|
-
return this.
|
|
1465
|
+
return this.FallbackFileName;
|
|
1126
1466
|
}
|
|
1127
1467
|
/** Human file size for the fallback card, when the version knows it. */
|
|
1128
|
-
get
|
|
1468
|
+
get FallbackSize() {
|
|
1129
1469
|
const content = this.artifactVersion?.Content;
|
|
1130
1470
|
if (this.artifactVersion?.ContentMode === 'Text' && content?.startsWith('data:')) {
|
|
1131
1471
|
const b64 = content.slice(content.indexOf(',') + 1);
|
|
@@ -1133,6 +1473,10 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1133
1473
|
}
|
|
1134
1474
|
return null;
|
|
1135
1475
|
}
|
|
1476
|
+
/** @deprecated Use {@link FallbackSize}. */
|
|
1477
|
+
get fallbackSize() {
|
|
1478
|
+
return this.FallbackSize;
|
|
1479
|
+
}
|
|
1136
1480
|
/** Text-like MIME types are shown inline on the fallback card; everything else is download-only. */
|
|
1137
1481
|
isTextLike(mime) {
|
|
1138
1482
|
const m = (mime ?? '').toLowerCase();
|
|
@@ -1143,10 +1487,10 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1143
1487
|
* inline: the data URL. Silent on failure — the Download action still works.
|
|
1144
1488
|
*/
|
|
1145
1489
|
async prepareNoPluginFallback(isCurrentLoad) {
|
|
1146
|
-
this.
|
|
1147
|
-
this.
|
|
1490
|
+
this.FallbackText = null;
|
|
1491
|
+
this.FallbackTextTruncated = false;
|
|
1148
1492
|
const version = this.artifactVersion;
|
|
1149
|
-
if (!this.
|
|
1493
|
+
if (!this.ShowFileFallback || !version || !this.isTextLike(version.MimeType))
|
|
1150
1494
|
return;
|
|
1151
1495
|
try {
|
|
1152
1496
|
let text = null;
|
|
@@ -1166,9 +1510,9 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1166
1510
|
return;
|
|
1167
1511
|
if (text.length > ArtifactViewerPanelComponent.FALLBACK_TEXT_MAX_CHARS) {
|
|
1168
1512
|
text = text.slice(0, ArtifactViewerPanelComponent.FALLBACK_TEXT_MAX_CHARS);
|
|
1169
|
-
this.
|
|
1513
|
+
this.FallbackTextTruncated = true;
|
|
1170
1514
|
}
|
|
1171
|
-
this.
|
|
1515
|
+
this.FallbackText = text;
|
|
1172
1516
|
this.cdr.markForCheck();
|
|
1173
1517
|
}
|
|
1174
1518
|
catch (err) {
|
|
@@ -1176,11 +1520,11 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1176
1520
|
}
|
|
1177
1521
|
}
|
|
1178
1522
|
/** Download the artifact's file: file-backed via MJ's URL (fetched to a blob so the browser saves it), inline via the data URL. */
|
|
1179
|
-
async
|
|
1523
|
+
async DownloadArtifactFile() {
|
|
1180
1524
|
const version = this.artifactVersion;
|
|
1181
|
-
if (!version || this.
|
|
1525
|
+
if (!version || this.FallbackBusy)
|
|
1182
1526
|
return;
|
|
1183
|
-
this.
|
|
1527
|
+
this.FallbackBusy = true;
|
|
1184
1528
|
try {
|
|
1185
1529
|
let objectUrl = null;
|
|
1186
1530
|
if (version.ContentMode === 'File') {
|
|
@@ -1206,7 +1550,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1206
1550
|
return;
|
|
1207
1551
|
const a = document.createElement('a');
|
|
1208
1552
|
a.href = objectUrl;
|
|
1209
|
-
a.download = this.
|
|
1553
|
+
a.download = this.FallbackFileName;
|
|
1210
1554
|
a.rel = 'noopener';
|
|
1211
1555
|
document.body.appendChild(a);
|
|
1212
1556
|
a.click();
|
|
@@ -1214,10 +1558,14 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1214
1558
|
setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000);
|
|
1215
1559
|
}
|
|
1216
1560
|
finally {
|
|
1217
|
-
this.
|
|
1561
|
+
this.FallbackBusy = false;
|
|
1218
1562
|
this.cdr.markForCheck();
|
|
1219
1563
|
}
|
|
1220
1564
|
}
|
|
1565
|
+
/** @deprecated Use {@link DownloadArtifactFile}. */
|
|
1566
|
+
async downloadArtifactFile() {
|
|
1567
|
+
return this.DownloadArtifactFile();
|
|
1568
|
+
}
|
|
1221
1569
|
formatBytes(n) {
|
|
1222
1570
|
if (n < 1024)
|
|
1223
1571
|
return `${n} B`;
|
|
@@ -1225,50 +1573,70 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1225
1573
|
return `${(n / 1024).toFixed(1)} KB`;
|
|
1226
1574
|
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
1227
1575
|
}
|
|
1228
|
-
get
|
|
1576
|
+
get HasPlugin() {
|
|
1229
1577
|
// Check if the artifact type has a DriverClass configured
|
|
1230
1578
|
// If DriverClass is set, we have a plugin available
|
|
1231
1579
|
return !!this.artifactTypeDriverClass;
|
|
1232
1580
|
}
|
|
1233
|
-
|
|
1581
|
+
/** @deprecated Use {@link HasPlugin}. */
|
|
1582
|
+
get hasPlugin() {
|
|
1583
|
+
return this.HasPlugin;
|
|
1584
|
+
}
|
|
1585
|
+
get HasJsonTab() {
|
|
1234
1586
|
// Query plugin directly (no cache needed - plugin always exists when it should)
|
|
1235
|
-
const pluginInstance = this.
|
|
1587
|
+
const pluginInstance = this.PluginViewer?.pluginInstance;
|
|
1236
1588
|
return pluginInstance?.parentShouldShowRawContent || false;
|
|
1237
1589
|
}
|
|
1590
|
+
/** @deprecated Use {@link HasJsonTab}. */
|
|
1591
|
+
get hasJsonTab() {
|
|
1592
|
+
return this.HasJsonTab;
|
|
1593
|
+
}
|
|
1594
|
+
get ArtifactTypeName() {
|
|
1595
|
+
return this.Artifact?.Type || '';
|
|
1596
|
+
}
|
|
1597
|
+
/** @deprecated Use {@link ArtifactTypeName}. */
|
|
1238
1598
|
get artifactTypeName() {
|
|
1239
|
-
return this.
|
|
1599
|
+
return this.ArtifactTypeName;
|
|
1240
1600
|
}
|
|
1241
|
-
get
|
|
1601
|
+
get ContentType() {
|
|
1242
1602
|
// Try to get content type from artifact type or attributes
|
|
1243
|
-
const contentTypeAttr = this.
|
|
1603
|
+
const contentTypeAttr = this.VersionAttributes.find(a => a.Name?.toLowerCase() === 'contenttype');
|
|
1244
1604
|
return contentTypeAttr?.Value || undefined;
|
|
1245
1605
|
}
|
|
1246
|
-
|
|
1606
|
+
/** @deprecated Use {@link ContentType}. */
|
|
1607
|
+
get contentType() {
|
|
1608
|
+
return this.ContentType;
|
|
1609
|
+
}
|
|
1610
|
+
get FilteredAttributes() {
|
|
1247
1611
|
// Filter out displayMarkdown and displayHtml as they're shown in the Display tab
|
|
1248
|
-
return this.
|
|
1612
|
+
return this.VersionAttributes.filter(attr => {
|
|
1249
1613
|
const name = attr.Name?.toLowerCase();
|
|
1250
1614
|
return name !== 'displaymarkdown' && name !== 'displayhtml';
|
|
1251
1615
|
});
|
|
1252
1616
|
}
|
|
1617
|
+
/** @deprecated Use {@link FilteredAttributes}. */
|
|
1618
|
+
get filteredAttributes() {
|
|
1619
|
+
return this.FilteredAttributes;
|
|
1620
|
+
}
|
|
1253
1621
|
setActiveTab(tab) {
|
|
1254
|
-
this.
|
|
1622
|
+
this.ActiveTab = tab;
|
|
1255
1623
|
}
|
|
1256
1624
|
/**
|
|
1257
1625
|
* Sets the active tab to the first available tab in the list.
|
|
1258
1626
|
* Called when tabs change or when the currently active tab becomes unavailable.
|
|
1259
1627
|
*/
|
|
1260
1628
|
setActiveTabToFirstAvailable() {
|
|
1261
|
-
const tabs = this.
|
|
1629
|
+
const tabs = this.AllTabs;
|
|
1262
1630
|
if (tabs.length > 0) {
|
|
1263
1631
|
// If current tab is still available, keep it; otherwise switch to first
|
|
1264
|
-
const currentTabStillAvailable = tabs.some(t => t.toLowerCase() === this.
|
|
1632
|
+
const currentTabStillAvailable = tabs.some(t => t.toLowerCase() === this.ActiveTab.toLowerCase());
|
|
1265
1633
|
if (!currentTabStillAvailable) {
|
|
1266
|
-
this.
|
|
1634
|
+
this.ActiveTab = tabs[0].toLowerCase();
|
|
1267
1635
|
}
|
|
1268
1636
|
}
|
|
1269
1637
|
else {
|
|
1270
1638
|
// Fallback to details if no tabs available (shouldn't happen)
|
|
1271
|
-
this.
|
|
1639
|
+
this.ActiveTab = 'details';
|
|
1272
1640
|
}
|
|
1273
1641
|
}
|
|
1274
1642
|
/**
|
|
@@ -1276,28 +1644,36 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1276
1644
|
* the full spec from the registry after initial render with a stripped spec).
|
|
1277
1645
|
* Forces re-evaluation of allTabs so new tab labels render correctly.
|
|
1278
1646
|
*/
|
|
1279
|
-
|
|
1647
|
+
OnTabsChanged() {
|
|
1280
1648
|
// If Display tab just became available (e.g., after plugin async load),
|
|
1281
1649
|
// switch to it — it should be the default when present.
|
|
1282
|
-
const tabs = this.
|
|
1283
|
-
if (tabs.length > 0 && tabs[0].toLowerCase() === 'display' && this.
|
|
1284
|
-
this.
|
|
1650
|
+
const tabs = this.AllTabs;
|
|
1651
|
+
if (tabs.length > 0 && tabs[0].toLowerCase() === 'display' && this.ActiveTab !== 'display') {
|
|
1652
|
+
this.ActiveTab = 'display';
|
|
1285
1653
|
}
|
|
1286
1654
|
this.cdr.detectChanges(); // zone.js 0.15: plugin emitted tabsChanged, force CD to re-evaluate allTabs
|
|
1287
1655
|
}
|
|
1656
|
+
/** @deprecated Use {@link OnTabsChanged}. */
|
|
1657
|
+
onTabsChanged() {
|
|
1658
|
+
return this.OnTabsChanged();
|
|
1659
|
+
}
|
|
1288
1660
|
/**
|
|
1289
1661
|
* Called when the plugin viewer finishes loading.
|
|
1290
1662
|
* Selects the first available tab now that plugin tabs are available.
|
|
1291
1663
|
*/
|
|
1292
|
-
|
|
1664
|
+
OnPluginLoaded() {
|
|
1293
1665
|
// Now that plugin is loaded, we have accurate tab information
|
|
1294
1666
|
// Always select the first tab since this is the initial load
|
|
1295
|
-
const tabs = this.
|
|
1667
|
+
const tabs = this.AllTabs;
|
|
1296
1668
|
if (tabs.length > 0) {
|
|
1297
|
-
this.
|
|
1669
|
+
this.ActiveTab = tabs[0].toLowerCase();
|
|
1298
1670
|
}
|
|
1299
1671
|
this.cdr.detectChanges(); // zone.js 0.15: plugin loaded via async callback, force CD
|
|
1300
1672
|
}
|
|
1673
|
+
/** @deprecated Use {@link OnPluginLoaded}. */
|
|
1674
|
+
onPluginLoaded() {
|
|
1675
|
+
return this.OnPluginLoaded();
|
|
1676
|
+
}
|
|
1301
1677
|
parseAttributeValue(value) {
|
|
1302
1678
|
if (!value)
|
|
1303
1679
|
return null;
|
|
@@ -1352,42 +1728,42 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1352
1728
|
)`,
|
|
1353
1729
|
Fields: ['ID', 'CollectionID', 'ArtifactVersionID', 'Sequence'],
|
|
1354
1730
|
ResultType: 'simple'
|
|
1355
|
-
}, this.
|
|
1731
|
+
}, this.CurrentUser);
|
|
1356
1732
|
collectionRows = (result.Success ? result.Results : []);
|
|
1357
1733
|
}
|
|
1358
1734
|
if (!isCurrentLoad()) {
|
|
1359
1735
|
return;
|
|
1360
1736
|
}
|
|
1361
1737
|
// Store as simple objects — these are read-only display data
|
|
1362
|
-
this.
|
|
1738
|
+
this.ArtifactCollections = collectionRows;
|
|
1363
1739
|
// Filter to get only collections containing the CURRENT version
|
|
1364
1740
|
const currentVersionId = this.artifactVersion?.ID;
|
|
1365
1741
|
if (currentVersionId) {
|
|
1366
1742
|
const currentIdStr = String(currentVersionId).toLowerCase();
|
|
1367
|
-
this.
|
|
1743
|
+
this.CurrentVersionCollections = collectionRows.filter(ca => {
|
|
1368
1744
|
const versionIdStr = String(ca['ArtifactVersionID'] || ca.ArtifactVersionID || '').toLowerCase();
|
|
1369
1745
|
return versionIdStr && currentIdStr && versionIdStr === currentIdStr;
|
|
1370
1746
|
});
|
|
1371
1747
|
}
|
|
1372
1748
|
else {
|
|
1373
|
-
this.
|
|
1749
|
+
this.CurrentVersionCollections = [];
|
|
1374
1750
|
}
|
|
1375
1751
|
// Load the primary collection details if exists
|
|
1376
|
-
if (this.
|
|
1752
|
+
if (this.ArtifactCollections.length > 0) {
|
|
1377
1753
|
const collectionId = collectionRows[0].CollectionID ||
|
|
1378
|
-
this.
|
|
1754
|
+
this.ArtifactCollections[0].CollectionID;
|
|
1379
1755
|
if (collectionId) {
|
|
1380
1756
|
const md = this.ProviderToUse;
|
|
1381
|
-
const primaryCollection = await md.GetEntityObject('MJ: Collections', this.
|
|
1757
|
+
const primaryCollection = await md.GetEntityObject('MJ: Collections', this.CurrentUser);
|
|
1382
1758
|
await primaryCollection.Load(collectionId);
|
|
1383
1759
|
if (!isCurrentLoad()) {
|
|
1384
1760
|
return;
|
|
1385
1761
|
}
|
|
1386
|
-
this.
|
|
1762
|
+
this.PrimaryCollection = primaryCollection;
|
|
1387
1763
|
}
|
|
1388
1764
|
}
|
|
1389
1765
|
else {
|
|
1390
|
-
this.
|
|
1766
|
+
this.PrimaryCollection = null;
|
|
1391
1767
|
}
|
|
1392
1768
|
}
|
|
1393
1769
|
catch (err) {
|
|
@@ -1409,53 +1785,69 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1409
1785
|
async loadCollectionAssociations() {
|
|
1410
1786
|
return this.processCollectionAssociations();
|
|
1411
1787
|
}
|
|
1788
|
+
get IsInCollection() {
|
|
1789
|
+
return this.CurrentVersionCollections.length > 0;
|
|
1790
|
+
}
|
|
1791
|
+
/** @deprecated Use {@link IsInCollection}. */
|
|
1412
1792
|
get isInCollection() {
|
|
1413
|
-
return this.
|
|
1793
|
+
return this.IsInCollection;
|
|
1414
1794
|
}
|
|
1415
1795
|
/**
|
|
1416
1796
|
* Get collection IDs that already contain the current version
|
|
1417
1797
|
* Used to exclude them from the save picker
|
|
1418
1798
|
*/
|
|
1799
|
+
get CurrentVersionCollectionIds() {
|
|
1800
|
+
return this.CurrentVersionCollections.map(ca => ca.CollectionID);
|
|
1801
|
+
}
|
|
1802
|
+
/** @deprecated Use {@link CurrentVersionCollectionIds}. */
|
|
1419
1803
|
get currentVersionCollectionIds() {
|
|
1420
|
-
return this.
|
|
1804
|
+
return this.CurrentVersionCollectionIds;
|
|
1421
1805
|
}
|
|
1422
|
-
|
|
1806
|
+
OnCopyToClipboard() {
|
|
1423
1807
|
// Get content from the currently active tab instead of always copying jsonContent
|
|
1424
|
-
const tabData = this.GetTabContent(this.
|
|
1808
|
+
const tabData = this.GetTabContent(this.ActiveTab);
|
|
1425
1809
|
if (tabData?.content) {
|
|
1426
1810
|
navigator.clipboard.writeText(tabData.content);
|
|
1427
1811
|
}
|
|
1428
|
-
else if (this.
|
|
1812
|
+
else if (this.JsonContent) {
|
|
1429
1813
|
// Fallback to jsonContent if tab content not found
|
|
1430
|
-
navigator.clipboard.writeText(this.
|
|
1814
|
+
navigator.clipboard.writeText(this.JsonContent);
|
|
1431
1815
|
}
|
|
1432
1816
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1817
|
+
/** @deprecated Use {@link OnCopyToClipboard}. */
|
|
1818
|
+
onCopyToClipboard() {
|
|
1819
|
+
return this.OnCopyToClipboard();
|
|
1820
|
+
}
|
|
1821
|
+
OnCopyDisplayContent() {
|
|
1822
|
+
const content = this.DisplayHtml || this.DisplayMarkdown;
|
|
1435
1823
|
if (content) {
|
|
1436
1824
|
navigator.clipboard.writeText(content).catch(err => {
|
|
1437
1825
|
console.error('Failed to copy to clipboard:', err);
|
|
1438
1826
|
});
|
|
1439
1827
|
}
|
|
1440
1828
|
}
|
|
1441
|
-
|
|
1829
|
+
/** @deprecated Use {@link OnCopyDisplayContent}. */
|
|
1830
|
+
onCopyDisplayContent() {
|
|
1831
|
+
return this.OnCopyDisplayContent();
|
|
1832
|
+
}
|
|
1833
|
+
OnPrintDisplayContent() {
|
|
1442
1834
|
// Try to delegate to the plugin viewer's print method
|
|
1443
|
-
if (this.
|
|
1444
|
-
const plugin = this.
|
|
1835
|
+
if (this.PluginViewer?.pluginInstance) {
|
|
1836
|
+
const plugin = this.PluginViewer.pluginInstance;
|
|
1445
1837
|
if (typeof plugin.printHtml === 'function') {
|
|
1446
1838
|
plugin.printHtml();
|
|
1447
1839
|
return;
|
|
1448
1840
|
}
|
|
1449
1841
|
}
|
|
1450
1842
|
// Fallback: create a temporary print window with displayHtml or displayMarkdown
|
|
1451
|
-
const content = this.
|
|
1843
|
+
const content = this.DisplayHtml || this.DisplayMarkdown;
|
|
1452
1844
|
if (content) {
|
|
1453
1845
|
const printWindow = window.open('', '_blank');
|
|
1454
1846
|
if (printWindow) {
|
|
1455
|
-
if (this.
|
|
1847
|
+
if (this.DisplayHtml) {
|
|
1456
1848
|
printWindow.document.write(content);
|
|
1457
1849
|
}
|
|
1458
|
-
else if (this.
|
|
1850
|
+
else if (this.DisplayMarkdown) {
|
|
1459
1851
|
// Wrap markdown in basic HTML for printing
|
|
1460
1852
|
printWindow.document.write(`
|
|
1461
1853
|
<!DOCTYPE html>
|
|
@@ -1482,19 +1874,27 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1482
1874
|
}
|
|
1483
1875
|
}
|
|
1484
1876
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1877
|
+
/** @deprecated Use {@link OnPrintDisplayContent}. */
|
|
1878
|
+
onPrintDisplayContent() {
|
|
1879
|
+
return this.OnPrintDisplayContent();
|
|
1880
|
+
}
|
|
1881
|
+
ToggleVersionDropdown() {
|
|
1882
|
+
if (this.AllVersions.length > 1) {
|
|
1883
|
+
this.ShowVersionDropdown = !this.ShowVersionDropdown;
|
|
1488
1884
|
}
|
|
1489
1885
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
this.
|
|
1886
|
+
/** @deprecated Use {@link ToggleVersionDropdown}. */
|
|
1887
|
+
toggleVersionDropdown() {
|
|
1888
|
+
return this.ToggleVersionDropdown();
|
|
1889
|
+
}
|
|
1890
|
+
async SelectVersion(version) {
|
|
1891
|
+
this.SelectedVersionNumber = version.VersionNumber || 1;
|
|
1892
|
+
this.ShowVersionDropdown = false;
|
|
1493
1893
|
// Load full content for the selected version (allVersions only has metadata)
|
|
1494
1894
|
const fullVersion = await this.loadVersionContent(version.ID);
|
|
1495
1895
|
if (fullVersion) {
|
|
1496
1896
|
this.artifactVersion = fullVersion;
|
|
1497
|
-
this.
|
|
1897
|
+
this.JsonContent = this.formatJSON(fullVersion.Content || '{}');
|
|
1498
1898
|
}
|
|
1499
1899
|
// Load attributes and collection data in parallel
|
|
1500
1900
|
await Promise.all([
|
|
@@ -1504,22 +1904,34 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1504
1904
|
]);
|
|
1505
1905
|
this.cdr.detectChanges();
|
|
1506
1906
|
}
|
|
1507
|
-
|
|
1907
|
+
/** @deprecated Use {@link SelectVersion}. */
|
|
1908
|
+
async selectVersion(version) {
|
|
1909
|
+
return this.SelectVersion(version);
|
|
1910
|
+
}
|
|
1911
|
+
async OnSaveToLibrary() {
|
|
1508
1912
|
// Always show the collection picker modal
|
|
1509
1913
|
// Artifacts can be saved to multiple collections
|
|
1510
|
-
this.
|
|
1914
|
+
this.SaveToCollectionRequested.emit({
|
|
1511
1915
|
artifactId: this.artifactId,
|
|
1512
|
-
excludedCollectionIds: this.
|
|
1916
|
+
excludedCollectionIds: this.ExcludedCollectionIds
|
|
1513
1917
|
});
|
|
1514
1918
|
}
|
|
1515
|
-
|
|
1919
|
+
/** @deprecated Use {@link OnSaveToLibrary}. */
|
|
1920
|
+
async onSaveToLibrary() {
|
|
1921
|
+
return this.OnSaveToLibrary();
|
|
1922
|
+
}
|
|
1923
|
+
get ExcludedCollectionIds() {
|
|
1516
1924
|
// Return IDs of collections that already contain the CURRENT VERSION
|
|
1517
1925
|
// This allows saving different versions to the same collection
|
|
1518
|
-
const excluded = this.
|
|
1926
|
+
const excluded = this.CurrentVersionCollections
|
|
1519
1927
|
.filter(ca => ca.CollectionID)
|
|
1520
1928
|
.map(ca => String(ca.CollectionID));
|
|
1521
1929
|
return excluded;
|
|
1522
1930
|
}
|
|
1931
|
+
/** @deprecated Use {@link ExcludedCollectionIds}. */
|
|
1932
|
+
get excludedCollectionIds() {
|
|
1933
|
+
return this.ExcludedCollectionIds;
|
|
1934
|
+
}
|
|
1523
1935
|
/**
|
|
1524
1936
|
* Reload the cached set of collections that contain the *current version* of this artifact.
|
|
1525
1937
|
* Called by the chat-area after the collection picker reports successful saves so the bookmark
|
|
@@ -1555,12 +1967,12 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1555
1967
|
EntityName: 'MJ: Collection Artifacts',
|
|
1556
1968
|
ExtraFilter: `CollectionID='${collectionId}' AND ArtifactVersionID='${currentVersionId}'`,
|
|
1557
1969
|
ResultType: 'entity_object'
|
|
1558
|
-
}, this.
|
|
1970
|
+
}, this.CurrentUser);
|
|
1559
1971
|
if (existingResult.Success && existingResult.Results && existingResult.Results.length > 0) {
|
|
1560
1972
|
continue;
|
|
1561
1973
|
}
|
|
1562
1974
|
// Create junction record with version ID
|
|
1563
|
-
const collectionArtifact = await md.GetEntityObject('MJ: Collection Artifacts', this.
|
|
1975
|
+
const collectionArtifact = await md.GetEntityObject('MJ: Collection Artifacts', this.CurrentUser);
|
|
1564
1976
|
collectionArtifact.CollectionID = collectionId;
|
|
1565
1977
|
collectionArtifact.ArtifactVersionID = currentVersionId;
|
|
1566
1978
|
collectionArtifact.Sequence = 0;
|
|
@@ -1616,7 +2028,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1616
2028
|
)`,
|
|
1617
2029
|
Fields: ['ID', 'CollectionID', 'ArtifactVersionID'],
|
|
1618
2030
|
ResultType: 'simple'
|
|
1619
|
-
}, this.
|
|
2031
|
+
}, this.CurrentUser);
|
|
1620
2032
|
collectionRows = (result.Success ? result.Results : []);
|
|
1621
2033
|
}
|
|
1622
2034
|
if (!isCurrentLoad()) {
|
|
@@ -1631,12 +2043,12 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1631
2043
|
ExtraFilter: collectionsFilter,
|
|
1632
2044
|
Fields: ['ID', 'Name', 'UserID', 'Description'],
|
|
1633
2045
|
ResultType: 'simple'
|
|
1634
|
-
}, this.
|
|
2046
|
+
}, this.CurrentUser);
|
|
1635
2047
|
if (!isCurrentLoad()) {
|
|
1636
2048
|
return;
|
|
1637
2049
|
}
|
|
1638
2050
|
if (collectionsEntityResult.Success && collectionsEntityResult.Results) {
|
|
1639
|
-
this.
|
|
2051
|
+
this.AllCollections = collectionsEntityResult.Results;
|
|
1640
2052
|
}
|
|
1641
2053
|
}
|
|
1642
2054
|
// Use pre-fetched conversation detail artifact data or fetch if not provided
|
|
@@ -1645,7 +2057,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1645
2057
|
convDetailRows = convDetailResult.Results;
|
|
1646
2058
|
}
|
|
1647
2059
|
else {
|
|
1648
|
-
const versionIds = this.
|
|
2060
|
+
const versionIds = this.AllVersions.map(v => v.ID);
|
|
1649
2061
|
if (versionIds.length > 0) {
|
|
1650
2062
|
const versionFilter = versionIds.map(id => `ArtifactVersionID='${id}'`).join(' OR ');
|
|
1651
2063
|
const result = await rv.RunView({
|
|
@@ -1654,7 +2066,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1654
2066
|
Fields: ['ID', 'ConversationDetailID', 'ArtifactVersionID'],
|
|
1655
2067
|
MaxRows: 1,
|
|
1656
2068
|
ResultType: 'simple'
|
|
1657
|
-
}, this.
|
|
2069
|
+
}, this.CurrentUser);
|
|
1658
2070
|
convDetailRows = (result.Success ? result.Results : []);
|
|
1659
2071
|
}
|
|
1660
2072
|
}
|
|
@@ -1668,37 +2080,37 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1668
2080
|
if (!isCurrentLoad()) {
|
|
1669
2081
|
return;
|
|
1670
2082
|
}
|
|
1671
|
-
this.
|
|
2083
|
+
this.OriginConversationVersionId = artifactVersionId;
|
|
1672
2084
|
// Load conversation detail to get conversation ID
|
|
1673
|
-
const conversationDetail = await md.GetEntityObject('MJ: Conversation Details', this.
|
|
2085
|
+
const conversationDetail = await md.GetEntityObject('MJ: Conversation Details', this.CurrentUser);
|
|
1674
2086
|
const detailLoaded = await conversationDetail.Load(conversationDetailId);
|
|
1675
2087
|
if (!isCurrentLoad()) {
|
|
1676
2088
|
return;
|
|
1677
2089
|
}
|
|
1678
2090
|
if (detailLoaded && conversationDetail.ConversationID) {
|
|
1679
|
-
const conversation = await md.GetEntityObject('MJ: Conversations', this.
|
|
2091
|
+
const conversation = await md.GetEntityObject('MJ: Conversations', this.CurrentUser);
|
|
1680
2092
|
const loaded = await conversation.Load(conversationDetail.ConversationID);
|
|
1681
2093
|
if (!isCurrentLoad()) {
|
|
1682
2094
|
return;
|
|
1683
2095
|
}
|
|
1684
2096
|
if (loaded) {
|
|
1685
2097
|
// Check if user has access (is owner or participant)
|
|
1686
|
-
const userIsOwner = UUIDsEqual(conversation.UserID, this.
|
|
2098
|
+
const userIsOwner = UUIDsEqual(conversation.UserID, this.CurrentUser.ID);
|
|
1687
2099
|
const participantResult = await rv.RunView({
|
|
1688
2100
|
EntityName: 'MJ: Conversation Details',
|
|
1689
|
-
ExtraFilter: `ConversationID='${conversation.ID}' AND UserID='${this.
|
|
2101
|
+
ExtraFilter: `ConversationID='${conversation.ID}' AND UserID='${this.CurrentUser.ID}'`,
|
|
1690
2102
|
MaxRows: 1,
|
|
1691
2103
|
Fields: ['ID'],
|
|
1692
2104
|
ResultType: 'simple'
|
|
1693
|
-
}, this.
|
|
2105
|
+
}, this.CurrentUser);
|
|
1694
2106
|
if (!isCurrentLoad()) {
|
|
1695
2107
|
return;
|
|
1696
2108
|
}
|
|
1697
2109
|
const userIsParticipant = participantResult.Success &&
|
|
1698
2110
|
participantResult.Results &&
|
|
1699
2111
|
participantResult.Results.length > 0;
|
|
1700
|
-
this.
|
|
1701
|
-
this.
|
|
2112
|
+
this.OriginConversation = conversation;
|
|
2113
|
+
this.HasAccessToOriginConversation = userIsOwner || userIsParticipant;
|
|
1702
2114
|
}
|
|
1703
2115
|
}
|
|
1704
2116
|
}
|
|
@@ -1721,15 +2133,15 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1721
2133
|
async loadLinksData() {
|
|
1722
2134
|
return this.processLinksData();
|
|
1723
2135
|
}
|
|
1724
|
-
get
|
|
2136
|
+
get LinksToShow() {
|
|
1725
2137
|
const links = [];
|
|
1726
2138
|
// Get current version ID being viewed
|
|
1727
2139
|
const currentVersionId = this.artifactVersion?.ID;
|
|
1728
2140
|
// RULE: In conversation context, show ONLY collection links
|
|
1729
2141
|
// RULE: In collection context, show ONLY conversation links
|
|
1730
|
-
if (this.
|
|
2142
|
+
if (this.ViewContext === 'conversation') {
|
|
1731
2143
|
// Show all collections containing this artifact (any version)
|
|
1732
|
-
for (const collection of this.
|
|
2144
|
+
for (const collection of this.AllCollections) {
|
|
1733
2145
|
links.push({
|
|
1734
2146
|
type: 'collection',
|
|
1735
2147
|
id: collection.ID,
|
|
@@ -1738,60 +2150,88 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1738
2150
|
});
|
|
1739
2151
|
}
|
|
1740
2152
|
}
|
|
1741
|
-
else if (this.
|
|
2153
|
+
else if (this.ViewContext === 'collection') {
|
|
1742
2154
|
// Show origin conversation if it exists
|
|
1743
2155
|
// Show for ALL versions of the artifact, not just the original version that was added
|
|
1744
|
-
if (this.
|
|
2156
|
+
if (this.OriginConversation) {
|
|
1745
2157
|
links.push({
|
|
1746
2158
|
type: 'conversation',
|
|
1747
|
-
id: this.
|
|
1748
|
-
name: this.
|
|
1749
|
-
hasAccess: this.
|
|
2159
|
+
id: this.OriginConversation.ID,
|
|
2160
|
+
name: this.OriginConversation.Name || 'Untitled Conversation',
|
|
2161
|
+
hasAccess: this.HasAccessToOriginConversation
|
|
1750
2162
|
});
|
|
1751
2163
|
}
|
|
1752
2164
|
}
|
|
1753
2165
|
// If viewContext is null, show nothing (no links)
|
|
1754
2166
|
return links;
|
|
1755
2167
|
}
|
|
2168
|
+
/** @deprecated Use {@link LinksToShow}. */
|
|
2169
|
+
get linksToShow() {
|
|
2170
|
+
return this.LinksToShow;
|
|
2171
|
+
}
|
|
1756
2172
|
/**
|
|
1757
2173
|
* Navigate to a linked conversation or collection
|
|
1758
2174
|
*/
|
|
1759
|
-
|
|
2175
|
+
OnNavigateToLink(link) {
|
|
1760
2176
|
if (!link.hasAccess) {
|
|
1761
2177
|
return;
|
|
1762
2178
|
}
|
|
1763
2179
|
// Include artifact ID, version number, and version ID so destination can show the artifact with correct URL
|
|
1764
|
-
this.
|
|
2180
|
+
this.NavigateToLink.emit({
|
|
1765
2181
|
type: link.type,
|
|
1766
2182
|
id: link.id,
|
|
1767
2183
|
artifactId: this.artifactId,
|
|
1768
|
-
versionNumber: this.
|
|
2184
|
+
versionNumber: this.SelectedVersionNumber,
|
|
1769
2185
|
versionId: this.artifactVersion?.ID
|
|
1770
2186
|
});
|
|
1771
2187
|
}
|
|
2188
|
+
/** @deprecated Use {@link OnNavigateToLink}. */
|
|
2189
|
+
onNavigateToLink(link) {
|
|
2190
|
+
return this.OnNavigateToLink(link);
|
|
2191
|
+
}
|
|
2192
|
+
OnClose() {
|
|
2193
|
+
this.Closed.emit();
|
|
2194
|
+
}
|
|
2195
|
+
/** @deprecated Use {@link OnClose}. */
|
|
1772
2196
|
onClose() {
|
|
1773
|
-
this.
|
|
2197
|
+
return this.OnClose();
|
|
1774
2198
|
}
|
|
2199
|
+
OnShare() {
|
|
2200
|
+
this.ShareRequested.emit(this.artifactId);
|
|
2201
|
+
}
|
|
2202
|
+
/** @deprecated Use {@link OnShare}. */
|
|
1775
2203
|
onShare() {
|
|
1776
|
-
this.
|
|
2204
|
+
return this.OnShare();
|
|
2205
|
+
}
|
|
2206
|
+
OnMaximizeToggle() {
|
|
2207
|
+
this.MaximizeToggled.emit();
|
|
1777
2208
|
}
|
|
2209
|
+
/** @deprecated Use {@link OnMaximizeToggle}. */
|
|
1778
2210
|
onMaximizeToggle() {
|
|
1779
|
-
this.
|
|
2211
|
+
return this.OnMaximizeToggle();
|
|
1780
2212
|
}
|
|
1781
2213
|
/**
|
|
1782
2214
|
* Handle entity record open request from artifact viewer plugin (React component)
|
|
1783
2215
|
* Propagates the event up to parent components
|
|
1784
2216
|
*/
|
|
2217
|
+
OnOpenEntityRecord(event) {
|
|
2218
|
+
this.OpenEntityRecord.emit(event);
|
|
2219
|
+
}
|
|
2220
|
+
/** @deprecated Use {@link OnOpenEntityRecord}. */
|
|
1785
2221
|
onOpenEntityRecord(event) {
|
|
1786
|
-
this.
|
|
2222
|
+
return this.OnOpenEntityRecord(event);
|
|
1787
2223
|
}
|
|
1788
2224
|
/**
|
|
1789
2225
|
* Handle navigation request from artifact viewer plugin.
|
|
1790
2226
|
* Propagates the event up to parent components for app-level navigation.
|
|
1791
2227
|
*/
|
|
1792
|
-
|
|
2228
|
+
OnNavigationRequest(event) {
|
|
1793
2229
|
this.navigationRequest.emit(event);
|
|
1794
2230
|
}
|
|
2231
|
+
/** @deprecated Use {@link OnNavigationRequest}. */
|
|
2232
|
+
onNavigationRequest(event) {
|
|
2233
|
+
return this.OnNavigationRequest(event);
|
|
2234
|
+
}
|
|
1795
2235
|
/**
|
|
1796
2236
|
* Resolves the DriverClass for an artifact type by traversing up the parent hierarchy.
|
|
1797
2237
|
* Returns the first DriverClass found, or null if none found in the hierarchy.
|
|
@@ -1818,7 +2258,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1818
2258
|
async getArtifactTypeById(id) {
|
|
1819
2259
|
try {
|
|
1820
2260
|
const md = this.ProviderToUse;
|
|
1821
|
-
const artifactType = await md.GetEntityObject('MJ: Artifact Types', this.
|
|
2261
|
+
const artifactType = await md.GetEntityObject('MJ: Artifact Types', this.CurrentUser);
|
|
1822
2262
|
const loaded = await artifactType.Load(id);
|
|
1823
2263
|
if (loaded) {
|
|
1824
2264
|
return artifactType;
|
|
@@ -1833,7 +2273,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1833
2273
|
/**
|
|
1834
2274
|
* Format JSON content using ParseJSONRecursive for deep parsing and formatting
|
|
1835
2275
|
*/
|
|
1836
|
-
|
|
2276
|
+
formatJSON(content) {
|
|
1837
2277
|
try {
|
|
1838
2278
|
// First parse the JSON string to an object
|
|
1839
2279
|
const obj = JSON.parse(content);
|
|
@@ -1875,7 +2315,7 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1875
2315
|
return baseIcons[tabName];
|
|
1876
2316
|
}
|
|
1877
2317
|
// Check plugin tabs
|
|
1878
|
-
const plugin = this.
|
|
2318
|
+
const plugin = this.PluginViewer?.pluginInstance;
|
|
1879
2319
|
if (plugin?.GetAdditionalTabs) {
|
|
1880
2320
|
const pluginTab = plugin.GetAdditionalTabs().find((t) => t.label === tabName);
|
|
1881
2321
|
if (pluginTab?.icon) {
|
|
@@ -1888,24 +2328,24 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1888
2328
|
* Set active tab
|
|
1889
2329
|
*/
|
|
1890
2330
|
SetActiveTab(tabName) {
|
|
1891
|
-
this.
|
|
2331
|
+
this.ActiveTab = tabName.toLowerCase();
|
|
1892
2332
|
}
|
|
1893
2333
|
/**
|
|
1894
2334
|
* Track artifact usage event
|
|
1895
2335
|
*/
|
|
1896
2336
|
async trackArtifactUsage(usageType) {
|
|
1897
2337
|
try {
|
|
1898
|
-
if (!this.artifactVersion?.ID || !this.
|
|
2338
|
+
if (!this.artifactVersion?.ID || !this.CurrentUser?.ID) {
|
|
1899
2339
|
return;
|
|
1900
2340
|
}
|
|
1901
2341
|
const md = this.ProviderToUse;
|
|
1902
2342
|
const usage = await md.GetEntityObject('MJ: Artifact Uses');
|
|
1903
2343
|
usage.ArtifactVersionID = this.artifactVersion.ID;
|
|
1904
|
-
usage.UserID = this.
|
|
2344
|
+
usage.UserID = this.CurrentUser.ID;
|
|
1905
2345
|
usage.UsageType = usageType;
|
|
1906
2346
|
usage.UsageContext = JSON.stringify({
|
|
1907
|
-
viewContext: this.
|
|
1908
|
-
contextCollectionId: this.
|
|
2347
|
+
viewContext: this.ViewContext,
|
|
2348
|
+
contextCollectionId: this.ContextCollectionId,
|
|
1909
2349
|
timestamp: new Date().toISOString()
|
|
1910
2350
|
});
|
|
1911
2351
|
// Save asynchronously - don't block UI
|
|
@@ -1922,11 +2362,19 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1922
2362
|
* Fallback priority: Plugin icon > Metadata icon > Hardcoded mapping > Generic icon
|
|
1923
2363
|
*/
|
|
1924
2364
|
/** Cached icon class — set once after artifact loads to avoid mid-cycle flicker */
|
|
1925
|
-
|
|
2365
|
+
ArtifactIcon = 'fa-file';
|
|
2366
|
+
/** @deprecated Use {@link ArtifactIcon}. */
|
|
2367
|
+
get artifactIcon() {
|
|
2368
|
+
return this.ArtifactIcon;
|
|
2369
|
+
}
|
|
2370
|
+
/** @deprecated Use {@link ArtifactIcon}. */
|
|
2371
|
+
set artifactIcon(value) {
|
|
2372
|
+
this.ArtifactIcon = value;
|
|
2373
|
+
}
|
|
1926
2374
|
/** Update the cached icon from the loaded artifact */
|
|
1927
2375
|
updateArtifactIcon() {
|
|
1928
|
-
this.
|
|
1929
|
-
? this.artifactIconService.getArtifactIcon(this.
|
|
2376
|
+
this.ArtifactIcon = this.Artifact
|
|
2377
|
+
? this.artifactIconService.getArtifactIcon(this.Artifact)
|
|
1930
2378
|
: 'fa-file';
|
|
1931
2379
|
}
|
|
1932
2380
|
/**
|
|
@@ -1935,9 +2383,9 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1935
2383
|
*/
|
|
1936
2384
|
OnAnalyze() {
|
|
1937
2385
|
const snapshot = this.GetCurrentStateSnapshot();
|
|
1938
|
-
if (snapshot && this.
|
|
1939
|
-
this.
|
|
1940
|
-
artifactId: this.
|
|
2386
|
+
if (snapshot && this.Artifact) {
|
|
2387
|
+
this.AnalyzeRequested.emit({
|
|
2388
|
+
artifactId: this.Artifact.ID,
|
|
1941
2389
|
snapshot
|
|
1942
2390
|
});
|
|
1943
2391
|
}
|
|
@@ -1950,15 +2398,15 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1950
2398
|
* Returns null if no plugin is loaded or the plugin has no snapshot.
|
|
1951
2399
|
*/
|
|
1952
2400
|
GetCurrentStateSnapshot() {
|
|
1953
|
-
return this.
|
|
2401
|
+
return this.PluginViewer?.pluginInstance?.GetCurrentStateSnapshot() ?? null;
|
|
1954
2402
|
}
|
|
1955
2403
|
static ɵfac = function ArtifactViewerPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ArtifactViewerPanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.MJNotificationService), i0.ɵɵdirectiveInject(i2.DomSanitizer), i0.ɵɵdirectiveInject(i3.ArtifactIconService), i0.ɵɵdirectiveInject(i4.ArtifactFileService)); };
|
|
1956
2404
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArtifactViewerPanelComponent, selectors: [["mj-artifact-viewer-panel"]], viewQuery: function ArtifactViewerPanelComponent_Query(rf, ctx) { if (rf & 1) {
|
|
1957
2405
|
i0.ɵɵviewQuery(ArtifactTypePluginViewerComponent, 5);
|
|
1958
2406
|
} if (rf & 2) {
|
|
1959
2407
|
let _t;
|
|
1960
|
-
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.
|
|
1961
|
-
} }, inputs: { artifactId: "artifactId", currentUser: "currentUser", environmentId: "environmentId", versionNumber: "versionNumber", showSaveToCollection: "showSaveToCollection", showHeader: "showHeader", showTabs: "showTabs", showCloseButton: "showCloseButton", showMaximizeButton: "showMaximizeButton", refreshTrigger: "refreshTrigger", viewContext: "viewContext", contextCollectionId: "contextCollectionId", canShare: "canShare", canEdit: "canEdit", isMaximized: "isMaximized" }, outputs: { closed: "closed", saveToCollectionRequested: "saveToCollectionRequested", navigateToLink: "navigateToLink", shareRequested: "shareRequested", maximizeToggled: "maximizeToggled", openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest", analyzeRequested: "analyzeRequested", applyFormRequested: "applyFormRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 6, consts: [[1, "artifact-viewer-panel"], [1, "panel-header"], [1, "panel-content"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load artifact", "Size", "compact", 3, "Message"], [1, "artifact-content-wrapper", 3, "no-tabs"], [1, "panel-header-left"], [1, "fas", 3, "ngClass"], [1, "header-description"], [1, "panel-header-right"], [1, "version-selector", 3, "click"], [1, "fas", "fa-history"], [1, "version-label"], [1, "fas", "fa-chevron-down", "dropdown-icon", 3, "open"], [1, "version-dropdown"], ["title", "Inspect & rate components", 1, "feedback-btn"], [1, "save-to-collection-btn", 3, "in-collection", "title"], ["title", "Share", 1, "share-btn"], ["title", "Analyze this artifact with AI", 1, "analyze-btn", 3, "click"], [1, "fas", "fa-magnifying-glass-chart"], [1, "maximize-btn", 3, "title"], ["title", "Close", 1, "close-btn"], [1, "fas", "fa-chevron-down", "dropdown-icon"], [1, "version-dropdown", 3, "click"], [1, "version-option", 3, "selected"], [1, "version-option", 3, "click"], [1, "version-number"], [1, "version-date"], [1, "fas", "fa-check"], ["title", "Inspect & rate components", 1, "feedback-btn", 3, "click"], [1, "fas", "fa-star-half-stroke"], [1, "save-to-collection-btn", 3, "click", "title"], ["title", "Share", 1, "share-btn", 3, "click"], [1, "fas", "fa-share-nodes"], [1, "maximize-btn", 3, "click", "title"], ["title", "Close", 1, "close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "fas", "fa-spinner", "fa-spin"], [1, "artifact-content-wrapper"], [1, "tab-navigation"], [1, "tab-content"], [1, "plugin-container", 3, "plugin-hidden"], [1, "display-content"], [1, "details-content"], [1, "artifact-meta"], [1, "meta-item"], [1, "meta-item", "full-width"], [1, "attributes-section"], [1, "links-container"], [1, "links-section"], ["Icon", "fa-solid fa-link", "Title", "No links available", "Size", "compact"], [1, "tab-btn", 3, "active"], [1, "tab-btn", 3, "click"], [3, "class"], [1, "plugin-container"], [3, "openEntityRecord", "navigationRequest", "pluginLoaded", "tabsChanged", "applyFormRequested", "artifactVersion", "artifactTypeName", "contentType", "readonly"], ["data-testid", "file-fallback", 1, "file-fallback"], [1, "display-toolbar"], ["title", "Print", 1, "btn-icon", 3, "click"], [1, "fas", "fa-print"], ["title", "Copy Content", 1, "btn-icon", 3, "click"], [1, "fas", "fa-copy"], [1, "markdown-content"], [1, "html-content", 3, "innerHTML"], [3, "data", "enableCollapsibleHeadings", "enableLineNumbers", "enableSmartypants", "enableHtml"], [1, "file-fallback-card"], ["aria-hidden", "true", 1, "fas", 3, "ngClass"], [1, "file-fallback-meta"], [1, "file-fallback-name"], [1, "file-fallback-sub"], ["type", "button", "title", "Download", 1, "btn-icon", "btn-primary", 3, "click", "disabled"], [1, "fas", "fa-download"], [1, "plaintext-viewer", "file-fallback-text"], [1, "attributes-list"], [1, "attribute-item"], [1, "attribute-empty-pill"], [1, "link-item", 3, "disabled", "clickable", "title"], [1, "link-item", 3, "click", "title"], [1, "link-icon"], [1, "fas", "fa-comments"], [1, "fas", "fa-folder"], [1, "link-content"], [1, "link-name"], [1, "link-type"], [1, "link-actions"], [1, "fas", "fa-arrow-right"], [1, "component-tab-content"], [4, "ngComponentOutlet", "ngComponentOutletInputs"], [1, "dynamic-tab-content"], [1, "markdown-viewer"], [1, "json-viewer"], [1, "code-viewer"], [1, "html-viewer", 3, "innerHTML"], [1, "plaintext-viewer"], [1, "json-toolbar"], ["title", "Copy JSON", 1, "btn-icon", 3, "click"], [1, "json-editor-container"], [2, "width", "100%", "height", "100%", 3, "value", "language", "readonly", "lineWrapping"], [1, "code-toolbar"], ["title", "Copy Code", 1, "btn-icon", 3, "click"], [1, "code-editor-container"]], template: function ArtifactViewerPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2408
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.PluginViewer = _t.first);
|
|
2409
|
+
} }, inputs: { artifactId: "artifactId", CurrentUser: "CurrentUser", currentUser: "currentUser", EnvironmentId: "EnvironmentId", environmentId: "environmentId", VersionNumber: "VersionNumber", versionNumber: "versionNumber", ShowSaveToCollection: "ShowSaveToCollection", showSaveToCollection: "showSaveToCollection", ShowHeader: "ShowHeader", showHeader: "showHeader", ShowTabs: "ShowTabs", showTabs: "showTabs", ShowCloseButton: "ShowCloseButton", showCloseButton: "showCloseButton", ShowMaximizeButton: "ShowMaximizeButton", showMaximizeButton: "showMaximizeButton", RefreshTrigger: "RefreshTrigger", refreshTrigger: "refreshTrigger", ViewContext: "ViewContext", viewContext: "viewContext", ContextCollectionId: "ContextCollectionId", contextCollectionId: "contextCollectionId", canShare: "canShare", canEdit: "canEdit", IsMaximized: "IsMaximized", isMaximized: "isMaximized" }, outputs: { Closed: "Closed", closed: "closed", SaveToCollectionRequested: "SaveToCollectionRequested", saveToCollectionRequested: "saveToCollectionRequested", NavigateToLink: "NavigateToLink", navigateToLink: "navigateToLink", ShareRequested: "ShareRequested", shareRequested: "shareRequested", MaximizeToggled: "MaximizeToggled", maximizeToggled: "maximizeToggled", OpenEntityRecord: "OpenEntityRecord", openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest", AnalyzeRequested: "AnalyzeRequested", analyzeRequested: "analyzeRequested", ApplyFormRequested: "ApplyFormRequested", applyFormRequested: "applyFormRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 6, consts: [[1, "artifact-viewer-panel"], [1, "panel-header"], [1, "panel-content"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load artifact", "Size", "compact", 3, "Message"], [1, "artifact-content-wrapper", 3, "no-tabs"], [1, "panel-header-left"], [1, "fas", 3, "ngClass"], [1, "header-description"], [1, "panel-header-right"], [1, "version-selector", 3, "click"], [1, "fas", "fa-history"], [1, "version-label"], [1, "fas", "fa-chevron-down", "dropdown-icon", 3, "open"], [1, "version-dropdown"], ["title", "Inspect & rate components", 1, "feedback-btn"], [1, "save-to-collection-btn", 3, "in-collection", "title"], ["title", "Share", 1, "share-btn"], ["title", "Analyze this artifact with AI", 1, "analyze-btn", 3, "click"], [1, "fas", "fa-magnifying-glass-chart"], [1, "maximize-btn", 3, "title"], ["title", "Close", 1, "close-btn"], [1, "fas", "fa-chevron-down", "dropdown-icon"], [1, "version-dropdown", 3, "click"], [1, "version-option", 3, "selected"], [1, "version-option", 3, "click"], [1, "version-number"], [1, "version-date"], [1, "fas", "fa-check"], ["title", "Inspect & rate components", 1, "feedback-btn", 3, "click"], [1, "fas", "fa-star-half-stroke"], [1, "save-to-collection-btn", 3, "click", "title"], ["title", "Share", 1, "share-btn", 3, "click"], [1, "fas", "fa-share-nodes"], [1, "maximize-btn", 3, "click", "title"], ["title", "Close", 1, "close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "fas", "fa-spinner", "fa-spin"], [1, "artifact-content-wrapper"], [1, "tab-navigation"], [1, "tab-content"], [1, "plugin-container", 3, "plugin-hidden"], [1, "display-content"], [1, "details-content"], [1, "artifact-meta"], [1, "meta-item"], [1, "meta-item", "full-width"], [1, "attributes-section"], [1, "links-container"], [1, "links-section"], ["Icon", "fa-solid fa-link", "Title", "No links available", "Size", "compact"], [1, "tab-btn", 3, "active"], [1, "tab-btn", 3, "click"], [3, "class"], [1, "plugin-container"], [3, "openEntityRecord", "navigationRequest", "pluginLoaded", "tabsChanged", "applyFormRequested", "artifactVersion", "artifactTypeName", "contentType", "readonly"], ["data-testid", "file-fallback", 1, "file-fallback"], [1, "display-toolbar"], ["title", "Print", 1, "btn-icon", 3, "click"], [1, "fas", "fa-print"], ["title", "Copy Content", 1, "btn-icon", 3, "click"], [1, "fas", "fa-copy"], [1, "markdown-content"], [1, "html-content", 3, "innerHTML"], [3, "data", "enableCollapsibleHeadings", "enableLineNumbers", "enableSmartypants", "enableHtml"], [1, "file-fallback-card"], ["aria-hidden", "true", 1, "fas", 3, "ngClass"], [1, "file-fallback-meta"], [1, "file-fallback-name"], [1, "file-fallback-sub"], ["type", "button", "title", "Download", 1, "btn-icon", "btn-primary", 3, "click", "disabled"], [1, "fas", "fa-download"], [1, "plaintext-viewer", "file-fallback-text"], [1, "attributes-list"], [1, "attribute-item"], [1, "attribute-empty-pill"], [1, "link-item", 3, "disabled", "clickable", "title"], [1, "link-item", 3, "click", "title"], [1, "link-icon"], [1, "fas", "fa-comments"], [1, "fas", "fa-folder"], [1, "link-content"], [1, "link-name"], [1, "link-type"], [1, "link-actions"], [1, "fas", "fa-arrow-right"], [1, "component-tab-content"], [4, "ngComponentOutlet", "ngComponentOutletInputs"], [1, "dynamic-tab-content"], [1, "markdown-viewer"], [1, "json-viewer"], [1, "code-viewer"], [1, "html-viewer", 3, "innerHTML"], [1, "plaintext-viewer"], [1, "json-toolbar"], ["title", "Copy JSON", 1, "btn-icon", 3, "click"], [1, "json-editor-container"], [2, "width", "100%", "height", "100%", 3, "value", "language", "readonly", "lineWrapping"], [1, "code-toolbar"], ["title", "Copy Code", 1, "btn-icon", 3, "click"], [1, "code-editor-container"]], template: function ArtifactViewerPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1962
2410
|
i0.ɵɵelementStart(0, "div", 0);
|
|
1963
2411
|
i0.ɵɵconditionalCreate(1, ArtifactViewerPanelComponent_Conditional_1_Template, 20, 13, "div", 1);
|
|
1964
2412
|
i0.ɵɵelementStart(2, "div", 2);
|
|
@@ -1983,53 +2431,93 @@ export class ArtifactViewerPanelComponent extends BaseAngularComponent {
|
|
|
1983
2431
|
args: [{ standalone: false, selector: 'mj-artifact-viewer-panel', template: "<div class=\"artifact-viewer-panel\" [class.no-header]=\"!showHeader\">\n @if (showHeader) {\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <h3>\n <i class=\"fas\" [ngClass]=\"artifactIcon\"></i>\n {{ displayName }}\n </h3>\n @if (displayDescription) {\n <p class=\"header-description\">{{ displayDescription }}</p>\n }\n </div>\n <div class=\"panel-header-right\">\n <div class=\"version-selector\"\n [class.clickable]=\"allVersions.length > 1 && viewContext !== 'collection'\"\n (click)=\"viewContext !== 'collection' && toggleVersionDropdown()\">\n <i class=\"fas fa-history\"></i>\n <span class=\"version-label\">v{{ selectedVersionNumber }}</span>\n @if (allVersions.length > 1 && viewContext !== 'collection') {\n <i class=\"fas fa-chevron-down dropdown-icon\" [class.open]=\"showVersionDropdown\"></i>\n }\n @if (showVersionDropdown) {\n <div class=\"version-dropdown\" (click)=\"$event.stopPropagation()\">\n @for (version of allVersions; track version.ID) {\n <div class=\"version-option\"\n [class.selected]=\"version.VersionNumber === selectedVersionNumber\"\n (click)=\"selectVersion(version); $event.stopPropagation()\">\n <span class=\"version-number\">v{{ version.VersionNumber }}</span>\n <span class=\"version-date\">{{ version.__mj_CreatedAt | date:'short' }}</span>\n @if (version.VersionNumber === selectedVersionNumber) {\n <i class=\"fas fa-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n @if (pluginViewer?.SupportsFeedback) {\n <button class=\"feedback-btn\"\n (click)=\"pluginViewer?.AskUserForFeedback()\"\n title=\"Inspect & rate components\">\n <i class=\"fas fa-star-half-stroke\"></i>\n </button>\n }\n @if (showSaveToCollection) {\n <button class=\"save-to-collection-btn\"\n [class.in-collection]=\"isInCollection\"\n (click)=\"onSaveToLibrary()\"\n [title]=\"isInCollection ? 'Current version saved to ' + currentVersionCollections.length + ' collection(s)' : 'Save to Collection'\">\n <i [class]=\"isInCollection ? 'fas fa-bookmark' : 'far fa-bookmark'\"></i>\n </button>\n }\n @if (canShare) {\n <button class=\"share-btn\"\n (click)=\"onShare()\"\n title=\"Share\">\n <i class=\"fas fa-share-nodes\"></i>\n </button>\n }\n <button class=\"analyze-btn\"\n (click)=\"OnAnalyze()\"\n title=\"Analyze this artifact with AI\">\n <i class=\"fas fa-magnifying-glass-chart\"></i>\n </button>\n @if (showMaximizeButton) {\n <button class=\"maximize-btn\"\n (click)=\"onMaximizeToggle()\"\n [title]=\"isMaximized ? 'Restore Width' : 'Maximize Width'\">\n <i class=\"fas\" [ngClass]=\"isMaximized ? 'fa-compress-arrows-alt' : 'fa-arrows-left-right'\"></i>\n </button>\n }\n @if (showCloseButton) {\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n }\n </div>\n </div>\n }\n\n <div class=\"panel-content\">\n @if (isLoading) {\n <div class=\"loading-state\">\n <i class=\"fas fa-spinner fa-spin\"></i>\n <span>Loading artifact...</span>\n </div>\n }\n\n @if (error) {\n <mj-empty-state\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load artifact\"\n [Message]=\"error\"\n Size=\"compact\" />\n }\n\n @if (!isLoading && !error && artifact) {\n <div class=\"artifact-content-wrapper\" [class.no-tabs]=\"!showTabs\">\n <!-- Tab Navigation (Dynamic) - Hidden when showTabs is false -->\n @if (showTabs) {\n <div class=\"tab-navigation\">\n @for (tab of allTabs; track tab) {\n <button class=\"tab-btn\"\n [class.active]=\"activeTab === tab.toLowerCase()\"\n (click)=\"SetActiveTab(tab)\">\n @if (GetTabIcon(tab)) {\n <i [class]=\"GetTabIcon(tab)!\"></i>\n }\n {{ tab }}\n </button>\n }\n </div>\n }\n\n <!-- Tab Content (Dynamic) -->\n <div class=\"tab-content\">\n <!-- Plugin Viewer - Created once, kept alive, shown/hidden with CSS -->\n @if (hasPlugin && artifactVersion && artifactTypeName) {\n <div class=\"plugin-container\" [class.plugin-hidden]=\"activeTab !== 'display'\">\n <mj-artifact-type-plugin-viewer\n [artifactVersion]=\"artifactVersion\"\n [artifactTypeName]=\"artifactTypeName\"\n [contentType]=\"contentType\"\n [readonly]=\"true\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (navigationRequest)=\"onNavigationRequest($event)\"\n (pluginLoaded)=\"onPluginLoaded()\"\n (tabsChanged)=\"onTabsChanged()\"\n (applyFormRequested)=\"applyFormRequested.emit($event)\">\n </mj-artifact-type-plugin-viewer>\n </div>\n }\n\n <!-- Display Tab - Fallback content when no plugin -->\n @if (activeTab === 'display' && (!hasPlugin || !artifactVersion)) {\n <div class=\"display-content\">\n @if (displayMarkdown || displayHtml) {\n <!-- Fallback to extracted markdown/HTML attributes -->\n <div class=\"display-toolbar\">\n <button class=\"btn-icon\" title=\"Print\" (click)=\"onPrintDisplayContent()\">\n <i class=\"fas fa-print\"></i> Print\n </button>\n <button class=\"btn-icon\" title=\"Copy Content\" (click)=\"onCopyDisplayContent()\">\n <i class=\"fas fa-copy\"></i> Copy\n </button>\n </div>\n @if (displayMarkdown) {\n <div class=\"markdown-content\">\n <mj-markdown [data]=\"displayMarkdown\"\n [enableCollapsibleHeadings]=\"true\"\n [enableLineNumbers]=\"true\"\n [enableSmartypants]=\"true\"\n [enableHtml]=\"true\"></mj-markdown>\n </div>\n } @else if (displayHtml) {\n <div class=\"html-content\" [innerHTML]=\"displayHtml\"></div>\n }\n } @else if (showFileFallback) {\n <!-- No viewer plugin for this type: a file card with a real Download, plus the text for text-like content -->\n <div class=\"file-fallback\" data-testid=\"file-fallback\">\n <div class=\"file-fallback-card\">\n <i class=\"fas\" [ngClass]=\"artifactIcon\" aria-hidden=\"true\"></i>\n <div class=\"file-fallback-meta\">\n <div class=\"file-fallback-name\">{{ fallbackFileName }}</div>\n <div class=\"file-fallback-sub\">{{ artifactVersion?.MimeType || artifact.Type }}@if (fallbackSize) { \u00B7 {{ fallbackSize }}}</div>\n </div>\n <button class=\"btn-icon btn-primary\" type=\"button\" (click)=\"downloadArtifactFile()\" [disabled]=\"fallbackBusy\" title=\"Download\">\n <i class=\"fas fa-download\"></i> {{ fallbackBusy ? 'Preparing\u2026' : 'Download' }}\n </button>\n </div>\n @if (fallbackText !== null) {\n <pre class=\"plaintext-viewer file-fallback-text\">{{ fallbackText }}</pre>\n @if (fallbackTextTruncated) {\n <div class=\"file-fallback-sub\">Showing the first part only \u2014 download the file for all of it.</div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Details Tab - Artifact metadata (always in DOM, hidden with CSS) -->\n <div class=\"details-content\" [class.tab-hidden]=\"activeTab !== 'details'\">\n <div class=\"artifact-meta\">\n <div class=\"meta-item\">\n <label>Type</label>\n <span>{{ artifact.Type }}</span>\n </div>\n <div class=\"meta-item\">\n <label>Version</label>\n <span>{{ artifactVersion?.VersionNumber || 1 }}</span>\n </div>\n <div class=\"meta-item\">\n <label>Created</label>\n <span>{{ artifact.__mj_CreatedAt | date:'short' }}</span>\n </div>\n <div class=\"meta-item\">\n <label>Updated</label>\n <span>{{ artifactVersion?.__mj_UpdatedAt | date:'short' }}</span>\n </div>\n @if (artifact.Description) {\n <div class=\"meta-item full-width\">\n <label>Artifact Description</label>\n <span>{{ artifact.Description }}</span>\n </div>\n }\n @if (artifactVersion?.Description && artifact.Description && artifactVersion?.Description !== artifact.Description) {\n <div class=\"meta-item full-width\">\n <label>Version Description</label>\n <span>{{ artifactVersion?.Description }}</span>\n </div>\n }\n </div>\n\n <!-- Version Attributes -->\n @if (filteredAttributes.length > 0) {\n <div class=\"attributes-section\">\n <h4>Version Attributes</h4>\n <div class=\"attributes-list\">\n @for (attr of filteredAttributes; track attr.ID) {\n <div class=\"attribute-item\">\n <label>{{ attr.Name }}</label>\n @if (attr.Value) {\n <span>{{ attr.Value }}</span>\n } @else {\n <span class=\"attribute-empty-pill\">Empty</span>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Links Tab - Conversations and Collections (always in DOM, hidden with CSS) -->\n <div class=\"links-container\" [class.tab-hidden]=\"activeTab !== 'links'\">\n @if (linksToShow.length > 0) {\n <div class=\"links-section\">\n @for (link of linksToShow; track link.id) {\n <div class=\"link-item\"\n [class.disabled]=\"!link.hasAccess\"\n [class.clickable]=\"link.hasAccess\"\n (click)=\"link.hasAccess ? onNavigateToLink(link) : null\"\n [title]=\"link.hasAccess ? 'Click to open' : 'No access'\">\n <div class=\"link-icon\">\n @if (link.type === 'conversation') {\n <i class=\"fas fa-comments\"></i>\n } @else {\n <i class=\"fas fa-folder\"></i>\n }\n </div>\n <div class=\"link-content\">\n <div class=\"link-name\">{{ link.name }}</div>\n <div class=\"link-type\">{{ link.type === 'conversation' ? 'Conversation' : 'Collection' }}</div>\n </div>\n <div class=\"link-actions\">\n <i class=\"fas fa-arrow-right\"></i>\n </div>\n </div>\n }\n </div>\n } @else {\n <mj-empty-state\n Icon=\"fa-solid fa-link\"\n Title=\"No links available\"\n Size=\"compact\" />\n }\n </div>\n\n <!-- Dynamic Plugin Tabs and Base Tabs (JSON, etc.) -->\n @if (activeTab !== 'display' && activeTab !== 'details' && activeTab !== 'links') {\n <!-- Check if this is a component tab -->\n @if (GetComponentTabType(activeTab); as componentType) {\n <div class=\"component-tab-content\">\n <ng-container *ngComponentOutlet=\"componentType; inputs: GetComponentInputs(activeTab)\"></ng-container>\n </div>\n } @else {\n @if (GetTabContent(activeTab); as tabData) {\n <div class=\"dynamic-tab-content\">\n @switch (tabData.type) {\n @case ('markdown') {\n <div class=\"markdown-viewer\">\n <mj-markdown [data]=\"tabData.content\"\n [enableCollapsibleHeadings]=\"true\"\n [enableLineNumbers]=\"true\"\n [enableSmartypants]=\"true\"\n [enableHtml]=\"true\"></mj-markdown>\n </div>\n }\n @case ('json') {\n <div class=\"json-viewer\">\n <div class=\"json-toolbar\">\n <button class=\"btn-icon\" title=\"Copy JSON\" (click)=\"onCopyToClipboard()\">\n <i class=\"fas fa-copy\"></i> Copy\n </button>\n </div>\n <div class=\"json-editor-container\">\n <mj-code-editor\n [value]=\"tabData.content\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n [lineWrapping]=\"true\"\n style=\"width: 100%; height: 100%;\">\n </mj-code-editor>\n </div>\n </div>\n }\n @case ('code') {\n <div class=\"code-viewer\">\n <div class=\"code-toolbar\">\n <button class=\"btn-icon\" title=\"Copy Code\" (click)=\"onCopyToClipboard()\">\n <i class=\"fas fa-copy\"></i> Copy\n </button>\n </div>\n <div class=\"code-editor-container\">\n <mj-code-editor\n [value]=\"tabData.content\"\n [language]=\"tabData.language || 'typescript'\"\n [readonly]=\"true\"\n [lineWrapping]=\"true\"\n style=\"width: 100%; height: 100%;\">\n </mj-code-editor>\n </div>\n </div>\n }\n @case ('html') {\n <div class=\"html-viewer\" [innerHTML]=\"tabData.content\"></div>\n }\n @case ('plaintext') {\n <pre class=\"plaintext-viewer\">{{ tabData.content }}</pre>\n }\n }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n</div>\n\n", styles: [".artifact-viewer-panel {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.panel-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n width: 100%;\n box-sizing: border-box;\n}\n\n.panel-header-left {\n flex: 1;\n min-width: 0;\n margin-right: 16px;\n}\n\n.panel-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n word-wrap: break-word;\n overflow-wrap: break-word;\n}\n\n.panel-header h3 i {\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.header-description {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: var(--mj-text-muted);\n font-weight: normal;\n word-wrap: break-word;\n overflow-wrap: break-word;\n}\n\n.panel-header-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n}\n\n.version-selector {\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n font-weight: 500;\n transition: all 0.2s;\n}\n\n.version-selector.clickable {\n cursor: pointer;\n}\n\n.version-selector.clickable:hover {\n background: var(--mj-border-default);\n}\n\n.version-selector i {\n font-size: 11px;\n}\n\n.version-selector .dropdown-icon {\n margin-left: 2px;\n transition: transform 0.2s;\n}\n\n.version-selector .dropdown-icon.open {\n transform: rotate(180deg);\n}\n\n.version-label {\n font-family: monospace;\n}\n\n.version-dropdown {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n min-width: 200px;\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 z-index: 1000;\n overflow: hidden;\n}\n\n.version-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n cursor: pointer;\n transition: background 0.15s;\n font-size: 13px;\n}\n\n.version-option:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.version-option.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.version-option .version-number {\n font-family: monospace;\n font-weight: 600;\n min-width: 35px;\n}\n\n.version-option .version-date {\n flex: 1;\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.version-option i.fa-check {\n color: var(--mj-status-success);\n margin-left: auto;\n}\n\n.feedback-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-disabled);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.feedback-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-brand-primary);\n}\n\n.feedback-btn i {\n font-size: 14px;\n}\n\n.save-to-collection-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-disabled);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.save-to-collection-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.save-to-collection-btn i {\n font-size: 14px;\n}\n\n.save-to-collection-btn.in-collection {\n color: var(--mj-brand-primary);\n}\n\n.save-to-collection-btn.in-collection:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.share-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-disabled);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.share-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-brand-primary);\n}\n\n.share-btn i {\n font-size: 14px;\n}\n\n.analyze-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-disabled);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.analyze-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.analyze-btn i {\n font-size: 14px;\n}\n\n.maximize-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.maximize-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n.maximize-btn i {\n font-size: 14px;\n}\n\n.close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n}\n\n.close-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n.close-btn i {\n font-size: 14px;\n}\n\n.panel-content {\n flex: 1;\n overflow: hidden;\n min-height: 0;\n display: flex;\n flex-direction: column;\n width: 100%;\n box-sizing: border-box;\n}\n\n.artifact-content-wrapper {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0;\n width: 100%;\n box-sizing: border-box;\n}\n\n.loading-state {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 40px 20px;\n color: var(--mj-text-muted);\n}\n\n.loading-state i {\n font-size: 24px;\n}\n\n.artifact-meta {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n gap: 16px;\n margin-bottom: 16px;\n padding: 16px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n flex-shrink: 0;\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.meta-item label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.meta-item span {\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.artifact-description {\n padding: 12px;\n margin-bottom: 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n border-radius: 4px;\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.json-viewer {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n min-height: 0;\n}\n\n.json-editor-container {\n flex: 1;\n overflow: hidden;\n min-height: 0;\n}\n\n.json-toolbar {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.btn-icon {\n display: flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n padding: 6px 12px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: all 0.2s;\n}\n\n.btn-icon:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon.btn-primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.btn-icon.btn-primary:hover {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n.btn-icon i {\n font-size: 14px;\n}\n\n/* Library Dialog Styles */\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n}\n\n.modal-content {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-lg);\n max-width: 500px;\n width: 90%;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.modal-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.modal-header h3 i {\n color: var(--mj-status-warning);\n}\n\n.modal-body {\n padding: 20px;\n overflow-y: auto;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.form-section {\n margin-bottom: 20px;\n}\n\n.form-section label {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.form-select,\n.form-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 6px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.2s;\n}\n\n.form-select:focus,\n.form-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.divider {\n display: flex;\n align-items: center;\n margin: 24px 0;\n color: var(--mj-text-disabled);\n font-size: 12px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.divider::before,\n.divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--mj-border-default);\n}\n\n.divider span {\n padding: 0 12px;\n}\n\n.create-collection-row {\n display: flex;\n gap: 8px;\n}\n\n.create-collection-row .form-input {\n flex: 1;\n}\n\n.btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n white-space: nowrap;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.btn-secondary {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-secondary);\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n}\n\n/* Tab Navigation */\n.tab-navigation {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 16px;\n background: var(--mj-bg-surface);\n}\n\n.tab-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 12px 20px;\n background: transparent;\n border: none;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 150ms ease;\n}\n\n.tab-btn:hover {\n color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-sunken);\n}\n\n.tab-btn.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.tab-btn i {\n font-size: 14px;\n}\n\n/* Tab Content */\n.tab-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: auto;\n width: 100%;\n box-sizing: border-box;\n margin-bottom: 5px;\n min-width: 0;\n min-height: 0;\n}\n\n/* Plugin Container */\n.plugin-container {\n width: 100%;\n height: 100%;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.plugin-container.plugin-hidden,\n.tab-hidden {\n display: none;\n}\n\n/* Ensure artifact viewer component takes full height */\n.plugin-container mj-artifact-type-plugin-viewer {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n width: 100%;\n}\n\n.display-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 0;\n min-width: 0;\n padding: 7px;\n}\n\n.display-toolbar {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n gap: 8px;\n z-index: 10;\n}\n\n.markdown-content,\n.html-content {\n flex: 1;\n font-size: 14px;\n line-height: 1.6;\n width: 100%;\n padding: 20px 10px 5px 20px; /* top right bottom left */\n box-sizing: border-box;\n overflow: auto;\n min-height: 0;\n}\n\n.details-content {\n padding: 20px;\n width: 100%;\n box-sizing: border-box;\n}\n\n.details-content .artifact-meta {\n margin-bottom: 0;\n padding: 20px;\n}\n\n.details-content .meta-item.full-width {\n grid-column: 1 / -1;\n}\n\n.attributes-section {\n margin-top: 24px;\n padding: 24px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n}\n\n.attributes-section h4 {\n margin: 0 0 16px 0;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.attributes-list {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 16px;\n}\n\n.attribute-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px;\n background: var(--mj-bg-surface);\n border-radius: 6px;\n border: 1px solid var(--mj-border-default);\n}\n\n.attribute-item label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.attribute-item span {\n font-size: 13px;\n color: var(--mj-text-primary);\n word-break: break-word;\n}\n\n.attribute-empty-pill {\n display: inline-block;\n padding: 2px 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n font-size: 11px;\n font-weight: 500;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n/* Links Tab */\n.links-container {\n padding: 20px;\n}\n\n.links-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.link-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n transition: all 0.15s;\n}\n\n.link-item.clickable {\n cursor: pointer;\n}\n\n.link-item.clickable:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n background: var(--mj-bg-surface-sunken);\n}\n\n.link-item.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.link-item:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.link-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border-radius: 8px;\n flex-shrink: 0;\n}\n\n.link-icon i {\n font-size: 18px;\n color: var(--mj-brand-primary);\n}\n\n.link-content {\n flex: 1;\n min-width: 0;\n}\n\n.link-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.link-type {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.link-actions {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n flex-shrink: 0;\n}\n\n.link-actions i {\n font-size: 14px;\n color: var(--mj-text-muted);\n transition: all 0.15s;\n}\n\n.link-item.clickable:hover .link-actions i {\n color: var(--mj-brand-primary);\n transform: translateX(2px);\n}\n\n/* Dynamic Tab Content Styles */\n.dynamic-tab-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 16px;\n}\n\n/* Custom component tab content */\n.component-tab-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: auto;\n padding: 16px;\n}\n\n.markdown-viewer {\n flex: 1;\n overflow: auto;\n padding: 20px;\n background: var(--mj-bg-surface);\n line-height: 1.6;\n}\n\n.markdown-viewer h1, .markdown-viewer h2, .markdown-viewer h3 {\n margin-top: 24px;\n margin-bottom: 12px;\n font-weight: 600;\n}\n\n.markdown-viewer h1 { font-size: 24px; }\n.markdown-viewer h2 { font-size: 20px; }\n.markdown-viewer h3 { font-size: 18px; }\n\n.markdown-viewer p {\n margin-bottom: 12px;\n}\n\n.markdown-viewer ul, .markdown-viewer ol {\n margin-bottom: 12px;\n padding-left: 24px;\n}\n\n.markdown-viewer code {\n background: var(--mj-bg-surface-sunken);\n padding: 2px 6px;\n border-radius: 3px;\n font-family: 'Courier New', monospace;\n font-size: 13px;\n}\n\n.markdown-viewer pre {\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 6px;\n overflow-x: auto;\n margin-bottom: 12px;\n}\n\n.markdown-viewer pre code {\n background: none;\n padding: 0;\n}\n\n.code-viewer {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n min-height: 0;\n}\n\n.code-toolbar {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.code-editor-container {\n flex: 1;\n overflow: hidden;\n min-height: 0;\n}\n\n.plaintext-viewer {\n flex: 1;\n overflow: auto;\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n font-family: 'Courier New', monospace;\n font-size: 13px;\n white-space: pre-wrap;\n}\n\n.html-viewer {\n flex: 1;\n overflow: auto;\n padding: 20px;\n background: var(--mj-bg-surface);\n}\n\n/* Mobile adjustments: 481px - 768px */\n@media (max-width: 768px) {\n /* Add top border with slight visual separation from top nav */\n .artifact-viewer-panel {\n border-top: 3px solid var(--mj-brand-primary);\n position: relative;\n }\n\n /* Create a small dark strip above the blue border to separate from top nav */\n .artifact-viewer-panel::before {\n content: '';\n position: absolute;\n top: -8px;\n left: 0;\n right: 0;\n height: 8px;\n background: var(--mj-brand-secondary);\n }\n\n .tab-navigation {\n padding: 0 8px;\n overflow-x: auto;\n overflow-y: hidden;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n }\n\n .tab-navigation::-webkit-scrollbar {\n display: none;\n }\n\n .tab-btn {\n padding: 10px 12px;\n font-size: 12px;\n white-space: nowrap;\n flex-shrink: 0;\n }\n\n .tab-btn i {\n font-size: 13px;\n }\n\n .panel-header {\n padding: 12px;\n }\n\n .dynamic-tab-content {\n padding: 12px;\n }\n\n .markdown-viewer {\n padding: 12px;\n }\n}\n\n/* Small Phone adjustments: <= 480px */\n@media (max-width: 480px) {\n .tab-navigation {\n padding: 0 4px;\n }\n\n .tab-btn {\n padding: 8px 10px;\n font-size: 11px;\n gap: 4px;\n }\n\n .tab-btn i {\n font-size: 12px;\n }\n\n .panel-header {\n padding: 8px;\n }\n\n .dynamic-tab-content {\n padding: 8px;\n }\n\n .markdown-viewer {\n padding: 8px;\n }\n}\n\n/* No-plugin fallback: a file card with a download action, text underneath when it is text-like */\n.file-fallback { display: flex; flex-direction: column; gap: var(--mj-space-3, 12px); padding: var(--mj-space-4, 16px); }\n.file-fallback-card {\n display: flex; align-items: center; gap: var(--mj-space-3, 12px);\n padding: var(--mj-space-3, 12px) var(--mj-space-4, 16px);\n border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card, var(--mj-bg-surface));\n}\n.file-fallback-card > i { font-size: 1.5rem; color: var(--mj-brand-primary); }\n.file-fallback-meta { flex: 1; min-width: 0; }\n.file-fallback-name { font-weight: var(--mj-font-semibold, 600); color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.file-fallback-sub { font-size: var(--mj-text-sm, 0.85rem); color: var(--mj-text-muted); }\n.file-fallback-text { max-height: 60vh; overflow: auto; margin: 0; }\n"] }]
|
|
1984
2432
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.MJNotificationService }, { type: i2.DomSanitizer }, { type: i3.ArtifactIconService }, { type: i4.ArtifactFileService }], { artifactId: [{
|
|
1985
2433
|
type: Input
|
|
2434
|
+
}], CurrentUser: [{
|
|
2435
|
+
type: Input
|
|
1986
2436
|
}], currentUser: [{
|
|
1987
2437
|
type: Input
|
|
2438
|
+
}], EnvironmentId: [{
|
|
2439
|
+
type: Input
|
|
1988
2440
|
}], environmentId: [{
|
|
1989
2441
|
type: Input
|
|
2442
|
+
}], VersionNumber: [{
|
|
2443
|
+
type: Input
|
|
1990
2444
|
}], versionNumber: [{
|
|
1991
2445
|
type: Input
|
|
2446
|
+
}], ShowSaveToCollection: [{
|
|
2447
|
+
type: Input
|
|
1992
2448
|
}], showSaveToCollection: [{
|
|
1993
2449
|
type: Input
|
|
2450
|
+
}], ShowHeader: [{
|
|
2451
|
+
type: Input
|
|
1994
2452
|
}], showHeader: [{
|
|
1995
2453
|
type: Input
|
|
2454
|
+
}], ShowTabs: [{
|
|
2455
|
+
type: Input
|
|
1996
2456
|
}], showTabs: [{
|
|
1997
2457
|
type: Input
|
|
2458
|
+
}], ShowCloseButton: [{
|
|
2459
|
+
type: Input
|
|
1998
2460
|
}], showCloseButton: [{
|
|
1999
2461
|
type: Input
|
|
2462
|
+
}], ShowMaximizeButton: [{
|
|
2463
|
+
type: Input
|
|
2000
2464
|
}], showMaximizeButton: [{
|
|
2001
2465
|
type: Input
|
|
2466
|
+
}], RefreshTrigger: [{
|
|
2467
|
+
type: Input
|
|
2002
2468
|
}], refreshTrigger: [{
|
|
2003
2469
|
type: Input
|
|
2470
|
+
}], ViewContext: [{
|
|
2471
|
+
type: Input
|
|
2004
2472
|
}], viewContext: [{
|
|
2005
2473
|
type: Input
|
|
2474
|
+
}], ContextCollectionId: [{
|
|
2475
|
+
type: Input
|
|
2006
2476
|
}], contextCollectionId: [{
|
|
2007
2477
|
type: Input
|
|
2008
2478
|
}], canShare: [{
|
|
2009
2479
|
type: Input
|
|
2010
2480
|
}], canEdit: [{
|
|
2011
2481
|
type: Input
|
|
2482
|
+
}], IsMaximized: [{
|
|
2483
|
+
type: Input
|
|
2012
2484
|
}], isMaximized: [{
|
|
2013
2485
|
type: Input
|
|
2486
|
+
}], Closed: [{
|
|
2487
|
+
type: Output
|
|
2014
2488
|
}], closed: [{
|
|
2015
2489
|
type: Output
|
|
2490
|
+
}], SaveToCollectionRequested: [{
|
|
2491
|
+
type: Output
|
|
2016
2492
|
}], saveToCollectionRequested: [{
|
|
2017
2493
|
type: Output
|
|
2494
|
+
}], NavigateToLink: [{
|
|
2495
|
+
type: Output
|
|
2018
2496
|
}], navigateToLink: [{
|
|
2019
2497
|
type: Output
|
|
2498
|
+
}], ShareRequested: [{
|
|
2499
|
+
type: Output
|
|
2020
2500
|
}], shareRequested: [{
|
|
2021
2501
|
type: Output
|
|
2502
|
+
}], MaximizeToggled: [{
|
|
2503
|
+
type: Output
|
|
2022
2504
|
}], maximizeToggled: [{
|
|
2023
2505
|
type: Output
|
|
2506
|
+
}], OpenEntityRecord: [{
|
|
2507
|
+
type: Output
|
|
2024
2508
|
}], openEntityRecord: [{
|
|
2025
2509
|
type: Output
|
|
2026
2510
|
}], navigationRequest: [{
|
|
2027
2511
|
type: Output
|
|
2512
|
+
}], AnalyzeRequested: [{
|
|
2513
|
+
type: Output
|
|
2028
2514
|
}], analyzeRequested: [{
|
|
2029
2515
|
type: Output
|
|
2516
|
+
}], ApplyFormRequested: [{
|
|
2517
|
+
type: Output
|
|
2030
2518
|
}], applyFormRequested: [{
|
|
2031
2519
|
type: Output
|
|
2032
|
-
}],
|
|
2520
|
+
}], PluginViewer: [{
|
|
2033
2521
|
type: ViewChild,
|
|
2034
2522
|
args: [ArtifactTypePluginViewerComponent]
|
|
2035
2523
|
}] }); })();
|