UNPKG

@aslaluroba/help-center

Version:

A powerful and customizable help center widget for Angular applications with real-time chat functionality, AI assistance, and multi-language support.

625 lines (618 loc) 194 kB
import * as i0 from '@angular/core'; import { HostBinding, Input, ViewEncapsulation, Component, EventEmitter, Output, Injectable, Pipe, HostListener, ViewChild } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i2 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as Ably from 'ably'; import { BehaviorSubject } from 'rxjs'; import * as i3 from 'ngx-markdown'; import { MarkdownModule } from 'ngx-markdown'; import 'prismjs'; import 'prismjs/components/prism-typescript'; import 'prismjs/components/prism-javascript'; import 'prismjs/components/prism-css'; import 'prismjs/components/prism-json'; class ClientAblyService { static client = null; static channel = null; static isConnected = false; static sessionId = null; static messageUnsubscribe = null; static async startConnection(sessionId, ablyToken, onMessageReceived, tenantId) { // Prevent multiple connections if (this.isConnected && this.sessionId === sessionId) { return; } // Close existing connection if connecting to a different session if (this.isConnected && this.sessionId !== sessionId) { await this.stopConnection(); } try { // Initialize Ably client with the token this.client = new Ably.Realtime({ authUrl: undefined, token: ablyToken, autoConnect: true, }); // Wait for connection to be established await new Promise((resolve, reject) => { if (!this.client) { reject(new Error('Failed to initialize Ably client')); return; } this.client.connection.once('connected', () => { this.isConnected = true; this.sessionId = sessionId; resolve(); }); this.client.connection.once('failed', (stateChange) => { console.error('Ably connection failed:', stateChange); reject(new Error(`Ably connection failed: ${stateChange.reason?.message || 'Unknown error'}`)); }); this.client.connection.once('disconnected', (stateChange) => { console.error('Ably connection disconnected:', stateChange); reject(new Error(`Ably connection disconnected: ${stateChange.reason?.message || 'Unknown error'}`)); }); // Set a timeout for connection setTimeout(() => { if (!this.isConnected) { reject(new Error('Ably connection timeout')); } }, 10000); }); // Subscribe to the session room await this.joinChannel(sessionId, onMessageReceived, tenantId); } catch (error) { console.error('Error during Ably connection setup:', error); this.isConnected = false; this.sessionId = null; throw error; } } static async joinChannel(sessionId, onMessageReceived, tenantId) { if (!this.client) { throw new Error('Chat client not initialized'); } const roomName = `session:${tenantId}:${sessionId}`; // Set up raw channel subscription for server messages if (this.client) { this.channel = this.client.channels.get(roomName); // Subscribe to assistant/system responses this.channel.subscribe('ReceiveMessage', (message) => { try { const messageContent = typeof message.data === 'string' ? message.data : message.data?.content || message.data?.message; const senderType = message.data?.senderType || 3; // Assistant const needsAgent = message.data?.needsAgent || false; onMessageReceived(messageContent, senderType, needsAgent); } catch (error) { console.error('Error processing ReceiveMessage:', error); } }); await this.channel.attach(); } } static async stopConnection() { try { // Unsubscribe from room messages if (this.messageUnsubscribe) { this.messageUnsubscribe(); this.messageUnsubscribe = null; } // Unsubscribe and detach from raw channel if (this.channel) { this.channel.unsubscribe(); await this.channel.detach(); this.channel = null; } // Close Ably connection if (this.client) { this.client.close(); this.client = null; } this.isConnected = false; this.sessionId = null; } catch (error) { console.error('Error stopping Ably connection:', error); // Reset state even if there's an error this.isConnected = false; this.sessionId = null; this.client = null; this.channel = null; this.messageUnsubscribe = null; } } static isConnectionActive() { return this.isConnected && this.client?.connection.state === 'connected'; } static getConnectionState() { return this.client?.connection.state || 'disconnected'; } // Method to manually send a message (if needed for debugging or direct messaging) static async sendMessage(messageContent, senderType = 1) { if (!this.channel || !this.isConnected) { throw new Error('Connection not active'); } try { const messageData = { text: messageContent, metadata: { senderType, sentAt: new Date().toISOString(), }, }; await this.channel.publish('message', messageData); } catch (error) { console.error('Error sending message:', error); throw error; } } } class CardComponent { variant = 'default'; class = ''; get hostClasses() { const classes = ['card']; classes.push(`card--${this.variant}`); if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardComponent, isStandalone: true, selector: "app-card", inputs: { variant: "variant", class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardComponent, decorators: [{ type: Component, args: [{ selector: 'app-card', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { variant: [{ type: Input }], class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class CardHeaderComponent { class = ''; get hostClasses() { const classes = ['card__header']; if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardHeaderComponent, isStandalone: true, selector: "app-card-header", inputs: { class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardHeaderComponent, decorators: [{ type: Component, args: [{ selector: 'app-card-header', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class CardTitleComponent { class = ''; get hostClasses() { const classes = ['card__title']; if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardTitleComponent, isStandalone: true, selector: "app-card-title", inputs: { class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardTitleComponent, decorators: [{ type: Component, args: [{ selector: 'app-card-title', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class CardDescriptionComponent { class = ''; get hostClasses() { const classes = ['card__description']; if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardDescriptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardDescriptionComponent, isStandalone: true, selector: "app-card-description", inputs: { class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardDescriptionComponent, decorators: [{ type: Component, args: [{ selector: 'app-card-description', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class CardContentComponent { class = ''; get hostClasses() { const classes = ['card__content']; if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardContentComponent, isStandalone: true, selector: "app-card-content", inputs: { class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardContentComponent, decorators: [{ type: Component, args: [{ selector: 'app-card-content', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class CardFooterComponent { class = ''; get hostClasses() { const classes = ['card__footer']; if (this.class) { classes.push(this.class); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: CardFooterComponent, isStandalone: true, selector: "app-card-footer", inputs: { class: "class" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CardFooterComponent, decorators: [{ type: Component, args: [{ selector: 'app-card-footer', standalone: true, imports: [CommonModule], template: `<ng-content></ng-content>`, encapsulation: ViewEncapsulation.None, styles: [".card{z-index:1;width:100%;box-sizing:border-box}.card--default{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card--rounded{border-radius:1.5rem;background-color:#fff;padding:1rem 1.5rem;display:block}.card--shadowed{border-radius:.5rem;border:1px solid #e2e2e2;background-color:#fff;color:#1f1f1f;box-shadow:#64646f33 0 7px 29px}.card__header{display:flex;flex-direction:column;padding:1.5rem;width:100%}.card__title{font-weight:600;line-height:1;letter-spacing:-.02em;width:100%}.card__description{font-size:.875rem;color:#606060;width:100%}.card__content{padding:0;width:100%;box-sizing:border-box}.card__footer{display:flex;align-items:center;padding:1.5rem;padding-top:0;width:100%}\n"] }] }], propDecorators: { class: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class ButtonComponent { variant = 'default'; type = 'button'; disabled = false; fullWidth = false; className = ''; size = 'default'; direction = 'ltr'; onClick = new EventEmitter(); getButtonClasses() { const classes = ['button']; // Add variant class classes.push(`button--${this.variant}`); // Add full width class if needed if (this.fullWidth) { classes.push('button--full-width'); } // Add size class if needed if (this.size) { classes.push(`button--${this.size}`); } // Add direction class classes.push(`button--${this.direction}`); // Add custom classes if (this.className) { classes.push(this.className); } return classes.join(' '); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: ButtonComponent, isStandalone: true, selector: "app-button", inputs: { variant: "variant", type: "type", disabled: "disabled", fullWidth: "fullWidth", className: "className", size: "size", direction: "direction" }, outputs: { onClick: "onClick" }, ngImport: i0, template: ` <button [type]="type" [disabled]="disabled" [class]="getButtonClasses()" (click)="onClick.emit($event)" [dir]="direction"> <ng-content></ng-content> </button> `, isInline: true, styles: [".button{cursor:pointer;transition:all .2s ease-out;display:inline-flex;align-items:center;justify-content:center}.button--rtl{direction:rtl}.button--rtl img{transform:scaleX(-1)}.button--default{background-color:#ad49e1;color:#fff;padding:.5rem 1rem;border-radius:9999px;border:none}.button--default:hover{background-color:#451d5a}.button--outline{background-color:transparent;color:#ad49e1;border:1px solid #ad49e1;padding:.5rem 1rem;border-radius:9999px}.button--outline:hover{background-color:#ad49e11a}.button--icon-bg{background-color:#ad49e1;color:#fff;padding:.75rem 1rem;border-radius:9999px;width:3rem;height:3rem;display:flex;align-items:center;justify-content:center;border:none}.button--icon-bg:hover{background-color:#451d5a}.button--icon-bg.button--white-bg{background-color:#fff}.button--icon-bg.button--white-bg:hover{background-color:#f3f3f3}.button--icon-bg.button--light-bg{background-color:#f6ecfc}.button--icon-bg.button--light-bg:hover{background-color:#deb6f3}.button--icon-bg.button--close-button{background-color:#ad49e1;padding:.5rem;border:none}.button--icon-bg.button--close-button:hover{background-color:#ad49e1cc}.button--icon-bg.button--small{padding:.25rem .5rem;width:2rem;height:2rem}.button--icon-bg.button--icon{padding:.75rem}.button--icon-only{background-color:transparent;border:none;padding:.25rem;display:flex;align-items:center;justify-content:center}.button--full-width{width:100%!important;display:flex!important}.button:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ButtonComponent, decorators: [{ type: Component, args: [{ selector: 'app-button', standalone: true, imports: [CommonModule], template: ` <button [type]="type" [disabled]="disabled" [class]="getButtonClasses()" (click)="onClick.emit($event)" [dir]="direction"> <ng-content></ng-content> </button> `, styles: [".button{cursor:pointer;transition:all .2s ease-out;display:inline-flex;align-items:center;justify-content:center}.button--rtl{direction:rtl}.button--rtl img{transform:scaleX(-1)}.button--default{background-color:#ad49e1;color:#fff;padding:.5rem 1rem;border-radius:9999px;border:none}.button--default:hover{background-color:#451d5a}.button--outline{background-color:transparent;color:#ad49e1;border:1px solid #ad49e1;padding:.5rem 1rem;border-radius:9999px}.button--outline:hover{background-color:#ad49e11a}.button--icon-bg{background-color:#ad49e1;color:#fff;padding:.75rem 1rem;border-radius:9999px;width:3rem;height:3rem;display:flex;align-items:center;justify-content:center;border:none}.button--icon-bg:hover{background-color:#451d5a}.button--icon-bg.button--white-bg{background-color:#fff}.button--icon-bg.button--white-bg:hover{background-color:#f3f3f3}.button--icon-bg.button--light-bg{background-color:#f6ecfc}.button--icon-bg.button--light-bg:hover{background-color:#deb6f3}.button--icon-bg.button--close-button{background-color:#ad49e1;padding:.5rem;border:none}.button--icon-bg.button--close-button:hover{background-color:#ad49e1cc}.button--icon-bg.button--small{padding:.25rem .5rem;width:2rem;height:2rem}.button--icon-bg.button--icon{padding:.75rem}.button--icon-only{background-color:transparent;border:none;padding:.25rem;display:flex;align-items:center;justify-content:center}.button--full-width{width:100%!important;display:flex!important}.button:disabled{opacity:.5;cursor:not-allowed}\n"] }] }], propDecorators: { variant: [{ type: Input }], type: [{ type: Input }], disabled: [{ type: Input }], fullWidth: [{ type: Input }], className: [{ type: Input }], size: [{ type: Input }], direction: [{ type: Input }], onClick: [{ type: Output }] } }); class ButtonContentComponent { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ButtonContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: ButtonContentComponent, isStandalone: true, selector: "app-button-content", ngImport: i0, template: ` <ng-content></ng-content> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ButtonContentComponent, decorators: [{ type: Component, args: [{ selector: 'app-button-content', standalone: true, imports: [CommonModule], template: ` <ng-content></ng-content> ` }] }] }); class IconButtonComponent { className = ''; disabled = false; onClick = new EventEmitter(); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: IconButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: IconButtonComponent, isStandalone: true, selector: "app-icon-button", inputs: { className: "className", disabled: "disabled" }, outputs: { onClick: "onClick" }, ngImport: i0, template: ` <app-button variant="icon-bg" [className]="className" [disabled]="disabled" (onClick)="onClick.emit($event)"> <ng-content></ng-content> </app-button> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ButtonComponent, selector: "app-button", inputs: ["variant", "type", "disabled", "fullWidth", "className", "size", "direction"], outputs: ["onClick"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: IconButtonComponent, decorators: [{ type: Component, args: [{ selector: 'app-icon-button', standalone: true, imports: [CommonModule, ButtonComponent], template: ` <app-button variant="icon-bg" [className]="className" [disabled]="disabled" (onClick)="onClick.emit($event)"> <ng-content></ng-content> </app-button> ` }] }], propDecorators: { className: [{ type: Input }], disabled: [{ type: Input }], onClick: [{ type: Output }] } }); class TransparentIconButtonComponent { className = ''; disabled = false; onClick = new EventEmitter(); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TransparentIconButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: TransparentIconButtonComponent, isStandalone: true, selector: "app-transparent-icon-button", inputs: { className: "className", disabled: "disabled" }, outputs: { onClick: "onClick" }, ngImport: i0, template: ` <app-button variant="icon-only" [className]="className" [disabled]="disabled" (onClick)="onClick.emit($event)"> <ng-content></ng-content> </app-button> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ButtonComponent, selector: "app-button", inputs: ["variant", "type", "disabled", "fullWidth", "className", "size", "direction"], outputs: ["onClick"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TransparentIconButtonComponent, decorators: [{ type: Component, args: [{ selector: 'app-transparent-icon-button', standalone: true, imports: [CommonModule, ButtonComponent], template: ` <app-button variant="icon-only" [className]="className" [disabled]="disabled" (onClick)="onClick.emit($event)"> <ng-content></ng-content> </app-button> ` }] }], propDecorators: { className: [{ type: Input }], disabled: [{ type: Input }], onClick: [{ type: Output }] } }); // src/app/services/translation.service.ts const defaultTranslations = { ChatIntroMessage: '', BabylaiTitle: '', BabylaiDescription: '', ChatNow: '', TryBableAI: '', ContactUs: '', PickTopicTitle: '', BabylAI: '', ChatPlaceholder: '', PoweredByBabylAI: '', EndChat: '', LeavingDialogTitle: '', LeavingDialogBody: '', Confirm: '', Cancel: '', title: '' }; class TranslationService { translations = { en: { ChatIntroMessage: 'Chat with BabylAI 🚀', BabylaiTitle: 'BabylAI', BabylaiDescription: "Hey there! 👋 I'm BabylAI, here to assist you.", ChatNow: 'Chat Now', TryBableAI: 'Try BabylAI for Free 🎉', ContactUs: "Contact us, Let's Talk! 💬", PickTopicTitle: 'Pick a Topic to Get Started', BabylAI: 'BabylAI', ChatPlaceholder: 'Type your message...', PoweredByBabylAI: 'Powered by BabylAI', EndChat: 'End Chat', LeavingDialogTitle: 'Leaving so soon? 👋', LeavingDialogBody: "Don't worry, you can come back anytime. We're always here if you need help or have questions.", Confirm: 'Confirm', Cancel: 'Cancel', title: 'Help Center' }, ar: { ChatIntroMessage: 'دردش مع BabylAI 🚀', BabylaiTitle: 'BabylAI', BabylaiDescription: 'مرحبا! 👋 أنا BabylAI، هنا لتساعدك.', ChatNow: 'دردش الآن', TryBableAI: 'جرب BabylAI مجانا 🎉', ContactUs: 'تواصل معنا, دعنا نتحدث! 💬', PickTopicTitle: 'اختر موضوع للبدء', BabylAI: 'BabylAI', ChatPlaceholder: 'اكتب رسالتك...', PoweredByBabylAI: 'مدعوم من BabylAI', EndChat: 'إنهاء الدردشة', LeavingDialogTitle: 'هل تغادر بالفعل؟ 👋', LeavingDialogBody: 'لا تقلق، يمكنك العودة في أي وقت. نحن دائماً هنا إذا كنت بحاجة إلى مساعدة أو لديك أسئلة.', Confirm: 'تأكيد', Cancel: 'إلغاء', title: 'مركز المساعدة' } }; _currentLang = new BehaviorSubject('en'); currentLang = this._currentLang.asObservable(); constructor() { } translate(key) { const lang = this._currentLang.value; return this.translations[lang][key] || key; } setLanguage(lang) { this._currentLang.next(lang); } getCurrentLang() { return this._currentLang.value; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TranslationService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TranslationService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); // src/app/pipes/translate.pipe.ts class TranslatePipe { translationService; constructor(translationService) { this.translationService = translationService; } transform(key) { return this.translationService.translate(key); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TranslatePipe, deps: [{ token: TranslationService }], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.11", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: TranslatePipe, decorators: [{ type: Pipe, args: [{ name: 'translate', standalone: true }] }], ctorParameters: () => [{ type: TranslationService }] }); class HelpScreenDataComponent { helpScreenData = null; title = ''; handleStartNewChat = new EventEmitter(); expandedItemId = null; get helpScreenDataList() { if (!this.helpScreenData?.options) return []; // Transform options to the format expected by HelpScreenDataComponent return this.helpScreenData.options.map((option) => ({ icon: option.icon || 'assets/icons/default.svg', title: option.title, description: option.paragraphs?.[0] || '', actionLabel: option.chatWithUs ? 'Chat Now' : '', action: option.chatWithUs ? () => this.handleStartChat(option) : null })); } ngOnInit() { } toggleExpand(itemId) { if (this.expandedItemId === itemId) { this.expandedItemId = null; } else { this.expandedItemId = itemId; setTimeout(() => { const element = document.getElementById(itemId); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } }, 100); } } handleStartChat(option) { this.handleStartNewChat.emit(option); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HelpScreenDataComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: HelpScreenDataComponent, isStandalone: true, selector: "app-help-screen-data", inputs: { helpScreenData: "helpScreenData", title: "title" }, outputs: { handleStartNewChat: "handleStartNewChat" }, ngImport: i0, template: "<div class=\"help-screen\">\n <h1 class=\"help-screen__title\">\n {{ 'PickTopicTitle' | translate }}\n </h1>\n <ng-container *ngFor=\"let item of helpScreenData?.options\">\n <app-card [id]=\"item.id\" variant=\"rounded\" class=\"help-screen__card\" (click)=\"toggleExpand(item.id)\">\n <app-card-content>\n <!-- Header section (always visible) -->\n <div class=\"help-screen__header\">\n <h4 class=\"help-screen__title-text\">{{ item.title }}</h4>\n <div\n class=\"help-screen__arrow-container\"\n [ngClass]=\"{\n 'help-screen__arrow-container--expanded': expandedItemId === item.id,\n 'help-screen__arrow-container--collapsed': expandedItemId !== item.id\n }\"\n >\n <!-- <img src=\"/icons/arrow-stripped-colored.svg\" alt=\"arrow-down\" /> -->\n <svg class=\"header__back-button-icon\" width=\"8\" height=\"16\" viewBox=\"0 0 8 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 15L1 8L2.5 6.25M7 1L5 3.333\" stroke=\"#7A1CAC\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n </div>\n\n <!-- Expanded content (visible only when expanded) -->\n <div *ngIf=\"expandedItemId === item.id\" class=\"help-screen__content\">\n <ng-container>\n <div *ngFor=\"let paragraph of item.paragraphs\" class=\"help-screen__paragraph\">\n {{ paragraph }}\n </div>\n </ng-container>\n\n <app-button *ngIf=\"item?.chatWithUs\" variant=\"default\" [fullWidth]=\"true\" (click)=\"handleStartChat(item)\">{{\n 'ChatNow' | translate\n }}</app-button>\n </div>\n </app-card-content>\n </app-card>\n </ng-container>\n</div>\n", styles: ["@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.help-screen{display:flex;flex-direction:column;gap:1rem;position:relative}.help-screen__title{font-size:2.875rem;font-weight:700;color:#1f1f1f;margin-bottom:.75rem;line-height:1}.help-screen__card{cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);transform-origin:top;background-color:#fff}.help-screen__card:hover{box-shadow:#64646f33 0 7px 29px;transform:translateY(-2px)}.help-screen__card.expanded{background-color:#fff}.help-screen__header{display:flex;align-items:center;justify-content:space-between;padding:.5rem}.help-screen__title-text{font-size:1rem;color:#606060;font-weight:600}.help-screen__arrow-container{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:9999px;color:#ad49e1;transition:transform .3s cubic-bezier(.4,0,.2,1)}.help-screen__arrow-container--expanded{transform:rotate(90deg)}.help-screen__arrow-container--collapsed{transform:rotate(270deg)}.help-screen__arrow-container img{width:1rem;height:1rem}.help-screen__content{animation:slideDown .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;gap:.5rem;margin-top:1rem;padding:.5rem;transform-origin:top}.help-screen__paragraph{font-size:1rem;color:#0a0a0a;margin-bottom:.5rem}.help-screen__button{width:100%;margin-top:.5rem;animation:slideDown .3s cubic-bezier(.4,0,.2,1) .1s}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CardComponent, selector: "app-card", inputs: ["variant", "class"] }, { kind: "component", type: CardContentComponent, selector: "app-card-content", inputs: ["class"] }, { kind: "component", type: ButtonComponent, selector: "app-button", inputs: ["variant", "type", "disabled", "fullWidth", "className", "size", "direction"], outputs: ["onClick"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HelpScreenDataComponent, decorators: [{ type: Component, args: [{ selector: 'app-help-screen-data', standalone: true, imports: [CommonModule, CardComponent, CardContentComponent, ButtonComponent, TranslatePipe], template: "<div class=\"help-screen\">\n <h1 class=\"help-screen__title\">\n {{ 'PickTopicTitle' | translate }}\n </h1>\n <ng-container *ngFor=\"let item of helpScreenData?.options\">\n <app-card [id]=\"item.id\" variant=\"rounded\" class=\"help-screen__card\" (click)=\"toggleExpand(item.id)\">\n <app-card-content>\n <!-- Header section (always visible) -->\n <div class=\"help-screen__header\">\n <h4 class=\"help-screen__title-text\">{{ item.title }}</h4>\n <div\n class=\"help-screen__arrow-container\"\n [ngClass]=\"{\n 'help-screen__arrow-container--expanded': expandedItemId === item.id,\n 'help-screen__arrow-container--collapsed': expandedItemId !== item.id\n }\"\n >\n <!-- <img src=\"/icons/arrow-stripped-colored.svg\" alt=\"arrow-down\" /> -->\n <svg class=\"header__back-button-icon\" width=\"8\" height=\"16\" viewBox=\"0 0 8 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 15L1 8L2.5 6.25M7 1L5 3.333\" stroke=\"#7A1CAC\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </div>\n </div>\n\n <!-- Expanded content (visible only when expanded) -->\n <div *ngIf=\"expandedItemId === item.id\" class=\"help-screen__content\">\n <ng-container>\n <div *ngFor=\"let paragraph of item.paragraphs\" class=\"help-screen__paragraph\">\n {{ paragraph }}\n </div>\n </ng-container>\n\n <app-button *ngIf=\"item?.chatWithUs\" variant=\"default\" [fullWidth]=\"true\" (click)=\"handleStartChat(item)\">{{\n 'ChatNow' | translate\n }}</app-button>\n </div>\n </app-card-content>\n </app-card>\n </ng-container>\n</div>\n", styles: ["@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.help-screen{display:flex;flex-direction:column;gap:1rem;position:relative}.help-screen__title{font-size:2.875rem;font-weight:700;color:#1f1f1f;margin-bottom:.75rem;line-height:1}.help-screen__card{cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);transform-origin:top;background-color:#fff}.help-screen__card:hover{box-shadow:#64646f33 0 7px 29px;transform:translateY(-2px)}.help-screen__card.expanded{background-color:#fff}.help-screen__header{display:flex;align-items:center;justify-content:space-between;padding:.5rem}.help-screen__title-text{font-size:1rem;color:#606060;font-weight:600}.help-screen__arrow-container{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:9999px;color:#ad49e1;transition:transform .3s cubic-bezier(.4,0,.2,1)}.help-screen__arrow-container--expanded{transform:rotate(90deg)}.help-screen__arrow-container--collapsed{transform:rotate(270deg)}.help-screen__arrow-container img{width:1rem;height:1rem}.help-screen__content{animation:slideDown .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;gap:.5rem;margin-top:1rem;padding:.5rem;transform-origin:top}.help-screen__paragraph{font-size:1rem;color:#0a0a0a;margin-bottom:.5rem}.help-screen__button{width:100%;margin-top:.5rem;animation:slideDown .3s cubic-bezier(.4,0,.2,1) .1s}\n"] }] }], propDecorators: { helpScreenData: [{ type: Input }], title: [{ type: Input }], handleStartNewChat: [{ type: Output }] } }); class HeaderComponent { headerType = 'standard'; showBackButton = false; showLogo = true; logoSrc = '/logo-white.svg'; logoAlt = 'BabylAI Logo'; language = 'en'; showCloseButton = false; onBack = new EventEmitter(); onClose = new EventEmitter(); get isRtl() { return this.language === 'ar'; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: HeaderComponent, isStandalone: true, selector: "app-header", inputs: { headerType: "headerType", showBackButton: "showBackButton", showLogo: "showLogo", logoSrc: "logoSrc", logoAlt: "logoAlt", language: "language", showCloseButton: "showCloseButton" }, outputs: { onBack: "onBack", onClose: "onClose" }, ngImport: i0, template: ` <div class="header"> <app-button *ngIf="showBackButton" variant="icon-bg" className="button--white-bg" [direction]="isRtl ? 'rtl' : 'ltr'" (click)="onBack.emit()" > <svg [style.transform]="isRtl ? 'rotate(180deg)' : 'rotate(0deg)'" class="header__back-button-icon" width="8" height="16" viewBox="0 0 8 16" fill="none" xmlns="http://www.w3.org/2000/svg" > <path d="M7 15L1 8L2.5 6.25M7 1L5 3.333" stroke="#7A1CAC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> </svg> </app-button> <app-button *ngIf="showCloseButton" variant="icon-bg" [direction]="isRtl ? 'rtl' : 'ltr'" className="button--close-button" (click)="onClose.emit()" > <svg width="40" height="40" viewBox="0 0 55 55" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M33.8568 21.1458L21.1484 33.8541M21.1484 21.1458L33.8568 33.8541M14.7943 5.48404C18.6562 3.24921 23.0407 2.07593 27.5026 2.08329C41.5402 2.08329 52.9193 13.4623 52.9193 27.5C52.9193 41.5376 41.5402 52.9166 27.5026 52.9166C13.465 52.9166 2.08594 41.5376 2.08594 27.5C2.08594 22.8716 3.32373 18.5279 5.48669 14.7916" stroke="white" stroke-width="3" stroke-linecap="round" /> </svg> </app-button> <svg *ngIf="show