[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6MFJAPTYW8X7KT0XTTXTR27":3,"skill-same:01M6MFJAPTYW8X7KT0XTTXTR27":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":59,"commitSha":60,"skillDirectory":61,"forksCount":62,"repositoryHtmlUrl":63,"repositoryHomepageUrl":64,"ownerHtmlUrl":65,"ownerBlogUrl":66,"ownerDescription":67,"updatedAt":68,"install":69,"resources":82},"01M6MFJAPTYW8X7KT0XTTXTR27","cometchat-react-v7-calls",null,"Add voice & video calling to a React app with the CometChat v7 UI Kit — install the Calls SDK, enable calling on the UIKit settings, mount the incoming-call surface, and wire 1:1 ring \u002F group join. Bakes the real gotchas (ms idle-timeout, zero-dimension call surface, Next.js SSR, no group-ring, HTTPS\u002FgetUserMedia). Triggers: 'add voice and video calls', 'add calling to my chat', 'enable video call', 'add call buttons', 'show call logs', 'cometchat calling react'.","hashgraph-online\u002Fawesome-codex-plugins","hashgraph-online","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F160371962?v=4","01M6CNB6NRJVNNGN14TR01Q6D3","HOL (Hashgraph Online)","01M6JT1HSKKG9V68KQAQGBA7XP",1575,"MIT","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fcometchat\u002Fcometchat-skills\u002Fskills\u002Fcometchat-react-v7-calls\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"cometchat react v7 calls","calls","react","cometchat","buttons","triggers","ZeRO","add","ui","my","verified","install","ssr","next","logs","webrtc","join","kit","ms","show","app","Video","2026-11-10 10:12:30",false,"---\nname: cometchat-react-v7-calls\ndescription: \"Add voice & video calling to a React app with the CometChat v7 UI Kit — install the Calls SDK, enable calling on the UIKit settings, mount the incoming-call surface, and wire 1:1 ring \u002F group join. Bakes the real gotchas (ms idle-timeout, zero-dimension call surface, Next.js SSR, no group-ring, HTTPS\u002FgetUserMedia). Triggers: 'add voice and video calls', 'add calling to my chat', 'enable video call', 'add call buttons', 'show call logs', 'cometchat calling react'.\"\nlicense: \"MIT\"\ncompatibility: \"React >=18 \u003C21; @cometchat\u002Fchat-uikit-react ^7 (7.2.x, verified 7.2.4); @cometchat\u002Fchat-sdk-javascript ^4.1.9; @cometchat\u002Fcalls-sdk-javascript ^5 (5.0.0 — separate, NOT bundled)\"\nmetadata:\n  author: \"CometChat\"\n  version: \"1.0.0\"\n  tags: \"cometchat react calls voice video calling webrtc v7 uikit\"\n---\n\n> **Ground truth:** calling is part of `@cometchat\u002Fchat-uikit-react@7` (the call UI ships INSIDE the kit — there is no separate calls-UI package), but the WebRTC engine is a SEPARATE, NOT-bundled dependency: `@cometchat\u002Fchat-uikit-react@^7` + `@cometchat\u002Fchat-sdk-javascript@^4.1.9` + **`@cometchat\u002Fcalls-sdk-javascript@^5`** (major 5 — distinct from the chat SDK's v4). Every `CometChat*` symbol below exists in the `web-v7` catalog. Exhaustive props + advanced call surfaces are FETCHED from the docs `.md` twins via `cometchat-react-v7-core\u002Freferences\u002Fdocs-map.md`. **APPEND to the user's app — additive wiring only** (`RULES.md`).\n\n## Companion skills (read first)\n- `cometchat-react-v7-core` — install, credentials, `init→login→render`, `CometChatProvider`. This skill ASSUMES it (calling is enabled on the SAME init).\n- `cometchat-react-v7-components` — the component catalog (message header the call buttons live in).\n- `cometchat-react-v7-features` — `voice-video-calls` is the feature entry; this skill is its operational depth.\n\n## Use this skill when\n\"add voice & video calls\", \"add calling to my chat\", \"enable video call\", \"add call buttons\", \"show call logs\". Precondition: chat is already integrated (core done) — calling rides on the same init\u002Flogin.\n\n## Prerequisites & install\nCore setup done (init + login + `CometChatProvider`). Calling adds ONE package:\n```bash\nnpm install @cometchat\u002Fcalls-sdk-javascript@5\n```\n> The Calls SDK is **major 5** (not the chat SDK's v4) and is **NOT bundled** by the UI Kit — if it isn't installed, every call affordance stays hidden. Do not \"upgrade\" `@cometchat\u002Fchat-sdk-javascript` to a non-existent v7; it stays `^4.1.9`.\n\n## Enable calling (BAKED — the two-part contract, the #1 gotcha)\nCalling is OFF until BOTH are true: the Calls SDK is installed (above) **and** calling is turned ON in the init settings. Since the core inits via **`CometChatUIKit.initFromSettings`** (telemetry — `RULES.md` §5 \u002F AUDIT-084), enable calling IN the SAME settings object with a truthy **`uiKit.callsSDK`**:\n```tsx\nimport { CometChatUIKit } from \"@cometchat\u002Fchat-uikit-react\";\n\nawait CometChatUIKit.initFromSettings({\n  appId: APP_ID, region: REGION,\n  credentials: { authKey: AUTH_KEY },                        \u002F\u002F dev-only; prod → server-minted auth token\n  chatSDK: { presenceSubscription: { type: \"ALL_USERS\" } },\n  uiKit: { callsSDK: {} },   \u002F\u002F ← turns calling ON (the initFromSettings equivalent of setCallingEnabled(true))\n});\n```\n> **How calling maps in the initFromSettings path (verified vs the compiled 7.1.x kit; DOCS-GAP).** The kit computes `callingEnabled = !!settings.uiKit.callsSDK`, so a **truthy `uiKit.callsSDK`** is the `initFromSettings` equivalent of `UIKitSettingsBuilder().setCallingEnabled(true)` — WITHOUT it, call buttons + incoming-call render nothing. The docs don't yet document this shape (owner: **docs** — confirm with a live call). The classic path still works — `CometChatUIKit.init(new UIKitSettingsBuilder()...setCallingEnabled(true).build())` — but it SKIPS the ai-agent telemetry attribution, so prefer `initFromSettings`.\n> No separate `CometChatUIKitCalls.init()` is needed for the default path. (`CometChatUIKitCalls` is the kit's namespace for advanced call-settings builders; reach for it only on a custom surface — FETCH then.) `initFromSettings` routes the Calls SDK internally — do NOT call raw `CometChat.init()`.\n\n## Wire the UI (BAKED — least-code path)\nTwo moves cover ringing end-to-end:\n1. **Mount `CometChatIncomingCall` ONCE at the app root** (above your routes). It self-manages visibility — renders nothing when idle, rings on an inbound call — so **no conditional wrapper**. It attaches its own SDK listeners.\n   ```tsx\n   import { CometChatIncomingCall } from \"@cometchat\u002Fchat-uikit-react\";\n   \u002F\u002F at the app shell, inside CometChatProvider, above the router:\n   \u003CCometChatIncomingCall \u002F>\n   ```\n2. **Let `CometChatMessageHeader` surface the call buttons.** When a `user`\u002F`group` is selected, the header composes voice\u002Fvideo call buttons itself once calling is enabled — you usually mount NOTHING extra. Only drop a standalone `CometChatCallButtons user={user}` (1:1) or `group={group}` on a CUSTOM header that has no `CometChatMessageHeader`. **Never mount both** — a standalone `CometChatCallButtons` next to a `CometChatMessageHeader` duplicates the buttons.\n\n**Call logs** (optional history surface): `CometChatCallLogs` on a sidebar or a `\u002Fcalls` route (`onItemClick`, `onCallButtonClicked`, `callLogRequestBuilder`). It does NOT auto-refresh on a new call — remount to refresh. **It shows the kit's \"OOPS! ...\" screen when call logs aren't available — and, verified live, it renders that from its OWN INTERNAL error state WITHOUT throwing, so a host `CometChatErrorBoundary`\u002F`fallbackView` CANNOT override it and `onError` may not fire. A fallbackView is NOT the fix (AUDIT-162).** **Enabling calling is NOT sufficient for `CometChatCallLogs`:** call-logs availability is a SEPARATE plan\u002Fcapability gate — an app can have calling ON (Voice\u002FVideo buttons render) yet `CometChatCallLogs` still \"OOPS\" because call-logs isn't in the plan. **The only reliable protection is to GATE the Calls tab on real call-logs AVAILABILITY** (a one-time SDK probe you own — `new CometChatCalls.CallLogRequestBuilder().setLimit(1).setAuthToken(await CometChatUIKitCalls.generateToken(...)).build().fetchNext()`, show the tab only on success — or your known plan), NOT merely on calling being enabled; default the tab OFF so a plain app never dead-ends. **Import discipline (AUDIT-204): `CallLogRequestBuilder` is NOT a named export of `@cometchat\u002Fcalls-sdk-javascript` — it is a STATIC on the `CometChatCalls` default-export class** (verified vs calls-sdk 5.0.5 `dist\u002Findex.d.ts`): use `import { CometChatCalls } from \"@cometchat\u002Fcalls-sdk-javascript\"; new CometChatCalls.CallLogRequestBuilder()...` — a `import { CallLogRequestBuilder } from \"@cometchat\u002Fcalls-sdk-javascript\"` FAILS (TS2614). `generateToken` is on `CometChatUIKitCalls` (from `@cometchat\u002Fchat-uikit-react`). (Gated Calls-tab recipe with the full probe + imports: `..\u002Fcometchat-react-v7-placement\u002Freferences\u002Frecipes.md` Combined-app recipe.)\n\n### 1:1 rings, groups JOIN (BAKED — no group-ring)\n- **1:1:** call buttons → the kit initiates a call → the recipient's root `CometChatIncomingCall` RINGS → accept → the ongoing-call surface. The kit drives outgoing → ongoing for you.\n- **Group:** call buttons on a group send a **group-call meeting message** into the chat; members **JOIN** from that message. There is **no \"ring the whole group\" primitive** — surface the join affordance, never wait for a ring.\n\n### Huddle \u002F docked call — React how-to (BAKED — verified vs 7.2.4)\nThe pattern (start → join → dock → indicator → leave) and when to use it are platform-independent: `cometchat-onboarding\u002Freferences\u002Fclone-mode.md` → *Huddle \u002F drop-in room pattern*. This is the React code for it.\n- **`\u003CCometChatOngoingCall>` can't be docked:** it portals into the kit's full-screen `.cometchat-call-overlay` wherever you mount it. **`useOngoingCall().startCall(el)` is broken in 7.2.4:** it calls `generateToken` without the auth token and its error handler throws, so nothing renders. Join the session into your own element the way `CometChatOngoingCall` does internally:\n```tsx\nimport { useEffect, useRef } from \"react\";\nimport { CometChat, type Group } from \"@cometchat\u002Fchat-sdk-javascript\";\nimport { CometChatCallBubble, CometChatUIKit, CometChatUIKitCalls, type CometChatMessagePlugin } from \"@cometchat\u002Fchat-uikit-react\";\n\n\u002F\u002F Start: the same \"meeting\" message the kit's group voice button sends (Join bubble + conversation\n\u002F\u002F preview). Session id = group GUID, so the whole channel shares one room.\nexport async function startHuddle(group: Group): Promise\u003Cstring> {\n  const sessionId = group.getGuid();\n  const msg = new CometChat.CustomMessage(sessionId, CometChat.RECEIVER_TYPE.GROUP, \"meeting\", { sessionID: sessionId, sessionId, callType: \"audio\" });\n  msg.shouldUpdateConversation(true);\n  await CometChat.sendCustomMessage(msg);\n  return sessionId;\n}\n\n\u002F\u002F Join: replace the kit's meeting bubble so \"Join\" opens YOUR dock, not the full-screen dialog.\nexport const huddlePlugin = (onJoin: (sessionId: string) => void): CometChatMessagePlugin => ({\n  id: \"huddle-meeting\", messageTypes: [\"meeting\"], messageCategories: [\"custom\"],\n  renderBubble: (message) => \u003CCometChatCallBubble message={message} onJoinClick={onJoin} \u002F>,\n  getOptions: () => [],\n});\n\n\u002F\u002F The dock: a fixed-size element you own; the Calls SDK renders tiles + controls into it.\nexport function HuddleDock({ sessionId, onLeave }: { sessionId: string; onLeave: () => void }) {\n  const frame = useRef\u003CHTMLDivElement>(null);\n  const onLeaveRef = useRef(onLeave);\n  onLeaveRef.current = onLeave;\n  useEffect(() => {\n    const calls = CometChatUIKitCalls;\n    const user = CometChatUIKit.getLoggedInUser();\n    if (!calls || !user || !frame.current) return;\n    const el = frame.current;\n    let left = false;\n    const leave = () => { if (left) return; left = true; calls.leaveSession(); onLeaveRef.current(); };\n    const unsubs = [calls.addEventListener(\"onSessionLeft\", leave), calls.addEventListener(\"onLeaveSessionButtonClicked\", leave)];\n    calls.generateToken(sessionId, user.getAuthToken()).then((res: { token: string }) =>\n      calls.joinSession(res.token, { sessionType: \"VOICE\", layout: \"TILE\", hideChangeLayoutButton: true, hideVirtualBackgroundButton: true }, el));\n    return () => { unsubs.forEach((u: () => void) => u()); if (!left) { left = true; calls.leaveSession(); } };\n  }, [sessionId]);\n  return \u003Cdiv ref={frame} className=\"cc-huddle-dock\" style={{ width: 320, height: 220, position: \"relative\" }} \u002F>;\n}\n```\n- **Wire it:** a \"Huddle\" button in the channel header (`CometChatMessageHeader` with `hideVoiceCallButton`, your button in `auxiliaryButtonView`) calls `startHuddle` and mounts `\u003CHuddleDock>` where the target shows it (Slack: bottom of the sidebar). Pass `plugins={[huddlePlugin(openDock), ...]}` to `CometChatProvider`, defined once and not per render. Keep the dock mounted while the user moves between channels; unmounting leaves the call.\n- **Size:** the dock needs a fixed width and height (zero-dimension pitfall below). Expanding it into a window is just a bigger container.\n- **Indicator:** from the latest `meeting` message; participant events → FETCH from the Calls SDK docs.\n- **Verified 2026-10-15 (7.2.4, calls-sdk 5.0.7):** strict `tsc` clean; in Chromium the session joined inside a 320x220 dock with no overlay, and Join from the replaced bubble opened the same dock. Not yet run with two users at once: do the two-user check before calling it done.\n\n## Custom call surfaces (FETCH — don't bake)\nFor anything past the default ring\u002Fjoin — a custom ongoing-call screen (`CometChatOngoingCall`), a bespoke call manager (`CometChatOutgoingCall`), idle-timeout tuning, screen-share, picture-in-picture, device selection, recording, virtual background — FETCH the exact props\u002FAPI from the docs `.md` twins via `cometchat-react-v7-core\u002Freferences\u002Fdocs-map.md` (rows: `call-buttons` · `incoming-call` · `outgoing-call` · `call-logs` · `calls-setup` · `calls-react-integration` · the per-feature Calls-SDK pages). These props change; never emit them from memory or read `.d.ts`.\n\n## Common pitfalls (BAKED — these bite in production)\n- **Idle-timeout is in MILLISECONDS.** `180` = 180 ms, so the \"are you still there?\" prompt fires and exits instantly. Use `180_000` for 3 minutes. (Exact v7 field\u002Fclamp → FETCH the idle-timeout page.)\n- **Zero-dimension call surface = crash\u002Fblank.** A custom ongoing-call container with 0 width\u002Fheight throws or renders nothing (e.g. `h-full` on an auto-height flex parent). Give the call surface a content-INDEPENDENT height + width per the reflow-free-surface standard (`..\u002Fcometchat-react-v7-core\u002Freferences\u002Flayout.md`) — a pinned flex chain with `min-height:0`, or a `100dvw\u002F100dvh` overlay. Same class of bug as the chat-collapse\u002Freflow issue: the kit call UI fills its parent, so a content-driven box collapses or grows-into-place.\n- **`Container dimensions and number of tiles must be positive` on leave (non-fatal).** Even the kit's DEFAULT ongoing-call dialog logs this error from a Jitsi `ResizeObserver` as the dialog collapses to 0x0 while unmounting on \"Leave session\". It's a Calls-SDK teardown race — **non-fatal: the app recovers to the chat surface** — but it's an uncaught console error every hang-up, so a strict \"no console errors\" gate must allowlist\u002Ffilter it. Owner: uikit\u002Fcalls-sdk (not a host bug). Don't try to \"fix\" it host-side.\n- **HTTPS required.** `getUserMedia` (camera\u002Fmic) only works on `localhost` or HTTPS — calling silently no-ops on a plain-HTTP non-localhost origin.\n- **Next.js SSR.** Render CometChat (and especially the calls SDK) client-only: `\"use client\"` + `dynamic(() => import(...), { ssr: false })`. If the build then fails with `Can't resolve 'fs'` \u002F `'path'`, add a webpack fallback (`config.resolve.fallback = { fs: false, path: false }`) — a Calls-SDK-v5 bundling quirk. See `cometchat-react-v7-core\u002Freferences\u002Fssr.md`.\n- **Cleanup on custom surfaces.** On a hand-rolled ongoing-call screen, end the session and stop the tracks (`stream.getTracks().forEach(t => t.stop())`) on hangup\u002Funmount or the camera light stays on. The kit-driven `CometChatIncomingCall`\u002Fongoing flow handles this for you — prefer it unless the target docks or shrinks the call (then use the huddle recipe above, which cleans up on unmount).\n- **Mounting `\u003CCometChatOngoingCall>` in your own panel to dock it doesn't work** — it portals to a full-screen overlay (7.2.4). Use the huddle recipe above.\n- **Screen-share \u002F PiP \u002F device-select \u002F recording \u002F virtual-background are NOT on by default** — each is separate wiring on its own Calls-SDK page. Don't claim the default screen includes them.\n\n## Verify it works\nCalling is enabled (SDK installed + `uiKit:{callsSDK:{}}` on `initFromSettings` — the `initFromSettings` equivalent of the classic `setCallingEnabled(true)`); a 1:1 call from the header buttons RINGS the other user's root `CometChatIncomingCall`; accept connects audio+video in a non-collapsed surface; a group call posts a join message (no ring); hang-up releases the camera\u002Fmic (light off). If call buttons never appear: the SDK isn't installed or calling isn't enabled. If the call screen is blank: the container is zero-dimension or the origin isn't HTTPS.\n\n**Close (after it builds):** end with the shared 3-option selectable menu and WAIT for the pick — **(1) add another feature** (suggest a few not-yet-wired) · **(2) customize theming** · **(3) test it manually** (stop; let the user check). Same contract as the `cometchat-react-v7-core` close (`RULES.md` §19).\n","> **Ground truth:** calling is part of `@cometchat\u002Fchat-uikit-react@7` (the call UI ships INSIDE the kit — there is no separate calls-UI package), but the WebRTC engine is a SEPARATE, NOT-bundled dependency: `@cometchat\u002Fchat-uikit-react@^7` + `@cometchat\u002Fchat-sdk-javascript@^4.1.9` + **`@cometchat\u002Fcalls-sdk-javascript@^5`** (major 5 — distinct from the chat SDK's v4). Every `CometChat*` symbol below exists in the `web-v7` catalog. Exhaustive props + advanced call surfaces are FETCHED from the docs `.md` twins via `cometchat-react-v7-core\u002Freferences\u002Fdocs-map.md`. **APPEND to the user's app — additive wiring only** (`RULES.md`).\n\n## Companion skills (read first)\n- `cometchat-react-v7-core` — install, credentials, `init→login→render`, `CometChatProvider`. This skill ASSUMES it (calling is enabled on the SAME init).\n- `cometchat-react-v7-components` — the component catalog (message header the call buttons live in).\n- `cometchat-react-v7-features` — `voice-video-calls` is the feature entry; this skill is its operational depth.\n\n## Use this skill when\n\"add voice & video calls\", \"add calling to my chat\", \"enable video call\", \"add call buttons\", \"show call logs\". Precondition: chat is already integrated (core done) — calling rides on the same init\u002Flogin.\n\n## Prerequisites & install\nCore setup done (init + login + `CometChatProvider`). Calling adds ONE package:\n```bash\nnpm install @cometchat\u002Fcalls-sdk-javascript@5\n```\n> The Calls SDK is **major 5** (not the chat SDK's v4) and is **NOT bundled** by the UI Kit — if it isn't installed, every call affordance stays hidden. Do not \"upgrade\" `@cometchat\u002Fchat-sdk-javascript` to a non-existent v7; it stays `^4.1.9`.\n\n## Enable calling (BAKED — the two-part contract, the #1 gotcha)\nCalling is OFF until BOTH are true: the Calls SDK is installed (above) **and** calling is turned ON in the init settings. Since the core inits via **`CometChatUIKit.initFromSettings`** (telemetry — `RULES.md` §5 \u002F AUDIT-084), enable calling IN the SAME settings object with a truthy **`uiKit.callsSDK`**:\n```tsx\nimport { CometChatUIKit } from \"@cometchat\u002Fchat-uikit-react\";\n\nawait CometChatUIKit.initFromSettings({\n  appId: APP_ID, region: REGION,\n  credentials: { authKey: AUTH_KEY },                        \u002F\u002F dev-only; prod → server-minted auth token\n  chatSDK: { presenceSubscription: { type: \"ALL_USERS\" } },\n  uiKit: { callsSDK: {} },   \u002F\u002F ← turns calling ON (the initFromSettings equivalent of setCallingEnabled(true))\n});\n```\n> **How calling maps in the initFromSettings path (verified vs the compiled 7.1.x kit; DOCS-GAP).** The kit computes `callingEnabled = !!settings.uiKit.callsSDK`, so a **truthy `uiKit.callsSDK`** is the `initFromSettings` equivalent of `UIKitSettingsBuilder().setCallingEnabled(true)` — WITHOUT it, call buttons + incoming-call render nothing. The docs don't yet document this shape (owner: **docs** — confirm with a live call). The classic path still works — `CometChatUIKit.init(new UIKitSettingsBuilder()...setCallingEnabled(true).build())` — but it SKIPS the ai-agent telemetry attribution, so prefer `initFromSettings`.\n> No separate `CometChatUIKitCalls.init()` is needed for the default path. (`CometChatUIKitCalls` is the kit's namespace for advanced call-settings builders; reach for it only on a custom surface — FETCH then.) `initFromSettings` routes the Calls SDK internally — do NOT call raw `CometChat.init()`.\n\n## Wire the UI (BAKED — least-code path)\nTwo moves cover ringing end-to-end:\n1. **Mount `CometChatIncomingCall` ONCE at the app root** (above your routes). It self-manages visibility — renders nothing when idle, rings on an inbound call — so **no conditional wrapper**. It attaches its own SDK listeners.\n   ```tsx\n   import { CometChatIncomingCall } from \"@cometchat\u002Fchat-uikit-react\";\n   \u002F\u002F at the app shell, inside CometChatProvider, above the router:\n   \u003CCometChatIncomingCall \u002F>\n   ```\n2. **Let `CometChatMessageHeader` surface the call buttons.** When a `user`\u002F`group` is selected, the header composes voice\u002Fvideo call buttons itself once calling is enabled — you usually mount NOTHING extra. Only drop a standalone `CometChatCallButtons user={user}` (1:1) or `group={group}` on a CUSTOM header that has no `CometChatMessageHeader`. **Never mount both** — a standalone `CometChatCallButtons` next to a `CometChatMessageHeader` duplicates the buttons.\n\n**Call logs** (optional history surface): `CometChatCallLogs` on a sidebar or a `\u002Fcalls` route (`onItemClick`, `onCallButtonClicked`, `callLogRequestBuilder`). It does NOT auto-refresh on a new call — remount to refresh. **It shows the kit's \"OOPS! ...\" screen when call logs aren't available — and, verified live, it renders that from its OWN INTERNAL error state WITHOUT throwing, so a host `CometChatErrorBoundary`\u002F`fallbackView` CANNOT override it and `onError` may not fire. A fallbackView is NOT the fix (AUDIT-162).** **Enabling calling is NOT sufficient for `CometChatCallLogs`:** call-logs availability is a SEPARATE plan\u002Fcapability gate — an app can have calling ON (Voice\u002FVideo buttons render) yet `CometChatCallLogs` still \"OOPS\" because call-logs isn't in the plan. **The only reliable protection is to GATE the Calls tab on real call-logs AVAILABILITY** (a one-time SDK probe you own — `new CometChatCalls.CallLogRequestBuilder().setLimit(1).setAuthToken(await CometChatUIKitCalls.generateToken(...)).build().fetchNext()`, show the tab only on success — or your known plan), NOT merely on calling being enabled; default the tab OFF so a plain app never dead-ends. **Import discipline (AUDIT-204): `CallLogRequestBuilder` is NOT a named export of `@cometchat\u002Fcalls-sdk-javascript` — it is a STATIC on the `CometChatCalls` default-export class** (verified vs calls-sdk 5.0.5 `dist\u002Findex.d.ts`): use `import { CometChatCalls } from \"@cometchat\u002Fcalls-sdk-javascript\"; new CometChatCalls.CallLogRequestBuilder()...` — a `import { CallLogRequestBuilder } from \"@cometchat\u002Fcalls-sdk-javascript\"` FAILS (TS2614). `generateToken` is on `CometChatUIKitCalls` (from `@cometchat\u002Fchat-uikit-react`). (Gated Calls-tab recipe with the full probe + imports: `..\u002Fcometchat-react-v7-placement\u002Freferences\u002Frecipes.md` Combined-app recipe.)\n\n### 1:1 rings, groups JOIN (BAKED — no group-ring)\n- **1:1:** call buttons → the kit initiates a call → the recipient's root `CometChatIncomingCall` RINGS → accept → the ongoing-call surface. The kit drives outgoing → ongoing for you.\n- **Group:** call buttons on a group send a **group-call meeting message** into the chat; members **JOIN** from that message. There is **no \"ring the whole group\" primitive** — surface the join affordance, never wait for a ring.\n\n### Huddle \u002F docked call — React how-to (BAKED — verified vs 7.2.4)\nThe pattern (start → join → dock → indicator → leave) and when to use it are platform-independent: `cometchat-onboarding\u002Freferences\u002Fclone-mode.md` → *Huddle \u002F drop-in room pattern*. This is the React code for it.\n- **`\u003CCometChatOngoingCall>` can't be docked:** it portals into the kit's full-screen `.cometchat-call-overlay` wherever you mount it. **`useOngoingCall().startCall(el)` is broken in 7.2.4:** it calls `generateToken` without the auth token and its error handler throws, so nothing renders. Join the session into your own element the way `CometChatOngoingCall` does internally:\n```tsx\nimport { useEffect, useRef } from \"react\";\nimport { CometChat, type Group } from \"@cometchat\u002Fchat-sdk-javascript\";\nimport { CometChatCallBubble, CometChatUIKit, CometChatUIKitCalls, type CometChatMessagePlugin } from \"@cometchat\u002Fchat-uikit-react\";\n\n\u002F\u002F Start: the same \"meeting\" message the kit's group voice button sends (Join bubble + conversation\n\u002F\u002F preview). Session id = group GUID, so the whole channel shares one room.\nexport async function startHuddle(group: Group): Promise\u003Cstring> {\n  const sessionId = group.getGuid();\n  const msg = new CometChat.CustomMessage(sessionId, CometChat.RECEIVER_TYPE.GROUP, \"meeting\", { sessionID: sessionId, sessionId, callType: \"audio\" });\n  msg.shouldUpdateConversation(true);\n  await CometChat.sendCustomMessage(msg);\n  return sessionId;\n}\n\n\u002F\u002F Join: replace the kit's meeting bubble so \"Join\" opens YOUR dock, not the full-screen dialog.\nexport const huddlePlugin = (onJoin: (sessionId: string) => void): CometChatMessagePlugin => ({\n  id: \"huddle-meeting\", messageTypes: [\"meeting\"], messageCategories: [\"custom\"],\n  renderBubble: (message) => \u003CCometChatCallBubble message={message} onJoinClick={onJoin} \u002F>,\n  getOptions: () => [],\n});\n\n\u002F\u002F The dock: a fixed-size element you own; the Calls SDK renders tiles + controls into it.\nexport function HuddleDock({ sessionId, onLeave }: { sessionId: string; onLeave: () => void }) {\n  const frame = useRef\u003CHTMLDivElement>(null);\n  const onLeaveRef = useRef(onLeave);\n  onLeaveRef.current = onLeave;\n  useEffect(() => {\n    const calls = CometChatUIKitCalls;\n    const user = CometChatUIKit.getLoggedInUser();\n    if (!calls || !user || !frame.current) return;\n    const el = frame.current;\n    let left = false;\n    const leave = () => { if (left) return; left = true; calls.leaveSession(); onLeaveRef.current(); };\n    const unsubs = [calls.addEventListener(\"onSessionLeft\", leave), calls.addEventListener(\"onLeaveSessionButtonClicked\", leave)];\n    calls.generateToken(sessionId, user.getAuthToken()).then((res: { token: string }) =>\n      calls.joinSession(res.token, { sessionType: \"VOICE\", layout: \"TILE\", hideChangeLayoutButton: true, hideVirtualBackgroundButton: true }, el));\n    return () => { unsubs.forEach((u: () => void) => u()); if (!left) { left = true; calls.leaveSession(); } };\n  }, [sessionId]);\n  return \u003Cdiv ref={frame} className=\"cc-huddle-dock\" style={{ width: 320, height: 220, position: \"relative\" }} \u002F>;\n}\n```\n- **Wire it:** a \"Huddle\" button in the channel header (`CometChatMessageHeader` with `hideVoiceCallButton`, your button in `auxiliaryButtonView`) calls `startHuddle` and mounts `\u003CHuddleDock>` where the target shows it (Slack: bottom of the sidebar). Pass `plugins={[huddlePlugin(openDock), ...]}` to `CometChatProvider`, defined once and not per render. Keep the dock mounted while the user moves between channels; unmounting leaves the call.\n- **Size:** the dock needs a fixed width and height (zero-dimension pitfall below). Expanding it into a window is just a bigger container.\n- **Indicator:** from the latest `meeting` message; participant events → FETCH from the Calls SDK docs.\n- **Verified 2026-10-15 (7.2.4, calls-sdk 5.0.7):** strict `tsc` clean; in Chromium the session joined inside a 320x220 dock with no overlay, and Join from the replaced bubble opened the same dock. Not yet run with two users at once: do the two-user check before calling it done.\n\n## Custom call surfaces (FETCH — don't bake)\nFor anything past the default ring\u002Fjoin — a custom ongoing-call screen (`CometChatOngoingCall`), a bespoke call manager (`CometChatOutgoingCall`), idle-timeout tuning, screen-share, picture-in-picture, device selection, recording, virtual background — FETCH the exact props\u002FAPI from the docs `.md` twins via `cometchat-react-v7-core\u002Freferences\u002Fdocs-map.md` (rows: `call-buttons` · `incoming-call` · `outgoing-call` · `call-logs` · `calls-setup` · `calls-react-integration` · the per-feature Calls-SDK pages). These props change; never emit them from memory or read `.d.ts`.\n\n## Common pitfalls (BAKED — these bite in production)\n- **Idle-timeout is in MILLISECONDS.** `180` = 180 ms, so the \"are you still there?\" prompt fires and exits instantly. Use `180_000` for 3 minutes. (Exact v7 field\u002Fclamp → FETCH the idle-timeout page.)\n- **Zero-dimension call surface = crash\u002Fblank.** A custom ongoing-call container with 0 width\u002Fheight throws or renders nothing (e.g. `h-full` on an auto-height flex parent). Give the call surface a content-INDEPENDENT height + width per the reflow-free-surface standard (`..\u002Fcometchat-react-v7-core\u002Freferences\u002Flayout.md`) — a pinned flex chain with `min-height:0`, or a `100dvw\u002F100dvh` overlay. Same class of bug as the chat-collapse\u002Freflow issue: the kit call UI fills its parent, so a content-driven box collapses or grows-into-place.\n- **`Container dimensions and number of tiles must be positive` on leave (non-fatal).** Even the kit's DEFAULT ongoing-call dialog logs this error from a Jitsi `ResizeObserver` as the dialog collapses to 0x0 while unmounting on \"Leave session\". It's a Calls-SDK teardown race — **non-fatal: the app recovers to the chat surface** — but it's an uncaught console error every hang-up, so a strict \"no console errors\" gate must allowlist\u002Ffilter it. Owner: uikit\u002Fcalls-sdk (not a host bug). Don't try to \"fix\" it host-side.\n- **HTTPS required.** `getUserMedia` (camera\u002Fmic) only works on `localhost` or HTTPS — calling silently no-ops on a plain-HTTP non-localhost origin.\n- **Next.js SSR.** Render CometChat (and especially the calls SDK) client-only: `\"use client\"` + `dynamic(() => import(...), { ssr: false })`. If the build then fails with `Can't resolve 'fs'` \u002F `'path'`, add a webpack fallback (`config.resolve.fallback = { fs: false, path: false }`) — a Calls-SDK-v5 bundling quirk. See `cometchat-react-v7-core\u002Freferences\u002Fssr.md`.\n- **Cleanup on custom surfaces.** On a hand-rolled ongoing-call screen, end the session and stop the tracks (`stream.getTracks().forEach(t => t.stop())`) on hangup\u002Funmount or the camera light stays on. The kit-driven `CometChatIncomingCall`\u002Fongoing flow handles this for you — prefer it unless the target docks or shrinks the call (then use the huddle recipe above, which cleans up on unmount).\n- **Mounting `\u003CCometChatOngoingCall>` in your own panel to dock it doesn't work** — it portals to a full-screen overlay (7.2.4). Use the huddle recipe above.\n- **Screen-share \u002F PiP \u002F device-select \u002F recording \u002F virtual-background are NOT on by default** — each is separate wiring on its own Calls-SDK page. Don't claim the default screen includes them.\n\n## Verify it works\nCalling is enabled (SDK installed + `uiKit:{callsSDK:{}}` on `initFromSettings` — the `initFromSettings` equivalent of the classic `setCallingEnabled(true)`); a 1:1 call from the header buttons RINGS the other user's root `CometChatIncomingCall`; accept connects audio+video in a non-collapsed surface; a group call posts a join message (no ring); hang-up releases the camera\u002Fmic (light off). If call buttons never appear: the SDK isn't installed or calling isn't enabled. If the call screen is blank: the container is zero-dimension or the origin isn't HTTPS.\n\n**Close (after it builds):** end with the shared 3-option selectable menu and WAIT for the pick — **(1) add another feature** (suggest a few not-yet-wired) · **(2) customize theming** · **(3) test it manually** (stop; let the user check). Same contract as the `cometchat-react-v7-core` close (`RULES.md` §19).\n",{"name":5,"license":15,"metadata":54,"description":7,"compatibility":58},{"tags":55,"author":56,"version":57},"cometchat react calls voice video calling webrtc v7 uikit","CometChat","1.0.0","React >=18 \u003C21; @cometchat\u002Fchat-uikit-react ^7 (7.2.x, verified 7.2.4); @cometchat\u002Fchat-sdk-javascript ^4.1.9; @cometchat\u002Fcalls-sdk-javascript ^5 (5.0.0 — separate, NOT bundled)","main","cebc3dce495d7eb80d3b1d5841f877a4a92e162d","plugins\u002Fcometchat\u002Fcometchat-skills\u002Fskills\u002Fcometchat-react-v7-calls",491,"https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins","https:\u002F\u002Fhol.org\u002Fplugins\u002Fbest-codex-plugins","https:\u002F\u002Fgithub.com\u002Fhashgraph-online","https:\u002F\u002Fhol.org","Open-source standards and developer tools for interoperable AI agents: discovery, identity, communication, payments, privacy and security.","2026-11-14 14:06:13",{"source":8,"skillName":5,"commands":70,"prompt":80,"zipAvailable":81},[71,74,77],{"tool":72,"command":73},"npx","npx skills add hashgraph-online\u002Fawesome-codex-plugins --skill cometchat-react-v7-calls",{"tool":75,"command":76},"bunx","bunx skills add hashgraph-online\u002Fawesome-codex-plugins --skill cometchat-react-v7-calls",{"tool":78,"command":79},"pnpm","pnpm dlx skills add hashgraph-online\u002Fawesome-codex-plugins --skill cometchat-react-v7-calls","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fcometchat\u002Fcometchat-skills\u002Fskills\u002Fcometchat-react-v7-calls\u002FSKILL.md",true,[],[84,93,100,107,134,158],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":92},"01M746ZWACVKMZ1BJXJZHYJSSX","migrate-to-e2e","Moves an existing Playwright, Cypress, Selenium, Appium, Detox, Maestro, or TesterArmy suite to e2e one slice at a time, with the old suite running beside the new one until each ported test is proven. Keeps a MIGRATION.md progress file so any later session resumes where the last stopped. Optionally plants a defect to show both suites catch it. Use for \u002Fmigrate-to-e2e, \"migrate our tests to e2e\", \"port these Maestro flows\", \"move our TesterArmy tests\", or when a project has an e2e config and an older suite still running.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fmigrate-to-e2e\u002FSKILL.md",[],[],"2026-11-11 15:11:14",{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":92},"01M746ZPTAX1Q0FBKAB1CYND7E","e2e","Writes, runs, and debugs end-to-end tests for web and mobile apps with e2e, the agentic test runner, which mixes agent.act goals with exact locator assertions on a Playwright browser or an iOS or Android device. Also drives a project's running app live over the e2e MCP server to look at a screen, check a UI change, take a screenshot or record a video, and try a locator; and runs bug bashes, parallel e2e explore runs with each bug proven by a repro test. Use when a project depends on e2e or has an e2e.config.ts, when asked for end-to-end, browser, mobile, or agentic UI tests, to check a change in the running app, to QA or bug bash an app or branch, or when an e2e run fails.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fe2e\u002FSKILL.md",[],[],{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":106,"publishedAt":92},"01M746ZJN784HVHMDP9MQGCSGG","create-verification-skill","Generate a project-local verify-\u003Capp> skill on top of e2e, so any coding agent can launch the app, check it is worth driving, exercise a feature the way a user does, keep evidence, and bug bash it. Writes the skill, a feature map with one explore charter per feature, and proves it once. Use for \u002Fcreate-verification-skill, \"make a verification skill for this repo\", or when a project has no scripted way to prove UI behavior.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fcreate-verification-skill\u002FSKILL.md",[],[],{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":113,"publishedAt":49},"01M716A6ETNB9B1SQ3GXZC75VR","deja-search","deja-vu memory — search the user's past AI coding sessions with the deja CLI. Holds what they already settled and never wrote into the repo (naming rules, banned or already-tried tools, required checks before merge, preferred values, who owns what). Use before your first edit in a task, before you call a change done or ready to merge, before you recommend a tool, library, design or reviewer, before re-debugging an error that may already be solved, when they say 'didn't we fix this before', 'what did we decide about X' or 'I already have that', and before telling them something on this machine does not exist.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fvshulcz\u002Fdeja-vu\u002Fskills\u002Fdeja-search\u002FSKILL.md",[],[114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133],"search","deja search","never","error","ready","memory","re","decide","fix","design","debugging","when","you","done","reviewer","io","change","cli","merge","past",{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":49},"01M716A3J2W5SENSE9VGMXB375","deja-reflect","End-of-session reflection and memory curation with deja-vu. Review what was decided, what commands broke and what repaired them, and record durable project rules or decisions with deja remember so the next session starts with them.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fvshulcz\u002Fdeja-vu\u002Fskills\u002Fdeja-reflect\u002FSKILL.md",[],[141,142,143,129,144,145,146,40,147,148,149,150,151,152,119,153,154,155,156,157],"deja reflect","reflect","review","with","record","github","https","node","Rules","decisions","brew","remember","project rules","project","session","end","reflection",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":88,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":171,"publishedAt":49},"01M715T3RR4BAYSR57HPPTJ0PF","revid","Make, edit, export and post AI videos with Revid (revid.ai) through the Revid MCP server. Use whenever the user wants a short-form or long-form video made (from an idea, a script, a URL, an article, a PDF, a tweet, a product page, their own clips, or a song), wants to find what is going viral on TikTok, YouTube, Instagram or X before making something, wants to edit or re-export an existing Revid project, generate a standalone AI image or clip, make a series episode, or publish and schedule a finished video to connected social accounts. Covers setup (hosted MCP with OAuth, or an API key), the 36 tools, credits, polling and the rules for publishing. Routes to the more specific revid-* skills when one fits.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftmakersas\u002Frevid-skills\u002Fskills\u002Frevid\u002FSKILL.md",[164,165,168],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":169,"categoryName":170,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[160,172,48,173,174,175,176,154,177,120,178,179,180,181,125,182,183,184,185,186],"URL","short-form","standalone","PDF","viral","tiktok","short","social","MCP","song","image","mcp server","schedule","api","publish"]