{"id":"466b261b-14cf-4620-8fe8-069f65fd7034","shortId":"ruEZHS","kind":"skill","title":"cometchat-angular-customization","tagline":"Customize the CometChat Angular UI Kit without forking — four-tier model: Angular inputs → request builders → text formatters + message templates → DataSource decorators + event bus.","description":"## Purpose\n\nTeaches Claude how to change the behavior or appearance of the Angular UI Kit **without modifying the kit itself**. Four tiers, from cheapest to deepest:\n\n```\nTier 1 — Angular inputs     (95% of asks solved here)\nTier 2 — RequestBuilder     (filter what data loads)\nTier 3 — Formatters + Templates   (change how text / messages render)\nTier 4 — DataSource decorators + Events  (last resort, powerful)\n```\n\n**Always try Tier 1 first.** Escalate only when the tier can't do what the user wants.\n\n**Read `cometchat-angular-components` first** — the catalog is the source of truth for input names, slot templates, and event names that this skill builds on.\n\nGround truth: `docs/ui-kit/angular/custom-text-formatter-guide`, `docs/ui-kit/angular/events`, `docs/ui-kit/angular/methods`, and the kit's source.\n\n---\n\n## Four-tier triage — pick the right tier before writing any code\n\n**Start with Tier 1 every time.** The Angular UI Kit follows a \"inputs over components\" philosophy — most additions are inputs on already-mounted components, not new components or custom code.\n\n### Quick task → input lookup\n\nBefore escalating to any tier, check if an existing component input already does what you need:\n\n| User asks for | Likely input on which component |\n|---|---|\n| Search bar | `[hideSearch]=\"false\"` on `<cometchat-conversations>`, `<cometchat-users>`, `<cometchat-groups>` |\n| Filter conversations | `[conversationsRequestBuilder]` on `<cometchat-conversations>` |\n| Filter messages | `[messagesRequestBuilder]` on `<cometchat-message-list>` |\n| Filter users / groups | `[usersRequestBuilder]` / `[groupsRequestBuilder]` |\n| Custom empty state | `[emptyStateView]` on list components |\n| Custom error UI | `[errorStateView]` |\n| Custom loading UI | `[loadingStateView]` |\n| Custom list item | `[listItemView]` on `<cometchat-conversations>`, `<cometchat-users>`, `<cometchat-groups>` |\n| Custom header subtitle | `[subtitleView]` on `<cometchat-message-header>` |\n| Custom header menu | `[menu]` on `<cometchat-message-header>` |\n| Hide receipts | `[hideReceipt]=\"true\"` on `<cometchat-message-list>` |\n| Disable reactions | `[disableReactions]=\"true\"` on `<cometchat-message-list>` |\n| Disable mentions | `[disableMentions]=\"true\"` on `<cometchat-message-composer>` |\n| Custom send button | `[sendButtonView]` on `<cometchat-message-composer>` |\n| Custom attachment options | `[attachmentOptions]` on `<cometchat-message-composer>` |\n| Click handler on conversation | `[onItemClick]` on `<cometchat-conversations>` |\n| Active conversation highlight | `[activeConversation]` on `<cometchat-conversations>` |\n\n> **Note:** `[hideReactions]`, `[hideReplyInThreadOption]`, `[hideEditMessageOption]`, `[hideDeleteMessageOption]`, `[hideTranslateMessageOption]` do **not** exist in the Angular v4 UIKit. Use `[disableReactions]` for reactions. Message action options are controlled via the `[options]` callback or `[templates]` prop.\n\nIf a matching input exists, **add the input and stop**. No new components, no custom CSS, no new files.\n\n| If they want to... | Use Tier | Cost |\n|---|---|---|\n| Hide a feature (thread option, receipts, edit, etc.) | Tier 1 — `[hide*]` inputs | 1 line of HTML |\n| Customize a subsection (header subtitle, list item, empty state) | Tier 1 — `[*View]` / `[*Template]` slot | 1 `ng-template` |\n| Filter what loads (only show online users, exclude blocked, include tags) | Tier 2 — `[*RequestBuilder]` | 1 builder |\n| Change how URLs / mentions / hashtags / emojis render inline | Tier 3 — `[textFormatters]` | Subclass of `CometChatTextFormatter` |\n| Render a custom message type (custom bubble, custom interactive msg) | Tier 3 — `[templates]` + `CometChatMessageTemplate` | 1 template + 1 component |\n| React to events from another component | Tier 4 — `CometChatConversationEvents` / `CometChatMessageEvents` | RxJS subscription |\n| Rewrite how data flows through the kit | Tier 4 — `DataSourceDecorator` | Class extension |\n\nIf a user's ask fits Tier 1 but you jumped to Tier 3, you've written 50 lines that a 1-line input could have replaced. Start low.\n\n---\n\n## Tier 1 — Angular inputs (hide / slot views / styles)\n\n### 1a. `[hide*]` inputs\n\nTurn features off with a single input binding:\n\n```html\n<cometchat-message-list\n  [user]=\"selectedUser\"\n  [hideReceipt]=\"true\"\n  [disableReactions]=\"false\"\n  [disableSoundForMessages]=\"false\"\n></cometchat-message-list>\n```\n\nReal hide/disable inputs on `<cometchat-message-list>`: `[hideReceipt]`, `[hideError]`, `[hideDateSeparator]`, `[disableReactions]`, `[disableSoundForMessages]`, `[disableMentions]`.\n\nFull list of inputs per component: `cometchat-angular-components`. Check there before writing custom code.\n\n### 1b. `ng-template` slot views — replace a section\n\nEvery component has slot inputs for replacing named sections of its default UI. Pass an `ng-template` reference:\n\n```html\n<cometchat-conversations\n  [listItemView]=\"customListItem\"\n></cometchat-conversations>\n\n<ng-template #customListItem let-conversation>\n  <div class=\"custom-item\">\n    <span class=\"name\">{{ conversation.getConversationWith().getName() }}</span>\n    <span class=\"time\">{{ conversation.getLastMessage()?.getSentAt() | date:'shortTime' }}</span>\n  </div>\n</ng-template>\n```\n\n```typescript\nimport { ViewChild, TemplateRef } from \"@angular/core\";\n\n@Component({ /* ... */ })\nexport class AppComponent {\n  @ViewChild(\"customListItem\") customListItem!: TemplateRef<any>;\n}\n```\n\nFor the message header's subtitle:\n\n```html\n<cometchat-message-header\n  [user]=\"selectedUser\"\n  [subtitleView]=\"customSubtitle\"\n></cometchat-message-header>\n\n<ng-template #customSubtitle let-user>\n  <span style=\"color: #09C26F; font-size: 12px;\">\n    {{ user?.getStatus() === 'online' ? 'Online' : 'Offline' }}\n  </span>\n</ng-template>\n```\n\n### 1c. `[*Style]` inputs — per-component styling\n\nSee `cometchat-angular-theming` § 4 for the full style object reference. Use `[*Style]` for one-off overrides on a single component instance.\n\n---\n\n## Tier 2 — RequestBuilder filtering\n\nFor \"I want to show a subset of X\", use the matching `[*RequestBuilder]`. Never post-filter in-render.\n\n```typescript\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\n\n// Only conversations in a specific tag group\nconversationsRequestBuilder = new CometChat.ConversationsRequestBuilder()\n  .setLimit(20)\n  .setUserTags([\"premium\"])\n  .setConversationType(CometChat.RECEIVER_TYPE.USER);\n\n// Only online users, exclude blocked\nusersRequestBuilder = new CometChat.UsersRequestBuilder()\n  .setLimit(30)\n  .setStatus(\"online\")\n  .hideBlockedUsers(true);\n\n// Only groups you've joined\ngroupsRequestBuilder = new CometChat.GroupsRequestBuilder()\n  .setLimit(30)\n  .joinedOnly(true);\n\n// Message list — exclude system messages\nmessagesRequestBuilder = new CometChat.MessagesRequestBuilder()\n  .setUID(this.selectedUser.getUid())\n  .setLimit(30)\n  .setCategories([\"message\"]);\n```\n\n---\n\n## Tier 3 — Text formatters + message templates\n\n### 3a. Custom text formatter — inline text patterns\n\n`CometChatTextFormatter` is an abstract base class for matching inline text patterns and replacing them with custom HTML.\n\n```typescript\n// hashtag-formatter.ts\nimport { CometChatTextFormatter } from \"@cometchat/uikit-shared\";\n\nexport class HashtagFormatter extends CometChatTextFormatter {\n  constructor() {\n    super();\n    this.setTrackingCharacter(\"#\");\n    this.setRegexPatterns([/\\B#(\\w+)\\b/g]);\n    this.setRegexToReplaceFormatting([/#(\\w+)/g]);\n  }\n\n  override getFormattedText(inputText: string): string {\n    if (!inputText) return \"\";\n    return inputText.replace(\n      /\\B#(\\w+)\\b/g,\n      '<span style=\"color: #6851D6; font-weight: 600;\">#$1</span>'\n    );\n  }\n\n  override getOriginalText(inputText: string): string {\n    if (!inputText) return \"\";\n    return inputText.replace(/<span[^>]*>(#\\w+)<\\/span>/g, \"$1\");\n  }\n}\n```\n\nRegister by passing to both `<cometchat-message-list>` and `<cometchat-message-composer>`:\n\n```typescript\n// In your component:\nimport { HashtagFormatter } from \"./hashtag-formatter\";\nimport { CometChatMentionsFormatter, CometChatUrlsFormatter } from \"@cometchat/uikit-shared\";\n\ntextFormatters = [\n  new CometChatMentionsFormatter(),\n  new CometChatUrlsFormatter([\n    /https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/gi,\n  ]),\n  new HashtagFormatter(),\n];\n```\n\n```html\n<cometchat-message-list\n  [user]=\"selectedUser\"\n  [textFormatters]=\"textFormatters\"\n></cometchat-message-list>\n<cometchat-message-composer\n  [user]=\"selectedUser\"\n  [textFormatters]=\"textFormatters\"\n></cometchat-message-composer>\n```\n\n**⚠️ Pass the same `textFormatters` array to both list and composer.** If they differ, messages look different when sent vs. received.\n\n### 3b. Custom message template — entire custom bubble\n\nFor rendering a totally custom message type, use `CometChatMessageTemplate`.\n\n```typescript\n// In your component:\nimport { CometChatMessageTemplate } from \"@cometchat/uikit-shared\";\nimport { ChatConfigurator } from \"@cometchat/chat-uikit-angular\";\n\n// Get default templates to merge with\nconst defaultTemplates = ChatConfigurator.getDataSource().getAllMessageTemplates();\n\n// Create a custom template for a \"poll\" message type\nconst pollTemplate = new CometChatMessageTemplate({\n  type: \"poll\",\n  category: \"custom\",\n  ContentView: (message: CometChat.BaseMessage, alignment: string) => {\n    // Return an Angular component reference or HTML string\n    // For Angular, use a ViewContainerRef approach or pass a component factory\n    return null; // implement with your Angular component\n  },\n});\n\nmessageTemplates = [pollTemplate, ...defaultTemplates];\n```\n\n```html\n<cometchat-message-list\n  [user]=\"selectedUser\"\n  [templates]=\"messageTemplates\"\n></cometchat-message-list>\n```\n\n---\n\n## Tier 4 — Event bus + DataSource decorators\n\n### 4a. Event bus — RxJS subscriptions\n\nSubscribe to events that UI Kit components emit so your own code can react.\n\n```typescript\nimport { Component, OnInit, OnDestroy } from \"@angular/core\";\nimport { Subscription } from \"rxjs\";\nimport {\n  CometChatMessageEvents,\n  CometChatConversationEvents,\n  CometChatGroupEvents,\n} from \"@cometchat/chat-uikit-angular\";\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\n\n@Component({ /* ... */ })\nexport class AppComponent implements OnInit, OnDestroy {\n  private subscriptions: Subscription[] = [];\n\n  ngOnInit(): void {\n    this.subscriptions.push(\n      CometChatMessageEvents.ccMessageSent.subscribe(\n        ({ message, status }: { message: CometChat.BaseMessage; status: string }) => {\n          if (status === \"sent\") {\n            // analytics.track(\"message_sent\", { id: message.getId() });\n          }\n        }\n      ),\n      CometChatConversationEvents.ccConversationDeleted.subscribe(\n        (conversation: CometChat.Conversation) => {\n          // Remove from local cache\n        }\n      ),\n      CometChatGroupEvents.ccGroupLeft.subscribe(\n        ({ userLeft, leftGroup }: any) => {\n          // Handle group leave\n        }\n      )\n    );\n  }\n\n  ngOnDestroy(): void {\n    this.subscriptions.forEach((sub) => sub.unsubscribe());\n  }\n}\n```\n\n**Always unsubscribe in `ngOnDestroy`.** Angular components are destroyed on navigation — leaked subscriptions cause duplicate event handling.\n\n### Available event streams\n\n| Event class | Key events |\n|---|---|\n| `CometChatMessageEvents` | `ccMessageSent`, `ccMessageEdited`, `ccMessageDeleted`, `ccMessageRead`, `ccLiveReaction` |\n| `CometChatConversationEvents` | `ccConversationDeleted`, `ccUpdateConversation` |\n| `CometChatGroupEvents` | `ccGroupCreated`, `ccGroupDeleted`, `ccGroupLeft`, `ccGroupMemberScopeChanged`, `ccGroupMemberKicked`, `ccGroupMemberBanned`, `ccGroupMemberJoined`, `ccGroupMemberAdded`, `ccOwnershipChanged` |\n| `CometChatUserEvents` | `ccUserBlocked`, `ccUserUnblocked` |\n\n### 4b. DataSource decorators\n\n`DataSourceDecorator` wraps the kit's internal data source to override specific methods without forking the whole kit.\n\n```typescript\nimport {\n  DataSource,\n  DataSourceDecorator,\n  ChatConfigurator,\n} from \"@cometchat/chat-uikit-angular\";\n\nclass MyDataSource extends DataSourceDecorator {\n  constructor(source: DataSource) {\n    super(source);\n  }\n\n  // Override only the method you want to change\n  override getConversationsRequestBuilder() {\n    const builder = super.getConversationsRequestBuilder();\n    builder.setUserAndGroupTags(true);\n    return builder;\n  }\n}\n\n// Register before init — wraps the default data source\nChatConfigurator.dataSource = new MyDataSource(ChatConfigurator.getDataSource());\n// Then call CometChatUIKit.init(settings)\n```\n\n**This is an escape hatch, not a first tool.** Re-check whether Tier 1 (inputs) or Tier 3 (templates) could have solved it before reaching for Tier 4.\n\n---\n\n## 5. Recipes (common customization asks → right tier)\n\n### \"Filter the conversation list to just premium users\"\n**Tier 2** — `[conversationsRequestBuilder]` with `.setUserTags([\"premium\"])`.\n\n### \"Custom empty state for the users list\"\n**Tier 1** — `[emptyStateView]` slot input on `<cometchat-users>`.\n\n### \"Custom list item for conversations\"\n**Tier 1** — `[listItemView]` slot input on `<cometchat-conversations>`.\n\n### \"Show a custom view when the user types @\"\n**Tier 3a** — subclass `CometChatMentionsFormatter`, implement `search(key)` with your own suggestion source.\n\n### \"When a message is sent, log it to our analytics\"\n**Tier 4a** — `CometChatMessageEvents.ccMessageSent.subscribe(...)`.\n\n### \"When a group is deleted, navigate away\"\n**Tier 4a** — `CometChatGroupEvents.ccGroupDeleted.subscribe(...)`.\n\n### \"Render custom avatars for all users based on their department\"\n**Tier 1** — `[listItemView]` slot on `<cometchat-conversations>` + `<cometchat-users>`.\n\n### \"Disable the file attachment option\"\n**Tier 1** — filter the `[attachmentOptions]` input on `<cometchat-message-composer>`.\n\n### \"Custom message type: a 'ping' message\"\n**Tier 3b** — create a `CometChatMessageTemplate` with `category: \"custom\"` + `type: \"ping\"`, render a custom Angular component, send via `CometChat.sendCustomMessage`.\n\n---\n\n## 6. Anti-patterns\n\n1. **Don't hand-roll a bubble when a template will do.** `CometChatMessageTemplate` (Tier 3b) gives you full control over rendering + options without losing theming, reactions, typing, receipts.\n\n2. **Don't post-filter a list's data after render.** If you want \"only online users,\" use Tier 2 `usersRequestBuilder.setStatus(\"online\")` — don't fetch everyone then hide rows with `*ngIf`.\n\n3. **Don't forget to unsubscribe in `ngOnDestroy`.** Angular components are destroyed on navigation; leaked subscriptions cause duplicate event handling.\n\n4. **Don't put `CometChatTextFormatter` instances in component state that gets recreated.** Construct them once at class level (as a property, not in `ngOnInit`); re-creating them on every change detection cycle loses the internal suggestion state.\n\n5. **Don't fork or patch `@cometchat/chat-uikit-angular` directly.** Every customization should be possible via Tiers 1-4. Forking breaks on kit upgrades.\n\n6. **Don't reach for Tier 4 before trying 1-3.** DataSource decorators are powerful but fragile to kit internal changes. Inputs, request builders, and templates are stable surface area.\n\n---\n\n## Skill routing reference\n\n| Skill | When to route |\n|---|---|\n| `cometchat-angular-core` | Init / login / module setup |\n| `cometchat-angular-components` | Input reference — which `[hide*]`, `[*View]`, `[*RequestBuilder]` is available |\n| `cometchat-angular-placement` | Where to put the customized components |\n| `cometchat-angular-theming` | App-wide color / typography — Tier 1 alternative to `[*Style]` |\n| `cometchat-angular-features` | Which out-of-the-box features exist |\n| `cometchat-angular-customization` | This skill — four-tier triage + custom formatters / templates / DataSource / events |\n| `cometchat-angular-production` | When customization depends on production auth |\n| `cometchat-angular-troubleshooting` | Formatter doesn't apply, listener fires twice, slot view renders nothing |","tags":["cometchat","angular","customization","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-angular-customization","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-customization","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (16,075 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:48.035Z","embedding":null,"createdAt":"2026-05-07T13:05:07.358Z","updatedAt":"2026-05-18T19:04:48.035Z","lastSeenAt":"2026-05-18T19:04:48.035Z","tsv":"'-3':1529 '-4':1513 '/g':785,813 '/gi':861 '/hashtag-formatter':828 '/https':839 '/span':812 '1':56,91,156,361,364,378,382,400,430,432,465,479,488,799,814,846,853,1220,1264,1275,1334,1344,1378,1512,1528,1596 '1a':495 '1b':545 '1c':619 '2':65,398,651,1251,1407,1427 '20':690 '256':847 '3':72,411,427,471,736,1224,1439 '30':704,718,732 '3a':741,1289 '3b':901,1357,1393 '4':81,441,454,631,1000,1234,1459,1525 '4a':1005,1311,1321 '4b':1137 '5':1235,1497 '50':475 '6':854,1374,1519 '9':845,852,860 '95':59 'a-za-z0':841,848,856 'abstract':751 'action':315 'activ':291 'activeconvers':294 'add':331 'addit':170 'align':959 'alreadi':175,199 'already-mount':174 'altern':1597 'alway':88,1092 'analyt':1309 'analytics.track':1068 'angular':3,8,17,41,57,108,160,307,489,537,629,963,970,985,1096,1369,1447,1558,1566,1578,1588,1602,1614,1629,1639 'angular/core':590,1030 'anoth':438 'anti':1376 'anti-pattern':1375 'app':1591 'app-wid':1590 'appcompon':594,1048 'appear':38 'appli':1644 'approach':974 'area':1548 'array':885 'ask':61,205,462,1239 'attach':281,1341 'attachmentopt':283,1347 'auth':1636 'avail':1108,1575 'avatar':1325 'away':1319 'b':780,796,855 'b/g':782,798 'bar':213 'base':752,1329 'behavior':36 'bind':505 'block':394,699 'box':1609 'break':1515 'bubbl':422,907,1385 'build':129 'builder':20,401,1184,1189,1542 'builder.setuserandgrouptags':1186 'bus':28,1002,1007 'button':277 'cach':1079 'call':1203 'callback':322 'catalog':112 'categori':954,1362 'caus':1104,1455 'ccconversationdelet':1122 'ccgroupcreat':1125 'ccgroupdelet':1126 'ccgroupleft':1127 'ccgroupmemberad':1132 'ccgroupmemberban':1130 'ccgroupmemberjoin':1131 'ccgroupmemberkick':1129 'ccgroupmemberscopechang':1128 'cclivereact':1120 'ccmessagedelet':1118 'ccmessageedit':1117 'ccmessageread':1119 'ccmessages':1116 'ccownershipchang':1133 'ccupdateconvers':1123 'ccuserblock':1135 'ccuserunblock':1136 'chang':34,75,402,1180,1489,1539 'chatconfigur':926,1161 'chatconfigurator.datasource':1198 'chatconfigurator.getdatasource':937,1201 'cheapest':52 'check':193,539,1217 'class':456,593,753,772,1047,1112,1164,1475 'claud':31 'click':285 'code':152,183,544,1021 'color':1593 'cometchat':2,7,107,508,536,575,607,628,676,866,874,992,1042,1557,1565,1577,1587,1601,1613,1628,1638 'cometchat-angular-compon':106,535,1564 'cometchat-angular-cor':1556 'cometchat-angular-custom':1,1612 'cometchat-angular-featur':1600 'cometchat-angular-plac':1576 'cometchat-angular-product':1627 'cometchat-angular-them':627,1586 'cometchat-angular-troubleshoot':1637 'cometchat-convers':574 'cometchat-message-compos':873 'cometchat-message-head':606 'cometchat-message-list':507,865,991 'cometchat.basemessage':958,1062 'cometchat.conversation':1075 'cometchat.conversationsrequestbuilder':688 'cometchat.groupsrequestbuilder':716 'cometchat.messagesrequestbuilder':728 'cometchat.receiver_type.user':694 'cometchat.sendcustommessage':1373 'cometchat.usersrequestbuilder':702 'cometchat/chat-sdk-javascript':678,1044 'cometchat/chat-uikit-angular':928,1040,1163,1503 'cometchat/uikit-shared':770,833,924 'cometchatconversationev':442,1037,1121 'cometchatconversationevents.ccconversationdeleted.subscribe':1073 'cometchatgroupev':1038,1124 'cometchatgroupevents.ccgroupdeleted.subscribe':1322 'cometchatgroupevents.ccgroupleft.subscribe':1080 'cometchatmentionsformatt':830,836,1291 'cometchatmessageev':443,1036,1115 'cometchatmessageevents.ccmessagesent.subscribe':1058,1312 'cometchatmessagetempl':429,916,922,951,1360,1391 'cometchattextformatt':415,748,768,775,1463 'cometchatuikit.init':1204 'cometchaturlsformatt':831,838 'cometchatuserev':1134 'common':1237 'compon':109,167,177,180,197,211,236,338,433,439,534,538,555,591,624,648,824,920,964,978,986,1016,1026,1045,1097,1370,1448,1466,1567,1585 'compos':876,890 'const':935,948,1183 'construct':1471 'constructor':776,1168 'contentview':956 'control':318,1397 'convers':218,288,292,576,680,1074,1244,1273 'conversation.getconversationwith':579 'conversation.getlastmessage':581 'conversationsrequestbuild':219,686,1252 'core':1559 'cost':351 'could':482,1226 'creat':939,1358,1485 'css':341 'custom':4,5,182,230,237,241,245,250,255,275,280,340,368,418,421,423,543,742,763,902,906,912,941,955,1238,1256,1269,1282,1324,1350,1363,1368,1506,1584,1615,1622,1632 'customlistitem':578,596,597 'customsubtitl':613 'cycl':1491 'data':69,448,1146,1196,1416 'datasourc':25,82,1003,1138,1159,1170,1530,1625 'datasourcedecor':455,1140,1160,1167 'date':583 'decor':26,83,1004,1139,1531 'deepest':54 'default':565,930,1195 'defaulttempl':936,989 'delet':1317 'depart':1332 'depend':1633 'destroy':1099,1450 'detect':1490 'differ':893,896 'direct':1504 'disabl':265,270,1338 'disablement':272,528 'disablereact':267,311,515,526 'disablesoundformessag':517,527 'docs/ui-kit/angular/custom-text-formatter-guide':133 'docs/ui-kit/angular/events':134 'docs/ui-kit/angular/methods':135 'doesn':1642 'duplic':1105,1456 'edit':358 'emit':1017 'emoji':407 'empti':231,375,1257 'emptystateview':233,1265 'entir':905 'error':238 'errorstateview':240 'escal':93,189 'escap':1209 'etc':359 'event':27,84,124,436,1001,1006,1012,1106,1109,1111,1114,1457,1626 'everi':157,554,1488,1505 'everyon':1433 'exclud':393,698,723 'exist':196,304,330,1611 'export':592,771,1046 'extend':774,1166 'extens':457 'factori':979 'fals':215,516,518 'featur':354,499,1603,1610 'fetch':1432 'file':344,1340 'filter':67,217,221,225,386,653,670,1242,1345,1412 'fire':1646 'first':92,110,1213 'fit':463 'flow':449 'follow':163 'forget':1442 'fork':12,1153,1500,1514 'formatt':22,73,738,744,1623,1641 'four':14,49,142,1619 'four-tier':13,141,1618 'fragil':1535 'full':529,634,1396 'get':929,1469 'getallmessagetempl':938 'getconversationsrequestbuild':1182 'getformattedtext':787 'getnam':580 'getoriginaltext':801 'getsentat':582 'getstatus':615 'give':1394 'ground':131 'group':227,685,710,1085,1315 'groupsrequestbuild':229,714 'hand':1382 'hand-rol':1381 'handl':1084,1107,1458 'handler':286 'hashtag':406 'hashtag-formatter.ts':766 'hashtagformatt':773,826,863 'hatch':1210 'header':251,256,371,602,609 'hide':260,352,362,491,496,1435,1571 'hide/disable':520 'hideblockedus':707 'hidedatesepar':525 'hidedeletemessageopt':300 'hideeditmessageopt':299 'hideerror':524 'hidereact':297 'hidereceipt':262,513,523 'hidereplyinthreadopt':298 'hidesearch':214 'hidetranslatemessageopt':301 'highlight':293 'html':367,506,573,605,764,864,967,990 'id':1071 'implement':982,1049,1292 'import':586,675,767,825,829,921,925,1025,1031,1035,1041,1158 'in-rend':671 'includ':395 'init':1192,1560 'inlin':409,745,756 'input':18,58,119,165,172,186,198,208,329,333,363,481,490,497,504,521,532,558,621,1221,1267,1278,1348,1540,1568 'inputtext':788,792,802,806 'inputtext.replace':795,809 'instanc':649,1464 'interact':424 'intern':1145,1494,1538 'item':247,374,1271 'join':713 'joinedon':719 'jump':468 'key':1113,1294 'kit':10,43,47,138,162,452,1015,1143,1156,1517,1537 'last':85 'leak':1102,1453 'leav':1086 'leftgroup':1082 'level':1476 'like':207 'line':365,476,480 'list':235,246,373,510,530,722,868,888,994,1245,1262,1270,1414 'listen':1645 'listitemview':248,577,1276,1335 'load':70,242,388 'loadingstateview':244 'local':1078 'log':1305 'login':1561 'look':895 'lookup':187 'lose':1402,1492 'low':486 'match':328,665,755 'mention':271,405 'menu':257,258 'merg':933 'messag':23,78,222,314,419,509,601,608,721,725,734,739,867,875,894,903,913,946,957,993,1059,1061,1069,1302,1351,1355 'message.getid':1072 'messagesrequestbuild':223,726 'messagetempl':987,998 'method':1151,1176 'model':16 'modifi':45 'modul':1562 'mount':176 'msg':425 'mydatasourc':1165,1200 'name':120,125,561 'navig':1101,1318,1452 'need':203 'never':667 'new':179,337,343,687,701,715,727,835,837,862,950,1199 'ng':384,547,570 'ng-templat':383,546,569 'ngif':1438 'ngondestroy':1087,1095,1446 'ngoninit':1055,1482 'note':296 'noth':1651 'null':981 'object':636 'offlin':618 'ondestroy':1028,1051 'one':642 'one-off':641 'oninit':1027,1050 'onitemclick':289 'onlin':391,616,617,696,706,1423,1429 'option':282,316,321,356,1342,1400 'out-of-the-box':1605 'overrid':644,786,800,1149,1173,1181 'pass':567,817,881,976 'patch':1502 'pattern':747,758,1377 'per':533,623 'per-compon':622 'philosophi':168 'pick':145 'ping':1354,1365 'placement':1579 'poll':945,953 'polltempl':949,988 'possibl':1509 'post':669,1411 'post-filt':668,1410 'power':87,1533 'premium':692,1248,1255 'privat':1052 'product':1630,1635 'prop':325 'properti':1479 'purpos':29 'put':1462,1582 'quick':184 're':1216,1484 're-check':1215 're-creat':1483 'reach':1231,1522 'react':434,1023 'reaction':266,313,1404 'read':105 'real':519 'receipt':261,357,1406 'receiv':900 'recip':1236 'recreat':1470 'refer':572,637,965,1551,1569 'regist':815,1190 'remov':1076 'render':79,408,416,673,909,1323,1366,1399,1418,1650 'replac':484,551,560,760 'request':19,1541 'requestbuild':66,399,652,666,1573 'resort':86 'return':793,794,807,808,961,980,1188 'rewrit':446 'right':147,1240 'roll':1383 'rout':1550,1555 'row':1436 'rxjs':444,1008,1034 'search':212,1293 'section':553,562 'see':626 'selectedus':512,611,870,878,996 'send':276,1371 'sendbuttonview':278 'sent':898,1067,1070,1304 'set':1205 'setcategori':733 'setconversationtyp':693 'setlimit':689,703,717,731 'setstatus':705 'setuid':729 'setup':1563 'setusertag':691,1254 'shorttim':584 'show':390,658,1280 'singl':503,647 'skill':128,1549,1552,1617 'skill-cometchat-angular-customization' 'slot':121,381,492,549,557,1266,1277,1336,1648 'solv':62,1228 'sourc':115,140,1147,1169,1172,1197,1299 'source-cometchat' 'span':810 'specif':683,1150 'stabl':1546 'start':153,485 'state':232,376,1258,1467,1496 'status':1060,1063,1066 'stop':335 'stream':1110 'string':789,790,803,804,960,968,1064 'style':494,620,625,635,639,1599 'sub':1090 'sub.unsubscribe':1091 'subclass':413,1290 'subscrib':1010 'subscript':445,1009,1032,1053,1054,1103,1454 'subsect':370 'subset':660 'subtitl':252,372,604 'subtitleview':253,612 'suggest':1298,1495 'super':777,1171 'super.getconversationsrequestbuilder':1185 'surfac':1547 'system':724 'tag':396,684 'task':185 'teach':30 'templat':24,74,122,324,380,385,428,431,548,571,740,904,931,942,997,1225,1388,1544,1624 'templateref':588,598 'text':21,77,737,743,746,757 'textformatt':412,834,871,872,879,880,884 'theme':630,1403,1589 'this.selecteduser.getuid':730 'this.setregexpatterns':779 'this.setregextoreplaceformatting':783 'this.settrackingcharacter':778 'this.subscriptions.foreach':1089 'this.subscriptions.push':1057 'thread':355 'tier':15,50,55,64,71,80,90,97,143,148,155,192,350,360,377,397,410,426,440,453,464,470,487,650,735,999,1219,1223,1233,1241,1250,1263,1274,1288,1310,1320,1333,1343,1356,1392,1426,1511,1524,1595,1620 'time':158 'tool':1214 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'total':911 'tri':89,1527 'triag':144,1621 'troubleshoot':1640 'true':263,268,273,514,708,720,1187 'truth':117,132 'turn':498 'twice':1647 'type':420,914,947,952,1287,1352,1364,1405 'typescript':585,674,765,821,917,1024,1157 'typographi':1594 'ui':9,42,161,239,243,566,1014 'uikit':309 'unsubscrib':1093,1444 'upgrad':1518 'url':404 'use':310,349,638,663,915,971,1425 'user':103,204,226,392,460,511,610,614,697,869,877,995,1249,1261,1286,1328,1424 'userleft':1081 'usersrequestbuild':228,700 'usersrequestbuilder.setstatus':1428 'v4':308 've':473,712 'via':319,1372,1510 'view':379,493,550,1283,1572,1649 'viewchild':587,595 'viewcontainerref':973 'void':1056,1088 'vs':899 'w':781,784,797,811 'want':104,347,656,1178,1421 'whether':1218 'whole':1155 'wide':1592 'without':11,44,1152,1401 'wrap':1141,1193 'write':150,542 'written':474 'www':840 'x':662 'z0':844,851,859 'za':843,850,858","prices":[{"id":"3b9fc463-56f6-41be-ba22-9e2f2483505d","listingId":"466b261b-14cf-4620-8fe8-069f65fd7034","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:07.358Z"}],"sources":[{"listingId":"466b261b-14cf-4620-8fe8-069f65fd7034","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-angular-customization","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-customization","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:07.358Z","lastSeenAt":"2026-05-18T19:04:48.035Z"}],"details":{"listingId":"466b261b-14cf-4620-8fe8-069f65fd7034","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-angular-customization","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"f74da288dab0300eb9bee75211b8e97546f0a0e4","skill_md_path":"skills/cometchat-angular-customization/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-customization"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-angular-customization","license":"MIT","description":"Customize the CometChat Angular UI Kit without forking — four-tier model: Angular inputs → request builders → text formatters + message templates → DataSource decorators + event bus.","compatibility":"Angular >=12 <=15; @cometchat/chat-uikit-angular ^4; @cometchat/chat-sdk-javascript ^4"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-customization"},"updatedAt":"2026-05-18T19:04:48.035Z"}}