@memberjunction/ng-dashboards 2.48.0 → 2.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -2
- package/dist/AI/ai-dashboard.component.d.ts +2 -0
- package/dist/AI/ai-dashboard.component.d.ts.map +1 -1
- package/dist/AI/ai-dashboard.component.js +66 -43
- package/dist/AI/ai-dashboard.component.js.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +45 -58
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.d.ts +6 -1
- package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +368 -366
- package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
- package/dist/AI/components/agents/agent-filter-panel.component.js +83 -85
- package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
- package/dist/AI/components/charts/performance-heatmap.component.d.ts +66 -0
- package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -0
- package/dist/AI/components/charts/performance-heatmap.component.js +428 -0
- package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -0
- package/dist/AI/components/charts/time-series-chart.component.d.ts +66 -0
- package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -0
- package/dist/AI/components/charts/time-series-chart.component.js +547 -0
- package/dist/AI/components/charts/time-series-chart.component.js.map +1 -0
- package/dist/AI/components/execution-monitoring.component.d.ts +157 -5
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +2032 -20
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +211 -237
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +208 -226
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-filter-panel.component.js +97 -99
- package/dist/AI/components/prompts/prompt-filter-panel.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.js +381 -424
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.js +173 -191
- package/dist/AI/components/prompts/prompt-version-control.component.js.map +1 -1
- package/dist/AI/components/system/system-config-filter-panel.component.js +85 -87
- package/dist/AI/components/system/system-config-filter-panel.component.js.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +86 -99
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/widgets/kpi-card.component.d.ts +25 -0
- package/dist/AI/components/widgets/kpi-card.component.d.ts.map +1 -0
- package/dist/AI/components/widgets/kpi-card.component.js +163 -0
- package/dist/AI/components/widgets/kpi-card.component.js.map +1 -0
- package/dist/AI/components/widgets/live-execution-widget.component.d.ts +25 -0
- package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -0
- package/dist/AI/components/widgets/live-execution-widget.component.js +298 -0
- package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -0
- package/dist/AI/index.d.ts +7 -0
- package/dist/AI/index.d.ts.map +1 -0
- package/dist/AI/index.js +9 -0
- package/dist/AI/index.js.map +1 -0
- package/dist/AI/services/ai-instrumentation.service.d.ts +109 -0
- package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -0
- package/dist/AI/services/ai-instrumentation.service.js +490 -0
- package/dist/AI/services/ai-instrumentation.service.js.map +1 -0
- package/dist/Actions/actions-management-dashboard.component.js +40 -41
- package/dist/Actions/actions-management-dashboard.component.js.map +1 -1
- package/dist/Actions/components/actions-list-view.component.js +117 -134
- package/dist/Actions/components/actions-list-view.component.js.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +274 -296
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/categories-list-view.component.js +12 -14
- package/dist/Actions/components/categories-list-view.component.js.map +1 -1
- package/dist/Actions/components/code-management.component.js +12 -14
- package/dist/Actions/components/code-management.component.js.map +1 -1
- package/dist/Actions/components/entity-integration.component.js +12 -14
- package/dist/Actions/components/entity-integration.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +238 -256
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/executions-list-view.component.js +12 -14
- package/dist/Actions/components/executions-list-view.component.js.map +1 -1
- package/dist/Actions/components/scheduled-actions.component.js +12 -14
- package/dist/Actions/components/scheduled-actions.component.js.map +1 -1
- package/dist/Actions/components/security-permissions.component.js +12 -14
- package/dist/Actions/components/security-permissions.component.js.map +1 -1
- package/dist/EntityAdmin/components/entity-details.component.js +105 -107
- package/dist/EntityAdmin/components/entity-details.component.js.map +1 -1
- package/dist/EntityAdmin/components/entity-filter-panel.component.js +100 -102
- package/dist/EntityAdmin/components/entity-filter-panel.component.js.map +1 -1
- package/dist/EntityAdmin/components/erd-composite.component.js +84 -100
- package/dist/EntityAdmin/components/erd-composite.component.js.map +1 -1
- package/dist/EntityAdmin/components/erd-diagram.component.js +50 -50
- package/dist/EntityAdmin/components/erd-diagram.component.js.map +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.js +45 -49
- package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
- package/dist/generic/base-dashboard.js +28 -40
- package/dist/generic/base-dashboard.js.map +1 -1
- package/dist/module.d.ts +16 -12
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +36 -15
- package/dist/module.js.map +1 -1
- package/package.json +6 -6
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
11
2
|
import { Metadata, CompositeKey, LogError, LogStatus } from '@memberjunction/core';
|
|
12
3
|
import { Subject, BehaviorSubject } from 'rxjs';
|
|
@@ -701,45 +692,46 @@ function PromptVersionControlComponent_Conditional_2_Template(rf, ctx) { if (rf
|
|
|
701
692
|
i0.ɵɵconditional(ctx_r1.error ? 52 : -1);
|
|
702
693
|
} }
|
|
703
694
|
export class PromptVersionControlComponent {
|
|
695
|
+
notificationService;
|
|
696
|
+
prompt = null;
|
|
697
|
+
autoLoad = true;
|
|
698
|
+
showRestoreActions = true;
|
|
699
|
+
showComparison = true;
|
|
700
|
+
maxVersions = 50;
|
|
701
|
+
versionSelected = new EventEmitter();
|
|
702
|
+
versionRestored = new EventEmitter();
|
|
703
|
+
versionCompared = new EventEmitter();
|
|
704
|
+
// Data
|
|
705
|
+
versions = [];
|
|
706
|
+
recordChanges = [];
|
|
707
|
+
templateContents = new Map();
|
|
708
|
+
availablePrompts = [];
|
|
709
|
+
filteredAvailablePrompts = [];
|
|
710
|
+
// UI State
|
|
711
|
+
isLoading = false;
|
|
712
|
+
loadingMessage = '';
|
|
713
|
+
error = null;
|
|
714
|
+
currentView = 'timeline';
|
|
715
|
+
selectedVersion = null;
|
|
716
|
+
compareFromVersion = null;
|
|
717
|
+
compareToVersion = null;
|
|
718
|
+
comparisonResult = null;
|
|
719
|
+
// Filtering and sorting
|
|
720
|
+
filterBy = 'all';
|
|
721
|
+
sortDirection = 'desc';
|
|
722
|
+
searchTerm$ = new BehaviorSubject('');
|
|
723
|
+
promptSearchTerm$ = new BehaviorSubject('');
|
|
724
|
+
showSystemChanges = false;
|
|
725
|
+
// Timeline configuration
|
|
726
|
+
timelineConfig = {
|
|
727
|
+
showThumbnails: true,
|
|
728
|
+
showDiffs: true,
|
|
729
|
+
compactMode: false,
|
|
730
|
+
groupByDate: true
|
|
731
|
+
};
|
|
732
|
+
destroy$ = new Subject();
|
|
704
733
|
constructor(notificationService) {
|
|
705
734
|
this.notificationService = notificationService;
|
|
706
|
-
this.prompt = null;
|
|
707
|
-
this.autoLoad = true;
|
|
708
|
-
this.showRestoreActions = true;
|
|
709
|
-
this.showComparison = true;
|
|
710
|
-
this.maxVersions = 50;
|
|
711
|
-
this.versionSelected = new EventEmitter();
|
|
712
|
-
this.versionRestored = new EventEmitter();
|
|
713
|
-
this.versionCompared = new EventEmitter();
|
|
714
|
-
// Data
|
|
715
|
-
this.versions = [];
|
|
716
|
-
this.recordChanges = [];
|
|
717
|
-
this.templateContents = new Map();
|
|
718
|
-
this.availablePrompts = [];
|
|
719
|
-
this.filteredAvailablePrompts = [];
|
|
720
|
-
// UI State
|
|
721
|
-
this.isLoading = false;
|
|
722
|
-
this.loadingMessage = '';
|
|
723
|
-
this.error = null;
|
|
724
|
-
this.currentView = 'timeline';
|
|
725
|
-
this.selectedVersion = null;
|
|
726
|
-
this.compareFromVersion = null;
|
|
727
|
-
this.compareToVersion = null;
|
|
728
|
-
this.comparisonResult = null;
|
|
729
|
-
// Filtering and sorting
|
|
730
|
-
this.filterBy = 'all';
|
|
731
|
-
this.sortDirection = 'desc';
|
|
732
|
-
this.searchTerm$ = new BehaviorSubject('');
|
|
733
|
-
this.promptSearchTerm$ = new BehaviorSubject('');
|
|
734
|
-
this.showSystemChanges = false;
|
|
735
|
-
// Timeline configuration
|
|
736
|
-
this.timelineConfig = {
|
|
737
|
-
showThumbnails: true,
|
|
738
|
-
showDiffs: true,
|
|
739
|
-
compactMode: false,
|
|
740
|
-
groupByDate: true
|
|
741
|
-
};
|
|
742
|
-
this.destroy$ = new Subject();
|
|
743
735
|
}
|
|
744
736
|
ngOnInit() {
|
|
745
737
|
this.loadAvailablePrompts();
|
|
@@ -752,89 +744,85 @@ export class PromptVersionControlComponent {
|
|
|
752
744
|
this.destroy$.next();
|
|
753
745
|
this.destroy$.complete();
|
|
754
746
|
}
|
|
755
|
-
loadVersionHistory() {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
747
|
+
async loadVersionHistory() {
|
|
748
|
+
if (!this.prompt) {
|
|
749
|
+
this.error = 'No prompt specified for version history';
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
this.isLoading = true;
|
|
754
|
+
this.error = null;
|
|
755
|
+
this.loadingMessage = 'Loading version history...';
|
|
756
|
+
const md = new Metadata();
|
|
757
|
+
const primaryKey = new CompositeKey();
|
|
758
|
+
primaryKey.KeyValuePairs.push({ FieldName: 'ID', Value: this.prompt.ID });
|
|
759
|
+
// Get record changes using the new method
|
|
760
|
+
this.recordChanges = await md.GetRecordChanges('AI Prompts', primaryKey);
|
|
761
|
+
if (this.recordChanges.length === 0) {
|
|
762
|
+
this.versions = [];
|
|
763
|
+
LogStatus(`No version history found for prompt: ${this.prompt.Name}`);
|
|
759
764
|
return;
|
|
760
765
|
}
|
|
766
|
+
// Load template contents for versions that have template changes
|
|
767
|
+
await this.loadTemplateContents();
|
|
768
|
+
// Process record changes into version objects
|
|
769
|
+
this.processRecordChanges();
|
|
770
|
+
// Apply current filters
|
|
771
|
+
this.applyFilters();
|
|
772
|
+
LogStatus(`Loaded ${this.versions.length} versions for prompt: ${this.prompt.Name}`);
|
|
773
|
+
}
|
|
774
|
+
catch (error) {
|
|
775
|
+
this.error = 'Failed to load version history. Please try again.';
|
|
776
|
+
LogError('Error loading prompt version history', undefined, error);
|
|
777
|
+
}
|
|
778
|
+
finally {
|
|
779
|
+
this.isLoading = false;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async loadTemplateContents() {
|
|
783
|
+
const templateIds = new Set();
|
|
784
|
+
// Extract template IDs from record changes
|
|
785
|
+
this.recordChanges.forEach(change => {
|
|
761
786
|
try {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
787
|
+
const fullRecord = JSON.parse(change.FullRecordJSON);
|
|
788
|
+
if (fullRecord.TemplateID) {
|
|
789
|
+
templateIds.add(fullRecord.TemplateID);
|
|
790
|
+
}
|
|
791
|
+
// Also check changes JSON for template changes
|
|
792
|
+
if (change.ChangesJSON) {
|
|
793
|
+
const changes = JSON.parse(change.ChangesJSON);
|
|
794
|
+
if (changes.TemplateID && changes.TemplateID.newValue) {
|
|
795
|
+
templateIds.add(changes.TemplateID.newValue);
|
|
796
|
+
}
|
|
797
|
+
if (changes.TemplateID && changes.TemplateID.oldValue) {
|
|
798
|
+
templateIds.add(changes.TemplateID.oldValue);
|
|
799
|
+
}
|
|
774
800
|
}
|
|
775
|
-
// Load template contents for versions that have template changes
|
|
776
|
-
yield this.loadTemplateContents();
|
|
777
|
-
// Process record changes into version objects
|
|
778
|
-
this.processRecordChanges();
|
|
779
|
-
// Apply current filters
|
|
780
|
-
this.applyFilters();
|
|
781
|
-
LogStatus(`Loaded ${this.versions.length} versions for prompt: ${this.prompt.Name}`);
|
|
782
|
-
}
|
|
783
|
-
catch (error) {
|
|
784
|
-
this.error = 'Failed to load version history. Please try again.';
|
|
785
|
-
LogError('Error loading prompt version history', undefined, error);
|
|
786
801
|
}
|
|
787
|
-
|
|
788
|
-
|
|
802
|
+
catch (e) {
|
|
803
|
+
// Ignore parsing errors
|
|
789
804
|
}
|
|
790
805
|
});
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
//
|
|
796
|
-
|
|
806
|
+
// Load template content entities
|
|
807
|
+
if (templateIds.size > 0) {
|
|
808
|
+
this.loadingMessage = 'Loading template content history...';
|
|
809
|
+
// Note: We would need a way to get historical template content
|
|
810
|
+
// For now, we'll get current template content and note this limitation
|
|
811
|
+
const md = new Metadata();
|
|
812
|
+
for (const templateId of templateIds) {
|
|
797
813
|
try {
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
// Also check changes JSON for template changes
|
|
803
|
-
if (change.ChangesJSON) {
|
|
804
|
-
const changes = JSON.parse(change.ChangesJSON);
|
|
805
|
-
if (changes.TemplateID && changes.TemplateID.newValue) {
|
|
806
|
-
templateIds.add(changes.TemplateID.newValue);
|
|
807
|
-
}
|
|
808
|
-
if (changes.TemplateID && changes.TemplateID.oldValue) {
|
|
809
|
-
templateIds.add(changes.TemplateID.oldValue);
|
|
810
|
-
}
|
|
814
|
+
const templateContent = await md.GetEntityObject('Template Contents', md.CurrentUser);
|
|
815
|
+
const loaded = await templateContent.Load(templateId);
|
|
816
|
+
if (loaded) {
|
|
817
|
+
this.templateContents.set(templateId, templateContent);
|
|
811
818
|
}
|
|
812
819
|
}
|
|
813
820
|
catch (e) {
|
|
814
|
-
//
|
|
815
|
-
|
|
816
|
-
});
|
|
817
|
-
// Load template content entities
|
|
818
|
-
if (templateIds.size > 0) {
|
|
819
|
-
this.loadingMessage = 'Loading template content history...';
|
|
820
|
-
// Note: We would need a way to get historical template content
|
|
821
|
-
// For now, we'll get current template content and note this limitation
|
|
822
|
-
const md = new Metadata();
|
|
823
|
-
for (const templateId of templateIds) {
|
|
824
|
-
try {
|
|
825
|
-
const templateContent = yield md.GetEntityObject('Template Contents', md.CurrentUser);
|
|
826
|
-
const loaded = yield templateContent.Load(templateId);
|
|
827
|
-
if (loaded) {
|
|
828
|
-
this.templateContents.set(templateId, templateContent);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
catch (e) {
|
|
832
|
-
// Template content might not exist anymore
|
|
833
|
-
LogError(`Failed to load template content for ID: ${templateId}`, undefined, e);
|
|
834
|
-
}
|
|
821
|
+
// Template content might not exist anymore
|
|
822
|
+
LogError(`Failed to load template content for ID: ${templateId}`, undefined, e);
|
|
835
823
|
}
|
|
836
824
|
}
|
|
837
|
-
}
|
|
825
|
+
}
|
|
838
826
|
}
|
|
839
827
|
processRecordChanges() {
|
|
840
828
|
this.versions = this.recordChanges.map((change, index) => {
|
|
@@ -934,52 +922,50 @@ export class PromptVersionControlComponent {
|
|
|
934
922
|
applyFiltersPublic() {
|
|
935
923
|
this.applyFilters();
|
|
936
924
|
}
|
|
937
|
-
restoreVersion(version) {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
promptToRestore[field] = historicalData[field];
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
const saved = yield promptToRestore.Save();
|
|
963
|
-
if (saved) {
|
|
964
|
-
this.notificationService.CreateSimpleNotification(`Version ${version.version} restored successfully`, 'success', 3000);
|
|
965
|
-
this.versionRestored.emit(promptToRestore);
|
|
966
|
-
// Reload version history to reflect the new change
|
|
967
|
-
yield this.loadVersionHistory();
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
throw new Error('Failed to save restored version');
|
|
925
|
+
async restoreVersion(version) {
|
|
926
|
+
if (!version.canRestore || !this.prompt) {
|
|
927
|
+
this.notificationService.CreateSimpleNotification('Cannot restore this version', 'warning', 3000);
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const confirm = window.confirm(`Are you sure you want to restore to version ${version.version} from ${version.changedAt.toLocaleString()}? This will overwrite the current prompt.`);
|
|
931
|
+
if (!confirm)
|
|
932
|
+
return;
|
|
933
|
+
try {
|
|
934
|
+
this.isLoading = true;
|
|
935
|
+
this.loadingMessage = 'Restoring version...';
|
|
936
|
+
const md = new Metadata();
|
|
937
|
+
const promptToRestore = await md.GetEntityObject('AI Prompts', md.CurrentUser);
|
|
938
|
+
await promptToRestore.Load(this.prompt.ID);
|
|
939
|
+
// Apply the historical data
|
|
940
|
+
if (version.fullRecordJSON) {
|
|
941
|
+
const historicalData = version.fullRecordJSON;
|
|
942
|
+
// Update prompt fields (excluding system fields)
|
|
943
|
+
const fieldsToRestore = ['Name', 'Description', 'CategoryID', 'TypeID', 'Status', 'TemplateID'];
|
|
944
|
+
fieldsToRestore.forEach(field => {
|
|
945
|
+
if (historicalData[field] !== undefined) {
|
|
946
|
+
promptToRestore[field] = historicalData[field];
|
|
971
947
|
}
|
|
948
|
+
});
|
|
949
|
+
const saved = await promptToRestore.Save();
|
|
950
|
+
if (saved) {
|
|
951
|
+
this.notificationService.CreateSimpleNotification(`Version ${version.version} restored successfully`, 'success', 3000);
|
|
952
|
+
this.versionRestored.emit(promptToRestore);
|
|
953
|
+
// Reload version history to reflect the new change
|
|
954
|
+
await this.loadVersionHistory();
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
throw new Error('Failed to save restored version');
|
|
972
958
|
}
|
|
973
959
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
this.error = 'Failed to restore version. Please try again.';
|
|
963
|
+
LogError('Error restoring prompt version', undefined, error);
|
|
964
|
+
this.notificationService.CreateSimpleNotification('Failed to restore version', 'error', 4000);
|
|
965
|
+
}
|
|
966
|
+
finally {
|
|
967
|
+
this.isLoading = false;
|
|
968
|
+
}
|
|
983
969
|
}
|
|
984
970
|
startComparison(fromVersion, toVersion) {
|
|
985
971
|
this.compareFromVersion = fromVersion;
|
|
@@ -1026,12 +1012,11 @@ export class PromptVersionControlComponent {
|
|
|
1026
1012
|
this.versionCompared.emit(this.comparisonResult);
|
|
1027
1013
|
}
|
|
1028
1014
|
compareTemplateContent(differences) {
|
|
1029
|
-
|
|
1030
|
-
const
|
|
1031
|
-
const toTemplate = (_b = this.compareToVersion) === null || _b === void 0 ? void 0 : _b.templateContent;
|
|
1015
|
+
const fromTemplate = this.compareFromVersion?.templateContent;
|
|
1016
|
+
const toTemplate = this.compareToVersion?.templateContent;
|
|
1032
1017
|
if (fromTemplate || toTemplate) {
|
|
1033
|
-
const fromContent =
|
|
1034
|
-
const toContent =
|
|
1018
|
+
const fromContent = fromTemplate?.TemplateText || '';
|
|
1019
|
+
const toContent = toTemplate?.TemplateText || '';
|
|
1035
1020
|
if (fromContent !== toContent) {
|
|
1036
1021
|
differences.push({
|
|
1037
1022
|
fieldName: 'TemplateText',
|
|
@@ -1109,10 +1094,9 @@ export class PromptVersionControlComponent {
|
|
|
1109
1094
|
return label;
|
|
1110
1095
|
}
|
|
1111
1096
|
exportVersionHistory() {
|
|
1112
|
-
var _a, _b, _c;
|
|
1113
1097
|
const exportData = {
|
|
1114
|
-
promptId:
|
|
1115
|
-
promptName:
|
|
1098
|
+
promptId: this.prompt?.ID,
|
|
1099
|
+
promptName: this.prompt?.Name,
|
|
1116
1100
|
exportedAt: new Date().toISOString(),
|
|
1117
1101
|
versions: this.versions.map(v => ({
|
|
1118
1102
|
version: v.version,
|
|
@@ -1127,27 +1111,25 @@ export class PromptVersionControlComponent {
|
|
|
1127
1111
|
const url = URL.createObjectURL(blob);
|
|
1128
1112
|
const a = document.createElement('a');
|
|
1129
1113
|
a.href = url;
|
|
1130
|
-
a.download = `prompt-version-history-${
|
|
1114
|
+
a.download = `prompt-version-history-${this.prompt?.Name || 'unknown'}-${new Date().toISOString().split('T')[0]}.json`;
|
|
1131
1115
|
a.click();
|
|
1132
1116
|
URL.revokeObjectURL(url);
|
|
1133
1117
|
}
|
|
1134
1118
|
refreshHistory() {
|
|
1135
1119
|
this.loadVersionHistory();
|
|
1136
1120
|
}
|
|
1137
|
-
loadAvailablePrompts() {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1121
|
+
async loadAvailablePrompts() {
|
|
1122
|
+
try {
|
|
1123
|
+
const metadata = new Metadata();
|
|
1124
|
+
const promptEntity = await metadata.GetEntityObject('AI Prompts');
|
|
1125
|
+
const prompts = await promptEntity.GetAll();
|
|
1126
|
+
this.availablePrompts = prompts.sort((a, b) => a.Name.localeCompare(b.Name));
|
|
1127
|
+
this.filteredAvailablePrompts = [...this.availablePrompts];
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
console.error('Failed to load available prompts:', error);
|
|
1131
|
+
LogError('Failed to load available prompts', undefined, error);
|
|
1132
|
+
}
|
|
1151
1133
|
}
|
|
1152
1134
|
setupPromptFiltering() {
|
|
1153
1135
|
this.promptSearchTerm$.subscribe(searchTerm => {
|
|
@@ -1171,16 +1153,16 @@ export class PromptVersionControlComponent {
|
|
|
1171
1153
|
this.prompt = prompt;
|
|
1172
1154
|
this.loadVersionHistory();
|
|
1173
1155
|
}
|
|
1156
|
+
static ɵfac = function PromptVersionControlComponent_Factory(t) { return new (t || PromptVersionControlComponent)(i0.ɵɵdirectiveInject(i1.MJNotificationService)); };
|
|
1157
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PromptVersionControlComponent, selectors: [["app-prompt-version-control"]], inputs: { prompt: "prompt", autoLoad: "autoLoad", showRestoreActions: "showRestoreActions", showComparison: "showComparison", maxVersions: "maxVersions" }, outputs: { versionSelected: "versionSelected", versionRestored: "versionRestored", versionCompared: "versionCompared" }, decls: 3, vars: 1, consts: [[1, "prompt-version-control"], [1, "prompt-selector-section"], [1, "section-title"], [1, "fa-solid", "fa-history"], [1, "text-muted", "mb-3"], [1, "prompt-selector"], [1, "search-box", "mb-3"], [1, "input-group"], [1, "input-group-text"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search prompts...", 1, "form-control", 3, "ngModelChange", "ngModel"], [1, "prompts-list"], [1, "empty-state"], [1, "fa-solid", "fa-comment-dots", "text-muted"], [1, "text-muted"], [1, "prompt-item"], [1, "prompt-item", 3, "click"], [1, "prompt-info"], [1, "prompt-title"], [1, "prompt-description"], [1, "prompt-actions"], [1, "fa-solid", "fa-arrow-right"], [1, "version-toolbar"], [1, "toolbar-section"], [1, "toolbar-title"], [1, "prompt-name"], [1, "view-mode-toggle"], ["role", "group", 1, "btn-group", "btn-group-sm"], ["type", "radio", "name", "view", "id", "timelineView", "value", "timeline", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "timelineView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-timeline"], ["type", "radio", "name", "view", "id", "comparisonView", "value", "comparison", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "comparisonView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-code-compare"], ["type", "radio", "name", "view", "id", "detailsView", "value", "details", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "detailsView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-list"], [1, "filter-controls"], [1, "form-label"], [1, "form-select", "form-select-sm", 3, "ngModelChange", "ngModel"], ["value", "all"], ["value", "updates"], ["value", "major"], ["value", "template"], [1, "form-check", "form-switch"], ["type", "checkbox", "id", "showSystem", 1, "form-check-input", 3, "ngModelChange", "ngModel"], ["for", "showSystem", 1, "form-check-label"], [1, "search-controls"], ["type", "text", "placeholder", "Search changes...", 1, "form-control", "form-control-sm", 3, "ngModelChange", "ngModel"], [1, "action-buttons"], ["type", "button", 1, "btn", "btn-sm", "btn-outline-secondary", 3, "click", "title"], [1, "fa-solid"], ["type", "button", "title", "Refresh history", 1, "btn", "btn-sm", "btn-outline-secondary", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["type", "button", "title", "Export history", 1, "btn", "btn-sm", "btn-outline-info", 3, "click"], [1, "fa-solid", "fa-download"], [1, "loading-container"], [1, "error-container"], [1, "loading-content"], ["role", "status", 1, "spinner-border", "text-primary"], [1, "visually-hidden"], [1, "loading-message"], [1, "timeline-container"], [1, "comparison-container"], [1, "details-container"], [1, "version-timeline"], [1, "no-versions-message"], [1, "timeline-item", 3, "selected", "active"], [1, "timeline-item", 3, "click"], [1, "timeline-marker"], [1, "marker-icon"], [1, "timeline-line"], [1, "timeline-content"], [1, "version-header"], [1, "version-info"], [1, "version-label"], [1, "version-meta"], [1, "change-date"], [1, "changed-by"], [1, "change-source", "badge"], [1, "version-actions"], ["type", "button", "title", "Compare with next version", 1, "btn", "btn-sm", "btn-outline-primary"], ["type", "button", "title", "Restore this version", 1, "btn", "btn-sm", "btn-outline-success"], [1, "version-description"], [1, "changes-summary"], ["type", "button", "title", "Compare with next version", 1, "btn", "btn-sm", "btn-outline-primary", 3, "click"], ["type", "button", "title", "Restore this version", 1, "btn", "btn-sm", "btn-outline-success", 3, "click"], [1, "fa-solid", "fa-undo"], [1, "changes-list"], [1, "change-item"], [1, "field-name"], [1, "change-arrow"], [1, "new-value"], [1, "old-value"], [1, "fa-solid", "fa-info-circle", "text-muted"], [1, "comparison-selectors"], [1, "selector-group"], [1, "form-select", 3, "ngModelChange", "ngModel"], [3, "ngValue"], [1, "comparison-arrow"], [1, "comparison-results"], [1, "comparison-header"], [1, "comparison-title"], [1, "changes-count"], [1, "differences-list"], [1, "no-differences"], [1, "difference-item", 3, "class"], [1, "difference-item"], [1, "diff-header"], [1, "field-info"], [1, "change-type", "badge"], [1, "template-badge", "badge", "badge-info"], [1, "diff-content"], [1, "side-by-side-diff"], [1, "diff-added"], [1, "diff-removed"], [1, "diff-old"], [1, "diff-label"], [1, "diff-value", "old-value"], [1, "template-content"], [1, "diff-new"], [1, "diff-value", "new-value"], [1, "fa-solid", "fa-check-circle", "text-success"], [1, "version-details"], [1, "no-selection-message"], [1, "details-header"], [1, "details-title"], [1, "details-meta"], [1, "details-content"], [1, "detail-section"], [1, "detail-grid"], [1, "detail-item"], [1, "detail-label"], [1, "detail-value"], [1, "json-viewer"], [1, "json-content"], [1, "template-viewer"], [1, "fa-solid", "fa-hand-pointer", "text-muted"], ["role", "alert", 1, "alert", "alert-danger"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", 1, "btn", "btn-sm", "btn-outline-danger", "ms-2", 3, "click"]], template: function PromptVersionControlComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1158
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
1159
|
+
i0.ɵɵtemplate(1, PromptVersionControlComponent_Conditional_1_Template, 15, 2, "div", 1)(2, PromptVersionControlComponent_Conditional_2_Template, 53, 17);
|
|
1160
|
+
i0.ɵɵelementEnd();
|
|
1161
|
+
} if (rf & 2) {
|
|
1162
|
+
i0.ɵɵadvance();
|
|
1163
|
+
i0.ɵɵconditional(!ctx.prompt ? 1 : 2);
|
|
1164
|
+
} }, dependencies: [i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.SelectControlValueAccessor, i2.RadioControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.JsonPipe, i3.DatePipe], styles: [".prompt-version-control[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #f8f9fa;\n\n .version-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: white;\n border-bottom: 1px solid #dee2e6;\n flex-shrink: 0;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-section {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-title {\n margin: 0;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n\n i {\n color: #0d6efd;\n }\n\n .prompt-name {\n font-weight: 400;\n color: #6c757d;\n }\n }\n\n .filter-controls,\n .search-controls {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .form-label {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n }\n\n .form-select,\n .form-control {\n min-width: 120px;\n }\n\n .form-check {\n margin: 0;\n \n .form-check-label {\n font-size: 0.875rem;\n }\n }\n }\n\n .action-buttons {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n font-size: 0.875rem;\n }\n }\n }\n }\n\n .loading-container {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n\n .loading-content {\n text-align: center;\n \n .loading-message {\n margin-top: 1rem;\n color: #6c757d;\n }\n }\n }\n\n .timeline-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-timeline {\n max-width: 800px;\n margin: 0 auto;\n\n .timeline-item {\n display: flex;\n position: relative;\n margin-bottom: 2rem;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &:hover {\n .timeline-content {\n box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1);\n transform: translateY(-1px);\n }\n }\n\n &.selected {\n .timeline-content {\n border-color: #0d6efd;\n box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);\n }\n }\n\n &.active {\n .timeline-marker .marker-icon {\n border-color: #28a745;\n background-color: #28a745;\n color: white;\n }\n }\n\n .timeline-marker {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-right: 1.5rem;\n flex-shrink: 0;\n\n .marker-icon {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 3px solid;\n background: white;\n font-size: 1rem;\n z-index: 2;\n\n &.change-create {\n border-color: #28a745;\n color: #28a745;\n }\n\n &.change-update {\n border-color: #0d6efd;\n color: #0d6efd;\n }\n\n &.change-delete {\n border-color: #dc3545;\n color: #dc3545;\n }\n }\n\n .timeline-line {\n width: 2px;\n flex: 1;\n background: #dee2e6;\n margin-top: 0.5rem;\n min-height: 2rem;\n }\n }\n\n .timeline-content {\n flex: 1;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n padding: 1.5rem;\n transition: all 0.2s ease;\n\n .version-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n\n .version-info {\n .version-label {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .version-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n\n .change-date {\n font-weight: 500;\n }\n\n .badge {\n font-size: 0.75rem;\n }\n }\n }\n\n .version-actions {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n }\n }\n }\n\n .version-description {\n color: #495057;\n margin-bottom: 1rem;\n line-height: 1.5;\n }\n\n .changes-summary {\n .changes-list {\n .change-item {\n display: flex;\n align-items: center;\n padding: 0.5rem;\n background: #f8f9fa;\n border-radius: 0.375rem;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n\n .field-name {\n font-weight: 500;\n color: #495057;\n margin-right: 0.75rem;\n min-width: 100px;\n }\n\n .change-arrow {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .old-value {\n color: #dc3545;\n text-decoration: line-through;\n }\n\n .new-value {\n color: #28a745;\n font-weight: 500;\n }\n\n i {\n color: #6c757d;\n }\n }\n }\n }\n }\n }\n }\n }\n\n .no-versions-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0.5rem 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .comparison-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .comparison-selectors {\n display: flex;\n align-items: end;\n gap: 1rem;\n margin-bottom: 2rem;\n padding: 1rem;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n\n .selector-group {\n flex: 1;\n\n .form-label {\n font-weight: 500;\n color: #495057;\n }\n }\n\n .comparison-arrow {\n padding-bottom: 0.375rem;\n color: #6c757d;\n font-size: 1.25rem;\n }\n }\n\n .comparison-results {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .comparison-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .comparison-title {\n margin: 0;\n color: #495057;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .changes-count {\n font-size: 0.875rem;\n color: #6c757d;\n font-weight: 500;\n }\n }\n\n .differences-list {\n .difference-item {\n border-bottom: 1px solid #f1f3f4;\n padding: 1.5rem;\n\n &:last-child {\n border-bottom: none;\n }\n\n &.diff-added {\n border-left: 4px solid #28a745;\n }\n\n &.diff-modified {\n border-left: 4px solid #ffc107;\n }\n\n &.diff-removed {\n border-left: 4px solid #dc3545;\n }\n\n .diff-header {\n margin-bottom: 1rem;\n\n .field-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n\n .field-name {\n font-size: 1rem;\n color: #495057;\n }\n\n .badge {\n font-size: 0.75rem;\n \n &.template-badge {\n background-color: #6f42c1;\n }\n }\n }\n }\n\n .diff-content {\n .side-by-side-diff {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n\n .diff-old,\n .diff-new {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n\n &.old-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n\n &.new-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .template-content {\n margin: 0;\n background: transparent;\n border: none;\n padding: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n white-space: pre-wrap;\n }\n }\n }\n }\n\n .diff-added,\n .diff-removed {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n }\n }\n\n .diff-added .diff-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .diff-removed .diff-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n }\n }\n }\n\n .no-differences {\n text-align: center;\n padding: 3rem 1rem;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n }\n }\n }\n }\n\n .details-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-details {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .details-header {\n padding: 1.5rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .details-title {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .details-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n }\n }\n\n .details-content {\n padding: 1.5rem;\n\n .detail-section {\n margin-bottom: 2rem;\n\n &:last-child {\n margin-bottom: 0;\n }\n\n .section-title {\n margin: 0 0 1rem 0;\n color: #495057;\n font-weight: 600;\n font-size: 1rem;\n border-bottom: 1px solid #e9ecef;\n padding-bottom: 0.5rem;\n }\n\n .detail-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1rem;\n\n .detail-item {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n\n .detail-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: #6c757d;\n }\n\n .detail-value {\n font-size: 0.875rem;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n }\n\n .json-viewer,\n .template-viewer {\n .json-content,\n .template-content {\n background: #f8f9fa;\n border: 1px solid #dee2e6;\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 0;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n overflow-x: auto;\n max-height: 400px;\n overflow-y: auto;\n }\n }\n }\n }\n }\n\n .no-selection-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .error-container {\n padding: 1rem;\n\n .alert {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n}\n\n//[_ngcontent-%COMP%] Responsive[_ngcontent-%COMP%] adjustments\n@media[_ngcontent-%COMP%] (max-width[_ngcontent-%COMP%]: 1200px)[_ngcontent-%COMP%] {\n .prompt-version-control {\n .version-toolbar {\n .toolbar-section {\n min-width: 100%;\n justify-content: space-between;\n }\n }\n\n .comparison-container {\n .comparison-selectors {\n flex-direction: column;\n align-items: stretch;\n\n .comparison-arrow {\n align-self: center;\n padding: 0.5rem 0;\n }\n }\n\n .comparison-results {\n .differences-list {\n .difference-item {\n .diff-content {\n .side-by-side-diff {\n grid-template-columns: 1fr;\n gap: 0.5rem;\n }\n }\n }\n }\n }\n }\n }\n}\n\n@media (max-width: 768px) {\n .prompt-version-control[_ngcontent-%COMP%] {\n .version-toolbar {\n padding: 0.75rem;\n \n .toolbar-section {\n flex-wrap: wrap;\n gap: 0.5rem;\n }\n }\n\n .timeline-container {\n padding: 0.75rem;\n\n .version-timeline {\n .timeline-item {\n .timeline-marker {\n margin-right: 1rem;\n\n .marker-icon {\n width: 2rem;\n height: 2rem;\n font-size: 0.875rem;\n }\n }\n\n .timeline-content {\n padding: 1rem;\n\n .version-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 0.75rem;\n\n .version-actions {\n align-self: flex-end;\n }\n }\n }\n }\n }\n }\n\n .comparison-container,\n .details-container {\n padding: 0.75rem;\n }\n }\n\n .prompt-selector-section[_ngcontent-%COMP%] {\n padding: 2rem;\n background: white;\n border-radius: 8px;\n margin: 1rem;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n\n .section-title {\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n\n i {\n color: #007bff;\n }\n }\n\n .prompt-selector {\n .search-box {\n .input-group-text {\n background: #f8f9fa;\n border-color: #ced4da;\n\n i {\n color: #6c757d;\n }\n }\n\n .form-control {\n border-color: #ced4da;\n\n &:focus {\n border-color: #007bff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n }\n }\n }\n\n .prompts-list {\n max-height: 400px;\n overflow-y: auto;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n\n .empty-state {\n text-align: center;\n padding: 2rem;\n\n i {\n font-size: 2rem;\n margin-bottom: 0.5rem;\n }\n }\n\n .prompt-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid #dee2e6;\n cursor: pointer;\n transition: background-color 0.2s;\n\n &:last-child {\n border-bottom: none;\n }\n\n &:hover {\n background-color: #f8f9fa;\n }\n\n .prompt-info {\n flex: 1;\n\n .prompt-title {\n margin: 0 0 0.25rem 0;\n font-weight: 600;\n color: #495057;\n }\n\n .prompt-description {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n line-height: 1.4;\n }\n }\n\n .prompt-actions {\n color: #007bff;\n opacity: 0.7;\n transition: opacity 0.2s;\n }\n\n &:hover .prompt-actions {\n opacity: 1;\n }\n }\n }\n }\n }\n}"] });
|
|
1174
1165
|
}
|
|
1175
|
-
PromptVersionControlComponent.ɵfac = function PromptVersionControlComponent_Factory(t) { return new (t || PromptVersionControlComponent)(i0.ɵɵdirectiveInject(i1.MJNotificationService)); };
|
|
1176
|
-
PromptVersionControlComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PromptVersionControlComponent, selectors: [["app-prompt-version-control"]], inputs: { prompt: "prompt", autoLoad: "autoLoad", showRestoreActions: "showRestoreActions", showComparison: "showComparison", maxVersions: "maxVersions" }, outputs: { versionSelected: "versionSelected", versionRestored: "versionRestored", versionCompared: "versionCompared" }, decls: 3, vars: 1, consts: [[1, "prompt-version-control"], [1, "prompt-selector-section"], [1, "section-title"], [1, "fa-solid", "fa-history"], [1, "text-muted", "mb-3"], [1, "prompt-selector"], [1, "search-box", "mb-3"], [1, "input-group"], [1, "input-group-text"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search prompts...", 1, "form-control", 3, "ngModelChange", "ngModel"], [1, "prompts-list"], [1, "empty-state"], [1, "fa-solid", "fa-comment-dots", "text-muted"], [1, "text-muted"], [1, "prompt-item"], [1, "prompt-item", 3, "click"], [1, "prompt-info"], [1, "prompt-title"], [1, "prompt-description"], [1, "prompt-actions"], [1, "fa-solid", "fa-arrow-right"], [1, "version-toolbar"], [1, "toolbar-section"], [1, "toolbar-title"], [1, "prompt-name"], [1, "view-mode-toggle"], ["role", "group", 1, "btn-group", "btn-group-sm"], ["type", "radio", "name", "view", "id", "timelineView", "value", "timeline", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "timelineView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-timeline"], ["type", "radio", "name", "view", "id", "comparisonView", "value", "comparison", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "comparisonView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-code-compare"], ["type", "radio", "name", "view", "id", "detailsView", "value", "details", 1, "btn-check", 3, "ngModelChange", "ngModel"], ["for", "detailsView", 1, "btn", "btn-outline-primary"], [1, "fa-solid", "fa-list"], [1, "filter-controls"], [1, "form-label"], [1, "form-select", "form-select-sm", 3, "ngModelChange", "ngModel"], ["value", "all"], ["value", "updates"], ["value", "major"], ["value", "template"], [1, "form-check", "form-switch"], ["type", "checkbox", "id", "showSystem", 1, "form-check-input", 3, "ngModelChange", "ngModel"], ["for", "showSystem", 1, "form-check-label"], [1, "search-controls"], ["type", "text", "placeholder", "Search changes...", 1, "form-control", "form-control-sm", 3, "ngModelChange", "ngModel"], [1, "action-buttons"], ["type", "button", 1, "btn", "btn-sm", "btn-outline-secondary", 3, "click", "title"], [1, "fa-solid"], ["type", "button", "title", "Refresh history", 1, "btn", "btn-sm", "btn-outline-secondary", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["type", "button", "title", "Export history", 1, "btn", "btn-sm", "btn-outline-info", 3, "click"], [1, "fa-solid", "fa-download"], [1, "loading-container"], [1, "error-container"], [1, "loading-content"], ["role", "status", 1, "spinner-border", "text-primary"], [1, "visually-hidden"], [1, "loading-message"], [1, "timeline-container"], [1, "comparison-container"], [1, "details-container"], [1, "version-timeline"], [1, "no-versions-message"], [1, "timeline-item", 3, "selected", "active"], [1, "timeline-item", 3, "click"], [1, "timeline-marker"], [1, "marker-icon"], [1, "timeline-line"], [1, "timeline-content"], [1, "version-header"], [1, "version-info"], [1, "version-label"], [1, "version-meta"], [1, "change-date"], [1, "changed-by"], [1, "change-source", "badge"], [1, "version-actions"], ["type", "button", "title", "Compare with next version", 1, "btn", "btn-sm", "btn-outline-primary"], ["type", "button", "title", "Restore this version", 1, "btn", "btn-sm", "btn-outline-success"], [1, "version-description"], [1, "changes-summary"], ["type", "button", "title", "Compare with next version", 1, "btn", "btn-sm", "btn-outline-primary", 3, "click"], ["type", "button", "title", "Restore this version", 1, "btn", "btn-sm", "btn-outline-success", 3, "click"], [1, "fa-solid", "fa-undo"], [1, "changes-list"], [1, "change-item"], [1, "field-name"], [1, "change-arrow"], [1, "new-value"], [1, "old-value"], [1, "fa-solid", "fa-info-circle", "text-muted"], [1, "comparison-selectors"], [1, "selector-group"], [1, "form-select", 3, "ngModelChange", "ngModel"], [3, "ngValue"], [1, "comparison-arrow"], [1, "comparison-results"], [1, "comparison-header"], [1, "comparison-title"], [1, "changes-count"], [1, "differences-list"], [1, "no-differences"], [1, "difference-item", 3, "class"], [1, "difference-item"], [1, "diff-header"], [1, "field-info"], [1, "change-type", "badge"], [1, "template-badge", "badge", "badge-info"], [1, "diff-content"], [1, "side-by-side-diff"], [1, "diff-added"], [1, "diff-removed"], [1, "diff-old"], [1, "diff-label"], [1, "diff-value", "old-value"], [1, "template-content"], [1, "diff-new"], [1, "diff-value", "new-value"], [1, "fa-solid", "fa-check-circle", "text-success"], [1, "version-details"], [1, "no-selection-message"], [1, "details-header"], [1, "details-title"], [1, "details-meta"], [1, "details-content"], [1, "detail-section"], [1, "detail-grid"], [1, "detail-item"], [1, "detail-label"], [1, "detail-value"], [1, "json-viewer"], [1, "json-content"], [1, "template-viewer"], [1, "fa-solid", "fa-hand-pointer", "text-muted"], ["role", "alert", 1, "alert", "alert-danger"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", 1, "btn", "btn-sm", "btn-outline-danger", "ms-2", 3, "click"]], template: function PromptVersionControlComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1177
|
-
i0.ɵɵelementStart(0, "div", 0);
|
|
1178
|
-
i0.ɵɵtemplate(1, PromptVersionControlComponent_Conditional_1_Template, 15, 2, "div", 1)(2, PromptVersionControlComponent_Conditional_2_Template, 53, 17);
|
|
1179
|
-
i0.ɵɵelementEnd();
|
|
1180
|
-
} if (rf & 2) {
|
|
1181
|
-
i0.ɵɵadvance();
|
|
1182
|
-
i0.ɵɵconditional(!ctx.prompt ? 1 : 2);
|
|
1183
|
-
} }, dependencies: [i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.SelectControlValueAccessor, i2.RadioControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.JsonPipe, i3.DatePipe], styles: [".prompt-version-control[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #f8f9fa;\n\n .version-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: white;\n border-bottom: 1px solid #dee2e6;\n flex-shrink: 0;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-section {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-title {\n margin: 0;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n\n i {\n color: #0d6efd;\n }\n\n .prompt-name {\n font-weight: 400;\n color: #6c757d;\n }\n }\n\n .filter-controls,\n .search-controls {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .form-label {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n }\n\n .form-select,\n .form-control {\n min-width: 120px;\n }\n\n .form-check {\n margin: 0;\n \n .form-check-label {\n font-size: 0.875rem;\n }\n }\n }\n\n .action-buttons {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n font-size: 0.875rem;\n }\n }\n }\n }\n\n .loading-container {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n\n .loading-content {\n text-align: center;\n \n .loading-message {\n margin-top: 1rem;\n color: #6c757d;\n }\n }\n }\n\n .timeline-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-timeline {\n max-width: 800px;\n margin: 0 auto;\n\n .timeline-item {\n display: flex;\n position: relative;\n margin-bottom: 2rem;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &:hover {\n .timeline-content {\n box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1);\n transform: translateY(-1px);\n }\n }\n\n &.selected {\n .timeline-content {\n border-color: #0d6efd;\n box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);\n }\n }\n\n &.active {\n .timeline-marker .marker-icon {\n border-color: #28a745;\n background-color: #28a745;\n color: white;\n }\n }\n\n .timeline-marker {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-right: 1.5rem;\n flex-shrink: 0;\n\n .marker-icon {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 3px solid;\n background: white;\n font-size: 1rem;\n z-index: 2;\n\n &.change-create {\n border-color: #28a745;\n color: #28a745;\n }\n\n &.change-update {\n border-color: #0d6efd;\n color: #0d6efd;\n }\n\n &.change-delete {\n border-color: #dc3545;\n color: #dc3545;\n }\n }\n\n .timeline-line {\n width: 2px;\n flex: 1;\n background: #dee2e6;\n margin-top: 0.5rem;\n min-height: 2rem;\n }\n }\n\n .timeline-content {\n flex: 1;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n padding: 1.5rem;\n transition: all 0.2s ease;\n\n .version-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n\n .version-info {\n .version-label {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .version-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n\n .change-date {\n font-weight: 500;\n }\n\n .badge {\n font-size: 0.75rem;\n }\n }\n }\n\n .version-actions {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n }\n }\n }\n\n .version-description {\n color: #495057;\n margin-bottom: 1rem;\n line-height: 1.5;\n }\n\n .changes-summary {\n .changes-list {\n .change-item {\n display: flex;\n align-items: center;\n padding: 0.5rem;\n background: #f8f9fa;\n border-radius: 0.375rem;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n\n .field-name {\n font-weight: 500;\n color: #495057;\n margin-right: 0.75rem;\n min-width: 100px;\n }\n\n .change-arrow {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .old-value {\n color: #dc3545;\n text-decoration: line-through;\n }\n\n .new-value {\n color: #28a745;\n font-weight: 500;\n }\n\n i {\n color: #6c757d;\n }\n }\n }\n }\n }\n }\n }\n }\n\n .no-versions-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0.5rem 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .comparison-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .comparison-selectors {\n display: flex;\n align-items: end;\n gap: 1rem;\n margin-bottom: 2rem;\n padding: 1rem;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n\n .selector-group {\n flex: 1;\n\n .form-label {\n font-weight: 500;\n color: #495057;\n }\n }\n\n .comparison-arrow {\n padding-bottom: 0.375rem;\n color: #6c757d;\n font-size: 1.25rem;\n }\n }\n\n .comparison-results {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .comparison-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .comparison-title {\n margin: 0;\n color: #495057;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .changes-count {\n font-size: 0.875rem;\n color: #6c757d;\n font-weight: 500;\n }\n }\n\n .differences-list {\n .difference-item {\n border-bottom: 1px solid #f1f3f4;\n padding: 1.5rem;\n\n &:last-child {\n border-bottom: none;\n }\n\n &.diff-added {\n border-left: 4px solid #28a745;\n }\n\n &.diff-modified {\n border-left: 4px solid #ffc107;\n }\n\n &.diff-removed {\n border-left: 4px solid #dc3545;\n }\n\n .diff-header {\n margin-bottom: 1rem;\n\n .field-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n\n .field-name {\n font-size: 1rem;\n color: #495057;\n }\n\n .badge {\n font-size: 0.75rem;\n \n &.template-badge {\n background-color: #6f42c1;\n }\n }\n }\n }\n\n .diff-content {\n .side-by-side-diff {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n\n .diff-old,\n .diff-new {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n\n &.old-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n\n &.new-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .template-content {\n margin: 0;\n background: transparent;\n border: none;\n padding: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n white-space: pre-wrap;\n }\n }\n }\n }\n\n .diff-added,\n .diff-removed {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n }\n }\n\n .diff-added .diff-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .diff-removed .diff-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n }\n }\n }\n\n .no-differences {\n text-align: center;\n padding: 3rem 1rem;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n }\n }\n }\n }\n\n .details-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-details {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .details-header {\n padding: 1.5rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .details-title {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .details-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n }\n }\n\n .details-content {\n padding: 1.5rem;\n\n .detail-section {\n margin-bottom: 2rem;\n\n &:last-child {\n margin-bottom: 0;\n }\n\n .section-title {\n margin: 0 0 1rem 0;\n color: #495057;\n font-weight: 600;\n font-size: 1rem;\n border-bottom: 1px solid #e9ecef;\n padding-bottom: 0.5rem;\n }\n\n .detail-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1rem;\n\n .detail-item {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n\n .detail-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: #6c757d;\n }\n\n .detail-value {\n font-size: 0.875rem;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n }\n\n .json-viewer,\n .template-viewer {\n .json-content,\n .template-content {\n background: #f8f9fa;\n border: 1px solid #dee2e6;\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 0;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n overflow-x: auto;\n max-height: 400px;\n overflow-y: auto;\n }\n }\n }\n }\n }\n\n .no-selection-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .error-container {\n padding: 1rem;\n\n .alert {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n}\n\n//[_ngcontent-%COMP%] Responsive[_ngcontent-%COMP%] adjustments\n@media[_ngcontent-%COMP%] (max-width[_ngcontent-%COMP%]: 1200px)[_ngcontent-%COMP%] {\n .prompt-version-control {\n .version-toolbar {\n .toolbar-section {\n min-width: 100%;\n justify-content: space-between;\n }\n }\n\n .comparison-container {\n .comparison-selectors {\n flex-direction: column;\n align-items: stretch;\n\n .comparison-arrow {\n align-self: center;\n padding: 0.5rem 0;\n }\n }\n\n .comparison-results {\n .differences-list {\n .difference-item {\n .diff-content {\n .side-by-side-diff {\n grid-template-columns: 1fr;\n gap: 0.5rem;\n }\n }\n }\n }\n }\n }\n }\n}\n\n@media (max-width: 768px) {\n .prompt-version-control[_ngcontent-%COMP%] {\n .version-toolbar {\n padding: 0.75rem;\n \n .toolbar-section {\n flex-wrap: wrap;\n gap: 0.5rem;\n }\n }\n\n .timeline-container {\n padding: 0.75rem;\n\n .version-timeline {\n .timeline-item {\n .timeline-marker {\n margin-right: 1rem;\n\n .marker-icon {\n width: 2rem;\n height: 2rem;\n font-size: 0.875rem;\n }\n }\n\n .timeline-content {\n padding: 1rem;\n\n .version-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 0.75rem;\n\n .version-actions {\n align-self: flex-end;\n }\n }\n }\n }\n }\n }\n\n .comparison-container,\n .details-container {\n padding: 0.75rem;\n }\n }\n\n .prompt-selector-section[_ngcontent-%COMP%] {\n padding: 2rem;\n background: white;\n border-radius: 8px;\n margin: 1rem;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n\n .section-title {\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n\n i {\n color: #007bff;\n }\n }\n\n .prompt-selector {\n .search-box {\n .input-group-text {\n background: #f8f9fa;\n border-color: #ced4da;\n\n i {\n color: #6c757d;\n }\n }\n\n .form-control {\n border-color: #ced4da;\n\n &:focus {\n border-color: #007bff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n }\n }\n }\n\n .prompts-list {\n max-height: 400px;\n overflow-y: auto;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n\n .empty-state {\n text-align: center;\n padding: 2rem;\n\n i {\n font-size: 2rem;\n margin-bottom: 0.5rem;\n }\n }\n\n .prompt-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid #dee2e6;\n cursor: pointer;\n transition: background-color 0.2s;\n\n &:last-child {\n border-bottom: none;\n }\n\n &:hover {\n background-color: #f8f9fa;\n }\n\n .prompt-info {\n flex: 1;\n\n .prompt-title {\n margin: 0 0 0.25rem 0;\n font-weight: 600;\n color: #495057;\n }\n\n .prompt-description {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n line-height: 1.4;\n }\n }\n\n .prompt-actions {\n color: #007bff;\n opacity: 0.7;\n transition: opacity 0.2s;\n }\n\n &:hover .prompt-actions {\n opacity: 1;\n }\n }\n }\n }\n }\n}"] });
|
|
1184
1166
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PromptVersionControlComponent, [{
|
|
1185
1167
|
type: Component,
|
|
1186
1168
|
args: [{ selector: 'app-prompt-version-control', template: "<div class=\"prompt-version-control\">\n <!-- Prompt Selector (when no prompt selected) -->\n @if (!prompt) {\n <div class=\"prompt-selector-section\">\n <h5 class=\"section-title\">\n <i class=\"fa-solid fa-history\"></i>\n Version History\n </h5>\n <p class=\"text-muted mb-3\">Select a prompt to view its version history</p>\n \n <div class=\"prompt-selector\">\n <div class=\"search-box mb-3\">\n <div class=\"input-group\">\n <span class=\"input-group-text\">\n <i class=\"fa-solid fa-search\"></i>\n </span>\n <input \n type=\"text\" \n class=\"form-control\" \n placeholder=\"Search prompts...\"\n [ngModel]=\"promptSearchTerm$.value\"\n (ngModelChange)=\"onPromptSearchChange($event)\">\n </div>\n </div>\n \n <div class=\"prompts-list\">\n @if (availablePrompts.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-comment-dots text-muted\"></i>\n <p class=\"text-muted\">No prompts available</p>\n </div>\n } @else {\n @for (promptItem of filteredAvailablePrompts; track promptItem.ID) {\n <div class=\"prompt-item\" (click)=\"selectPromptForHistory(promptItem)\">\n <div class=\"prompt-info\">\n <h6 class=\"prompt-title\">{{ promptItem.Name }}</h6>\n @if (promptItem.Description) {\n <p class=\"prompt-description\">{{ promptItem.Description }}</p>\n }\n </div>\n <div class=\"prompt-actions\">\n <i class=\"fa-solid fa-arrow-right\"></i>\n </div>\n </div>\n }\n }\n </div>\n </div>\n </div>\n } @else {\n <!-- Header toolbar -->\n <div class=\"version-toolbar\">\n <div class=\"toolbar-section\">\n <h5 class=\"toolbar-title\">\n <i class=\"fa-solid fa-history\"></i>\n Version History\n @if (prompt) {\n <span class=\"prompt-name\">- {{ prompt.Name }}</span>\n }\n </h5>\n \n <div class=\"view-mode-toggle\">\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <input type=\"radio\" class=\"btn-check\" name=\"view\" id=\"timelineView\" value=\"timeline\" [(ngModel)]=\"currentView\" (ngModelChange)=\"onViewChange($event)\">\n <label class=\"btn btn-outline-primary\" for=\"timelineView\">\n <i class=\"fa-solid fa-timeline\"></i>\n Timeline\n </label>\n \n <input type=\"radio\" class=\"btn-check\" name=\"view\" id=\"comparisonView\" value=\"comparison\" [(ngModel)]=\"currentView\" (ngModelChange)=\"onViewChange($event)\">\n <label class=\"btn btn-outline-primary\" for=\"comparisonView\">\n <i class=\"fa-solid fa-code-compare\"></i>\n Compare\n </label>\n \n <input type=\"radio\" class=\"btn-check\" name=\"view\" id=\"detailsView\" value=\"details\" [(ngModel)]=\"currentView\" (ngModelChange)=\"onViewChange($event)\">\n <label class=\"btn btn-outline-primary\" for=\"detailsView\">\n <i class=\"fa-solid fa-list\"></i>\n Details\n </label>\n </div>\n </div>\n </div>\n \n <div class=\"toolbar-section\">\n <div class=\"filter-controls\">\n <label class=\"form-label\">Filter:</label>\n <select class=\"form-select form-select-sm\" [(ngModel)]=\"filterBy\" (ngModelChange)=\"onFilterChange($event)\">\n <option value=\"all\">All Changes</option>\n <option value=\"updates\">Updates Only</option>\n <option value=\"major\">Major Changes</option>\n <option value=\"template\">Template Changes</option>\n </select>\n \n <div class=\"form-check form-switch\">\n <input class=\"form-check-input\" type=\"checkbox\" id=\"showSystem\" [(ngModel)]=\"showSystemChanges\" (ngModelChange)=\"applyFiltersPublic()\">\n <label class=\"form-check-label\" for=\"showSystem\">System</label>\n </div>\n </div>\n \n <div class=\"search-controls\">\n <input \n type=\"text\" \n class=\"form-control form-control-sm\" \n placeholder=\"Search changes...\"\n [ngModel]=\"searchTerm$.value\" (ngModelChange)=\"onSearchChange($event)\">\n </div>\n </div>\n \n <div class=\"toolbar-section\">\n <div class=\"action-buttons\">\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" (click)=\"onSortDirectionChange()\" [title]=\"'Sort ' + (sortDirection === 'asc' ? 'descending' : 'ascending')\">\n <i class=\"fa-solid\" [class.fa-sort-up]=\"sortDirection === 'asc'\" [class.fa-sort-down]=\"sortDirection === 'desc'\"></i>\n Sort\n </button>\n \n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" (click)=\"refreshHistory()\" [disabled]=\"isLoading\" title=\"Refresh history\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"isLoading\"></i>\n Refresh\n </button>\n \n <button type=\"button\" class=\"btn btn-sm btn-outline-info\" (click)=\"exportVersionHistory()\" title=\"Export history\">\n <i class=\"fa-solid fa-download\"></i>\n Export\n </button>\n </div>\n </div>\n </div>\n \n <!-- Loading state -->\n @if (isLoading) {\n <div class=\"loading-container\">\n <div class=\"loading-content\">\n <div class=\"spinner-border text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p class=\"loading-message\">{{ loadingMessage }}</p>\n </div>\n </div>\n } @else {\n \n <!-- Timeline View -->\n @if (currentView === 'timeline') {\n <div class=\"timeline-container\">\n @if (versions.length > 0) {\n <div class=\"version-timeline\">\n @for (version of versions; track version.id; let i = $index) {\n <div class=\"timeline-item\" \n [class.selected]=\"selectedVersion?.id === version.id\"\n [class.active]=\"version.isActive\"\n (click)=\"onVersionSelect(version)\">\n \n <div class=\"timeline-marker\">\n <div class=\"marker-icon\" [class]=\"getChangeTypeClass(version.changeType)\">\n <i class=\"fa-solid\" [class]=\"getChangeTypeIcon(version.changeType)\"></i>\n </div>\n @if (i < versions.length - 1) {\n <div class=\"timeline-line\"></div>\n }\n </div>\n \n <div class=\"timeline-content\">\n <div class=\"version-header\">\n <div class=\"version-info\">\n <h6 class=\"version-label\">{{ getVersionLabel(version) }}</h6>\n <div class=\"version-meta\">\n <span class=\"change-date\">{{ version.changedAt | date:'medium' }}</span>\n <span class=\"changed-by\">by {{ version.changedBy }}</span>\n <span class=\"change-source badge\" [class.badge-primary]=\"version.changeSource === 'Internal'\" [class.badge-secondary]=\"version.changeSource === 'External'\">\n {{ version.changeSource }}\n </span>\n </div>\n </div>\n \n <div class=\"version-actions\">\n @if (showComparison && i < versions.length - 1) {\n <button \n type=\"button\" \n class=\"btn btn-sm btn-outline-primary\"\n (click)=\"$event.stopPropagation(); startComparison(version)\"\n title=\"Compare with next version\">\n <i class=\"fa-solid fa-code-compare\"></i>\n </button>\n }\n \n @if (showRestoreActions && version.canRestore) {\n <button \n type=\"button\" \n class=\"btn btn-sm btn-outline-success\"\n (click)=\"$event.stopPropagation(); onVersionRestore(version)\"\n title=\"Restore this version\">\n <i class=\"fa-solid fa-undo\"></i>\n </button>\n }\n </div>\n </div>\n \n <div class=\"version-description\">\n {{ version.changesDescription }}\n </div>\n \n @if (version.changesJSON && timelineConfig.showDiffs) {\n <div class=\"changes-summary\">\n <div class=\"changes-list\">\n @for (field of getObjectKeys(version.changesJSON); track field) {\n <div class=\"change-item\">\n <span class=\"field-name\">{{ getFieldDisplayNamePublic(field) }}</span>\n @if (version.changesJSON[field].oldValue !== undefined) {\n <span class=\"change-arrow\">\n <span class=\"old-value\">{{ formatChangeValue(version.changesJSON[field].oldValue) }}</span>\n <i class=\"fa-solid fa-arrow-right\"></i>\n <span class=\"new-value\">{{ formatChangeValue(version.changesJSON[field].newValue) }}</span>\n </span>\n } @else {\n <span class=\"new-value\">{{ formatChangeValue(version.changesJSON[field].newValue) }}</span>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"no-versions-message\">\n <i class=\"fa-solid fa-info-circle text-muted\"></i>\n <p class=\"text-muted\">No version history available for this prompt.</p>\n @if (prompt && !prompt.EntityInfo.TrackRecordChanges) {\n <p class=\"text-muted\"><small>Record changes tracking may not be enabled for prompts.</small></p>\n }\n </div>\n }\n </div>\n }\n \n <!-- Comparison View -->\n @if (currentView === 'comparison') {\n <div class=\"comparison-container\">\n <!-- Version selectors -->\n <div class=\"comparison-selectors\">\n <div class=\"selector-group\">\n <label class=\"form-label\">Compare from:</label>\n <select class=\"form-select\" [(ngModel)]=\"compareFromVersion\" (ngModelChange)=\"generateComparisonPublic()\">\n @for (version of versions; track version.id) {\n <option [ngValue]=\"version\">{{ getVersionLabel(version) }} - {{ version.changedAt | date:'short' }}</option>\n }\n </select>\n </div>\n \n <div class=\"comparison-arrow\">\n <i class=\"fa-solid fa-arrow-right\"></i>\n </div>\n \n <div class=\"selector-group\">\n <label class=\"form-label\">Compare to:</label>\n <select class=\"form-select\" [(ngModel)]=\"compareToVersion\" (ngModelChange)=\"generateComparisonPublic()\">\n @for (version of versions; track version.id) {\n <option [ngValue]=\"version\">{{ getVersionLabel(version) }} - {{ version.changedAt | date:'short' }}</option>\n }\n </select>\n </div>\n </div>\n \n <!-- Comparison results -->\n @if (comparisonResult) {\n <div class=\"comparison-results\">\n <div class=\"comparison-header\">\n <h6 class=\"comparison-title\">\n <i class=\"fa-solid fa-code-compare\"></i>\n Changes between {{ getVersionLabel(comparisonResult.fromVersion) }} and {{ getVersionLabel(comparisonResult.toVersion) }}\n </h6>\n <div class=\"changes-count\">\n {{ comparisonResult.differences.length }} change(s) found\n </div>\n </div>\n \n @if (comparisonResult.differences.length > 0) {\n <div class=\"differences-list\">\n @for (diff of comparisonResult.differences; track diff.fieldName) {\n <div class=\"difference-item\" [class]=\"'diff-' + diff.changeType\">\n <div class=\"diff-header\">\n <div class=\"field-info\">\n <strong class=\"field-name\">{{ diff.displayName }}</strong>\n <span class=\"change-type badge\" \n [class.badge-success]=\"diff.changeType === 'added'\"\n [class.badge-warning]=\"diff.changeType === 'modified'\"\n [class.badge-danger]=\"diff.changeType === 'removed'\">\n {{ diff.changeType }}\n </span>\n @if (diff.isTemplate) {\n <span class=\"template-badge badge badge-info\">Template</span>\n }\n </div>\n </div>\n \n <div class=\"diff-content\">\n @if (diff.changeType === 'modified') {\n <div class=\"side-by-side-diff\">\n <div class=\"diff-old\">\n <h6 class=\"diff-label\">Before:</h6>\n <div class=\"diff-value old-value\">\n @if (diff.isTemplate && diff.fieldName === 'TemplateText') {\n <pre class=\"template-content\">{{ diff.oldValue }}</pre>\n } @else {\n {{ formatChangeValue(diff.oldValue) }}\n }\n </div>\n </div>\n \n <div class=\"diff-new\">\n <h6 class=\"diff-label\">After:</h6>\n <div class=\"diff-value new-value\">\n @if (diff.isTemplate && diff.fieldName === 'TemplateText') {\n <pre class=\"template-content\">{{ diff.newValue }}</pre>\n } @else {\n {{ formatChangeValue(diff.newValue) }}\n }\n </div>\n </div>\n </div>\n } @else if (diff.changeType === 'added') {\n <div class=\"diff-added\">\n <h6 class=\"diff-label\">Added:</h6>\n <div class=\"diff-value new-value\">{{ formatChangeValue(diff.newValue) }}</div>\n </div>\n } @else if (diff.changeType === 'removed') {\n <div class=\"diff-removed\">\n <h6 class=\"diff-label\">Removed:</h6>\n <div class=\"diff-value old-value\">{{ formatChangeValue(diff.oldValue) }}</div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"no-differences\">\n <i class=\"fa-solid fa-check-circle text-success\"></i>\n <p class=\"text-muted\">No differences found between the selected versions.</p>\n </div>\n }\n </div>\n }\n </div>\n }\n \n <!-- Details View -->\n @if (currentView === 'details') {\n <div class=\"details-container\">\n @if (selectedVersion) {\n <div class=\"version-details\">\n <div class=\"details-header\">\n <h6 class=\"details-title\">{{ getVersionLabel(selectedVersion) }}</h6>\n <div class=\"details-meta\">\n <span class=\"change-date\">{{ selectedVersion.changedAt | date:'full' }}</span>\n <span class=\"changed-by\">Changed by {{ selectedVersion.changedBy }}</span>\n </div>\n </div>\n \n <div class=\"details-content\">\n <div class=\"detail-section\">\n <h6 class=\"section-title\">Change Information</h6>\n <div class=\"detail-grid\">\n <div class=\"detail-item\">\n <span class=\"detail-label\">Type:</span>\n <span class=\"detail-value\">\n <i class=\"fa-solid\" [class]=\"getChangeTypeIcon(selectedVersion.changeType)\"></i>\n {{ selectedVersion.changeType }}\n </span>\n </div>\n \n <div class=\"detail-item\">\n <span class=\"detail-label\">Source:</span>\n <span class=\"detail-value\">{{ selectedVersion.changeSource }}</span>\n </div>\n \n <div class=\"detail-item\">\n <span class=\"detail-label\">Description:</span>\n <span class=\"detail-value\">{{ selectedVersion.changesDescription }}</span>\n </div>\n </div>\n </div>\n \n @if (selectedVersion.fullRecordJSON) {\n <div class=\"detail-section\">\n <h6 class=\"section-title\">Record State</h6>\n <div class=\"json-viewer\">\n <pre class=\"json-content\">{{ selectedVersion.fullRecordJSON | json }}</pre>\n </div>\n </div>\n }\n \n @if (selectedVersion.templateContent) {\n <div class=\"detail-section\">\n <h6 class=\"section-title\">Template Content</h6>\n <div class=\"template-viewer\">\n <pre class=\"template-content\">{{ selectedVersion.templateContent.TemplateText }}</pre>\n </div>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"no-selection-message\">\n <i class=\"fa-solid fa-hand-pointer text-muted\"></i>\n <p class=\"text-muted\">Select a version from the timeline to view details.</p>\n </div>\n }\n </div>\n }\n }\n \n <!-- Error state -->\n @if (error) {\n <div class=\"error-container\">\n <div class=\"alert alert-danger\" role=\"alert\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n {{ error }}\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger ms-2\" (click)=\"refreshHistory()\">\n <i class=\"fa-solid fa-refresh\"></i>\n Retry\n </button>\n </div>\n </div>\n }\n }\n</div>", styles: [".prompt-version-control {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #f8f9fa;\n\n .version-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: white;\n border-bottom: 1px solid #dee2e6;\n flex-shrink: 0;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-section {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-wrap: wrap;\n\n .toolbar-title {\n margin: 0;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n\n i {\n color: #0d6efd;\n }\n\n .prompt-name {\n font-weight: 400;\n color: #6c757d;\n }\n }\n\n .filter-controls,\n .search-controls {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .form-label {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n }\n\n .form-select,\n .form-control {\n min-width: 120px;\n }\n\n .form-check {\n margin: 0;\n \n .form-check-label {\n font-size: 0.875rem;\n }\n }\n }\n\n .action-buttons {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n font-size: 0.875rem;\n }\n }\n }\n }\n\n .loading-container {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n\n .loading-content {\n text-align: center;\n \n .loading-message {\n margin-top: 1rem;\n color: #6c757d;\n }\n }\n }\n\n .timeline-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-timeline {\n max-width: 800px;\n margin: 0 auto;\n\n .timeline-item {\n display: flex;\n position: relative;\n margin-bottom: 2rem;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &:hover {\n .timeline-content {\n box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1);\n transform: translateY(-1px);\n }\n }\n\n &.selected {\n .timeline-content {\n border-color: #0d6efd;\n box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);\n }\n }\n\n &.active {\n .timeline-marker .marker-icon {\n border-color: #28a745;\n background-color: #28a745;\n color: white;\n }\n }\n\n .timeline-marker {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-right: 1.5rem;\n flex-shrink: 0;\n\n .marker-icon {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 3px solid;\n background: white;\n font-size: 1rem;\n z-index: 2;\n\n &.change-create {\n border-color: #28a745;\n color: #28a745;\n }\n\n &.change-update {\n border-color: #0d6efd;\n color: #0d6efd;\n }\n\n &.change-delete {\n border-color: #dc3545;\n color: #dc3545;\n }\n }\n\n .timeline-line {\n width: 2px;\n flex: 1;\n background: #dee2e6;\n margin-top: 0.5rem;\n min-height: 2rem;\n }\n }\n\n .timeline-content {\n flex: 1;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n padding: 1.5rem;\n transition: all 0.2s ease;\n\n .version-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n\n .version-info {\n .version-label {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .version-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n\n .change-date {\n font-weight: 500;\n }\n\n .badge {\n font-size: 0.75rem;\n }\n }\n }\n\n .version-actions {\n display: flex;\n gap: 0.5rem;\n\n .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n }\n }\n }\n\n .version-description {\n color: #495057;\n margin-bottom: 1rem;\n line-height: 1.5;\n }\n\n .changes-summary {\n .changes-list {\n .change-item {\n display: flex;\n align-items: center;\n padding: 0.5rem;\n background: #f8f9fa;\n border-radius: 0.375rem;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n\n .field-name {\n font-weight: 500;\n color: #495057;\n margin-right: 0.75rem;\n min-width: 100px;\n }\n\n .change-arrow {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n\n .old-value {\n color: #dc3545;\n text-decoration: line-through;\n }\n\n .new-value {\n color: #28a745;\n font-weight: 500;\n }\n\n i {\n color: #6c757d;\n }\n }\n }\n }\n }\n }\n }\n }\n\n .no-versions-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0.5rem 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .comparison-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .comparison-selectors {\n display: flex;\n align-items: end;\n gap: 1rem;\n margin-bottom: 2rem;\n padding: 1rem;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n\n .selector-group {\n flex: 1;\n\n .form-label {\n font-weight: 500;\n color: #495057;\n }\n }\n\n .comparison-arrow {\n padding-bottom: 0.375rem;\n color: #6c757d;\n font-size: 1.25rem;\n }\n }\n\n .comparison-results {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .comparison-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .comparison-title {\n margin: 0;\n color: #495057;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .changes-count {\n font-size: 0.875rem;\n color: #6c757d;\n font-weight: 500;\n }\n }\n\n .differences-list {\n .difference-item {\n border-bottom: 1px solid #f1f3f4;\n padding: 1.5rem;\n\n &:last-child {\n border-bottom: none;\n }\n\n &.diff-added {\n border-left: 4px solid #28a745;\n }\n\n &.diff-modified {\n border-left: 4px solid #ffc107;\n }\n\n &.diff-removed {\n border-left: 4px solid #dc3545;\n }\n\n .diff-header {\n margin-bottom: 1rem;\n\n .field-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n\n .field-name {\n font-size: 1rem;\n color: #495057;\n }\n\n .badge {\n font-size: 0.75rem;\n \n &.template-badge {\n background-color: #6f42c1;\n }\n }\n }\n }\n\n .diff-content {\n .side-by-side-diff {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n\n .diff-old,\n .diff-new {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n\n &.old-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n\n &.new-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .template-content {\n margin: 0;\n background: transparent;\n border: none;\n padding: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n white-space: pre-wrap;\n }\n }\n }\n }\n\n .diff-added,\n .diff-removed {\n .diff-label {\n font-size: 0.875rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n color: #6c757d;\n }\n\n .diff-value {\n padding: 1rem;\n border-radius: 0.375rem;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n }\n }\n\n .diff-added .diff-value {\n background: #f0fff4;\n border: 1px solid #9ae6b4;\n color: #2f855a;\n }\n\n .diff-removed .diff-value {\n background: #fff5f5;\n border: 1px solid #fed7d7;\n color: #c53030;\n }\n }\n }\n }\n\n .no-differences {\n text-align: center;\n padding: 3rem 1rem;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n }\n }\n }\n }\n\n .details-container {\n flex: 1;\n padding: 1rem;\n overflow-y: auto;\n\n .version-details {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 0.5rem;\n overflow: hidden;\n\n .details-header {\n padding: 1.5rem;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n\n .details-title {\n margin: 0 0 0.5rem 0;\n color: #495057;\n font-weight: 600;\n }\n\n .details-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.875rem;\n color: #6c757d;\n flex-wrap: wrap;\n }\n }\n\n .details-content {\n padding: 1.5rem;\n\n .detail-section {\n margin-bottom: 2rem;\n\n &:last-child {\n margin-bottom: 0;\n }\n\n .section-title {\n margin: 0 0 1rem 0;\n color: #495057;\n font-weight: 600;\n font-size: 1rem;\n border-bottom: 1px solid #e9ecef;\n padding-bottom: 0.5rem;\n }\n\n .detail-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1rem;\n\n .detail-item {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n\n .detail-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: #6c757d;\n }\n\n .detail-value {\n font-size: 0.875rem;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n }\n\n .json-viewer,\n .template-viewer {\n .json-content,\n .template-content {\n background: #f8f9fa;\n border: 1px solid #dee2e6;\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 0;\n font-family: 'Fira Code', Monaco, Consolas, monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n overflow-x: auto;\n max-height: 400px;\n overflow-y: auto;\n }\n }\n }\n }\n }\n\n .no-selection-message {\n text-align: center;\n padding: 3rem 1rem;\n color: #6c757d;\n\n i {\n font-size: 3rem;\n margin-bottom: 1rem;\n display: block;\n opacity: 0.5;\n }\n\n p {\n margin: 0;\n font-size: 1rem;\n line-height: 1.5;\n }\n }\n }\n\n .error-container {\n padding: 1rem;\n\n .alert {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n }\n}\n\n// Responsive adjustments\n@media (max-width: 1200px) {\n .prompt-version-control {\n .version-toolbar {\n .toolbar-section {\n min-width: 100%;\n justify-content: space-between;\n }\n }\n\n .comparison-container {\n .comparison-selectors {\n flex-direction: column;\n align-items: stretch;\n\n .comparison-arrow {\n align-self: center;\n padding: 0.5rem 0;\n }\n }\n\n .comparison-results {\n .differences-list {\n .difference-item {\n .diff-content {\n .side-by-side-diff {\n grid-template-columns: 1fr;\n gap: 0.5rem;\n }\n }\n }\n }\n }\n }\n }\n}\n\n@media (max-width: 768px) {\n .prompt-version-control {\n .version-toolbar {\n padding: 0.75rem;\n \n .toolbar-section {\n flex-wrap: wrap;\n gap: 0.5rem;\n }\n }\n\n .timeline-container {\n padding: 0.75rem;\n\n .version-timeline {\n .timeline-item {\n .timeline-marker {\n margin-right: 1rem;\n\n .marker-icon {\n width: 2rem;\n height: 2rem;\n font-size: 0.875rem;\n }\n }\n\n .timeline-content {\n padding: 1rem;\n\n .version-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 0.75rem;\n\n .version-actions {\n align-self: flex-end;\n }\n }\n }\n }\n }\n }\n\n .comparison-container,\n .details-container {\n padding: 0.75rem;\n }\n }\n\n .prompt-selector-section {\n padding: 2rem;\n background: white;\n border-radius: 8px;\n margin: 1rem;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n\n .section-title {\n color: #495057;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n\n i {\n color: #007bff;\n }\n }\n\n .prompt-selector {\n .search-box {\n .input-group-text {\n background: #f8f9fa;\n border-color: #ced4da;\n\n i {\n color: #6c757d;\n }\n }\n\n .form-control {\n border-color: #ced4da;\n\n &:focus {\n border-color: #007bff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n }\n }\n }\n\n .prompts-list {\n max-height: 400px;\n overflow-y: auto;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n\n .empty-state {\n text-align: center;\n padding: 2rem;\n\n i {\n font-size: 2rem;\n margin-bottom: 0.5rem;\n }\n }\n\n .prompt-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid #dee2e6;\n cursor: pointer;\n transition: background-color 0.2s;\n\n &:last-child {\n border-bottom: none;\n }\n\n &:hover {\n background-color: #f8f9fa;\n }\n\n .prompt-info {\n flex: 1;\n\n .prompt-title {\n margin: 0 0 0.25rem 0;\n font-weight: 600;\n color: #495057;\n }\n\n .prompt-description {\n margin: 0;\n font-size: 0.875rem;\n color: #6c757d;\n line-height: 1.4;\n }\n }\n\n .prompt-actions {\n color: #007bff;\n opacity: 0.7;\n transition: opacity 0.2s;\n }\n\n &:hover .prompt-actions {\n opacity: 1;\n }\n }\n }\n }\n }\n}"] }]
|