{"id":44724700,"url":"https://github.com/avs2001/ngx-support-chat","last_synced_at":"2026-03-06T00:19:02.083Z","repository":{"id":330856492,"uuid":"1124198900","full_name":"avs2001/ngx-support-chat","owner":"avs2001","description":"A **pure presentational Angular component** for customer support chat. ","archived":false,"fork":false,"pushed_at":"2025-12-29T17:02:48.000Z","size":535,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-31T03:53:02.224Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/avs2001.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-28T14:48:04.000Z","updated_at":"2025-12-29T17:02:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/avs2001/ngx-support-chat","commit_stats":null,"previous_names":["avs2001/ngx-support-chat"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/avs2001/ngx-support-chat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avs2001%2Fngx-support-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avs2001%2Fngx-support-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avs2001%2Fngx-support-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avs2001%2Fngx-support-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avs2001","download_url":"https://codeload.github.com/avs2001/ngx-support-chat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avs2001%2Fngx-support-chat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30156269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-15T17:00:26.115Z","updated_at":"2026-03-06T00:19:02.072Z","avatar_url":"https://github.com/avs2001.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["UI Libraries"],"readme":"# ngx-support-chat\n\nA pure presentational Angular component library for customer support chat interfaces. Built with Angular 21 signals, OnPush change detection, and comprehensive theming support.\n\n[![npm version](https://badge.fury.io/js/ngx-support-chat.svg)](https://www.npmjs.com/package/ngx-support-chat)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Features\n\n- **Pure Presentational Components** - All business logic delegated to parent; components handle only UI\n- **Signal-Based APIs** - Modern Angular 21 signals for inputs, outputs, and state\n- **OnPush Change Detection** - Optimized performance across all components\n- **Virtual Scrolling** - Efficient rendering for large message lists via CDK\n- **CSS Custom Properties** - 70+ design tokens for complete theming control\n- **Markdown Support** - Optional ngx-markdown integration for rich text\n- **Accessibility** - WCAG compliant with screen reader support, keyboard navigation\n- **Message Grouping** - Automatic grouping by date and sender\n- **Quick Replies** - Interactive buttons for guided conversations\n- **File Attachments** - Image previews and file upload support\n- **Typing Indicators** - Real-time typing status display\n\n## Live Demo\n\n[View the Demo](https://avs2001.github.io/ngx-support-chat/)\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Components](#components)\n- [Configuration](#configuration)\n- [Models \u0026 Interfaces](#models--interfaces)\n- [Theming](#theming)\n- [Pipes](#pipes)\n- [Directives](#directives)\n- [Utilities](#utilities)\n- [Accessibility](#accessibility)\n- [Advanced Usage](#advanced-usage)\n\n---\n\n## Installation\n\n### Using ng add (Recommended)\n\n```bash\nng add ngx-support-chat\n```\n\nThe schematic will:\n- Install required peer dependencies (`@angular/cdk`)\n- Optionally add `ngx-markdown` for markdown support\n- Add CSS tokens import to your global styles\n\n### Manual Installation\n\n```bash\nnpm install ngx-support-chat @angular/cdk\n```\n\nAdd CSS tokens to your global styles:\n\n```scss\n// styles.scss\n@import 'ngx-support-chat/styles/tokens.css';\n```\n\n---\n\n## Quick Start\n\n### 1. Configure Providers\n\n```typescript\n// app.config.ts\nimport { ApplicationConfig } from '@angular/core';\nimport { provideChatConfig } from 'ngx-support-chat';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideChatConfig({\n      dateFormat: 'MMM d, yyyy',\n      timeFormat: 'HH:mm',\n      dateSeparatorLabels: {\n        today: 'Today',\n        yesterday: 'Yesterday'\n      }\n    })\n  ]\n};\n```\n\n### 2. Import and Use the Component\n\n```typescript\n// app.component.ts\nimport { Component, signal } from '@angular/core';\nimport {\n  ChatContainerComponent,\n  ChatMessage,\n  MessageSendEvent\n} from 'ngx-support-chat';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [ChatContainerComponent],\n  template: `\n    \u003cngx-chat-container\n      [messages]=\"messages()\"\n      [currentUserId]=\"currentUserId\"\n      [typingIndicator]=\"typingIndicator()\"\n      [quickReplies]=\"quickReplies()\"\n      [pendingAttachments]=\"attachments()\"\n      [(inputValue)]=\"inputValue\"\n      (messageSend)=\"onMessageSend($event)\"\n      (messageRetry)=\"onRetry($event)\"\n      (attachmentSelect)=\"onFilesSelected($event)\"\n      (attachmentRemove)=\"onAttachmentRemove($event)\"\n      (quickReplySubmit)=\"onQuickReply($event)\"\n      (imagePreview)=\"onImageClick($event)\"\n      (fileDownload)=\"onFileDownload($event)\"\n    \u003e\n      \u003cdiv chatHeader\u003eSupport Chat\u003c/div\u003e\n      \u003cdiv chatEmptyState\u003eStart a conversation\u003c/div\u003e\n    \u003c/ngx-chat-container\u003e\n  `\n})\nexport class AppComponent {\n  messages = signal\u003cChatMessage[]\u003e([]);\n  typingIndicator = signal(null);\n  quickReplies = signal(null);\n  attachments = signal([]);\n  inputValue = signal('');\n  currentUserId = 'user-1';\n\n  onMessageSend(event: MessageSendEvent) {\n    // Handle message send\n  }\n}\n```\n\n---\n\n## Components\n\n### ChatContainerComponent\n\nThe main container that orchestrates the complete chat interface.\n\n**Selector:** `ngx-chat-container`\n\n#### Inputs\n\n| Input | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `messages` | `ChatMessage[]` | **Yes** | - | Array of chat messages to display |\n| `currentUserId` | `string` | **Yes** | - | Current user's ID (determines message alignment) |\n| `typingIndicator` | `TypingIndicator \\| null` | No | `null` | Shows who is currently typing |\n| `quickReplies` | `QuickReplySet \\| null` | No | `null` | Interactive quick reply options |\n| `pendingAttachments` | `Attachment[]` | No | `[]` | Files pending upload |\n| `inputValue` | `string` | No | `''` | Two-way bound input value |\n| `disabled` | `boolean` | No | `false` | Disables the input |\n\n#### Outputs\n\n| Output | Type | Description |\n|--------|------|-------------|\n| `messageSend` | `MessageSendEvent` | User sends a message |\n| `messageRetry` | `ChatMessage` | User retries a failed message |\n| `attachmentSelect` | `File[]` | User selects files |\n| `attachmentRemove` | `Attachment` | User removes a pending attachment |\n| `quickReplySubmit` | `QuickReplySubmitEvent` | User submits a quick reply |\n| `imagePreview` | `ImageContent` | User clicks an image |\n| `fileDownload` | `FileContent` | User requests file download |\n| `scrollTop` | `void` | User scrolls to top (for pagination) |\n\n#### Content Projection\n\n```html\n\u003cngx-chat-container\u003e\n  \u003cdiv chatHeader\u003eCustom Header Content\u003c/div\u003e\n  \u003cdiv chatEmptyState\u003eNo messages yet\u003c/div\u003e\n\u003c/ngx-chat-container\u003e\n```\n\n---\n\n### ChatHeaderComponent\n\nHeader container with content projection.\n\n**Selector:** `ngx-chat-header`\n\n```html\n\u003cngx-chat-header\u003e\n  \u003cimg [src]=\"agentAvatar\" alt=\"Agent\" /\u003e\n  \u003cspan\u003eSupport Chat\u003c/span\u003e\n\u003c/ngx-chat-header\u003e\n```\n\n---\n\n### ChatMessageAreaComponent\n\nScrollable message list with virtual scrolling support.\n\n**Selector:** `ngx-chat-message-area`\n\n#### Inputs\n\n| Input | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `messages` | `ChatMessage[]` | **Yes** | - | Messages to display |\n| `currentUserId` | `string` | **Yes** | - | Current user's ID |\n| `showAvatars` | `boolean` | No | `true` | Show sender avatars |\n| `showSenderNames` | `boolean` | No | `true` | Show sender names |\n| `itemSize` | `number` | No | `80` | Virtual scroll item size (px) |\n| `autoScrollToBottom` | `boolean` | No | `true` | Auto-scroll on new messages |\n\n---\n\n### ChatMessageComponent\n\nIndividual message bubble with support for text, image, file, and system messages.\n\n**Selector:** `ngx-chat-message`\n\n#### Inputs\n\n| Input | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `message` | `ChatMessage` | **Yes** | - | The message to display |\n| `isCurrentUser` | `boolean` | No | `false` | Aligns message to right |\n| `showAvatar` | `boolean` | No | `true` | Show avatar |\n| `showSenderName` | `boolean` | No | `true` | Show sender name |\n| `isFirstInGroup` | `boolean` | No | `true` | Full bubble radius |\n| `isLastInGroup` | `boolean` | No | `true` | Shows timestamp/status |\n\n---\n\n### ChatInputComponent\n\nAuto-resizing textarea for message composition.\n\n**Selector:** `ngx-chat-input`\n\n#### Inputs\n\n| Input | Type | Default | Description |\n|-------|------|---------|-------------|\n| `value` | `string` | `''` | Two-way bound value |\n| `placeholder` | `string` | `'Type a message...'` | Placeholder text |\n| `disabled` | `boolean` | `false` | Disabled state |\n| `maxHeight` | `number` | `120` | Max height before scrolling |\n\n#### Outputs\n\n| Output | Description |\n|--------|-------------|\n| `send` | Enter pressed (without Shift) |\n\n---\n\n### ChatQuickRepliesComponent\n\nInteractive buttons for guided responses.\n\n**Selector:** `ngx-chat-quick-replies`\n\nSupports three types:\n- **confirmation** - Single button, immediate submit\n- **single-choice** - Radio-style, selection submits\n- **multiple-choice** - Checkboxes with Submit button\n\n```html\n\u003cngx-chat-quick-replies\n  [quickReplies]=\"quickReplySet\"\n  (quickReplySubmit)=\"onSubmit($event)\"\n/\u003e\n```\n\n---\n\n### ChatTypingIndicatorComponent\n\nAnimated typing indicator bubble.\n\n**Selector:** `ngx-chat-typing-indicator`\n\n```html\n\u003cngx-chat-typing-indicator\n  [typingIndicator]=\"{ userId: 'agent-1', userName: 'Support Agent' }\"\n  [showText]=\"true\"\n/\u003e\n```\n\n---\n\n### ChatFooterComponent\n\nContainer for input, attachments, and action buttons.\n\n**Selector:** `ngx-chat-footer`\n\n```html\n\u003cngx-chat-footer\n  [pendingAttachments]=\"attachments\"\n  [(inputValue)]=\"messageText\"\n  [hasContent]=\"hasContent()\"\n  (messageSend)=\"onSend()\"\n  (attachmentSelect)=\"onFiles($event)\"\n\u003e\n  \u003cbutton chatFooterPrefix\u003eEmoji\u003c/button\u003e\n  \u003cbutton chatFooterActions\u003eVoice\u003c/button\u003e\n\u003c/ngx-chat-footer\u003e\n```\n\n---\n\n### ChatAttachmentPreviewComponent\n\nDisplays pending file attachments as chips.\n\n**Selector:** `ngx-chat-attachment-preview`\n\n```html\n\u003cngx-chat-attachment-preview\n  [attachments]=\"pendingAttachments\"\n  (attachmentRemove)=\"onRemove($event)\"\n/\u003e\n```\n\n---\n\n### ChatDateSeparatorComponent\n\nDate divider showing \"Today\", \"Yesterday\", or formatted date.\n\n**Selector:** `ngx-chat-date-separator`\n\n```html\n\u003cngx-chat-date-separator [date]=\"messageDate\" /\u003e\n```\n\n---\n\n## Configuration\n\n### ChatConfig Interface\n\n```typescript\ninterface ChatConfig {\n  markdown: {\n    enabled: boolean;      // Enable markdown support\n    displayMode: boolean;  // Render markdown in messages\n    inputMode: boolean;    // Allow markdown in input\n  };\n  dateFormat: string;      // e.g., 'MMMM d, yyyy'\n  timeFormat: string;      // e.g., 'HH:mm'\n  dateSeparatorLabels: {\n    today: string;         // Label for today\n    yesterday: string;     // Label for yesterday\n  };\n}\n```\n\n### Provider Configuration\n\n```typescript\nimport { provideChatConfig } from 'ngx-support-chat';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideChatConfig({\n      markdown: {\n        enabled: true,\n        displayMode: true,\n        inputMode: false\n      },\n      dateFormat: 'dd/MM/yyyy',\n      timeFormat: 'h:mm a',\n      dateSeparatorLabels: {\n        today: 'Aujourd\\'hui',\n        yesterday: 'Hier'\n      }\n    })\n  ]\n};\n```\n\n### Markdown Support\n\nTo enable markdown rendering, install and configure ngx-markdown:\n\n```bash\nnpm install ngx-markdown marked\n```\n\n```typescript\n// app.config.ts\nimport { provideMarkdown, MarkdownService } from 'ngx-markdown';\nimport { provideChatConfig, MARKDOWN_SERVICE } from 'ngx-support-chat';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideMarkdown(),\n    { provide: MARKDOWN_SERVICE, useExisting: MarkdownService },\n    provideChatConfig({\n      markdown: { enabled: true, displayMode: true }\n    })\n  ]\n};\n```\n\n---\n\n## Models \u0026 Interfaces\n\n### ChatMessage\n\n```typescript\ninterface ChatMessage {\n  id: string;                    // Unique identifier\n  type: MessageType;             // 'text' | 'image' | 'file' | 'system'\n  senderId: string;              // Sender's ID\n  senderName: string;            // Display name\n  senderAvatar?: string;         // Avatar URL\n  timestamp: Date;               // Message time\n  status: MessageStatus;         // 'sending' | 'sent' | 'delivered' | 'read' | 'failed'\n  content: MessageContent;       // Type-specific content\n}\n```\n\n### Message Content Types\n\n```typescript\n// Text message\ninterface TextContent {\n  text: string;\n}\n\n// Image message\ninterface ImageContent {\n  thumbnailUrl: string;\n  fullUrl: string;\n  altText?: string;\n  width?: number;\n  height?: number;\n}\n\n// File message\ninterface FileContent {\n  fileName: string;\n  fileSize?: number;\n  fileType: string;\n  downloadUrl: string;\n  icon?: string;\n}\n\n// System message\ninterface SystemContent {\n  text: string;\n}\n```\n\n### Type Guards\n\n```typescript\nimport {\n  isTextMessage,\n  isImageMessage,\n  isFileMessage,\n  isSystemMessage\n} from 'ngx-support-chat';\n\nif (isTextMessage(message)) {\n  console.log(message.content.text);\n}\n```\n\n### QuickReplySet\n\n```typescript\ninterface QuickReplySet {\n  id: string;\n  type: QuickReplyType;           // 'confirmation' | 'single-choice' | 'multiple-choice'\n  prompt?: string;                // Optional prompt text\n  options: QuickReplyOption[];\n  submitted: boolean;\n  selectedValues?: unknown[];\n}\n\ninterface QuickReplyOption {\n  value: unknown;\n  label: string;\n  disabled?: boolean;\n}\n```\n\n### TypingIndicator\n\n```typescript\ninterface TypingIndicator {\n  userId: string;\n  userName: string;\n  avatar?: string;\n}\n```\n\n### Attachment\n\n```typescript\ninterface Attachment {\n  id: string;\n  file: File;\n  previewUrl?: string;      // For image previews\n  uploadProgress?: number;  // 0-100\n}\n```\n\n### Event Types\n\n```typescript\ninterface MessageSendEvent {\n  text: string;\n  attachments: Attachment[];\n}\n\ninterface QuickReplySubmitEvent {\n  type: QuickReplyType;\n  value: unknown;\n}\n```\n\n---\n\n## Theming\n\nThe library uses CSS custom properties (design tokens) for complete theming control. All tokens use the `--ngx-` prefix.\n\n### Quick Theme Customization\n\n```css\n:root {\n  /* Primary brand color */\n  --ngx-bubble-user-bg: #7c3aed;\n  --ngx-button-primary-bg: #7c3aed;\n  --ngx-quick-reply-border: #7c3aed;\n\n  /* Dark mode */\n  --ngx-chat-bg: #1a1a1a;\n  --ngx-chat-message-area-bg: #121212;\n}\n```\n\n### Available Tokens\n\n#### Color Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-chat-bg` | `#ffffff` | Main container background |\n| `--ngx-chat-header-bg` | `#ffffff` | Header background |\n| `--ngx-chat-footer-bg` | `#ffffff` | Footer background |\n| `--ngx-chat-message-area-bg` | `#f5f5f5` | Message area background |\n| `--ngx-bubble-user-bg` | `#0066cc` | User message bubble |\n| `--ngx-bubble-user-text` | `#ffffff` | User message text |\n| `--ngx-bubble-agent-bg` | `#e8e8e8` | Agent message bubble |\n| `--ngx-bubble-agent-text` | `#1a1a1a` | Agent message text |\n| `--ngx-bubble-system-bg` | `transparent` | System message background |\n| `--ngx-bubble-system-text` | `#666666` | System message text |\n| `--ngx-input-bg` | `#ffffff` | Input background |\n| `--ngx-input-border` | `#dddddd` | Input border |\n| `--ngx-input-focus-border` | `#0066cc` | Input focus border |\n| `--ngx-button-primary-bg` | `#0066cc` | Primary button background |\n| `--ngx-button-primary-text` | `#ffffff` | Primary button text |\n| `--ngx-status-sending` | `#999999` | Sending status color |\n| `--ngx-status-sent` | `#666666` | Sent status color |\n| `--ngx-status-delivered` | `#0066cc` | Delivered status color |\n| `--ngx-status-read` | `#00cc66` | Read status color |\n| `--ngx-status-failed` | `#cc0000` | Failed status color |\n| `--ngx-quick-reply-bg` | `#ffffff` | Quick reply background |\n| `--ngx-quick-reply-border` | `#0066cc` | Quick reply border |\n| `--ngx-quick-reply-selected-bg` | `#0066cc` | Selected quick reply |\n| `--ngx-typing-indicator-dot` | `#666666` | Typing dots color |\n| `--ngx-link-color` | `#0066cc` | Link color |\n| `--ngx-timestamp-text` | `#999999` | Timestamp color |\n\n#### Spacing Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-spacing-xs` | `4px` | Extra small spacing |\n| `--ngx-spacing-sm` | `8px` | Small spacing |\n| `--ngx-spacing-md` | `16px` | Medium spacing |\n| `--ngx-spacing-lg` | `24px` | Large spacing |\n| `--ngx-spacing-xl` | `32px` | Extra large spacing |\n| `--ngx-message-gap` | `8px` | Gap between messages |\n| `--ngx-bubble-padding` | `12px 16px` | Message bubble padding |\n| `--ngx-header-padding` | `16px` | Header padding |\n| `--ngx-footer-padding` | `12px 16px` | Footer padding |\n\n#### Border Radius Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-radius-sm` | `4px` | Small radius |\n| `--ngx-radius-md` | `12px` | Medium radius |\n| `--ngx-radius-lg` | `16px` | Large radius |\n| `--ngx-radius-full` | `9999px` | Full/pill radius |\n| `--ngx-bubble-radius` | `16px` | Message bubble radius |\n| `--ngx-input-radius` | `20px` | Input field radius |\n| `--ngx-button-radius` | `20px` | Button radius |\n| `--ngx-avatar-radius` | `50%` | Avatar radius |\n\n#### Typography Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-font-family` | `system-ui, ...` | Font family |\n| `--ngx-font-size-xs` | `0.75rem` | Timestamps, metadata |\n| `--ngx-font-size-sm` | `0.875rem` | Secondary text |\n| `--ngx-font-size-md` | `1rem` | Message text |\n| `--ngx-font-size-lg` | `1.125rem` | Headers |\n| `--ngx-font-weight-normal` | `400` | Normal weight |\n| `--ngx-font-weight-medium` | `500` | Medium weight |\n| `--ngx-font-weight-bold` | `600` | Bold weight |\n| `--ngx-line-height` | `1.5` | Base line height |\n\n#### Dimension Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-avatar-size` | `36px` | Avatar size |\n| `--ngx-max-bubble-width` | `70%` | Max message width |\n| `--ngx-input-min-height` | `44px` | Input minimum height |\n| `--ngx-input-max-height` | `120px` | Input maximum height |\n| `--ngx-button-size` | `44px` | Button size |\n\n#### Animation Tokens\n\n| Token | Default | Description |\n|-------|---------|-------------|\n| `--ngx-transition-duration` | `200ms` | Transition speed |\n| `--ngx-transition-easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | Easing function |\n| `--ngx-typing-animation-duration` | `1.4s` | Typing dots animation |\n\n### Dark Mode\n\nApply dark mode using CSS media query or class:\n\n```scss\n// Automatic dark mode\n@media (prefers-color-scheme: dark) {\n  :root {\n    --ngx-chat-bg: #1a1a1a;\n    --ngx-chat-message-area-bg: #121212;\n    --ngx-bubble-agent-bg: #2d2d2d;\n    --ngx-bubble-agent-text: #e0e0e0;\n    --ngx-input-bg: #2d2d2d;\n    --ngx-input-text: #e0e0e0;\n    --ngx-input-border: #404040;\n    --ngx-separator-line: #404040;\n  }\n}\n\n// Or with a class\n.dark-theme {\n  --ngx-chat-bg: #1a1a1a;\n  // ... other dark tokens\n}\n```\n\n### SCSS Mixins\n\nFor SCSS projects, import the theme mixins:\n\n```scss\n@use 'ngx-support-chat/styles/theme-default' as chat;\n\n:root {\n  @include chat.ngx-chat-default-tokens;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    @include chat.ngx-chat-dark-tokens;\n  }\n}\n```\n\n---\n\n## Pipes\n\n### SafeMarkdownPipe\n\nTransforms markdown text to sanitized HTML. Returns an Observable for async pipe usage.\n\n```html\n\u003cspan [innerHTML]=\"message.text | safeMarkdown | async\"\u003e\u003c/span\u003e\n```\n\n**Requirements:**\n- `ngx-markdown` installed\n- `MARKDOWN_SERVICE` provided\n- `markdown.enabled` and `markdown.displayMode` set to `true`\n\n---\n\n### TimeAgoPipe\n\nDisplays relative time (e.g., \"2 minutes ago\").\n\n```html\n{{ message.timestamp | timeAgo }}\n\u003c!-- Output: \"Just now\", \"5 minutes ago\", \"2 hours ago\" --\u003e\n```\n\n---\n\n### FileSizePipe\n\nFormats bytes to human-readable size.\n\n```html\n{{ 1536 | fileSize }}     \u003c!-- \"1.5 KB\" --\u003e\n{{ 1048576 | fileSize:0 }} \u003c!-- \"1 MB\" --\u003e\n{{ 2500000 | fileSize:2 }} \u003c!-- \"2.38 MB\" --\u003e\n```\n\n---\n\n## Directives\n\n### AutoResizeDirective\n\nAuto-resizing textarea that grows with content.\n\n**Selector:** `[ngxAutoResize]`\n\n```html\n\u003c!-- Default max height (120px) --\u003e\n\u003ctextarea ngxAutoResize\u003e\u003c/textarea\u003e\n\n\u003c!-- Custom max height --\u003e\n\u003ctextarea [ngxAutoResize]=\"200\"\u003e\u003c/textarea\u003e\n```\n\n---\n\n### AutoScrollDirective\n\nAuto-scrolls to bottom when new items are added (if user was at bottom).\n\n**Selector:** `[ngxAutoScroll]`\n\n```html\n\u003cdiv [ngxAutoScroll]=\"messages()\" [ngxAutoScrollThreshold]=\"100\"\u003e\n  @for (message of messages(); track message.id) {\n    \u003cngx-chat-message [message]=\"message\" /\u003e\n  }\n\u003c/div\u003e\n```\n\n| Input | Type | Default | Description |\n|-------|------|---------|-------------|\n| `ngxAutoScroll` | `unknown[]` | required | Array to watch for changes |\n| `ngxAutoScrollThreshold` | `number` | `100` | Distance from bottom to consider \"at bottom\" |\n\n---\n\n## Utilities\n\n### Message Grouping\n\nGroup messages by date and sender for efficient display:\n\n```typescript\nimport {\n  groupMessagesByDate,\n  shouldGroupWithPrevious,\n  getTotalMessageCount,\n  flattenGroupedMessages,\n  DEFAULT_GROUP_THRESHOLD_MS  // 5 minutes\n} from 'ngx-support-chat';\n\n// Group messages\nconst grouped = groupMessagesByDate(messages, currentUserId);\n\n// Check if messages should be grouped\nconst shouldGroup = shouldGroupWithPrevious(currentMsg, prevMsg, 5 * 60 * 1000);\n\n// Get total count\nconst count = getTotalMessageCount(grouped);\n\n// Flatten back to array\nconst flat = flattenGroupedMessages(grouped);\n```\n\n### Date Helpers\n\n```typescript\nimport {\n  formatDate,\n  formatTime,\n  isToday,\n  isYesterday,\n  isSameDay,\n  startOfDay,\n  getTimeDifferenceMs,\n  getRelativeTime\n} from 'ngx-support-chat';\n\nformatDate(new Date(), 'MMMM d, yyyy');  // \"December 29, 2025\"\nformatTime(new Date(), 'HH:mm');          // \"14:30\"\nisToday(date);                            // true/false\nisYesterday(date);                        // true/false\ngetRelativeTime(date);                    // \"5 minutes ago\"\n```\n\n**Supported format tokens:**\n- Date: `yyyy`, `MMMM`, `MMM`, `MM`, `M`, `dd`, `d`, `EEEE`, `EEE`\n- Time: `HH`, `H`, `hh`, `h`, `mm`, `m`, `ss`, `s`, `a`\n\n---\n\n## Accessibility\n\nThe library is built with accessibility in mind:\n\n### Screen Reader Support\n\n- **Live Announcements** - New messages announced via `LiveAnnouncer`\n- **ARIA Labels** - All interactive elements have appropriate labels\n- **Role Attributes** - Proper semantic roles (`list`, `listitem`, etc.)\n\n```typescript\nimport { ChatAnnouncerService } from 'ngx-support-chat';\n\n// Inject the service for custom announcements\nconstructor(private announcer: ChatAnnouncerService) {}\n\n// Announce a message\nthis.announcer.announceMessage(message);\n\n// Announce typing\nthis.announcer.announceTyping(indicator);\n\n// Announce quick reply selection\nthis.announcer.announceQuickReplySelection(option);\n```\n\n### Keyboard Navigation\n\n| Key | Action |\n|-----|--------|\n| `Tab` | Navigate between interactive elements |\n| `Enter` | Send message / Select option |\n| `Shift+Enter` | New line in input |\n| `Arrow Up/Down` | Navigate messages in message area |\n| `Escape` | Exit navigation mode |\n\n### Focus Management\n\n- Focus returns to input after sending messages\n- Focus returns to input after quick reply submission\n- Skip links for efficient navigation\n\n---\n\n## Advanced Usage\n\n### Building a Complete Chat Service\n\n```typescript\n@Injectable({ providedIn: 'root' })\nexport class ChatService {\n  private readonly messages = signal\u003cChatMessage[]\u003e([]);\n  private readonly typing = signal\u003cTypingIndicator | null\u003e(null);\n\n  readonly messages$ = this.messages.asReadonly();\n  readonly typing$ = this.typing.asReadonly();\n\n  sendMessage(event: MessageSendEvent): void {\n    const tempId = `temp-${Date.now()}`;\n\n    // Add optimistic message\n    const message: ChatMessage = {\n      id: tempId,\n      type: 'text',\n      senderId: this.currentUserId,\n      senderName: 'You',\n      timestamp: new Date(),\n      status: 'sending',\n      content: { text: event.text }\n    };\n\n    this.messages.update(msgs =\u003e [...msgs, message]);\n\n    // Send to API\n    this.api.sendMessage(event).subscribe({\n      next: (response) =\u003e {\n        this.messages.update(msgs =\u003e\n          msgs.map(m =\u003e m.id === tempId\n            ? { ...m, id: response.id, status: 'sent' }\n            : m\n          )\n        );\n      },\n      error: () =\u003e {\n        this.messages.update(msgs =\u003e\n          msgs.map(m =\u003e m.id === tempId\n            ? { ...m, status: 'failed' }\n            : m\n          )\n        );\n      }\n    });\n  }\n\n  setTyping(indicator: TypingIndicator | null): void {\n    this.typing.set(indicator);\n  }\n}\n```\n\n### Custom Message Types\n\nExtend the library for custom content:\n\n```typescript\n// Define custom content\ninterface CustomContent {\n  type: 'custom';\n  payload: Record\u003cstring, unknown\u003e;\n}\n\n// Create message\nconst message: ChatMessage = {\n  id: '1',\n  type: 'text', // Use 'text' as base type\n  senderId: 'system',\n  senderName: 'System',\n  timestamp: new Date(),\n  status: 'sent',\n  content: {\n    text: JSON.stringify(customPayload) // Encode in text\n  }\n};\n\n// Render custom content in your component\n@if (isCustomMessage(message)) {\n  \u003cmy-custom-renderer [data]=\"parseCustomContent(message)\" /\u003e\n}\n```\n\n### WebSocket Integration\n\n```typescript\n@Injectable({ providedIn: 'root' })\nexport class RealtimeChatService {\n  private socket: WebSocket;\n\n  connect(): void {\n    this.socket = new WebSocket('wss://api.example.com/chat');\n\n    this.socket.onmessage = (event) =\u003e {\n      const data = JSON.parse(event.data);\n\n      switch (data.type) {\n        case 'message':\n          this.addMessage(data.message);\n          break;\n        case 'typing':\n          this.setTyping(data.indicator);\n          break;\n        case 'status':\n          this.updateStatus(data.messageId, data.status);\n          break;\n      }\n    };\n  }\n}\n```\n\n---\n\n## Browser Support\n\n- Chrome (latest)\n- Firefox (latest)\n- Safari (latest)\n- Edge (latest)\n\nRequires Angular 21+.\n\n---\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/my-feature`\n3. Commit changes: `git commit -m 'Add my feature'`\n4. Push to branch: `git push origin feature/my-feature`\n5. Open a Pull Request\n\n---\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n---\n\n## Links\n\n- [GitHub Repository](https://github.com/avs2001/ngx-support-chat)\n- [npm Package](https://www.npmjs.com/package/ngx-support-chat)\n- [Live Demo](https://avs2001.github.io/ngx-support-chat/)\n- [Report Issues](https://github.com/avs2001/ngx-support-chat/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favs2001%2Fngx-support-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favs2001%2Fngx-support-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favs2001%2Fngx-support-chat/lists"}