@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bloonio-chat-angular.mjs","sources":["../../src/lib/bloonio-chat.token.ts","../../src/lib/bloonio-chat.service.ts","../../src/lib/components/launcher.component.ts","../../src/lib/components/input-bar.component.ts","../../src/lib/components/message-list.component.ts","../../src/lib/components/quick-replies.component.ts","../../src/lib/components/panel.component.ts","../../src/lib/components/thread.component.ts","../../src/index.ts","../../src/bloonio-chat-angular.ts"],"sourcesContent":["/**\n * Dependency-injection plumbing for the Angular chat SDK.\n *\n * Consumers wire the SDK into their app via the standard Angular pattern\n * documented in docs/12 §7.3:\n *\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideBloonioChat({\n * tenantId: env.CHAT_TENANT_ID,\n * publicKey: env.CHAT_PUBLIC_KEY,\n * baseUrl: 'https://chat-relay.bloonio.com',\n * visitorTokenProvider: () => inject(HttpClient).get<{token: string}>(\n * '/api/chat/visitor-token'\n * ).pipe(map(r => r.token)),\n * locale: 'fr',\n * }),\n * ],\n * };\n */\n\nimport {\n inject,\n InjectionToken,\n type EnvironmentProviders,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport type { Observable } from 'rxjs';\n\nimport type { Attachment } from '@bloonio/chat-client-core';\n\n/** Caller-supplied resolver for the tenant-signed visitor token.\n *\n * Two acceptable shapes:\n * - Returns nothing (or `null`) → SDK falls back to anonymous mode.\n * A session is minted via `/create/visitor-session` and the visitor\n * stays unidentified until you supply one.\n * - Returns a string / Observable<string> → SDK calls `/identify/visitor`\n * with this signed token to bind the anonymous session to a tenant user.\n */\nexport type VisitorTokenProvider =\n | (() => string | null | Promise<string | null> | Observable<string | null>)\n | undefined;\n\nexport interface BloonioChatConfig {\n /** Tenant UUID v7 (the canonical handle issued by chat_relay). */\n tenantId: string;\n /** `pk_…` widget key bound to the tenant's `allowed_origins`. */\n publicKey: string;\n /** Base URL of the chat relay (https://chat-relay.bloonio.com or local). */\n baseUrl: string;\n /** Visitor-identification resolver. See {@link VisitorTokenProvider}. */\n visitorTokenProvider?: VisitorTokenProvider;\n /** Default locale for new conversations. */\n locale?: string;\n /** Free-form metadata attached to every visitor session. */\n metadata?: Record<string, unknown>;\n /** Optional override of `window.location.origin` (tests / SSR). */\n origin?: string;\n /**\n * Pre-fetched attachments to attach to the first outbound message.\n * Reserved for phase 11; kept on the interface for forward compat.\n */\n attachments?: Attachment[];\n}\n\nexport const BLOONIO_CHAT_CONFIG = new InjectionToken<BloonioChatConfig>(\n 'BLOONIO_CHAT_CONFIG',\n);\n\n/** Standalone-provider factory.\n *\n * Returns ``EnvironmentProviders`` so consumers can spread it into\n * ``app.config.ts``'s providers array. The service itself is\n * ``providedIn: 'root'`` so we don't need an explicit provider entry — we\n * only need the config token to be registered.\n */\nexport function provideBloonioChat(\n config: BloonioChatConfig,\n): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: BLOONIO_CHAT_CONFIG, useValue: config },\n ]);\n}\n\n/** Internal accessor used by the components to fail loud when the\n * consumer forgot ``provideBloonioChat()``. */\nexport function injectBloonioChatConfig(): BloonioChatConfig {\n const cfg = inject(BLOONIO_CHAT_CONFIG, { optional: true });\n if (!cfg) {\n throw new Error(\n '[bloonio-chat-angular] provideBloonioChat() was not called in your app.config.ts. ' +\n 'Add it to providers: [...] alongside provideRouter/provideHttpClient.',\n );\n }\n return cfg;\n}\n","/**\n * BloonioChatService — signals-based reactive state container.\n *\n * Mirrors the Preact widget's `Widget.tsx` state machine but uses Angular\n * signals so templates auto-track the slots they read. Public surface:\n *\n * readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);\n * readonly session = signal<CreateVisitorSessionResponse | null>(null);\n * readonly messages = signal<ChatMessage[]>([]);\n * readonly botTyping = signal(false);\n * readonly open = signal(false);\n * readonly connecting= signal(false);\n * readonly unreadCount = computed(...);\n *\n * async open(): Promise<void>\n * close(): void\n * async toggle(): Promise<void>\n * sendMessage(text: string): void\n *\n * SSR-safe: every transport call (fetch, WebSocket) is gated behind a\n * platform check via `afterNextRender()` so server pre-rendering doesn't\n * try to open sockets.\n */\n\nimport {\n afterNextRender,\n computed,\n DestroyRef,\n inject,\n Injectable,\n signal,\n type Signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID } from '@angular/core';\nimport { firstValueFrom, isObservable } from 'rxjs';\n\nimport {\n ChatClient,\n WSClient,\n type CreateVisitorSessionResponse,\n type ServerMsgFrame,\n type WidgetBootstrapConfig,\n} from '@bloonio/chat-client-core';\n\nimport { injectBloonioChatConfig } from './bloonio-chat.token';\n\nexport interface ChatMessage {\n id: string;\n from: 'visitor' | 'bot' | 'agent' | 'system';\n content: string;\n pending?: boolean;\n client_msg_id?: string;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class BloonioChatService {\n private readonly config = injectBloonioChatConfig();\n private readonly platformId = inject(PLATFORM_ID);\n private readonly destroyRef = inject(DestroyRef);\n\n /** True when running in the browser (vs. server pre-render). */\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n\n /** Bootstrap config — populated by the first `loadBootstrap()` call. */\n readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);\n /** Visitor session (id + JWT + expires_at) — set lazily on first open. */\n readonly session = signal<CreateVisitorSessionResponse | null>(null);\n /** Message log, oldest → newest. */\n readonly messages = signal<ChatMessage[]>([]);\n /** True between bot's typing(start) and typing(stop) frames. */\n readonly botTyping = signal(false);\n /** Panel visibility — drives Launcher / Panel rendering. */\n readonly open = signal(false);\n /** True while `ensureSession()` is in flight (initial WS handshake). */\n readonly connecting = signal(false);\n /** Set when the WS handshake fails permanently (e.g. tenant suspended). */\n readonly fatalError = signal<string | null>(null);\n\n /** Number of bot/agent messages received while the panel was closed.\n * Resets to 0 the moment the panel opens. */\n private readonly _seenCount = signal(0);\n readonly unreadCount: Signal<number> = computed(() => {\n if (this.open()) return 0;\n const total = this.messages().filter(\n (m) => m.from === 'bot' || m.from === 'agent',\n ).length;\n return Math.max(0, total - this._seenCount());\n });\n\n private client: ChatClient | null = null;\n private ws: WSClient | null = null;\n private bootstrapPromise: Promise<WidgetBootstrapConfig | null> | null = null;\n private sessionPromise: Promise<CreateVisitorSessionResponse | null> | null = null;\n\n constructor() {\n // SSR safety: schedule the initial bootstrap fetch on first browser render.\n afterNextRender(() => {\n this.loadBootstrap();\n });\n\n this.destroyRef.onDestroy(() => {\n this.ws?.close();\n this.ws = null;\n });\n }\n\n // ── Public methods ─────────────────────────────────────────────────────\n\n /** Toggle panel visibility. First open also opens the WS session. */\n async toggle(): Promise<void> {\n const next = !this.open();\n this.open.set(next);\n if (next) {\n this._seenCount.set(\n this.messages().filter((m) => m.from === 'bot' || m.from === 'agent').length,\n );\n await this.ensureSession();\n }\n }\n\n /** Force the panel open. */\n async openPanel(): Promise<void> {\n if (this.open()) return;\n await this.toggle();\n }\n\n /** Force the panel closed. Keeps the WS open so reopening is instant. */\n closePanel(): void {\n this.open.set(false);\n }\n\n /** Send a visitor message. No-op if the WS isn't connected. */\n sendMessage(content: string): void {\n const trimmed = content.trim();\n if (!trimmed || !this.ws) return;\n const clientMsgId = `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n this.messages.update((prev) => [\n ...prev,\n {\n id: clientMsgId,\n client_msg_id: clientMsgId,\n from: 'visitor',\n content: trimmed,\n pending: true,\n },\n ]);\n this.ws.send({ type: 'msg', client_msg_id: clientMsgId, content: trimmed });\n }\n\n // ── Internals ──────────────────────────────────────────────────────────\n\n private async loadBootstrap(): Promise<WidgetBootstrapConfig | null> {\n if (!this.isBrowser) return null;\n if (this.bootstrap()) return this.bootstrap();\n if (this.bootstrapPromise) return this.bootstrapPromise;\n\n this.bootstrapPromise = (async () => {\n try {\n const client = this.getClient();\n const cfg = await client.fetchWidgetConfig({\n tenantId: this.config.tenantId,\n widgetPublicKey: this.config.publicKey,\n origin: this.resolvedOrigin(),\n });\n this.bootstrap.set(cfg);\n return cfg;\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n this.fatalError.set(`Bootstrap failed: ${msg}`);\n return null;\n }\n })();\n return this.bootstrapPromise;\n }\n\n private async ensureSession(): Promise<CreateVisitorSessionResponse | null> {\n if (!this.isBrowser) return null;\n if (this.session()) return this.session();\n if (this.sessionPromise) return this.sessionPromise;\n\n this.connecting.set(true);\n this.sessionPromise = (async () => {\n try {\n // Make sure bootstrap is resolved first — the visibility/online\n // flags it carries influence whether we connect at all.\n await this.loadBootstrap();\n\n const client = this.getClient();\n const session = await client.createVisitorSession({\n tenantId: this.config.tenantId,\n widgetPublicKey: this.config.publicKey,\n origin: this.resolvedOrigin(),\n locale: this.config.locale ?? 'fr',\n metadata: this.config.metadata ?? {},\n });\n this.session.set(session);\n this.openWebSocket(session);\n // Optional: identify the visitor server-side if the consumer wired\n // a token provider.\n this.tryIdentify(session).catch(() => undefined);\n return session;\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n this.fatalError.set(`Session failed: ${msg}`);\n return null;\n } finally {\n this.connecting.set(false);\n }\n })();\n return this.sessionPromise;\n }\n\n private openWebSocket(session: CreateVisitorSessionResponse): void {\n if (this.ws) return;\n const ws = new WSClient(\n {\n baseUrl: this.config.baseUrl,\n visitorToken: session.visitor_token,\n heartbeatIntervalMs: 30_000,\n },\n {\n onWelcome: () => undefined,\n onMessage: (frame: ServerMsgFrame) => {\n this.messages.update((prev) => [\n ...prev,\n { id: frame.id, from: frame.from, content: frame.content },\n ]);\n },\n onAck: (frame) => {\n this.messages.update((prev) =>\n prev.map((m) =>\n m.client_msg_id === frame.client_msg_id\n ? { ...m, id: frame.server_msg_id, pending: false }\n : m,\n ),\n );\n },\n onTyping: (frame) => {\n if (frame.from === 'bot' || frame.from === 'agent') {\n this.botTyping.set(frame.state === 'start');\n }\n },\n onError: (frame) => {\n this.messages.update((prev) => [\n ...prev,\n { id: `err_${Date.now()}`, from: 'system', content: `Error: ${frame.message}` },\n ]);\n },\n // Phase 10c — agent takeover visibility. Renders a system message\n // and clears any stale bot-typing indicator (the bot's mid-stream\n // turn may have been pre-empted by the operator claim).\n onAgentJoined: (frame) => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_join_${Date.now()}`,\n from: 'system',\n content: `${frame.agent_display_name} has joined the chat.`,\n },\n ]);\n this.botTyping.set(false);\n },\n onAgentLeft: () => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_left_${Date.now()}`,\n from: 'system',\n content: 'The agent has left the chat.',\n },\n ]);\n this.botTyping.set(false);\n },\n // Phase 10f — chat_api opened a support ticket.\n onTicketCreated: (frame) => {\n const sla =\n frame.sla_hours === 1\n ? 'within 1h'\n : frame.sla_hours <= 24\n ? `within ${frame.sla_hours}h`\n : `within ${Math.round(frame.sla_hours / 24)}d`;\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_ticket_${frame.ticket_id}`,\n from: 'system',\n content:\n `🎫 Support ticket #${frame.ticket_number} created — ` +\n `${frame.category} · ${frame.priority}. ` +\n `We'll follow up ${sla}.`,\n },\n ]);\n },\n },\n );\n this.ws = ws;\n ws.connect();\n }\n\n private async tryIdentify(\n session: CreateVisitorSessionResponse,\n ): Promise<void> {\n const provider = this.config.visitorTokenProvider;\n if (!provider) return;\n\n let raw: string | null | undefined;\n const result = provider();\n if (typeof result === 'string') {\n raw = result;\n } else if (isObservable(result)) {\n raw = await firstValueFrom(result);\n } else {\n raw = await result;\n }\n if (!raw) return;\n\n // Caller's token is `userId:signature` (colon-delimited). Falling back\n // to plain user_id when no colon is present is too easy to misuse —\n // require explicit signature to avoid identity forgery footguns.\n const sep = raw.indexOf(':');\n if (sep < 0) {\n this.fatalError.set(\n 'visitorTokenProvider returned a value without \"userId:signature\" — refusing to identify.',\n );\n return;\n }\n const userId = raw.slice(0, sep);\n const userSignature = raw.slice(sep + 1);\n await this.getClient().identifyVisitor({\n visitorToken: session.visitor_token,\n userId,\n userSignature,\n });\n }\n\n private getClient(): ChatClient {\n if (!this.client) {\n this.client = new ChatClient({ baseUrl: this.config.baseUrl });\n }\n return this.client;\n }\n\n private resolvedOrigin(): string {\n if (this.config.origin) return this.config.origin;\n if (this.isBrowser && typeof window !== 'undefined') {\n return window.location.origin;\n }\n // SSR-safe fallback; the relay validates origins server-side anyway.\n return '';\n }\n}\n","/**\n * Floating launcher — circular chat bubble with optional label.\n * Wraps the `BloonioChatService` so consumers just drop\n * `<bloonio-chat-launcher />` somewhere and the SDK handles state.\n */\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\n\nimport { BloonioChatService } from '../bloonio-chat.service';\n\n@Component({\n selector: 'bloonio-chat-launcher',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-root\" [attr.data-position]=\"position()\" [style]=\"themeStyle()\">\n <button\n type=\"button\"\n class=\"bch-launcher\"\n [attr.aria-label]=\"ariaLabel()\"\n (click)=\"onToggle()\"\n >\n @if (label()) {\n <span class=\"bch-launcher-label\">{{ label() }}</span>\n }\n <span class=\"bch-launcher-bubble\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\" />\n </svg>\n </span>\n </button>\n </div>\n `,\n})\nexport class BloonioChatLauncherComponent {\n protected readonly chat = inject(BloonioChatService);\n\n protected readonly position = computed(\n () => this.chat.bootstrap()?.widget_position ?? 'right',\n );\n\n /** Tint the bubble with the tenant's brand colour (falls back to the\n * stylesheet default until bootstrap config loads). */\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n protected readonly label = computed(() => {\n const b = this.chat.bootstrap();\n return b?.button_label_enabled ? b.button_label_text : null;\n });\n\n protected readonly ariaLabel = computed(() => {\n const unread = this.chat.unreadCount();\n return unread > 0\n ? `Open chat (${unread} unread)`\n : 'Open chat';\n });\n\n onToggle(): void {\n void this.chat.toggle();\n }\n}\n","/**\n * Visitor input bar — multi-line textarea + send button. Enter sends,\n * Shift+Enter inserts a newline. Emits `send` with the trimmed content\n * once it's non-empty.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n signal,\n} from '@angular/core';\n\n@Component({\n selector: 'bloonio-chat-input-bar',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-input\">\n <textarea\n rows=\"1\"\n placeholder=\"Enter your message...\"\n [disabled]=\"disabled\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKey($event)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"bch-input-send\"\n aria-label=\"Send\"\n [disabled]=\"!canSend()\"\n (click)=\"submit()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n </button>\n </div>\n `,\n})\nexport class InputBarComponent {\n @Input() disabled = false;\n @Output() send = new EventEmitter<string>();\n\n protected readonly value = signal('');\n\n protected canSend(): boolean {\n return !this.disabled && this.value().trim().length > 0;\n }\n\n onInput(e: Event): void {\n this.value.set((e.target as HTMLTextAreaElement).value);\n }\n\n onKey(e: KeyboardEvent): void {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.submit();\n }\n }\n\n submit(): void {\n if (!this.canSend()) return;\n const trimmed = this.value().trim();\n this.send.emit(trimmed);\n this.value.set('');\n }\n}\n","/**\n * Renders the message log + typing indicator. Pure presentational —\n * receives messages + typing flag as inputs so it can also be used\n * outside the panel (custom layouts, debug screens).\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n Input,\n} from '@angular/core';\n\nimport type { ChatMessage } from '../bloonio-chat.service';\n\n@Component({\n selector: 'bloonio-chat-message-list',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @for (m of messages; track m.id) {\n <div\n class=\"bch-bubble\"\n [attr.data-from]=\"m.from\"\n [attr.data-pending]=\"m.pending ? 'true' : null\"\n >\n {{ m.content }}\n </div>\n }\n @if (botTyping) {\n <div class=\"bch-typing\" aria-label=\"Assistant is typing\">\n <span></span><span></span><span></span>\n </div>\n }\n `,\n})\nexport class MessageListComponent {\n @Input({ required: true }) messages: ChatMessage[] = [];\n @Input() botTyping = false;\n}\n","/**\n * `<bloonio-chat-quick-replies>` — tenant-configured opening prompt chips,\n * shown under the welcome message until the visitor sends their first\n * message. Tapping a chip sends its text as that first message.\n *\n * Driven by `BloonioChatService` (reads `bootstrap().quick_replies` +\n * `messages()`), so it self-hides once the conversation starts. Used inside\n * both the floating panel and the headless thread.\n */\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\n\nimport { BloonioChatService } from '../bloonio-chat.service';\n\n@Component({\n selector: 'bloonio-chat-quick-replies',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (replies().length) {\n <div class=\"bch-quick-replies\" role=\"group\" aria-label=\"Suggested replies\">\n @for (reply of replies(); track reply) {\n <button type=\"button\" class=\"bch-quick-reply\" (click)=\"pick(reply)\">\n {{ reply }}\n </button>\n }\n </div>\n }\n `,\n})\nexport class QuickRepliesComponent {\n protected readonly chat = inject(BloonioChatService);\n\n /** Show the configured chips only until the visitor's first message. */\n protected readonly replies = computed<string[]>(() => {\n const bootstrap = this.chat.bootstrap();\n if (!bootstrap || bootstrap.quick_replies.length === 0) return [];\n const visitorSpoke = this.chat.messages().some((m) => m.from === 'visitor');\n return visitorSpoke ? [] : bootstrap.quick_replies;\n });\n\n protected pick(text: string): void {\n this.chat.sendMessage(text);\n }\n}\n","/**\n * Floating or docked chat panel. Renders the header, message list, input,\n * and footer. Sits inside `<bloonio-chat-launcher>` for the floating\n * variant, or stand-alone for `mode=\"docked\"`/`mode=\"inline\"`.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n Input,\n signal,\n type AfterViewChecked,\n ElementRef,\n ViewChild,\n} from '@angular/core';\n\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { InputBarComponent } from './input-bar.component';\nimport { MessageListComponent } from './message-list.component';\nimport { QuickRepliesComponent } from './quick-replies.component';\n\nexport type ChatPanelMode = 'floating' | 'docked' | 'inline';\n\n@Component({\n selector: 'bloonio-chat-panel',\n standalone: true,\n imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div\n class=\"bch-root\"\n [class.bch-open]=\"visible()\"\n [attr.data-position]=\"position()\"\n [attr.data-mode]=\"mode\"\n >\n <div class=\"bch-panel\" [style]=\"themeStyle()\">\n <header class=\"bch-header\">\n <div class=\"bch-header-avatar\">\n @if (logoUrl()) {\n <img [src]=\"logoUrl()\" alt=\"\" />\n } @else {\n <span style=\"font-weight:600;font-size:15px\">{{ initials() }}</span>\n }\n </div>\n <div class=\"bch-header-title\">\n <div class=\"bch-header-name\">Chat with {{ displayName() }}</div>\n <div class=\"bch-header-status\">\n <span class=\"bch-header-status-dot\" [attr.data-online]=\"isOnlineStr()\"></span>\n {{ statusText() }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"bch-header-close\"\n aria-label=\"Close\"\n (click)=\"onClose()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"bch-messages\" #messages>\n <bloonio-chat-message-list\n [messages]=\"visibleMessages()\"\n [botTyping]=\"chat.botTyping()\"\n />\n </div>\n\n <bloonio-chat-quick-replies />\n\n <bloonio-chat-input-bar\n [disabled]=\"chat.connecting()\"\n (send)=\"onSend($event)\"\n />\n\n <div class=\"bch-footer\">\n Powered by\n <a href=\"https://bloonio.com\" target=\"_blank\" rel=\"noopener noreferrer\">Bloonio</a>\n </div>\n </div>\n </div>\n `,\n})\nexport class BloonioChatPanelComponent implements AfterViewChecked {\n /** floating = paired with launcher (default). docked/inline = always-on\n * inside the page flow (use for SSR landing pages or sidebar layouts). */\n @Input() mode: ChatPanelMode = 'floating';\n\n protected readonly chat = inject(BloonioChatService);\n\n @ViewChild('messages') private messagesRef?: ElementRef<HTMLDivElement>;\n private lastScrollLen = signal(-1);\n\n protected readonly position = computed(\n () => this.chat.bootstrap()?.widget_position ?? 'right',\n );\n\n protected readonly visible = computed(() => {\n // Floating mode: only when service.open() is true.\n // Docked/inline: always rendered if bootstrap is loaded.\n if (this.mode === 'floating') return this.chat.open();\n return !!this.chat.bootstrap();\n });\n\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n protected readonly displayName = computed(\n () => this.chat.bootstrap()?.branding_display_name\n ?? this.chat.bootstrap()?.tenant_name\n ?? 'Support',\n );\n\n protected readonly logoUrl = computed(\n () => this.chat.bootstrap()?.branding_logo_url ?? null,\n );\n\n protected readonly initials = computed(() =>\n this.displayName()\n .split(/\\s+/)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?',\n );\n\n protected readonly statusText = computed(() => {\n const b = this.chat.bootstrap();\n if (!b) return '';\n return b.is_online ? b.online_status_text : b.offline_status_text;\n });\n\n protected readonly isOnlineStr = computed(\n () => String(this.chat.bootstrap()?.is_online ?? false),\n );\n\n protected readonly visibleMessages = computed(() => {\n const b = this.chat.bootstrap();\n const messages = this.chat.messages();\n if (b && !b.is_online && messages.length === 0) {\n return [\n {\n id: 'sys_offline',\n from: 'system' as const,\n content: b.offline_message,\n },\n ];\n }\n return messages;\n });\n\n ngAfterViewChecked(): void {\n // Scroll-to-bottom whenever message count changes. We compare against a\n // stamped length so we don't churn when only typing/status updates fire.\n const el = this.messagesRef?.nativeElement;\n if (!el) return;\n const len = this.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);\n if (len !== this.lastScrollLen()) {\n el.scrollTop = el.scrollHeight;\n this.lastScrollLen.set(len);\n }\n }\n\n onClose(): void {\n this.chat.closePanel();\n }\n\n onSend(content: string): void {\n this.chat.sendMessage(content);\n }\n}\n","/**\n * `<bloonio-chat-thread>` — headless variant of the panel. Renders just\n * the message log + input, no header, no floating chrome. Used when the\n * tenant wants to embed the chat *inside* their own layout (e.g. inside\n * a support modal, a sidebar, a help center page).\n *\n * Theming and behaviour are otherwise identical — the same\n * `BloonioChatService` drives state, so a thread and a panel side-by-side\n * stay in sync.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n AfterViewChecked,\n ElementRef,\n ViewChild,\n signal,\n} from '@angular/core';\n\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { InputBarComponent } from './input-bar.component';\nimport { MessageListComponent } from './message-list.component';\nimport { QuickRepliesComponent } from './quick-replies.component';\n\n@Component({\n selector: 'bloonio-chat-thread',\n standalone: true,\n imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-root\" data-mode=\"inline\" [style]=\"themeStyle()\">\n <div class=\"bch-panel\">\n <div class=\"bch-messages\" #messages>\n <bloonio-chat-message-list\n [messages]=\"visibleMessages()\"\n [botTyping]=\"chat.botTyping()\"\n />\n </div>\n <bloonio-chat-quick-replies />\n <bloonio-chat-input-bar\n [disabled]=\"chat.connecting()\"\n (send)=\"chat.sendMessage($event)\"\n />\n </div>\n </div>\n `,\n})\nexport class BloonioChatThreadComponent implements AfterViewChecked {\n protected readonly chat = inject(BloonioChatService);\n\n @ViewChild('messages') private messagesRef?: ElementRef<HTMLDivElement>;\n private lastScrollLen = signal(-1);\n\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n protected readonly visibleMessages = computed(() => {\n const b = this.chat.bootstrap();\n const messages = this.chat.messages();\n if (b && !b.is_online && messages.length === 0) {\n return [\n {\n id: 'sys_offline',\n from: 'system' as const,\n content: b.offline_message,\n },\n ];\n }\n return messages;\n });\n\n ngAfterViewChecked(): void {\n const el = this.messagesRef?.nativeElement;\n if (!el) return;\n const len = this.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);\n if (len !== this.lastScrollLen()) {\n el.scrollTop = el.scrollHeight;\n this.lastScrollLen.set(len);\n }\n }\n\n /** Convenience: thread is meant to be always-on, so it opens the\n * service-level session as soon as it's instantiated.\n * (Floating panel waits for the launcher click.) */\n constructor() {\n void this.chat.openPanel();\n }\n}\n","/**\n * @bloonio/chat-angular — Angular library for the bloonio chat PaaS.\n *\n * Public surface:\n * - {@link provideBloonioChat} — environment-provider factory for app.config.ts\n * - {@link BloonioChatService} — signals-based reactive state\n * - Three standalone components:\n * <bloonio-chat-launcher> floating bubble (collapsed)\n * <bloonio-chat-panel> expandable card (the full chat UX)\n * <bloonio-chat-thread> headless variant for inline embedding\n *\n * Styles ship separately at `@bloonio/chat-angular/styles.css` — import once\n * in your global stylesheet (see README).\n */\n\nexport { provideBloonioChat, BLOONIO_CHAT_CONFIG, injectBloonioChatConfig } from './lib/bloonio-chat.token';\nexport type {\n BloonioChatConfig,\n VisitorTokenProvider,\n} from './lib/bloonio-chat.token';\n\nexport { BloonioChatService } from './lib/bloonio-chat.service';\nexport type { ChatMessage } from './lib/bloonio-chat.service';\n\nexport { BloonioChatLauncherComponent } from './lib/components/launcher.component';\nexport {\n BloonioChatPanelComponent,\n type ChatPanelMode,\n} from './lib/components/panel.component';\nexport { BloonioChatThreadComponent } from './lib/components/thread.component';\nexport { MessageListComponent } from './lib/components/message-list.component';\nexport { InputBarComponent } from './lib/components/input-bar.component';\nexport { QuickRepliesComponent } from './lib/components/quick-replies.component';\n\n// Re-export the bits of @bloonio/chat-client-core a consumer is likely to\n// touch (types, error classes). Keeps `import {…} from '@bloonio/chat-angular'`\n// the one entry point for most cases.\nexport type {\n WidgetBootstrapConfig,\n WidgetPosition,\n WidgetVisibility,\n ServerMsgFrame,\n MessageSender,\n} from '@bloonio/chat-client-core';\nexport {\n BloonioChatError,\n BloonioChatNetworkError,\n BloonioChatServerError,\n BloonioChatAuthError,\n} from '@bloonio/chat-client-core';\n\nexport const VERSION = '0.1.0';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bloonio-chat-angular.mjs","sources":["../../src/lib/bloonio-chat.token.ts","../../src/lib/bloonio-chat.i18n.ts","../../src/lib/bloonio-chat.service.ts","../../src/lib/components/launcher.component.ts","../../src/lib/components/input-bar.component.ts","../../src/lib/components/message-list.component.ts","../../src/lib/components/quick-replies.component.ts","../../src/lib/components/panel.component.ts","../../src/lib/components/thread.component.ts","../../src/index.ts","../../src/bloonio-chat-angular.ts"],"sourcesContent":["/**\n * Dependency-injection plumbing for the Angular chat SDK.\n *\n * Consumers wire the SDK into their app via the standard Angular pattern\n * documented in docs/12 §7.3:\n *\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideBloonioChat({\n * tenantId: env.CHAT_TENANT_ID,\n * publicKey: env.CHAT_PUBLIC_KEY,\n * baseUrl: 'https://chat-relay.example.com',\n * visitorTokenProvider: () => inject(HttpClient).get<{token: string}>(\n * '/api/chat/visitor-token'\n * ).pipe(map(r => r.token)),\n * locale: 'fr',\n * }),\n * ],\n * };\n */\n\nimport {\n inject,\n InjectionToken,\n type EnvironmentProviders,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport type { Observable } from 'rxjs';\n\nimport type { Attachment } from '@bloonio/chat-client-core';\n\nimport type { BloonioChatLocale } from './bloonio-chat.i18n';\n\n/** Caller-supplied resolver for the tenant-signed visitor token.\n *\n * Two acceptable shapes:\n * - Returns nothing (or `null`) → SDK falls back to anonymous mode.\n * A session is minted via `/create/visitor-session` and the visitor\n * stays unidentified until you supply one.\n * - Returns a string / Observable<string> → SDK calls `/identify/visitor`\n * with this signed token to bind the anonymous session to a tenant user.\n */\nexport type VisitorTokenProvider =\n | (() => string | null | Promise<string | null> | Observable<string | null>)\n | undefined;\n\n/** A fixed value, or a function (a signal works) re-read when it changes. */\nexport type BloonioChatOption<T> = T | (() => T);\n\n/** Reads a config entry that is either a value or a function. */\nexport function readOption<T>(option: BloonioChatOption<T> | undefined): T | undefined {\n return typeof option === 'function' ? (option as () => T)() : option;\n}\n\nexport interface BloonioChatConfig {\n /** Tenant UUID v7 (the canonical handle issued by chat_relay). */\n tenantId: string;\n /** `pk_…` widget key bound to the tenant's `allowed_origins`. */\n publicKey: string;\n /** Base URL of the chat relay (https://chat-relay.example.com or local). */\n baseUrl: string;\n /** Visitor-identification resolver. See {@link VisitorTokenProvider}. */\n visitorTokenProvider?: VisitorTokenProvider;\n /**\n * Language of the widget's own strings and of new conversations:\n * `'fr' | 'en' | 'zh'`, default `'en'`. Pass a function (a signal works) to\n * follow the host app's language at runtime.\n */\n locale?: BloonioChatLocale | (string & {}) | (() => string);\n // Host copy. The tenant's relay config holds one language; each entry\n // below replaces the matching tenant text, and as a function it follows\n // the host's language. Left out, the tenant's text is used.\n /**\n * Opening bubble, drawn like an assistant message at the top of a new\n * conversation. Local only: never sent or stored, and not shown above a\n * thread replayed after a reload. Shown in place of the tenant's offline\n * message.\n */\n greeting?: BloonioChatOption<string>;\n /** Label next to the launcher. An empty string shows none. */\n launcherLabel?: BloonioChatOption<string>;\n /** Header subtitle during opening hours. */\n onlineStatusText?: BloonioChatOption<string>;\n /** Header subtitle outside opening hours. */\n offlineStatusText?: BloonioChatOption<string>;\n /** Suggested replies shown until the visitor's first message. */\n quickReplies?: BloonioChatOption<string[]>;\n /** Free-form metadata attached to every visitor session. */\n metadata?: Record<string, unknown>;\n /** Optional override of `window.location.origin` (tests / SSR). */\n origin?: string;\n /**\n * Pre-fetched attachments to attach to the first outbound message.\n * Reserved for phase 11; kept on the interface for forward compat.\n */\n attachments?: Attachment[];\n}\n\nexport const BLOONIO_CHAT_CONFIG = new InjectionToken<BloonioChatConfig>(\n 'BLOONIO_CHAT_CONFIG',\n);\n\n/** Standalone-provider factory.\n *\n * Returns ``EnvironmentProviders`` so consumers can spread it into\n * ``app.config.ts``'s providers array. The service itself is\n * ``providedIn: 'root'`` so we don't need an explicit provider entry — we\n * only need the config token to be registered.\n */\nexport function provideBloonioChat(\n /** The config, or a factory run in an injection context (so it can\n * `inject()` the host's language signal, for instance). */\n config: BloonioChatConfig | (() => BloonioChatConfig),\n): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: BLOONIO_CHAT_CONFIG,\n useFactory: () => (typeof config === 'function' ? config() : config),\n },\n ]);\n}\n\n/** Internal accessor used by the components to fail loud when the\n * consumer forgot ``provideBloonioChat()``. */\nexport function injectBloonioChatConfig(): BloonioChatConfig {\n const cfg = inject(BLOONIO_CHAT_CONFIG, { optional: true });\n if (!cfg) {\n throw new Error(\n '[bloonio-chat-angular] provideBloonioChat() was not called in your app.config.ts. ' +\n 'Add it to providers: [...] alongside provideRouter/provideHttpClient.',\n );\n }\n return cfg;\n}\n","/**\n * Built-in strings for everything the widget renders itself. Text that comes\n * from the relay (status texts, offline message, quick replies, messages) is\n * shown as received.\n */\nimport { computed, type Signal } from '@angular/core';\n\nimport { injectBloonioChatConfig, readOption } from './bloonio-chat.token';\n\nexport type BloonioChatLocale = 'fr' | 'en' | 'zh';\n\nexport interface BloonioChatStrings {\n placeholder: string;\n send: string;\n close: string;\n openChat: string;\n closeChat: string;\n unread: (count: number) => string;\n typing: string;\n suggestedReplies: string;\n chatWith: (name: string) => string;\n /** Header name when the tenant has none. */\n support: string;\n poweredBy: string;\n connecting: string;\n unavailable: string;\n error: (message: string) => string;\n agentJoined: (name: string) => string;\n agentLeft: string;\n ticketCreated: (\n ticketNumber: string,\n category: string,\n priority: string,\n slaHours: number,\n ) => string;\n}\n\n/** \"4h\" up to a day, \"3d\" beyond. */\nconst sla = (hours: number, hour: string, day: string): string =>\n hours <= 24 ? `${hours}${hour}` : `${Math.round(hours / 24)}${day}`;\n\nconst STRINGS: Record<BloonioChatLocale, BloonioChatStrings> = {\n en: {\n placeholder: 'Enter your message...',\n send: 'Send',\n close: 'Close',\n openChat: 'Open chat',\n closeChat: 'Close chat',\n unread: (n) => `Open chat (${n} unread)`,\n typing: 'Assistant is typing',\n suggestedReplies: 'Suggested replies',\n chatWith: (name) => `Chat with ${name}`,\n support: 'Support',\n poweredBy: 'Powered by',\n connecting: 'Connecting…',\n unavailable: 'Chat is unavailable right now. Please try again later.',\n error: (message) => `Error: ${message}`,\n agentJoined: (name) => `${name} has joined the chat`,\n agentLeft: 'The agent has left the chat',\n ticketCreated: (n, category, priority, hours) =>\n `Support ticket #${n} created (${category}, ${priority}). ` +\n `We'll follow up within ${sla(hours, 'h', 'd')}.`,\n },\n fr: {\n placeholder: 'Votre message',\n send: 'Envoyer',\n close: 'Fermer',\n openChat: 'Ouvrir la conversation',\n closeChat: 'Fermer la conversation',\n unread: (n) => `Ouvrir la conversation (${n} non lu${n > 1 ? 's' : ''})`,\n typing: 'Réponse en cours de rédaction',\n suggestedReplies: 'Réponses suggérées',\n chatWith: (name) => `Discuter avec ${name}`,\n support: 'Assistance',\n poweredBy: 'Propulsé par',\n connecting: 'Connexion…',\n unavailable: 'La conversation est indisponible pour le moment. Réessayez plus tard.',\n error: (message) => `Erreur : ${message}`,\n agentJoined: (name) => `${name} a rejoint la conversation`,\n agentLeft: 'Le conseiller a quitté la conversation',\n ticketCreated: (n, category, priority, hours) =>\n `Demande d'assistance n° ${n} créée (${category}, ${priority}). ` +\n `Nous revenons vers vous sous ${sla(hours, ' h', ' j')}.`,\n },\n zh: {\n placeholder: '输入消息',\n send: '发送',\n close: '关闭',\n openChat: '打开聊天',\n closeChat: '关闭聊天',\n unread: (n) => `打开聊天(${n} 条未读)`,\n typing: '对方正在输入',\n suggestedReplies: '快捷回复',\n chatWith: (name) => `与 ${name} 聊天`,\n support: '客服',\n poweredBy: '技术支持',\n connecting: '正在连接…',\n unavailable: '聊天暂时无法使用,请稍后再试。',\n error: (message) => `错误:${message}`,\n agentJoined: (name) => `${name} 已加入对话`,\n agentLeft: '客服已离开对话',\n ticketCreated: (n, category, priority, hours) =>\n `工单 #${n} 已创建(${category},${priority})。` +\n `我们将在 ${sla(hours, ' 小时', ' 天')}内回复您。`,\n },\n};\n\n/** The strings of the configured locale, re-read whenever a signal behind\n * it changes. Anything other than fr / en / zh (after trimming a region:\n * \"fr-FR\") is \"en\". */\nexport function injectChatStrings(): Signal<BloonioChatStrings> {\n const { locale } = injectBloonioChatConfig();\n return computed(() => {\n const code = String(readOption(locale) ?? '').slice(0, 2).toLowerCase();\n return STRINGS[code in STRINGS ? (code as BloonioChatLocale) : 'en'];\n });\n}\n","/**\n * BloonioChatService — signals-based reactive state container.\n *\n * Mirrors the Preact widget's `Widget.tsx` state machine but uses Angular\n * signals so templates auto-track the slots they read. Public surface:\n *\n * readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);\n * readonly session = signal<CreateVisitorSessionResponse | null>(null);\n * readonly messages = signal<ChatMessage[]>([]);\n * readonly botTyping = signal(false);\n * readonly open = signal(false);\n * readonly connecting= signal(false);\n * readonly unreadCount = computed(...);\n *\n * async open(): Promise<void>\n * close(): void\n * async toggle(): Promise<void>\n * sendMessage(text: string): void\n *\n * SSR-safe: every transport call (fetch, WebSocket) and every storage access\n * is gated behind a platform check via `afterNextRender()` so server\n * pre-rendering doesn't try to open sockets.\n *\n * Resume: the visitor session is kept in `localStorage` per tenant. On the\n * next page load the token is refreshed, the thread replayed and the socket\n * reopened, so a visitor waiting for an agent keeps the conversation.\n */\n\nimport {\n afterNextRender,\n computed,\n DestroyRef,\n inject,\n Injectable,\n signal,\n type Signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID } from '@angular/core';\nimport { firstValueFrom, isObservable } from 'rxjs';\n\nimport {\n BloonioChatServerError,\n ChatClient,\n tenantKey,\n WSClient,\n type CreateVisitorSessionResponse,\n type ServerMsgFrame,\n type WidgetBootstrapConfig,\n} from '@bloonio/chat-client-core';\n\nimport { injectChatStrings } from './bloonio-chat.i18n';\nimport { injectBloonioChatConfig, readOption } from './bloonio-chat.token';\n\nexport interface ChatMessage {\n id: string;\n from: 'visitor' | 'bot' | 'agent' | 'system';\n content: string;\n pending?: boolean;\n client_msg_id?: string;\n}\n\n/** What survives a reload: enough to refresh the token and rejoin the thread. */\ntype StoredSession = Pick<\n CreateVisitorSessionResponse,\n 'session_id' | 'visitor_token' | 'expires_at'\n>;\n\n/** `sender` of a stored relay message → the widget's `from`. */\nconst HISTORY_FROM: Record<string, ChatMessage['from']> = {\n visitor: 'visitor',\n operator: 'agent',\n bot: 'bot',\n};\n\n/** Socket close codes the relay uses to refuse a visitor for good. */\nconst WS_BAD_TOKEN = 4401;\nconst WS_TENANT_UNAVAILABLE = 4403;\nconst WS_SESSION_EXPIRED = 4404;\n\n/** The relay sends one typing \"start\" per bot answer and the \"stop\" can be\n * lost, so the dots give up after this long without news. Long enough for\n * a slow answer, which clears them itself when it arrives. */\nconst TYPING_TIMEOUT_MS = 30_000;\n\nconst isAnswer = (m: ChatMessage): boolean => m.from === 'bot' || m.from === 'agent';\n\n@Injectable({ providedIn: 'root' })\nexport class BloonioChatService {\n private readonly config = injectBloonioChatConfig();\n private readonly platformId = inject(PLATFORM_ID);\n private readonly destroyRef = inject(DestroyRef);\n private readonly t = injectChatStrings();\n\n /** True when running in the browser (vs. server pre-render). */\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n /** Identified visitors are not resumed: a stored thread would outlive a\n * logout and show to the next account on the same browser.\n * shortcut: scope the storage key by user id to resume them too. */\n private readonly canPersist =\n this.isBrowser && !this.config.visitorTokenProvider;\n\n /** Bootstrap config — populated by the first `loadBootstrap()` call. */\n readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);\n /** Visitor session (id + JWT + expires_at) — set lazily on first open,\n * or on load when a stored session resumes. */\n readonly session = signal<CreateVisitorSessionResponse | null>(null);\n /** Message log, oldest → newest. */\n readonly messages = signal<ChatMessage[]>([]);\n /** True between bot's typing(start) and typing(stop) frames. */\n readonly botTyping = signal(false);\n /** Panel visibility — drives Launcher / Panel rendering. */\n readonly open = signal(false);\n /** True from the start of `ensureSession()` until the relay's welcome\n * frame: while it is set, nothing can be sent yet. */\n readonly connecting = signal(false);\n /** Set when bootstrap or the session fails; cleared by the next attempt\n * (reopening the panel retries). Diagnostic text, not shown to visitors. */\n readonly fatalError = signal<string | null>(null);\n\n /** True once a reload replayed an existing thread: the host's greeting\n * opens new conversations only. */\n private readonly replayed = signal(false);\n\n /** What the panel renders: the log, under the host's greeting when the\n * conversation is new, plus one status line when the chat is connecting,\n * unavailable, or closed for the day with nothing said yet. The greeting\n * and the status lines are local: they are not in `messages`, never sent\n * and never stored. */\n readonly visibleMessages: Signal<ChatMessage[]> = computed(() => {\n const messages = this.messages();\n const line = (id: string, from: ChatMessage['from'], content: string): ChatMessage => ({\n id,\n from,\n content,\n });\n if (this.fatalError() && !this.session()) {\n return [...messages, line('sys_unavailable', 'system', this.t().unavailable)];\n }\n const greeting = this.replayed() ? '' : readOption(this.config.greeting);\n const log = greeting ? [line('sys_greeting', 'bot', greeting), ...messages] : messages;\n if (messages.length) return log;\n if (this.connecting()) {\n return [...log, line('sys_connecting', 'system', this.t().connecting)];\n }\n // The host's greeting stands in for the tenant's offline message.\n const b = this.bootstrap();\n return b && !b.is_online && !greeting\n ? [line('sys_offline', 'system', b.offline_message)]\n : log;\n });\n\n /** Number of bot/agent messages received while the panel was closed.\n * Resets to 0 the moment the panel opens; what arrived while it was open\n * has been seen too. */\n private readonly _seenCount = signal(0);\n readonly unreadCount: Signal<number> = computed(() => {\n if (this.open()) return 0;\n const total = this.messages().filter(isAnswer).length;\n return Math.max(0, total - this._seenCount());\n });\n\n private client: ChatClient | null = null;\n private ws: WSClient | null = null;\n private bootstrapPromise: Promise<WidgetBootstrapConfig | null> | null = null;\n private sessionPromise: Promise<CreateVisitorSessionResponse | null> | null = null;\n /** Sockets the relay refused in a row (reset by a welcome frame). */\n private wsRejections = 0;\n private typingTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n // SSR safety: schedule the initial bootstrap fetch on first browser render.\n afterNextRender(() => {\n void this.loadBootstrap().then((bootstrap) => {\n // A returning visitor reconnects in the background, so an agent's\n // reply still arrives while the panel is closed.\n if (bootstrap && this.readStored()) void this.ensureSession();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n this.setTyping(false);\n this.ws?.close();\n this.ws = null;\n });\n }\n\n // ── Public methods ─────────────────────────────────────────────────────\n\n /** Toggle panel visibility. First open also opens the WS session. */\n async toggle(): Promise<void> {\n const next = !this.open();\n this.markSeen();\n this.open.set(next);\n if (next) await this.ensureSession();\n }\n\n /** Force the panel open. */\n async openPanel(): Promise<void> {\n if (this.open()) return;\n await this.toggle();\n }\n\n /** Force the panel closed. Keeps the WS open so reopening is instant. */\n closePanel(): void {\n this.markSeen();\n this.open.set(false);\n }\n\n /** Send a visitor message. No-op if the WS isn't connected. */\n sendMessage(content: string): void {\n const trimmed = content.trim();\n if (!trimmed || !this.ws) return;\n const clientMsgId = `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n this.messages.update((prev) => [\n ...prev,\n {\n id: clientMsgId,\n client_msg_id: clientMsgId,\n from: 'visitor',\n content: trimmed,\n pending: true,\n },\n ]);\n this.ws.send({ type: 'msg', client_msg_id: clientMsgId, content: trimmed });\n }\n\n // ── Internals ──────────────────────────────────────────────────────────\n\n /** Everything received so far counts as read (on open and on close). */\n private markSeen(): void {\n this._seenCount.set(this.messages().filter(isAnswer).length);\n }\n\n private setTyping(typing: boolean): void {\n clearTimeout(this.typingTimer);\n this.botTyping.set(typing);\n if (typing) {\n this.typingTimer = setTimeout(() => this.botTyping.set(false), TYPING_TIMEOUT_MS);\n }\n }\n\n private async loadBootstrap(): Promise<WidgetBootstrapConfig | null> {\n if (!this.isBrowser) return null;\n if (this.bootstrap()) return this.bootstrap();\n if (this.bootstrapPromise) return this.bootstrapPromise;\n\n this.bootstrapPromise = (async () => {\n try {\n const client = this.getClient();\n const cfg = await client.fetchWidgetConfig({\n tenantId: this.config.tenantId,\n widgetPublicKey: this.config.publicKey,\n origin: this.resolvedOrigin(),\n });\n this.bootstrap.set(cfg);\n return cfg;\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n this.fatalError.set(`Bootstrap failed: ${msg}`);\n return null;\n }\n })();\n // A failed bootstrap is fetched again on the next attempt.\n void this.bootstrapPromise.then((cfg) => {\n if (!cfg) this.bootstrapPromise = null;\n });\n return this.bootstrapPromise;\n }\n\n /** Resume the stored session, or — once the panel is open — mint a new\n * one. A closed panel never creates a session by itself. */\n private async ensureSession(): Promise<CreateVisitorSessionResponse | null> {\n if (!this.isBrowser) return null;\n if (this.session()) return this.session();\n if (this.sessionPromise) return this.sessionPromise;\n\n this.connecting.set(true);\n this.fatalError.set(null);\n this.sessionPromise = (async () => {\n try {\n // Bootstrap first: without it the relay is unreachable or refuses\n // this origin, and `fatalError` already says why.\n const bootstrap = await this.loadBootstrap();\n if (!bootstrap) return null;\n\n const session =\n (await this.resumeSession(bootstrap)) ??\n (this.open() ? await this.createSession() : null);\n if (!session) return null;\n this.session.set(session);\n this.openWebSocket(session);\n // Optional: identify the visitor server-side if the consumer wired\n // a token provider.\n this.tryIdentify(session).catch(() => undefined);\n return session;\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'unknown error';\n this.fatalError.set(`Session failed: ${msg}`);\n return null;\n } finally {\n // With a session, `connecting` lasts until the socket's welcome.\n // Without one, let the next open (or load) try again.\n if (!this.session()) {\n this.connecting.set(false);\n this.sessionPromise = null;\n }\n }\n })();\n return this.sessionPromise;\n }\n\n private async createSession(): Promise<CreateVisitorSessionResponse> {\n const session = await this.getClient().createVisitorSession({\n tenantId: this.config.tenantId,\n widgetPublicKey: this.config.publicKey,\n origin: this.resolvedOrigin(),\n // The configured value as given (a function is read now). With none,\n // \"fr\": what 0.1.x sent, kept so existing hosts see no change.\n locale: readOption(this.config.locale) ?? 'fr',\n metadata: this.config.metadata ?? {},\n });\n this.writeStored(session);\n return session;\n }\n\n /** Rejoin the stored session: refresh its token (the relay accepts an\n * expired one while the session lives), replay the thread, hand back the\n * same session. Null when there is none or it cannot be resumed — the\n * caller then starts a fresh one, and the visitor sees no error. */\n private async resumeSession(\n bootstrap: WidgetBootstrapConfig,\n ): Promise<CreateVisitorSessionResponse | null> {\n const stored = this.readStored();\n if (!stored) return null;\n try {\n const refreshed = await this.relay<StoredSession>(\n 'POST',\n '/refresh/visitor-token',\n stored.visitor_token,\n );\n if (typeof refreshed?.visitor_token !== 'string') {\n throw new BloonioChatServerError('malformed refresh response', 400, refreshed);\n }\n // shortcut: replays the newest page only (50 messages), page with\n // `next_before` if longer threads need their full history.\n const history = await this.relay<{\n messages?: { message_id: string; sender: string; content: string }[];\n }>('GET', '/fetch/visitor-messages', refreshed.visitor_token);\n const messages = (history.messages ?? []).map(\n (m): ChatMessage => ({\n id: m.message_id,\n from: HISTORY_FROM[m.sender] ?? 'system',\n content: m.content,\n }),\n );\n this.messages.set(messages);\n this.replayed.set(messages.length > 0);\n // Replayed answers are not \"unread\".\n this.markSeen();\n this.writeStored(refreshed);\n return { ...refreshed, widget_config: bootstrap };\n } catch (err) {\n // The relay refused the session (4xx): forget it. A network failure\n // or a 5xx keeps it, so the next page load can still resume.\n if (err instanceof BloonioChatServerError && err.status < 500) {\n this.writeStored(null);\n }\n return null;\n }\n }\n\n /** The two visitor endpoints `ChatClient` (core 0.1.0) does not wrap. */\n private async relay<T>(\n method: 'GET' | 'POST',\n path: string,\n visitorToken: string,\n ): Promise<T> {\n const res = await fetch(\n `${this.config.baseUrl.replace(/\\/+$/, '')}/api/v1${path}`,\n {\n method,\n headers: { Authorization: `Bearer ${visitorToken}` },\n signal: AbortSignal.timeout(10_000),\n },\n );\n if (!res.ok) {\n throw new BloonioChatServerError(`HTTP ${res.status}`, res.status, null);\n }\n const json = await res.json();\n return (json?.data ?? json) as T;\n }\n\n private readStored(): StoredSession | null {\n if (!this.canPersist) return null;\n try {\n const stored = JSON.parse(localStorage.getItem(this.storageKey()) ?? 'null');\n return typeof stored?.visitor_token === 'string' ? stored : null;\n } catch {\n return null; // storage blocked, or the entry is not ours\n }\n }\n\n private writeStored(session: StoredSession | null): void {\n if (!this.canPersist) return;\n try {\n if (!session) return localStorage.removeItem(this.storageKey());\n const { session_id, visitor_token, expires_at } = session;\n localStorage.setItem(\n this.storageKey(),\n JSON.stringify({ session_id, visitor_token, expires_at }),\n );\n } catch {\n // Storage unavailable: the conversation just won't survive a reload.\n }\n }\n\n private storageKey(): string {\n return tenantKey(this.config.tenantId, 'session');\n }\n\n /** The relay closed the socket for good. WSClient would retry the same\n * dead token forever, so stop it and recover here. */\n private onSocketRejected(ws: WSClient, code: number): void {\n ws.close();\n if (this.ws !== ws) return;\n this.ws = null;\n this.session.set(null);\n this.sessionPromise = null;\n this.setTyping(false);\n this.wsRejections += 1;\n if (code === WS_TENANT_UNAVAILABLE || this.wsRejections > 2) {\n this.connecting.set(false);\n this.fatalError.set(`Socket refused (${code})`);\n return;\n }\n // First refusal: the token may only have expired, and resuming\n // refreshes it. Refused again: drop the session and start fresh.\n if (this.wsRejections > 1) this.writeStored(null);\n void this.ensureSession();\n }\n\n private openWebSocket(session: CreateVisitorSessionResponse): void {\n if (this.ws) return;\n const ws: WSClient = new WSClient(\n {\n baseUrl: this.config.baseUrl,\n visitorToken: session.visitor_token,\n heartbeatIntervalMs: 30_000,\n },\n {\n onWelcome: () => {\n this.wsRejections = 0;\n this.connecting.set(false);\n },\n onClose: ({ code }) => {\n if (\n code !== WS_BAD_TOKEN &&\n code !== WS_SESSION_EXPIRED &&\n code !== WS_TENANT_UNAVAILABLE\n ) {\n return; // a dropped connection: WSClient reconnects by itself\n }\n // WSClient schedules its reconnect right after this callback, so\n // it can only be cancelled one tick later.\n queueMicrotask(() => this.onSocketRejected(ws, code));\n },\n onMessage: (frame: ServerMsgFrame) => {\n // The answer is here: whoever was typing has finished, even if\n // the \"stop\" frame never comes.\n if (frame.from !== 'system') this.setTyping(false);\n this.messages.update((prev) => [\n ...prev,\n { id: frame.id, from: frame.from, content: frame.content },\n ]);\n },\n onAck: (frame) => {\n this.messages.update((prev) =>\n prev.map((m) =>\n m.client_msg_id === frame.client_msg_id\n ? { ...m, id: frame.server_msg_id, pending: false }\n : m,\n ),\n );\n },\n onTyping: (frame) => {\n if (frame.from === 'bot' || frame.from === 'agent') {\n this.setTyping(frame.state === 'start');\n }\n },\n onError: (frame) => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `err_${Date.now()}`,\n from: 'system',\n content: this.t().error(frame.message),\n },\n ]);\n },\n // Phase 10c — agent takeover visibility. Renders a system message\n // and clears any stale bot-typing indicator (the bot's mid-stream\n // turn may have been pre-empted by the operator claim).\n onAgentJoined: (frame) => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_join_${Date.now()}`,\n from: 'system',\n content: this.t().agentJoined(frame.agent_display_name),\n },\n ]);\n this.setTyping(false);\n },\n onAgentLeft: () => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_left_${Date.now()}`,\n from: 'system',\n content: this.t().agentLeft,\n },\n ]);\n this.setTyping(false);\n },\n // Phase 10f — chat_api opened a support ticket.\n onTicketCreated: (frame) => {\n this.messages.update((prev) => [\n ...prev,\n {\n id: `sys_ticket_${frame.ticket_id}`,\n from: 'system',\n content: this.t().ticketCreated(\n frame.ticket_number,\n frame.category,\n frame.priority,\n frame.sla_hours,\n ),\n },\n ]);\n },\n },\n );\n this.ws = ws;\n ws.connect();\n }\n\n private async tryIdentify(\n session: CreateVisitorSessionResponse,\n ): Promise<void> {\n const provider = this.config.visitorTokenProvider;\n if (!provider) return;\n\n let raw: string | null | undefined;\n const result = provider();\n if (typeof result === 'string') {\n raw = result;\n } else if (isObservable(result)) {\n raw = await firstValueFrom(result);\n } else {\n raw = await result;\n }\n if (!raw) return;\n\n // Caller's token is `userId:signature` (colon-delimited). Falling back\n // to plain user_id when no colon is present is too easy to misuse —\n // require explicit signature to avoid identity forgery footguns.\n const sep = raw.indexOf(':');\n if (sep < 0) {\n this.fatalError.set(\n 'visitorTokenProvider returned a value without \"userId:signature\" — refusing to identify.',\n );\n return;\n }\n const userId = raw.slice(0, sep);\n const userSignature = raw.slice(sep + 1);\n await this.getClient().identifyVisitor({\n visitorToken: session.visitor_token,\n userId,\n userSignature,\n });\n }\n\n private getClient(): ChatClient {\n if (!this.client) {\n this.client = new ChatClient({ baseUrl: this.config.baseUrl });\n }\n return this.client;\n }\n\n private resolvedOrigin(): string {\n if (this.config.origin) return this.config.origin;\n if (this.isBrowser && typeof window !== 'undefined') {\n return window.location.origin;\n }\n // SSR-safe fallback; the relay validates origins server-side anyway.\n return '';\n }\n}\n","/**\n * Floating launcher — circular chat bubble with optional label.\n * Wraps the `BloonioChatService` so consumers just drop\n * `<bloonio-chat-launcher />` somewhere and the SDK handles state.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n ViewChild,\n} from '@angular/core';\n\nimport { injectChatStrings } from '../bloonio-chat.i18n';\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { injectBloonioChatConfig, readOption } from '../bloonio-chat.token';\n\n@Component({\n selector: 'bloonio-chat-launcher',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-root\" [attr.data-position]=\"position()\" [style]=\"themeStyle()\">\n <button\n #button\n type=\"button\"\n class=\"bch-launcher\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-expanded]=\"chat.open()\"\n (click)=\"onToggle()\"\n >\n @if (label()) {\n <span class=\"bch-launcher-label\">{{ label() }}</span>\n }\n <span class=\"bch-launcher-bubble\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\" />\n </svg>\n <!-- Hidden from assistive tech: the count is in the button's name. -->\n @if (chat.unreadCount(); as unread) {\n <span class=\"bch-launcher-badge\" aria-hidden=\"true\">{{ unread > 9 ? '9+' : unread }}</span>\n }\n </span>\n </button>\n </div>\n `,\n})\nexport class BloonioChatLauncherComponent {\n protected readonly chat = inject(BloonioChatService);\n protected readonly t = injectChatStrings();\n private readonly config = injectBloonioChatConfig();\n\n @ViewChild('button') private button?: ElementRef<HTMLButtonElement>;\n\n constructor() {\n let wasOpen = false;\n effect(() => {\n const open = this.chat.open();\n const button = this.button?.nativeElement;\n // Closing from inside the panel (Escape, its close button) hands the\n // keyboard focus back to the launcher instead of dropping it on <body>.\n if (wasOpen && !open && button?.ownerDocument.activeElement?.closest('.bch-panel')) {\n button.focus();\n }\n wasOpen = open;\n });\n }\n\n protected readonly position = computed(\n () => this.chat.bootstrap()?.widget_position ?? 'right',\n );\n\n /** Tint the bubble with the tenant's brand colour (falls back to the\n * stylesheet default until bootstrap config loads). */\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n protected readonly label = computed(() => {\n const own = readOption(this.config.launcherLabel);\n if (own !== undefined) return own || null;\n const b = this.chat.bootstrap();\n return b?.button_label_enabled ? b.button_label_text : null;\n });\n\n protected readonly ariaLabel = computed(() => {\n if (this.chat.open()) return this.t().closeChat;\n const unread = this.chat.unreadCount();\n return unread > 0 ? this.t().unread(unread) : this.t().openChat;\n });\n\n onToggle(): void {\n void this.chat.toggle();\n }\n}\n","/**\n * Visitor input bar — multi-line textarea + send button. Enter sends,\n * Shift+Enter inserts a newline. Emits `send` with the trimmed content\n * once it's non-empty.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n signal,\n} from '@angular/core';\n\nimport { injectChatStrings } from '../bloonio-chat.i18n';\n\n@Component({\n selector: 'bloonio-chat-input-bar',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-input\">\n <textarea\n rows=\"1\"\n [placeholder]=\"t().placeholder\"\n [attr.aria-label]=\"t().placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKey($event)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"bch-input-send\"\n [attr.aria-label]=\"t().send\"\n [disabled]=\"!canSend()\"\n (click)=\"submit()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n </button>\n </div>\n `,\n})\nexport class InputBarComponent {\n @Input() disabled = false;\n @Output() send = new EventEmitter<string>();\n\n protected readonly t = injectChatStrings();\n protected readonly value = signal('');\n\n protected canSend(): boolean {\n return !this.disabled && this.value().trim().length > 0;\n }\n\n onInput(e: Event): void {\n this.value.set((e.target as HTMLTextAreaElement).value);\n }\n\n onKey(e: KeyboardEvent): void {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.submit();\n }\n }\n\n submit(): void {\n if (!this.canSend()) return;\n const trimmed = this.value().trim();\n this.send.emit(trimmed);\n this.value.set('');\n }\n}\n","/**\n * Renders the message log + typing indicator. Pure presentational —\n * receives messages + typing flag as inputs so it can also be used\n * outside the panel (custom layouts, debug screens).\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n Input,\n} from '@angular/core';\n\nimport { injectChatStrings } from '../bloonio-chat.i18n';\nimport type { ChatMessage } from '../bloonio-chat.service';\n\n@Component({\n selector: 'bloonio-chat-message-list',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // The bubbles are laid out by the parent's flex column (`.bch-messages`:\n // visitor right, answers left, notices centred). Without this the host\n // box sits in between and every bubble falls back to the left. A binding,\n // not a style attribute, so a strict CSP does not drop it.\n host: { '[style.display]': '\"contents\"' },\n template: `\n @for (m of messages; track m.id) {\n <!-- No whitespace around the text: bubbles are pre-wrap and would show it. -->\n <div\n class=\"bch-bubble\"\n [attr.data-from]=\"m.from\"\n [attr.data-pending]=\"m.pending ? 'true' : null\"\n >{{ m.content }}</div>\n }\n @if (botTyping) {\n <div class=\"bch-typing\" role=\"status\" [attr.aria-label]=\"t().typing\">\n <span></span><span></span><span></span>\n </div>\n }\n `,\n})\nexport class MessageListComponent {\n @Input({ required: true }) messages: ChatMessage[] = [];\n @Input() botTyping = false;\n\n protected readonly t = injectChatStrings();\n}\n","/**\n * `<bloonio-chat-quick-replies>` — tenant-configured opening prompt chips,\n * shown under the welcome message until the visitor sends their first\n * message. Tapping a chip sends its text as that first message.\n *\n * Driven by `BloonioChatService` (reads `bootstrap().quick_replies` +\n * `messages()`), so it self-hides once the conversation starts. Used inside\n * both the floating panel and the headless thread.\n */\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\n\nimport { injectChatStrings } from '../bloonio-chat.i18n';\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { injectBloonioChatConfig, readOption } from '../bloonio-chat.token';\n\n@Component({\n selector: 'bloonio-chat-quick-replies',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (replies().length) {\n <div class=\"bch-quick-replies\" role=\"group\" [attr.aria-label]=\"t().suggestedReplies\">\n @for (reply of replies(); track reply) {\n <button type=\"button\" class=\"bch-quick-reply\" (click)=\"pick(reply)\">\n {{ reply }}\n </button>\n }\n </div>\n }\n `,\n})\nexport class QuickRepliesComponent {\n protected readonly chat = inject(BloonioChatService);\n protected readonly t = injectChatStrings();\n private readonly config = injectBloonioChatConfig();\n\n /** Show the chips (the host's, else the tenant's) only until the\n * visitor's first message, and only once a tap can actually be sent. */\n protected readonly replies = computed<string[]>(() => {\n if (!this.chat.session() || this.chat.connecting()) return [];\n const replies =\n readOption(this.config.quickReplies) ??\n this.chat.bootstrap()?.quick_replies ??\n [];\n const visitorSpoke = this.chat.messages().some((m) => m.from === 'visitor');\n return visitorSpoke ? [] : replies;\n });\n\n protected pick(text: string): void {\n this.chat.sendMessage(text);\n }\n}\n","/**\n * Floating or docked chat panel. Renders the header, message list, input,\n * and footer. Sits inside `<bloonio-chat-launcher>` for the floating\n * variant, or stand-alone for `mode=\"docked\"`/`mode=\"inline\"`.\n */\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n inject,\n Injector,\n Input,\n signal,\n type AfterViewChecked,\n ElementRef,\n ViewChild,\n} from '@angular/core';\n\nimport { injectChatStrings } from '../bloonio-chat.i18n';\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { injectBloonioChatConfig, readOption } from '../bloonio-chat.token';\nimport { InputBarComponent } from './input-bar.component';\nimport { MessageListComponent } from './message-list.component';\nimport { QuickRepliesComponent } from './quick-replies.component';\n\nexport type ChatPanelMode = 'floating' | 'docked' | 'inline';\n\n@Component({\n selector: 'bloonio-chat-panel',\n standalone: true,\n imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div\n class=\"bch-root\"\n [class.bch-open]=\"visible()\"\n [attr.data-position]=\"position()\"\n [attr.data-mode]=\"mode\"\n >\n <div\n #panel\n class=\"bch-panel\"\n [style]=\"themeStyle()\"\n [attr.role]=\"mode === 'floating' ? 'dialog' : null\"\n [attr.aria-label]=\"mode === 'floating' ? t().chatWith(displayName()) : null\"\n [attr.tabindex]=\"mode === 'floating' ? -1 : null\"\n (keydown.escape)=\"mode === 'floating' && onClose()\"\n >\n <header class=\"bch-header\">\n <div class=\"bch-header-avatar\">\n @if (logoUrl()) {\n <img [src]=\"logoUrl()\" alt=\"\" />\n } @else {\n <span>{{ initials() }}</span>\n }\n </div>\n <div class=\"bch-header-title\">\n <div class=\"bch-header-name\">{{ t().chatWith(displayName()) }}</div>\n <div class=\"bch-header-status\">\n <span class=\"bch-header-status-dot\" [attr.data-online]=\"isOnlineStr()\"></span>\n {{ statusText() }}\n </div>\n </div>\n <button\n type=\"button\"\n class=\"bch-header-close\"\n [attr.aria-label]=\"t().close\"\n (click)=\"onClose()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"bch-messages\" role=\"log\" #messages>\n <bloonio-chat-message-list\n [messages]=\"chat.visibleMessages()\"\n [botTyping]=\"chat.botTyping()\"\n />\n </div>\n\n <bloonio-chat-quick-replies />\n\n <bloonio-chat-input-bar\n [disabled]=\"chat.connecting() || !chat.session()\"\n (send)=\"onSend($event)\"\n />\n\n <div class=\"bch-footer\">\n {{ t().poweredBy }}\n <a href=\"https://bloonio.com\" target=\"_blank\" rel=\"noopener noreferrer\">Bloonio</a>\n </div>\n </div>\n </div>\n `,\n})\nexport class BloonioChatPanelComponent implements AfterViewChecked {\n /** floating = paired with launcher (default). docked/inline = always-on\n * inside the page flow (use for SSR landing pages or sidebar layouts). */\n @Input() mode: ChatPanelMode = 'floating';\n\n protected readonly chat = inject(BloonioChatService);\n protected readonly t = injectChatStrings();\n private readonly config = injectBloonioChatConfig();\n private readonly injector = inject(Injector);\n\n @ViewChild('messages') private messagesRef?: ElementRef<HTMLDivElement>;\n @ViewChild('panel') private panelRef?: ElementRef<HTMLDivElement>;\n private lastScrollLen = signal(-1);\n\n constructor() {\n effect(() => {\n if (this.mode !== 'floating' || !this.chat.open()) return;\n // Opening moves focus into the dialog (so Escape and Tab work from\n // there). The panel is still `visibility: hidden` until this render\n // lands, hence after it.\n afterNextRender(() => this.panelRef?.nativeElement.focus(), {\n injector: this.injector,\n });\n });\n }\n\n protected readonly position = computed(\n () => this.chat.bootstrap()?.widget_position ?? 'right',\n );\n\n protected readonly visible = computed(() => {\n // Floating mode: only when service.open() is true.\n // Docked/inline: always rendered if bootstrap is loaded.\n if (this.mode === 'floating') return this.chat.open();\n return !!this.chat.bootstrap();\n });\n\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n protected readonly displayName = computed(\n () => this.chat.bootstrap()?.branding_display_name\n ?? this.chat.bootstrap()?.tenant_name\n ?? this.t().support,\n );\n\n protected readonly logoUrl = computed(\n () => this.chat.bootstrap()?.branding_logo_url ?? null,\n );\n\n protected readonly initials = computed(() =>\n this.displayName()\n .split(/\\s+/)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?',\n );\n\n protected readonly statusText = computed(() => {\n const b = this.chat.bootstrap();\n if (!b) return '';\n return b.is_online\n ? readOption(this.config.onlineStatusText) ?? b.online_status_text\n : readOption(this.config.offlineStatusText) ?? b.offline_status_text;\n });\n\n protected readonly isOnlineStr = computed(\n () => String(this.chat.bootstrap()?.is_online ?? false),\n );\n\n ngAfterViewChecked(): void {\n // Scroll-to-bottom whenever message count changes. We compare against a\n // stamped length so we don't churn when only typing/status updates fire.\n const el = this.messagesRef?.nativeElement;\n if (!el) return;\n const len = this.chat.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);\n if (len !== this.lastScrollLen()) {\n el.scrollTop = el.scrollHeight;\n this.lastScrollLen.set(len);\n }\n }\n\n onClose(): void {\n this.chat.closePanel();\n }\n\n onSend(content: string): void {\n this.chat.sendMessage(content);\n }\n}\n","/**\n * `<bloonio-chat-thread>` — headless variant of the panel. Renders just\n * the message log + input, no header, no floating chrome. Used when the\n * tenant wants to embed the chat *inside* their own layout (e.g. inside\n * a support modal, a sidebar, a help center page).\n *\n * Theming and behaviour are otherwise identical — the same\n * `BloonioChatService` drives state, so a thread and a panel side-by-side\n * stay in sync.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n AfterViewChecked,\n ElementRef,\n ViewChild,\n signal,\n} from '@angular/core';\n\nimport { BloonioChatService } from '../bloonio-chat.service';\nimport { InputBarComponent } from './input-bar.component';\nimport { MessageListComponent } from './message-list.component';\nimport { QuickRepliesComponent } from './quick-replies.component';\n\n@Component({\n selector: 'bloonio-chat-thread',\n standalone: true,\n imports: [InputBarComponent, MessageListComponent, QuickRepliesComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"bch-root\" data-mode=\"inline\" [style]=\"themeStyle()\">\n <div class=\"bch-panel\">\n <div class=\"bch-messages\" role=\"log\" #messages>\n <bloonio-chat-message-list\n [messages]=\"chat.visibleMessages()\"\n [botTyping]=\"chat.botTyping()\"\n />\n </div>\n <bloonio-chat-quick-replies />\n <bloonio-chat-input-bar\n [disabled]=\"chat.connecting() || !chat.session()\"\n (send)=\"chat.sendMessage($event)\"\n />\n </div>\n </div>\n `,\n})\nexport class BloonioChatThreadComponent implements AfterViewChecked {\n protected readonly chat = inject(BloonioChatService);\n\n @ViewChild('messages') private messagesRef?: ElementRef<HTMLDivElement>;\n private lastScrollLen = signal(-1);\n\n protected readonly themeStyle = computed(() => {\n const color = this.chat.bootstrap()?.branding_primary_color;\n return color ? `--bch-primary: ${color}` : '';\n });\n\n ngAfterViewChecked(): void {\n const el = this.messagesRef?.nativeElement;\n if (!el) return;\n const len = this.chat.visibleMessages().length + (this.chat.botTyping() ? 1 : 0);\n if (len !== this.lastScrollLen()) {\n el.scrollTop = el.scrollHeight;\n this.lastScrollLen.set(len);\n }\n }\n\n /** Convenience: thread is meant to be always-on, so it opens the\n * service-level session as soon as it's instantiated.\n * (Floating panel waits for the launcher click.) */\n constructor() {\n void this.chat.openPanel();\n }\n}\n","/**\n * @bloonio/chat-angular — Angular library for the bloonio chat PaaS.\n *\n * Public surface:\n * - {@link provideBloonioChat} — environment-provider factory for app.config.ts\n * - {@link BloonioChatService} — signals-based reactive state\n * - Three standalone components:\n * <bloonio-chat-launcher> floating bubble (collapsed)\n * <bloonio-chat-panel> expandable card (the full chat UX)\n * <bloonio-chat-thread> headless variant for inline embedding\n *\n * Styles ship separately at `@bloonio/chat-angular/styles.css` — import once\n * in your global stylesheet (see README).\n */\n\nexport { provideBloonioChat, BLOONIO_CHAT_CONFIG, injectBloonioChatConfig } from './lib/bloonio-chat.token';\nexport type {\n BloonioChatConfig,\n BloonioChatOption,\n VisitorTokenProvider,\n} from './lib/bloonio-chat.token';\nexport type { BloonioChatLocale } from './lib/bloonio-chat.i18n';\n\nexport { BloonioChatService } from './lib/bloonio-chat.service';\nexport type { ChatMessage } from './lib/bloonio-chat.service';\n\nexport { BloonioChatLauncherComponent } from './lib/components/launcher.component';\nexport {\n BloonioChatPanelComponent,\n type ChatPanelMode,\n} from './lib/components/panel.component';\nexport { BloonioChatThreadComponent } from './lib/components/thread.component';\nexport { MessageListComponent } from './lib/components/message-list.component';\nexport { InputBarComponent } from './lib/components/input-bar.component';\nexport { QuickRepliesComponent } from './lib/components/quick-replies.component';\n\n// Re-export the bits of @bloonio/chat-client-core a consumer is likely to\n// touch (types, error classes). Keeps `import {…} from '@bloonio/chat-angular'`\n// the one entry point for most cases.\nexport type {\n WidgetBootstrapConfig,\n WidgetPosition,\n WidgetVisibility,\n ServerMsgFrame,\n MessageSender,\n} from '@bloonio/chat-client-core';\nexport {\n BloonioChatError,\n BloonioChatNetworkError,\n BloonioChatServerError,\n BloonioChatAuthError,\n} from '@bloonio/chat-client-core';\n\nexport const VERSION = '0.2.0';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;AAoBG;AA8BH;AACM,SAAU,UAAU,CAAI,MAAwC,EAAA;AACpE,IAAA,OAAO,OAAO,MAAM,KAAK,UAAU,GAAI,MAAkB,EAAE,GAAG,MAAM;AACtE;MA8Ca,mBAAmB,GAAG,IAAI,cAAc,CACnD,qBAAqB;AAGvB;;;;;;AAMG;SACa,kBAAkB;AAChC;AAC4D;AAC5D,MAAqD,EAAA;AAErD,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA;AACE,YAAA,OAAO,EAAE,mBAAmB;AAC5B,YAAA,UAAU,EAAE,OAAO,OAAO,MAAM,KAAK,UAAU,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;AACrE,SAAA;AACF,KAAA,CAAC;AACJ;AAEA;AACgD;SAChC,uBAAuB,GAAA;AACrC,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,GAAG,EAAE;QACR,MAAM,IAAI,KAAK,CACb,oFAAoF;AACpF,YAAA,uEAAuE,CACxE;IACH;AACA,IAAA,OAAO,GAAG;AACZ;;ACtIA;;;;AAIG;AAiCH;AACA,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,IAAY,EAAE,GAAW,KACnD,KAAK,IAAI,EAAE,GAAG,CAAA,EAAG,KAAK,CAAA,EAAG,IAAI,CAAA,CAAE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAA,EAAG,GAAG,EAAE;AAErE,MAAM,OAAO,GAAkD;AAC7D,IAAA,EAAE,EAAE;AACF,QAAA,WAAW,EAAE,uBAAuB;AACpC,QAAA,IAAI,EAAE,MAAM;AACZ,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,QAAQ,EAAE,WAAW;AACrB,QAAA,SAAS,EAAE,YAAY;QACvB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAA,WAAA,EAAc,CAAC,CAAA,QAAA,CAAU;AACxC,QAAA,MAAM,EAAE,qBAAqB;AAC7B,QAAA,gBAAgB,EAAE,mBAAmB;QACrC,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAA,UAAA,EAAa,IAAI,CAAA,CAAE;AACvC,QAAA,OAAO,EAAE,SAAS;AAClB,QAAA,SAAS,EAAE,YAAY;AACvB,QAAA,UAAU,EAAE,aAAa;AACzB,QAAA,WAAW,EAAE,wDAAwD;QACrE,KAAK,EAAE,CAAC,OAAO,KAAK,CAAA,OAAA,EAAU,OAAO,CAAA,CAAE;QACvC,WAAW,EAAE,CAAC,IAAI,KAAK,CAAA,EAAG,IAAI,CAAA,oBAAA,CAAsB;AACpD,QAAA,SAAS,EAAE,6BAA6B;AACxC,QAAA,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,KAC1C,CAAA,gBAAA,EAAmB,CAAC,aAAa,QAAQ,CAAA,EAAA,EAAK,QAAQ,CAAA,GAAA,CAAK;YAC3D,CAAA,uBAAA,EAA0B,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA,CAAA,CAAG;AACpD,KAAA;AACD,IAAA,EAAE,EAAE;AACF,QAAA,WAAW,EAAE,eAAe;AAC5B,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,KAAK,EAAE,QAAQ;AACf,QAAA,QAAQ,EAAE,wBAAwB;AAClC,QAAA,SAAS,EAAE,wBAAwB;QACnC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAA,wBAAA,EAA2B,CAAC,CAAA,OAAA,EAAU,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE,CAAA,CAAA,CAAG;AACxE,QAAA,MAAM,EAAE,+BAA+B;AACvC,QAAA,gBAAgB,EAAE,oBAAoB;QACtC,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAA,cAAA,EAAiB,IAAI,CAAA,CAAE;AAC3C,QAAA,OAAO,EAAE,YAAY;AACrB,QAAA,SAAS,EAAE,cAAc;AACzB,QAAA,UAAU,EAAE,YAAY;AACxB,QAAA,WAAW,EAAE,uEAAuE;QACpF,KAAK,EAAE,CAAC,OAAO,KAAK,CAAA,SAAA,EAAY,OAAO,CAAA,CAAE;QACzC,WAAW,EAAE,CAAC,IAAI,KAAK,CAAA,EAAG,IAAI,CAAA,0BAAA,CAA4B;AAC1D,QAAA,SAAS,EAAE,wCAAwC;AACnD,QAAA,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,KAC1C,CAAA,wBAAA,EAA2B,CAAC,WAAW,QAAQ,CAAA,EAAA,EAAK,QAAQ,CAAA,GAAA,CAAK;YACjE,CAAA,6BAAA,EAAgC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA,CAAA,CAAG;AAC5D,KAAA;AACD,IAAA,EAAE,EAAE;AACF,QAAA,WAAW,EAAE,MAAM;AACnB,QAAA,IAAI,EAAE,IAAI;AACV,QAAA,KAAK,EAAE,IAAI;AACX,QAAA,QAAQ,EAAE,MAAM;AAChB,QAAA,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAA,KAAA,EAAQ,CAAC,CAAA,KAAA,CAAO;AAC/B,QAAA,MAAM,EAAE,QAAQ;AAChB,QAAA,gBAAgB,EAAE,MAAM;QACxB,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAA,EAAA,EAAK,IAAI,CAAA,GAAA,CAAK;AAClC,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,SAAS,EAAE,MAAM;AACjB,QAAA,UAAU,EAAE,OAAO;AACnB,QAAA,WAAW,EAAE,iBAAiB;QAC9B,KAAK,EAAE,CAAC,OAAO,KAAK,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE;QACnC,WAAW,EAAE,CAAC,IAAI,KAAK,CAAA,EAAG,IAAI,CAAA,MAAA,CAAQ;AACtC,QAAA,SAAS,EAAE,SAAS;AACpB,QAAA,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,KAC1C,CAAA,IAAA,EAAO,CAAC,QAAQ,QAAQ,CAAA,CAAA,EAAI,QAAQ,CAAA,EAAA,CAAI;YACxC,CAAA,KAAA,EAAQ,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA,KAAA,CAAO;AACzC,KAAA;CACF;AAED;;AAEwB;SACR,iBAAiB,GAAA;AAC/B,IAAA,MAAM,EAAE,MAAM,EAAE,GAAG,uBAAuB,EAAE;IAC5C,OAAO,QAAQ,CAAC,MAAK;QACnB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE;AACvE,QAAA,OAAO,OAAO,CAAC,IAAI,IAAI,OAAO,GAAI,IAA0B,GAAG,IAAI,CAAC;AACtE,IAAA,CAAC,CAAC;AACJ;;ACpHA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AA0CH;AACA,MAAM,YAAY,GAAwC;AACxD,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,QAAQ,EAAE,OAAO;AACjB,IAAA,GAAG,EAAE,KAAK;CACX;AAED;AACA,MAAM,YAAY,GAAG,IAAI;AACzB,MAAM,qBAAqB,GAAG,IAAI;AAClC,MAAM,kBAAkB,GAAG,IAAI;AAE/B;;AAE+D;AAC/D,MAAM,iBAAiB,GAAG,MAAM;AAEhC,MAAM,QAAQ,GAAG,CAAC,CAAc,KAAc,CAAC,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;MAGvE,kBAAkB,CAAA;IACZ,MAAM,GAAG,uBAAuB,EAAE;AAClC,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;AAChC,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAC/B,CAAC,GAAG,iBAAiB,EAAE;;AAGvB,IAAA,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;AAC/D;;AAEqE;IACpD,UAAU,GACzB,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB;;IAG5C,SAAS,GAAG,MAAM,CAA+B,IAAI;kFAAC;AAC/D;AACgD;IACvC,OAAO,GAAG,MAAM,CAAsC,IAAI;gFAAC;;IAE3D,QAAQ,GAAG,MAAM,CAAgB,EAAE;iFAAC;;IAEpC,SAAS,GAAG,MAAM,CAAC,KAAK;kFAAC;;IAEzB,IAAI,GAAG,MAAM,CAAC,KAAK;6EAAC;AAC7B;AACuD;IAC9C,UAAU,GAAG,MAAM,CAAC,KAAK;mFAAC;AACnC;AAC6E;IACpE,UAAU,GAAG,MAAM,CAAgB,IAAI;mFAAC;AAEjD;AACoC;IACnB,QAAQ,GAAG,MAAM,CAAC,KAAK;iFAAC;AAEzC;;;;AAIwB;AACf,IAAA,eAAe,GAA0B,QAAQ,CAAC,MAAK;AAC9D,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,IAAyB,EAAE,OAAe,MAAmB;YACrF,EAAE;YACF,IAAI;YACJ,OAAO;AACR,SAAA,CAAC;QACF,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;AACxC,YAAA,OAAO,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;QAC/E;QACA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxE,MAAM,GAAG,GAAG,QAAQ,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,QAAQ;QACtF,IAAI,QAAQ,CAAC,MAAM;AAAE,YAAA,OAAO,GAAG;AAC/B,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE;AACrB,YAAA,OAAO,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;QACxE;;AAEA,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC;AAC3B,cAAE,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC,eAAe,CAAC;cACjD,GAAG;IACT,CAAC;wFAAC;AAEF;;AAEyB;IACR,UAAU,GAAG,MAAM,CAAC,CAAC;mFAAC;AAC9B,IAAA,WAAW,GAAmB,QAAQ,CAAC,MAAK;QACnD,IAAI,IAAI,CAAC,IAAI,EAAE;AAAE,YAAA,OAAO,CAAC;AACzB,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM;AACrD,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;IAC/C,CAAC;oFAAC;IAEM,MAAM,GAAsB,IAAI;IAChC,EAAE,GAAoB,IAAI;IAC1B,gBAAgB,GAAiD,IAAI;IACrE,cAAc,GAAwD,IAAI;;IAE1E,YAAY,GAAG,CAAC;AAChB,IAAA,WAAW;AAEnB,IAAA,WAAA,GAAA;;QAEE,eAAe,CAAC,MAAK;YACnB,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,KAAI;;;AAG3C,gBAAA,IAAI,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE;AAAE,oBAAA,KAAK,IAAI,CAAC,aAAa,EAAE;AAC/D,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;AAC7B,YAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;AACrB,YAAA,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE;AAChB,YAAA,IAAI,CAAC,EAAE,GAAG,IAAI;AAChB,QAAA,CAAC,CAAC;IACJ;;;AAKA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;QACzB,IAAI,CAAC,QAAQ,EAAE;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AACnB,QAAA,IAAI,IAAI;AAAE,YAAA,MAAM,IAAI,CAAC,aAAa,EAAE;IACtC;;AAGA,IAAA,MAAM,SAAS,GAAA;QACb,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE;AACjB,QAAA,MAAM,IAAI,CAAC,MAAM,EAAE;IACrB;;IAGA,UAAU,GAAA;QACR,IAAI,CAAC,QAAQ,EAAE;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;IACtB;;AAGA,IAAA,WAAW,CAAC,OAAe,EAAA;AACzB,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;AAC9B,QAAA,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE;QAC1B,MAAM,WAAW,GAAG,CAAA,EAAA,EAAK,IAAI,CAAC,GAAG,EAAE,CAAA,CAAA,EAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA,CAAE;QAC/E,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,YAAA,GAAG,IAAI;AACP,YAAA;AACE,gBAAA,EAAE,EAAE,WAAW;AACf,gBAAA,aAAa,EAAE,WAAW;AAC1B,gBAAA,IAAI,EAAE,SAAS;AACf,gBAAA,OAAO,EAAE,OAAO;AAChB,gBAAA,OAAO,EAAE,IAAI;AACd,aAAA;AACF,SAAA,CAAC;AACF,QAAA,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC7E;;;IAKQ,QAAQ,GAAA;AACd,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAC9D;AAEQ,IAAA,SAAS,CAAC,MAAe,EAAA;AAC/B,QAAA,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC;AAC9B,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,MAAM,EAAE;AACV,YAAA,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;QACnF;IACF;AAEQ,IAAA,MAAM,aAAa,GAAA;QACzB,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,IAAI;QAChC,IAAI,IAAI,CAAC,SAAS,EAAE;AAAE,YAAA,OAAO,IAAI,CAAC,SAAS,EAAE;QAC7C,IAAI,IAAI,CAAC,gBAAgB;YAAE,OAAO,IAAI,CAAC,gBAAgB;AAEvD,QAAA,IAAI,CAAC,gBAAgB,GAAG,CAAC,YAAW;AAClC,YAAA,IAAI;AACF,gBAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE;AAC/B,gBAAA,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC;AACzC,oBAAA,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;AAC9B,oBAAA,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;AACtC,oBAAA,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE;AAC9B,iBAAA,CAAC;AACF,gBAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;AACvB,gBAAA,OAAO,GAAG;YACZ;YAAE,OAAO,GAAG,EAAE;AACZ,gBAAA,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,GAAG,GAAG,CAAC,OAAO,GAAG,eAAe;gBAChE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA,kBAAA,EAAqB,GAAG,CAAA,CAAE,CAAC;AAC/C,gBAAA,OAAO,IAAI;YACb;QACF,CAAC,GAAG;;QAEJ,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,GAAG,KAAI;AACtC,YAAA,IAAI,CAAC,GAAG;AAAE,gBAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI;AACxC,QAAA,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,gBAAgB;IAC9B;AAEA;AAC6D;AACrD,IAAA,MAAM,aAAa,GAAA;QACzB,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,IAAI;QAChC,IAAI,IAAI,CAAC,OAAO,EAAE;AAAE,YAAA,OAAO,IAAI,CAAC,OAAO,EAAE;QACzC,IAAI,IAAI,CAAC,cAAc;YAAE,OAAO,IAAI,CAAC,cAAc;AAEnD,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AACzB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AACzB,QAAA,IAAI,CAAC,cAAc,GAAG,CAAC,YAAW;AAChC,YAAA,IAAI;;;AAGF,gBAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE;AAC5C,gBAAA,IAAI,CAAC,SAAS;AAAE,oBAAA,OAAO,IAAI;gBAE3B,MAAM,OAAO,GACX,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC;AACpC,qBAAC,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC;AACnD,gBAAA,IAAI,CAAC,OAAO;AAAE,oBAAA,OAAO,IAAI;AACzB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AACzB,gBAAA,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;;;AAG3B,gBAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC;AAChD,gBAAA,OAAO,OAAO;YAChB;YAAE,OAAO,GAAG,EAAE;AACZ,gBAAA,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,GAAG,GAAG,CAAC,OAAO,GAAG,eAAe;gBAChE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA,gBAAA,EAAmB,GAAG,CAAA,CAAE,CAAC;AAC7C,gBAAA,OAAO,IAAI;YACb;oBAAU;;;AAGR,gBAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;AACnB,oBAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,oBAAA,IAAI,CAAC,cAAc,GAAG,IAAI;gBAC5B;YACF;QACF,CAAC,GAAG;QACJ,OAAO,IAAI,CAAC,cAAc;IAC5B;AAEQ,IAAA,MAAM,aAAa,GAAA;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,oBAAoB,CAAC;AAC1D,YAAA,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;AAC9B,YAAA,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;AACtC,YAAA,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE;;;YAG7B,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI;AAC9C,YAAA,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE;AACrC,SAAA,CAAC;AACF,QAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;AACzB,QAAA,OAAO,OAAO;IAChB;AAEA;;;AAGqE;IAC7D,MAAM,aAAa,CACzB,SAAgC,EAAA;AAEhC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE;AAChC,QAAA,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,IAAI;AACxB,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAChC,MAAM,EACN,wBAAwB,EACxB,MAAM,CAAC,aAAa,CACrB;AACD,YAAA,IAAI,OAAO,SAAS,EAAE,aAAa,KAAK,QAAQ,EAAE;gBAChD,MAAM,IAAI,sBAAsB,CAAC,4BAA4B,EAAE,GAAG,EAAE,SAAS,CAAC;YAChF;;;AAGA,YAAA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAE7B,KAAK,EAAE,yBAAyB,EAAE,SAAS,CAAC,aAAa,CAAC;AAC7D,YAAA,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,EAAE,GAAG,CAC3C,CAAC,CAAC,MAAmB;gBACnB,EAAE,EAAE,CAAC,CAAC,UAAU;gBAChB,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,QAAQ;gBACxC,OAAO,EAAE,CAAC,CAAC,OAAO;AACnB,aAAA,CAAC,CACH;AACD,YAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;;YAEtC,IAAI,CAAC,QAAQ,EAAE;AACf,YAAA,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;YAC3B,OAAO,EAAE,GAAG,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE;QACnD;QAAE,OAAO,GAAG,EAAE;;;YAGZ,IAAI,GAAG,YAAY,sBAAsB,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;AAC7D,gBAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACxB;AACA,YAAA,OAAO,IAAI;QACb;IACF;;AAGQ,IAAA,MAAM,KAAK,CACjB,MAAsB,EACtB,IAAY,EACZ,YAAoB,EAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA,OAAA,EAAU,IAAI,CAAA,CAAE,EAC1D;YACE,MAAM;AACN,YAAA,OAAO,EAAE,EAAE,aAAa,EAAE,CAAA,OAAA,EAAU,YAAY,EAAE,EAAE;AACpD,YAAA,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;AACpC,SAAA,CACF;AACD,QAAA,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;AACX,YAAA,MAAM,IAAI,sBAAsB,CAAC,CAAA,KAAA,EAAQ,GAAG,CAAC,MAAM,CAAA,CAAE,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC;QAC1E;AACA,QAAA,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE;AAC7B,QAAA,QAAQ,IAAI,EAAE,IAAI,IAAI,IAAI;IAC5B;IAEQ,UAAU,GAAA;QAChB,IAAI,CAAC,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO,IAAI;AACjC,QAAA,IAAI;AACF,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,MAAM,CAAC;AAC5E,YAAA,OAAO,OAAO,MAAM,EAAE,aAAa,KAAK,QAAQ,GAAG,MAAM,GAAG,IAAI;QAClE;AAAE,QAAA,MAAM;YACN,OAAO,IAAI,CAAC;QACd;IACF;AAEQ,IAAA,WAAW,CAAC,OAA6B,EAAA;QAC/C,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE;AACtB,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,OAAO;gBAAE,OAAO,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAC/D,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,OAAO;YACzD,YAAY,CAAC,OAAO,CAClB,IAAI,CAAC,UAAU,EAAE,EACjB,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAC1D;QACH;AAAE,QAAA,MAAM;;QAER;IACF;IAEQ,UAAU,GAAA;QAChB,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC;IACnD;AAEA;AACuD;IAC/C,gBAAgB,CAAC,EAAY,EAAE,IAAY,EAAA;QACjD,EAAE,CAAC,KAAK,EAAE;AACV,QAAA,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE;YAAE;AACpB,QAAA,IAAI,CAAC,EAAE,GAAG,IAAI;AACd,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;AACrB,QAAA,IAAI,CAAC,YAAY,IAAI,CAAC;QACtB,IAAI,IAAI,KAAK,qBAAqB,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE;AAC3D,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA,gBAAA,EAAmB,IAAI,CAAA,CAAA,CAAG,CAAC;YAC/C;QACF;;;AAGA,QAAA,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACjD,QAAA,KAAK,IAAI,CAAC,aAAa,EAAE;IAC3B;AAEQ,IAAA,aAAa,CAAC,OAAqC,EAAA;QACzD,IAAI,IAAI,CAAC,EAAE;YAAE;AACb,QAAA,MAAM,EAAE,GAAa,IAAI,QAAQ,CAC/B;AACE,YAAA,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,YAAY,EAAE,OAAO,CAAC,aAAa;AACnC,YAAA,mBAAmB,EAAE,MAAM;SAC5B,EACD;YACE,SAAS,EAAE,MAAK;AACd,gBAAA,IAAI,CAAC,YAAY,GAAG,CAAC;AACrB,gBAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;YAC5B,CAAC;AACD,YAAA,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAI;gBACpB,IACE,IAAI,KAAK,YAAY;AACrB,oBAAA,IAAI,KAAK,kBAAkB;oBAC3B,IAAI,KAAK,qBAAqB,EAC9B;AACA,oBAAA,OAAO;gBACT;;;AAGA,gBAAA,cAAc,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACvD,CAAC;AACD,YAAA,SAAS,EAAE,CAAC,KAAqB,KAAI;;;AAGnC,gBAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,oBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBAClD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,oBAAA,GAAG,IAAI;AACP,oBAAA,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;AAC3D,iBAAA,CAAC;YACJ,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,KAAK,KAAI;gBACf,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KACxB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KACT,CAAC,CAAC,aAAa,KAAK,KAAK,CAAC;AACxB,sBAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK;AACjD,sBAAE,CAAC,CACN,CACF;YACH,CAAC;AACD,YAAA,QAAQ,EAAE,CAAC,KAAK,KAAI;AAClB,gBAAA,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;oBAClD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC;gBACzC;YACF,CAAC;AACD,YAAA,OAAO,EAAE,CAAC,KAAK,KAAI;gBACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,oBAAA,GAAG,IAAI;AACP,oBAAA;AACE,wBAAA,EAAE,EAAE,CAAA,IAAA,EAAO,IAAI,CAAC,GAAG,EAAE,CAAA,CAAE;AACvB,wBAAA,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;AACvC,qBAAA;AACF,iBAAA,CAAC;YACJ,CAAC;;;;AAID,YAAA,aAAa,EAAE,CAAC,KAAK,KAAI;gBACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,oBAAA,GAAG,IAAI;AACP,oBAAA;AACE,wBAAA,EAAE,EAAE,CAAA,SAAA,EAAY,IAAI,CAAC,GAAG,EAAE,CAAA,CAAE;AAC5B,wBAAA,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC;AACxD,qBAAA;AACF,iBAAA,CAAC;AACF,gBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YACvB,CAAC;YACD,WAAW,EAAE,MAAK;gBAChB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,oBAAA,GAAG,IAAI;AACP,oBAAA;AACE,wBAAA,EAAE,EAAE,CAAA,SAAA,EAAY,IAAI,CAAC,GAAG,EAAE,CAAA,CAAE;AAC5B,wBAAA,IAAI,EAAE,QAAQ;AACd,wBAAA,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS;AAC5B,qBAAA;AACF,iBAAA,CAAC;AACF,gBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YACvB,CAAC;;AAED,YAAA,eAAe,EAAE,CAAC,KAAK,KAAI;gBACzB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK;AAC7B,oBAAA,GAAG,IAAI;AACP,oBAAA;AACE,wBAAA,EAAE,EAAE,CAAA,WAAA,EAAc,KAAK,CAAC,SAAS,CAAA,CAAE;AACnC,wBAAA,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAC7B,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,SAAS,CAChB;AACF,qBAAA;AACF,iBAAA,CAAC;YACJ,CAAC;AACF,SAAA,CACF;AACD,QAAA,IAAI,CAAC,EAAE,GAAG,EAAE;QACZ,EAAE,CAAC,OAAO,EAAE;IACd;IAEQ,MAAM,WAAW,CACvB,OAAqC,EAAA;AAErC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB;AACjD,QAAA,IAAI,CAAC,QAAQ;YAAE;AAEf,QAAA,IAAI,GAA8B;AAClC,QAAA,MAAM,MAAM,GAAG,QAAQ,EAAE;AACzB,QAAA,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YAC9B,GAAG,GAAG,MAAM;QACd;AAAO,aAAA,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE;AAC/B,YAAA,GAAG,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC;QACpC;aAAO;YACL,GAAG,GAAG,MAAM,MAAM;QACpB;AACA,QAAA,IAAI,CAAC,GAAG;YAAE;;;;QAKV,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;AAC5B,QAAA,IAAI,GAAG,GAAG,CAAC,EAAE;AACX,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CACjB,0FAA0F,CAC3F;YACD;QACF;QACA,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAChC,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;AACxC,QAAA,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,eAAe,CAAC;YACrC,YAAY,EAAE,OAAO,CAAC,aAAa;YACnC,MAAM;YACN,aAAa;AACd,SAAA,CAAC;IACJ;IAEQ,SAAS,GAAA;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAChB,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAChE;QACA,OAAO,IAAI,CAAC,MAAM;IACpB;IAEQ,cAAc,GAAA;AACpB,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;AAAE,YAAA,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM;QACjD,IAAI,IAAI,CAAC,SAAS,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACnD,YAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM;QAC/B;;AAEA,QAAA,OAAO,EAAE;IACX;uGA7fW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACvFlC;;;;AAIG;MA8CU,4BAA4B,CAAA;AACpB,IAAA,IAAI,GAAG,MAAM,CAAC,kBAAkB,CAAC;IACjC,CAAC,GAAG,iBAAiB,EAAE;IACzB,MAAM,GAAG,uBAAuB,EAAE;AAEtB,IAAA,MAAM;AAEnC,IAAA,WAAA,GAAA;QACE,IAAI,OAAO,GAAG,KAAK;QACnB,MAAM,CAAC,MAAK;YACV,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AAC7B,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa;;;AAGzC,YAAA,IAAI,OAAO,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,OAAO,CAAC,YAAY,CAAC,EAAE;gBAClF,MAAM,CAAC,KAAK,EAAE;YAChB;YACA,OAAO,GAAG,IAAI;AAChB,QAAA,CAAC,CAAC;IACJ;AAEmB,IAAA,QAAQ,GAAG,QAAQ,CACpC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,eAAe,IAAI,OAAO;iFACxD;AAED;AACwD;AACrC,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,sBAAsB;QAC3D,OAAO,KAAK,GAAG,CAAA,eAAA,EAAkB,KAAK,CAAA,CAAE,GAAG,EAAE;IAC/C,CAAC;mFAAC;AAEiB,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAK;QACvC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QACjD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,GAAG,IAAI,IAAI;QACzC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AAC/B,QAAA,OAAO,CAAC,EAAE,oBAAoB,GAAG,CAAC,CAAC,iBAAiB,GAAG,IAAI;IAC7D,CAAC;8EAAC;AAEiB,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AAC3C,QAAA,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AAAE,YAAA,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;QACtC,OAAO,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,QAAQ;IACjE,CAAC;kFAAC;IAEF,QAAQ,GAAA;AACN,QAAA,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;IACzB;uGA/CW,4BAA4B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA5B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,4BAA4B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,QAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA3B7B;;;;;;;;;;;;;;;;;;;;;;;;;AAyBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAEU,4BAA4B,EAAA,UAAA,EAAA,CAAA;kBA/BxC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,uBAAuB;AACjC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;AAyBT,EAAA,CAAA;AACF,iBAAA;;sBAME,SAAS;uBAAC,QAAQ;;;ACvDrB;;;;AAIG;MAyCU,iBAAiB,CAAA;IACnB,QAAQ,GAAG,KAAK;AACf,IAAA,IAAI,GAAG,IAAI,YAAY,EAAU;IAExB,CAAC,GAAG,iBAAiB,EAAE;IACvB,KAAK,GAAG,MAAM,CAAC,EAAE;8EAAC;IAE3B,OAAO,GAAA;AACf,QAAA,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;IACzD;AAEA,IAAA,OAAO,CAAC,CAAQ,EAAA;QACd,IAAI,CAAC,KAAK,CAAC,GAAG,CAAE,CAAC,CAAC,MAA8B,CAAC,KAAK,CAAC;IACzD;AAEA,IAAA,KAAK,CAAC,CAAgB,EAAA;QACpB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE;YACpC,CAAC,CAAC,cAAc,EAAE;YAClB,IAAI,CAAC,MAAM,EAAE;QACf;IACF;IAEA,MAAM,GAAA;AACJ,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;AACnC,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;AACvB,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACpB;uGA3BW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAzBlB;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAEU,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBA7B7B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,wBAAwB;AAClC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA;AACF,iBAAA;;sBAEE;;sBACA;;;AC/CH;;;;AAIG;MAmCU,oBAAoB,CAAA;IACJ,QAAQ,GAAkB,EAAE;IAC9C,SAAS,GAAG,KAAK;IAEP,CAAC,GAAG,iBAAiB,EAAE;uGAJ/B,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,UAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,cAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhBrB;;;;;;;;;;;;;;AAcT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAEU,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAzBhC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,2BAA2B;AACrC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;;;;;AAK/C,oBAAA,IAAI,EAAE,EAAE,iBAAiB,EAAE,YAAY,EAAE;AACzC,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;AAcT,EAAA,CAAA;AACF,iBAAA;;sBAEE,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;;sBACxB;;;ACzCH;;;;;;;;AAQG;MAuBU,qBAAqB,CAAA;AACb,IAAA,IAAI,GAAG,MAAM,CAAC,kBAAkB,CAAC;IACjC,CAAC,GAAG,iBAAiB,EAAE;IACzB,MAAM,GAAG,uBAAuB,EAAE;AAEnD;AACyE;AACtD,IAAA,OAAO,GAAG,QAAQ,CAAW,MAAK;AACnD,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;AAAE,YAAA,OAAO,EAAE;QAC7D,MAAM,OAAO,GACX,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;AACpC,YAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa;AACpC,YAAA,EAAE;QACJ,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;QAC3E,OAAO,YAAY,GAAG,EAAE,GAAG,OAAO;IACpC,CAAC;gFAAC;AAEQ,IAAA,IAAI,CAAC,IAAY,EAAA;AACzB,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC7B;uGAnBW,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,4BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAZtB;;;;;;;;;;AAUT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAEU,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAhBjC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,4BAA4B;AACtC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;AAUT,EAAA,CAAA;AACF,iBAAA;;;AC9BD;;;;AAIG;MAiGU,yBAAyB,CAAA;AACpC;AAC2E;IAClE,IAAI,GAAkB,UAAU;AAEtB,IAAA,IAAI,GAAG,MAAM,CAAC,kBAAkB,CAAC;IACjC,CAAC,GAAG,iBAAiB,EAAE;IACzB,MAAM,GAAG,uBAAuB,EAAE;AAClC,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAEb,IAAA,WAAW;AACd,IAAA,QAAQ;AAC5B,IAAA,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC;sFAAC;AAElC,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE;;;;AAInD,YAAA,eAAe,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,EAAE;gBAC1D,QAAQ,EAAE,IAAI,CAAC,QAAQ;AACxB,aAAA,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;AAEmB,IAAA,QAAQ,GAAG,QAAQ,CACpC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,eAAe,IAAI,OAAO;iFACxD;AAEkB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;;;AAGzC,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;AAAE,YAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QACrD,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;IAChC,CAAC;gFAAC;AAEiB,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,sBAAsB;QAC3D,OAAO,KAAK,GAAG,CAAA,eAAA,EAAkB,KAAK,CAAA,CAAE,GAAG,EAAE;IAC/C,CAAC;mFAAC;AAEiB,IAAA,WAAW,GAAG,QAAQ,CACvC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE;AACxB,WAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE;AACvB,WAAA,IAAI,CAAC,CAAC,EAAE,CAAC,OAAO;oFACtB;AAEkB,IAAA,OAAO,GAAG,QAAQ,CACnC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,iBAAiB,IAAI,IAAI;gFACvD;IAEkB,QAAQ,GAAG,QAAQ,CAAC,MACrC,IAAI,CAAC,WAAW;SACb,KAAK,CAAC,KAAK;AACX,SAAA,KAAK,CAAC,CAAC,EAAE,CAAC;AACV,SAAA,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;AACpC,SAAA,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG;iFACnB;AAEkB,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AAC/B,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;QACjB,OAAO,CAAC,CAAC;AACP,cAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAChD,cAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,mBAAmB;IACxE,CAAC;mFAAC;AAEiB,IAAA,WAAW,GAAG,QAAQ,CACvC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,IAAI,KAAK,CAAC;oFACxD;IAED,kBAAkB,GAAA;;;AAGhB,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa;AAC1C,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAChF,QAAA,IAAI,GAAG,KAAK,IAAI,CAAC,aAAa,EAAE,EAAE;AAChC,YAAA,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,YAAY;AAC9B,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAC7B;IACF;IAEA,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;IACxB;AAEA,IAAA,MAAM,CAAC,OAAe,EAAA;AACpB,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IAChC;uGA1FW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,UAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,UAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,OAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAnE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAnES,iBAAiB,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,oBAAoB,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,qBAAqB,EAAA,QAAA,EAAA,4BAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAqE7D,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAxErC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,oBAAoB;AAC9B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,qBAAqB,CAAC;oBACzE,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiET,EAAA,CAAA;AACF,iBAAA;;sBAIE;;sBAOA,SAAS;uBAAC,UAAU;;sBACpB,SAAS;uBAAC,OAAO;;;AChHpB;;;;;;;;;AASG;MAwCU,0BAA0B,CAAA;AAClB,IAAA,IAAI,GAAG,MAAM,CAAC,kBAAkB,CAAC;AAErB,IAAA,WAAW;AAClC,IAAA,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC;sFAAC;AAEf,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,sBAAsB;QAC3D,OAAO,KAAK,GAAG,CAAA,eAAA,EAAkB,KAAK,CAAA,CAAE,GAAG,EAAE;IAC/C,CAAC;mFAAC;IAEF,kBAAkB,GAAA;AAChB,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa;AAC1C,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAChF,QAAA,IAAI,GAAG,KAAK,IAAI,CAAC,aAAa,EAAE,EAAE;AAChC,YAAA,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,YAAY;AAC9B,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAC7B;IACF;AAEA;;AAEqD;AACrD,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;IAC5B;uGA1BW,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA1B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,UAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAlB3B;;;;;;;;;;;;;;;;AAgBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAlBS,iBAAiB,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,oBAAoB,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,qBAAqB,EAAA,QAAA,EAAA,4BAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAoB7D,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAvBtC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,qBAAqB,CAAC;oBACzE,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;;;AAgBT,EAAA,CAAA;AACF,iBAAA;;sBAIE,SAAS;uBAAC,UAAU;;;ACpDvB;;;;;;;;;;;;;AAaG;AAwCI,MAAM,OAAO,GAAG;;ACrDvB;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloonio/chat-angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Angular library for the bloonio chat PaaS — standalone components, signals-based service, SSR-safe. Built on @bloonio/chat-client-core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
"default": "./package.json"
|
|
10
10
|
},
|
|
11
11
|
".": {
|
|
12
|
-
"types": "./
|
|
13
|
-
"esm2022": "./esm2022/bloonio-chat-angular.mjs",
|
|
14
|
-
"esm": "./esm2022/bloonio-chat-angular.mjs",
|
|
12
|
+
"types": "./types/bloonio-chat-angular.d.ts",
|
|
15
13
|
"default": "./fesm2022/bloonio-chat-angular.mjs"
|
|
16
14
|
}
|
|
17
15
|
},
|
|
@@ -25,13 +23,13 @@
|
|
|
25
23
|
],
|
|
26
24
|
"license": "MIT",
|
|
27
25
|
"peerDependencies": {
|
|
28
|
-
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
29
|
-
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
26
|
+
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
27
|
+
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
30
28
|
"@bloonio/chat-client-core": "*",
|
|
31
29
|
"rxjs": "^7.0.0"
|
|
32
30
|
},
|
|
33
31
|
"module": "fesm2022/bloonio-chat-angular.mjs",
|
|
34
|
-
"typings": "
|
|
32
|
+
"typings": "types/bloonio-chat-angular.d.ts",
|
|
35
33
|
"dependencies": {
|
|
36
34
|
"tslib": "^2.3.0"
|
|
37
35
|
}
|
package/src/lib/styles/chat.css
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* Or via app.config.ts → styles array. All selectors are scoped under
|
|
9
9
|
* `.bch-*` so the rules don't collide with the host application.
|
|
10
10
|
*
|
|
11
|
-
* Theme tokens (--bch-primary etc.) are
|
|
11
|
+
* Theme tokens (--bch-primary etc.) are declared on `.bch-root` so a
|
|
12
12
|
* tenant's branding_primary_color applied at runtime flows to every
|
|
13
|
-
* descendant. To
|
|
14
|
-
*
|
|
13
|
+
* descendant. To theme the widget, redeclare them in a `.bch-root` rule
|
|
14
|
+
* placed after this import (see README, "Theming").
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
.bch-root {
|
|
@@ -67,6 +67,16 @@
|
|
|
67
67
|
}
|
|
68
68
|
.bch-launcher:hover .bch-launcher-bubble { transform: translateY(-2px); }
|
|
69
69
|
.bch-launcher-bubble svg { width: 26px; height: 26px; }
|
|
70
|
+
.bch-launcher-bubble { position: relative; }
|
|
71
|
+
/* Unread count, in the panel's own colours so it reads on any brand tint. */
|
|
72
|
+
.bch-launcher-badge {
|
|
73
|
+
position: absolute; top: -4px; right: -4px;
|
|
74
|
+
box-sizing: border-box; min-width: 20px; height: 20px; padding: 0 6px;
|
|
75
|
+
border-radius: 9999px; border: 1px solid var(--bch-border);
|
|
76
|
+
background: var(--bch-bg); color: var(--bch-text);
|
|
77
|
+
font-size: 12px; font-weight: 600; line-height: 18px; text-align: center;
|
|
78
|
+
font-variant-numeric: tabular-nums;
|
|
79
|
+
}
|
|
70
80
|
|
|
71
81
|
.bch-launcher-label {
|
|
72
82
|
background: var(--bch-bg); color: var(--bch-text);
|
|
@@ -149,6 +159,7 @@
|
|
|
149
159
|
overflow: hidden; flex-shrink: 0;
|
|
150
160
|
}
|
|
151
161
|
.bch-header-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
162
|
+
.bch-header-avatar span { font-weight: 600; font-size: 15px; }
|
|
152
163
|
.bch-header-title { flex: 1; min-width: 0; }
|
|
153
164
|
.bch-header-name {
|
|
154
165
|
font-weight: 600; font-size: 15px;
|
|
@@ -234,6 +245,8 @@
|
|
|
234
245
|
padding: 8px 0; min-height: 24px; max-height: 96px; line-height: 1.4;
|
|
235
246
|
}
|
|
236
247
|
.bch-input textarea::placeholder { color: var(--bch-text-muted); }
|
|
248
|
+
/* The textarea draws no outline of its own, so the bar shows the focus. */
|
|
249
|
+
.bch-input:focus-within { box-shadow: inset 0 2px 0 var(--bch-primary); }
|
|
237
250
|
.bch-input-send {
|
|
238
251
|
flex-shrink: 0; width: 36px; height: 36px;
|
|
239
252
|
border-radius: 9999px;
|