@bloonio/chat-angular 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +120 -6
- package/fesm2022/bloonio-chat-angular.mjs +557 -168
- package/fesm2022/bloonio-chat-angular.mjs.map +1 -1
- package/package.json +5 -7
- package/src/lib/styles/chat.css +16 -3
- package/types/bloonio-chat-angular.d.ts +330 -0
- package/esm2022/bloonio-chat-angular.mjs +0 -5
- package/esm2022/index.mjs +0 -25
- package/esm2022/lib/bloonio-chat.service.mjs +0 -311
- package/esm2022/lib/bloonio-chat.token.mjs +0 -46
- package/esm2022/lib/components/input-bar.component.mjs +0 -91
- package/esm2022/lib/components/launcher.component.mjs +0 -81
- package/esm2022/lib/components/message-list.component.mjs +0 -58
- package/esm2022/lib/components/panel.component.mjs +0 -211
- package/esm2022/lib/components/quick-replies.component.mjs +0 -58
- package/esm2022/lib/components/thread.component.mjs +0 -103
- package/index.d.ts +0 -27
- package/lib/bloonio-chat.service.d.ts +0 -79
- package/lib/bloonio-chat.token.d.ts +0 -67
- package/lib/components/input-bar.component.d.ts +0 -18
- package/lib/components/launcher.component.d.ts +0 -14
- package/lib/components/message-list.component.d.ts +0 -8
- package/lib/components/panel.component.d.ts +0 -31
- package/lib/components/quick-replies.component.d.ts +0 -10
- package/lib/components/thread.component.d.ts +0 -27
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Renders the message log + typing indicator. Pure presentational —
|
|
3
|
-
* receives messages + typing flag as inputs so it can also be used
|
|
4
|
-
* outside the panel (custom layouts, debug screens).
|
|
5
|
-
*/
|
|
6
|
-
import { ChangeDetectionStrategy, Component, Input, } from '@angular/core';
|
|
7
|
-
import * as i0 from "@angular/core";
|
|
8
|
-
export class MessageListComponent {
|
|
9
|
-
messages = [];
|
|
10
|
-
botTyping = false;
|
|
11
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MessageListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MessageListComponent, isStandalone: true, selector: "bloonio-chat-message-list", inputs: { messages: "messages", botTyping: "botTyping" }, ngImport: i0, template: `
|
|
13
|
-
@for (m of messages; track m.id) {
|
|
14
|
-
<div
|
|
15
|
-
class="bch-bubble"
|
|
16
|
-
[attr.data-from]="m.from"
|
|
17
|
-
[attr.data-pending]="m.pending ? 'true' : null"
|
|
18
|
-
>
|
|
19
|
-
{{ m.content }}
|
|
20
|
-
</div>
|
|
21
|
-
}
|
|
22
|
-
@if (botTyping) {
|
|
23
|
-
<div class="bch-typing" aria-label="Assistant is typing">
|
|
24
|
-
<span></span><span></span><span></span>
|
|
25
|
-
</div>
|
|
26
|
-
}
|
|
27
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
28
|
-
}
|
|
29
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MessageListComponent, decorators: [{
|
|
30
|
-
type: Component,
|
|
31
|
-
args: [{
|
|
32
|
-
selector: 'bloonio-chat-message-list',
|
|
33
|
-
standalone: true,
|
|
34
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
35
|
-
template: `
|
|
36
|
-
@for (m of messages; track m.id) {
|
|
37
|
-
<div
|
|
38
|
-
class="bch-bubble"
|
|
39
|
-
[attr.data-from]="m.from"
|
|
40
|
-
[attr.data-pending]="m.pending ? 'true' : null"
|
|
41
|
-
>
|
|
42
|
-
{{ m.content }}
|
|
43
|
-
</div>
|
|
44
|
-
}
|
|
45
|
-
@if (botTyping) {
|
|
46
|
-
<div class="bch-typing" aria-label="Assistant is typing">
|
|
47
|
-
<span></span><span></span><span></span>
|
|
48
|
-
</div>
|
|
49
|
-
}
|
|
50
|
-
`,
|
|
51
|
-
}]
|
|
52
|
-
}], propDecorators: { messages: [{
|
|
53
|
-
type: Input,
|
|
54
|
-
args: [{ required: true }]
|
|
55
|
-
}], botTyping: [{
|
|
56
|
-
type: Input
|
|
57
|
-
}] } });
|
|
58
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnZS1saXN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9tZXNzYWdlLWxpc3QuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFDSCxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxLQUFLLEdBQ04sTUFBTSxlQUFlLENBQUM7O0FBeUJ2QixNQUFNLE9BQU8sb0JBQW9CO0lBQ0osUUFBUSxHQUFrQixFQUFFLENBQUM7SUFDL0MsU0FBUyxHQUFHLEtBQUssQ0FBQzt3R0FGaEIsb0JBQW9COzRGQUFwQixvQkFBb0IsK0lBakJyQjs7Ozs7Ozs7Ozs7Ozs7O0dBZVQ7OzRGQUVVLG9CQUFvQjtrQkFyQmhDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLDJCQUEyQjtvQkFDckMsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLGVBQWUsRUFBRSx1QkFBdUIsQ0FBQyxNQUFNO29CQUMvQyxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7OztHQWVUO2lCQUNGOzhCQUU0QixRQUFRO3NCQUFsQyxLQUFLO3VCQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtnQkFDaEIsU0FBUztzQkFBakIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVuZGVycyB0aGUgbWVzc2FnZSBsb2cgKyB0eXBpbmcgaW5kaWNhdG9yLiBQdXJlIHByZXNlbnRhdGlvbmFsIOKAlFxuICogcmVjZWl2ZXMgbWVzc2FnZXMgKyB0eXBpbmcgZmxhZyBhcyBpbnB1dHMgc28gaXQgY2FuIGFsc28gYmUgdXNlZFxuICogb3V0c2lkZSB0aGUgcGFuZWwgKGN1c3RvbSBsYXlvdXRzLCBkZWJ1ZyBzY3JlZW5zKS5cbiAqL1xuaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgSW5wdXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgdHlwZSB7IENoYXRNZXNzYWdlIH0gZnJvbSAnLi4vYmxvb25pby1jaGF0LnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdibG9vbmlvLWNoYXQtbWVzc2FnZS1saXN0JyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHRlbXBsYXRlOiBgXG4gICAgQGZvciAobSBvZiBtZXNzYWdlczsgdHJhY2sgbS5pZCkge1xuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImJjaC1idWJibGVcIlxuICAgICAgICBbYXR0ci5kYXRhLWZyb21dPVwibS5mcm9tXCJcbiAgICAgICAgW2F0dHIuZGF0YS1wZW5kaW5nXT1cIm0ucGVuZGluZyA/ICd0cnVlJyA6IG51bGxcIlxuICAgICAgPlxuICAgICAgICB7eyBtLmNvbnRlbnQgfX1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICBAaWYgKGJvdFR5cGluZykge1xuICAgICAgPGRpdiBjbGFzcz1cImJjaC10eXBpbmdcIiBhcmlhLWxhYmVsPVwiQXNzaXN0YW50IGlzIHR5cGluZ1wiPlxuICAgICAgICA8c3Bhbj48L3NwYW4+PHNwYW4+PC9zcGFuPjxzcGFuPjwvc3Bhbj5cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgTWVzc2FnZUxpc3RDb21wb25lbnQge1xuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KSBtZXNzYWdlczogQ2hhdE1lc3NhZ2VbXSA9IFtdO1xuICBASW5wdXQoKSBib3RUeXBpbmcgPSBmYWxzZTtcbn1cbiJdfQ==
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Floating or docked chat panel. Renders the header, message list, input,
|
|
3
|
-
* and footer. Sits inside `<bloonio-chat-launcher>` for the floating
|
|
4
|
-
* variant, or stand-alone for `mode="docked"`/`mode="inline"`.
|
|
5
|
-
*/
|
|
6
|
-
import { ChangeDetectionStrategy, Component, computed, inject, Input, signal, ViewChild, } from '@angular/core';
|
|
7
|
-
import { BloonioChatService } from '../bloonio-chat.service';
|
|
8
|
-
import { InputBarComponent } from './input-bar.component';
|
|
9
|
-
import { MessageListComponent } from './message-list.component';
|
|
10
|
-
import { QuickRepliesComponent } from './quick-replies.component';
|
|
11
|
-
import * as i0 from "@angular/core";
|
|
12
|
-
export class BloonioChatPanelComponent {
|
|
13
|
-
/** floating = paired with launcher (default). docked/inline = always-on
|
|
14
|
-
* inside the page flow (use for SSR landing pages or sidebar layouts). */
|
|
15
|
-
mode = 'floating';
|
|
16
|
-
chat = inject(BloonioChatService);
|
|
17
|
-
messagesRef;
|
|
18
|
-
lastScrollLen = signal(-1);
|
|
19
|
-
position = computed(() => this.chat.bootstrap()?.widget_position ?? 'right');
|
|
20
|
-
visible = computed(() => {
|
|
21
|
-
// Floating mode: only when service.open() is true.
|
|
22
|
-
// Docked/inline: always rendered if bootstrap is loaded.
|
|
23
|
-
if (this.mode === 'floating')
|
|
24
|
-
return this.chat.open();
|
|
25
|
-
return !!this.chat.bootstrap();
|
|
26
|
-
});
|
|
27
|
-
themeStyle = computed(() => {
|
|
28
|
-
const color = this.chat.bootstrap()?.branding_primary_color;
|
|
29
|
-
return color ? `--bch-primary: ${color}` : '';
|
|
30
|
-
});
|
|
31
|
-
displayName = computed(() => this.chat.bootstrap()?.branding_display_name
|
|
32
|
-
?? this.chat.bootstrap()?.tenant_name
|
|
33
|
-
?? 'Support');
|
|
34
|
-
logoUrl = computed(() => this.chat.bootstrap()?.branding_logo_url ?? null);
|
|
35
|
-
initials = computed(() => this.displayName()
|
|
36
|
-
.split(/\s+/)
|
|
37
|
-
.slice(0, 2)
|
|
38
|
-
.map((w) => w[0]?.toUpperCase() ?? '')
|
|
39
|
-
.join('') || '?');
|
|
40
|
-
statusText = computed(() => {
|
|
41
|
-
const b = this.chat.bootstrap();
|
|
42
|
-
if (!b)
|
|
43
|
-
return '';
|
|
44
|
-
return b.is_online ? b.online_status_text : b.offline_status_text;
|
|
45
|
-
});
|
|
46
|
-
isOnlineStr = computed(() => String(this.chat.bootstrap()?.is_online ?? false));
|
|
47
|
-
visibleMessages = computed(() => {
|
|
48
|
-
const b = this.chat.bootstrap();
|
|
49
|
-
const messages = this.chat.messages();
|
|
50
|
-
if (b && !b.is_online && messages.length === 0) {
|
|
51
|
-
return [
|
|
52
|
-
{
|
|
53
|
-
id: 'sys_offline',
|
|
54
|
-
from: 'system',
|
|
55
|
-
content: b.offline_message,
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
|
-
}
|
|
59
|
-
return messages;
|
|
60
|
-
});
|
|
61
|
-
ngAfterViewChecked() {
|
|
62
|
-
// Scroll-to-bottom whenever message count changes. We compare against a
|
|
63
|
-
// stamped length so we don't churn when only typing/status updates fire.
|
|
64
|
-
const el = this.messagesRef?.nativeElement;
|
|
65
|
-
if (!el)
|
|
66
|
-
return;
|
|
67
|
-
const len = this.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);
|
|
68
|
-
if (len !== this.lastScrollLen()) {
|
|
69
|
-
el.scrollTop = el.scrollHeight;
|
|
70
|
-
this.lastScrollLen.set(len);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
onClose() {
|
|
74
|
-
this.chat.closePanel();
|
|
75
|
-
}
|
|
76
|
-
onSend(content) {
|
|
77
|
-
this.chat.sendMessage(content);
|
|
78
|
-
}
|
|
79
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
80
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: BloonioChatPanelComponent, isStandalone: true, selector: "bloonio-chat-panel", inputs: { mode: "mode" }, viewQueries: [{ propertyName: "messagesRef", first: true, predicate: ["messages"], descendants: true }], ngImport: i0, template: `
|
|
81
|
-
<div
|
|
82
|
-
class="bch-root"
|
|
83
|
-
[class.bch-open]="visible()"
|
|
84
|
-
[attr.data-position]="position()"
|
|
85
|
-
[attr.data-mode]="mode"
|
|
86
|
-
>
|
|
87
|
-
<div class="bch-panel" [style]="themeStyle()">
|
|
88
|
-
<header class="bch-header">
|
|
89
|
-
<div class="bch-header-avatar">
|
|
90
|
-
@if (logoUrl()) {
|
|
91
|
-
<img [src]="logoUrl()" alt="" />
|
|
92
|
-
} @else {
|
|
93
|
-
<span style="font-weight:600;font-size:15px">{{ initials() }}</span>
|
|
94
|
-
}
|
|
95
|
-
</div>
|
|
96
|
-
<div class="bch-header-title">
|
|
97
|
-
<div class="bch-header-name">Chat with {{ displayName() }}</div>
|
|
98
|
-
<div class="bch-header-status">
|
|
99
|
-
<span class="bch-header-status-dot" [attr.data-online]="isOnlineStr()"></span>
|
|
100
|
-
{{ statusText() }}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
<button
|
|
104
|
-
type="button"
|
|
105
|
-
class="bch-header-close"
|
|
106
|
-
aria-label="Close"
|
|
107
|
-
(click)="onClose()"
|
|
108
|
-
>
|
|
109
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
110
|
-
stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
|
|
111
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
112
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
113
|
-
</svg>
|
|
114
|
-
</button>
|
|
115
|
-
</header>
|
|
116
|
-
|
|
117
|
-
<div class="bch-messages" #messages>
|
|
118
|
-
<bloonio-chat-message-list
|
|
119
|
-
[messages]="visibleMessages()"
|
|
120
|
-
[botTyping]="chat.botTyping()"
|
|
121
|
-
/>
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<bloonio-chat-quick-replies />
|
|
125
|
-
|
|
126
|
-
<bloonio-chat-input-bar
|
|
127
|
-
[disabled]="chat.connecting()"
|
|
128
|
-
(send)="onSend($event)"
|
|
129
|
-
/>
|
|
130
|
-
|
|
131
|
-
<div class="bch-footer">
|
|
132
|
-
Powered by
|
|
133
|
-
<a href="https://bloonio.com" target="_blank" rel="noopener noreferrer">Bloonio</a>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
`, isInline: true, dependencies: [{ kind: "component", type: InputBarComponent, selector: "bloonio-chat-input-bar", inputs: ["disabled"], outputs: ["send"] }, { kind: "component", type: MessageListComponent, selector: "bloonio-chat-message-list", inputs: ["messages", "botTyping"] }, { kind: "component", type: QuickRepliesComponent, selector: "bloonio-chat-quick-replies" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
138
|
-
}
|
|
139
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatPanelComponent, decorators: [{
|
|
140
|
-
type: Component,
|
|
141
|
-
args: [{
|
|
142
|
-
selector: 'bloonio-chat-panel',
|
|
143
|
-
standalone: true,
|
|
144
|
-
imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],
|
|
145
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
146
|
-
template: `
|
|
147
|
-
<div
|
|
148
|
-
class="bch-root"
|
|
149
|
-
[class.bch-open]="visible()"
|
|
150
|
-
[attr.data-position]="position()"
|
|
151
|
-
[attr.data-mode]="mode"
|
|
152
|
-
>
|
|
153
|
-
<div class="bch-panel" [style]="themeStyle()">
|
|
154
|
-
<header class="bch-header">
|
|
155
|
-
<div class="bch-header-avatar">
|
|
156
|
-
@if (logoUrl()) {
|
|
157
|
-
<img [src]="logoUrl()" alt="" />
|
|
158
|
-
} @else {
|
|
159
|
-
<span style="font-weight:600;font-size:15px">{{ initials() }}</span>
|
|
160
|
-
}
|
|
161
|
-
</div>
|
|
162
|
-
<div class="bch-header-title">
|
|
163
|
-
<div class="bch-header-name">Chat with {{ displayName() }}</div>
|
|
164
|
-
<div class="bch-header-status">
|
|
165
|
-
<span class="bch-header-status-dot" [attr.data-online]="isOnlineStr()"></span>
|
|
166
|
-
{{ statusText() }}
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
<button
|
|
170
|
-
type="button"
|
|
171
|
-
class="bch-header-close"
|
|
172
|
-
aria-label="Close"
|
|
173
|
-
(click)="onClose()"
|
|
174
|
-
>
|
|
175
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
176
|
-
stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
|
|
177
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
178
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
179
|
-
</svg>
|
|
180
|
-
</button>
|
|
181
|
-
</header>
|
|
182
|
-
|
|
183
|
-
<div class="bch-messages" #messages>
|
|
184
|
-
<bloonio-chat-message-list
|
|
185
|
-
[messages]="visibleMessages()"
|
|
186
|
-
[botTyping]="chat.botTyping()"
|
|
187
|
-
/>
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
<bloonio-chat-quick-replies />
|
|
191
|
-
|
|
192
|
-
<bloonio-chat-input-bar
|
|
193
|
-
[disabled]="chat.connecting()"
|
|
194
|
-
(send)="onSend($event)"
|
|
195
|
-
/>
|
|
196
|
-
|
|
197
|
-
<div class="bch-footer">
|
|
198
|
-
Powered by
|
|
199
|
-
<a href="https://bloonio.com" target="_blank" rel="noopener noreferrer">Bloonio</a>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
`,
|
|
204
|
-
}]
|
|
205
|
-
}], propDecorators: { mode: [{
|
|
206
|
-
type: Input
|
|
207
|
-
}], messagesRef: [{
|
|
208
|
-
type: ViewChild,
|
|
209
|
-
args: ['messages']
|
|
210
|
-
}] } });
|
|
211
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFuZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL3BhbmVsLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBQ0gsT0FBTyxFQUNMLHVCQUF1QixFQUN2QixTQUFTLEVBQ1QsUUFBUSxFQUNSLE1BQU0sRUFDTixLQUFLLEVBQ0wsTUFBTSxFQUdOLFNBQVMsR0FDVixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUFvRWxFLE1BQU0sT0FBTyx5QkFBeUI7SUFDcEM7K0VBQzJFO0lBQ2xFLElBQUksR0FBa0IsVUFBVSxDQUFDO0lBRXZCLElBQUksR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUV0QixXQUFXLENBQThCO0lBQ2hFLGFBQWEsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUVoQixRQUFRLEdBQUcsUUFBUSxDQUNwQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFLGVBQWUsSUFBSSxPQUFPLENBQ3hELENBQUM7SUFFaUIsT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDekMsbURBQW1EO1FBQ25ELHlEQUF5RDtRQUN6RCxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssVUFBVTtZQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN0RCxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNB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LEVBQUUsRUFBRSxDQUFDO1lBQ2pDLEVBQUUsQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQztZQUMvQixJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM5QixDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU87UUFDTCxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxNQUFNLENBQUMsT0FBZTtRQUNwQixJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNqQyxDQUFDO3dHQXZGVSx5QkFBeUI7NEZBQXpCLHlCQUF5QixpTkEzRDFCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5RFQsNERBM0RTLGlCQUFpQiw0R0FBRSxvQkFBb0IseUdBQUUscUJBQXFCOzs0RkE2RDdELHlCQUF5QjtrQkFoRXJDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLG9CQUFvQjtvQkFDOUIsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRSxDQUFDLGlCQUFpQixFQUFFLG9CQUFvQixFQUFFLHFCQUFxQixDQUFDO29CQUN6RSxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5RFQ7aUJBQ0Y7OEJBSVUsSUFBSTtzQkFBWixLQUFLO2dCQUl5QixXQUFXO3NCQUF6QyxTQUFTO3VCQUFDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZsb2F0aW5nIG9yIGRvY2tlZCBjaGF0IHBhbmVsLiBSZW5kZXJzIHRoZSBoZWFkZXIsIG1lc3NhZ2UgbGlzdCwgaW5wdXQsXG4gKiBhbmQgZm9vdGVyLiBTaXRzIGluc2lkZSBgPGJsb29uaW8tY2hhdC1sYXVuY2hlcj5gIGZvciB0aGUgZmxvYXRpbmdcbiAqIHZhcmlhbnQsIG9yIHN0YW5kLWFsb25lIGZvciBgbW9kZT1cImRvY2tlZFwiYC9gbW9kZT1cImlubGluZVwiYC5cbiAqL1xuaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgY29tcHV0ZWQsXG4gIGluamVjdCxcbiAgSW5wdXQsXG4gIHNpZ25hbCxcbiAgdHlwZSBBZnRlclZpZXdDaGVja2VkLFxuICBFbGVtZW50UmVmLFxuICBWaWV3Q2hpbGQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBCbG9vbmlvQ2hhdFNlcnZpY2UgfSBmcm9tICcuLi9ibG9vbmlvLWNoYXQuc2VydmljZSc7XG5pbXBvcnQgeyBJbnB1dEJhckNvbXBvbmVudCB9IGZyb20gJy4vaW5wdXQtYmFyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBNZXNzYWdlTGlzdENvbXBvbmVudCB9IGZyb20gJy4vbWVzc2FnZS1saXN0LmNvbXBvbmVudCc7XG5pbXBvcnQgeyBRdWlja1JlcGxpZXNDb21wb25lbnQgfSBmcm9tICcuL3F1aWNrLXJlcGxpZXMuY29tcG9uZW50JztcblxuZXhwb3J0IHR5cGUgQ2hhdFBhbmVsTW9kZSA9ICdmbG9hdGluZycgfCAnZG9ja2VkJyB8ICdpbmxpbmUnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdibG9vbmlvLWNoYXQtcGFuZWwnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbSW5wdXRCYXJDb21wb25lbnQsIE1lc3NhZ2VMaXN0Q29tcG9uZW50LCBRdWlja1JlcGxpZXNDb21wb25lbnRdLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2XG4gICAgICBjbGFzcz1cImJjaC1yb290XCJcbiAgICAgIFtjbGFzcy5iY2gtb3Blbl09XCJ2aXNpYmxlKClcIlxuICAgICAgW2F0dHIuZGF0YS1wb3NpdGlvbl09XCJwb3NpdGlvbigpXCJcbiAgICAgIFthdHRyLmRhdGEtbW9kZV09XCJtb2RlXCJcbiAgICA+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJiY2gtcGFuZWxcIiBbc3R5bGVdPVwidGhlbWVTdHlsZSgpXCI+XG4gICAgICAgICAgPGhlYWRlciBjbGFzcz1cImJjaC1oZWFkZXJcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJiY2gtaGVhZGVyLWF2YXRhclwiPlxuICAgICAgICAgICAgICBAaWYgKGxvZ29VcmwoKSkge1xuICAgICAgICAgICAgICAgIDxpbWcgW3NyY109XCJsb2dvVXJsKClcIiBhbHQ9XCJcIiAvPlxuICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBzdHlsZT1cImZvbnQtd2VpZ2h0OjYwMDtmb250LXNpemU6MTVweFwiPnt7IGluaXRpYWxzKCkgfX08L3NwYW4+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImJjaC1oZWFkZXItdGl0bGVcIj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImJjaC1oZWFkZXItbmFtZVwiPkNoYXQgd2l0aCB7eyBkaXNwbGF5TmFtZSgpIH19PC9kaXY+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJiY2gtaGVhZGVyLXN0YXR1c1wiPlxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiYmNoLWhlYWRlci1zdGF0dXMtZG90XCIgW2F0dHIuZGF0YS1vbmxpbmVdPVwiaXNPbmxpbmVTdHIoKVwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICB7eyBzdGF0dXNUZXh0KCkgfX1cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIGNsYXNzPVwiYmNoLWhlYWRlci1jbG9zZVwiXG4gICAgICAgICAgICAgIGFyaWEtbGFiZWw9XCJDbG9zZVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJvbkNsb3NlKClcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICA8c3ZnIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiXG4gICAgICAgICAgICAgICAgICAgc3Ryb2tlLXdpZHRoPVwiMi40XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCI+XG4gICAgICAgICAgICAgICAgPGxpbmUgeDE9XCIxOFwiIHkxPVwiNlwiIHgyPVwiNlwiIHkyPVwiMThcIiAvPlxuICAgICAgICAgICAgICAgIDxsaW5lIHgxPVwiNlwiIHkxPVwiNlwiIHgyPVwiMThcIiB5Mj1cIjE4XCIgLz5cbiAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICA8L2hlYWRlcj5cblxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJiY2gtbWVzc2FnZXNcIiAjbWVzc2FnZXM+XG4gICAgICAgICAgICA8Ymxvb25pby1jaGF0LW1lc3NhZ2UtbGlzdFxuICAgICAgICAgICAgICBbbWVzc2FnZXNdPVwidmlzaWJsZU1lc3NhZ2VzKClcIlxuICAgICAgICAgICAgICBbYm90VHlwaW5nXT1cImNoYXQuYm90VHlwaW5nKClcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIDxibG9vbmlvLWNoYXQtcXVpY2stcmVwbGllcyAvPlxuXG4gICAgICAgICAgPGJsb29uaW8tY2hhdC1pbnB1dC1iYXJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJjaGF0LmNvbm5lY3RpbmcoKVwiXG4gICAgICAgICAgICAoc2VuZCk9XCJvblNlbmQoJGV2ZW50KVwiXG4gICAgICAgICAgLz5cblxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJiY2gtZm9vdGVyXCI+XG4gICAgICAgICAgICBQb3dlcmVkIGJ5XG4gICAgICAgICAgICA8YSBocmVmPVwiaHR0cHM6Ly9ibG9vbmlvLmNvbVwiIHRhcmdldD1cIl9ibGFua1wiIHJlbD1cIm5vb3BlbmVyIG5vcmVmZXJyZXJcIj5CbG9vbmlvPC9hPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBCbG9vbmlvQ2hhdFBhbmVsQ29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3Q2hlY2tlZCB7XG4gIC8qKiBmbG9hdGluZyA9IHBhaXJlZCB3aXRoIGxhdW5jaGVyIChkZWZhdWx0KS4gZG9ja2VkL2lubGluZSA9IGFsd2F5cy1vblxuICAgKiAgaW5zaWRlIHRoZSBwYWdlIGZsb3cgKHVzZSBmb3IgU1NSIGxhbmRpbmcgcGFnZXMgb3Igc2lkZWJhciBsYXlvdXRzKS4gKi9cbiAgQElucHV0KCkgbW9kZTogQ2hhdFBhbmVsTW9kZSA9ICdmbG9hdGluZyc7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNoYXQgPSBpbmplY3QoQmxvb25pb0NoYXRTZXJ2aWNlKTtcblxuICBAVmlld0NoaWxkKCdtZXNzYWdlcycpIHByaXZhdGUgbWVzc2FnZXNSZWY/OiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcbiAgcHJpdmF0ZSBsYXN0U2Nyb2xsTGVuID0gc2lnbmFsKC0xKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcG9zaXRpb24gPSBjb21wdXRlZChcbiAgICAoKSA9PiB0aGlzLmNoYXQuYm9vdHN0cmFwKCk/LndpZGdldF9wb3NpdGlvbiA/PyAncmlnaHQnLFxuICApO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSB2aXNpYmxlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIC8vIEZsb2F0aW5nIG1vZGU6IG9ubHkgd2hlbiBzZXJ2aWNlLm9wZW4oKSBpcyB0cnVlLlxuICAgIC8vIERvY2tlZC9pbmxpbmU6IGFsd2F5cyByZW5kZXJlZCBpZiBib290c3RyYXAgaXMgbG9hZGVkLlxuICAgIGlmICh0aGlzLm1vZGUgPT09ICdmbG9hdGluZycpIHJldHVybiB0aGlzLmNoYXQub3BlbigpO1xuICAgIHJldHVybiAhIXRoaXMuY2hhdC5ib290c3RyYXAoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRoZW1lU3R5bGUgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgY29sb3IgPSB0aGlzLmNoYXQuYm9vdHN0cmFwKCk/LmJyYW5kaW5nX3ByaW1hcnlfY29sb3I7XG4gICAgcmV0dXJuIGNvbG9yID8gYC0tYmNoLXByaW1hcnk6ICR7Y29sb3J9YCA6ICcnO1xuICB9KTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZGlzcGxheU5hbWUgPSBjb21wdXRlZChcbiAgICAoKSA9PiB0aGlzLmNoYXQuYm9vdHN0cmFwKCk/LmJyYW5kaW5nX2Rpc3BsYXlfbmFtZVxuICAgICAgPz8gdGhpcy5jaGF0LmJvb3RzdHJhcCgpPy50ZW5hbnRfbmFtZVxuICAgICAgPz8gJ1N1cHBvcnQnLFxuICApO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBsb2dvVXJsID0gY29tcHV0ZWQoXG4gICAgKCkgPT4gdGhpcy5jaGF0LmJvb3RzdHJhcCgpPy5icmFuZGluZ19sb2dvX3VybCA/PyBudWxsLFxuICApO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBpbml0aWFscyA9IGNvbXB1dGVkKCgpID0+XG4gICAgdGhpcy5kaXNwbGF5TmFtZSgpXG4gICAgICAuc3BsaXQoL1xccysvKVxuICAgICAgLnNsaWNlKDAsIDIpXG4gICAgICAubWFwKCh3KSA9PiB3WzBdPy50b1VwcGVyQ2FzZSgpID8/ICcnKVxuICAgICAgLmpvaW4oJycpIHx8ICc/JyxcbiAgKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RhdHVzVGV4dCA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBiID0gdGhpcy5jaGF0LmJvb3RzdHJhcCgpO1xuICAgIGlmICghYikgcmV0dXJuICcnO1xuICAgIHJldHVybiBiLmlzX29ubGluZSA/IGIub25saW5lX3N0YXR1c190ZXh0IDogYi5vZmZsaW5lX3N0YXR1c190ZXh0O1xuICB9KTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNPbmxpbmVTdHIgPSBjb21wdXRlZChcbiAgICAoKSA9PiBTdHJpbmcodGhpcy5jaGF0LmJvb3RzdHJhcCgpPy5pc19vbmxpbmUgPz8gZmFsc2UpLFxuICApO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSB2aXNpYmxlTWVzc2FnZXMgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgYiA9IHRoaXMuY2hhdC5ib290c3RyYXAoKTtcbiAgICBjb25zdCBtZXNzYWdlcyA9IHRoaXMuY2hhdC5tZXNzYWdlcygpO1xuICAgIGlmIChiICYmICFiLmlzX29ubGluZSAmJiBtZXNzYWdlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBbXG4gICAgICAgIHtcbiAgICAgICAgICBpZDogJ3N5c19vZmZsaW5lJyxcbiAgICAgICAgICBmcm9tOiAnc3lzdGVtJyBhcyBjb25zdCxcbiAgICAgICAgICBjb250ZW50OiBiLm9mZmxpbmVfbWVzc2FnZSxcbiAgICAgICAgfSxcbiAgICAgIF07XG4gICAgfVxuICAgIHJldHVybiBtZXNzYWdlcztcbiAgfSk7XG5cbiAgbmdBZnRlclZpZXdDaGVja2VkKCk6IHZvaWQge1xuICAgIC8vIFNjcm9sbC10by1ib3R0b20gd2hlbmV2ZXIgbWVzc2FnZSBjb3VudCBjaGFuZ2VzLiBXZSBjb21wYXJlIGFnYWluc3QgYVxuICAgIC8vIHN0YW1wZWQgbGVuZ3RoIHNvIHdlIGRvbid0IGNodXJuIHdoZW4gb25seSB0eXBpbmcvc3RhdHVzIHVwZGF0ZXMgZmlyZS5cbiAgICBjb25zdCBlbCA9IHRoaXMubWVzc2FnZXNSZWY/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgaWYgKCFlbCkgcmV0dXJuO1xuICAgIGNvbnN0IGxlbiA9IHRoaXMudmlzaWJsZU1lc3NhZ2VzKCkubGVuZ3RoICsgKHRoaXMuY2hhdC5ib3RUeXBpbmcoKSA/IDEgOiAwKTtcbiAgICBpZiAobGVuICE9PSB0aGlzLmxhc3RTY3JvbGxMZW4oKSkge1xuICAgICAgZWwuc2Nyb2xsVG9wID0gZWwuc2Nyb2xsSGVpZ2h0O1xuICAgICAgdGhpcy5sYXN0U2Nyb2xsTGVuLnNldChsZW4pO1xuICAgIH1cbiAgfVxuXG4gIG9uQ2xvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5jaGF0LmNsb3NlUGFuZWwoKTtcbiAgfVxuXG4gIG9uU2VuZChjb250ZW50OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmNoYXQuc2VuZE1lc3NhZ2UoY29udGVudCk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `<bloonio-chat-quick-replies>` — tenant-configured opening prompt chips,
|
|
3
|
-
* shown under the welcome message until the visitor sends their first
|
|
4
|
-
* message. Tapping a chip sends its text as that first message.
|
|
5
|
-
*
|
|
6
|
-
* Driven by `BloonioChatService` (reads `bootstrap().quick_replies` +
|
|
7
|
-
* `messages()`), so it self-hides once the conversation starts. Used inside
|
|
8
|
-
* both the floating panel and the headless thread.
|
|
9
|
-
*/
|
|
10
|
-
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
|
|
11
|
-
import { BloonioChatService } from '../bloonio-chat.service';
|
|
12
|
-
import * as i0 from "@angular/core";
|
|
13
|
-
export class QuickRepliesComponent {
|
|
14
|
-
chat = inject(BloonioChatService);
|
|
15
|
-
/** Show the configured chips only until the visitor's first message. */
|
|
16
|
-
replies = computed(() => {
|
|
17
|
-
const bootstrap = this.chat.bootstrap();
|
|
18
|
-
if (!bootstrap || bootstrap.quick_replies.length === 0)
|
|
19
|
-
return [];
|
|
20
|
-
const visitorSpoke = this.chat.messages().some((m) => m.from === 'visitor');
|
|
21
|
-
return visitorSpoke ? [] : bootstrap.quick_replies;
|
|
22
|
-
});
|
|
23
|
-
pick(text) {
|
|
24
|
-
this.chat.sendMessage(text);
|
|
25
|
-
}
|
|
26
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: QuickRepliesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
27
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: QuickRepliesComponent, isStandalone: true, selector: "bloonio-chat-quick-replies", ngImport: i0, template: `
|
|
28
|
-
@if (replies().length) {
|
|
29
|
-
<div class="bch-quick-replies" role="group" aria-label="Suggested replies">
|
|
30
|
-
@for (reply of replies(); track reply) {
|
|
31
|
-
<button type="button" class="bch-quick-reply" (click)="pick(reply)">
|
|
32
|
-
{{ reply }}
|
|
33
|
-
</button>
|
|
34
|
-
}
|
|
35
|
-
</div>
|
|
36
|
-
}
|
|
37
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
38
|
-
}
|
|
39
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: QuickRepliesComponent, decorators: [{
|
|
40
|
-
type: Component,
|
|
41
|
-
args: [{
|
|
42
|
-
selector: 'bloonio-chat-quick-replies',
|
|
43
|
-
standalone: true,
|
|
44
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
45
|
-
template: `
|
|
46
|
-
@if (replies().length) {
|
|
47
|
-
<div class="bch-quick-replies" role="group" aria-label="Suggested replies">
|
|
48
|
-
@for (reply of replies(); track reply) {
|
|
49
|
-
<button type="button" class="bch-quick-reply" (click)="pick(reply)">
|
|
50
|
-
{{ reply }}
|
|
51
|
-
</button>
|
|
52
|
-
}
|
|
53
|
-
</div>
|
|
54
|
-
}
|
|
55
|
-
`,
|
|
56
|
-
}]
|
|
57
|
-
}] });
|
|
58
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVpY2stcmVwbGllcy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvcXVpY2stcmVwbGllcy5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFDSCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFckYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7O0FBa0I3RCxNQUFNLE9BQU8scUJBQXFCO0lBQ2IsSUFBSSxHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBRXJELHdFQUF3RTtJQUNyRCxPQUFPLEdBQUcsUUFBUSxDQUFXLEdBQUcsRUFBRTtRQUNuRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3hDLElBQUksQ0FBQyxTQUFTLElBQUksU0FBUyxDQUFDLGFBQWEsQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUFFLE9BQU8sRUFBRSxDQUFDO1FBQ2xFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLFNBQVMsQ0FBQyxDQUFDO1FBQzVFLE9BQU8sWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUM7SUFDckQsQ0FBQyxDQUFDLENBQUM7SUFFTyxJQUFJLENBQUMsSUFBWTtRQUN6QixJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM5QixDQUFDO3dHQWJVLHFCQUFxQjs0RkFBckIscUJBQXFCLHNGQVp0Qjs7Ozs7Ozs7OztHQVVUOzs0RkFFVSxxQkFBcUI7a0JBaEJqQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSw0QkFBNEI7b0JBQ3RDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7O0dBVVQ7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGA8Ymxvb25pby1jaGF0LXF1aWNrLXJlcGxpZXM+YCDigJQgdGVuYW50LWNvbmZpZ3VyZWQgb3BlbmluZyBwcm9tcHQgY2hpcHMsXG4gKiBzaG93biB1bmRlciB0aGUgd2VsY29tZSBtZXNzYWdlIHVudGlsIHRoZSB2aXNpdG9yIHNlbmRzIHRoZWlyIGZpcnN0XG4gKiBtZXNzYWdlLiBUYXBwaW5nIGEgY2hpcCBzZW5kcyBpdHMgdGV4dCBhcyB0aGF0IGZpcnN0IG1lc3NhZ2UuXG4gKlxuICogRHJpdmVuIGJ5IGBCbG9vbmlvQ2hhdFNlcnZpY2VgIChyZWFkcyBgYm9vdHN0cmFwKCkucXVpY2tfcmVwbGllc2AgK1xuICogYG1lc3NhZ2VzKClgKSwgc28gaXQgc2VsZi1oaWRlcyBvbmNlIHRoZSBjb252ZXJzYXRpb24gc3RhcnRzLiBVc2VkIGluc2lkZVxuICogYm90aCB0aGUgZmxvYXRpbmcgcGFuZWwgYW5kIHRoZSBoZWFkbGVzcyB0aHJlYWQuXG4gKi9cbmltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIGNvbXB1dGVkLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgQmxvb25pb0NoYXRTZXJ2aWNlIH0gZnJvbSAnLi4vYmxvb25pby1jaGF0LnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdibG9vbmlvLWNoYXQtcXVpY2stcmVwbGllcycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICB0ZW1wbGF0ZTogYFxuICAgIEBpZiAocmVwbGllcygpLmxlbmd0aCkge1xuICAgICAgPGRpdiBjbGFzcz1cImJjaC1xdWljay1yZXBsaWVzXCIgcm9sZT1cImdyb3VwXCIgYXJpYS1sYWJlbD1cIlN1Z2dlc3RlZCByZXBsaWVzXCI+XG4gICAgICAgIEBmb3IgKHJlcGx5IG9mIHJlcGxpZXMoKTsgdHJhY2sgcmVwbHkpIHtcbiAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cImJjaC1xdWljay1yZXBseVwiIChjbGljayk9XCJwaWNrKHJlcGx5KVwiPlxuICAgICAgICAgICAge3sgcmVwbHkgfX1cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBRdWlja1JlcGxpZXNDb21wb25lbnQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2hhdCA9IGluamVjdChCbG9vbmlvQ2hhdFNlcnZpY2UpO1xuXG4gIC8qKiBTaG93IHRoZSBjb25maWd1cmVkIGNoaXBzIG9ubHkgdW50aWwgdGhlIHZpc2l0b3IncyBmaXJzdCBtZXNzYWdlLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcmVwbGllcyA9IGNvbXB1dGVkPHN0cmluZ1tdPigoKSA9PiB7XG4gICAgY29uc3QgYm9vdHN0cmFwID0gdGhpcy5jaGF0LmJvb3RzdHJhcCgpO1xuICAgIGlmICghYm9vdHN0cmFwIHx8IGJvb3RzdHJhcC5xdWlja19yZXBsaWVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xuICAgIGNvbnN0IHZpc2l0b3JTcG9rZSA9IHRoaXMuY2hhdC5tZXNzYWdlcygpLnNvbWUoKG0pID0+IG0uZnJvbSA9PT0gJ3Zpc2l0b3InKTtcbiAgICByZXR1cm4gdmlzaXRvclNwb2tlID8gW10gOiBib290c3RyYXAucXVpY2tfcmVwbGllcztcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHBpY2sodGV4dDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5jaGF0LnNlbmRNZXNzYWdlKHRleHQpO1xuICB9XG59XG4iXX0=
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `<bloonio-chat-thread>` — headless variant of the panel. Renders just
|
|
3
|
-
* the message log + input, no header, no floating chrome. Used when the
|
|
4
|
-
* tenant wants to embed the chat *inside* their own layout (e.g. inside
|
|
5
|
-
* a support modal, a sidebar, a help center page).
|
|
6
|
-
*
|
|
7
|
-
* Theming and behaviour are otherwise identical — the same
|
|
8
|
-
* `BloonioChatService` drives state, so a thread and a panel side-by-side
|
|
9
|
-
* stay in sync.
|
|
10
|
-
*/
|
|
11
|
-
import { ChangeDetectionStrategy, Component, computed, inject, ViewChild, signal, } from '@angular/core';
|
|
12
|
-
import { BloonioChatService } from '../bloonio-chat.service';
|
|
13
|
-
import { InputBarComponent } from './input-bar.component';
|
|
14
|
-
import { MessageListComponent } from './message-list.component';
|
|
15
|
-
import { QuickRepliesComponent } from './quick-replies.component';
|
|
16
|
-
import * as i0 from "@angular/core";
|
|
17
|
-
export class BloonioChatThreadComponent {
|
|
18
|
-
chat = inject(BloonioChatService);
|
|
19
|
-
messagesRef;
|
|
20
|
-
lastScrollLen = signal(-1);
|
|
21
|
-
themeStyle = computed(() => {
|
|
22
|
-
const color = this.chat.bootstrap()?.branding_primary_color;
|
|
23
|
-
return color ? `--bch-primary: ${color}` : '';
|
|
24
|
-
});
|
|
25
|
-
visibleMessages = computed(() => {
|
|
26
|
-
const b = this.chat.bootstrap();
|
|
27
|
-
const messages = this.chat.messages();
|
|
28
|
-
if (b && !b.is_online && messages.length === 0) {
|
|
29
|
-
return [
|
|
30
|
-
{
|
|
31
|
-
id: 'sys_offline',
|
|
32
|
-
from: 'system',
|
|
33
|
-
content: b.offline_message,
|
|
34
|
-
},
|
|
35
|
-
];
|
|
36
|
-
}
|
|
37
|
-
return messages;
|
|
38
|
-
});
|
|
39
|
-
ngAfterViewChecked() {
|
|
40
|
-
const el = this.messagesRef?.nativeElement;
|
|
41
|
-
if (!el)
|
|
42
|
-
return;
|
|
43
|
-
const len = this.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);
|
|
44
|
-
if (len !== this.lastScrollLen()) {
|
|
45
|
-
el.scrollTop = el.scrollHeight;
|
|
46
|
-
this.lastScrollLen.set(len);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/** Convenience: thread is meant to be always-on, so it opens the
|
|
50
|
-
* service-level session as soon as it's instantiated.
|
|
51
|
-
* (Floating panel waits for the launcher click.) */
|
|
52
|
-
constructor() {
|
|
53
|
-
void this.chat.openPanel();
|
|
54
|
-
}
|
|
55
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
56
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: BloonioChatThreadComponent, isStandalone: true, selector: "bloonio-chat-thread", viewQueries: [{ propertyName: "messagesRef", first: true, predicate: ["messages"], descendants: true }], ngImport: i0, template: `
|
|
57
|
-
<div class="bch-root" data-mode="inline" [style]="themeStyle()">
|
|
58
|
-
<div class="bch-panel">
|
|
59
|
-
<div class="bch-messages" #messages>
|
|
60
|
-
<bloonio-chat-message-list
|
|
61
|
-
[messages]="visibleMessages()"
|
|
62
|
-
[botTyping]="chat.botTyping()"
|
|
63
|
-
/>
|
|
64
|
-
</div>
|
|
65
|
-
<bloonio-chat-quick-replies />
|
|
66
|
-
<bloonio-chat-input-bar
|
|
67
|
-
[disabled]="chat.connecting()"
|
|
68
|
-
(send)="chat.sendMessage($event)"
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
`, isInline: true, dependencies: [{ kind: "component", type: InputBarComponent, selector: "bloonio-chat-input-bar", inputs: ["disabled"], outputs: ["send"] }, { kind: "component", type: MessageListComponent, selector: "bloonio-chat-message-list", inputs: ["messages", "botTyping"] }, { kind: "component", type: QuickRepliesComponent, selector: "bloonio-chat-quick-replies" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
73
|
-
}
|
|
74
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatThreadComponent, decorators: [{
|
|
75
|
-
type: Component,
|
|
76
|
-
args: [{
|
|
77
|
-
selector: 'bloonio-chat-thread',
|
|
78
|
-
standalone: true,
|
|
79
|
-
imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],
|
|
80
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
81
|
-
template: `
|
|
82
|
-
<div class="bch-root" data-mode="inline" [style]="themeStyle()">
|
|
83
|
-
<div class="bch-panel">
|
|
84
|
-
<div class="bch-messages" #messages>
|
|
85
|
-
<bloonio-chat-message-list
|
|
86
|
-
[messages]="visibleMessages()"
|
|
87
|
-
[botTyping]="chat.botTyping()"
|
|
88
|
-
/>
|
|
89
|
-
</div>
|
|
90
|
-
<bloonio-chat-quick-replies />
|
|
91
|
-
<bloonio-chat-input-bar
|
|
92
|
-
[disabled]="chat.connecting()"
|
|
93
|
-
(send)="chat.sendMessage($event)"
|
|
94
|
-
/>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
`,
|
|
98
|
-
}]
|
|
99
|
-
}], ctorParameters: () => [], propDecorators: { messagesRef: [{
|
|
100
|
-
type: ViewChild,
|
|
101
|
-
args: ['messages']
|
|
102
|
-
}] } });
|
|
103
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyZWFkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy90aHJlYWQuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUNILE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsU0FBUyxFQUNULFFBQVEsRUFDUixNQUFNLEVBR04sU0FBUyxFQUNULE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUF5QmxFLE1BQU0sT0FBTywwQkFBMEI7SUFDbEIsSUFBSSxHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBRXRCLFdBQVcsQ0FBOEI7SUFDaEUsYUFBYSxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRWhCLFVBQVUsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQzVDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEVBQUUsc0JBQXNCLENBQUM7UUFDNUQsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ2hELENBQUMsQ0FBQyxDQUFDO0lBRWdCLGVBQWUsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ2pELE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDaEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN0QyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLElBQUksUUFBUSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMvQyxPQUFPO2dCQUNMO29CQUNFLEVBQUUsRUFBRSxhQUFhO29CQUNqQixJQUFJLEVBQUUsUUFBaUI7b0JBQ3ZCLE9BQU8sRUFBRSxDQUFDLENBQUMsZUFBZTtpQkFDM0I7YUFDRixDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUMsQ0FBQyxDQUFDO0lBRUgsa0JBQWtCO1FBQ2hCLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsYUFBYSxDQUFDO1FBQzNDLElBQUksQ0FBQyxFQUFFO1lBQUUsT0FBTztRQUNoQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1RSxJQUFJLEdBQUcsS0FBSyxJQUFJLENBQUMsYUFBYSxFQUFFLEVBQUUsQ0FBQztZQUNqQyxFQUFFLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUM7WUFDL0IsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDOUIsQ0FBQztJQUNILENBQUM7SUFFRDs7eURBRXFEO0lBQ3JEO1FBQ0UsS0FBSyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQzdCLENBQUM7d0dBekNVLDBCQUEwQjs0RkFBMUIsMEJBQTBCLHdMQWxCM0I7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQlQsNERBbEJTLGlCQUFpQiw0R0FBRSxvQkFBb0IseUdBQUUscUJBQXFCOzs0RkFvQjdELDBCQUEwQjtrQkF2QnRDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLHFCQUFxQjtvQkFDL0IsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRSxDQUFDLGlCQUFpQixFQUFFLG9CQUFvQixFQUFFLHFCQUFxQixDQUFDO29CQUN6RSxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JUO2lCQUNGO3dEQUlnQyxXQUFXO3NCQUF6QyxTQUFTO3VCQUFDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGA8Ymxvb25pby1jaGF0LXRocmVhZD5gIOKAlCBoZWFkbGVzcyB2YXJpYW50IG9mIHRoZSBwYW5lbC4gUmVuZGVycyBqdXN0XG4gKiB0aGUgbWVzc2FnZSBsb2cgKyBpbnB1dCwgbm8gaGVhZGVyLCBubyBmbG9hdGluZyBjaHJvbWUuIFVzZWQgd2hlbiB0aGVcbiAqIHRlbmFudCB3YW50cyB0byBlbWJlZCB0aGUgY2hhdCAqaW5zaWRlKiB0aGVpciBvd24gbGF5b3V0IChlLmcuIGluc2lkZVxuICogYSBzdXBwb3J0IG1vZGFsLCBhIHNpZGViYXIsIGEgaGVscCBjZW50ZXIgcGFnZSkuXG4gKlxuICogVGhlbWluZyBhbmQgYmVoYXZpb3VyIGFyZSBvdGhlcndpc2UgaWRlbnRpY2FsIOKAlCB0aGUgc2FtZVxuICogYEJsb29uaW9DaGF0U2VydmljZWAgZHJpdmVzIHN0YXRlLCBzbyBhIHRocmVhZCBhbmQgYSBwYW5lbCBzaWRlLWJ5LXNpZGVcbiAqIHN0YXkgaW4gc3luYy5cbiAqL1xuaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgY29tcHV0ZWQsXG4gIGluamVjdCxcbiAgQWZ0ZXJWaWV3Q2hlY2tlZCxcbiAgRWxlbWVudFJlZixcbiAgVmlld0NoaWxkLFxuICBzaWduYWwsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBCbG9vbmlvQ2hhdFNlcnZpY2UgfSBmcm9tICcuLi9ibG9vbmlvLWNoYXQuc2VydmljZSc7XG5pbXBvcnQgeyBJbnB1dEJhckNvbXBvbmVudCB9IGZyb20gJy4vaW5wdXQtYmFyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBNZXNzYWdlTGlzdENvbXBvbmVudCB9IGZyb20gJy4vbWVzc2FnZS1saXN0LmNvbXBvbmVudCc7XG5pbXBvcnQgeyBRdWlja1JlcGxpZXNDb21wb25lbnQgfSBmcm9tICcuL3F1aWNrLXJlcGxpZXMuY29tcG9uZW50JztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYmxvb25pby1jaGF0LXRocmVhZCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtJbnB1dEJhckNvbXBvbmVudCwgTWVzc2FnZUxpc3RDb21wb25lbnQsIFF1aWNrUmVwbGllc0NvbXBvbmVudF0sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgY2xhc3M9XCJiY2gtcm9vdFwiIGRhdGEtbW9kZT1cImlubGluZVwiIFtzdHlsZV09XCJ0aGVtZVN0eWxlKClcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJiY2gtcGFuZWxcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImJjaC1tZXNzYWdlc1wiICNtZXNzYWdlcz5cbiAgICAgICAgICA8Ymxvb25pby1jaGF0LW1lc3NhZ2UtbGlzdFxuICAgICAgICAgICAgW21lc3NhZ2VzXT1cInZpc2libGVNZXNzYWdlcygpXCJcbiAgICAgICAgICAgIFtib3RUeXBpbmddPVwiY2hhdC5ib3RUeXBpbmcoKVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxibG9vbmlvLWNoYXQtcXVpY2stcmVwbGllcyAvPlxuICAgICAgICA8Ymxvb25pby1jaGF0LWlucHV0LWJhclxuICAgICAgICAgIFtkaXNhYmxlZF09XCJjaGF0LmNvbm5lY3RpbmcoKVwiXG4gICAgICAgICAgKHNlbmQpPVwiY2hhdC5zZW5kTWVzc2FnZSgkZXZlbnQpXCJcbiAgICAgICAgLz5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBCbG9vbmlvQ2hhdFRocmVhZENvbXBvbmVudCBpbXBsZW1lbnRzIEFmdGVyVmlld0NoZWNrZWQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2hhdCA9IGluamVjdChCbG9vbmlvQ2hhdFNlcnZpY2UpO1xuXG4gIEBWaWV3Q2hpbGQoJ21lc3NhZ2VzJykgcHJpdmF0ZSBtZXNzYWdlc1JlZj86IEVsZW1lbnRSZWY8SFRNTERpdkVsZW1lbnQ+O1xuICBwcml2YXRlIGxhc3RTY3JvbGxMZW4gPSBzaWduYWwoLTEpO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSB0aGVtZVN0eWxlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IGNvbG9yID0gdGhpcy5jaGF0LmJvb3RzdHJhcCgpPy5icmFuZGluZ19wcmltYXJ5X2NvbG9yO1xuICAgIHJldHVybiBjb2xvciA/IGAtLWJjaC1wcmltYXJ5OiAke2NvbG9yfWAgOiAnJztcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHZpc2libGVNZXNzYWdlcyA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBiID0gdGhpcy5jaGF0LmJvb3RzdHJhcCgpO1xuICAgIGNvbnN0IG1lc3NhZ2VzID0gdGhpcy5jaGF0Lm1lc3NhZ2VzKCk7XG4gICAgaWYgKGIgJiYgIWIuaXNfb25saW5lICYmIG1lc3NhZ2VzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIFtcbiAgICAgICAge1xuICAgICAgICAgIGlkOiAnc3lzX29mZmxpbmUnLFxuICAgICAgICAgIGZyb206ICdzeXN0ZW0nIGFzIGNvbnN0LFxuICAgICAgICAgIGNvbnRlbnQ6IGIub2ZmbGluZV9tZXNzYWdlLFxuICAgICAgICB9LFxuICAgICAgXTtcbiAgICB9XG4gICAgcmV0dXJuIG1lc3NhZ2VzO1xuICB9KTtcblxuICBuZ0FmdGVyVmlld0NoZWNrZWQoKTogdm9pZCB7XG4gICAgY29uc3QgZWwgPSB0aGlzLm1lc3NhZ2VzUmVmPy5uYXRpdmVFbGVtZW50O1xuICAgIGlmICghZWwpIHJldHVybjtcbiAgICBjb25zdCBsZW4gPSB0aGlzLnZpc2libGVNZXNzYWdlcygpLmxlbmd0aCArICh0aGlzLmNoYXQuYm90VHlwaW5nKCkgPyAxIDogMCk7XG4gICAgaWYgKGxlbiAhPT0gdGhpcy5sYXN0U2Nyb2xsTGVuKCkpIHtcbiAgICAgIGVsLnNjcm9sbFRvcCA9IGVsLnNjcm9sbEhlaWdodDtcbiAgICAgIHRoaXMubGFzdFNjcm9sbExlbi5zZXQobGVuKTtcbiAgICB9XG4gIH1cblxuICAvKiogQ29udmVuaWVuY2U6IHRocmVhZCBpcyBtZWFudCB0byBiZSBhbHdheXMtb24sIHNvIGl0IG9wZW5zIHRoZVxuICAgKiAgc2VydmljZS1sZXZlbCBzZXNzaW9uIGFzIHNvb24gYXMgaXQncyBpbnN0YW50aWF0ZWQuXG4gICAqICAoRmxvYXRpbmcgcGFuZWwgd2FpdHMgZm9yIHRoZSBsYXVuY2hlciBjbGljay4pICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHZvaWQgdGhpcy5jaGF0Lm9wZW5QYW5lbCgpO1xuICB9XG59XG4iXX0=
|
package/index.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @bloonio/chat-angular — Angular library for the bloonio chat PaaS.
|
|
3
|
-
*
|
|
4
|
-
* Public surface:
|
|
5
|
-
* - {@link provideBloonioChat} — environment-provider factory for app.config.ts
|
|
6
|
-
* - {@link BloonioChatService} — signals-based reactive state
|
|
7
|
-
* - Three standalone components:
|
|
8
|
-
* <bloonio-chat-launcher> floating bubble (collapsed)
|
|
9
|
-
* <bloonio-chat-panel> expandable card (the full chat UX)
|
|
10
|
-
* <bloonio-chat-thread> headless variant for inline embedding
|
|
11
|
-
*
|
|
12
|
-
* Styles ship separately at `@bloonio/chat-angular/styles.css` — import once
|
|
13
|
-
* in your global stylesheet (see README).
|
|
14
|
-
*/
|
|
15
|
-
export { provideBloonioChat, BLOONIO_CHAT_CONFIG, injectBloonioChatConfig } from './lib/bloonio-chat.token';
|
|
16
|
-
export type { BloonioChatConfig, VisitorTokenProvider, } from './lib/bloonio-chat.token';
|
|
17
|
-
export { BloonioChatService } from './lib/bloonio-chat.service';
|
|
18
|
-
export type { ChatMessage } from './lib/bloonio-chat.service';
|
|
19
|
-
export { BloonioChatLauncherComponent } from './lib/components/launcher.component';
|
|
20
|
-
export { BloonioChatPanelComponent, type ChatPanelMode, } from './lib/components/panel.component';
|
|
21
|
-
export { BloonioChatThreadComponent } from './lib/components/thread.component';
|
|
22
|
-
export { MessageListComponent } from './lib/components/message-list.component';
|
|
23
|
-
export { InputBarComponent } from './lib/components/input-bar.component';
|
|
24
|
-
export { QuickRepliesComponent } from './lib/components/quick-replies.component';
|
|
25
|
-
export type { WidgetBootstrapConfig, WidgetPosition, WidgetVisibility, ServerMsgFrame, MessageSender, } from '@bloonio/chat-client-core';
|
|
26
|
-
export { BloonioChatError, BloonioChatNetworkError, BloonioChatServerError, BloonioChatAuthError, } from '@bloonio/chat-client-core';
|
|
27
|
-
export declare const VERSION = "0.1.0";
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BloonioChatService — signals-based reactive state container.
|
|
3
|
-
*
|
|
4
|
-
* Mirrors the Preact widget's `Widget.tsx` state machine but uses Angular
|
|
5
|
-
* signals so templates auto-track the slots they read. Public surface:
|
|
6
|
-
*
|
|
7
|
-
* readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);
|
|
8
|
-
* readonly session = signal<CreateVisitorSessionResponse | null>(null);
|
|
9
|
-
* readonly messages = signal<ChatMessage[]>([]);
|
|
10
|
-
* readonly botTyping = signal(false);
|
|
11
|
-
* readonly open = signal(false);
|
|
12
|
-
* readonly connecting= signal(false);
|
|
13
|
-
* readonly unreadCount = computed(...);
|
|
14
|
-
*
|
|
15
|
-
* async open(): Promise<void>
|
|
16
|
-
* close(): void
|
|
17
|
-
* async toggle(): Promise<void>
|
|
18
|
-
* sendMessage(text: string): void
|
|
19
|
-
*
|
|
20
|
-
* SSR-safe: every transport call (fetch, WebSocket) is gated behind a
|
|
21
|
-
* platform check via `afterNextRender()` so server pre-rendering doesn't
|
|
22
|
-
* try to open sockets.
|
|
23
|
-
*/
|
|
24
|
-
import { type Signal } from '@angular/core';
|
|
25
|
-
import { type CreateVisitorSessionResponse, type WidgetBootstrapConfig } from '@bloonio/chat-client-core';
|
|
26
|
-
import * as i0 from "@angular/core";
|
|
27
|
-
export interface ChatMessage {
|
|
28
|
-
id: string;
|
|
29
|
-
from: 'visitor' | 'bot' | 'agent' | 'system';
|
|
30
|
-
content: string;
|
|
31
|
-
pending?: boolean;
|
|
32
|
-
client_msg_id?: string;
|
|
33
|
-
}
|
|
34
|
-
export declare class BloonioChatService {
|
|
35
|
-
private readonly config;
|
|
36
|
-
private readonly platformId;
|
|
37
|
-
private readonly destroyRef;
|
|
38
|
-
/** True when running in the browser (vs. server pre-render). */
|
|
39
|
-
private readonly isBrowser;
|
|
40
|
-
/** Bootstrap config — populated by the first `loadBootstrap()` call. */
|
|
41
|
-
readonly bootstrap: import("@angular/core").WritableSignal<WidgetBootstrapConfig>;
|
|
42
|
-
/** Visitor session (id + JWT + expires_at) — set lazily on first open. */
|
|
43
|
-
readonly session: import("@angular/core").WritableSignal<CreateVisitorSessionResponse>;
|
|
44
|
-
/** Message log, oldest → newest. */
|
|
45
|
-
readonly messages: import("@angular/core").WritableSignal<ChatMessage[]>;
|
|
46
|
-
/** True between bot's typing(start) and typing(stop) frames. */
|
|
47
|
-
readonly botTyping: import("@angular/core").WritableSignal<boolean>;
|
|
48
|
-
/** Panel visibility — drives Launcher / Panel rendering. */
|
|
49
|
-
readonly open: import("@angular/core").WritableSignal<boolean>;
|
|
50
|
-
/** True while `ensureSession()` is in flight (initial WS handshake). */
|
|
51
|
-
readonly connecting: import("@angular/core").WritableSignal<boolean>;
|
|
52
|
-
/** Set when the WS handshake fails permanently (e.g. tenant suspended). */
|
|
53
|
-
readonly fatalError: import("@angular/core").WritableSignal<string>;
|
|
54
|
-
/** Number of bot/agent messages received while the panel was closed.
|
|
55
|
-
* Resets to 0 the moment the panel opens. */
|
|
56
|
-
private readonly _seenCount;
|
|
57
|
-
readonly unreadCount: Signal<number>;
|
|
58
|
-
private client;
|
|
59
|
-
private ws;
|
|
60
|
-
private bootstrapPromise;
|
|
61
|
-
private sessionPromise;
|
|
62
|
-
constructor();
|
|
63
|
-
/** Toggle panel visibility. First open also opens the WS session. */
|
|
64
|
-
toggle(): Promise<void>;
|
|
65
|
-
/** Force the panel open. */
|
|
66
|
-
openPanel(): Promise<void>;
|
|
67
|
-
/** Force the panel closed. Keeps the WS open so reopening is instant. */
|
|
68
|
-
closePanel(): void;
|
|
69
|
-
/** Send a visitor message. No-op if the WS isn't connected. */
|
|
70
|
-
sendMessage(content: string): void;
|
|
71
|
-
private loadBootstrap;
|
|
72
|
-
private ensureSession;
|
|
73
|
-
private openWebSocket;
|
|
74
|
-
private tryIdentify;
|
|
75
|
-
private getClient;
|
|
76
|
-
private resolvedOrigin;
|
|
77
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<BloonioChatService, never>;
|
|
78
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<BloonioChatService>;
|
|
79
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency-injection plumbing for the Angular chat SDK.
|
|
3
|
-
*
|
|
4
|
-
* Consumers wire the SDK into their app via the standard Angular pattern
|
|
5
|
-
* documented in docs/12 §7.3:
|
|
6
|
-
*
|
|
7
|
-
* // app.config.ts
|
|
8
|
-
* export const appConfig: ApplicationConfig = {
|
|
9
|
-
* providers: [
|
|
10
|
-
* provideBloonioChat({
|
|
11
|
-
* tenantId: env.CHAT_TENANT_ID,
|
|
12
|
-
* publicKey: env.CHAT_PUBLIC_KEY,
|
|
13
|
-
* baseUrl: 'https://chat-relay.bloonio.com',
|
|
14
|
-
* visitorTokenProvider: () => inject(HttpClient).get<{token: string}>(
|
|
15
|
-
* '/api/chat/visitor-token'
|
|
16
|
-
* ).pipe(map(r => r.token)),
|
|
17
|
-
* locale: 'fr',
|
|
18
|
-
* }),
|
|
19
|
-
* ],
|
|
20
|
-
* };
|
|
21
|
-
*/
|
|
22
|
-
import { InjectionToken, type EnvironmentProviders } from '@angular/core';
|
|
23
|
-
import type { Observable } from 'rxjs';
|
|
24
|
-
import type { Attachment } from '@bloonio/chat-client-core';
|
|
25
|
-
/** Caller-supplied resolver for the tenant-signed visitor token.
|
|
26
|
-
*
|
|
27
|
-
* Two acceptable shapes:
|
|
28
|
-
* - Returns nothing (or `null`) → SDK falls back to anonymous mode.
|
|
29
|
-
* A session is minted via `/create/visitor-session` and the visitor
|
|
30
|
-
* stays unidentified until you supply one.
|
|
31
|
-
* - Returns a string / Observable<string> → SDK calls `/identify/visitor`
|
|
32
|
-
* with this signed token to bind the anonymous session to a tenant user.
|
|
33
|
-
*/
|
|
34
|
-
export type VisitorTokenProvider = (() => string | null | Promise<string | null> | Observable<string | null>) | undefined;
|
|
35
|
-
export interface BloonioChatConfig {
|
|
36
|
-
/** Tenant UUID v7 (the canonical handle issued by chat_relay). */
|
|
37
|
-
tenantId: string;
|
|
38
|
-
/** `pk_…` widget key bound to the tenant's `allowed_origins`. */
|
|
39
|
-
publicKey: string;
|
|
40
|
-
/** Base URL of the chat relay (https://chat-relay.bloonio.com or local). */
|
|
41
|
-
baseUrl: string;
|
|
42
|
-
/** Visitor-identification resolver. See {@link VisitorTokenProvider}. */
|
|
43
|
-
visitorTokenProvider?: VisitorTokenProvider;
|
|
44
|
-
/** Default locale for new conversations. */
|
|
45
|
-
locale?: string;
|
|
46
|
-
/** Free-form metadata attached to every visitor session. */
|
|
47
|
-
metadata?: Record<string, unknown>;
|
|
48
|
-
/** Optional override of `window.location.origin` (tests / SSR). */
|
|
49
|
-
origin?: string;
|
|
50
|
-
/**
|
|
51
|
-
* Pre-fetched attachments to attach to the first outbound message.
|
|
52
|
-
* Reserved for phase 11; kept on the interface for forward compat.
|
|
53
|
-
*/
|
|
54
|
-
attachments?: Attachment[];
|
|
55
|
-
}
|
|
56
|
-
export declare const BLOONIO_CHAT_CONFIG: InjectionToken<BloonioChatConfig>;
|
|
57
|
-
/** Standalone-provider factory.
|
|
58
|
-
*
|
|
59
|
-
* Returns ``EnvironmentProviders`` so consumers can spread it into
|
|
60
|
-
* ``app.config.ts``'s providers array. The service itself is
|
|
61
|
-
* ``providedIn: 'root'`` so we don't need an explicit provider entry — we
|
|
62
|
-
* only need the config token to be registered.
|
|
63
|
-
*/
|
|
64
|
-
export declare function provideBloonioChat(config: BloonioChatConfig): EnvironmentProviders;
|
|
65
|
-
/** Internal accessor used by the components to fail loud when the
|
|
66
|
-
* consumer forgot ``provideBloonioChat()``. */
|
|
67
|
-
export declare function injectBloonioChatConfig(): BloonioChatConfig;
|