@memberjunction/ng-conversations 6.2.0-edge.1 → 6.2.0-edge.2
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 +9 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.js +1 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +102 -2
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +457 -119
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts +3 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.js +22 -8
- package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
- package/dist/lib/components/message/actionable-commands.component.d.ts +12 -0
- package/dist/lib/components/message/actionable-commands.component.d.ts.map +1 -1
- package/dist/lib/components/message/actionable-commands.component.js +60 -12
- package/dist/lib/components/message/actionable-commands.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +68 -4
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +161 -22
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +80 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +193 -23
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +10 -1
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +38 -1
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +4 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/models/agent-turn.model.d.ts +12 -2
- package/dist/lib/models/agent-turn.model.d.ts.map +1 -1
- package/dist/lib/models/agent-turn.model.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +21 -0
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +28 -2
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-liveness-dom.service.d.ts +42 -0
- package/dist/lib/services/conversation-liveness-dom.service.d.ts.map +1 -0
- package/dist/lib/services/conversation-liveness-dom.service.js +74 -0
- package/dist/lib/services/conversation-liveness-dom.service.js.map +1 -0
- package/dist/lib/services/ui-command-handler.service.d.ts +40 -3
- package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
- package/dist/lib/services/ui-command-handler.service.js +78 -9
- package/dist/lib/services/ui-command-handler.service.js.map +1 -1
- package/dist/lib/utils/agent-turn-routing.d.ts +7 -6
- package/dist/lib/utils/agent-turn-routing.d.ts.map +1 -1
- package/dist/lib/utils/agent-turn-routing.js +4 -16
- package/dist/lib/utils/agent-turn-routing.js.map +1 -1
- package/dist/lib/utils/decision-routing.d.ts +68 -0
- package/dist/lib/utils/decision-routing.d.ts.map +1 -0
- package/dist/lib/utils/decision-routing.js +101 -0
- package/dist/lib/utils/decision-routing.js.map +1 -0
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +5 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +32 -32
|
@@ -23,6 +23,8 @@ export declare class PinnedMessagesPanelComponent {
|
|
|
23
23
|
set isLoading(value: PinnedMessagesPanelComponent['IsLoading']);
|
|
24
24
|
/** @deprecated Use {@link IsLoading}. */
|
|
25
25
|
get isLoading(): PinnedMessagesPanelComponent['IsLoading'];
|
|
26
|
+
/** When false, pins stay listed and jumpable but Unpin is hidden. */
|
|
27
|
+
AllowUnpin: boolean;
|
|
26
28
|
Closed: EventEmitter<void>;
|
|
27
29
|
/**
|
|
28
30
|
* @deprecated Use {@link Closed}.
|
|
@@ -67,6 +69,6 @@ export declare class PinnedMessagesPanelComponent {
|
|
|
67
69
|
/** Display name for the message sender */
|
|
68
70
|
GetSenderName(message: MJConversationDetailEntity): string;
|
|
69
71
|
static ɵfac: i0.ɵɵFactoryDeclaration<PinnedMessagesPanelComponent, never>;
|
|
70
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<PinnedMessagesPanelComponent, "mj-pinned-messages-panel", never, { "PinnedMessages": { "alias": "PinnedMessages"; "required": false; }; "pinnedMessages": { "alias": "pinnedMessages"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; }, { "Closed": "Closed"; "closed": "closed"; "JumpRequested": "JumpRequested"; "jumpRequested": "jumpRequested"; "UnpinRequested": "UnpinRequested"; "unpinRequested": "unpinRequested"; }, never, never, false, never>;
|
|
72
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PinnedMessagesPanelComponent, "mj-pinned-messages-panel", never, { "PinnedMessages": { "alias": "PinnedMessages"; "required": false; }; "pinnedMessages": { "alias": "pinnedMessages"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; "AllowUnpin": { "alias": "AllowUnpin"; "required": false; }; }, { "Closed": "Closed"; "closed": "closed"; "JumpRequested": "JumpRequested"; "jumpRequested": "jumpRequested"; "UnpinRequested": "UnpinRequested"; "unpinRequested": "unpinRequested"; }, never, never, false, never>;
|
|
71
73
|
}
|
|
72
74
|
//# sourceMappingURL=pinned-messages-panel.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinned-messages-panel.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/conversation/pinned-messages-panel.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;;AAE3E,qBAMa,4BAA4B;IACvC;;;;OAIG;IACa,cAAc,EAAE,SAAS,0BAA0B,EAAE,CAAM;IAE3E,8CAA8C;IAC9C,IAAoB,cAAc,CAAC,KAAK,EAAE,SAAS,0BAA0B,EAAE,EAE9E;IACD,8CAA8C;IAC9C,IAAW,cAAc,IAAI,SAAS,0BAA0B,EAAE,CAEjE;IAED;;;;;OAKG;IACa,SAAS,UAAS;IAElC,yCAAyC;IACzC,IAAoB,SAAS,CAAC,KAAK,EAAE,4BAA4B,CAAC,WAAW,CAAC,EAE7E;IACD,yCAAyC;IACzC,IAAW,SAAS,IAAI,4BAA4B,CAAC,WAAW,CAAC,CAEhE;
|
|
1
|
+
{"version":3,"file":"pinned-messages-panel.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/conversation/pinned-messages-panel.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;;AAE3E,qBAMa,4BAA4B;IACvC;;;;OAIG;IACa,cAAc,EAAE,SAAS,0BAA0B,EAAE,CAAM;IAE3E,8CAA8C;IAC9C,IAAoB,cAAc,CAAC,KAAK,EAAE,SAAS,0BAA0B,EAAE,EAE9E;IACD,8CAA8C;IAC9C,IAAW,cAAc,IAAI,SAAS,0BAA0B,EAAE,CAEjE;IAED;;;;;OAKG;IACa,SAAS,UAAS;IAElC,yCAAyC;IACzC,IAAoB,SAAS,CAAC,KAAK,EAAE,4BAA4B,CAAC,WAAW,CAAC,EAE7E;IACD,yCAAyC;IACzC,IAAW,SAAS,IAAI,4BAA4B,CAAC,WAAW,CAAC,CAEhE;IAED,qEAAqE;IACrD,UAAU,UAAQ;IAEjB,MAAM,qBAA4B;IAEnD;;;;;;OAMG;IACc,MAAM,qBAAe;IACrB,aAAa,uBAA8B;IAE5D;;;;;;OAMG;IACc,aAAa,uBAAsB;IACnC,cAAc,2CAAkD;IAEjF;;;;;;OAMG;IACc,cAAc,2CAAuB;IAEtD,2DAA2D;IACpD,YAAY,cAAqB;IAExC,4CAA4C;IAC5C,IAAW,YAAY,gBAEtB;IACD,4CAA4C;IAC5C,IAAW,YAAY,CAAC,KAAK,aAAA,EAE5B;IAEM,KAAK,IAAI,IAAI;IAIb,MAAM,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI;IAIjD,OAAO,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI;IAYlD,WAAW,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO;IAIhE,qDAAqD;IAC9C,cAAc,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM;IAgBlE,wCAAwC;IACjC,eAAe,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM;IAenE,0CAA0C;IACnC,aAAa,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM;yCA1ItD,4BAA4B;2CAA5B,4BAA4B;CA8IxC"}
|
|
@@ -9,6 +9,14 @@ function PinnedMessagesPanelComponent_Conditional_11_Template(rf, ctx) { if (rf
|
|
|
9
9
|
function PinnedMessagesPanelComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
10
10
|
i0.ɵɵelement(0, "mj-empty-state", 9);
|
|
11
11
|
} }
|
|
12
|
+
function PinnedMessagesPanelComponent_For_14_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
13
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
14
|
+
i0.ɵɵelementStart(0, "button", 23);
|
|
15
|
+
i0.ɵɵlistener("click", function PinnedMessagesPanelComponent_For_14_Conditional_15_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const message_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnUnpin(message_r2)); });
|
|
16
|
+
i0.ɵɵelement(1, "i", 3);
|
|
17
|
+
i0.ɵɵtext(2, " Unpin ");
|
|
18
|
+
i0.ɵɵelementEnd();
|
|
19
|
+
} }
|
|
12
20
|
function PinnedMessagesPanelComponent_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
13
21
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
14
22
|
i0.ɵɵelementStart(0, "div", 11)(1, "div", 12)(2, "div", 13)(3, "div", 14);
|
|
@@ -28,11 +36,8 @@ function PinnedMessagesPanelComponent_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
|
28
36
|
i0.ɵɵelement(13, "i", 21);
|
|
29
37
|
i0.ɵɵtext(14, " Jump to message ");
|
|
30
38
|
i0.ɵɵelementEnd();
|
|
31
|
-
i0.ɵɵ
|
|
32
|
-
i0.ɵɵ
|
|
33
|
-
i0.ɵɵelement(16, "i", 3);
|
|
34
|
-
i0.ɵɵtext(17, " Unpin ");
|
|
35
|
-
i0.ɵɵelementEnd()()();
|
|
39
|
+
i0.ɵɵconditionalCreate(15, PinnedMessagesPanelComponent_For_14_Conditional_15_Template, 3, 0, "button", 22);
|
|
40
|
+
i0.ɵɵelementEnd()();
|
|
36
41
|
} if (rf & 2) {
|
|
37
42
|
const message_r2 = ctx.$implicit;
|
|
38
43
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
@@ -43,6 +48,8 @@ function PinnedMessagesPanelComponent_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
|
43
48
|
i0.ɵɵtextInterpolate(ctx_r2.GetRelativeTime(message_r2));
|
|
44
49
|
i0.ɵɵadvance(2);
|
|
45
50
|
i0.ɵɵtextInterpolate(ctx_r2.GetPreviewText(message_r2));
|
|
51
|
+
i0.ɵɵadvance(5);
|
|
52
|
+
i0.ɵɵconditional(ctx_r2.AllowUnpin ? 15 : -1);
|
|
46
53
|
} }
|
|
47
54
|
export class PinnedMessagesPanelComponent {
|
|
48
55
|
/**
|
|
@@ -74,6 +81,8 @@ export class PinnedMessagesPanelComponent {
|
|
|
74
81
|
get isLoading() {
|
|
75
82
|
return this.IsLoading;
|
|
76
83
|
}
|
|
84
|
+
/** When false, pins stay listed and jumpable but Unpin is hidden. */
|
|
85
|
+
AllowUnpin = true;
|
|
77
86
|
Closed = new EventEmitter();
|
|
78
87
|
/**
|
|
79
88
|
* @deprecated Use {@link Closed}.
|
|
@@ -118,6 +127,9 @@ export class PinnedMessagesPanelComponent {
|
|
|
118
127
|
this.JumpRequested.emit(message.ID);
|
|
119
128
|
}
|
|
120
129
|
OnUnpin(message) {
|
|
130
|
+
if (!this.AllowUnpin) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
121
133
|
this.UnpinningIds.add(message.ID);
|
|
122
134
|
// Let the card animate out before the parent removes it from the list
|
|
123
135
|
setTimeout(() => {
|
|
@@ -172,7 +184,7 @@ export class PinnedMessagesPanelComponent {
|
|
|
172
184
|
return 'AI Response';
|
|
173
185
|
}
|
|
174
186
|
static ɵfac = function PinnedMessagesPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PinnedMessagesPanelComponent)(); };
|
|
175
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PinnedMessagesPanelComponent, selectors: [["mj-pinned-messages-panel"]], inputs: { PinnedMessages: "PinnedMessages", pinnedMessages: "pinnedMessages", IsLoading: "IsLoading", isLoading: "isLoading" }, outputs: { Closed: "Closed", closed: "closed", JumpRequested: "JumpRequested", jumpRequested: "jumpRequested", UnpinRequested: "UnpinRequested", unpinRequested: "unpinRequested" }, standalone: false, decls: 15, vars: 2, consts: [[1, "pins-panel"], [1, "pins-header"], [1, "pins-title"], [1, "fas", "fa-thumbtack"], [1, "pins-count"], ["title", "Close", 1, "pins-close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "pins-body"], ["text", "Loading pinned messages\u2026", "size", "small"], ["Size", "compact", "Icon", "fa-solid fa-thumbtack", "Title", "No pinned messages", 1, "pins-empty"], [1, "pin-card", 3, "unpinning"], [1, "pin-card"], [1, "pin-card-meta"], [1, "pin-sender"], [1, "pin-avatar"], [1, "fas", "fa-robot"], [1, "pin-sender-name"], [1, "pin-time"], [1, "pin-preview"], [1, "pin-actions"], ["title", "Jump to message", 1, "pin-action-btn", "jump-btn", 3, "click"], [1, "fas", "fa-arrow-up-right-from-square"], ["title", "Unpin this message", 1, "pin-action-btn", "unpin-btn", 3, "click"]], template: function PinnedMessagesPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
187
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PinnedMessagesPanelComponent, selectors: [["mj-pinned-messages-panel"]], inputs: { PinnedMessages: "PinnedMessages", pinnedMessages: "pinnedMessages", IsLoading: "IsLoading", isLoading: "isLoading", AllowUnpin: "AllowUnpin" }, outputs: { Closed: "Closed", closed: "closed", JumpRequested: "JumpRequested", jumpRequested: "jumpRequested", UnpinRequested: "UnpinRequested", unpinRequested: "unpinRequested" }, standalone: false, decls: 15, vars: 2, consts: [[1, "pins-panel"], [1, "pins-header"], [1, "pins-title"], [1, "fas", "fa-thumbtack"], [1, "pins-count"], ["title", "Close", 1, "pins-close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "pins-body"], ["text", "Loading pinned messages\u2026", "size", "small"], ["Size", "compact", "Icon", "fa-solid fa-thumbtack", "Title", "No pinned messages", 1, "pins-empty"], [1, "pin-card", 3, "unpinning"], [1, "pin-card"], [1, "pin-card-meta"], [1, "pin-sender"], [1, "pin-avatar"], [1, "fas", "fa-robot"], [1, "pin-sender-name"], [1, "pin-time"], [1, "pin-preview"], [1, "pin-actions"], ["title", "Jump to message", 1, "pin-action-btn", "jump-btn", 3, "click"], [1, "fas", "fa-arrow-up-right-from-square"], ["title", "Unpin this message", 1, "pin-action-btn", "unpin-btn"], ["title", "Unpin this message", 1, "pin-action-btn", "unpin-btn", 3, "click"]], template: function PinnedMessagesPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
176
188
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
|
|
177
189
|
i0.ɵɵelement(3, "i", 3);
|
|
178
190
|
i0.ɵɵelementStart(4, "span");
|
|
@@ -187,7 +199,7 @@ export class PinnedMessagesPanelComponent {
|
|
|
187
199
|
i0.ɵɵelementEnd()();
|
|
188
200
|
i0.ɵɵelementStart(10, "div", 7);
|
|
189
201
|
i0.ɵɵconditionalCreate(11, PinnedMessagesPanelComponent_Conditional_11_Template, 1, 0, "mj-loading", 8)(12, PinnedMessagesPanelComponent_Conditional_12_Template, 1, 0, "mj-empty-state", 9);
|
|
190
|
-
i0.ɵɵrepeaterCreate(13, PinnedMessagesPanelComponent_For_14_Template,
|
|
202
|
+
i0.ɵɵrepeaterCreate(13, PinnedMessagesPanelComponent_For_14_Template, 16, 6, "div", 10, _forTrack0);
|
|
191
203
|
i0.ɵɵelementEnd()();
|
|
192
204
|
} if (rf & 2) {
|
|
193
205
|
i0.ɵɵadvance(7);
|
|
@@ -200,7 +212,7 @@ export class PinnedMessagesPanelComponent {
|
|
|
200
212
|
}
|
|
201
213
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PinnedMessagesPanelComponent, [{
|
|
202
214
|
type: Component,
|
|
203
|
-
args: [{ standalone: false, selector: 'mj-pinned-messages-panel', template: "<div class=\"pins-panel\">\n <div class=\"pins-header\">\n <div class=\"pins-title\">\n <i class=\"fas fa-thumbtack\"></i>\n <span>Pinned Messages</span>\n <span class=\"pins-count\">{{ pinnedMessages.length }}</span>\n </div>\n <button class=\"pins-close-btn\" (click)=\"Close()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n\n <div class=\"pins-body\">\n <!-- Rows are fetched on first open now, so \"empty\" and \"not loaded yet\" are different\n states \u2014 showing the empty state during the fetch would claim there are no pins. -->\n @if (isLoading) {\n <mj-loading text=\"Loading pinned messages\u2026\" size=\"small\"></mj-loading>\n } @else if (pinnedMessages.length === 0) {\n <mj-empty-state class=\"pins-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-thumbtack\"\n Title=\"No pinned messages\" />\n }\n\n @for (message of pinnedMessages; track message.ID) {\n <div class=\"pin-card\" [class.unpinning]=\"IsUnpinning(message)\">\n <div class=\"pin-card-meta\">\n <div class=\"pin-sender\">\n <div class=\"pin-avatar\">\n <i class=\"fas fa-robot\"></i>\n </div>\n <span class=\"pin-sender-name\">{{ GetSenderName(message) }}</span>\n </div>\n <span class=\"pin-time\">{{ GetRelativeTime(message) }}</span>\n </div>\n\n <p class=\"pin-preview\">{{ GetPreviewText(message) }}</p>\n\n <div class=\"pin-actions\">\n <button class=\"pin-action-btn jump-btn\" (click)=\"OnJump(message)\" title=\"Jump to message\">\n <i class=\"fas fa-arrow-up-right-from-square\"></i>\n Jump to message\n </button>\n <button class=\"pin-action-btn unpin-btn\" (click)=\"OnUnpin(message)\" title=\"Unpin this message\">\n
|
|
215
|
+
args: [{ standalone: false, selector: 'mj-pinned-messages-panel', template: "<div class=\"pins-panel\">\n <div class=\"pins-header\">\n <div class=\"pins-title\">\n <i class=\"fas fa-thumbtack\"></i>\n <span>Pinned Messages</span>\n <span class=\"pins-count\">{{ pinnedMessages.length }}</span>\n </div>\n <button class=\"pins-close-btn\" (click)=\"Close()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n\n <div class=\"pins-body\">\n <!-- Rows are fetched on first open now, so \"empty\" and \"not loaded yet\" are different\n states \u2014 showing the empty state during the fetch would claim there are no pins. -->\n @if (isLoading) {\n <mj-loading text=\"Loading pinned messages\u2026\" size=\"small\"></mj-loading>\n } @else if (pinnedMessages.length === 0) {\n <mj-empty-state class=\"pins-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-thumbtack\"\n Title=\"No pinned messages\" />\n }\n\n @for (message of pinnedMessages; track message.ID) {\n <div class=\"pin-card\" [class.unpinning]=\"IsUnpinning(message)\">\n <div class=\"pin-card-meta\">\n <div class=\"pin-sender\">\n <div class=\"pin-avatar\">\n <i class=\"fas fa-robot\"></i>\n </div>\n <span class=\"pin-sender-name\">{{ GetSenderName(message) }}</span>\n </div>\n <span class=\"pin-time\">{{ GetRelativeTime(message) }}</span>\n </div>\n\n <p class=\"pin-preview\">{{ GetPreviewText(message) }}</p>\n\n <div class=\"pin-actions\">\n <button class=\"pin-action-btn jump-btn\" (click)=\"OnJump(message)\" title=\"Jump to message\">\n <i class=\"fas fa-arrow-up-right-from-square\"></i>\n Jump to message\n </button>\n @if (AllowUnpin) {\n <button class=\"pin-action-btn unpin-btn\" (click)=\"OnUnpin(message)\" title=\"Unpin this message\">\n <i class=\"fas fa-thumbtack\"></i>\n Unpin\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["/* ============================================================\n Pinned Messages Panel \u2014 overlays the right edge of the chat\n ============================================================ */\n\n:host {\n display: block;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 340px;\n z-index: 50;\n pointer-events: auto;\n animation: slideInPanel 0.22s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes slideInPanel {\n from {\n opacity: 0;\n transform: translateX(24px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.pins-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12);\n}\n\n/* \u2500\u2500 Header \u2500\u2500 */\n.pins-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n background: var(--mj-bg-surface-card);\n}\n\n.pins-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.pins-title > i.fa-thumbtack {\n color: var(--mj-status-warning);\n font-size: 13px;\n}\n\n.pins-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 40%, transparent);\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n font-size: 11px;\n font-weight: 700;\n}\n\n.pins-close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.pins-close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* \u2500\u2500 Body / Scrollable list \u2500\u2500 */\n.pins-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* \u2500\u2500 Empty state \u2500\u2500 */\n.pins-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 12px;\n color: var(--mj-text-muted);\n}\n\n.pins-empty i {\n font-size: 32px;\n opacity: 0.4;\n}\n\n.pins-empty p {\n font-size: 14px;\n margin: 0;\n}\n\n/* \u2500\u2500 Pin Card \u2500\u2500 */\n.pin-card {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-left: 3px solid var(--mj-status-warning);\n transition: opacity 0.2s, transform 0.2s;\n}\n\n.pin-card.unpinning {\n opacity: 0;\n transform: translateX(12px);\n}\n\n.pin-card:hover {\n border-color: var(--mj-border-strong);\n border-left-color: var(--mj-status-warning);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n}\n\n/* Card meta row: avatar+name on left, time on right */\n.pin-card-meta {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.pin-sender {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.pin-avatar {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.pin-avatar i {\n font-size: 10px;\n color: var(--mj-brand-primary);\n}\n\n.pin-sender-name {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.pin-time {\n font-size: 11px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* Preview text */\n.pin-preview {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--mj-text-primary);\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Card action buttons */\n.pin-actions {\n display: flex;\n gap: 6px;\n margin-top: 2px;\n}\n\n.pin-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 10px;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n border: 1px solid var(--mj-border-default);\n}\n\n.pin-action-btn i {\n font-size: 11px;\n}\n\n.jump-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.jump-btn:hover {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n.unpin-btn {\n background: transparent;\n color: var(--mj-text-secondary);\n}\n\n.unpin-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n/* \u2500\u2500 Scrollbar styling \u2500\u2500 */\n.pins-body::-webkit-scrollbar { width: 4px; }\n.pins-body::-webkit-scrollbar-track { background: transparent; }\n.pins-body::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 2px;\n}\n"] }]
|
|
204
216
|
}], null, { PinnedMessages: [{
|
|
205
217
|
type: Input
|
|
206
218
|
}], pinnedMessages: [{
|
|
@@ -209,6 +221,8 @@ export class PinnedMessagesPanelComponent {
|
|
|
209
221
|
type: Input
|
|
210
222
|
}], isLoading: [{
|
|
211
223
|
type: Input
|
|
224
|
+
}], AllowUnpin: [{
|
|
225
|
+
type: Input
|
|
212
226
|
}], Closed: [{
|
|
213
227
|
type: Output
|
|
214
228
|
}], closed: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinned-messages-panel.component.js","sourceRoot":"","sources":["../../../../src/lib/components/conversation/pinned-messages-panel.component.ts","../../../../src/lib/components/conversation/pinned-messages-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { MJConversationDetailEntity } from '@memberjunction/core-entities';\n\n@Component({\n standalone: false,\n selector: 'mj-pinned-messages-panel',\n templateUrl: './pinned-messages-panel.component.html',\n styleUrls: ['./pinned-messages-panel.component.css']\n})\nexport class PinnedMessagesPanelComponent {\n /**\n * Readonly because the chat area hands over the window store's own pin array by reference —\n * a stable reference between real changes is what keeps this input from re-firing every\n * change detection cycle. The panel renders it; it never mutates it.\n */\n @Input() public PinnedMessages: readonly MJConversationDetailEntity[] = [];\n\n /** @deprecated Use {@link PinnedMessages}. */\n @Input() public set pinnedMessages(value: readonly MJConversationDetailEntity[]) {\n this.PinnedMessages = value;\n }\n /** @deprecated Use {@link PinnedMessages}. */\n public get pinnedMessages(): readonly MJConversationDetailEntity[] {\n return this.PinnedMessages;\n }\n\n /**\n * True during the panel's first open, while its rows are being fetched.\n *\n * Pins are no longer loaded during conversation open — the open path reads only a count —\n * so the panel can now render before its contents exist.\n */\n @Input() public IsLoading = false;\n\n /** @deprecated Use {@link IsLoading}. */\n @Input() public set isLoading(value: PinnedMessagesPanelComponent['IsLoading']) {\n this.IsLoading = value;\n }\n /** @deprecated Use {@link IsLoading}. */\n public get isLoading(): PinnedMessagesPanelComponent['IsLoading'] {\n return this.IsLoading;\n }\n\n @Output() public Closed = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link Closed}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (closed) keeps working. Must stay AFTER Closed: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public closed = this.Closed;\n @Output() public JumpRequested = new EventEmitter<string>();\n\n /**\n * @deprecated Use {@link JumpRequested}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (jumpRequested) keeps working. Must stay AFTER JumpRequested: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public jumpRequested = this.JumpRequested; // emits messageId\n @Output() public UnpinRequested = new EventEmitter<MJConversationDetailEntity>();\n\n /**\n * @deprecated Use {@link UnpinRequested}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (unpinRequested) keeps working. Must stay AFTER UnpinRequested: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public unpinRequested = this.UnpinRequested;\n\n /** IDs currently being removed (for fade-out animation) */\n public UnpinningIds = new Set<string>();\n\n /** @deprecated Use {@link UnpinningIds}. */\n public get unpinningIds() {\n return this.UnpinningIds;\n }\n /** @deprecated Use {@link UnpinningIds}. */\n public set unpinningIds(value) {\n this.UnpinningIds = value;\n }\n\n public Close(): void {\n this.Closed.emit();\n }\n\n public OnJump(message: MJConversationDetailEntity): void {\n this.JumpRequested.emit(message.ID);\n }\n\n public OnUnpin(message: MJConversationDetailEntity): void {\n this.UnpinningIds.add(message.ID);\n // Let the card animate out before the parent removes it from the list\n setTimeout(() => {\n this.UnpinningIds.delete(message.ID);\n this.UnpinRequested.emit(message);\n }, 200);\n }\n\n public IsUnpinning(message: MJConversationDetailEntity): boolean {\n return this.UnpinningIds.has(message.ID);\n }\n\n /** Strip markdown to plain prose for card preview */\n public GetPreviewText(message: MJConversationDetailEntity): string {\n const raw = message.Message || '';\n const stripped = raw\n .replace(/```[\\s\\S]*?```/g, '[code]') // fenced code blocks\n .replace(/`[^`]*`/g, '[code]') // inline code\n .replace(/#{1,6}\\s+/g, '') // headings\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // bold\n .replace(/\\*(.+?)\\*/g, '$1') // italic\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // links\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '') // images\n .replace(/>\\s+/g, '') // blockquotes\n .replace(/\\n+/g, ' ') // newlines → spaces\n .trim();\n return stripped.length > 200 ? stripped.substring(0, 197) + '…' : stripped;\n }\n\n /** Human-readable relative timestamp */\n public GetRelativeTime(message: MJConversationDetailEntity): string {\n const created = message.__mj_CreatedAt;\n if (!created) return '';\n const diffMs = Date.now() - new Date(created).getTime();\n const mins = Math.floor(diffMs / 60_000);\n const hours = Math.floor(diffMs / 3_600_000);\n const days = Math.floor(diffMs / 86_400_000);\n if (mins < 1) return 'just now';\n if (mins < 60) return `${mins}m ago`;\n if (hours < 24) return `${hours}h ago`;\n if (days === 1) return 'Yesterday';\n if (days < 7) return `${days} days ago`;\n return new Date(created).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n\n /** Display name for the message sender */\n public GetSenderName(message: MJConversationDetailEntity): string {\n if (message.Role === 'User') return 'You';\n return 'AI Response';\n }\n}\n","<div class=\"pins-panel\">\n <div class=\"pins-header\">\n <div class=\"pins-title\">\n <i class=\"fas fa-thumbtack\"></i>\n <span>Pinned Messages</span>\n <span class=\"pins-count\">{{ pinnedMessages.length }}</span>\n </div>\n <button class=\"pins-close-btn\" (click)=\"Close()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n\n <div class=\"pins-body\">\n <!-- Rows are fetched on first open now, so \"empty\" and \"not loaded yet\" are different\n states — showing the empty state during the fetch would claim there are no pins. -->\n @if (isLoading) {\n <mj-loading text=\"Loading pinned messages…\" size=\"small\"></mj-loading>\n } @else if (pinnedMessages.length === 0) {\n <mj-empty-state class=\"pins-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-thumbtack\"\n Title=\"No pinned messages\" />\n }\n\n @for (message of pinnedMessages; track message.ID) {\n <div class=\"pin-card\" [class.unpinning]=\"IsUnpinning(message)\">\n <div class=\"pin-card-meta\">\n <div class=\"pin-sender\">\n <div class=\"pin-avatar\">\n <i class=\"fas fa-robot\"></i>\n </div>\n <span class=\"pin-sender-name\">{{ GetSenderName(message) }}</span>\n </div>\n <span class=\"pin-time\">{{ GetRelativeTime(message) }}</span>\n </div>\n\n <p class=\"pin-preview\">{{ GetPreviewText(message) }}</p>\n\n <div class=\"pin-actions\">\n <button class=\"pin-action-btn jump-btn\" (click)=\"OnJump(message)\" title=\"Jump to message\">\n <i class=\"fas fa-arrow-up-right-from-square\"></i>\n Jump to message\n </button>\n <button class=\"pin-action-btn unpin-btn\" (click)=\"OnUnpin(message)\" title=\"Unpin this message\">\n <i class=\"fas fa-thumbtack\"></i>\n Unpin\n </button>\n </div>\n </div>\n }\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"pinned-messages-panel.component.js","sourceRoot":"","sources":["../../../../src/lib/components/conversation/pinned-messages-panel.component.ts","../../../../src/lib/components/conversation/pinned-messages-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { MJConversationDetailEntity } from '@memberjunction/core-entities';\n\n@Component({\n standalone: false,\n selector: 'mj-pinned-messages-panel',\n templateUrl: './pinned-messages-panel.component.html',\n styleUrls: ['./pinned-messages-panel.component.css']\n})\nexport class PinnedMessagesPanelComponent {\n /**\n * Readonly because the chat area hands over the window store's own pin array by reference —\n * a stable reference between real changes is what keeps this input from re-firing every\n * change detection cycle. The panel renders it; it never mutates it.\n */\n @Input() public PinnedMessages: readonly MJConversationDetailEntity[] = [];\n\n /** @deprecated Use {@link PinnedMessages}. */\n @Input() public set pinnedMessages(value: readonly MJConversationDetailEntity[]) {\n this.PinnedMessages = value;\n }\n /** @deprecated Use {@link PinnedMessages}. */\n public get pinnedMessages(): readonly MJConversationDetailEntity[] {\n return this.PinnedMessages;\n }\n\n /**\n * True during the panel's first open, while its rows are being fetched.\n *\n * Pins are no longer loaded during conversation open — the open path reads only a count —\n * so the panel can now render before its contents exist.\n */\n @Input() public IsLoading = false;\n\n /** @deprecated Use {@link IsLoading}. */\n @Input() public set isLoading(value: PinnedMessagesPanelComponent['IsLoading']) {\n this.IsLoading = value;\n }\n /** @deprecated Use {@link IsLoading}. */\n public get isLoading(): PinnedMessagesPanelComponent['IsLoading'] {\n return this.IsLoading;\n }\n\n /** When false, pins stay listed and jumpable but Unpin is hidden. */\n @Input() public AllowUnpin = true;\n\n @Output() public Closed = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link Closed}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (closed) keeps working. Must stay AFTER Closed: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public closed = this.Closed;\n @Output() public JumpRequested = new EventEmitter<string>();\n\n /**\n * @deprecated Use {@link JumpRequested}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (jumpRequested) keeps working. Must stay AFTER JumpRequested: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public jumpRequested = this.JumpRequested; // emits messageId\n @Output() public UnpinRequested = new EventEmitter<MJConversationDetailEntity>();\n\n /**\n * @deprecated Use {@link UnpinRequested}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (unpinRequested) keeps working. Must stay AFTER UnpinRequested: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public unpinRequested = this.UnpinRequested;\n\n /** IDs currently being removed (for fade-out animation) */\n public UnpinningIds = new Set<string>();\n\n /** @deprecated Use {@link UnpinningIds}. */\n public get unpinningIds() {\n return this.UnpinningIds;\n }\n /** @deprecated Use {@link UnpinningIds}. */\n public set unpinningIds(value) {\n this.UnpinningIds = value;\n }\n\n public Close(): void {\n this.Closed.emit();\n }\n\n public OnJump(message: MJConversationDetailEntity): void {\n this.JumpRequested.emit(message.ID);\n }\n\n public OnUnpin(message: MJConversationDetailEntity): void {\n if (!this.AllowUnpin) {\n return;\n }\n this.UnpinningIds.add(message.ID);\n // Let the card animate out before the parent removes it from the list\n setTimeout(() => {\n this.UnpinningIds.delete(message.ID);\n this.UnpinRequested.emit(message);\n }, 200);\n }\n\n public IsUnpinning(message: MJConversationDetailEntity): boolean {\n return this.UnpinningIds.has(message.ID);\n }\n\n /** Strip markdown to plain prose for card preview */\n public GetPreviewText(message: MJConversationDetailEntity): string {\n const raw = message.Message || '';\n const stripped = raw\n .replace(/```[\\s\\S]*?```/g, '[code]') // fenced code blocks\n .replace(/`[^`]*`/g, '[code]') // inline code\n .replace(/#{1,6}\\s+/g, '') // headings\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // bold\n .replace(/\\*(.+?)\\*/g, '$1') // italic\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // links\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '') // images\n .replace(/>\\s+/g, '') // blockquotes\n .replace(/\\n+/g, ' ') // newlines → spaces\n .trim();\n return stripped.length > 200 ? stripped.substring(0, 197) + '…' : stripped;\n }\n\n /** Human-readable relative timestamp */\n public GetRelativeTime(message: MJConversationDetailEntity): string {\n const created = message.__mj_CreatedAt;\n if (!created) return '';\n const diffMs = Date.now() - new Date(created).getTime();\n const mins = Math.floor(diffMs / 60_000);\n const hours = Math.floor(diffMs / 3_600_000);\n const days = Math.floor(diffMs / 86_400_000);\n if (mins < 1) return 'just now';\n if (mins < 60) return `${mins}m ago`;\n if (hours < 24) return `${hours}h ago`;\n if (days === 1) return 'Yesterday';\n if (days < 7) return `${days} days ago`;\n return new Date(created).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n\n /** Display name for the message sender */\n public GetSenderName(message: MJConversationDetailEntity): string {\n if (message.Role === 'User') return 'You';\n return 'AI Response';\n }\n}\n","<div class=\"pins-panel\">\n <div class=\"pins-header\">\n <div class=\"pins-title\">\n <i class=\"fas fa-thumbtack\"></i>\n <span>Pinned Messages</span>\n <span class=\"pins-count\">{{ pinnedMessages.length }}</span>\n </div>\n <button class=\"pins-close-btn\" (click)=\"Close()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n\n <div class=\"pins-body\">\n <!-- Rows are fetched on first open now, so \"empty\" and \"not loaded yet\" are different\n states — showing the empty state during the fetch would claim there are no pins. -->\n @if (isLoading) {\n <mj-loading text=\"Loading pinned messages…\" size=\"small\"></mj-loading>\n } @else if (pinnedMessages.length === 0) {\n <mj-empty-state class=\"pins-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-thumbtack\"\n Title=\"No pinned messages\" />\n }\n\n @for (message of pinnedMessages; track message.ID) {\n <div class=\"pin-card\" [class.unpinning]=\"IsUnpinning(message)\">\n <div class=\"pin-card-meta\">\n <div class=\"pin-sender\">\n <div class=\"pin-avatar\">\n <i class=\"fas fa-robot\"></i>\n </div>\n <span class=\"pin-sender-name\">{{ GetSenderName(message) }}</span>\n </div>\n <span class=\"pin-time\">{{ GetRelativeTime(message) }}</span>\n </div>\n\n <p class=\"pin-preview\">{{ GetPreviewText(message) }}</p>\n\n <div class=\"pin-actions\">\n <button class=\"pin-action-btn jump-btn\" (click)=\"OnJump(message)\" title=\"Jump to message\">\n <i class=\"fas fa-arrow-up-right-from-square\"></i>\n Jump to message\n </button>\n @if (AllowUnpin) {\n <button class=\"pin-action-btn unpin-btn\" (click)=\"OnUnpin(message)\" title=\"Unpin this message\">\n <i class=\"fas fa-thumbtack\"></i>\n Unpin\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n"]}
|
|
@@ -47,6 +47,18 @@ export declare class ActionableCommandsComponent {
|
|
|
47
47
|
OnCommandClick(command: ActionableCommand): void;
|
|
48
48
|
/** @deprecated Use {@link OnCommandClick}. */
|
|
49
49
|
onCommandClick(command: ActionableCommand): void;
|
|
50
|
+
/**
|
|
51
|
+
* Recipients to show BESIDE a compose:email button, or null for any other command.
|
|
52
|
+
*
|
|
53
|
+
* SECURITY, not decoration. The button renders only the agent-authored `label`, so without this
|
|
54
|
+
* the user cannot see who a draft is addressed to until their mail client is already open and
|
|
55
|
+
* populated. An agent influenced by injected content in retrieved material could pair a benign
|
|
56
|
+
* label ("Open draft in Mail") with an attacker's address and the conversation's context as the
|
|
57
|
+
* body; showing the address is what lets the user notice before they send from their real
|
|
58
|
+
* mailbox. Empty recipients read as "no recipient" rather than rendering nothing, so an address
|
|
59
|
+
* the agent never supplied cannot be mistaken for one it did.
|
|
60
|
+
*/
|
|
61
|
+
ComposeEmailRecipients(command: ActionableCommand): string | null;
|
|
50
62
|
/**
|
|
51
63
|
* Get button variant based on command type
|
|
52
64
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionable-commands.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/actionable-commands.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;;AAEjE;;;GAGG;AACH,qBAMa,2BAA2B;IAC7B,QAAQ,EAAE,iBAAiB,EAAE,CAAM;IAE5C,wCAAwC;IACxC,IAAa,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAE/C;IACD,wCAAwC;IACxC,IAAI,QAAQ,IAAI,iBAAiB,EAAE,CAElC;IACQ,QAAQ,EAAE,OAAO,CAAS;IAEnC,wCAAwC;IACxC,IAAa,QAAQ,CAAC,KAAK,EAAE,OAAO,EAEnC;IACD,wCAAwC;IACxC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACQ,aAAa,EAAE,OAAO,CAAS;IAExC,6CAA6C;IAC7C,IAAa,aAAa,CAAC,KAAK,EAAE,OAAO,EAExC;IACD,6CAA6C;IAC7C,IAAI,aAAa,IAAI,OAAO,CAE3B;IACQ,mBAAmB,EAAE,OAAO,CAAS;IAE9C,mDAAmD;IACnD,IAAa,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAE9C;IACD,mDAAmD;IACnD,IAAI,mBAAmB,IAAI,OAAO,CAEjC;IAES,eAAe,kCAAyC;IAElE;;;;;;OAMG;IACO,eAAe,kCAAwB;IAEjD;;OAEG;IACH,IAAW,SAAS,IAAI,OAAO,CAO9B;IAED,yCAAyC;IACzC,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;OAEG;IACI,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAMvD,8CAA8C;IACvC,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIvD;;OAEG;IACI,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"actionable-commands.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/actionable-commands.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;;AAEjE;;;GAGG;AACH,qBAMa,2BAA2B;IAC7B,QAAQ,EAAE,iBAAiB,EAAE,CAAM;IAE5C,wCAAwC;IACxC,IAAa,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAE/C;IACD,wCAAwC;IACxC,IAAI,QAAQ,IAAI,iBAAiB,EAAE,CAElC;IACQ,QAAQ,EAAE,OAAO,CAAS;IAEnC,wCAAwC;IACxC,IAAa,QAAQ,CAAC,KAAK,EAAE,OAAO,EAEnC;IACD,wCAAwC;IACxC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACQ,aAAa,EAAE,OAAO,CAAS;IAExC,6CAA6C;IAC7C,IAAa,aAAa,CAAC,KAAK,EAAE,OAAO,EAExC;IACD,6CAA6C;IAC7C,IAAI,aAAa,IAAI,OAAO,CAE3B;IACQ,mBAAmB,EAAE,OAAO,CAAS;IAE9C,mDAAmD;IACnD,IAAa,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAE9C;IACD,mDAAmD;IACnD,IAAI,mBAAmB,IAAI,OAAO,CAEjC;IAES,eAAe,kCAAyC;IAElE;;;;;;OAMG;IACO,eAAe,kCAAwB;IAEjD;;OAEG;IACH,IAAW,SAAS,IAAI,OAAO,CAO9B;IAED,yCAAyC;IACzC,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;OAEG;IACI,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAMvD,8CAA8C;IACvC,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIvD;;;;;;;;;;OAUG;IACI,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI;IAiBxE;;OAEG;IACI,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM;IAUjG,gDAAgD;IACzC,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM;IAIjG;;OAEG;IACI,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM;IAIxE,8CAA8C;IACvC,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM;yCA1I7D,2BAA2B;2CAA3B,2BAA2B;CA6IvC"}
|
|
@@ -1,34 +1,51 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
import * as i1 from "@memberjunction/ng-ui-components";
|
|
4
|
-
function
|
|
5
|
-
i0.ɵɵelement(0, "i",
|
|
4
|
+
function ActionableCommandsComponent_Conditional_0_For_5_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
5
|
+
i0.ɵɵelement(0, "i", 7);
|
|
6
6
|
} if (rf & 2) {
|
|
7
7
|
const command_r2 = i0.ɵɵnextContext().$implicit;
|
|
8
8
|
i0.ɵɵclassMap(i0.ɵɵinterpolate1("fa ", command_r2.icon));
|
|
9
9
|
} }
|
|
10
|
+
function ActionableCommandsComponent_Conditional_0_For_5_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
11
|
+
i0.ɵɵelementStart(0, "div", 6);
|
|
12
|
+
i0.ɵɵtext(1);
|
|
13
|
+
i0.ɵɵelementEnd();
|
|
14
|
+
} if (rf & 2) {
|
|
15
|
+
i0.ɵɵnextContext();
|
|
16
|
+
const recipients_r4 = i0.ɵɵreadContextLet(0);
|
|
17
|
+
i0.ɵɵadvance();
|
|
18
|
+
i0.ɵɵtextInterpolate(recipients_r4);
|
|
19
|
+
} }
|
|
10
20
|
function ActionableCommandsComponent_Conditional_0_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
11
21
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
12
|
-
i0.ɵɵ
|
|
13
|
-
i0.ɵɵ
|
|
14
|
-
i0.ɵɵ
|
|
15
|
-
i0.ɵɵ
|
|
22
|
+
i0.ɵɵdeclareLet(0);
|
|
23
|
+
i0.ɵɵelementStart(1, "div", 3)(2, "button", 4);
|
|
24
|
+
i0.ɵɵlistener("click", function ActionableCommandsComponent_Conditional_0_For_5_Template_button_click_2_listener() { const command_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCommandClick(command_r2)); });
|
|
25
|
+
i0.ɵɵconditionalCreate(3, ActionableCommandsComponent_Conditional_0_For_5_Conditional_3_Template, 1, 3, "i", 5);
|
|
26
|
+
i0.ɵɵtext(4);
|
|
27
|
+
i0.ɵɵelementEnd();
|
|
28
|
+
i0.ɵɵconditionalCreate(5, ActionableCommandsComponent_Conditional_0_For_5_Conditional_5_Template, 2, 1, "div", 6);
|
|
16
29
|
i0.ɵɵelementEnd();
|
|
17
30
|
} if (rf & 2) {
|
|
18
31
|
const command_r2 = ctx.$implicit;
|
|
19
32
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
33
|
+
const recipients_r5 = i0.ɵɵstoreLet(ctx_r2.ComposeEmailRecipients(command_r2));
|
|
34
|
+
i0.ɵɵadvance(2);
|
|
20
35
|
i0.ɵɵproperty("variant", ctx_r2.getButtonVariant(command_r2))("disabled", ctx_r2.disabled);
|
|
21
36
|
i0.ɵɵadvance();
|
|
22
|
-
i0.ɵɵconditional(command_r2.icon ?
|
|
37
|
+
i0.ɵɵconditional(command_r2.icon ? 3 : -1);
|
|
23
38
|
i0.ɵɵadvance();
|
|
24
39
|
i0.ɵɵtextInterpolate1(" ", command_r2.label, " ");
|
|
40
|
+
i0.ɵɵadvance();
|
|
41
|
+
i0.ɵɵconditional(recipients_r5 ? 5 : -1);
|
|
25
42
|
} }
|
|
26
43
|
function ActionableCommandsComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
27
44
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1);
|
|
28
45
|
i0.ɵɵtext(2, "Actions:");
|
|
29
46
|
i0.ɵɵelementEnd();
|
|
30
47
|
i0.ɵɵelementStart(3, "div", 2);
|
|
31
|
-
i0.ɵɵrepeaterCreate(4, ActionableCommandsComponent_Conditional_0_For_5_Template,
|
|
48
|
+
i0.ɵɵrepeaterCreate(4, ActionableCommandsComponent_Conditional_0_For_5_Template, 6, 6, "div", 3, i0.ɵɵcomponentInstance().trackByCommand, true);
|
|
32
49
|
i0.ɵɵelementEnd()();
|
|
33
50
|
} if (rf & 2) {
|
|
34
51
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
@@ -110,11 +127,42 @@ export class ActionableCommandsComponent {
|
|
|
110
127
|
onCommandClick(command) {
|
|
111
128
|
return this.OnCommandClick(command);
|
|
112
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Recipients to show BESIDE a compose:email button, or null for any other command.
|
|
132
|
+
*
|
|
133
|
+
* SECURITY, not decoration. The button renders only the agent-authored `label`, so without this
|
|
134
|
+
* the user cannot see who a draft is addressed to until their mail client is already open and
|
|
135
|
+
* populated. An agent influenced by injected content in retrieved material could pair a benign
|
|
136
|
+
* label ("Open draft in Mail") with an attacker's address and the conversation's context as the
|
|
137
|
+
* body; showing the address is what lets the user notice before they send from their real
|
|
138
|
+
* mailbox. Empty recipients read as "no recipient" rather than rendering nothing, so an address
|
|
139
|
+
* the agent never supplied cannot be mistaken for one it did.
|
|
140
|
+
*/
|
|
141
|
+
ComposeEmailRecipients(command) {
|
|
142
|
+
if (command.type !== 'compose:email') {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const to = (command.to ?? []).map((r) => r.trim()).filter((r) => r.length > 0);
|
|
146
|
+
const cc = (command.cc ?? []).map((r) => r.trim()).filter((r) => r.length > 0);
|
|
147
|
+
const bcc = (command.bcc ?? []).map((r) => r.trim()).filter((r) => r.length > 0);
|
|
148
|
+
if (to.length === 0 && cc.length === 0 && bcc.length === 0) {
|
|
149
|
+
return 'no recipient — you\'ll add one';
|
|
150
|
+
}
|
|
151
|
+
const parts = [];
|
|
152
|
+
if (to.length > 0)
|
|
153
|
+
parts.push(`To ${to.join(', ')}`);
|
|
154
|
+
if (cc.length > 0)
|
|
155
|
+
parts.push(`Cc ${cc.join(', ')}`);
|
|
156
|
+
if (bcc.length > 0)
|
|
157
|
+
parts.push(`Bcc ${bcc.join(', ')}`);
|
|
158
|
+
return parts.join(' · ');
|
|
159
|
+
}
|
|
113
160
|
/**
|
|
114
161
|
* Get button variant based on command type
|
|
115
162
|
*/
|
|
116
163
|
GetButtonVariant(command) {
|
|
117
|
-
|
|
164
|
+
// A drafted email leads the turn the same way an opened resource does.
|
|
165
|
+
if (command.type === 'open:resource' || command.type === 'compose:email') {
|
|
118
166
|
return 'primary';
|
|
119
167
|
}
|
|
120
168
|
else if (command.type === 'open:url') {
|
|
@@ -137,15 +185,15 @@ export class ActionableCommandsComponent {
|
|
|
137
185
|
return this.TrackByCommand(index, command);
|
|
138
186
|
}
|
|
139
187
|
static ɵfac = function ActionableCommandsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ActionableCommandsComponent)(); };
|
|
140
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ActionableCommandsComponent, selectors: [["mj-actionable-commands"]], inputs: { Commands: "Commands", commands: "commands", Disabled: "Disabled", disabled: "disabled", IsLastMessage: "IsLastMessage", isLastMessage: "isLastMessage", IsConversationOwner: "IsConversationOwner", isConversationOwner: "isConversationOwner" }, outputs: { CommandExecuted: "CommandExecuted", commandExecuted: "commandExecuted" }, standalone: false, decls: 1, vars: 1, consts: [[1, "actionable-commands"], [1, "commands-label"], [1, "commands-buttons"], [
|
|
188
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ActionableCommandsComponent, selectors: [["mj-actionable-commands"]], inputs: { Commands: "Commands", commands: "commands", Disabled: "Disabled", disabled: "disabled", IsLastMessage: "IsLastMessage", isLastMessage: "isLastMessage", IsConversationOwner: "IsConversationOwner", isConversationOwner: "isConversationOwner" }, outputs: { CommandExecuted: "CommandExecuted", commandExecuted: "commandExecuted" }, standalone: false, decls: 1, vars: 1, consts: [[1, "actionable-commands"], [1, "commands-label"], [1, "commands-buttons"], [1, "command-item"], ["mjButton", "", 1, "command-button", 3, "click", "variant", "disabled"], ["aria-hidden", "true", 3, "class"], [1, "command-recipients"], ["aria-hidden", "true"]], template: function ActionableCommandsComponent_Template(rf, ctx) { if (rf & 1) {
|
|
141
189
|
i0.ɵɵconditionalCreate(0, ActionableCommandsComponent_Conditional_0_Template, 6, 0, "div", 0);
|
|
142
190
|
} if (rf & 2) {
|
|
143
191
|
i0.ɵɵconditional(ctx.isVisible ? 0 : -1);
|
|
144
|
-
} }, dependencies: [i1.MJButtonDirective], styles: [".actionable-commands[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border-left: 3px solid var(--mj-brand-primary);\n border-radius: 4px;\n}\n\n.commands-label[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.commands-buttons[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.command-button[_ngcontent-%COMP%] {\n padding: 8px 16px;\n font-size: 14px;\n}\n\n.command-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 6px;\n}\n\n\n@media (max-width: 768px) {\n .commands-buttons[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .command-button[_ngcontent-%COMP%] {\n width: 100%;\n }\n}"] });
|
|
192
|
+
} }, dependencies: [i1.MJButtonDirective], styles: [".actionable-commands[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border-left: 3px solid var(--mj-brand-primary);\n border-radius: 4px;\n}\n\n.commands-label[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.commands-buttons[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.command-button[_ngcontent-%COMP%] {\n padding: 8px 16px;\n font-size: 14px;\n}\n\n.command-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 6px;\n}\n\n\n@media (max-width: 768px) {\n .commands-buttons[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .command-button[_ngcontent-%COMP%] {\n width: 100%;\n }\n}\n\n\n\n.command-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n\n\n.command-recipients[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted, #6b6b6b);\n max-width: 320px;\n white-space: normal;\n overflow-wrap: anywhere;\n}"] });
|
|
145
193
|
}
|
|
146
194
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ActionableCommandsComponent, [{
|
|
147
195
|
type: Component,
|
|
148
|
-
args: [{ standalone: false, selector: 'mj-actionable-commands', template: "@if (isVisible) {\n <div class=\"actionable-commands\">\n <div class=\"commands-label\">Actions:</div>\n <div class=\"commands-buttons\">\n @for (command of commands; track trackByCommand($index, command)) {\n <button mjButton\n
|
|
196
|
+
args: [{ standalone: false, selector: 'mj-actionable-commands', template: "@if (isVisible) {\n <div class=\"actionable-commands\">\n <div class=\"commands-label\">Actions:</div>\n <div class=\"commands-buttons\">\n @for (command of commands; track trackByCommand($index, command)) {\n @let recipients = ComposeEmailRecipients(command);\n <div class=\"command-item\">\n <button mjButton\n [variant]=\"getButtonVariant(command)\"\n [disabled]=\"disabled\"\n (click)=\"onCommandClick(command)\"\n class=\"command-button\"\n >\n @if (command.icon) {\n <i class=\"fa {{ command.icon }}\" aria-hidden=\"true\"></i>\n }\n {{ command.label }}\n </button>\n @if (recipients) {\n <!-- Shown so the user can see WHO a draft is addressed to before their mail client\n opens already populated \u2014 see ComposeEmailRecipients(). -->\n <div class=\"command-recipients\">{{ recipients }}</div>\n }\n </div>\n }\n </div>\n </div>\n}\n", styles: [".actionable-commands {\n margin-top: 16px;\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border-left: 3px solid var(--mj-brand-primary);\n border-radius: 4px;\n}\n\n.commands-label {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.commands-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.command-button {\n padding: 8px 16px;\n font-size: 14px;\n}\n\n.command-button i {\n margin-right: 6px;\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .commands-buttons {\n flex-direction: column;\n }\n\n .command-button {\n width: 100%;\n }\n}\n\n/* compose:email shows its recipients under the button so the address is visible\n before the mail client opens. */\n.command-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n/* Wraps rather than truncating: Bcc is listed LAST, so an ellipsis would cut off exactly the\n address this line exists to expose. overflow-wrap breaks a long address that has no spaces. */\n.command-recipients {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted, #6b6b6b);\n max-width: 320px;\n white-space: normal;\n overflow-wrap: anywhere;\n}\n"] }]
|
|
149
197
|
}], null, { Commands: [{
|
|
150
198
|
type: Input
|
|
151
199
|
}], commands: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionable-commands.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/actionable-commands.component.ts","../../../../src/lib/components/message/actionable-commands.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { ActionableCommand } from '@memberjunction/ai-core-plus';\n\n/**\n * Component for displaying actionable command buttons\n * These are actions the user can trigger after an agent completes a task\n */\n@Component({\n standalone: false,\n selector: 'mj-actionable-commands',\n templateUrl: './actionable-commands.component.html',\n styleUrls: ['./actionable-commands.component.css']\n})\nexport class ActionableCommandsComponent {\n @Input() Commands: ActionableCommand[] = [];\n\n /** @deprecated Use {@link Commands}. */\n @Input() set commands(value: ActionableCommand[]) {\n this.Commands = value;\n }\n /** @deprecated Use {@link Commands}. */\n get commands(): ActionableCommand[] {\n return this.Commands;\n }\n @Input() Disabled: boolean = false;\n\n /** @deprecated Use {@link Disabled}. */\n @Input() set disabled(value: boolean) {\n this.Disabled = value;\n }\n /** @deprecated Use {@link Disabled}. */\n get disabled(): boolean {\n return this.Disabled;\n }\n @Input() IsLastMessage: boolean = false;\n\n /** @deprecated Use {@link IsLastMessage}. */\n @Input() set isLastMessage(value: boolean) {\n this.IsLastMessage = value;\n }\n /** @deprecated Use {@link IsLastMessage}. */\n get isLastMessage(): boolean {\n return this.IsLastMessage;\n }\n @Input() IsConversationOwner: boolean = false;\n\n /** @deprecated Use {@link IsConversationOwner}. */\n @Input() set isConversationOwner(value: boolean) {\n this.IsConversationOwner = value;\n }\n /** @deprecated Use {@link IsConversationOwner}. */\n get isConversationOwner(): boolean {\n return this.IsConversationOwner;\n }\n\n @Output() CommandExecuted = new EventEmitter<ActionableCommand>();\n\n /**\n * @deprecated Use {@link CommandExecuted}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (commandExecuted) keeps working. Must stay AFTER CommandExecuted: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() commandExecuted = this.CommandExecuted;\n\n /**\n * Check if component should be visible\n */\n public get IsVisible(): boolean {\n return (\n this.IsLastMessage &&\n this.IsConversationOwner &&\n this.Commands &&\n this.Commands.length > 0\n );\n }\n\n /** @deprecated Use {@link IsVisible}. */\n public get isVisible(): boolean {\n return this.IsVisible;\n }\n\n /**\n * Handle command button click\n */\n public OnCommandClick(command: ActionableCommand): void {\n if (!this.Disabled) {\n this.CommandExecuted.emit(command);\n }\n }\n\n /** @deprecated Use {@link OnCommandClick}. */\n public onCommandClick(command: ActionableCommand): void {\n return this.OnCommandClick(command);\n }\n\n /**\n * Get button variant based on command type\n */\n public GetButtonVariant(command: ActionableCommand): 'primary' | 'secondary' | 'outline' | 'flat' {\n if (command.type === 'open:resource') {\n return 'primary';\n } else if (command.type === 'open:url') {\n return 'outline';\n }\n return 'secondary';\n }\n\n /** @deprecated Use {@link GetButtonVariant}. */\n public getButtonVariant(command: ActionableCommand): 'primary' | 'secondary' | 'outline' | 'flat' {\n return this.GetButtonVariant(command);\n }\n\n /**\n * Track by function for commands list\n */\n public TrackByCommand(index: number, command: ActionableCommand): string {\n return `${command.type}_${index}`;\n }\n\n /** @deprecated Use {@link TrackByCommand}. */\n public trackByCommand(index: number, command: ActionableCommand): string {\n return this.TrackByCommand(index, command);\n }\n}\n","@if (isVisible) {\n <div class=\"actionable-commands\">\n <div class=\"commands-label\">Actions:</div>\n <div class=\"commands-buttons\">\n @for (command of commands; track trackByCommand($index, command)) {\n <button mjButton\n [variant]=\"getButtonVariant(command)\"\n [disabled]=\"disabled\"\n (click)=\"onCommandClick(command)\"\n class=\"command-button\"\n >\n @if (command.icon) {\n <i class=\"fa {{ command.icon }}\" aria-hidden=\"true\"></i>\n }\n {{ command.label }}\n </button>\n }\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"actionable-commands.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/actionable-commands.component.ts","../../../../src/lib/components/message/actionable-commands.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { ActionableCommand } from '@memberjunction/ai-core-plus';\n\n/**\n * Component for displaying actionable command buttons\n * These are actions the user can trigger after an agent completes a task\n */\n@Component({\n standalone: false,\n selector: 'mj-actionable-commands',\n templateUrl: './actionable-commands.component.html',\n styleUrls: ['./actionable-commands.component.css']\n})\nexport class ActionableCommandsComponent {\n @Input() Commands: ActionableCommand[] = [];\n\n /** @deprecated Use {@link Commands}. */\n @Input() set commands(value: ActionableCommand[]) {\n this.Commands = value;\n }\n /** @deprecated Use {@link Commands}. */\n get commands(): ActionableCommand[] {\n return this.Commands;\n }\n @Input() Disabled: boolean = false;\n\n /** @deprecated Use {@link Disabled}. */\n @Input() set disabled(value: boolean) {\n this.Disabled = value;\n }\n /** @deprecated Use {@link Disabled}. */\n get disabled(): boolean {\n return this.Disabled;\n }\n @Input() IsLastMessage: boolean = false;\n\n /** @deprecated Use {@link IsLastMessage}. */\n @Input() set isLastMessage(value: boolean) {\n this.IsLastMessage = value;\n }\n /** @deprecated Use {@link IsLastMessage}. */\n get isLastMessage(): boolean {\n return this.IsLastMessage;\n }\n @Input() IsConversationOwner: boolean = false;\n\n /** @deprecated Use {@link IsConversationOwner}. */\n @Input() set isConversationOwner(value: boolean) {\n this.IsConversationOwner = value;\n }\n /** @deprecated Use {@link IsConversationOwner}. */\n get isConversationOwner(): boolean {\n return this.IsConversationOwner;\n }\n\n @Output() CommandExecuted = new EventEmitter<ActionableCommand>();\n\n /**\n * @deprecated Use {@link CommandExecuted}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (commandExecuted) keeps working. Must stay AFTER CommandExecuted: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() commandExecuted = this.CommandExecuted;\n\n /**\n * Check if component should be visible\n */\n public get IsVisible(): boolean {\n return (\n this.IsLastMessage &&\n this.IsConversationOwner &&\n this.Commands &&\n this.Commands.length > 0\n );\n }\n\n /** @deprecated Use {@link IsVisible}. */\n public get isVisible(): boolean {\n return this.IsVisible;\n }\n\n /**\n * Handle command button click\n */\n public OnCommandClick(command: ActionableCommand): void {\n if (!this.Disabled) {\n this.CommandExecuted.emit(command);\n }\n }\n\n /** @deprecated Use {@link OnCommandClick}. */\n public onCommandClick(command: ActionableCommand): void {\n return this.OnCommandClick(command);\n }\n\n /**\n * Recipients to show BESIDE a compose:email button, or null for any other command.\n *\n * SECURITY, not decoration. The button renders only the agent-authored `label`, so without this\n * the user cannot see who a draft is addressed to until their mail client is already open and\n * populated. An agent influenced by injected content in retrieved material could pair a benign\n * label (\"Open draft in Mail\") with an attacker's address and the conversation's context as the\n * body; showing the address is what lets the user notice before they send from their real\n * mailbox. Empty recipients read as \"no recipient\" rather than rendering nothing, so an address\n * the agent never supplied cannot be mistaken for one it did.\n */\n public ComposeEmailRecipients(command: ActionableCommand): string | null {\n if (command.type !== 'compose:email') {\n return null;\n }\n const to = (command.to ?? []).map((r) => r.trim()).filter((r) => r.length > 0);\n const cc = (command.cc ?? []).map((r) => r.trim()).filter((r) => r.length > 0);\n const bcc = (command.bcc ?? []).map((r) => r.trim()).filter((r) => r.length > 0);\n if (to.length === 0 && cc.length === 0 && bcc.length === 0) {\n return 'no recipient — you\\'ll add one';\n }\n const parts: string[] = [];\n if (to.length > 0) parts.push(`To ${to.join(', ')}`);\n if (cc.length > 0) parts.push(`Cc ${cc.join(', ')}`);\n if (bcc.length > 0) parts.push(`Bcc ${bcc.join(', ')}`);\n return parts.join(' · ');\n }\n\n /**\n * Get button variant based on command type\n */\n public GetButtonVariant(command: ActionableCommand): 'primary' | 'secondary' | 'outline' | 'flat' {\n // A drafted email leads the turn the same way an opened resource does.\n if (command.type === 'open:resource' || command.type === 'compose:email') {\n return 'primary';\n } else if (command.type === 'open:url') {\n return 'outline';\n }\n return 'secondary';\n }\n\n /** @deprecated Use {@link GetButtonVariant}. */\n public getButtonVariant(command: ActionableCommand): 'primary' | 'secondary' | 'outline' | 'flat' {\n return this.GetButtonVariant(command);\n }\n\n /**\n * Track by function for commands list\n */\n public TrackByCommand(index: number, command: ActionableCommand): string {\n return `${command.type}_${index}`;\n }\n\n /** @deprecated Use {@link TrackByCommand}. */\n public trackByCommand(index: number, command: ActionableCommand): string {\n return this.TrackByCommand(index, command);\n }\n}\n","@if (isVisible) {\n <div class=\"actionable-commands\">\n <div class=\"commands-label\">Actions:</div>\n <div class=\"commands-buttons\">\n @for (command of commands; track trackByCommand($index, command)) {\n @let recipients = ComposeEmailRecipients(command);\n <div class=\"command-item\">\n <button mjButton\n [variant]=\"getButtonVariant(command)\"\n [disabled]=\"disabled\"\n (click)=\"onCommandClick(command)\"\n class=\"command-button\"\n >\n @if (command.icon) {\n <i class=\"fa {{ command.icon }}\" aria-hidden=\"true\"></i>\n }\n {{ command.label }}\n </button>\n @if (recipients) {\n <!-- Shown so the user can see WHO a draft is addressed to before their mail client\n opens already populated — see ComposeEmailRecipients(). -->\n <div class=\"command-recipients\">{{ recipients }}</div>\n }\n </div>\n }\n </div>\n </div>\n}\n"]}
|