@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.
- package/README.md +64 -3
- package/dist/lib/components/collection/collections-full-view.component.d.ts +10 -0
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +74 -14
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +31 -5
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +169 -101
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +5 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js +13 -3
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts +16 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.js +439 -351
- package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
- package/dist/lib/components/export/export-modal.component.d.ts +25 -3
- package/dist/lib/components/export/export-modal.component.d.ts.map +1 -1
- package/dist/lib/components/export/export-modal.component.js +172 -34
- package/dist/lib/components/export/export-modal.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +4 -1
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +15 -3
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +46 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +295 -201
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +19 -1
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +55 -1
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +14 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.js +38 -5
- package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
- package/dist/lib/components/slots/slot-interfaces.d.ts +23 -1
- package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -1
- package/dist/lib/components/slots/slot-interfaces.js.map +1 -1
- package/dist/lib/directives/chat-slot.directive.d.ts +10 -2
- package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -1
- package/dist/lib/directives/chat-slot.directive.js.map +1 -1
- package/dist/lib/models/lazy-artifact-info.d.ts +8 -7
- package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.js +12 -11
- package/dist/lib/models/lazy-artifact-info.js.map +1 -1
- package/dist/lib/services/export.service.d.ts +149 -0
- package/dist/lib/services/export.service.d.ts.map +1 -1
- package/dist/lib/services/export.service.js +270 -36
- package/dist/lib/services/export.service.js.map +1 -1
- 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,
|
|
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;
|
|
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
|
-
/**
|
|
29
|
-
|
|
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
|
|
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;;
|
|
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,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
14
|
-
*
|
|
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
|
|
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,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
14
|
-
*
|
|
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.
|
|
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.
|
|
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.
|
|
99
|
-
const hasVersion = !!engine.
|
|
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":"AAAA,OAAO,EAA6C,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAClH,OAAO,EAAE,QAAQ,EAAY,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,gBAAgB;IAsBjB;IArBV,8EAA8E;IAC9D,oBAAoB,CAAS;IAC7B,SAAS,CAAS;IAClB,iBAAiB,CAAS;IAC1B,aAAa,CAAS;IACtB,WAAW,CAAgB;IAC3B,kBAAkB,CAAgB;IAClC,gBAAgB,CAAO;IACvB,UAAU,CAAS;IACnB,YAAY,CAAS;IACrB,YAAY,CAAS;IACrB,mBAAmB,CAAS;IAC5B,UAAU,CAAS;IAEnC,sEAAsE;IAC9D,iBAAiB,GAA4B,IAAI,CAAC;IAClD,gBAAgB,GAAmC,IAAI,CAAC;IACxD,oBAAoB,GAAyB,IAAI,CAAC;IAE1D,YACE,WAAoC,EAC5B,WAAqB,EAC7B,iBAAoC,EACpC,gBAA0C;QAFlC,gBAAW,GAAX,WAAW,CAAU;QAI7B,0CAA0C;QAC1C,uDAAuD;QACvD,IAAI,CAAC,oBAAoB,GAAG,WAAW,CAAC,oBAA8B,CAAC;QACvE,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,SAAmB,CAAC;QACjD,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC,iBAA2B,CAAC;QACjE,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,aAAuB,CAAC;QACzD,IAAI,CAAC,WAAW,GAAI,WAAW,CAAC,WAAsB,IAAI,IAAI,CAAC;QAC/D,IAAI,CAAC,kBAAkB,GAAI,WAAW,CAAC,kBAA6B,IAAI,IAAI,CAAC;QAC7E,IAAI,CAAC,gBAAgB,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,gBAA0B,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QACrH,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,UAAoB,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,YAAsB,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,YAAsB,CAAC;QACvD,IAAI,CAAC,mBAAmB,GAAI,WAAW,CAAC,mBAA8B,IAAI,EAAE,CAAC;QAC7E,IAAI,CAAC,UAAU,GAAI,WAAW,CAAC,UAAqB,IAAI,MAAM,CAAC;QAE/D,kEAAkE;QAClE,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC7C,CAAC;QACD,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QAC3C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,gEAAgE;QAChE,MAAM,MAAM,GAAG,sBAAsB,CAAC,QAAQ,CAAC;QAC/C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,UAAU,GAAG,MAAM,CAAC,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAClE,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,UAAU,CAAC;YACpB,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,OAAO,IAAI,CAAC,oBAAoB,EAAE,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,gEAAgE;QAChE,MAAM,MAAM,GAAG,sBAAsB,CAAC,QAAQ,CAAC;QAC/C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,UAAU,GAAG,MAAM,CAAC,6BAA6B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAChF,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,UAAU,CAAC;YACpB,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,OAAO,IAAI,CAAC,mBAAmB,EAAE,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,IAAI,QAAQ;QACV,MAAM,MAAM,GAAG,sBAAsB,CAAC,QAAQ,CAAC;QAC/C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrE,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,6BAA6B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAClF,IAAI,WAAW,IAAI,UAAU,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,KAAK,IAAI,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,CAAC;IAC3E,CAAC;IAED;;OAEG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,oBAAoB,KAAK,IAAI,CAAC;IAC5C,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,oBAAoB;QAChC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,iBAAiB,CAAC;QAChC,CAAC;QAED,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,iBAAkB,CAAC;IACjC,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,mBAAmB;QAC/B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,gBAAgB,CAAC;QAC/B,CAAC;QAED,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,gBAAiB,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,qBAAqB;QACjC,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,oBAAoB,CAAC;YAChC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,oBAAoB,CAAC;QAClC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACnC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC,wDAAwD;YACnF,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC5C,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC9F,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;aAC5F,CAAC,CAAC;YACH,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC;YAClC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,oDAAoD,EAAE,KAAK,CAAC,CAAC;YAC3E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,EAAY;QAC3C,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,eAAe,CAAmB,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/F,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,EAAY;QAC1C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,eAAe,CAA0B,uBAAuB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7G,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC;IACjB,CAAC;CACF","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":"
|
|
1
|
+
{"version":3,"file":"export.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"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"}
|