@memberjunction/ng-conversations 5.49.0 → 5.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.
Files changed (52) hide show
  1. package/README.md +64 -3
  2. package/dist/lib/components/collection/collections-full-view.component.d.ts +10 -0
  3. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
  4. package/dist/lib/components/collection/collections-full-view.component.js +74 -14
  5. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
  6. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +31 -5
  7. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  8. package/dist/lib/components/conversation/conversation-chat-area.component.js +169 -101
  9. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  10. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +5 -1
  11. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
  12. package/dist/lib/components/conversation/conversation-empty-state.component.js +13 -3
  13. package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
  14. package/dist/lib/components/conversation/conversation-list.component.d.ts +16 -1
  15. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  16. package/dist/lib/components/conversation/conversation-list.component.js +439 -351
  17. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  18. package/dist/lib/components/export/export-modal.component.d.ts +25 -3
  19. package/dist/lib/components/export/export-modal.component.d.ts.map +1 -1
  20. package/dist/lib/components/export/export-modal.component.js +172 -34
  21. package/dist/lib/components/export/export-modal.component.js.map +1 -1
  22. package/dist/lib/components/message/message-input.component.d.ts +4 -1
  23. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  24. package/dist/lib/components/message/message-input.component.js +15 -3
  25. package/dist/lib/components/message/message-input.component.js.map +1 -1
  26. package/dist/lib/components/message/message-item.component.d.ts +46 -1
  27. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  28. package/dist/lib/components/message/message-item.component.js +295 -201
  29. package/dist/lib/components/message/message-item.component.js.map +1 -1
  30. package/dist/lib/components/message/message-list.component.d.ts +19 -1
  31. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  32. package/dist/lib/components/message/message-list.component.js +55 -1
  33. package/dist/lib/components/message/message-list.component.js.map +1 -1
  34. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +14 -1
  35. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
  36. package/dist/lib/components/sidebar/conversation-sidebar.component.js +38 -5
  37. package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
  38. package/dist/lib/components/slots/slot-interfaces.d.ts +23 -1
  39. package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -1
  40. package/dist/lib/components/slots/slot-interfaces.js.map +1 -1
  41. package/dist/lib/directives/chat-slot.directive.d.ts +10 -2
  42. package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -1
  43. package/dist/lib/directives/chat-slot.directive.js.map +1 -1
  44. package/dist/lib/models/lazy-artifact-info.d.ts +8 -7
  45. package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
  46. package/dist/lib/models/lazy-artifact-info.js +12 -11
  47. package/dist/lib/models/lazy-artifact-info.js.map +1 -1
  48. package/dist/lib/services/export.service.d.ts +149 -0
  49. package/dist/lib/services/export.service.d.ts.map +1 -1
  50. package/dist/lib/services/export.service.js +270 -36
  51. package/dist/lib/services/export.service.js.map +1 -1
  52. package/package.json +29 -29
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-sidebar.component.js","sourceRoot":"","sources":["../../../../src/lib/components/sidebar/conversation-sidebar.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;;;;;;IAU3D,AADF,8BAA6B,8BAW2B;IAApD,AADA,AADA,AADA,kPAAwB,wCAAiC,KAAC,uOAC9B,sCAA+B,KAAC,6NACrC,8BAAuB,KAAC,iOACtB,gCAAyB,KAAC;IAEvD,AADE,iBAAuB,EACnB;;;IAXF,cAA+B;IAK/B,AADA,AADA,AADA,AADA,AADA,oDAA+B,mCACJ,yDACsB,uDACF,2CACZ,qCACN;;;IASjC,8BAA6B;IAC3B,wCAGqB;IACvB,iBAAM;;;IAHF,cAA+B;IAC/B,AADA,oDAA+B,mCACJ;;AA0CvC,MAAM,OAAO,4BAA6B,SAAQ,oBAAoB;IAC3D,SAAS,GAAkB,eAAe,CAAC;IAC3C,aAAa,CAAU;IACvB,WAAW,CAAY;IACvB,sBAAsB,GAAkB,IAAI,CAAC;IAC7C,qBAAqB,GAAkB,IAAI,CAAC;IAC5C,eAAe,GAAY,IAAI,CAAC;IAChC,YAAY,GAAY,KAAK,CAAC;IACvC,yGAAyG;IAChG,YAAY,GAAY,IAAI,CAAC;IAE5B,oBAAoB,GAAG,IAAI,YAAY,EAAU,CAAC;IAC5D,yFAAyF;IAC/E,gBAAgB,GAAG,IAAI,YAAY,EAAsD,CAAC;IAC1F,wBAAwB,GAAG,IAAI,YAAY,EAAQ,CAAC;IACpD,mBAAmB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC/C,qBAAqB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAE3D,qBAAqB;QACnB,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAED,uBAAuB;QACrB,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;6RAxBU,4BAA4B,yBAA5B,4BAA4B;6DAA5B,4BAA4B;YA/DrC,8BAAkC;YAChC,8FAAqC;YAgBrC,8FAAmC;YAYnC,2DAIyD;YAAvD,AADA,+JAAoB,iCAA6B,IAAC,kJAC9B,qCAAiC,IAAC;YAE1D,AADE,iBAAmC,EAC/B;;YAlCJ,cAeC;YAfD,4DAeC;YACD,cAOC;YAPD,0DAOC;YAMC,cAAqB;YACrB,AADA,uCAAqB,kCACQ;;;iFAgCxB,4BAA4B;cAnExC,SAAS;6BACI,KAAK,YACP,yBAAyB,YACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqCP;;kBA4BF,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAEL,KAAK;;kBAEL,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kFAhBI,4BAA4B","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { CompositeKey, UserInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { NavigationTab } from '../../models/conversation-state.model';\n\n@Component({\n standalone: false,\n selector: 'mj-conversation-sidebar',\n template: `\n <div class=\"conversation-sidebar\">\n @if (activeTab === 'conversations') {\n <div class=\"sidebar-content\">\n <mj-conversation-list\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [selectedConversationId]=\"selectedConversationId\"\n [renamedConversationId]=\"renamedConversationId\"\n [isSidebarPinned]=\"isSidebarPinned\"\n [isMobileView]=\"isMobileView\"\n (conversationSelected)=\"conversationSelected.emit($event)\"\n (newConversationRequested)=\"newConversationRequested.emit()\"\n (pinSidebarRequested)=\"onPinSidebarRequested()\"\n (unpinSidebarRequested)=\"onUnpinSidebarRequested()\">\n </mj-conversation-list>\n </div>\n }\n @if (activeTab === 'collections') {\n <div class=\"sidebar-content\">\n <mj-collection-tree\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\">\n </mj-collection-tree>\n </div>\n }\n\n <!-- Routines — pinned at the very bottom of the sidebar. Gated by the\n ShowRoutines opt-out AND the user's Read permission on\n 'MJ: User Routines' (checked inside the section component). -->\n <mj-conversation-routines-section\n [Provider]=\"Provider\"\n [ShowRoutines]=\"ShowRoutines\"\n (openEntityRecord)=\"openEntityRecord.emit($event)\"\n (openConversation)=\"conversationSelected.emit($event)\">\n </mj-conversation-routines-section>\n </div>\n `,\n styles: [`\n .conversation-sidebar {\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n .sidebar-content {\n flex: 1;\n overflow-y: auto;\n }\n .placeholder {\n padding: 24px;\n text-align: center;\n color: var(--mj-text-disabled);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n }\n .placeholder p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class ConversationSidebarComponent extends BaseAngularComponent {\n @Input() activeTab: NavigationTab = 'conversations';\n @Input() environmentId!: string;\n @Input() currentUser!: UserInfo;\n @Input() selectedConversationId: string | null = null;\n @Input() renamedConversationId: string | null = null;\n @Input() isSidebarPinned: boolean = true;\n @Input() isMobileView: boolean = false;\n /** Show the Routines section at the bottom of the sidebar (bubbled from the workspace; default true). */\n @Input() ShowRoutines: boolean = true;\n\n @Output() conversationSelected = new EventEmitter<string>();\n /** Forwarded from the routines section — a run's linked execution record was clicked. */\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; compositeKey: CompositeKey }>();\n @Output() newConversationRequested = new EventEmitter<void>();\n @Output() pinSidebarRequested = new EventEmitter<void>();\n @Output() unpinSidebarRequested = new EventEmitter<void>();\n\n onPinSidebarRequested(): void {\n this.pinSidebarRequested.emit();\n }\n\n onUnpinSidebarRequested(): void {\n this.unpinSidebarRequested.emit();\n }\n}"]}
1
+ {"version":3,"file":"conversation-sidebar.component.js","sourceRoot":"","sources":["../../../../src/lib/components/sidebar/conversation-sidebar.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;;;;;;IAU3D,AADF,8BAA6B,8BAiB2B;IAApD,AADA,AADA,AADA,AADA,AADA,kPAAwB,wCAAiC,KAAC,mOACnC,uCAAgC,KAAC,uOAC5B,sCAA+B,KAAC,uNACxC,8BAAuB,KAAC,6NACrB,8BAAuB,KAAC,iOACtB,gCAAyB,KAAC;IAEvD,AADE,iBAAuB,EACnB;;;IAjBF,cAA+B;IAS/B,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,oDAA+B,mCACJ,yDACsB,uDACF,2CACZ,qCACN,iCACJ,+DAC8B,yCACtB,iDACQ;;;IAW7C,8BAA6B;IAC3B,wCAGqB;IACvB,iBAAM;;;IAHF,cAA+B;IAC/B,AADA,oDAA+B,mCACJ;;AA0CvC,MAAM,OAAO,4BAA6B,SAAQ,oBAAoB;IAC3D,SAAS,GAAkB,eAAe,CAAC;IAC3C,aAAa,CAAU;IACvB,WAAW,CAAY;IACvB,sBAAsB,GAAkB,IAAI,CAAC;IAC7C,qBAAqB,GAAkB,IAAI,CAAC;IAC5C,eAAe,GAAY,IAAI,CAAC;IAChC,YAAY,GAAY,KAAK,CAAC;IACvC,yGAAyG;IAChG,YAAY,GAAY,IAAI,CAAC;IAEtC,yEAAyE;IACzE,6EAA6E;IAC7E,kCAAkC;IACzB,UAAU,GAAY,IAAI,CAAC;IACpC,iDAAiD;IACxC,yBAAyB,GAAY,IAAI,CAAC;IACnD,6CAA6C;IACpC,cAAc,GAAY,IAAI,CAAC;IACxC,mDAAmD;IAC1C,kBAAkB,GAAY,IAAI,CAAC;IAElC,oBAAoB,GAAG,IAAI,YAAY,EAAU,CAAC;IAC5D,yFAAyF;IAC/E,gBAAgB,GAAG,IAAI,YAAY,EAAsD,CAAC;IAC1F,wBAAwB,GAAG,IAAI,YAAY,EAAQ,CAAC;IACpD,mBAAmB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC/C,qBAAqB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC3D;6EACyE;IAC/D,mBAAmB,GAAG,IAAI,YAAY,EAAU,CAAC;IAC3D,2EAA2E;IACjE,gBAAgB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAEtD,qBAAqB;QACnB,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAED,uBAAuB;QACrB,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;6RAxCU,4BAA4B,yBAA5B,4BAA4B;6DAA5B,4BAA4B;YArErC,8BAAkC;YAChC,+FAAqC;YAsBrC,8FAAmC;YAYnC,2DAIyD;YAAvD,AADA,+JAAoB,iCAA6B,IAAC,kJAC9B,qCAAiC,IAAC;YAE1D,AADE,iBAAmC,EAC/B;;YAxCJ,cAqBC;YArBD,4DAqBC;YACD,cAOC;YAPD,0DAOC;YAMC,cAAqB;YACrB,AADA,uCAAqB,kCACQ;;;iFAgCxB,4BAA4B;cAzExC,SAAS;6BACI,KAAK,YACP,yBAAyB,YACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2CP;;kBA4BF,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAEL,KAAK;;kBAKL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK;;kBAEL,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBAGN,MAAM;;kBAEN,MAAM;;kFAhCI,4BAA4B","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { CompositeKey, UserInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { NavigationTab } from '../../models/conversation-state.model';\n\n@Component({\n standalone: false,\n selector: 'mj-conversation-sidebar',\n template: `\n <div class=\"conversation-sidebar\">\n @if (activeTab === 'conversations') {\n <div class=\"sidebar-content\">\n <mj-conversation-list\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [selectedConversationId]=\"selectedConversationId\"\n [renamedConversationId]=\"renamedConversationId\"\n [isSidebarPinned]=\"isSidebarPinned\"\n [isMobileView]=\"isMobileView\"\n [showSearch]=\"showSearch\"\n [showNewConversationButton]=\"showNewConversationButton\"\n [showHeaderMenu]=\"showHeaderMenu\"\n [showSectionHeaders]=\"showSectionHeaders\"\n (conversationSelected)=\"conversationSelected.emit($event)\"\n (conversationDeleted)=\"conversationDeleted.emit($event)\"\n (newConversationRequested)=\"newConversationRequested.emit()\"\n (refreshRequested)=\"refreshRequested.emit()\"\n (pinSidebarRequested)=\"onPinSidebarRequested()\"\n (unpinSidebarRequested)=\"onUnpinSidebarRequested()\">\n </mj-conversation-list>\n </div>\n }\n @if (activeTab === 'collections') {\n <div class=\"sidebar-content\">\n <mj-collection-tree\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\">\n </mj-collection-tree>\n </div>\n }\n\n <!-- Routines — pinned at the very bottom of the sidebar. Gated by the\n ShowRoutines opt-out AND the user's Read permission on\n 'MJ: User Routines' (checked inside the section component). -->\n <mj-conversation-routines-section\n [Provider]=\"Provider\"\n [ShowRoutines]=\"ShowRoutines\"\n (openEntityRecord)=\"openEntityRecord.emit($event)\"\n (openConversation)=\"conversationSelected.emit($event)\">\n </mj-conversation-routines-section>\n </div>\n `,\n styles: [`\n .conversation-sidebar {\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n .sidebar-content {\n flex: 1;\n overflow-y: auto;\n }\n .placeholder {\n padding: 24px;\n text-align: center;\n color: var(--mj-text-disabled);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n }\n .placeholder p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class ConversationSidebarComponent extends BaseAngularComponent {\n @Input() activeTab: NavigationTab = 'conversations';\n @Input() environmentId!: string;\n @Input() currentUser!: UserInfo;\n @Input() selectedConversationId: string | null = null;\n @Input() renamedConversationId: string | null = null;\n @Input() isSidebarPinned: boolean = true;\n @Input() isMobileView: boolean = false;\n /** Show the Routines section at the bottom of the sidebar (bubbled from the workspace; default true). */\n @Input() ShowRoutines: boolean = true;\n\n // ── White-label chrome toggles, passed through to the conversation list\n // (all default true = stock rendering; see ConversationListComponent). ──\n /** Show the list's search box. */\n @Input() showSearch: boolean = true;\n /** Show the list's \"New Conversation\" button. */\n @Input() showNewConversationButton: boolean = true;\n /** Show the list's ⋯ header options menu. */\n @Input() showHeaderMenu: boolean = true;\n /** Show the list's collapsible section headers. */\n @Input() showSectionHeaders: boolean = true;\n\n @Output() conversationSelected = new EventEmitter<string>();\n /** Forwarded from the routines section — a run's linked execution record was clicked. */\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; compositeKey: CompositeKey }>();\n @Output() newConversationRequested = new EventEmitter<void>();\n @Output() pinSidebarRequested = new EventEmitter<void>();\n @Output() unpinSidebarRequested = new EventEmitter<void>();\n /** Re-emitted from the conversation list — a conversation was deleted (payload = its ID).\n * Hosts use this to recover when the ACTIVE conversation is deleted. */\n @Output() conversationDeleted = new EventEmitter<string>();\n /** Re-emitted from the conversation list — the user refreshed the list. */\n @Output() refreshRequested = new EventEmitter<void>();\n\n onPinSidebarRequested(): void {\n this.pinSidebarRequested.emit();\n }\n\n onUnpinSidebarRequested(): void {\n this.unpinSidebarRequested.emit();\n }\n}"]}
@@ -15,7 +15,7 @@
15
15
  * @module @memberjunction/ng-conversations
16
16
  */
17
17
  import type { EventEmitter } from '@angular/core';
18
- import type { MJConversationDetailEntity } from '@memberjunction/core-entities';
18
+ import type { MJConversationDetailEntity, MJConversationEntity } from '@memberjunction/core-entities';
19
19
  /** State surfaced to the agent-presence slot for visualization. */
20
20
  export type MJChatAgentPresenceState = 'idle' | 'listening' | 'thinking' | 'speaking';
21
21
  /** Contract for the `emptyState` slot — what renders before any messages exist. */
@@ -51,6 +51,28 @@ export interface IMJChatHeaderComponent {
51
51
  /** Pass-through whether the artifact-count chip should render at all. */
52
52
  ShowArtifactIndicator?: boolean;
53
53
  }
54
+ /**
55
+ * Template CONTEXT for the `headerActions` slot — extra host buttons rendered
56
+ * INSIDE the default header's action strip, as the last children after the
57
+ * stock buttons (pin / artifacts / members / pickers / export / share).
58
+ *
59
+ * Unlike the other slot contracts this is a template-context shape, not a
60
+ * component contract: the slot is purely additive chrome, so consumers project
61
+ * an ad-hoc `<ng-template mjChatSlot="headerActions" let-conversation>` rather
62
+ * than implementing a component. For visual parity, projected buttons should
63
+ * use `mjButton variant="flat" size="sm"` like the stock actions.
64
+ *
65
+ * NOT rendered when a full `header` slot is projected — that slot replaces the
66
+ * entire header, actions included.
67
+ */
68
+ export interface IMJChatHeaderActionsContext {
69
+ /** The active conversation (null before one exists). */
70
+ $implicit: MJConversationEntity | null;
71
+ /** The active conversation's ID (null before one exists). */
72
+ conversationId: string | null;
73
+ /** True while an agent turn is in flight — disable actions that mutate the conversation. */
74
+ isProcessing: boolean;
75
+ }
54
76
  /**
55
77
  * Contract for the `messageExtra` slot — per-message inline decoration rendered
56
78
  * within the message bubble, after the content.
@@ -1 +1 @@
1
- {"version":3,"file":"slot-interfaces.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/slots/slot-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAEhF,mEAAmE;AACnE,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtF,mFAAmF;AACnF,MAAM,WAAW,0BAA0B;IACvC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,kEAAkE;IAClE,cAAc,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC1C,KAAK,EAAE,wBAAwB,CAAC;IAChC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;CACjC;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAsB;IACnC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IACzC,OAAO,EAAE,0BAA0B,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,oCAAoC;IACjD,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,+BAA+B;IAC5C,6CAA6C;IAC7C,OAAO,EAAE,OAAO,+BAA+B,EAAE,0BAA0B,GAAG,IAAI,CAAC;CACtF"}
1
+ {"version":3,"file":"slot-interfaces.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/slots/slot-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAEtG,mEAAmE;AACnE,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtF,mFAAmF;AACnF,MAAM,WAAW,0BAA0B;IACvC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,kEAAkE;IAClE,cAAc,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC1C,KAAK,EAAE,wBAAwB,CAAC;IAChC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;CACjC;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAsB;IACnC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,2BAA2B;IACxC,wDAAwD;IACxD,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC,6DAA6D;IAC7D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IACzC,OAAO,EAAE,0BAA0B,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,oCAAoC;IACjD,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,+BAA+B;IAC5C,6CAA6C;IAC7C,OAAO,EAAE,OAAO,+BAA+B,EAAE,0BAA0B,GAAG,IAAI,CAAC;CACtF"}
@@ -1 +1 @@
1
- {"version":3,"file":"slot-interfaces.js","sourceRoot":"","sources":["../../../../src/lib/components/slots/slot-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG","sourcesContent":["/**\n * @fileoverview Interface contracts for the chat-area's slot system.\n *\n * Each slot has a typed interface so the widget can speak with confidence to\n * whatever component (default or consumer-supplied) is rendered into it. Each\n * interface is paired with an exported standalone default component (in\n * `*-default.component.ts` siblings) that consumers can:\n *\n * 1. **Project an ad-hoc template into**, via the `mjChatSlot` directive.\n * 2. **Wrap with a containment pattern** — render the default inside their own\n * component to add framing without losing the default behavior.\n * 3. **Subclass** — extend the default standalone component, override only what\n * they need, declare the subclass as the slot's component.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport type { EventEmitter } from '@angular/core';\nimport type { MJConversationDetailEntity } from '@memberjunction/core-entities';\n\n/** State surfaced to the agent-presence slot for visualization. */\nexport type MJChatAgentPresenceState = 'idle' | 'listening' | 'thinking' | 'speaking';\n\n/** Contract for the `emptyState` slot — what renders before any messages exist. */\nexport interface IMJChatEmptyStateComponent {\n /** Top-line welcome text. */\n Greeting: string;\n /** Optional secondary line beneath the greeting. */\n Subtext?: string;\n /** Optional suggested prompts to render as clickable chips. */\n SuggestedPrompts?: string[];\n /** Emitted when the user selects one of the suggested prompts. */\n PromptSelected: EventEmitter<string>;\n}\n\n/**\n * Contract for the `agentPresence` slot — character / avatar / voice-state\n * visualization, rendered alongside the chat composer.\n */\nexport interface IMJChatAgentPresenceComponent {\n State: MJChatAgentPresenceState;\n /** Display name of the agent, e.g., \"Sage\" or \"Sid\". */\n AgentName?: string;\n /** Optional avatar image URL. */\n AvatarUrl?: string;\n /** Visual intensity — subtle for production chat, prominent for tutor surfaces. */\n Mode?: 'subtle' | 'prominent';\n}\n\n/** Contract for the `header` slot — top bar above the message list. */\nexport interface IMJChatHeaderComponent {\n ConversationTitle?: string | null;\n SharedBy?: string | null;\n /** Number of artifacts on the conversation (for an inline badge). */\n ArtifactCount?: number;\n /** Pass-through whether the artifact-count chip should render at all. */\n ShowArtifactIndicator?: boolean;\n}\n\n/**\n * Contract for the `messageExtra` slot — per-message inline decoration rendered\n * within the message bubble, after the content.\n */\nexport interface IMJChatMessageExtraComponent {\n Message: MJConversationDetailEntity;\n}\n\n/**\n * Contract for the `demonstrationSurface` slot — full-width adjacent surface\n * for content the agent is \"walking through\" (e.g., annotated lesson material,\n * step-by-step screenshots). Off unless a slot is supplied.\n */\nexport interface IMJChatDemonstrationSurfaceComponent {\n /** Optional content payload — shape is consumer-defined. */\n Content?: unknown;\n /** Whether the surface should be visible. */\n Visible?: boolean;\n}\n\n/**\n * Contract for the `messageRenderer` slot — per-item template applied to every\n * message in the list. Unlike the other slots (which replace a fixed positional\n * zone), `messageRenderer` is invoked once per message via `*ngTemplateOutlet`\n * with the message as the implicit context (`let-message`).\n *\n * Lets consumers swap the entire feed-vs-bubble-vs-anything-else rendering\n * decision without forking. Two default components ship:\n *\n * • `MJChatMessageFeedDefaultComponent` — the existing avatar+name+timestamp\n * feed layout (current behavior; default when no slot is supplied).\n * • `MJChatMessageBubbleDefaultComponent` — bubble layout where identity\n * comes from side + shape (user on right, agent on left), using the\n * `--mj-chat-bubble-*` tokens for color.\n *\n * Implementing components keep the contract minimal — just receive the message\n * entity and render however they want. Subclasses extend a default to inherit\n * its shell and override specific behavior.\n */\nexport interface IMJChatMessageRendererComponent {\n /** The conversation detail row to render. */\n Message: import('@memberjunction/core-entities').MJConversationDetailEntity | null;\n}\n"]}
1
+ {"version":3,"file":"slot-interfaces.js","sourceRoot":"","sources":["../../../../src/lib/components/slots/slot-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG","sourcesContent":["/**\n * @fileoverview Interface contracts for the chat-area's slot system.\n *\n * Each slot has a typed interface so the widget can speak with confidence to\n * whatever component (default or consumer-supplied) is rendered into it. Each\n * interface is paired with an exported standalone default component (in\n * `*-default.component.ts` siblings) that consumers can:\n *\n * 1. **Project an ad-hoc template into**, via the `mjChatSlot` directive.\n * 2. **Wrap with a containment pattern** — render the default inside their own\n * component to add framing without losing the default behavior.\n * 3. **Subclass** — extend the default standalone component, override only what\n * they need, declare the subclass as the slot's component.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport type { EventEmitter } from '@angular/core';\nimport type { MJConversationDetailEntity, MJConversationEntity } from '@memberjunction/core-entities';\n\n/** State surfaced to the agent-presence slot for visualization. */\nexport type MJChatAgentPresenceState = 'idle' | 'listening' | 'thinking' | 'speaking';\n\n/** Contract for the `emptyState` slot — what renders before any messages exist. */\nexport interface IMJChatEmptyStateComponent {\n /** Top-line welcome text. */\n Greeting: string;\n /** Optional secondary line beneath the greeting. */\n Subtext?: string;\n /** Optional suggested prompts to render as clickable chips. */\n SuggestedPrompts?: string[];\n /** Emitted when the user selects one of the suggested prompts. */\n PromptSelected: EventEmitter<string>;\n}\n\n/**\n * Contract for the `agentPresence` slot — character / avatar / voice-state\n * visualization, rendered alongside the chat composer.\n */\nexport interface IMJChatAgentPresenceComponent {\n State: MJChatAgentPresenceState;\n /** Display name of the agent, e.g., \"Sage\" or \"Sid\". */\n AgentName?: string;\n /** Optional avatar image URL. */\n AvatarUrl?: string;\n /** Visual intensity — subtle for production chat, prominent for tutor surfaces. */\n Mode?: 'subtle' | 'prominent';\n}\n\n/** Contract for the `header` slot — top bar above the message list. */\nexport interface IMJChatHeaderComponent {\n ConversationTitle?: string | null;\n SharedBy?: string | null;\n /** Number of artifacts on the conversation (for an inline badge). */\n ArtifactCount?: number;\n /** Pass-through whether the artifact-count chip should render at all. */\n ShowArtifactIndicator?: boolean;\n}\n\n/**\n * Template CONTEXT for the `headerActions` slot — extra host buttons rendered\n * INSIDE the default header's action strip, as the last children after the\n * stock buttons (pin / artifacts / members / pickers / export / share).\n *\n * Unlike the other slot contracts this is a template-context shape, not a\n * component contract: the slot is purely additive chrome, so consumers project\n * an ad-hoc `<ng-template mjChatSlot=\"headerActions\" let-conversation>` rather\n * than implementing a component. For visual parity, projected buttons should\n * use `mjButton variant=\"flat\" size=\"sm\"` like the stock actions.\n *\n * NOT rendered when a full `header` slot is projected — that slot replaces the\n * entire header, actions included.\n */\nexport interface IMJChatHeaderActionsContext {\n /** The active conversation (null before one exists). */\n $implicit: MJConversationEntity | null;\n /** The active conversation's ID (null before one exists). */\n conversationId: string | null;\n /** True while an agent turn is in flight — disable actions that mutate the conversation. */\n isProcessing: boolean;\n}\n\n/**\n * Contract for the `messageExtra` slot — per-message inline decoration rendered\n * within the message bubble, after the content.\n */\nexport interface IMJChatMessageExtraComponent {\n Message: MJConversationDetailEntity;\n}\n\n/**\n * Contract for the `demonstrationSurface` slot — full-width adjacent surface\n * for content the agent is \"walking through\" (e.g., annotated lesson material,\n * step-by-step screenshots). Off unless a slot is supplied.\n */\nexport interface IMJChatDemonstrationSurfaceComponent {\n /** Optional content payload — shape is consumer-defined. */\n Content?: unknown;\n /** Whether the surface should be visible. */\n Visible?: boolean;\n}\n\n/**\n * Contract for the `messageRenderer` slot — per-item template applied to every\n * message in the list. Unlike the other slots (which replace a fixed positional\n * zone), `messageRenderer` is invoked once per message via `*ngTemplateOutlet`\n * with the message as the implicit context (`let-message`).\n *\n * Lets consumers swap the entire feed-vs-bubble-vs-anything-else rendering\n * decision without forking. Two default components ship:\n *\n * • `MJChatMessageFeedDefaultComponent` — the existing avatar+name+timestamp\n * feed layout (current behavior; default when no slot is supplied).\n * • `MJChatMessageBubbleDefaultComponent` — bubble layout where identity\n * comes from side + shape (user on right, agent on left), using the\n * `--mj-chat-bubble-*` tokens for color.\n *\n * Implementing components keep the contract minimal — just receive the message\n * entity and render however they want. Subclasses extend a default to inherit\n * its shell and override specific behavior.\n */\nexport interface IMJChatMessageRendererComponent {\n /** The conversation detail row to render. */\n Message: import('@memberjunction/core-entities').MJConversationDetailEntity | null;\n}\n"]}
@@ -25,8 +25,16 @@
25
25
  */
26
26
  import { TemplateRef } from '@angular/core';
27
27
  import * as i0 from "@angular/core";
28
- /** Named slots the chat-area exposes. Keep in sync with the slot-interfaces module. */
29
- export type MJChatSlotName = 'emptyState' | 'agentPresence' | 'header' | 'messageExtra' | 'demonstrationSurface' | 'messageRenderer';
28
+ /**
29
+ * Named slots the chat-area exposes. Keep in sync with the slot-interfaces module.
30
+ *
31
+ * Note the header pair: `header` REPLACES the entire chat header (the consumer
32
+ * owns title, badges, and every action button), while `headerActions` is
33
+ * ADDITIVE — it renders the projected template inside the DEFAULT header's
34
+ * action strip, after the stock buttons. Projecting `header` suppresses
35
+ * `headerActions` (the replacement owns the whole header, actions included).
36
+ */
37
+ export type MJChatSlotName = 'emptyState' | 'agentPresence' | 'header' | 'headerActions' | 'messageExtra' | 'demonstrationSurface' | 'messageRenderer';
30
38
  /**
31
39
  * Marks an `<ng-template>` as a slot fill for the chat-area.
32
40
  *
@@ -1 +1 @@
1
- {"version":3,"file":"chat-slot.directive.d.ts","sourceRoot":"","sources":["../../../src/lib/directives/chat-slot.directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAoB,WAAW,EAAE,MAAM,eAAe,CAAC;;AAE9D,uFAAuF;AACvF,MAAM,MAAM,cAAc,GACpB,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,cAAc,GACd,sBAAsB,GACtB,iBAAiB,CAAC;AAExB;;;;;GAKG;AACH,qBAIa,iBAAiB;aAIE,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;IAH1D,sCAAsC;IACV,QAAQ,EAAG,cAAc,CAAC;gBAE1B,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;yCAJjD,iBAAiB;2CAAjB,iBAAiB;CAK7B"}
1
+ {"version":3,"file":"chat-slot.directive.d.ts","sourceRoot":"","sources":["../../../src/lib/directives/chat-slot.directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAoB,WAAW,EAAE,MAAM,eAAe,CAAC;;AAE9D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GACpB,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,eAAe,GACf,cAAc,GACd,sBAAsB,GACtB,iBAAiB,CAAC;AAExB;;;;;GAKG;AACH,qBAIa,iBAAiB;aAIE,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;IAH1D,sCAAsC;IACV,QAAQ,EAAG,cAAc,CAAC;gBAE1B,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;yCAJjD,iBAAiB;2CAAjB,iBAAiB;CAK7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"chat-slot.directive.js","sourceRoot":"","sources":["../../../src/lib/directives/chat-slot.directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAe,MAAM,eAAe,CAAC;;AAW9D;;;;;GAKG;AAKH,MAAM,OAAO,iBAAiB;IAIE;IAH5B,sCAAsC;IACV,QAAQ,CAAkB;IAEtD,YAA4B,QAA8B;QAA9B,aAAQ,GAAR,QAAQ,CAAsB;IAAG,CAAC;2GAJrD,iBAAiB;6DAAjB,iBAAiB;;iFAAjB,iBAAiB;cAJ7B,SAAS;eAAC;gBACP,QAAQ,EAAE,cAAc;gBACxB,UAAU,EAAE,IAAI;aACnB;;kBAGI,KAAK;mBAAC,YAAY","sourcesContent":["/**\n * @fileoverview ChatSlotDirective — content-projection lookup for chat-area slots.\n *\n * Consumers project a template into a specific slot by attaching this directive:\n *\n * ```html\n * <mj-conversation-chat-area>\n * <ng-template mjChatSlot=\"emptyState\">\n * <warm-tutor-welcome />\n * </ng-template>\n *\n * <ng-template mjChatSlot=\"agentPresence\" let-state>\n * <my-character-presence [State]=\"state\" />\n * </ng-template>\n * </mj-conversation-chat-area>\n * ```\n *\n * The chat-area component queries `@ContentChildren(ChatSlotDirective)` and\n * looks up the `TemplateRef` for each known slot name. When a consumer template\n * is present for a slot, the component renders it via `*ngTemplateOutlet`\n * (with a typed context); when absent, the component falls back to its\n * exported standalone default component for that slot.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport { Directive, Input, TemplateRef } from '@angular/core';\n\n/** Named slots the chat-area exposes. Keep in sync with the slot-interfaces module. */\nexport type MJChatSlotName =\n | 'emptyState'\n | 'agentPresence'\n | 'header'\n | 'messageExtra'\n | 'demonstrationSurface'\n | 'messageRenderer';\n\n/**\n * Marks an `<ng-template>` as a slot fill for the chat-area.\n *\n * Standalone — no module declaration required; consumers import it as part of\n * the conversations module's exports.\n */\n@Directive({\n selector: '[mjChatSlot]',\n standalone: true,\n})\nexport class ChatSlotDirective {\n /** Which slot this template fills. */\n @Input('mjChatSlot') public SlotName!: MJChatSlotName;\n\n constructor(public readonly Template: TemplateRef<unknown>) {}\n}\n"]}
1
+ {"version":3,"file":"chat-slot.directive.js","sourceRoot":"","sources":["../../../src/lib/directives/chat-slot.directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAe,MAAM,eAAe,CAAC;;AAoB9D;;;;;GAKG;AAKH,MAAM,OAAO,iBAAiB;IAIE;IAH5B,sCAAsC;IACV,QAAQ,CAAkB;IAEtD,YAA4B,QAA8B;QAA9B,aAAQ,GAAR,QAAQ,CAAsB;IAAG,CAAC;2GAJrD,iBAAiB;6DAAjB,iBAAiB;;iFAAjB,iBAAiB;cAJ7B,SAAS;eAAC;gBACP,QAAQ,EAAE,cAAc;gBACxB,UAAU,EAAE,IAAI;aACnB;;kBAGI,KAAK;mBAAC,YAAY","sourcesContent":["/**\n * @fileoverview ChatSlotDirective — content-projection lookup for chat-area slots.\n *\n * Consumers project a template into a specific slot by attaching this directive:\n *\n * ```html\n * <mj-conversation-chat-area>\n * <ng-template mjChatSlot=\"emptyState\">\n * <warm-tutor-welcome />\n * </ng-template>\n *\n * <ng-template mjChatSlot=\"agentPresence\" let-state>\n * <my-character-presence [State]=\"state\" />\n * </ng-template>\n * </mj-conversation-chat-area>\n * ```\n *\n * The chat-area component queries `@ContentChildren(ChatSlotDirective)` and\n * looks up the `TemplateRef` for each known slot name. When a consumer template\n * is present for a slot, the component renders it via `*ngTemplateOutlet`\n * (with a typed context); when absent, the component falls back to its\n * exported standalone default component for that slot.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport { Directive, Input, TemplateRef } from '@angular/core';\n\n/**\n * Named slots the chat-area exposes. Keep in sync with the slot-interfaces module.\n *\n * Note the header pair: `header` REPLACES the entire chat header (the consumer\n * owns title, badges, and every action button), while `headerActions` is\n * ADDITIVE — it renders the projected template inside the DEFAULT header's\n * action strip, after the stock buttons. Projecting `header` suppresses\n * `headerActions` (the replacement owns the whole header, actions included).\n */\nexport type MJChatSlotName =\n | 'emptyState'\n | 'agentPresence'\n | 'header'\n | 'headerActions'\n | 'messageExtra'\n | 'demonstrationSurface'\n | 'messageRenderer';\n\n/**\n * Marks an `<ng-template>` as a slot fill for the chat-area.\n *\n * Standalone — no module declaration required; consumers import it as part of\n * the conversations module's exports.\n */\n@Directive({\n selector: '[mjChatSlot]',\n standalone: true,\n})\nexport class ChatSlotDirective {\n /** Which slot this template fills. */\n @Input('mjChatSlot') public SlotName!: MJChatSlotName;\n\n constructor(public readonly Template: TemplateRef<unknown>) {}\n}\n"]}
@@ -4,14 +4,15 @@ import { UserInfo } from '@memberjunction/core';
4
4
  * Represents artifact information with lazy-loading capabilities.
5
5
  * Stores minimal display data initially (from query) and loads full entities on-demand.
6
6
  *
7
- * For full entity access, delegates to {@link ArtifactMetadataEngine} which is kept
8
- * in sync automatically via BaseEngine's MJGlobal event listener. When any
9
- * BaseEntity save/delete fires in the same process, the engine's in-memory
10
- * arrays are updated immediately so callers always get the freshest data
11
- * without tight coupling between the conversation layer and artifact editors.
7
+ * For full entity access, this first checks {@link ArtifactMetadataEngine}'s
8
+ * on-demand caches (populated when an artifact's versions were loaded earlier
9
+ * this session) and otherwise loads the single artifact/version directly from
10
+ * the database. Artifacts and versions are no longer bulk-loaded at boot — a
11
+ * version's `Content` can be arbitrarily large so a direct load is the normal
12
+ * path here, not just a fallback.
12
13
  *
13
- * A one-time direct load fallback is used when the engine hasn't loaded yet
14
- * or the entity is brand-new and hasn't propagated to the engine's cache.
14
+ * The direct load is coalesced via a shared promise so concurrent callers issue
15
+ * a single query.
15
16
  */
16
17
  export declare class LazyArtifactInfo {
17
18
  private currentUser;
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-artifact-info.d.ts","sourceRoot":"","sources":["../../../src/lib/models/lazy-artifact-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAA0B,MAAM,+BAA+B,CAAC;AAClH,OAAO,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAgB;IAsBzB,OAAO,CAAC,WAAW;IApBrB,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAC7C,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAC1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,SAAgB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,SAAgB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,SAAgB,gBAAgB,EAAE,IAAI,CAAC;IACvC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAC5C,SAAgB,UAAU,EAAE,MAAM,CAAC;IAGnC,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,oBAAoB,CAA8B;gBAGxD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,WAAW,EAAE,QAAQ,EAC7B,iBAAiB,CAAC,EAAE,gBAAgB,EACpC,gBAAgB,CAAC,EAAE,uBAAuB;IA0B5C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAc9C;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAcpD;;;OAGG;IACH,IAAI,QAAQ,IAAI,OAAO,CAUtB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;OAGG;YACW,oBAAoB;IASlC;;;OAGG;YACW,mBAAmB;IASjC;;;;OAIG;YACW,qBAAqB;YAkBrB,cAAc;YAed,kBAAkB;YAMlB,iBAAiB;CAKhC"}
1
+ {"version":3,"file":"lazy-artifact-info.d.ts","sourceRoot":"","sources":["../../../src/lib/models/lazy-artifact-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAA0B,MAAM,+BAA+B,CAAC;AAClH,OAAO,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,qBAAa,gBAAgB;IAsBzB,OAAO,CAAC,WAAW;IApBrB,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAC7C,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAC1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,SAAgB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,SAAgB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,SAAgB,gBAAgB,EAAE,IAAI,CAAC;IACvC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,SAAgB,mBAAmB,EAAE,MAAM,CAAC;IAC5C,SAAgB,UAAU,EAAE,MAAM,CAAC;IAGnC,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,oBAAoB,CAA8B;gBAGxD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,WAAW,EAAE,QAAQ,EAC7B,iBAAiB,CAAC,EAAE,gBAAgB,EACpC,gBAAgB,CAAC,EAAE,uBAAuB;IA0B5C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAc9C;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAcpD;;;OAGG;IACH,IAAI,QAAQ,IAAI,OAAO,CAUtB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;OAGG;YACW,oBAAoB;IASlC;;;OAGG;YACW,mBAAmB;IASjC;;;;OAIG;YACW,qBAAqB;YAkBrB,cAAc;YAed,kBAAkB;YAMlB,iBAAiB;CAKhC"}
@@ -4,14 +4,15 @@ import { Metadata } from '@memberjunction/core';
4
4
  * Represents artifact information with lazy-loading capabilities.
5
5
  * Stores minimal display data initially (from query) and loads full entities on-demand.
6
6
  *
7
- * For full entity access, delegates to {@link ArtifactMetadataEngine} which is kept
8
- * in sync automatically via BaseEngine's MJGlobal event listener. When any
9
- * BaseEntity save/delete fires in the same process, the engine's in-memory
10
- * arrays are updated immediately so callers always get the freshest data
11
- * without tight coupling between the conversation layer and artifact editors.
7
+ * For full entity access, this first checks {@link ArtifactMetadataEngine}'s
8
+ * on-demand caches (populated when an artifact's versions were loaded earlier
9
+ * this session) and otherwise loads the single artifact/version directly from
10
+ * the database. Artifacts and versions are no longer bulk-loaded at boot — a
11
+ * version's `Content` can be arbitrarily large so a direct load is the normal
12
+ * path here, not just a fallback.
12
13
  *
13
- * A one-time direct load fallback is used when the engine hasn't loaded yet
14
- * or the entity is brand-new and hasn't propagated to the engine's cache.
14
+ * The direct load is coalesced via a shared promise so concurrent callers issue
15
+ * a single query.
15
16
  */
16
17
  export class LazyArtifactInfo {
17
18
  currentUser;
@@ -64,7 +65,7 @@ export class LazyArtifactInfo {
64
65
  // Try the engine first — it stays in sync via BaseEntity events
65
66
  const engine = ArtifactMetadataEngine.Instance;
66
67
  if (engine.Loaded) {
67
- const fromEngine = engine.FindArtifactByID(this.artifactId);
68
+ const fromEngine = engine.FindCachedArtifactByID(this.artifactId);
68
69
  if (fromEngine) {
69
70
  return fromEngine;
70
71
  }
@@ -80,7 +81,7 @@ export class LazyArtifactInfo {
80
81
  // Try the engine first — it stays in sync via BaseEntity events
81
82
  const engine = ArtifactMetadataEngine.Instance;
82
83
  if (engine.Loaded) {
83
- const fromEngine = engine.FindArtifactVersionByID(this.artifactVersionId);
84
+ const fromEngine = engine.FindCachedArtifactVersionByID(this.artifactVersionId);
84
85
  if (fromEngine) {
85
86
  return fromEngine;
86
87
  }
@@ -95,8 +96,8 @@ export class LazyArtifactInfo {
95
96
  get isLoaded() {
96
97
  const engine = ArtifactMetadataEngine.Instance;
97
98
  if (engine.Loaded) {
98
- const hasArtifact = !!engine.FindArtifactByID(this.artifactId);
99
- const hasVersion = !!engine.FindArtifactVersionByID(this.artifactVersionId);
99
+ const hasArtifact = !!engine.FindCachedArtifactByID(this.artifactId);
100
+ const hasVersion = !!engine.FindCachedArtifactVersionByID(this.artifactVersionId);
100
101
  if (hasArtifact && hasVersion) {
101
102
  return true;
102
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-artifact-info.js","sourceRoot":"","sources":["../../../src/lib/models/lazy-artifact-info.ts"],"names":[],"mappings":"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","sourcesContent":["import { MJArtifactEntity, MJArtifactVersionEntity, ArtifactMetadataEngine } from '@memberjunction/core-entities';\nimport { Metadata, UserInfo } from '@memberjunction/core';\n\n/**\n * Represents artifact information with lazy-loading capabilities.\n * Stores minimal display data initially (from query) and loads full entities on-demand.\n *\n * For full entity access, delegates to {@link ArtifactMetadataEngine} which is kept\n * in sync automatically via BaseEngine's MJGlobal event listener. When any\n * BaseEntity save/delete fires in the same process, the engine's in-memory\n * arrays are updated immediately — so callers always get the freshest data\n * without tight coupling between the conversation layer and artifact editors.\n *\n * A one-time direct load fallback is used when the engine hasn't loaded yet\n * or the entity is brand-new and hasn't propagated to the engine's cache.\n */\nexport class LazyArtifactInfo {\n // Display data (always available from initial query - no lazy loading needed)\n public readonly conversationDetailId: string;\n public readonly direction: string;\n public readonly artifactVersionId: string;\n public readonly versionNumber: number;\n public readonly versionName: string | null;\n public readonly versionDescription: string | null;\n public readonly versionCreatedAt: Date;\n public readonly artifactId: string;\n public readonly artifactName: string;\n public readonly artifactType: string;\n public readonly artifactDescription: string;\n public readonly visibility: string;\n\n // Fallback entities loaded directly when engine doesn't have them yet\n private _fallbackArtifact: MJArtifactEntity | null = null;\n private _fallbackVersion: MJArtifactVersionEntity | null = null;\n private _fallbackLoadPromise: Promise<void> | null = null;\n\n constructor(\n queryResult: Record<string, unknown>,\n private currentUser: UserInfo,\n preloadedArtifact?: MJArtifactEntity,\n preloadedVersion?: MJArtifactVersionEntity\n ) {\n // Populate display data from query result\n // These fields come from GetConversationComplete query\n this.conversationDetailId = queryResult.ConversationDetailID as string;\n this.direction = queryResult.Direction as string;\n this.artifactVersionId = queryResult.ArtifactVersionID as string;\n this.versionNumber = queryResult.VersionNumber as number;\n this.versionName = (queryResult.VersionName as string) || null;\n this.versionDescription = (queryResult.VersionDescription as string) || null;\n this.versionCreatedAt = queryResult.VersionCreatedAt ? new Date(queryResult.VersionCreatedAt as string) : new Date();\n this.artifactId = queryResult.ArtifactID as string;\n this.artifactName = queryResult.ArtifactName as string;\n this.artifactType = queryResult.ArtifactType as string;\n this.artifactDescription = (queryResult.ArtifactDescription as string) || '';\n this.visibility = (queryResult.Visibility as string) || 'User';\n\n // If entities were pre-loaded via batch query, store as fallbacks\n if (preloadedArtifact) {\n this._fallbackArtifact = preloadedArtifact;\n }\n if (preloadedVersion) {\n this._fallbackVersion = preloadedVersion;\n }\n }\n\n /**\n * Gets the full Artifact entity.\n * Checks ArtifactMetadataEngine first (always fresh), falls back to direct load.\n */\n async getArtifact(): Promise<MJArtifactEntity> {\n // Try the engine first — it stays in sync via BaseEntity events\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const fromEngine = engine.FindArtifactByID(this.artifactId);\n if (fromEngine) {\n return fromEngine;\n }\n }\n\n // Engine doesn't have it yet — use fallback\n return this.loadArtifactFallback();\n }\n\n /**\n * Gets the full ArtifactVersion entity including Content field.\n * Checks ArtifactMetadataEngine first (always fresh), falls back to direct load.\n */\n async getVersion(): Promise<MJArtifactVersionEntity> {\n // Try the engine first — it stays in sync via BaseEntity events\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const fromEngine = engine.FindArtifactVersionByID(this.artifactVersionId);\n if (fromEngine) {\n return fromEngine;\n }\n }\n\n // Engine doesn't have it yet — use fallback\n return this.loadVersionFallback();\n }\n\n /**\n * Checks if the artifact entities can be accessed without triggering a load.\n * True if the engine has them or fallbacks are populated.\n */\n get isLoaded(): boolean {\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const hasArtifact = !!engine.FindArtifactByID(this.artifactId);\n const hasVersion = !!engine.FindArtifactVersionByID(this.artifactVersionId);\n if (hasArtifact && hasVersion) {\n return true;\n }\n }\n return this._fallbackArtifact !== null && this._fallbackVersion !== null;\n }\n\n /**\n * Checks if a fallback load is currently in progress.\n */\n get isLoading(): boolean {\n return this._fallbackLoadPromise !== null;\n }\n\n /**\n * Loads the artifact directly from the database as a fallback.\n * Uses a shared promise to coalesce concurrent calls.\n */\n private async loadArtifactFallback(): Promise<MJArtifactEntity> {\n if (this._fallbackArtifact) {\n return this._fallbackArtifact;\n }\n\n await this.ensureFallbacksLoaded();\n return this._fallbackArtifact!;\n }\n\n /**\n * Loads the artifact version directly from the database as a fallback.\n * Uses a shared promise to coalesce concurrent calls.\n */\n private async loadVersionFallback(): Promise<MJArtifactVersionEntity> {\n if (this._fallbackVersion) {\n return this._fallbackVersion;\n }\n\n await this.ensureFallbacksLoaded();\n return this._fallbackVersion!;\n }\n\n /**\n * Loads both artifact and version from database in parallel.\n * Called only when ArtifactMetadataEngine doesn't have the entities.\n * Shares a single promise across concurrent requests.\n */\n private async ensureFallbacksLoaded(): Promise<void> {\n if (this._fallbackArtifact && this._fallbackVersion) {\n return;\n }\n\n if (this._fallbackLoadPromise) {\n await this._fallbackLoadPromise;\n return;\n }\n\n this._fallbackLoadPromise = this.doFallbackLoad();\n try {\n await this._fallbackLoadPromise;\n } finally {\n this._fallbackLoadPromise = null;\n }\n }\n\n private async doFallbackLoad(): Promise<void> {\n try {\n const md = new Metadata(); // global-provider-ok: utility — single-provider context\n const [artifact, version] = await Promise.all([\n this._fallbackArtifact ? Promise.resolve(this._fallbackArtifact) : this.loadSingleArtifact(md),\n this._fallbackVersion ? Promise.resolve(this._fallbackVersion) : this.loadSingleVersion(md)\n ]);\n this._fallbackArtifact = artifact;\n this._fallbackVersion = version;\n } catch (error) {\n console.error('LazyArtifactInfo: Error loading fallback entities:', error);\n throw error;\n }\n }\n\n private async loadSingleArtifact(md: Metadata): Promise<MJArtifactEntity> {\n const artifact = await md.GetEntityObject<MJArtifactEntity>('MJ: Artifacts', this.currentUser);\n await artifact.Load(this.artifactId);\n return artifact;\n }\n\n private async loadSingleVersion(md: Metadata): Promise<MJArtifactVersionEntity> {\n const version = await md.GetEntityObject<MJArtifactVersionEntity>('MJ: Artifact Versions', this.currentUser);\n await version.Load(this.artifactVersionId);\n return version;\n }\n}\n"]}
1
+ {"version":3,"file":"lazy-artifact-info.js","sourceRoot":"","sources":["../../../src/lib/models/lazy-artifact-info.ts"],"names":[],"mappings":"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","sourcesContent":["import { MJArtifactEntity, MJArtifactVersionEntity, ArtifactMetadataEngine } from '@memberjunction/core-entities';\nimport { Metadata, UserInfo } from '@memberjunction/core';\n\n/**\n * Represents artifact information with lazy-loading capabilities.\n * Stores minimal display data initially (from query) and loads full entities on-demand.\n *\n * For full entity access, this first checks {@link ArtifactMetadataEngine}'s\n * on-demand caches (populated when an artifact's versions were loaded earlier\n * this session) and otherwise loads the single artifact/version directly from\n * the database. Artifacts and versions are no longer bulk-loaded at boot — a\n * version's `Content` can be arbitrarily large — so a direct load is the normal\n * path here, not just a fallback.\n *\n * The direct load is coalesced via a shared promise so concurrent callers issue\n * a single query.\n */\nexport class LazyArtifactInfo {\n // Display data (always available from initial query - no lazy loading needed)\n public readonly conversationDetailId: string;\n public readonly direction: string;\n public readonly artifactVersionId: string;\n public readonly versionNumber: number;\n public readonly versionName: string | null;\n public readonly versionDescription: string | null;\n public readonly versionCreatedAt: Date;\n public readonly artifactId: string;\n public readonly artifactName: string;\n public readonly artifactType: string;\n public readonly artifactDescription: string;\n public readonly visibility: string;\n\n // Fallback entities loaded directly when engine doesn't have them yet\n private _fallbackArtifact: MJArtifactEntity | null = null;\n private _fallbackVersion: MJArtifactVersionEntity | null = null;\n private _fallbackLoadPromise: Promise<void> | null = null;\n\n constructor(\n queryResult: Record<string, unknown>,\n private currentUser: UserInfo,\n preloadedArtifact?: MJArtifactEntity,\n preloadedVersion?: MJArtifactVersionEntity\n ) {\n // Populate display data from query result\n // These fields come from GetConversationComplete query\n this.conversationDetailId = queryResult.ConversationDetailID as string;\n this.direction = queryResult.Direction as string;\n this.artifactVersionId = queryResult.ArtifactVersionID as string;\n this.versionNumber = queryResult.VersionNumber as number;\n this.versionName = (queryResult.VersionName as string) || null;\n this.versionDescription = (queryResult.VersionDescription as string) || null;\n this.versionCreatedAt = queryResult.VersionCreatedAt ? new Date(queryResult.VersionCreatedAt as string) : new Date();\n this.artifactId = queryResult.ArtifactID as string;\n this.artifactName = queryResult.ArtifactName as string;\n this.artifactType = queryResult.ArtifactType as string;\n this.artifactDescription = (queryResult.ArtifactDescription as string) || '';\n this.visibility = (queryResult.Visibility as string) || 'User';\n\n // If entities were pre-loaded via batch query, store as fallbacks\n if (preloadedArtifact) {\n this._fallbackArtifact = preloadedArtifact;\n }\n if (preloadedVersion) {\n this._fallbackVersion = preloadedVersion;\n }\n }\n\n /**\n * Gets the full Artifact entity.\n * Checks ArtifactMetadataEngine first (always fresh), falls back to direct load.\n */\n async getArtifact(): Promise<MJArtifactEntity> {\n // Try the engine first — it stays in sync via BaseEntity events\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const fromEngine = engine.FindCachedArtifactByID(this.artifactId);\n if (fromEngine) {\n return fromEngine;\n }\n }\n\n // Engine doesn't have it yet — use fallback\n return this.loadArtifactFallback();\n }\n\n /**\n * Gets the full ArtifactVersion entity including Content field.\n * Checks ArtifactMetadataEngine first (always fresh), falls back to direct load.\n */\n async getVersion(): Promise<MJArtifactVersionEntity> {\n // Try the engine first — it stays in sync via BaseEntity events\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const fromEngine = engine.FindCachedArtifactVersionByID(this.artifactVersionId);\n if (fromEngine) {\n return fromEngine;\n }\n }\n\n // Engine doesn't have it yet — use fallback\n return this.loadVersionFallback();\n }\n\n /**\n * Checks if the artifact entities can be accessed without triggering a load.\n * True if the engine has them or fallbacks are populated.\n */\n get isLoaded(): boolean {\n const engine = ArtifactMetadataEngine.Instance;\n if (engine.Loaded) {\n const hasArtifact = !!engine.FindCachedArtifactByID(this.artifactId);\n const hasVersion = !!engine.FindCachedArtifactVersionByID(this.artifactVersionId);\n if (hasArtifact && hasVersion) {\n return true;\n }\n }\n return this._fallbackArtifact !== null && this._fallbackVersion !== null;\n }\n\n /**\n * Checks if a fallback load is currently in progress.\n */\n get isLoading(): boolean {\n return this._fallbackLoadPromise !== null;\n }\n\n /**\n * Loads the artifact directly from the database as a fallback.\n * Uses a shared promise to coalesce concurrent calls.\n */\n private async loadArtifactFallback(): Promise<MJArtifactEntity> {\n if (this._fallbackArtifact) {\n return this._fallbackArtifact;\n }\n\n await this.ensureFallbacksLoaded();\n return this._fallbackArtifact!;\n }\n\n /**\n * Loads the artifact version directly from the database as a fallback.\n * Uses a shared promise to coalesce concurrent calls.\n */\n private async loadVersionFallback(): Promise<MJArtifactVersionEntity> {\n if (this._fallbackVersion) {\n return this._fallbackVersion;\n }\n\n await this.ensureFallbacksLoaded();\n return this._fallbackVersion!;\n }\n\n /**\n * Loads both artifact and version from database in parallel.\n * Called only when ArtifactMetadataEngine doesn't have the entities.\n * Shares a single promise across concurrent requests.\n */\n private async ensureFallbacksLoaded(): Promise<void> {\n if (this._fallbackArtifact && this._fallbackVersion) {\n return;\n }\n\n if (this._fallbackLoadPromise) {\n await this._fallbackLoadPromise;\n return;\n }\n\n this._fallbackLoadPromise = this.doFallbackLoad();\n try {\n await this._fallbackLoadPromise;\n } finally {\n this._fallbackLoadPromise = null;\n }\n }\n\n private async doFallbackLoad(): Promise<void> {\n try {\n const md = new Metadata(); // global-provider-ok: utility — single-provider context\n const [artifact, version] = await Promise.all([\n this._fallbackArtifact ? Promise.resolve(this._fallbackArtifact) : this.loadSingleArtifact(md),\n this._fallbackVersion ? Promise.resolve(this._fallbackVersion) : this.loadSingleVersion(md)\n ]);\n this._fallbackArtifact = artifact;\n this._fallbackVersion = version;\n } catch (error) {\n console.error('LazyArtifactInfo: Error loading fallback entities:', error);\n throw error;\n }\n }\n\n private async loadSingleArtifact(md: Metadata): Promise<MJArtifactEntity> {\n const artifact = await md.GetEntityObject<MJArtifactEntity>('MJ: Artifacts', this.currentUser);\n await artifact.Load(this.artifactId);\n return artifact;\n }\n\n private async loadSingleVersion(md: Metadata): Promise<MJArtifactVersionEntity> {\n const version = await md.GetEntityObject<MJArtifactVersionEntity>('MJ: Artifact Versions', this.currentUser);\n await version.Load(this.artifactVersionId);\n return version;\n }\n}\n"]}
@@ -1,12 +1,93 @@
1
+ import { MJConversationEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';
1
2
  import { UserInfo, IMetadataProvider } from '@memberjunction/core';
2
3
  import * as i0 from "@angular/core";
3
4
  export type ExportFormat = 'json' | 'markdown' | 'html' | 'text';
5
+ /**
6
+ * Branding applied to exported files, across EVERY format. HTML gets the full
7
+ * treatment (theme colors, an inlined logo, the title, and a trademark footer);
8
+ * JSON/markdown/text carry the title and trademark (markdown also references the
9
+ * logo). Hosts supply this via `mj-conversation-chat-area`'s `exportBranding`
10
+ * input. Color theming (`brandTokens`) is HTML-only — it has no meaning in the
11
+ * data/plain-text formats.
12
+ */
13
+ export interface ExportBranding {
14
+ /**
15
+ * CSS custom-property snapshot emitted as a `:root{}` block in the exported
16
+ * document, so the export's stylesheet (which reads `var(--mj-…, fallback)`)
17
+ * renders in the app's theme. HTML export only. Keys must be valid
18
+ * custom-property names (`--like-this`). Entries are DROPPED when the key is
19
+ * invalid or the value carries declaration-escape / network-call characters
20
+ * (`<>{};@\`, `url(`, `expression(`) — plain colors, `var()`, and
21
+ * `color-mix()` pass. When omitted and `ExportOptions.includeTheme` is true,
22
+ * the current theme is auto-snapshotted from `document.documentElement` (see
23
+ * {@link DEFAULT_EXPORT_THEME_TOKENS}).
24
+ */
25
+ brandTokens?: Record<string, string>;
26
+ /**
27
+ * Logo URL. In HTML it is fetched and inlined as a data URI (so the file stays
28
+ * self-contained) and rendered above the title — requires
29
+ * `ExportOptions.includeCSS`, since it needs its stylesheet rule. In markdown it is
30
+ * referenced by URL. JSON carries it as a string; plain text omits it.
31
+ *
32
+ * If the fetch fails, or the response isn't `content-type: image/*`, the raw URL is
33
+ * emitted instead — so the exported file will reach out to that URL when opened.
34
+ * There is no size or timeout guard on the fetch: a very large logo inflates the
35
+ * export.
36
+ */
37
+ logoUrl?: string;
38
+ /** Overrides the exported document's title (defaults to the conversation name). */
39
+ title?: string;
40
+ /**
41
+ * A short trademark / attribution line (e.g. `© 2026 Acme Association ·
42
+ * Powered by Betty`) rendered at the FOOT of the exported document: a styled HTML
43
+ * footer, a markdown/text trailer, and a field in the JSON `branding` block.
44
+ *
45
+ * In HTML it needs its stylesheet rule, so — like the logo — it is emitted only when
46
+ * `ExportOptions.includeCSS` is true. The markdown/text/JSON trailers have no such
47
+ * dependency and always emit.
48
+ *
49
+ * NOT escaped for markdown: a value containing `_`, `]`, `)`, or a newline can break
50
+ * the surrounding markdown. Supply plain text (HTML and JSON are escaped).
51
+ */
52
+ trademark?: string;
53
+ }
4
54
  export interface ExportOptions {
5
55
  includeMessages?: boolean;
6
56
  includeMetadata?: boolean;
7
57
  prettyPrint?: boolean;
8
58
  includeCSS?: boolean;
59
+ /**
60
+ * Emit the current app theme into the HTML export. When true and
61
+ * `branding.brandTokens` is absent, the {@link DEFAULT_EXPORT_THEME_TOKENS}
62
+ * are auto-snapshotted from the live document at export time. Default false —
63
+ * an unthemed export renders exactly as before (the stylesheet's `var()`
64
+ * fallbacks carry the legacy palette).
65
+ *
66
+ * Gates only the TOKEN auto-snapshot: a supplied `branding`'s `logoUrl`,
67
+ * `title`, and `trademark` apply whenever `branding` is present — in HTML AND
68
+ * in the JSON/markdown/text formats. (The export modal passes `branding`
69
+ * through only while its "Include branding" checkbox is on, so in the UI flow
70
+ * everything travels together.)
71
+ */
72
+ includeTheme?: boolean;
73
+ /**
74
+ * Which theme mode the snapshot captures — NOT whichever mode the app happens to be
75
+ * in when the user clicks Export. Defaults to `'light'`: an export taken during a
76
+ * dark session would otherwise bake dark text onto the page's white background and
77
+ * come out unreadable. `'dark'` pairs the dark palette with the dark
78
+ * `--mj-bg-page` the body rule applies, so the file is coherent either way.
79
+ * HTML only — the data formats carry no colors.
80
+ */
81
+ themeMode?: 'light' | 'dark';
82
+ /** Branding (tokens / logo / title) applied to the export — see {@link ExportBranding}. */
83
+ branding?: ExportBranding;
9
84
  }
85
+ /**
86
+ * Tokens captured by the theme auto-snapshot — exactly the custom properties the
87
+ * HTML export's stylesheet consumes. Hosts wanting more control supply their own
88
+ * map via {@link ExportBranding.brandTokens} instead.
89
+ */
90
+ export declare const DEFAULT_EXPORT_THEME_TOKENS: readonly string[];
10
91
  export declare class ExportService {
11
92
  private _provider;
12
93
  /**
@@ -15,6 +96,72 @@ export declare class ExportService {
15
96
  set Provider(value: IMetadataProvider | null);
16
97
  get Provider(): IMetadataProvider;
17
98
  exportConversation(conversationId: string, format: ExportFormat, currentUser: UserInfo, options?: ExportOptions): Promise<void>;
99
+ /**
100
+ * Build the export document for already-loaded conversation data — the
101
+ * download-free seam of {@link exportConversation}. Async because HTML-format
102
+ * branding may inline a logo. Exposed publicly so hosts (and tests) can build
103
+ * export content without triggering a browser download.
104
+ */
105
+ BuildExportContent(data: {
106
+ conversation: MJConversationEntity;
107
+ details: MJConversationDetailEntity[];
108
+ }, format: ExportFormat, options?: ExportOptions): Promise<{
109
+ content: string;
110
+ filename: string;
111
+ mimeType: string;
112
+ }>;
113
+ /**
114
+ * Snapshot the given theme tokens from the live document. Empty/unset tokens are
115
+ * skipped.
116
+ *
117
+ * `mode` captures a SPECIFIC palette rather than whatever the app is rendering right
118
+ * now. Without it, exporting from a dark session bakes dark text onto the export's
119
+ * white page and the file is unreadable. When the requested mode isn't the active one,
120
+ * `data-theme` is flipped on `documentElement`, read, and restored — all synchronous
121
+ * within one task, so the browser never paints the intermediate state. Omit `mode`
122
+ * to read the live values as-is.
123
+ */
124
+ SnapshotBrandTokens(tokens?: readonly string[], mode?: 'light' | 'dark'): Record<string, string>;
125
+ /** Resolve the HTML export's theme pieces once: tokens (explicit > snapshot), logo, title. */
126
+ private resolveTheme;
127
+ /** Explicit host tokens win; `includeTheme` falls back to a live snapshot; else no theme. */
128
+ private resolveBrandTokens;
129
+ /** Document title used by every format: branding override → conversation name → generic. */
130
+ private resolveTitle;
131
+ /**
132
+ * The JSON `branding` block — the data-format analogue of the HTML header +
133
+ * trademark footer. Carries the title, trademark, and logo URL the host set;
134
+ * null when no branding (or none of those fields) is present. Theme color
135
+ * tokens are HTML-only and never included here.
136
+ */
137
+ private buildBrandingBlock;
138
+ /**
139
+ * CSS functions permitted inside an exported token value. This is an ALLOWLIST
140
+ * (not a blocklist) so no network-capable function can slip through: blocking
141
+ * only `url(`/`expression(` let `image-set()`, `-webkit-image-set()`,
142
+ * `cross-fade()`, `image()`, and `src()` fetch a beacon URL without ever
143
+ * writing the literal `url(`. Anything not on this list drops the whole entry.
144
+ * The set is the safe value/color-math functions the export could legitimately
145
+ * carry; extend it only with functions that CANNOT load an external resource.
146
+ */
147
+ private static readonly SAFE_CSS_FUNCTIONS;
148
+ /**
149
+ * Emit the sanitized `:root{}` style block. Keys must be custom-property names.
150
+ * A value is REJECTED (the entry is dropped, not stripped) when it carries any
151
+ * declaration/tag-escape or network-reach vector: `<>{}` (tag/rule breakout),
152
+ * `;` (declaration breakout — e.g. `; background: url(beacon)`), `\` (CSS
153
+ * escapes), `@` (at-rules), OR any CSS function not in
154
+ * {@link SAFE_CSS_FUNCTIONS} (which excludes every resource-loading function —
155
+ * url/image-set/cross-fade/image/src/expression/…). Plain colors, `var()`, and
156
+ * `color-mix()` pass untouched.
157
+ */
158
+ private buildRootBlock;
159
+ /** True if the value calls any CSS function outside {@link SAFE_CSS_FUNCTIONS}. */
160
+ private hasUnsafeCssFunction;
161
+ /** Fetch → data URI so the export stays self-contained; raw URL on any failure
162
+ * (or when the response isn't actually an image — don't inline HTML/other as
163
+ * a bogus `data:` image). */
164
+ private inlineLogo;
18
165
  private loadConversationData;
19
166
  private exportAsJSON;
20
167
  private exportAsMarkdown;
@@ -24,6 +171,8 @@ export declare class ExportService {
24
171
  private formatDate;
25
172
  private capitalizeRole;
26
173
  private escapeHtml;
174
+ /** escapeHtml + quote escaping — for values interpolated into HTML attributes. */
175
+ private escapeAttr;
27
176
  private getTimestamp;
28
177
  static ɵfac: i0.ɵɵFactoryDeclaration<ExportService, never>;
29
178
  static ɵprov: i0.ɵɵInjectableDeclaration<ExportService>;
@@ -1 +1 @@
1
- {"version":3,"file":"export.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,QAAQ,EAAY,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;;AAEtF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAGa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAkC;IAEnD;;OAEG;IACH,IAAW,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAElD;IAED,IAAW,QAAQ,IAAI,iBAAiB,CAEvC;IAEK,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,QAAQ,EACrB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC;YA2CF,oBAAoB;IA+BlC,OAAO,CAAC,YAAY;IA8CpB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,YAAY;IA+DpB,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,YAAY;yCAnTT,aAAa;6CAAb,aAAa;CAuTzB"}
1
+ {"version":3,"file":"export.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"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"}