@memberjunction/ng-artifacts 6.1.0-edge.3 → 6.1.0-edge.5

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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Preparing an artifact version for download.
3
+ *
4
+ * Kept separate from the component so the decoding rules are unit-testable without Angular.
5
+ */
6
+ /** What the browser should be handed: the bytes (or text), its MIME type, and a filename. */
7
+ export interface VersionDownload {
8
+ data: BlobPart;
9
+ mimeType: string;
10
+ fileName: string;
11
+ }
12
+ /**
13
+ * Turn a version's stored `Content` into a real download.
14
+ *
15
+ * An artifact version holding a FILE stores it as a `data:<mime>;base64,...` URI (see
16
+ * `AgentRunner.createInlineFileArtifact`, used whenever no file storage account is configured).
17
+ * Writing that string out verbatim as `text/plain` named `<name>.txt` — which is what this did —
18
+ * produced a text file containing base64, so opening the "downloaded document" gave an invalid
19
+ * file format. The bytes were always correct; only the download was wrong.
20
+ *
21
+ * A data URI is therefore decoded to bytes and given its real MIME type and filename. Text
22
+ * content (Markdown, JSON, code) keeps the previous behaviour.
23
+ *
24
+ * @param content the version's `Content`
25
+ * @param fileName the version's `FileName`, when it has one
26
+ * @param artifactName fallback name
27
+ * @param versionNumber used only in the fallback name
28
+ * @param mimeType the version's `MimeType`, used when the content is not a data URI
29
+ */
30
+ export declare function buildVersionDownload(content: string, fileName: string | null | undefined, artifactName: string | null | undefined, versionNumber: number | null | undefined, mimeType?: string | null): VersionDownload;
31
+ //# sourceMappingURL=artifact-version-download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-version-download.d.ts","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-download.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6FAA6F;AAC7F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,eAAe,CAmCjB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Preparing an artifact version for download.
3
+ *
4
+ * Kept separate from the component so the decoding rules are unit-testable without Angular.
5
+ */
6
+ /**
7
+ * Turn a version's stored `Content` into a real download.
8
+ *
9
+ * An artifact version holding a FILE stores it as a `data:<mime>;base64,...` URI (see
10
+ * `AgentRunner.createInlineFileArtifact`, used whenever no file storage account is configured).
11
+ * Writing that string out verbatim as `text/plain` named `<name>.txt` — which is what this did —
12
+ * produced a text file containing base64, so opening the "downloaded document" gave an invalid
13
+ * file format. The bytes were always correct; only the download was wrong.
14
+ *
15
+ * A data URI is therefore decoded to bytes and given its real MIME type and filename. Text
16
+ * content (Markdown, JSON, code) keeps the previous behaviour.
17
+ *
18
+ * @param content the version's `Content`
19
+ * @param fileName the version's `FileName`, when it has one
20
+ * @param artifactName fallback name
21
+ * @param versionNumber used only in the fallback name
22
+ * @param mimeType the version's `MimeType`, used when the content is not a data URI
23
+ */
24
+ export function buildVersionDownload(content, fileName, artifactName, versionNumber, mimeType) {
25
+ const dataUri = /^data:([^;,]+);base64,(.*)$/is.exec(content.trim());
26
+ if (dataUri) {
27
+ const [, uriMimeType, base64] = dataUri;
28
+ // `atob` throws on malformed base64 that still matched the pattern. The caller only logs, so
29
+ // an unguarded throw turns "download" into a click that does nothing at all; falling through
30
+ // to the text path at least hands the user the bytes that are stored.
31
+ let binary = null;
32
+ try {
33
+ binary = atob(base64);
34
+ }
35
+ catch {
36
+ binary = null;
37
+ }
38
+ if (binary !== null) {
39
+ const bytes = new Uint8Array(binary.length);
40
+ for (let i = 0; i < binary.length; i++) {
41
+ bytes[i] = binary.charCodeAt(i);
42
+ }
43
+ return {
44
+ data: bytes,
45
+ mimeType: uriMimeType,
46
+ // The stored FileName already carries the correct extension; only fall back when absent.
47
+ fileName: fileName?.trim() || `${artifactName ?? 'artifact'}_v${versionNumber ?? 1}`,
48
+ };
49
+ }
50
+ // Fall through to the text path below: a click that hands over the stored bytes beats one
51
+ // that silently does nothing.
52
+ }
53
+ // Text content — unchanged from the original behaviour, but honour a real filename when present.
54
+ return {
55
+ data: content,
56
+ mimeType: mimeType?.trim() || 'text/plain',
57
+ fileName: fileName?.trim() || `${artifactName ?? 'artifact'}_v${versionNumber ?? 1}.txt`,
58
+ };
59
+ }
60
+ //# sourceMappingURL=artifact-version-download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-version-download.js","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-download.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,QAAmC,EACnC,YAAuC,EACvC,aAAwC,EACxC,QAAwB;IAExB,MAAM,OAAO,GAAG,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACrE,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QACxC,6FAA6F;QAC7F,6FAA6F;QAC7F,sEAAsE;QACtE,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAClC,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,WAAW;gBACrB,yFAAyF;gBACzF,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,YAAY,IAAI,UAAU,KAAK,aAAa,IAAI,CAAC,EAAE;aACrF,CAAC;QACJ,CAAC;QACD,0FAA0F;QAC1F,8BAA8B;IAChC,CAAC;IAED,iGAAiG;IACjG,OAAO;QACL,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,YAAY;QAC1C,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,YAAY,IAAI,UAAU,KAAK,aAAa,IAAI,CAAC,MAAM;KACzF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Preparing an artifact version for download.\n *\n * Kept separate from the component so the decoding rules are unit-testable without Angular.\n */\n\n/** What the browser should be handed: the bytes (or text), its MIME type, and a filename. */\nexport interface VersionDownload {\n data: BlobPart;\n mimeType: string;\n fileName: string;\n}\n\n/**\n * Turn a version's stored `Content` into a real download.\n *\n * An artifact version holding a FILE stores it as a `data:<mime>;base64,...` URI (see\n * `AgentRunner.createInlineFileArtifact`, used whenever no file storage account is configured).\n * Writing that string out verbatim as `text/plain` named `<name>.txt` — which is what this did —\n * produced a text file containing base64, so opening the \"downloaded document\" gave an invalid\n * file format. The bytes were always correct; only the download was wrong.\n *\n * A data URI is therefore decoded to bytes and given its real MIME type and filename. Text\n * content (Markdown, JSON, code) keeps the previous behaviour.\n *\n * @param content the version's `Content`\n * @param fileName the version's `FileName`, when it has one\n * @param artifactName fallback name\n * @param versionNumber used only in the fallback name\n * @param mimeType the version's `MimeType`, used when the content is not a data URI\n */\nexport function buildVersionDownload(\n content: string,\n fileName: string | null | undefined,\n artifactName: string | null | undefined,\n versionNumber: number | null | undefined,\n mimeType?: string | null\n): VersionDownload {\n const dataUri = /^data:([^;,]+);base64,(.*)$/is.exec(content.trim());\n if (dataUri) {\n const [, uriMimeType, base64] = dataUri;\n // `atob` throws on malformed base64 that still matched the pattern. The caller only logs, so\n // an unguarded throw turns \"download\" into a click that does nothing at all; falling through\n // to the text path at least hands the user the bytes that are stored.\n let binary: string | null = null;\n try {\n binary = atob(base64);\n } catch {\n binary = null;\n }\n if (binary !== null) {\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return {\n data: bytes,\n mimeType: uriMimeType,\n // The stored FileName already carries the correct extension; only fall back when absent.\n fileName: fileName?.trim() || `${artifactName ?? 'artifact'}_v${versionNumber ?? 1}`,\n };\n }\n // Fall through to the text path below: a click that hands over the stored bytes beats one\n // that silently does nothing.\n }\n\n // Text content — unchanged from the original behaviour, but honour a real filename when present.\n return {\n data: content,\n mimeType: mimeType?.trim() || 'text/plain',\n fileName: fileName?.trim() || `${artifactName ?? 'artifact'}_v${versionNumber ?? 1}.txt`,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-version-history.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-history.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAW,MAAM,sBAAsB,CAAC;;AAEzD,qBAuGa,+BAAgC,SAAQ,oBAAqB,YAAW,MAAM;IACzF,OAAO,CAAC,cAAc,CAA4B;IAEzC,QAAQ,EAAG,gBAAgB,CAAC;IAC5B,WAAW,EAAG,QAAQ,CAAC;IAEtB,MAAM,qBAA4B;IAClC,eAAe,uBAA8B;IAC7C,eAAe,uBAA8B;IAEhD,QAAQ,EAAE,uBAAuB,EAAE,CAAM;IACzC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,QAAQ,EAAE,OAAO,CAAS;IAC1B,qBAAqB,EAAE,MAAM,CAAM;IACnC,sBAAsB,EAAE,MAAM,CAAM;IAE3C,QAAQ;YAIM,YAAY;IAkB1B,eAAe,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAKjD,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBvE,iBAAiB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAgBzD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAQ9C,cAAc,IAAI,MAAM;IAYxB,OAAO,IAAI,IAAI;yCAjHJ,+BAA+B;2CAA/B,+BAA+B;CAoH3C"}
1
+ {"version":3,"file":"artifact-version-history.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-history.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAW,MAAM,sBAAsB,CAAC;;AAGzD,qBAuGa,+BAAgC,SAAQ,oBAAqB,YAAW,MAAM;IACzF,OAAO,CAAC,cAAc,CAA4B;IAEzC,QAAQ,EAAG,gBAAgB,CAAC;IAC5B,WAAW,EAAG,QAAQ,CAAC;IAEtB,MAAM,qBAA4B;IAClC,eAAe,uBAA8B;IAC7C,eAAe,uBAA8B;IAEhD,QAAQ,EAAE,uBAAuB,EAAE,CAAM;IACzC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IACtC,QAAQ,EAAE,OAAO,CAAS;IAC1B,qBAAqB,EAAE,MAAM,CAAM;IACnC,sBAAsB,EAAE,MAAM,CAAM;IAE3C,QAAQ;YAIM,YAAY;IAkB1B,eAAe,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAKjD,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBvE,iBAAiB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAiBzD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAQ9C,cAAc,IAAI,MAAM;IAYxB,OAAO,IAAI,IAAI;yCAlHJ,+BAA+B;2CAA/B,+BAA+B;CAqH3C"}
@@ -2,6 +2,7 @@ import { Component, Input, Output, EventEmitter, inject } from '@angular/core';
2
2
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
3
  import { MJConfirmService } from '@memberjunction/ng-ui-components';
4
4
  import { RunView } from '@memberjunction/core';
5
+ import { buildVersionDownload } from './artifact-version-download.js';
5
6
  import * as i0 from "@angular/core";
6
7
  import * as i1 from "@memberjunction/ng-ui-components";
7
8
  import * as i2 from "@angular/common";
@@ -145,11 +146,12 @@ export class ArtifactVersionHistoryComponent extends BaseAngularComponent {
145
146
  onDownloadVersion(version) {
146
147
  try {
147
148
  const content = version.Content || '';
148
- const blob = new Blob([content], { type: 'text/plain' });
149
+ const download = buildVersionDownload(content, version.FileName, this.artifact.Name, version.VersionNumber, version.MimeType);
150
+ const blob = new Blob([download.data], { type: download.mimeType });
149
151
  const url = window.URL.createObjectURL(blob);
150
152
  const link = document.createElement('a');
151
153
  link.href = url;
152
- link.download = `${this.artifact.Name}_v${version.VersionNumber}.txt`;
154
+ link.download = download.fileName;
153
155
  link.click();
154
156
  window.URL.revokeObjectURL(url);
155
157
  }
@@ -282,5 +284,5 @@ export class ArtifactVersionHistoryComponent extends BaseAngularComponent {
282
284
  }], versionSelected: [{
283
285
  type: Output
284
286
  }] }); })();
285
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactVersionHistoryComponent, { className: "ArtifactVersionHistoryComponent", filePath: "src/lib/components/artifact-version-history.component.ts", lineNumber: 110 }); })();
287
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactVersionHistoryComponent, { className: "ArtifactVersionHistoryComponent", filePath: "src/lib/components/artifact-version-history.component.ts", lineNumber: 111 }); })();
286
288
  //# sourceMappingURL=artifact-version-history.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-version-history.component.js","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-history.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MJConfirmService } from '@memberjunction/ng-ui-components';\nimport { MJArtifactEntity, MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { UserInfo, RunView } from '@memberjunction/core';\n\n@Component({\n standalone: false,\n selector: 'mj-artifact-version-history',\n template: `\n <div class=\"version-history\">\n <div class=\"history-header\">\n <h3>Version History</h3>\n <button class=\"btn-close\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n \n <div class=\"version-list\">\n @if (versions.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-clock-rotate-left\"\n Title=\"No version history available\"\n Size=\"compact\" />\n }\n \n @for (version of versions; track version) {\n <div\n class=\"version-item\"\n [class.selected]=\"version.VersionNumber === selectedVersion\"\n (click)=\"onSelectVersion(version)\">\n <div class=\"version-header\">\n <div class=\"version-info\">\n <span class=\"version-number\">v{{ version.VersionNumber }}</span>\n <span class=\"version-date\">{{ version.__mj_CreatedAt | date:'short' }}</span>\n </div>\n <div class=\"version-meta\">\n <span class=\"version-size\">\n {{ getContentSize(version.Content) }}\n </span>\n </div>\n </div>\n @if (version.VersionNumber === selectedVersion) {\n <div class=\"version-actions\">\n <button class=\"btn-action\" (click)=\"onRestoreVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-undo\"></i> Restore\n </button>\n <button class=\"btn-action\" (click)=\"onCompareVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-code-compare\"></i> Compare\n </button>\n <button class=\"btn-action\" (click)=\"onDownloadVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-download\"></i> Download\n </button>\n </div>\n }\n </div>\n }\n </div>\n \n @if (showDiff && currentVersionContent && previousVersionContent && selectedVersion) {\n <div class=\"diff-panel\">\n <div class=\"diff-header\">\n <h4>Changes from v{{ (selectedVersion || 1) - 1 }} to v{{ selectedVersion }}</h4>\n <button class=\"btn-close-diff\" (click)=\"showDiff = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"diff-content\">\n <pre>{{ getDiffSummary() }}</pre>\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n .version-history { display: flex; flex-direction: column; height: 100%; background: white; }\n .history-header { padding: 16px; border-bottom: 1px solid #D9D9D9; display: flex; justify-content: space-between; align-items: center; }\n .history-header h3 { margin: 0; font-size: 16px; }\n .btn-close { padding: 6px; background: transparent; border: none; cursor: pointer; border-radius: 3px; color: #666; }\n .btn-close:hover { background: rgba(0,0,0,0.1); }\n\n .version-list { flex: 1; overflow-y: auto; }\n .version-item { padding: 16px; border-bottom: 1px solid #E8E8E8; cursor: pointer; transition: background 150ms ease; }\n .version-item:hover { background: #F9F9F9; }\n .version-item.selected { background: #E3F2FD; border-left: 3px solid #1976D2; }\n\n .version-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px; }\n .version-info { display: flex; flex-direction: column; gap: 4px; }\n .version-number { font-size: 14px; font-weight: 600; }\n .version-date { font-size: 12px; color: #666; }\n .version-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; font-size: 12px; color: #666; }\n .version-author { display: flex; align-items: center; gap: 4px; }\n .version-size { color: #999; }\n\n .version-notes { font-size: 13px; color: #333; margin-bottom: 8px; }\n\n .version-actions { display: flex; gap: 8px; margin-top: 12px; }\n .btn-action { padding: 6px 12px; background: #0076B6; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; display: flex; align-items: center; gap: 6px; }\n .btn-action:hover { background: #005A8C; }\n\n .diff-panel { border-top: 2px solid #D9D9D9; max-height: 50%; overflow: hidden; display: flex; flex-direction: column; }\n .diff-header { padding: 12px 16px; background: #F8F8F8; border-bottom: 1px solid #D9D9D9; display: flex; justify-content: space-between; align-items: center; }\n .diff-header h4 { margin: 0; font-size: 13px; font-weight: 600; }\n .btn-close-diff { padding: 4px; background: transparent; border: none; cursor: pointer; border-radius: 3px; color: #666; }\n .btn-close-diff:hover { background: rgba(0,0,0,0.1); }\n .diff-content { flex: 1; overflow-y: auto; padding: 16px; }\n .diff-content pre { margin: 0; font-family: 'Courier New', monospace; font-size: 12px; white-space: pre-wrap; }\n `]\n})\nexport class ArtifactVersionHistoryComponent extends BaseAngularComponent implements OnInit {\n private confirmService = inject(MJConfirmService);\n\n @Input() artifact!: MJArtifactEntity;\n @Input() currentUser!: UserInfo;\n\n @Output() closed = new EventEmitter<void>();\n @Output() versionRestored = new EventEmitter<number>();\n @Output() versionSelected = new EventEmitter<number>();\n\n public versions: MJArtifactVersionEntity[] = [];\n public selectedVersion: number | null = null;\n public showDiff: boolean = false;\n public currentVersionContent: string = '';\n public previousVersionContent: string = '';\n\n ngOnInit() {\n this.loadVersions();\n }\n\n private async loadVersions(): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJArtifactVersionEntity>({\n EntityName: 'MJ: Artifact Versions',\n ExtraFilter: `ArtifactID='${this.artifact.ID}'`,\n OrderBy: 'VersionNumber DESC',\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success) {\n this.versions = result.Results || [];\n }\n } catch (error) {\n console.error('Failed to load version history:', error);\n }\n }\n\n onSelectVersion(version: MJArtifactVersionEntity): void {\n this.selectedVersion = version.VersionNumber;\n this.versionSelected.emit(version.VersionNumber);\n }\n\n async onRestoreVersion(version: MJArtifactVersionEntity): Promise<void> {\n if (!(await this.confirmService.Confirm({ title: 'Restore version', message: `Restore to version ${version.VersionNumber}?`, detail: 'This will create a new version.' }))) return;\n\n try {\n // Restoring creates a new version with the old content\n this.versionRestored.emit(version.VersionNumber);\n alert(`Version ${version.VersionNumber} has been restored as the latest version`);\n } catch (error) {\n console.error('Failed to restore version:', error);\n alert('Failed to restore version');\n }\n }\n\n async onCompareVersion(version: MJArtifactVersionEntity): Promise<void> {\n if (version.VersionNumber === 1) {\n alert('Cannot compare: this is the first version');\n return;\n }\n\n try {\n this.currentVersionContent = version.Content || '';\n\n // Load previous version\n const previousVersion = this.versions.find(v => v.VersionNumber === version.VersionNumber - 1);\n if (previousVersion) {\n this.previousVersionContent = previousVersion.Content || '';\n this.showDiff = true;\n }\n } catch (error) {\n console.error('Failed to load version for comparison:', error);\n alert('Failed to compare versions');\n }\n }\n\n onDownloadVersion(version: MJArtifactVersionEntity): void {\n try {\n const content = version.Content || '';\n const blob = new Blob([content], { type: 'text/plain' });\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `${this.artifact.Name}_v${version.VersionNumber}.txt`;\n link.click();\n window.URL.revokeObjectURL(url);\n } catch (error) {\n console.error('Failed to download version:', error);\n alert('Failed to download version');\n }\n }\n\n getContentSize(content: string | null): string {\n if (!content) return '0 B';\n const bytes = new Blob([content]).size;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n getDiffSummary(): string {\n // Simple line-based diff summary\n const currentLines = this.currentVersionContent.split('\\n');\n const previousLines = this.previousVersionContent.split('\\n');\n\n const added = currentLines.length - previousLines.length;\n const summary = `Lines: ${previousLines.length} → ${currentLines.length} (${added > 0 ? '+' : ''}${added})\\n\\n`;\n\n // Show a simple comparison\n return summary + '(Full diff view would require a diff library)';\n }\n\n onClose(): void {\n this.closed.emit();\n }\n}"]}
1
+ {"version":3,"file":"artifact-version-history.component.js","sourceRoot":"","sources":["../../../src/lib/components/artifact-version-history.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MJConfirmService } from '@memberjunction/ng-ui-components';\nimport { MJArtifactEntity, MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { UserInfo, RunView } from '@memberjunction/core';\nimport { buildVersionDownload } from './artifact-version-download.js';\n\n@Component({\n standalone: false,\n selector: 'mj-artifact-version-history',\n template: `\n <div class=\"version-history\">\n <div class=\"history-header\">\n <h3>Version History</h3>\n <button class=\"btn-close\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n \n <div class=\"version-list\">\n @if (versions.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-clock-rotate-left\"\n Title=\"No version history available\"\n Size=\"compact\" />\n }\n \n @for (version of versions; track version) {\n <div\n class=\"version-item\"\n [class.selected]=\"version.VersionNumber === selectedVersion\"\n (click)=\"onSelectVersion(version)\">\n <div class=\"version-header\">\n <div class=\"version-info\">\n <span class=\"version-number\">v{{ version.VersionNumber }}</span>\n <span class=\"version-date\">{{ version.__mj_CreatedAt | date:'short' }}</span>\n </div>\n <div class=\"version-meta\">\n <span class=\"version-size\">\n {{ getContentSize(version.Content) }}\n </span>\n </div>\n </div>\n @if (version.VersionNumber === selectedVersion) {\n <div class=\"version-actions\">\n <button class=\"btn-action\" (click)=\"onRestoreVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-undo\"></i> Restore\n </button>\n <button class=\"btn-action\" (click)=\"onCompareVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-code-compare\"></i> Compare\n </button>\n <button class=\"btn-action\" (click)=\"onDownloadVersion(version); $event.stopPropagation()\">\n <i class=\"fas fa-download\"></i> Download\n </button>\n </div>\n }\n </div>\n }\n </div>\n \n @if (showDiff && currentVersionContent && previousVersionContent && selectedVersion) {\n <div class=\"diff-panel\">\n <div class=\"diff-header\">\n <h4>Changes from v{{ (selectedVersion || 1) - 1 }} to v{{ selectedVersion }}</h4>\n <button class=\"btn-close-diff\" (click)=\"showDiff = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"diff-content\">\n <pre>{{ getDiffSummary() }}</pre>\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n .version-history { display: flex; flex-direction: column; height: 100%; background: white; }\n .history-header { padding: 16px; border-bottom: 1px solid #D9D9D9; display: flex; justify-content: space-between; align-items: center; }\n .history-header h3 { margin: 0; font-size: 16px; }\n .btn-close { padding: 6px; background: transparent; border: none; cursor: pointer; border-radius: 3px; color: #666; }\n .btn-close:hover { background: rgba(0,0,0,0.1); }\n\n .version-list { flex: 1; overflow-y: auto; }\n .version-item { padding: 16px; border-bottom: 1px solid #E8E8E8; cursor: pointer; transition: background 150ms ease; }\n .version-item:hover { background: #F9F9F9; }\n .version-item.selected { background: #E3F2FD; border-left: 3px solid #1976D2; }\n\n .version-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px; }\n .version-info { display: flex; flex-direction: column; gap: 4px; }\n .version-number { font-size: 14px; font-weight: 600; }\n .version-date { font-size: 12px; color: #666; }\n .version-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; font-size: 12px; color: #666; }\n .version-author { display: flex; align-items: center; gap: 4px; }\n .version-size { color: #999; }\n\n .version-notes { font-size: 13px; color: #333; margin-bottom: 8px; }\n\n .version-actions { display: flex; gap: 8px; margin-top: 12px; }\n .btn-action { padding: 6px 12px; background: #0076B6; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; display: flex; align-items: center; gap: 6px; }\n .btn-action:hover { background: #005A8C; }\n\n .diff-panel { border-top: 2px solid #D9D9D9; max-height: 50%; overflow: hidden; display: flex; flex-direction: column; }\n .diff-header { padding: 12px 16px; background: #F8F8F8; border-bottom: 1px solid #D9D9D9; display: flex; justify-content: space-between; align-items: center; }\n .diff-header h4 { margin: 0; font-size: 13px; font-weight: 600; }\n .btn-close-diff { padding: 4px; background: transparent; border: none; cursor: pointer; border-radius: 3px; color: #666; }\n .btn-close-diff:hover { background: rgba(0,0,0,0.1); }\n .diff-content { flex: 1; overflow-y: auto; padding: 16px; }\n .diff-content pre { margin: 0; font-family: 'Courier New', monospace; font-size: 12px; white-space: pre-wrap; }\n `]\n})\nexport class ArtifactVersionHistoryComponent extends BaseAngularComponent implements OnInit {\n private confirmService = inject(MJConfirmService);\n\n @Input() artifact!: MJArtifactEntity;\n @Input() currentUser!: UserInfo;\n\n @Output() closed = new EventEmitter<void>();\n @Output() versionRestored = new EventEmitter<number>();\n @Output() versionSelected = new EventEmitter<number>();\n\n public versions: MJArtifactVersionEntity[] = [];\n public selectedVersion: number | null = null;\n public showDiff: boolean = false;\n public currentVersionContent: string = '';\n public previousVersionContent: string = '';\n\n ngOnInit() {\n this.loadVersions();\n }\n\n private async loadVersions(): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<MJArtifactVersionEntity>({\n EntityName: 'MJ: Artifact Versions',\n ExtraFilter: `ArtifactID='${this.artifact.ID}'`,\n OrderBy: 'VersionNumber DESC',\n ResultType: 'entity_object'\n }, this.currentUser);\n\n if (result.Success) {\n this.versions = result.Results || [];\n }\n } catch (error) {\n console.error('Failed to load version history:', error);\n }\n }\n\n onSelectVersion(version: MJArtifactVersionEntity): void {\n this.selectedVersion = version.VersionNumber;\n this.versionSelected.emit(version.VersionNumber);\n }\n\n async onRestoreVersion(version: MJArtifactVersionEntity): Promise<void> {\n if (!(await this.confirmService.Confirm({ title: 'Restore version', message: `Restore to version ${version.VersionNumber}?`, detail: 'This will create a new version.' }))) return;\n\n try {\n // Restoring creates a new version with the old content\n this.versionRestored.emit(version.VersionNumber);\n alert(`Version ${version.VersionNumber} has been restored as the latest version`);\n } catch (error) {\n console.error('Failed to restore version:', error);\n alert('Failed to restore version');\n }\n }\n\n async onCompareVersion(version: MJArtifactVersionEntity): Promise<void> {\n if (version.VersionNumber === 1) {\n alert('Cannot compare: this is the first version');\n return;\n }\n\n try {\n this.currentVersionContent = version.Content || '';\n\n // Load previous version\n const previousVersion = this.versions.find(v => v.VersionNumber === version.VersionNumber - 1);\n if (previousVersion) {\n this.previousVersionContent = previousVersion.Content || '';\n this.showDiff = true;\n }\n } catch (error) {\n console.error('Failed to load version for comparison:', error);\n alert('Failed to compare versions');\n }\n }\n\n onDownloadVersion(version: MJArtifactVersionEntity): void {\n try {\n const content = version.Content || '';\n const download = buildVersionDownload(content, version.FileName, this.artifact.Name, version.VersionNumber, version.MimeType);\n const blob = new Blob([download.data], { type: download.mimeType });\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = download.fileName;\n link.click();\n window.URL.revokeObjectURL(url);\n } catch (error) {\n console.error('Failed to download version:', error);\n alert('Failed to download version');\n }\n }\n\n getContentSize(content: string | null): string {\n if (!content) return '0 B';\n const bytes = new Blob([content]).size;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n getDiffSummary(): string {\n // Simple line-based diff summary\n const currentLines = this.currentVersionContent.split('\\n');\n const previousLines = this.previousVersionContent.split('\\n');\n\n const added = currentLines.length - previousLines.length;\n const summary = `Lines: ${previousLines.length} → ${currentLines.length} (${added > 0 ? '+' : ''}${added})\\n\\n`;\n\n // Show a simple comparison\n return summary + '(Full diff view would require a diff library)';\n }\n\n onClose(): void {\n this.closed.emit();\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-artifacts",
3
- "version": "6.1.0-edge.3",
3
+ "version": "6.1.0-edge.5",
4
4
  "description": "MemberJunction: Artifact viewer plugin system for rendering different artifact types (JSON, Code, Markdown, HTML, SVG, Components)",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -13,8 +13,8 @@
13
13
  "devDependencies": {
14
14
  "@angular/compiler": "21.1.3",
15
15
  "@angular/compiler-cli": "21.1.3",
16
- "@memberjunction/core": "6.1.0-edge.3",
17
- "@memberjunction/ng-test-utils": "6.1.0-edge.3",
16
+ "@memberjunction/core": "6.1.0-edge.5",
17
+ "@memberjunction/ng-test-utils": "6.1.0-edge.5",
18
18
  "sass": "^1.97.3",
19
19
  "vitest": "^4.0.18"
20
20
  },
@@ -29,24 +29,24 @@
29
29
  "dependencies": {
30
30
  "@angular/cdk": "21.1.3",
31
31
  "@angular/forms": "21.1.3",
32
- "@memberjunction/core": "6.1.0-edge.3",
33
- "@memberjunction/core-entities": "6.1.0-edge.3",
34
- "@memberjunction/global": "6.1.0-edge.3",
35
- "@memberjunction/graphql-dataprovider": "6.1.0-edge.3",
36
- "@memberjunction/interactive-component-types": "6.1.0-edge.3",
37
- "@memberjunction/ng-base-forms": "6.1.0-edge.3",
38
- "@memberjunction/ng-base-types": "6.1.0-edge.3",
39
- "@memberjunction/ng-code-editor": "6.1.0-edge.3",
40
- "@memberjunction/ng-export-service": "6.1.0-edge.3",
41
- "@memberjunction/ng-markdown": "6.1.0-edge.3",
42
- "@memberjunction/ng-media-player": "6.1.0-edge.3",
43
- "@memberjunction/ng-notifications": "6.1.0-edge.3",
44
- "@memberjunction/ng-pagination": "6.1.0-edge.3",
45
- "@memberjunction/ng-query-viewer": "6.1.0-edge.3",
46
- "@memberjunction/ng-react": "6.1.0-edge.3",
47
- "@memberjunction/ng-shared-generic": "6.1.0-edge.3",
48
- "@memberjunction/ng-trees": "6.1.0-edge.3",
49
- "@memberjunction/ng-ui-components": "6.1.0-edge.3",
32
+ "@memberjunction/core": "6.1.0-edge.5",
33
+ "@memberjunction/core-entities": "6.1.0-edge.5",
34
+ "@memberjunction/global": "6.1.0-edge.5",
35
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.5",
36
+ "@memberjunction/interactive-component-types": "6.1.0-edge.5",
37
+ "@memberjunction/ng-base-forms": "6.1.0-edge.5",
38
+ "@memberjunction/ng-base-types": "6.1.0-edge.5",
39
+ "@memberjunction/ng-code-editor": "6.1.0-edge.5",
40
+ "@memberjunction/ng-export-service": "6.1.0-edge.5",
41
+ "@memberjunction/ng-markdown": "6.1.0-edge.5",
42
+ "@memberjunction/ng-media-player": "6.1.0-edge.5",
43
+ "@memberjunction/ng-notifications": "6.1.0-edge.5",
44
+ "@memberjunction/ng-pagination": "6.1.0-edge.5",
45
+ "@memberjunction/ng-query-viewer": "6.1.0-edge.5",
46
+ "@memberjunction/ng-react": "6.1.0-edge.5",
47
+ "@memberjunction/ng-shared-generic": "6.1.0-edge.5",
48
+ "@memberjunction/ng-trees": "6.1.0-edge.5",
49
+ "@memberjunction/ng-ui-components": "6.1.0-edge.5",
50
50
  "mammoth": "^1.8.0",
51
51
  "pdfjs-dist": "^4.10.38",
52
52
  "rxjs": "^7.8.2",