@memberjunction/ng-conversations 2.128.0 → 2.130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/lib/components/attachment/image-viewer.component.d.ts +95 -0
  2. package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -0
  3. package/dist/lib/components/attachment/image-viewer.component.js +293 -0
  4. package/dist/lib/components/attachment/image-viewer.component.js.map +1 -0
  5. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +88 -26
  6. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  7. package/dist/lib/components/conversation/conversation-chat-area.component.js +542 -338
  8. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  9. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +12 -3
  10. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
  11. package/dist/lib/components/conversation/conversation-empty-state.component.js +68 -55
  12. package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
  13. package/dist/lib/components/conversation/conversation-list.component.d.ts +13 -1
  14. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  15. package/dist/lib/components/conversation/conversation-list.component.js +237 -119
  16. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  17. package/dist/lib/components/mention/mention-editor.component.d.ts +102 -5
  18. package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
  19. package/dist/lib/components/mention/mention-editor.component.js +349 -21
  20. package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
  21. package/dist/lib/components/message/agent-response-form.component.d.ts +18 -0
  22. package/dist/lib/components/message/agent-response-form.component.d.ts.map +1 -1
  23. package/dist/lib/components/message/agent-response-form.component.js +149 -26
  24. package/dist/lib/components/message/agent-response-form.component.js.map +1 -1
  25. package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
  26. package/dist/lib/components/message/conversation-message-rating.component.js +3 -2
  27. package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
  28. package/dist/lib/components/message/form-question.component.js +3 -3
  29. package/dist/lib/components/message/message-input-box.component.d.ts +29 -2
  30. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  31. package/dist/lib/components/message/message-input-box.component.js +79 -12
  32. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  33. package/dist/lib/components/message/message-input.component.d.ts +60 -5
  34. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  35. package/dist/lib/components/message/message-input.component.js +303 -119
  36. package/dist/lib/components/message/message-input.component.js.map +1 -1
  37. package/dist/lib/components/message/message-item.component.d.ts +41 -3
  38. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  39. package/dist/lib/components/message/message-item.component.js +237 -106
  40. package/dist/lib/components/message/message-item.component.js.map +1 -1
  41. package/dist/lib/components/message/message-list.component.d.ts +7 -2
  42. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  43. package/dist/lib/components/message/message-list.component.js +19 -4
  44. package/dist/lib/components/message/message-list.component.js.map +1 -1
  45. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +7 -1
  46. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
  47. package/dist/lib/components/sidebar/conversation-sidebar.component.js +28 -6
  48. package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
  49. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +83 -10
  50. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  51. package/dist/lib/components/workspace/conversation-workspace.component.js +290 -103
  52. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  53. package/dist/lib/conversations.module.d.ts +26 -25
  54. package/dist/lib/conversations.module.d.ts.map +1 -1
  55. package/dist/lib/conversations.module.js +7 -3
  56. package/dist/lib/conversations.module.js.map +1 -1
  57. package/dist/lib/models/conversation-state.model.d.ts +2 -1
  58. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  59. package/dist/lib/services/active-tasks.service.d.ts +23 -0
  60. package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
  61. package/dist/lib/services/active-tasks.service.js +91 -2
  62. package/dist/lib/services/active-tasks.service.js.map +1 -1
  63. package/dist/lib/services/agent-state.service.d.ts +2 -0
  64. package/dist/lib/services/agent-state.service.d.ts.map +1 -1
  65. package/dist/lib/services/agent-state.service.js +20 -3
  66. package/dist/lib/services/agent-state.service.js.map +1 -1
  67. package/dist/lib/services/conversation-agent.service.d.ts +38 -6
  68. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  69. package/dist/lib/services/conversation-agent.service.js +233 -71
  70. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  71. package/dist/lib/services/conversation-attachment.service.d.ts +79 -0
  72. package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -0
  73. package/dist/lib/services/conversation-attachment.service.js +327 -0
  74. package/dist/lib/services/conversation-attachment.service.js.map +1 -0
  75. package/dist/lib/services/conversation-data.service.d.ts +15 -1
  76. package/dist/lib/services/conversation-data.service.d.ts.map +1 -1
  77. package/dist/lib/services/conversation-data.service.js +23 -1
  78. package/dist/lib/services/conversation-data.service.js.map +1 -1
  79. package/dist/lib/services/conversation-streaming.service.d.ts +50 -1
  80. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  81. package/dist/lib/services/conversation-streaming.service.js +92 -4
  82. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  83. package/dist/lib/services/mention-autocomplete.service.d.ts +1 -1
  84. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  85. package/dist/lib/services/mention-parser.service.d.ts +16 -1
  86. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  87. package/dist/lib/services/mention-parser.service.js +30 -0
  88. package/dist/lib/services/mention-parser.service.js.map +1 -1
  89. package/dist/public-api.d.ts +2 -0
  90. package/dist/public-api.d.ts.map +1 -1
  91. package/dist/public-api.js +2 -0
  92. package/dist/public-api.js.map +1 -1
  93. package/package.json +17 -17
@@ -0,0 +1,95 @@
1
+ import { EventEmitter, OnInit, OnDestroy } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * Full-screen image viewer modal with zoom and pan capabilities.
5
+ * Provides a lightbox-style experience for viewing attachment images.
6
+ */
7
+ export declare class ImageViewerComponent implements OnInit, OnDestroy {
8
+ /** Image source URL (can be data URL or regular URL) */
9
+ imageUrl: string;
10
+ /** Image alt text */
11
+ alt: string;
12
+ /** Image filename for download */
13
+ fileName: string;
14
+ /** Whether the viewer is visible */
15
+ visible: boolean;
16
+ /** Emits when the viewer should be closed */
17
+ closed: EventEmitter<void>;
18
+ zoomLevel: number;
19
+ minZoom: number;
20
+ maxZoom: number;
21
+ translateX: number;
22
+ translateY: number;
23
+ isDragging: boolean;
24
+ private lastMouseX;
25
+ private lastMouseY;
26
+ ngOnInit(): void;
27
+ ngOnDestroy(): void;
28
+ /**
29
+ * Handle escape key to close
30
+ */
31
+ onEscapeKey(): void;
32
+ /**
33
+ * Handle mouse wheel for zoom
34
+ */
35
+ onWheel(event: WheelEvent): void;
36
+ /**
37
+ * Close the viewer
38
+ */
39
+ close(): void;
40
+ /**
41
+ * Handle backdrop click
42
+ */
43
+ onBackdropClick(event: MouseEvent): void;
44
+ /**
45
+ * Zoom in
46
+ */
47
+ zoomIn(): void;
48
+ /**
49
+ * Zoom out
50
+ */
51
+ zoomOut(): void;
52
+ /**
53
+ * Reset to 100% zoom
54
+ */
55
+ resetZoom(): void;
56
+ /**
57
+ * Fit image to screen
58
+ */
59
+ fitToScreen(): void;
60
+ /**
61
+ * Reset view to initial state
62
+ */
63
+ private resetView;
64
+ /**
65
+ * Adjust zoom level
66
+ */
67
+ private adjustZoom;
68
+ /**
69
+ * Handle mouse down for dragging
70
+ */
71
+ onMouseDown(event: MouseEvent): void;
72
+ /**
73
+ * Handle mouse move for dragging
74
+ */
75
+ onMouseMove(event: MouseEvent): void;
76
+ /**
77
+ * Handle mouse up to stop dragging
78
+ */
79
+ onMouseUp(): void;
80
+ /**
81
+ * Download the image
82
+ */
83
+ downloadImage(): void;
84
+ /**
85
+ * Get current zoom percentage for display
86
+ */
87
+ get zoomPercentage(): string;
88
+ /**
89
+ * Get transform style for image
90
+ */
91
+ get imageTransform(): string;
92
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImageViewerComponent, never>;
93
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImageViewerComponent, "mj-image-viewer", never, { "imageUrl": { "alias": "imageUrl"; "required": false; }; "alt": { "alias": "alt"; "required": false; }; "fileName": { "alias": "fileName"; "required": false; }; "visible": { "alias": "visible"; "required": false; }; }, { "closed": "closed"; }, never, never, false, never>;
94
+ }
95
+ //# sourceMappingURL=image-viewer.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/attachment/image-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAEZ,MAAM,EACN,SAAS,EACV,MAAM,eAAe,CAAC;;AAEvB;;;GAGG;AACH,qBAKa,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAC5D,wDAAwD;IAC/C,QAAQ,EAAE,MAAM,CAAM;IAE/B,qBAAqB;IACZ,GAAG,EAAE,MAAM,CAAW;IAE/B,kCAAkC;IACzB,QAAQ,EAAE,MAAM,CAAW;IAEpC,oCAAoC;IAC3B,OAAO,EAAE,OAAO,CAAS;IAElC,6CAA6C;IACnC,MAAM,qBAA4B;IAGrC,SAAS,EAAE,MAAM,CAAK;IACtB,OAAO,EAAE,MAAM,CAAO;IACtB,OAAO,EAAE,MAAM,CAAK;IACpB,UAAU,EAAE,MAAM,CAAK;IACvB,UAAU,EAAE,MAAM,CAAK;IAGvB,UAAU,EAAE,OAAO,CAAS;IACnC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAAa;IAE/B,QAAQ,IAAI,IAAI;IAOhB,WAAW,IAAI,IAAI;IAKnB;;OAEG;IAEH,WAAW,IAAI,IAAI;IAMnB;;OAEG;IAEH,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQxC;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;OAEG;IACH,SAAS,IAAI,IAAI;IAMjB;;OAEG;IACH,WAAW,IAAI,IAAI;IAKnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAMjB;;OAEG;IACH,OAAO,CAAC,UAAU;IAoBlB;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAUpC;;OAEG;IAEH,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAapC;;OAEG;IAEH,SAAS,IAAI,IAAI;IAIjB;;OAEG;IACH,aAAa,IAAI,IAAI;IAOrB;;OAEG;IACH,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED;;OAEG;IACH,IAAI,cAAc,IAAI,MAAM,CAE3B;yCA7MU,oBAAoB;2CAApB,oBAAoB;CA8MhC"}
@@ -0,0 +1,293 @@
1
+ import { Component, Input, Output, EventEmitter, HostListener } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ import * as i1 from "@angular/common";
4
+ function ImageViewerComponent_div_0_span_3_Template(rf, ctx) { if (rf & 1) {
5
+ i0.ɵɵelementStart(0, "span", 25);
6
+ i0.ɵɵtext(1);
7
+ i0.ɵɵelementEnd();
8
+ } if (rf & 2) {
9
+ const ctx_r1 = i0.ɵɵnextContext(2);
10
+ i0.ɵɵadvance();
11
+ i0.ɵɵtextInterpolate(ctx_r1.fileName);
12
+ } }
13
+ function ImageViewerComponent_div_0_Template(rf, ctx) { if (rf & 1) {
14
+ const _r1 = i0.ɵɵgetCurrentView();
15
+ i0.ɵɵelementStart(0, "div", 1);
16
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onBackdropClick($event)); });
17
+ i0.ɵɵelementStart(1, "div", 2)(2, "div", 3);
18
+ i0.ɵɵtemplate(3, ImageViewerComponent_div_0_span_3_Template, 2, 1, "span", 4);
19
+ i0.ɵɵelementEnd();
20
+ i0.ɵɵelementStart(4, "div", 5)(5, "button", 6);
21
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.zoomOut()); });
22
+ i0.ɵɵelement(6, "i", 7);
23
+ i0.ɵɵelementEnd();
24
+ i0.ɵɵelementStart(7, "span", 8);
25
+ i0.ɵɵtext(8);
26
+ i0.ɵɵelementEnd();
27
+ i0.ɵɵelementStart(9, "button", 9);
28
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.zoomIn()); });
29
+ i0.ɵɵelement(10, "i", 10);
30
+ i0.ɵɵelementEnd();
31
+ i0.ɵɵelement(11, "div", 11);
32
+ i0.ɵɵelementStart(12, "button", 12);
33
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.resetZoom()); });
34
+ i0.ɵɵelement(13, "i", 13);
35
+ i0.ɵɵelementEnd();
36
+ i0.ɵɵelementStart(14, "button", 14);
37
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.fitToScreen()); });
38
+ i0.ɵɵelement(15, "i", 15);
39
+ i0.ɵɵelementEnd()();
40
+ i0.ɵɵelementStart(16, "div", 16)(17, "button", 17);
41
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.downloadImage()); });
42
+ i0.ɵɵelement(18, "i", 18);
43
+ i0.ɵɵelementEnd();
44
+ i0.ɵɵelementStart(19, "button", 19);
45
+ i0.ɵɵlistener("click", function ImageViewerComponent_div_0_Template_button_click_19_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.close()); });
46
+ i0.ɵɵelement(20, "i", 20);
47
+ i0.ɵɵelementEnd()()();
48
+ i0.ɵɵelementStart(21, "div", 21);
49
+ i0.ɵɵlistener("mousedown", function ImageViewerComponent_div_0_Template_div_mousedown_21_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onMouseDown($event)); });
50
+ i0.ɵɵelement(22, "img", 22);
51
+ i0.ɵɵelementEnd();
52
+ i0.ɵɵelementStart(23, "div", 23)(24, "span");
53
+ i0.ɵɵtext(25, "Scroll to zoom");
54
+ i0.ɵɵelementEnd();
55
+ i0.ɵɵelementStart(26, "span", 24);
56
+ i0.ɵɵtext(27, "|");
57
+ i0.ɵɵelementEnd();
58
+ i0.ɵɵelementStart(28, "span");
59
+ i0.ɵɵtext(29, "Drag to pan when zoomed");
60
+ i0.ɵɵelementEnd();
61
+ i0.ɵɵelementStart(30, "span", 24);
62
+ i0.ɵɵtext(31, "|");
63
+ i0.ɵɵelementEnd();
64
+ i0.ɵɵelementStart(32, "span");
65
+ i0.ɵɵtext(33, "Esc to close");
66
+ i0.ɵɵelementEnd()()();
67
+ } if (rf & 2) {
68
+ const ctx_r1 = i0.ɵɵnextContext();
69
+ i0.ɵɵadvance(3);
70
+ i0.ɵɵproperty("ngIf", ctx_r1.fileName);
71
+ i0.ɵɵadvance(5);
72
+ i0.ɵɵtextInterpolate(ctx_r1.zoomPercentage);
73
+ i0.ɵɵadvance(13);
74
+ i0.ɵɵstyleProp("cursor", ctx_r1.zoomLevel > 1 ? ctx_r1.isDragging ? "grabbing" : "grab" : "default");
75
+ i0.ɵɵadvance();
76
+ i0.ɵɵstyleProp("transform", ctx_r1.imageTransform);
77
+ i0.ɵɵproperty("src", ctx_r1.imageUrl, i0.ɵɵsanitizeUrl)("alt", ctx_r1.alt);
78
+ } }
79
+ /**
80
+ * Full-screen image viewer modal with zoom and pan capabilities.
81
+ * Provides a lightbox-style experience for viewing attachment images.
82
+ */
83
+ export class ImageViewerComponent {
84
+ /** Image source URL (can be data URL or regular URL) */
85
+ imageUrl = '';
86
+ /** Image alt text */
87
+ alt = 'Image';
88
+ /** Image filename for download */
89
+ fileName = 'image';
90
+ /** Whether the viewer is visible */
91
+ visible = false;
92
+ /** Emits when the viewer should be closed */
93
+ closed = new EventEmitter();
94
+ // Zoom and pan state
95
+ zoomLevel = 1;
96
+ minZoom = 0.1;
97
+ maxZoom = 5;
98
+ translateX = 0;
99
+ translateY = 0;
100
+ // Drag state
101
+ isDragging = false;
102
+ lastMouseX = 0;
103
+ lastMouseY = 0;
104
+ ngOnInit() {
105
+ // Lock body scroll when viewer is open
106
+ if (this.visible) {
107
+ document.body.style.overflow = 'hidden';
108
+ }
109
+ }
110
+ ngOnDestroy() {
111
+ // Restore body scroll
112
+ document.body.style.overflow = '';
113
+ }
114
+ /**
115
+ * Handle escape key to close
116
+ */
117
+ onEscapeKey() {
118
+ if (this.visible) {
119
+ this.close();
120
+ }
121
+ }
122
+ /**
123
+ * Handle mouse wheel for zoom
124
+ */
125
+ onWheel(event) {
126
+ if (!this.visible)
127
+ return;
128
+ event.preventDefault();
129
+ const delta = event.deltaY > 0 ? -0.1 : 0.1;
130
+ this.adjustZoom(delta, event.clientX, event.clientY);
131
+ }
132
+ /**
133
+ * Close the viewer
134
+ */
135
+ close() {
136
+ this.resetView();
137
+ document.body.style.overflow = '';
138
+ this.closed.emit();
139
+ }
140
+ /**
141
+ * Handle backdrop click
142
+ */
143
+ onBackdropClick(event) {
144
+ // Only close if clicking directly on backdrop
145
+ const target = event.target;
146
+ if (target.classList.contains('image-viewer-backdrop')) {
147
+ this.close();
148
+ }
149
+ }
150
+ /**
151
+ * Zoom in
152
+ */
153
+ zoomIn() {
154
+ this.adjustZoom(0.25);
155
+ }
156
+ /**
157
+ * Zoom out
158
+ */
159
+ zoomOut() {
160
+ this.adjustZoom(-0.25);
161
+ }
162
+ /**
163
+ * Reset to 100% zoom
164
+ */
165
+ resetZoom() {
166
+ this.zoomLevel = 1;
167
+ this.translateX = 0;
168
+ this.translateY = 0;
169
+ }
170
+ /**
171
+ * Fit image to screen
172
+ */
173
+ fitToScreen() {
174
+ // Reset position and set zoom to 1 (CSS will handle fitting)
175
+ this.resetView();
176
+ }
177
+ /**
178
+ * Reset view to initial state
179
+ */
180
+ resetView() {
181
+ this.zoomLevel = 1;
182
+ this.translateX = 0;
183
+ this.translateY = 0;
184
+ }
185
+ /**
186
+ * Adjust zoom level
187
+ */
188
+ adjustZoom(delta, centerX, centerY) {
189
+ const newZoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.zoomLevel + delta));
190
+ if (centerX !== undefined && centerY !== undefined && newZoom !== this.zoomLevel) {
191
+ // Zoom towards mouse position
192
+ const factor = newZoom / this.zoomLevel;
193
+ const imageContainer = document.querySelector('.image-container');
194
+ if (imageContainer) {
195
+ const rect = imageContainer.getBoundingClientRect();
196
+ const offsetX = centerX - rect.left - rect.width / 2 - this.translateX;
197
+ const offsetY = centerY - rect.top - rect.height / 2 - this.translateY;
198
+ this.translateX = this.translateX - offsetX * (factor - 1);
199
+ this.translateY = this.translateY - offsetY * (factor - 1);
200
+ }
201
+ }
202
+ this.zoomLevel = newZoom;
203
+ }
204
+ /**
205
+ * Handle mouse down for dragging
206
+ */
207
+ onMouseDown(event) {
208
+ if (event.button !== 0)
209
+ return; // Only left click
210
+ if (this.zoomLevel <= 1)
211
+ return; // Only allow pan when zoomed in
212
+ this.isDragging = true;
213
+ this.lastMouseX = event.clientX;
214
+ this.lastMouseY = event.clientY;
215
+ event.preventDefault();
216
+ }
217
+ /**
218
+ * Handle mouse move for dragging
219
+ */
220
+ onMouseMove(event) {
221
+ if (!this.isDragging)
222
+ return;
223
+ const deltaX = event.clientX - this.lastMouseX;
224
+ const deltaY = event.clientY - this.lastMouseY;
225
+ this.translateX += deltaX;
226
+ this.translateY += deltaY;
227
+ this.lastMouseX = event.clientX;
228
+ this.lastMouseY = event.clientY;
229
+ }
230
+ /**
231
+ * Handle mouse up to stop dragging
232
+ */
233
+ onMouseUp() {
234
+ this.isDragging = false;
235
+ }
236
+ /**
237
+ * Download the image
238
+ */
239
+ downloadImage() {
240
+ const link = document.createElement('a');
241
+ link.href = this.imageUrl;
242
+ link.download = this.fileName;
243
+ link.click();
244
+ }
245
+ /**
246
+ * Get current zoom percentage for display
247
+ */
248
+ get zoomPercentage() {
249
+ return Math.round(this.zoomLevel * 100) + '%';
250
+ }
251
+ /**
252
+ * Get transform style for image
253
+ */
254
+ get imageTransform() {
255
+ return `translate(${this.translateX}px, ${this.translateY}px) scale(${this.zoomLevel})`;
256
+ }
257
+ static ɵfac = function ImageViewerComponent_Factory(t) { return new (t || ImageViewerComponent)(); };
258
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ImageViewerComponent, selectors: [["mj-image-viewer"]], hostBindings: function ImageViewerComponent_HostBindings(rf, ctx) { if (rf & 1) {
259
+ i0.ɵɵlistener("keydown.escape", function ImageViewerComponent_keydown_escape_HostBindingHandler() { return ctx.onEscapeKey(); }, false, i0.ɵɵresolveDocument)("wheel", function ImageViewerComponent_wheel_HostBindingHandler($event) { return ctx.onWheel($event); })("mousemove", function ImageViewerComponent_mousemove_HostBindingHandler($event) { return ctx.onMouseMove($event); }, false, i0.ɵɵresolveDocument)("mouseup", function ImageViewerComponent_mouseup_HostBindingHandler() { return ctx.onMouseUp(); }, false, i0.ɵɵresolveDocument);
260
+ } }, inputs: { imageUrl: "imageUrl", alt: "alt", fileName: "fileName", visible: "visible" }, outputs: { closed: "closed" }, decls: 1, vars: 1, consts: [["class", "image-viewer-backdrop", 3, "click", 4, "ngIf"], [1, "image-viewer-backdrop", 3, "click"], [1, "viewer-toolbar"], [1, "toolbar-left"], ["class", "file-name", 4, "ngIf"], [1, "toolbar-center"], ["title", "Zoom Out", 1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-minus"], [1, "zoom-display"], ["title", "Zoom In", 1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "toolbar-separator"], ["title", "Reset Zoom (100%)", 1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-expand"], ["title", "Fit to Screen", 1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-compress"], [1, "toolbar-right"], ["title", "Download", 1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Close (Esc)", 1, "toolbar-btn", "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "image-container", 3, "mousedown"], ["draggable", "false", 1, "viewer-image", 3, "src", "alt"], [1, "viewer-instructions"], [1, "separator"], [1, "file-name"]], template: function ImageViewerComponent_Template(rf, ctx) { if (rf & 1) {
261
+ i0.ɵɵtemplate(0, ImageViewerComponent_div_0_Template, 34, 8, "div", 0);
262
+ } if (rf & 2) {
263
+ i0.ɵɵproperty("ngIf", ctx.visible);
264
+ } }, dependencies: [i1.NgIf], styles: [".image-viewer-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n z-index: 10000;\n background: rgba(0, 0, 0, 0.92);\n display: flex;\n flex-direction: column;\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n\n\n.viewer-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.toolbar-left[_ngcontent-%COMP%], \n.toolbar-center[_ngcontent-%COMP%], \n.toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.toolbar-right[_ngcontent-%COMP%] {\n flex: 1;\n justify-content: flex-end;\n}\n\n.file-name[_ngcontent-%COMP%] {\n color: rgba(255, 255, 255, 0.9);\n font-size: 14px;\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 300px;\n}\n\n.toolbar-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n transition: background 0.2s, transform 0.15s;\n}\n\n.toolbar-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(1.05);\n}\n\n.toolbar-btn[_ngcontent-%COMP%]:active {\n transform: scale(0.95);\n}\n\n.toolbar-btn.close-btn[_ngcontent-%COMP%] {\n background: rgba(255, 59, 48, 0.2);\n}\n\n.toolbar-btn.close-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 59, 48, 0.4);\n}\n\n.toolbar-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.zoom-display[_ngcontent-%COMP%] {\n color: rgba(255, 255, 255, 0.9);\n font-size: 13px;\n font-weight: 500;\n min-width: 50px;\n text-align: center;\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\n}\n\n.toolbar-separator[_ngcontent-%COMP%] {\n width: 1px;\n height: 20px;\n background: rgba(255, 255, 255, 0.2);\n margin: 0 4px;\n}\n\n\n\n.image-container[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 20px;\n}\n\n.viewer-image[_ngcontent-%COMP%] {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n transition: transform 0.1s ease-out;\n user-select: none;\n pointer-events: none;\n border-radius: 4px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\n}\n\n\n\n.viewer-instructions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px;\n color: rgba(255, 255, 255, 0.5);\n font-size: 12px;\n}\n\n.viewer-instructions[_ngcontent-%COMP%] .separator[_ngcontent-%COMP%] {\n opacity: 0.3;\n}\n\n\n\n@media (max-width: 768px) {\n .viewer-toolbar[_ngcontent-%COMP%] {\n padding: 8px 12px;\n }\n\n .toolbar-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n }\n\n .file-name[_ngcontent-%COMP%] {\n max-width: 150px;\n font-size: 12px;\n }\n\n .zoom-display[_ngcontent-%COMP%] {\n min-width: 40px;\n font-size: 11px;\n }\n\n .viewer-instructions[_ngcontent-%COMP%] {\n font-size: 10px;\n padding: 8px;\n }\n\n .image-container[_ngcontent-%COMP%] {\n padding: 10px;\n }\n}\n\n\n\n@media (hover: none) {\n .toolbar-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 255, 255, 0.1);\n transform: none;\n }\n\n .toolbar-btn.close-btn[_ngcontent-%COMP%]:hover {\n background: rgba(255, 59, 48, 0.2);\n }\n\n .viewer-instructions[_ngcontent-%COMP%] {\n display: none;\n }\n}"] });
265
+ }
266
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ImageViewerComponent, [{
267
+ type: Component,
268
+ args: [{ selector: 'mj-image-viewer', template: "<div class=\"image-viewer-backdrop\"\n *ngIf=\"visible\"\n (click)=\"onBackdropClick($event)\">\n\n <!-- Toolbar -->\n <div class=\"viewer-toolbar\">\n <div class=\"toolbar-left\">\n <span class=\"file-name\" *ngIf=\"fileName\">{{ fileName }}</span>\n </div>\n\n <div class=\"toolbar-center\">\n <button class=\"toolbar-btn\" (click)=\"zoomOut()\" title=\"Zoom Out\">\n <i class=\"fa-solid fa-minus\"></i>\n </button>\n\n <span class=\"zoom-display\">{{ zoomPercentage }}</span>\n\n <button class=\"toolbar-btn\" (click)=\"zoomIn()\" title=\"Zoom In\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <div class=\"toolbar-separator\"></div>\n\n <button class=\"toolbar-btn\" (click)=\"resetZoom()\" title=\"Reset Zoom (100%)\">\n <i class=\"fa-solid fa-expand\"></i>\n </button>\n\n <button class=\"toolbar-btn\" (click)=\"fitToScreen()\" title=\"Fit to Screen\">\n <i class=\"fa-solid fa-compress\"></i>\n </button>\n </div>\n\n <div class=\"toolbar-right\">\n <button class=\"toolbar-btn\" (click)=\"downloadImage()\" title=\"Download\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n\n <button class=\"toolbar-btn close-btn\" (click)=\"close()\" title=\"Close (Esc)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n\n <!-- Image Container -->\n <div class=\"image-container\"\n (mousedown)=\"onMouseDown($event)\"\n [style.cursor]=\"zoomLevel > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default'\">\n <img [src]=\"imageUrl\"\n [alt]=\"alt\"\n class=\"viewer-image\"\n [style.transform]=\"imageTransform\"\n draggable=\"false\">\n </div>\n\n <!-- Instructions -->\n <div class=\"viewer-instructions\">\n <span>Scroll to zoom</span>\n <span class=\"separator\">|</span>\n <span>Drag to pan when zoomed</span>\n <span class=\"separator\">|</span>\n <span>Esc to close</span>\n </div>\n</div>\n", styles: [".image-viewer-backdrop {\n position: fixed;\n inset: 0;\n z-index: 10000;\n background: rgba(0, 0, 0, 0.92);\n display: flex;\n flex-direction: column;\n animation: fadeIn 0.2s ease-out;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n/* Toolbar */\n.viewer-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n min-width: 0;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.file-name {\n color: rgba(255, 255, 255, 0.9);\n font-size: 14px;\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 300px;\n}\n\n.toolbar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n transition: background 0.2s, transform 0.15s;\n}\n\n.toolbar-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(1.05);\n}\n\n.toolbar-btn:active {\n transform: scale(0.95);\n}\n\n.toolbar-btn.close-btn {\n background: rgba(255, 59, 48, 0.2);\n}\n\n.toolbar-btn.close-btn:hover {\n background: rgba(255, 59, 48, 0.4);\n}\n\n.toolbar-btn i {\n font-size: 14px;\n}\n\n.zoom-display {\n color: rgba(255, 255, 255, 0.9);\n font-size: 13px;\n font-weight: 500;\n min-width: 50px;\n text-align: center;\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\n}\n\n.toolbar-separator {\n width: 1px;\n height: 20px;\n background: rgba(255, 255, 255, 0.2);\n margin: 0 4px;\n}\n\n/* Image Container */\n.image-container {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 20px;\n}\n\n.viewer-image {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n transition: transform 0.1s ease-out;\n user-select: none;\n pointer-events: none;\n border-radius: 4px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\n}\n\n/* Instructions */\n.viewer-instructions {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px;\n color: rgba(255, 255, 255, 0.5);\n font-size: 12px;\n}\n\n.viewer-instructions .separator {\n opacity: 0.3;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .viewer-toolbar {\n padding: 8px 12px;\n }\n\n .toolbar-btn {\n width: 32px;\n height: 32px;\n }\n\n .file-name {\n max-width: 150px;\n font-size: 12px;\n }\n\n .zoom-display {\n min-width: 40px;\n font-size: 11px;\n }\n\n .viewer-instructions {\n font-size: 10px;\n padding: 8px;\n }\n\n .image-container {\n padding: 10px;\n }\n}\n\n/* Touch support */\n@media (hover: none) {\n .toolbar-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n transform: none;\n }\n\n .toolbar-btn.close-btn:hover {\n background: rgba(255, 59, 48, 0.2);\n }\n\n .viewer-instructions {\n display: none;\n }\n}\n"] }]
269
+ }], null, { imageUrl: [{
270
+ type: Input
271
+ }], alt: [{
272
+ type: Input
273
+ }], fileName: [{
274
+ type: Input
275
+ }], visible: [{
276
+ type: Input
277
+ }], closed: [{
278
+ type: Output
279
+ }], onEscapeKey: [{
280
+ type: HostListener,
281
+ args: ['document:keydown.escape']
282
+ }], onWheel: [{
283
+ type: HostListener,
284
+ args: ['wheel', ['$event']]
285
+ }], onMouseMove: [{
286
+ type: HostListener,
287
+ args: ['document:mousemove', ['$event']]
288
+ }], onMouseUp: [{
289
+ type: HostListener,
290
+ args: ['document:mouseup']
291
+ }] }); })();
292
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ImageViewerComponent, { className: "ImageViewerComponent", filePath: "src/lib/components/attachment/image-viewer.component.ts", lineNumber: 20 }); })();
293
+ //# sourceMappingURL=image-viewer.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/attachment/image-viewer.component.ts","../../../../src/lib/components/attachment/image-viewer.component.html"],"names":[],"mappings":"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"}
@@ -1,14 +1,19 @@
1
1
  import { EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, AfterViewChecked } from '@angular/core';
2
2
  import { UserInfo, CompositeKey } from '@memberjunction/core';
3
- import { ConversationEntity, ConversationDetailEntity, AIAgentRunEntity, AIAgentRunEntityExtended, ArtifactEntity, TaskEntity } from '@memberjunction/core-entities';
3
+ import { ConversationEntity, ConversationDetailEntity, AIAgentRunEntity, ArtifactEntity, TaskEntity } from '@memberjunction/core-entities';
4
+ import { AIAgentRunEntityExtended } from "@memberjunction/ai-core-plus";
4
5
  import { ConversationDataService } from '../../services/conversation-data.service';
5
6
  import { AgentStateService } from '../../services/agent-state.service';
6
7
  import { ConversationAgentService } from '../../services/conversation-agent.service';
7
8
  import { ActiveTasksService } from '../../services/active-tasks.service';
8
9
  import { MentionAutocompleteService } from '../../services/mention-autocomplete.service';
9
10
  import { ArtifactPermissionService } from '../../services/artifact-permission.service';
11
+ import { ConversationAttachmentService } from '../../services/conversation-attachment.service';
12
+ import { ConversationStreamingService } from '../../services/conversation-streaming.service';
13
+ import { MessageAttachment } from '../message/message-item.component';
10
14
  import { LazyArtifactInfo } from '../../models/lazy-artifact-info';
11
15
  import { RatingJSON } from '../../models/conversation-complete-query.model';
16
+ import { PendingAttachment } from '../mention/mention-editor.component';
12
17
  import { DialogService } from '@progress/kendo-angular-dialog';
13
18
  import { Subject } from 'rxjs';
14
19
  import * as i0 from "@angular/core";
@@ -21,6 +26,8 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
21
26
  private mentionAutocompleteService;
22
27
  private artifactPermissionService;
23
28
  private dialogService;
29
+ private attachmentService;
30
+ private streamingService;
24
31
  environmentId: string;
25
32
  currentUser: UserInfo;
26
33
  private _conversationId;
@@ -29,9 +36,15 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
29
36
  conversation: ConversationEntity | null;
30
37
  threadId: string | null;
31
38
  isNewConversation: boolean;
32
- pendingMessage: string | null;
39
+ private _pendingMessage;
40
+ set pendingMessage(value: string | null);
41
+ get pendingMessage(): string | null;
42
+ private _pendingAttachments;
43
+ set pendingAttachments(value: PendingAttachment[] | null);
44
+ get pendingAttachments(): PendingAttachment[] | null;
33
45
  pendingArtifactId: string | null;
34
46
  pendingArtifactVersionNumber: number | null;
47
+ showSidebarToggle: boolean;
35
48
  conversationRenamed: EventEmitter<{
36
49
  conversationId: string;
37
50
  name: string;
@@ -46,19 +59,27 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
46
59
  type: 'conversation' | 'collection';
47
60
  id: string;
48
61
  }>;
49
- conversationCreated: EventEmitter<ConversationEntity>;
62
+ sidebarToggleClicked: EventEmitter<void>;
63
+ conversationCreated: EventEmitter<{
64
+ conversation: ConversationEntity;
65
+ pendingMessage?: string | undefined;
66
+ pendingAttachments?: PendingAttachment[] | undefined;
67
+ }>;
50
68
  threadOpened: EventEmitter<string>;
51
69
  threadClosed: EventEmitter<void>;
52
70
  pendingArtifactConsumed: EventEmitter<void>;
53
71
  pendingMessageConsumed: EventEmitter<void>;
54
- pendingMessageRequested: EventEmitter<string>;
72
+ pendingMessageRequested: EventEmitter<{
73
+ text: string;
74
+ attachments: PendingAttachment[];
75
+ }>;
55
76
  private scrollContainer;
56
77
  private messageInputComponents;
57
78
  private artifactViewerComponent?;
58
79
  messages: ConversationDetailEntity[];
59
80
  showScrollToBottomIcon: boolean;
60
81
  private scrollToBottom;
61
- private lastLoadedConversationId;
82
+ private loadedPeripheralConversationIds;
62
83
  private currentlyLoadingConversationId;
63
84
  isProcessing: boolean;
64
85
  private intentCheckMessage;
@@ -100,13 +121,15 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
100
121
  * Ratings by conversation detail ID (parsed from RatingsJSON)
101
122
  */
102
123
  ratingsByDetailId: Map<string, RatingJSON[]>;
124
+ /**
125
+ * Attachments by conversation detail ID (loaded from ConversationDetailAttachments)
126
+ */
127
+ attachmentsByDetailId: Map<string, MessageAttachment[]>;
103
128
  /**
104
129
  * In-progress message IDs for streaming reconnection
105
130
  * Passed to message-input component to reconnect PubSub updates
106
131
  */
107
132
  inProgressMessageIds: string[];
108
- private agentRunUpdateTimer;
109
- private previousMessageStatuses;
110
133
  private messageInputMetadataCache;
111
134
  readonly emptyArtifactsMap: Map<string, LazyArtifactInfo[]>;
112
135
  readonly emptyAgentRunsMap: Map<string, AIAgentRunEntityExtended>;
@@ -116,13 +139,35 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
116
139
  artifactId: string;
117
140
  versionNumber: number;
118
141
  }>;
142
+ private destroy$;
119
143
  private isInitialized;
120
144
  private isResizing;
121
145
  private startX;
122
146
  private startWidth;
123
147
  private readonly ARTIFACT_PANE_WIDTH_KEY;
124
- constructor(conversationData: ConversationDataService, agentStateService: AgentStateService, conversationAgentService: ConversationAgentService, activeTasks: ActiveTasksService, cdr: ChangeDetectorRef, mentionAutocompleteService: MentionAutocompleteService, artifactPermissionService: ArtifactPermissionService, dialogService: DialogService);
148
+ showImageViewer: boolean;
149
+ selectedImageUrl: string;
150
+ selectedImageAlt: string;
151
+ selectedImageFileName: string;
152
+ isUploadingAttachments: boolean;
153
+ uploadingMessage: string;
154
+ enableAttachments: boolean;
155
+ maxAttachments: number;
156
+ maxAttachmentSizeBytes: number;
157
+ acceptedFileTypes: string;
158
+ private conversationManagerAgent;
159
+ constructor(conversationData: ConversationDataService, agentStateService: AgentStateService, conversationAgentService: ConversationAgentService, activeTasks: ActiveTasksService, cdr: ChangeDetectorRef, mentionAutocompleteService: MentionAutocompleteService, artifactPermissionService: ArtifactPermissionService, dialogService: DialogService, attachmentService: ConversationAttachmentService, streamingService: ConversationStreamingService);
125
160
  ngOnInit(): Promise<void>;
161
+ /**
162
+ * Initializes attachment support by checking if the conversation manager agent (Sage)
163
+ * or any recent agent in the conversation supports non-text input modalities.
164
+ */
165
+ private initializeAttachmentSupport;
166
+ /**
167
+ * Updates attachment support based on the current conversation context.
168
+ * Called when conversation changes to check if any agent in the conversation supports attachments.
169
+ */
170
+ private updateAttachmentSupport;
126
171
  ngAfterViewChecked(): void;
127
172
  ngOnDestroy(): void;
128
173
  private onConversationChanged;
@@ -168,7 +213,12 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
168
213
  * Restoring them here causes duplicate "Agent Processing..." entries.
169
214
  */
170
215
  private restoreActiveTasks;
171
- onMessageSent(message: ConversationDetailEntity): void;
216
+ onMessageSent(message: ConversationDetailEntity): Promise<void>;
217
+ /**
218
+ * Loads attachments for a single message and adds them to the attachmentsByDetailId map.
219
+ * Called after a new message is sent to ensure attachments are displayed immediately.
220
+ */
221
+ private loadAttachmentsForMessage;
172
222
  /**
173
223
  * Ensures the current user is in the avatar map
174
224
  * Called when new messages are created to ensure avatar data is available
@@ -190,6 +240,15 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
190
240
  conversationDetailId: string;
191
241
  agentId?: string;
192
242
  }): Promise<void>;
243
+ /**
244
+ * Handle message completion from PubSub streaming service.
245
+ * This is the primary completion handler that replaces timer-based polling.
246
+ * Called when the streaming service receives a completion event from the backend.
247
+ *
248
+ * @param message The message entity that completed
249
+ * @param agentRunId The agent run ID from the completion event
250
+ */
251
+ private handleMessageCompletion;
193
252
  /**
194
253
  * Handle agent run update event from progress updates
195
254
  * This is called on EVERY progress update with the full, live agent run object
@@ -200,26 +259,11 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
200
259
  agentRun?: AIAgentRunEntityExtended;
201
260
  agentRunId?: string;
202
261
  }): Promise<void>;
203
- /**
204
- * Start 1-second timer for smooth agent run UI updates
205
- * Updates the message list every second to keep elapsed times current
206
- * Also detects when messages complete and reloads agent runs
207
- */
208
- private startAgentRunUpdateTimer;
209
- /**
210
- * Stop the agent run update timer
211
- */
212
- private stopAgentRunUpdateTimer;
213
262
  /**
214
263
  * Reload messages for the active conversation from the database
215
264
  * Called when completion is detected to discover newly delegated agent messages
216
265
  */
217
266
  private reloadMessagesForActiveConversation;
218
- /**
219
- * Detect messages that changed from In-Progress to Complete and reload their agent runs
220
- * This handles the navigation scenario where onMessageComplete event isn't fired
221
- */
222
- private detectAndHandleCompletedMessages;
223
267
  onAgentResponse(event: {
224
268
  message: ConversationDetailEntity;
225
269
  agentResult: any;
@@ -311,6 +355,21 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
311
355
  customInput?: string;
312
356
  }): Promise<void>;
313
357
  onRetryMessage(message: ConversationDetailEntity): void;
358
+ /**
359
+ * Handle attachment click - opens the image viewer for images
360
+ */
361
+ onAttachmentClicked(attachment: MessageAttachment): void;
362
+ /**
363
+ * Handle image viewer close
364
+ */
365
+ onImageViewerClosed(): void;
366
+ /**
367
+ * Handle upload state changes from message input component
368
+ */
369
+ onUploadStateChanged(event: {
370
+ isUploading: boolean;
371
+ message: string;
372
+ }): void;
314
373
  onArtifactClicked(data: {
315
374
  artifactId: string;
316
375
  versionId?: string;
@@ -373,7 +432,10 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
373
432
  * Handle message sent from empty state component
374
433
  * Creates a new conversation and emits to parent to update selection
375
434
  */
376
- onEmptyStateMessageSent(messageText: string): Promise<void>;
435
+ onEmptyStateMessageSent(event: {
436
+ text: string;
437
+ attachments: PendingAttachment[];
438
+ }): Promise<void>;
377
439
  onOpenEntityRecord(event: {
378
440
  entityName: string;
379
441
  compositeKey: CompositeKey;
@@ -435,6 +497,6 @@ export declare class ConversationChatAreaComponent implements OnInit, OnDestroy,
435
497
  */
436
498
  onIntentCheckCompleted(): void;
437
499
  static ɵfac: i0.ɵɵFactoryDeclaration<ConversationChatAreaComponent, never>;
438
- static ɵcmp: i0.ɵɵComponentDeclaration<ConversationChatAreaComponent, "mj-conversation-chat-area", never, { "environmentId": { "alias": "environmentId"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "conversationId": { "alias": "conversationId"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "threadId": { "alias": "threadId"; "required": false; }; "isNewConversation": { "alias": "isNewConversation"; "required": false; }; "pendingMessage": { "alias": "pendingMessage"; "required": false; }; "pendingArtifactId": { "alias": "pendingArtifactId"; "required": false; }; "pendingArtifactVersionNumber": { "alias": "pendingArtifactVersionNumber"; "required": false; }; }, { "conversationRenamed": "conversationRenamed"; "openEntityRecord": "openEntityRecord"; "taskClicked": "taskClicked"; "artifactLinkClicked": "artifactLinkClicked"; "conversationCreated": "conversationCreated"; "threadOpened": "threadOpened"; "threadClosed": "threadClosed"; "pendingArtifactConsumed": "pendingArtifactConsumed"; "pendingMessageConsumed": "pendingMessageConsumed"; "pendingMessageRequested": "pendingMessageRequested"; }, never, never, false, never>;
500
+ static ɵcmp: i0.ɵɵComponentDeclaration<ConversationChatAreaComponent, "mj-conversation-chat-area", never, { "environmentId": { "alias": "environmentId"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "conversationId": { "alias": "conversationId"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "threadId": { "alias": "threadId"; "required": false; }; "isNewConversation": { "alias": "isNewConversation"; "required": false; }; "pendingMessage": { "alias": "pendingMessage"; "required": false; }; "pendingAttachments": { "alias": "pendingAttachments"; "required": false; }; "pendingArtifactId": { "alias": "pendingArtifactId"; "required": false; }; "pendingArtifactVersionNumber": { "alias": "pendingArtifactVersionNumber"; "required": false; }; "showSidebarToggle": { "alias": "showSidebarToggle"; "required": false; }; }, { "conversationRenamed": "conversationRenamed"; "openEntityRecord": "openEntityRecord"; "taskClicked": "taskClicked"; "artifactLinkClicked": "artifactLinkClicked"; "sidebarToggleClicked": "sidebarToggleClicked"; "conversationCreated": "conversationCreated"; "threadOpened": "threadOpened"; "threadClosed": "threadClosed"; "pendingArtifactConsumed": "pendingArtifactConsumed"; "pendingMessageConsumed": "pendingMessageConsumed"; "pendingMessageRequested": "pendingMessageRequested"; }, never, never, false, never>;
439
501
  }
440
502
  //# sourceMappingURL=conversation-chat-area.component.d.ts.map