@bloonio/chat-angular 0.1.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.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Visitor input bar — multi-line textarea + send button. Enter sends,
3
+ * Shift+Enter inserts a newline. Emits `send` with the trimmed content
4
+ * once it's non-empty.
5
+ */
6
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, signal, } from '@angular/core';
7
+ import * as i0 from "@angular/core";
8
+ export class InputBarComponent {
9
+ disabled = false;
10
+ send = new EventEmitter();
11
+ value = signal('');
12
+ canSend() {
13
+ return !this.disabled && this.value().trim().length > 0;
14
+ }
15
+ onInput(e) {
16
+ this.value.set(e.target.value);
17
+ }
18
+ onKey(e) {
19
+ if (e.key === 'Enter' && !e.shiftKey) {
20
+ e.preventDefault();
21
+ this.submit();
22
+ }
23
+ }
24
+ submit() {
25
+ if (!this.canSend())
26
+ return;
27
+ const trimmed = this.value().trim();
28
+ this.send.emit(trimmed);
29
+ this.value.set('');
30
+ }
31
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: InputBarComponent, isStandalone: true, selector: "bloonio-chat-input-bar", inputs: { disabled: "disabled" }, outputs: { send: "send" }, ngImport: i0, template: `
33
+ <div class="bch-input">
34
+ <textarea
35
+ rows="1"
36
+ placeholder="Enter your message..."
37
+ [disabled]="disabled"
38
+ [value]="value()"
39
+ (input)="onInput($event)"
40
+ (keydown)="onKey($event)"
41
+ ></textarea>
42
+ <button
43
+ type="button"
44
+ class="bch-input-send"
45
+ aria-label="Send"
46
+ [disabled]="!canSend()"
47
+ (click)="submit()"
48
+ >
49
+ <svg viewBox="0 0 24 24" fill="currentColor">
50
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
51
+ </svg>
52
+ </button>
53
+ </div>
54
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
55
+ }
56
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputBarComponent, decorators: [{
57
+ type: Component,
58
+ args: [{
59
+ selector: 'bloonio-chat-input-bar',
60
+ standalone: true,
61
+ changeDetection: ChangeDetectionStrategy.OnPush,
62
+ template: `
63
+ <div class="bch-input">
64
+ <textarea
65
+ rows="1"
66
+ placeholder="Enter your message..."
67
+ [disabled]="disabled"
68
+ [value]="value()"
69
+ (input)="onInput($event)"
70
+ (keydown)="onKey($event)"
71
+ ></textarea>
72
+ <button
73
+ type="button"
74
+ class="bch-input-send"
75
+ aria-label="Send"
76
+ [disabled]="!canSend()"
77
+ (click)="submit()"
78
+ >
79
+ <svg viewBox="0 0 24 24" fill="currentColor">
80
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
81
+ </svg>
82
+ </button>
83
+ </div>
84
+ `,
85
+ }]
86
+ }], propDecorators: { disabled: [{
87
+ type: Input
88
+ }], send: [{
89
+ type: Output
90
+ }] } });
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQtYmFyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9pbnB1dC1iYXIuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFDSCxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUNMLE1BQU0sRUFDTixNQUFNLEdBQ1AsTUFBTSxlQUFlLENBQUM7O0FBOEJ2QixNQUFNLE9BQU8saUJBQWlCO0lBQ25CLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDaEIsSUFBSSxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7SUFFekIsS0FBSyxHQUFHLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUU1QixPQUFPO1FBQ2YsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVELE9BQU8sQ0FBQyxDQUFRO1FBQ2QsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUUsQ0FBQyxDQUFDLE1BQThCLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVELEtBQUssQ0FBQyxDQUFnQjtRQUNwQixJQUFJLENBQUMsQ0FBQyxHQUFHLEtBQUssT0FBTyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3JDLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUNuQixJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDaEIsQ0FBQztJQUNILENBQUM7SUFFRCxNQUFNO1FBQ0osSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUU7WUFBRSxPQUFPO1FBQzVCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNwQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN4QixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNyQixDQUFDO3dHQTFCVSxpQkFBaUI7NEZBQWpCLGlCQUFpQiwrSUF4QmxCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JUOzs0RkFFVSxpQkFBaUI7a0JBNUI3QixTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSx3QkFBd0I7b0JBQ2xDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JUO2lCQUNGOzhCQUVVLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0ksSUFBSTtzQkFBYixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBWaXNpdG9yIGlucHV0IGJhciDigJQgbXVsdGktbGluZSB0ZXh0YXJlYSArIHNlbmQgYnV0dG9uLiBFbnRlciBzZW5kcyxcbiAqIFNoaWZ0K0VudGVyIGluc2VydHMgYSBuZXdsaW5lLiBFbWl0cyBgc2VuZGAgd2l0aCB0aGUgdHJpbW1lZCBjb250ZW50XG4gKiBvbmNlIGl0J3Mgbm9uLWVtcHR5LlxuICovXG5pbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ29tcG9uZW50LFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPdXRwdXQsXG4gIHNpZ25hbCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2Jsb29uaW8tY2hhdC1pbnB1dC1iYXInLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2IGNsYXNzPVwiYmNoLWlucHV0XCI+XG4gICAgICA8dGV4dGFyZWFcbiAgICAgICAgcm93cz1cIjFcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cIkVudGVyIHlvdXIgbWVzc2FnZS4uLlwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJkaXNhYmxlZFwiXG4gICAgICAgIFt2YWx1ZV09XCJ2YWx1ZSgpXCJcbiAgICAgICAgKGlucHV0KT1cIm9uSW5wdXQoJGV2ZW50KVwiXG4gICAgICAgIChrZXlkb3duKT1cIm9uS2V5KCRldmVudClcIlxuICAgICAgPjwvdGV4dGFyZWE+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cImJjaC1pbnB1dC1zZW5kXCJcbiAgICAgICAgYXJpYS1sYWJlbD1cIlNlbmRcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiIWNhblNlbmQoKVwiXG4gICAgICAgIChjbGljayk9XCJzdWJtaXQoKVwiXG4gICAgICA+XG4gICAgICAgIDxzdmcgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIj5cbiAgICAgICAgICA8cGF0aCBkPVwiTTIuMDEgMjFMMjMgMTIgMi4wMSAzIDIgMTBsMTUgMi0xNSAyelwiIC8+XG4gICAgICAgIDwvc3ZnPlxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG4gIGAsXG59KVxuZXhwb3J0IGNsYXNzIElucHV0QmFyQ29tcG9uZW50IHtcbiAgQElucHV0KCkgZGlzYWJsZWQgPSBmYWxzZTtcbiAgQE91dHB1dCgpIHNlbmQgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdmFsdWUgPSBzaWduYWwoJycpO1xuXG4gIHByb3RlY3RlZCBjYW5TZW5kKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhdGhpcy5kaXNhYmxlZCAmJiB0aGlzLnZhbHVlKCkudHJpbSgpLmxlbmd0aCA+IDA7XG4gIH1cblxuICBvbklucHV0KGU6IEV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy52YWx1ZS5zZXQoKGUudGFyZ2V0IGFzIEhUTUxUZXh0QXJlYUVsZW1lbnQpLnZhbHVlKTtcbiAgfVxuXG4gIG9uS2V5KGU6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZS5rZXkgPT09ICdFbnRlcicgJiYgIWUuc2hpZnRLZXkpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgIHRoaXMuc3VibWl0KCk7XG4gICAgfVxuICB9XG5cbiAgc3VibWl0KCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5jYW5TZW5kKCkpIHJldHVybjtcbiAgICBjb25zdCB0cmltbWVkID0gdGhpcy52YWx1ZSgpLnRyaW0oKTtcbiAgICB0aGlzLnNlbmQuZW1pdCh0cmltbWVkKTtcbiAgICB0aGlzLnZhbHVlLnNldCgnJyk7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Floating launcher — circular chat bubble with optional label.
3
+ * Wraps the `BloonioChatService` so consumers just drop
4
+ * `<bloonio-chat-launcher />` somewhere and the SDK handles state.
5
+ */
6
+ import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
7
+ import { BloonioChatService } from '../bloonio-chat.service';
8
+ import * as i0 from "@angular/core";
9
+ export class BloonioChatLauncherComponent {
10
+ chat = inject(BloonioChatService);
11
+ position = computed(() => this.chat.bootstrap()?.widget_position ?? 'right');
12
+ /** Tint the bubble with the tenant's brand colour (falls back to the
13
+ * stylesheet default until bootstrap config loads). */
14
+ themeStyle = computed(() => {
15
+ const color = this.chat.bootstrap()?.branding_primary_color;
16
+ return color ? `--bch-primary: ${color}` : '';
17
+ });
18
+ label = computed(() => {
19
+ const b = this.chat.bootstrap();
20
+ return b?.button_label_enabled ? b.button_label_text : null;
21
+ });
22
+ ariaLabel = computed(() => {
23
+ const unread = this.chat.unreadCount();
24
+ return unread > 0
25
+ ? `Open chat (${unread} unread)`
26
+ : 'Open chat';
27
+ });
28
+ onToggle() {
29
+ void this.chat.toggle();
30
+ }
31
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatLauncherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: BloonioChatLauncherComponent, isStandalone: true, selector: "bloonio-chat-launcher", ngImport: i0, template: `
33
+ <div class="bch-root" [attr.data-position]="position()" [style]="themeStyle()">
34
+ <button
35
+ type="button"
36
+ class="bch-launcher"
37
+ [attr.aria-label]="ariaLabel()"
38
+ (click)="onToggle()"
39
+ >
40
+ @if (label()) {
41
+ <span class="bch-launcher-label">{{ label() }}</span>
42
+ }
43
+ <span class="bch-launcher-bubble">
44
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
45
+ stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
46
+ <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
47
+ </svg>
48
+ </span>
49
+ </button>
50
+ </div>
51
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatLauncherComponent, decorators: [{
54
+ type: Component,
55
+ args: [{
56
+ selector: 'bloonio-chat-launcher',
57
+ standalone: true,
58
+ changeDetection: ChangeDetectionStrategy.OnPush,
59
+ template: `
60
+ <div class="bch-root" [attr.data-position]="position()" [style]="themeStyle()">
61
+ <button
62
+ type="button"
63
+ class="bch-launcher"
64
+ [attr.aria-label]="ariaLabel()"
65
+ (click)="onToggle()"
66
+ >
67
+ @if (label()) {
68
+ <span class="bch-launcher-label">{{ label() }}</span>
69
+ }
70
+ <span class="bch-launcher-bubble">
71
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
72
+ stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
73
+ <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
74
+ </svg>
75
+ </span>
76
+ </button>
77
+ </div>
78
+ `,
79
+ }]
80
+ }] });
81
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF1bmNoZXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL2xhdW5jaGVyLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBQ0gsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXJGLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDOztBQTJCN0QsTUFBTSxPQUFPLDRCQUE0QjtJQUNwQixJQUFJLEdBQUcsTUFBTSxDQUFDLGtCQUFrQixDQUFDLENBQUM7SUFFbEMsUUFBUSxHQUFHLFFBQVEsQ0FDcEMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxlQUFlLElBQUksT0FBTyxDQUN4RCxDQUFDO0lBRUY7NERBQ3dEO0lBQ3JDLFVBQVUsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQzVDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEVBQUUsc0JBQXNCLENBQUM7UUFDNUQsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ2hELENBQUMsQ0FBQyxDQUFDO0lBRWdCLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3ZDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDaEMsT0FBTyxDQUFDLEVBQUUsb0JBQW9CLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQzlELENBQUMsQ0FBQyxDQUFDO0lBRWdCLFNBQVMsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQzNDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDdkMsT0FBTyxNQUFNLEdBQUcsQ0FBQztZQUNmLENBQUMsQ0FBQyxjQUFjLE1BQU0sVUFBVTtZQUNoQyxDQUFDLENBQUMsV0FBVyxDQUFDO0lBQ2xCLENBQUMsQ0FBQyxDQUFDO0lBRUgsUUFBUTtRQUNOLEtBQUssSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUMxQixDQUFDO3dHQTVCVSw0QkFBNEI7NEZBQTVCLDRCQUE0QixpRkFyQjdCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJUOzs0RkFFVSw0QkFBNEI7a0JBekJ4QyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSx1QkFBdUI7b0JBQ2pDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJUO2lCQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGbG9hdGluZyBsYXVuY2hlciDigJQgY2lyY3VsYXIgY2hhdCBidWJibGUgd2l0aCBvcHRpb25hbCBsYWJlbC5cbiAqIFdyYXBzIHRoZSBgQmxvb25pb0NoYXRTZXJ2aWNlYCBzbyBjb25zdW1lcnMganVzdCBkcm9wXG4gKiBgPGJsb29uaW8tY2hhdC1sYXVuY2hlciAvPmAgc29tZXdoZXJlIGFuZCB0aGUgU0RLIGhhbmRsZXMgc3RhdGUuXG4gKi9cbmltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIGNvbXB1dGVkLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgQmxvb25pb0NoYXRTZXJ2aWNlIH0gZnJvbSAnLi4vYmxvb25pby1jaGF0LnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdibG9vbmlvLWNoYXQtbGF1bmNoZXInLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2IGNsYXNzPVwiYmNoLXJvb3RcIiBbYXR0ci5kYXRhLXBvc2l0aW9uXT1cInBvc2l0aW9uKClcIiBbc3R5bGVdPVwidGhlbWVTdHlsZSgpXCI+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cImJjaC1sYXVuY2hlclwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiYXJpYUxhYmVsKClcIlxuICAgICAgICAoY2xpY2spPVwib25Ub2dnbGUoKVwiXG4gICAgICA+XG4gICAgICAgIEBpZiAobGFiZWwoKSkge1xuICAgICAgICAgIDxzcGFuIGNsYXNzPVwiYmNoLWxhdW5jaGVyLWxhYmVsXCI+e3sgbGFiZWwoKSB9fTwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgICA8c3BhbiBjbGFzcz1cImJjaC1sYXVuY2hlci1idWJibGVcIj5cbiAgICAgICAgICA8c3ZnIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiXG4gICAgICAgICAgICAgICBzdHJva2Utd2lkdGg9XCIyLjJcIiBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgc3Ryb2tlLWxpbmVqb2luPVwicm91bmRcIj5cbiAgICAgICAgICAgIDxwYXRoIGQ9XCJNMjEgMTEuNWE4LjM4IDguMzggMCAwIDEtLjkgMy44IDguNSA4LjUgMCAwIDEtNy42IDQuNyA4LjM4IDguMzggMCAwIDEtMy44LS45TDMgMjFsMS45LTUuN2E4LjM4IDguMzggMCAwIDEtLjktMy44IDguNSA4LjUgMCAwIDEgNC43LTcuNiA4LjM4IDguMzggMCAwIDEgMy44LS45aC41YTguNDggOC40OCAwIDAgMSA4IDh2LjV6XCIgLz5cbiAgICAgICAgICA8L3N2Zz5cbiAgICAgICAgPC9zcGFuPlxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG4gIGAsXG59KVxuZXhwb3J0IGNsYXNzIEJsb29uaW9DaGF0TGF1bmNoZXJDb21wb25lbnQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2hhdCA9IGluamVjdChCbG9vbmlvQ2hhdFNlcnZpY2UpO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBwb3NpdGlvbiA9IGNvbXB1dGVkKFxuICAgICgpID0+IHRoaXMuY2hhdC5ib290c3RyYXAoKT8ud2lkZ2V0X3Bvc2l0aW9uID8/ICdyaWdodCcsXG4gICk7XG5cbiAgLyoqIFRpbnQgdGhlIGJ1YmJsZSB3aXRoIHRoZSB0ZW5hbnQncyBicmFuZCBjb2xvdXIgKGZhbGxzIGJhY2sgdG8gdGhlXG4gICAqICBzdHlsZXNoZWV0IGRlZmF1bHQgdW50aWwgYm9vdHN0cmFwIGNvbmZpZyBsb2FkcykuICovXG4gIHByb3RlY3RlZCByZWFkb25seSB0aGVtZVN0eWxlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IGNvbG9yID0gdGhpcy5jaGF0LmJvb3RzdHJhcCgpPy5icmFuZGluZ19wcmltYXJ5X2NvbG9yO1xuICAgIHJldHVybiBjb2xvciA/IGAtLWJjaC1wcmltYXJ5OiAke2NvbG9yfWAgOiAnJztcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGxhYmVsID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IGIgPSB0aGlzLmNoYXQuYm9vdHN0cmFwKCk7XG4gICAgcmV0dXJuIGI/LmJ1dHRvbl9sYWJlbF9lbmFibGVkID8gYi5idXR0b25fbGFiZWxfdGV4dCA6IG51bGw7XG4gIH0pO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBhcmlhTGFiZWwgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgdW5yZWFkID0gdGhpcy5jaGF0LnVucmVhZENvdW50KCk7XG4gICAgcmV0dXJuIHVucmVhZCA+IDBcbiAgICAgID8gYE9wZW4gY2hhdCAoJHt1bnJlYWR9IHVucmVhZClgXG4gICAgICA6ICdPcGVuIGNoYXQnO1xuICB9KTtcblxuICBvblRvZ2dsZSgpOiB2b2lkIHtcbiAgICB2b2lkIHRoaXMuY2hhdC50b2dnbGUoKTtcbiAgfVxufVxuIl19
@@ -0,0 +1,58 @@
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==
@@ -0,0 +1,211 @@
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==
@@ -0,0 +1,58 @@
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=