diff --git a/skills/stream-react/RULES.md b/skills/stream-react/RULES.md index 6581e7f..5c00a1a 100644 --- a/skills/stream-react/RULES.md +++ b/skills/stream-react/RULES.md @@ -59,6 +59,8 @@ Shadcn components use `@base-ui/react`, NOT `@radix-ui`. Key differences: Use whatever theme Shadcn generates. Do not modify `globals.css` after init - no dark mode overrides, no custom variable blocks. The scaffold includes `next-themes` with a `ThemeProvider` (system default, class-based toggle) - use it as-is. +**Design-matching exception:** when a screenshot / Figma dictates the palette, match it through the sanctioned channels - the shadcn `--preset` and Stream's documented `str-chat` theming variables / `` theming - not ad-hoc `globals.css` edits. See [`references/design-matching.md`](references/design-matching.md) > Palette through the sanctioned channels. + ## Reference authority **Prebuilt-component-first.** Stream's React SDKs ship rich prebuilt components, and the React @@ -70,9 +72,14 @@ docs/cookbooks are built around customizing them. Default to that path: **Customize via the documented hooks/props** (e.g. ``, `useChannelStateContext()`, `useMessageContext()`, `useCallStateHooks()`, `` theming) - -fetch the matching docs page first (see Docs-first below). **Hand-build raw markup ONLY when the -user explicitly asks for fully bespoke UI**; in that case use the `references/*-blueprints.md` -"Fully custom UI" fallback section (raw element structure + wiring tables) - still docs-first. +fetch the matching docs page first (see Docs-first below). + +**Writing your OWN component for a prebuilt region triggers [`references/custom-ui.md`](references/custom-ui.md) - load it BEFORE you build.** This is the predicate, and it is easy to get wrong: + +- **Passing props / theme** to a prebuilt component (a cookbook recipe's prop, `` theme tokens) -> just fetch the page. +- **Writing your own component/markup for a region** - a custom message row / `MessageUI`, a custom composer, a custom channel preview or header, a custom call layout - **load `custom-ui.md` first, then fetch the page.** This holds **even when you wire it via the documented `Message=` / `WithComponents` prop**, and it is **not** limited to a "fully bespoke app". + +Why: replacing a prebuilt region means you inherit every sub-feature it rendered. `custom-ui.md` carries the **completion contract** (reproduce-or-mark-`N/A`: attachments, reactions, quoted replies, receipts, threads, edited/deleted, grouping) that stops a custom row from silently dropping them. Still docs-first either way. **Reference files are the source of truth for the components they document** - which prebuilt component to use, its props, SDK wiring, and property paths. Do not generate Stream SDK code from @@ -86,11 +93,11 @@ and memory - fetch it. ## Docs-first for cookbook / advanced features -**Before implementing any feature that matches a UI component, UI Cookbook, or Advanced Guide topic, fetch the matching Stream docs page first.** [`references/DOCS.md`](references/DOCS.md) holds the keyword -> page map and the protocol. The bundled `references/*-blueprints.md` cover the common path; component-reference and cookbook/advanced topics - the prebuilt component props, typing indicator, custom message UI, message actions, reactions, message composer / input UI, channel header, emoji picker, autocomplete, link previews, AI integrations, advanced search, multiple lists, infinite scroll, channel read state, online status, location sharing, blocking users, message reminders, notifications, attachment previews, audio playback, date/time formatting, SDK state management, dialog management, TypeScript custom data types, chat + video integration, call layouts, PiP, network quality, livestream watching, recording, broadcasting - change often and **must** be built from the current page, not memory. +**Before implementing any feature that matches a UI component, UI Cookbook, or Advanced Guide topic, fetch the matching Stream docs page first.** [`references/docs-map.md`](references/docs-map.md) holds the keyword -> page map and the protocol. The bundled `references/*-blueprints.md` cover the common path; component-reference and cookbook/advanced topics - the prebuilt component props, typing indicator, custom message UI, message actions, reactions, message composer / input UI, channel header, emoji picker, autocomplete, link previews, AI integrations, advanced search, multiple lists, infinite scroll, channel read state, online status, location sharing, blocking users, message reminders, notifications, attachment previews, audio playback, date/time formatting, SDK state management, dialog management, TypeScript custom data types, chat + video integration, call layouts, PiP, network quality, livestream watching, recording, broadcasting - change often and **must** be built from the current page, not memory. -**This rule also governs migration (Track M).** Never apply an SDK upgrade from memory - fetch the matching release / upgrade guide from the index in [`references/DOCS.md`](references/DOCS.md) (or [`migrate.md`](migrate.md)) and apply *that*. +**This rule also governs migration (Track M).** Never apply an SDK upgrade from memory - fetch the matching release / upgrade guide from the index in [`references/docs-map.md`](references/docs-map.md) (or [`migrate.md`](migrate.md)) and apply *that*. -**Flow:** match a trigger -> `WebFetch` the `.md` URL from [`references/DOCS.md`](references/DOCS.md) -> read it this turn -> implement to match. +**Flow:** match a trigger -> `WebFetch` the `.md` URL from [`references/docs-map.md`](references/docs-map.md) -> read it this turn -> implement to match. **Hard gate on fetch failure.** If the page does not load, hand the lookup to the `stream-docs` skill. If **neither** the page nor `stream-docs` resolves the API, **stop and tell the user** - report that you could not confirm the current API and ask how to proceed. **Do not implement the feature from memory.** A guess that happens to compile is still a guess. @@ -126,7 +133,7 @@ The generic onboarding + phase-order discipline lives in [`../stream/RULES.md`]( - Do not load `references/*.md` until the user names the product(s). - Do not load [`builder-ui.md`](builder-ui.md) before Step 4. -- Shadcn/ui is always installed during Step 3 - never skip. Third-party **frontend skills** (`vercel-labs/*`, `anthropics/*`) require one explicit user confirmation per session before install - see [`SKILL.md`](SKILL.md) Task A.2. +- Shadcn/ui is always installed during Step 3 - never skip. **stream-react does not install third-party frontend skills** (no install step). If such packs (`frontend-design`, `vercel-react-best-practices`, `web-design-guidelines`) are already present in the session, use them for generic UI polish only - Stream references stay authoritative for SDK wiring. (The consent-to-install rule in [`../stream/RULES.md`](../stream/RULES.md) > Builder phase order is for `stream-builder`, which retains that step; this file wins for React.) ## Moderation is Dashboard-only diff --git a/skills/stream-react/SKILL.md b/skills/stream-react/SKILL.md index b822c98..cadbb60 100644 --- a/skills/stream-react/SKILL.md +++ b/skills/stream-react/SKILL.md @@ -11,7 +11,7 @@ allowed-tools: >- Bash(getstream *), Bash(npx *), Bash(npm install *), Bash(npm run *), Bash(yarn *), Bash(pnpm *), - Bash(node -e *), Bash(node --version), Bash(openssl rand *), + Bash(node -e *), Bash(node --version), Bash(node .design-verify/*), Bash(openssl rand *), Bash(mv .scaffold*), Bash(rm -rf .scaffold), Bash(ls *), Bash(test *), Bash(grep *), @@ -31,7 +31,7 @@ This skill builds, enhances, audits, and migrates Stream Chat, Video, and Feeds - **Track A - Scaffold a new app:** Steps 0-7 below. Use when the cwd is empty / new and the user said "build me a ... app". - **Track E - Enhance an existing app:** see [`enhance.md`](enhance.md). Skips scaffold + theme; reuses the same SDK wiring and component blueprints. -- **Track F - Audit an existing integration (read-only):** for "audit/review my video integration", "check my app against best practices", "is my video app production-ready?", "what am I missing before launch?". **Video only:** load the **Integration best-practices audit** section in [`references/VIDEO.md`](references/VIDEO.md) and follow its protocol - it has a Video-specific checklist + output contract. **If the user asks to audit Chat or Feeds**, say up front there is no dedicated best-practices checklist for those yet, then do a general docs-based review (fetch the relevant pages from [`references/DOCS.md`](references/DOCS.md) and check the app against them) rather than applying the Video checklist. **Skip onboarding, auth, the CLI, and all build steps** - this track only reads the app and reports findings. Fix issues only if the user then asks. +- **Track F - Audit an existing integration (read-only):** for "audit/review my video integration", "check my app against best practices", "is my video app production-ready?", "what am I missing before launch?". **Video only:** load the **Integration best-practices audit** section in [`references/VIDEO.md`](references/VIDEO.md) and follow its protocol - it has a Video-specific checklist + output contract. **If the user asks to audit Chat or Feeds**, say up front there is no dedicated best-practices checklist for those yet, then do a general docs-based review (fetch the relevant pages from [`references/docs-map.md`](references/docs-map.md) and check the app against them) rather than applying the Video checklist. **Skip onboarding, auth, the CLI, and all build steps** - this track only reads the app and reports findings. Fix issues only if the user then asks. - **Track M - Migrate / upgrade an SDK version:** see [`migrate.md`](migrate.md). For "upgrade stream-chat-react to v14", "migrate to the new SDK", "bump my Stream version". Docs-driven: detect the installed version, fetch the matching release guide, apply it. Never migrate from memory. ### Flow dispatch - choose exactly one @@ -41,18 +41,20 @@ This skill builds, enhances, audits, and migrates Stream Chat, Video, and Feeds - **Track F:** skip onboarding and go directly to the audit in [`references/VIDEO.md`](references/VIDEO.md). **Do not enter Start or any build step.** - **Track M:** skip onboarding and Read [`migrate.md`](migrate.md) first; it fetches the live release guide before any edit. **Do not enter Start or any scaffold task.** +**Styling-depth flag (orthogonal to the track).** If the request carries a **target appearance** - an attached screenshot, a Figma frame, or "make it look like WhatsApp / Slack / " - route through [`references/design-matching.md`](references/design-matching.md): a reference design is a **checklist of regions, not a color tweak**. Its pipeline is **Classify -> Spec -> Route -> Ground -> Build -> Verify**: classify the fidelity tier + viewport, write a per-screen spec that **names the Stream concept behind every visual signal** via per-product identification checklists (chat / video / feeds), route each region to a component + mechanism (theming / injection -> the [`references/custom-ui.md`](references/custom-ui.md) completion contract / bespoke), ground the names against the live docs, build batched, and close with **an empirical verify loop - screenshot + computed-style checks via session browser tooling or a Playwright fallback - iterated until the spec table passes**. This composes with the track: **Track A** scaffolds first, then matches before Step 4's build; **Track E** matches within E3. Load it **before** writing UI. + --- ## Docs-first triggers (consult docs before building) -**For any feature that matches a UI component, cookbook, or advanced-guide topic, fetch the matching Stream docs page BEFORE writing code.** The live docs are the source of truth for the current API and the recommended pattern; the bundled `references/*-blueprints.md` cover the common path only. Full keyword -> page map with exact URLs: [`references/DOCS.md`](references/DOCS.md). Enforced by [`RULES.md`](RULES.md) > Docs-first for cookbook / advanced features. +**For any feature that matches a UI component, cookbook, or advanced-guide topic, fetch the matching Stream docs page BEFORE writing code.** The live docs are the source of truth for the current API and the recommended pattern; the bundled `references/*-blueprints.md` cover the prebuilt common path only. Full keyword -> page map with exact URLs: [`references/docs-map.md`](references/docs-map.md) - it opens with the **docs convention** (the `.md`-twin rule + per-product live index) so any unmapped page is still reachable: never guess a path, fetch the index. Enforced by [`RULES.md`](RULES.md) > Docs-first for cookbook / advanced features. This skill is **prebuilt-component-first**: build the common path with the SDK's prebuilt React components and customize via the documented hooks/props - see [`RULES.md`](RULES.md) > Reference authority. The docs-first protocol covers both the **component reference pages** and the **cookbook / advanced** recipes: - **UI Cookbook (customization / theming):** typing indicator, custom message UI, message actions, reactions customization, message composer / input UI, channel header, channel list preview, emoji picker, autocomplete / suggestion list, link previews, pin indicator, thread header, search, collapsible sidebar, system message / banner, mentions actions, attachment actions, hide channel history, localization / i18n; Video: replacing call controls, custom layouts, lobby preview, PiP, network quality, livestream watching, ringing. - **Advanced Guides:** AI integrations (LangChain, AI SDK), advanced search, multiple lists, infinite scroll, read state, online status, location sharing, blocking, message reminders, notifications / web push, attachment previews, audio playback, date formatting, SDK state management, dialog management, TypeScript custom data, chat + video integration, recording, broadcasting, video filters. -When a request hits one of these: **match -> `WebFetch` the page's `.md` URL from [`references/DOCS.md`](references/DOCS.md) -> implement to match.** On fetch failure, hand to the `stream-docs` skill; if neither resolves the API, **stop and ask the user** - never build from memory. +When a request hits one of these: **match -> `WebFetch` the page's `.md` URL from [`references/docs-map.md`](references/docs-map.md) -> implement to match.** On fetch failure, hand to the `stream-docs` skill; if neither resolves the API, **stop and ask the user** - never build from memory. --- @@ -60,7 +62,7 @@ When a request hits one of these: **match -> `WebFetch` the page's `.md` URL fro > **Track A only.** Tracks E, F, and M branch in **Flow dispatch** above and never enter this section. -Once `getstream init` has onboarded (authenticated + selected/created org + app + written credentials), announce the network plan once, then **immediately start executing Steps 0-7** - do not ask permission to begin (the user has authorized the build by asking for it). The only pauses for input are the theme + app pick (Step 1b) and the skill-pack consent (Task A.2). +Once `getstream init` has onboarded (authenticated + selected/created org + app + written credentials), announce the network plan once, then **immediately start executing Steps 0-7** - do not ask permission to begin (the user has authorized the build by asking for it). The only pause for input is the theme + app pick (Step 1b). ### Trust readout (announce, then continue on the same turn - do not wait) @@ -71,11 +73,11 @@ Before the first network command, print this verbatim to the user, then proceed > - `npm install --legacy-peer-deps` - Stream SDKs from npm (`stream-chat-react`, `@stream-io/video-react-sdk`, etc.). > - `getstream env` - local CLI, no network; writes `.env.local` (gitignored by the Next.js scaffold's default; Task B verifies). > -> Interrupt me at any point if something looks wrong. I'll pause twice for your input: the theme + Stream-app pick (Step 1b) and the optional third-party skill packs (Task A.2). +> Interrupt me at any point if something looks wrong. I'll pause once for your input: the theme + Stream-app pick (Step 1b). Full per-command audit (publisher, why unpinned, what each writes): section Install trust & integrity below. The user's continued silence after the readout is implicit consent for this scaffold; an objection or stop instruction aborts the run. -Shadcn/ui is always installed during Step 3. Third-party **frontend skills** (`vercel-react-best-practices`, `web-design-guidelines`, `frontend-design`) are installed **only with explicit user consent** - see Task A.2 for the disclosure script. If the user declines, Step 4 proceeds using Stream references only. **Precedence (when the skills are present):** Stream references win for SDK wiring; frontend skills guide generic React / UI polish. +Shadcn/ui is always installed during Step 3. **stream-react does not install third-party frontend skills** - the build uses Stream references + Shadcn. **If** frontend skill packs (`frontend-design`, `vercel-react-best-practices`, `web-design-guidelines`) are already available in the session, use them for generic React / UI polish only; Stream references remain authoritative for SDK wiring. --- @@ -88,15 +90,15 @@ This builder runs three classes of network-touching commands. Each is listed her | `npx shadcn@latest init ...` (Task A) | Vercel - [`shadcn-ui/ui`](https://github.com/shadcn-ui/ui) | Scaffolder; `@latest` is the maintainer's documented usage. Pinning ships outdated scaffolds. | Project files in cwd. Next.js scaffold's `.gitignore` ignores `.env*` by default. | | `npx shadcn@latest add ...` (Task A.1) | Vercel - same source as above | Same scaffolder; component sync depends on registry parity. | Component files under `components/ui/`. | | `npm install --legacy-peer-deps` (Task C) | GetStream (npm) for `@stream-io/*` and `stream-chat-react`; transitive deps via standard npm trust | Latest published versions of GetStream's own SDKs - same trust model as the CLI itself. | Modules under `node_modules/`. Runtime SDKs + transitive deps. | -| `npx skills add ` (Task A.2) | `vercel-labs/agent-skills` and `anthropics/skills` | Optional. Markdown-only skill packs; `npx skills add` is the published install path. | Markdown files in the user's skills directory. **Gated by explicit user consent in Task A.2** - never runs without an affirmative answer. | | `getstream env` (Task B) | GetStream - install instructions in the root skill's "Stream CLI" section in [`../stream/SKILL.md`](../stream/SKILL.md) | n/a (local CLI, no network at this step) | `.env.local` in the project root with `NEXT_PUBLIC_STREAM_API_KEY` + `STREAM_API_SECRET`. Task B verifies `.gitignore` covers `.env*` before writing (Next.js scaffold's default already does). The agent never reads `.env.local` (RULES.md > Secrets). | +| Playwright into a self-contained `.design-verify/` harness (`npm install --prefix .design-verify -D playwright` + browser install) - design-matching verify-loop fallback only | Microsoft - [`microsoft/playwright`](https://github.com/microsoft/playwright) | Latest published; runs **only** when a design match needs a capture and no in-session browser tooling exists (may never run), announced inline at point of use. | Everything under `.design-verify/` (its own `package.json` + `node_modules`, gitignored) - **the app's `package.json` / lockfile are untouched** - plus ~120MB Chromium in the shared Playwright cache. Deleted wholesale at loop exit; nothing lands in the app manifest. | **Reviewer checklist:** - All `npx` invocations resolve to the publishers listed above; substitute a different publisher and the install fails. -- `npx skills add` runs **only after** the disclosure prompt in Task A.2 and an explicit user "yes." - `.env.local` is written by the Stream CLI directly, not by the agent, and is not transmitted into the conversation. - If the user wants to pin a specific shadcn version, replace `@latest` with `@` in Tasks A and A.1. +- The Playwright row is a **design-matching verify-loop fallback**, not part of the scaffold: it is announced inline if and when it runs (it may never run), installs only into a self-contained `.design-verify/` harness (never the app root), and does **not** appear in the Start trust readout above. --- @@ -104,25 +106,26 @@ This builder runs three classes of network-touching commands. Each is listed her Execute phases **in order** (later steps depend on earlier ones). Do **not** run independent phases in parallel. Shell discipline (one `bash -c` per phase, no `bash -ce`, `getstream login` standalone) lives in [`../stream/RULES.md`](../stream/RULES.md) > Shell discipline. -**Two-call exception:** If you must Read JSON (e.g. `OrganizationRead`) and then choose IDs, use one call for the read, one batched call for all creates. +**Two-call exception:** If you must Read JSON from a `getstream api` call and then choose IDs, use one call for the read, one batched call for all creates. ### Step 0: Package manager Always use `npm`. Never use bun. ([`RULES.md`](RULES.md) > Package manager.) ### Step 1: Auth -Run the **Provisioning > Step 1: Auth** flow in [`builder.md`](builder.md) (auth probe via `getstream api OrganizationRead`; `getstream login` as its own invocation on exit 2; hang recovery). On **exit 0**, continue to Step 1b. +Authentication is handled by `getstream init` (Step 2) - it opens the browser as its **own invocation** if you're not signed in ([`builder.md`](builder.md) > Provisioning; [`../stream/RULES.md`](../stream/RULES.md) > Shell discipline). There is **no separate `getstream api` auth probe** - CLI v1.0.0 removed the pre-1.0 `OrganizationRead` probe, and `getstream api` subcommands are now product-namespaced (e.g. `getstream api chat ...`). Continue to Step 1b. ### Step 1b: Theme + app pick -Ask both setup questions in **one message** before doing anything else - a single pause, the same "ask exactly once, then act" pattern the other platform packs use for credentials. Build the app options from what is already in context: the configured org/app from `getstream init` and the org list from Step 1's `OrganizationRead` output. +Ask both setup questions in **one message** before doing anything else - a single pause, the same "ask exactly once, then act" pattern the other platform packs use for credentials. Build the app options from what is already in context: the org/app already configured in this project by a prior `getstream init`, if any. If none is configured yet, `getstream init` lists your orgs/apps interactively when it runs (Step 2) - don't try to enumerate them with a raw `getstream api` call. > **Quick setup - two questions:** -> 1. **Theme:** I can use a random shadcn theme, or you can design your own at [ui.shadcn.com/create](https://ui.shadcn.com/create) and share the `--preset` value (e.g. `--preset b1Gdi7z7r`). Random, or do you have a preset? -> 2. **Stream app:** *(an app is configured)* Use the currently configured app **``** (default), pick another existing org/app, or create a fresh one? / *(no app configured)* You have these orgs: ``. Pick one to use - I'll list its apps - or create a fresh org + app? +> 1. **Theme:** I can use a random shadcn theme, or you can design your own at [ui.shadcn.com/create](https://ui.shadcn.com/create) and share the `--preset` value (e.g. `--preset b1Gdi7z7r`). Random, or do you have a preset? *(If you already shared a screenshot or Figma, I'll match that instead - skip this.)* +> 2. **Stream app:** *(an app is already configured)* Use the currently configured app **``** (default), or pick/create a different one? / *(no app configured yet)* `getstream init` will list your orgs and apps when it runs - use an existing one, or create a fresh org + app? **STOP here and wait for the user's answer.** Do not continue with any other step until the user responds. Asking a question and continuing to work in parallel is confusing - the user misses the question as output scrolls past. - **Theme - preset provided** -> store it for Task A scaffold command. **Random / doesn't care** -> pick a random preset from `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`. +- **Theme - a reference design (screenshot / Figma) was provided** -> the design dictates the theme: **skip question 1**, pick the shadcn preset closest to the sampled palette, ask only question 2 (the Stream app), and match the design via [`references/design-matching.md`](references/design-matching.md). - **App - named choice, "default", or "don't care"** -> Step 2 applies it (the configured app wins whenever one exists). **Create new** -> Step 2 runs the create flow. - **Account has no orgs at all** -> drop question 2, announce that a fresh org + app will be created, and ask only the theme. @@ -139,8 +142,7 @@ Order: 2. **Step 2:** Apply the org/app choice (select existing or create). 3. **Task A:** Scaffold with Shadcn + Next.js using the chosen preset. 4. **Task A.1:** Add base Shadcn components. -5. **Task A.2:** Disclose + ask about third-party frontend skill installs; install only with user consent. -6. Continue with Task B (.env), Task C (SDKs), Task D (CLI config). +5. Continue with Task B (.env), Task C (SDKs), Task D (CLI config). **Task A: Scaffold** - scaffolds Next.js + Tailwind + Shadcn/ui (Base UI) into the current directory. Use the theme preset chosen in **Step 1b**. @@ -158,24 +160,6 @@ npx shadcn@latest add button input textarea card avatar badge separator Add more components as the use case requires (e.g. `dialog`, `dropdown-menu`, `tabs`, `popover`). -**Task A.2: Frontend skills** - third-party skill packs. **You must disclose and ask before installing.** Do NOT construct your own command variant. - -Print this disclosure verbatim, then stop and wait for the user's answer: - -> I'd like to install three third-party skill packs that improve generic UI quality: -> - `vercel-react-best-practices` - from [`vercel-labs/agent-skills`](https://github.com/vercel-labs/agent-skills) -> - `web-design-guidelines` - from [`vercel-labs/agent-skills`](https://github.com/vercel-labs/agent-skills) -> - `frontend-design` - from [`anthropics/skills`](https://github.com/anthropics/skills) -> -> The packs are markdown only - no scripts execute. If you say yes, I'll run `npx skills add ... -y` once per pack from those GitHub repos at their current `main` branch (`-y` skips the installer's own confirmation since you've consented here). These aren't required - Stream reference files cover SDK wiring either way. Install them? - -- **User agrees** -> run: - ```bash - npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices -y && npx skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines -y && npx skills add https://github.com/anthropics/skills --skill frontend-design -y - ``` -- **User declines** -> skip silently and continue to Task B. Do not retry, do not bring it up again this session. -- **Install fails** -> continue with Stream reference files only; mention the failure briefly. - Do **not** modify `layout.tsx` or `globals.css` after scaffold - use Shadcn's defaults as-is (RULES.md > Theme). **Task B: .env** - run AFTER scaffold so the `.env` lands inside the project directory. @@ -206,7 +190,9 @@ getstream env npm install --legacy-peer-deps ``` -After installing SDKs, verify an icon package is available. Some Shadcn presets bundle one, others don't: +After installing SDKs, note the resolved **Stream Chat React major**: the bundled blueprints assume **v14** (`MessageComposer`, `stream-chat-react/css/index.css`). `@latest` is v14 today; if a future install resolves a **newer major**, fetch that major's component reference from [`references/docs-map.md`](references/docs-map.md) (Version note) before writing the provider tree - don't apply v14 names from memory. + +Also verify an icon package is available. Some Shadcn presets bundle one, others don't: ```bash node -e "const p=['lucide-react','@phosphor-icons/react','@hugeicons/react'];console.log(p.some(m=>{try{require.resolve(m);return true}catch{return false}})?'ICONS_OK':'NO_ICONS')" @@ -218,11 +204,11 @@ If `NO_ICONS`, install `lucide-react`: `npm install lucide-react --legacy-peer-d ### Step 4: Generate code and UI -**Prebuilt-component-first.** Build the common path with the SDK's prebuilt React components and customize via the documented hooks/props ([`RULES.md`](RULES.md) > Reference authority). Hand-build markup only when the user explicitly wants fully bespoke UI. +**Prebuilt-component-first.** Build the common path with the SDK's prebuilt React components and customize via the documented hooks/props ([`RULES.md`](RULES.md) > Reference authority). Writing your own component for a region (custom message row, composer, channel preview/header, call layout) - not just passing props - loads [`references/custom-ui.md`](references/custom-ui.md) (the completion contract) first; see [`RULES.md`](RULES.md) > Reference authority. -**Docs-first:** before implementing any component, cookbook, or advanced feature (typing indicator, custom message UI, reactions, AI integrations, read state, notifications, call layouts, ...), follow the **Docs-first triggers** section above - `WebFetch` the matching [`references/DOCS.md`](references/DOCS.md) page first, then build to match. +**Docs-first:** before implementing any component, cookbook, or advanced feature (typing indicator, custom message UI, reactions, AI integrations, read state, notifications, call layouts, ...), follow the **Docs-first triggers** section above - `WebFetch` the matching [`references/docs-map.md`](references/docs-map.md) page first, then build to match. -**Load [`builder-ui.md`](builder-ui.md) and [`sdk.md`](sdk.md)** (cross-cutting SDK wiring: token route, instantiation, CSS imports), plus **only** the relevant [`references/.md`](references/) header + `references/-blueprints.md` for the sections you are implementing - not every reference file. Pull **Use Case Matching** and **Page Flow** from [`builder.md`](builder.md) to choose products and navigation structure. **For multi-product apps (Chat + Video, Chat + Feeds, Video + Feeds, etc.), also load [`references/CROSS-PRODUCT.md`](references/CROSS-PRODUCT.md) before writing AppShell** - it has the canonical multi-client provider hierarchy and an error -> cause -> fix table. +**Load [`builder-ui.md`](builder-ui.md) and [`sdk.md`](sdk.md)** (cross-cutting SDK wiring: token route, instantiation, CSS imports), plus **only** the relevant [`references/.md`](references/) header + `references/-blueprints.md` (the prebuilt provider tree + props) for the product(s) you are implementing - not every reference file. For any customization, fetch the matching live page from [`references/docs-map.md`](references/docs-map.md) first (and if you are writing your own component for a region, load [`references/custom-ui.md`](references/custom-ui.md) first per Step 4's rule above / [`RULES.md`](RULES.md) > Reference authority). Pull **Use Case Matching** and **Page Flow** from [`builder.md`](builder.md) to choose products and navigation structure. **For multi-product apps (Chat + Video, Chat + Feeds, Video + Feeds, etc.), also load [`references/CROSS-PRODUCT.md`](references/CROSS-PRODUCT.md) before writing AppShell** - it has the canonical multi-client provider hierarchy and an error -> cause -> fix table. ### Step 5: Verify diff --git a/skills/stream-react/builder-ui.md b/skills/stream-react/builder-ui.md index c1d8743..aa953f7 100644 --- a/skills/stream-react/builder-ui.md +++ b/skills/stream-react/builder-ui.md @@ -2,8 +2,10 @@ Load when executing **Step 4** (after scaffold). Rules: [`RULES.md`](RULES.md) (login screen first, theme, reference authority). +> **Reference-design override.** If the request carries a screenshot / Figma / "make it look like X", the **reference's frame wins over the generic shell defined here** (App Header, sidebar, shell geometry). Reproduce the reference's frame per [`references/design-matching.md`](references/design-matching.md) - **remove in-app chrome the reference doesn't show** (a bare phone-chat reference has no app top-bar and no chat-list sidebar) and **fill the viewport** (no fixed-width chat strip beside empty background) - then apply the region + theming guidance here within it. The Login Screen stays as the auth gate unless the reference itself is the full app. + ### Step 4: Generate ALL code files -Write every file sequentially. Follow the UI Guidelines below for all visual styling. See **RULES.md > Reference authority** - reference files are the only source of truth for SDK wiring. Before writing each component, load the relevant `references/-blueprints.md` section. +Write every file sequentially. Follow the UI Guidelines below for all visual styling. See **RULES.md > Reference authority** - reference files are the only source of truth for SDK wiring. Before writing each component, load the prebuilt path in the relevant `references/-blueprints.md`; fetch the matching live page from [`references/docs-map.md`](references/docs-map.md) for any customization, and load [`references/custom-ui.md`](references/custom-ui.md) (the completion contract) first when you write your own component for a region - even one, even via `Message=` / `WithComponents`. #### Login Screen (required for every app - RULES.md > Login Screen first) @@ -22,9 +24,9 @@ Centered card on a neutral background. No sidebar, no nav - just the login form. - After successful token fetch, render the main app UI (state gate, not redirect) - App name / use-case label above the input -#### App Header (required for every app) +#### App Header (default shell - omit when a reference design doesn't show it) -Once logged in, every app MUST show a persistent header bar: +Once logged in, the default shell shows a persistent header bar (but see the **Reference-design override** above - a screenshot that has no app top-bar means you drop this): - **Left:** App name (derived from use case) - **Right:** Avatar circle (initial letter) + username + "Switch User" button @@ -52,7 +54,7 @@ Use whatever `globals.css` Shadcn generates. Do not add custom variables, custom ### Design -Use Shadcn components, Tailwind utilities, and - if the user approved them in Step 3 Task A.2 - the frontend skills to build a polished UI. No further opinions; use your best judgement. Stream references provide structure and wiring; frontend skills (when present) provide generic design guidance. +Use Shadcn components, Tailwind utilities, and - if frontend skill packs are already available in the session - the frontend skills to build a polished UI. No further opinions; use your best judgement. Stream references provide structure and wiring; frontend skills (when present) provide generic design guidance. ### Button labels @@ -60,18 +62,24 @@ Use Shadcn components, Tailwind utilities, and - if the user approved them in St ### Stream SDK CSS & Providers -- **Chat:** Import `stream-chat-react/css/index.css` (v14+ preferred alias; v13 used `dist/css/v2/index.css`). Use `useCreateChatClient` from `stream-chat-react` to instantiate. Match theme: `useTheme()` -> `str-chat__theme-dark` or `str-chat__theme-light` to ``. +- **Chat:** Import the Chat CSS ([`sdk.md`](sdk.md) > CSS imports for the path + version variants). Use `useCreateChatClient` from `stream-chat-react` to instantiate. Match theme: `useTheme()` -> `str-chat__theme-dark` or `str-chat__theme-light` to ``. - **Video:** Import `@stream-io/video-react-sdk/dist/css/styles.css`. Instantiate `StreamVideoClient` with the canonical `useState` + `useEffect` pattern (NOT `useMemo` - see `references/VIDEO.md`). - **Feeds:** No CSS import - headless SDK. Wrap app in ``, then per-feed in ``. Use `useCreateFeedsClient()` for client creation - **gate rendering on `client !== null`** (returns `null` until connected). Call `feed.getOrCreate({ watch: true })` inside `setTimeout(50ms)` + `mounted` guard (strict mode protection) before passing to ``. See `references/FEEDS.md` for complete patterns. **Provider hierarchy:** mount **all** Stream providers - ``, ``, `` - once at AppShell, in any order. Per-screen components render ``, ``, or `` from the existing root providers. **Never re-instantiate Stream clients per screen** - the cleanup of one screen's effect will disconnect a client another screen is still using. For multi-product apps, see [`references/CROSS-PRODUCT.md`](references/CROSS-PRODUCT.md) for the full skeleton. +**Layout / sizing (Stream regions fill their parent).** The prebuilt regions must **grow to fill the container you put them in** - the SDK's default `str-chat` CSS can otherwise cap the **channel list at a fixed width** (you may see ~30% / ~288px) and leave the message list or header not filling. Own the sizing with your wrapper: +- A **flex row**: the channel-list column gets the width you want (a fixed sidebar width, or `flex-1` to fill); the `` / `` column is `flex-1 min-w-0` (the `min-w-0` defeats flexbox `min-width: auto`, which otherwise blocks `` from growing/shrinking to its column). +- A **height chain**: root the height at the AppShell (`h-dvh` / `h-full`) so `` and `` fill vertically instead of collapsing. +- Each region `w-full h-full` inside its column - the **wrapper decides the width**, not the component's own default; `` (or a custom header) spans the full width of the main pane. +- If the SDK default still caps the channel-list width, override it through the documented `str-chat` sizing variable (confirm the current name on the Theming page - do not guess it) or a wrapper width utility. Do not ship the ~288px default when full width is wanted. + ### Moderation **Never build moderation review UI in the app** (RULES.md > Moderation is Dashboard-only). All review happens in the [Stream Dashboard](https://beta.dashboard.getstream.io). The app's role is **CLI setup only** (blocklists, automod config in Step 3). ### Reference Blueprints -See RULES.md > Reference authority. Load `references/.md` (header) for setup + gotchas, and `references/-blueprints.md` for structure and wiring of each component. Load only the product(s) relevant to the current use case. +See RULES.md > Reference authority. Load `references/.md` (header) for setup + gotchas, and `references/-blueprints.md` for the prebuilt provider tree + props. For fully bespoke UI on the low-level client, see [`references/custom-ui.md`](references/custom-ui.md). Load only the product(s) relevant to the current use case. -**Prebuilt-first default (Chat + Video).** Build the common path with the SDK's prebuilt React components - Chat: `` / `` / `` / `` / `` / `` / `` (v14 uses `MessageComposer`, not `MessageInput`); Video: `` / `` / `SpeakerLayout` / `PaginatedGridLayout` / `ParticipantView` / `CallControls`. Customize via the documented hooks/props - register custom UI through `` or pass a per-list ``; read state with `useChannelStateContext()` / `useCallStateHooks()`) - fetch the matching cookbook page first (RULES.md > Docs-first). Drop to fully hand-built markup **only** when the user explicitly wants bespoke UI; the `*-blueprints.md` "Fully custom UI" fallback section has the raw element + wiring tables for that case. Feeds is headless (no prebuilt UI) - always build from its hooks. +**Prebuilt-first default (Chat + Video).** Build the common path with the SDK's prebuilt React components - Chat: `` / `` / `` / `` / `` / `` / `` (v14 uses `MessageComposer`, not `MessageInput`); Video: `` / `` / `SpeakerLayout` / `PaginatedGridLayout` / `ParticipantView` / `CallControls`. Customize via the documented hooks/props - register custom UI through `` or pass a per-list ``; read state with `useChannelStateContext()` / `useCallStateHooks()`) - fetch the matching cookbook page first (RULES.md > Docs-first). Drop to fully hand-built markup **only** when the user explicitly wants bespoke UI; [`references/custom-ui.md`](references/custom-ui.md) carries the prebuilt-vs-bespoke decision + the headless context-hook map for that case. Feeds is headless (no prebuilt UI) - always build from its hooks. diff --git a/skills/stream-react/enhance.md b/skills/stream-react/enhance.md index 0ce37d4..0ae64c6 100644 --- a/skills/stream-react/enhance.md +++ b/skills/stream-react/enhance.md @@ -14,7 +14,7 @@ For adding Stream products to an **existing React project** - Next.js or any oth Before writing any code, understand what's already in place: -1. **Packages:** check `package.json` for `stream-chat`, `stream-chat-react`, `@stream-io/video-react-sdk`, `@stream-io/node-sdk`. +1. **Packages + installed version:** check `package.json` for `stream-chat`, `stream-chat-react`, `@stream-io/video-react-sdk`, `@stream-io/node-sdk` - and read the **installed major** of each (from the `package.json` range and the lockfile). The bundled blueprints assume **Chat React v14** (`MessageComposer`, `stream-chat-react/css/index.css`); if the project pins an **older major** (v13: `MessageInput`, `dist/css/v2/index.css`), do **not** apply v14 blueprint names from memory - fetch that major's docs index from [`references/docs-map.md`](references/docs-map.md) (Version note) and match the installed version. Never bump the SDK as a side effect of adding a feature. 2. **Framework:** detect from `package.json` - **Next.js** (has `next`; route handlers under `app/` or `pages/api`; build = `next build`) vs **another React stack** (Vite/CRA/Remix/etc.; no Next.js `/api` routes; build = the project's own `build` script). This drives where the token endpoint lives (E3) and the verify command (E4). 3. **Auth:** does the app already have a server token endpoint (Next.js `/api/token` route, or an equivalent endpoint in the app's own backend)? If so, **extend** it with the new product's token - don't create a second token endpoint. 4. **Credentials:** check for `.env` with `STREAM_API_KEY` / `STREAM_API_SECRET`. If missing, run `getstream init` (if the dir isn't a Stream project yet) then `getstream env` to write them - never read or print the secret. @@ -25,13 +25,13 @@ Before writing any code, understand what's already in place: 1. **Install** only the new SDKs **with the project's detected package manager** (from E1) - never introduce a second lockfile: npm -> `npm install --legacy-peer-deps`; yarn -> `yarn add `; pnpm -> `pnpm add `. `--legacy-peer-deps` is **npm-only** (yarn/pnpm resolve Stream peers without it). (the `stream` skill's [`RULES.md`](../stream/RULES.md) > Package manager.) 2. **Configure via CLI:** run setup commands from the relevant `references/.md` (App Integration -> Setup). Feeds needs feed groups created; Moderation needs blocklist + config. -3. **Import CSS** if the product needs it (Chat: `stream-chat-react/css/index.css` (v14+ preferred alias; v13 used `dist/css/v2/index.css`), Video: `@stream-io/video-react-sdk/dist/css/styles.css`). +3. **Import CSS** if the product needs it - paths + version variants live in [`sdk.md`](sdk.md) > CSS imports (Chat `stream-chat-react/css/index.css`, Video `@stream-io/video-react-sdk/dist/css/styles.css`). ## E3: Integrate 1. **Token endpoint:** extend the existing token endpoint to return the new product's token alongside existing ones. On **Next.js** this is the `/api/token` route handler; on **other React stacks** it is the project's own server endpoint (Express/Fastify route, Remix loader/action, Vite server fn, etc.) - the server-side instantiation in [`sdk.md`](sdk.md) is identical, only the host differs. Tokens are always minted server-side ([`RULES.md`](RULES.md) > Env vars are server-side only). 2. **API routes:** add the product-specific server routes from `references/.md` (App Integration -> API Routes) - as Next.js route handlers on Next.js, or equivalent endpoints in the app's backend otherwise. Feeds needs several (`/api/feed/get`, `/api/feed/post`, etc.); Chat and Video typically only need the token endpoint. -3. **Components:** load the relevant `references/-blueprints.md` sections and build components using the existing project's patterns and styling conventions - not the [`builder-ui.md`](builder-ui.md) defaults. +3. **Components:** load the prebuilt path in `references/-blueprints.md`; fetch the matching live page from [`references/docs-map.md`](references/docs-map.md) for any customization. **Writing your own component for a region - even one, even via `Message=` / `WithComponents` - loads [`references/custom-ui.md`](references/custom-ui.md) (the completion contract) first** ([`RULES.md`](RULES.md) > Reference authority). Build using the existing project's patterns and styling conventions - not the [`builder-ui.md`](builder-ui.md) defaults. 4. **State:** if the app already manages user state (auth context, session), wire Stream tokens into that - don't add a separate Login Screen unless the app has no auth. ## E4: Verify @@ -49,7 +49,7 @@ Fix any errors. Use the project's existing package manager and `build` script - ## Key constraints -- Do **not** re-scaffold, re-initialize Shadcn, install frontend skills, or modify `globals.css` / `layout.tsx`. +- Do **not** re-scaffold, re-initialize Shadcn, or modify `globals.css` / `layout.tsx`. - Do **not** overwrite or restructure existing files - add new files alongside them. - Do **not** change the existing auth flow. Adapt Stream's token generation to fit the app's existing auth, not the other way around. - If the project uses a different package manager (yarn, pnpm), match what it already uses - the npm-only rule applies to new scaffolds, not existing projects. diff --git a/skills/stream-react/migrate.md b/skills/stream-react/migrate.md index 545e398..36ed0c4 100644 --- a/skills/stream-react/migrate.md +++ b/skills/stream-react/migrate.md @@ -28,9 +28,9 @@ Establish **from version -> to version** for each package the user wants to move **Match the upgrade to its guide and `WebFetch` it this turn.** Known entry point: -- **Chat React v13 -> v14:** https://getstream.io/chat/docs/sdk/react/release-guides/upgrade-to-v14.md (also in [`references/DOCS.md`](references/DOCS.md) > Chat Advanced Guides). +- **Chat React v13 -> v14:** https://getstream.io/chat/docs/sdk/react/release-guides/upgrade-to-v14.md (also in [`references/docs-map.md`](references/docs-map.md) > Chat Advanced Guides). -For any other package/version, discover the guide from the product `llms.txt` index ([`references/DOCS.md`](references/DOCS.md) > Source indexes) - look for a `release-guides/` or `upgrade-*` page that matches the from/to versions. Read the guide in full before touching code; note every breaking change, rename, removed export, and codemod it lists. +For any other package/version, discover the guide from the product index ([`references/docs-map.md`](references/docs-map.md) > The docs convention) - look for a `release-guides/` or `upgrade-*` page that matches the from/to versions. Read the guide in full before touching code; note every breaking change, rename, removed export, and codemod it lists. **Hard gate ([`RULES.md`](RULES.md)):** if no guide loads, hand the lookup to the `stream-docs` skill. If neither the guide nor `stream-docs` confirms the migration steps, **stop and tell the user** - report that you could not confirm the upgrade path and ask how to proceed. Do not migrate from memory; a guess that happens to compile is still a guess. diff --git a/skills/stream-react/references/CHAT-blueprints.md b/skills/stream-react/references/CHAT-blueprints.md index 02c1977..e8a04ed 100644 --- a/skills/stream-react/references/CHAT-blueprints.md +++ b/skills/stream-react/references/CHAT-blueprints.md @@ -2,7 +2,7 @@ Setup, routes, and gotchas: [CHAT.md](CHAT.md). Rules: [`../RULES.md`](../RULES.md) (reference authority, strict mode protection) and the cross-cutting [../../stream/RULES.md](../../stream/RULES.md) (secrets, no auto-seeding). -**Build the common path with `stream-chat-react`'s prebuilt components and customize via the documented hooks/props.** Only drop to the hand-built markup in **Fully custom UI (fallback)** below when the user explicitly wants fully bespoke UI - and even then, fetch the matching docs page first ([`DOCS.md`](DOCS.md)). +**Build the common path with `stream-chat-react`'s prebuilt components and customize via the documented hooks/props.** For fully hand-built, bespoke UI on the low-level client, see [`custom-ui.md`](custom-ui.md) - and even then, fetch the matching docs page first ([`docs-map.md`](docs-map.md)). --- @@ -65,7 +65,7 @@ import { WithComponents } from 'stream-chat-react'; **Bonus:** `ChatView` (+ `ChatView.Selector` / `.Channels` / `.Threads`) and `ThreadList` / `ThreadListItem` for a channel/thread switcher. -**Beyond the common path (docs-first expansion points)** - features the blueprints above don't cover; fetch the matching [`DOCS.md`](DOCS.md) row before building: +**Beyond the common path (docs-first expansion points)** - features the blueprints above don't cover; fetch the matching [`docs-map.md`](docs-map.md) row before building: - **Polls** - prebuilt `Poll` component; create / vote (needs `polls` enabled on the channel type) - **AI assistant / streaming responses** - typewriter effect + `AIStateIndicator` (AI Integrations, LangChain, Vercel AI SDK) - **Voice messages** - record in the composer (`audioRecordingEnabled`) and play back the voice-recording attachment @@ -75,690 +75,10 @@ import { WithComponents } from 'stream-chat-react'; - **Threads manager** - `ChatView` channel/thread switcher + `ThreadList` (unread-threads inbox) - **Moderation bounce** - let a user review / edit / retry their own message bounced by moderation (`MessageBounce`) -**Docs-first:** for any customization (custom message UI, theming, reactions, composer UI, channel header, search, AI, ...) fetch the matching page from [`DOCS.md`](DOCS.md) before writing - the prebuilt props and customization API evolve. Component reference pages live at `https://getstream.io/chat/docs/sdk/react/components/{category}/{component}.md`. +**Docs-first:** for any customization (custom message UI, theming, reactions, composer UI, channel header, search, AI, ...) fetch the matching page from [`docs-map.md`](docs-map.md) before writing - the prebuilt props and customization API evolve. Component reference pages live at `https://getstream.io/chat/docs/sdk/react/components/{category}/{component}.md`. --- ## Fully custom UI (fallback) -> **Use these only when the user explicitly wants bespoke, fully hand-built UI** (not the prebuilt components above). Each section gives the raw element structure + a wiring table mapping DOM to SDK calls. Still fetch the matching [`DOCS.md`](DOCS.md) page first. The BEM class names are a structural spec (elements + conditional states) - implement with Shadcn components and Tailwind utilities; do not ship the BEM classes or hand-written CSS. - -## Channel List - -Sidebar listing channels the user belongs to. Shows last message preview, unread count, and online presence. - -### Blueprint - -```html -
- -
- -

Messages

- -
- - - - -
- - - -
- - - -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `channel-list__items` | `client.queryChannels(filter, sort, { watch: true, state: true })` | - | Returns array of `channel` objects | -| `channel-list__item-avatar` (1:1) | Channel members | - | Other member's `user.image` | -| `channel-list__item-avatar` (group) | `channel.data.image` | `channel.update({ image })` | `channel.data.image` | -| `channel-list__item-name` (1:1) | Channel members | - | Other member's `user.name` | -| `channel-list__item-name` (group) | `channel.data.name` | `channel.update({ name })` | `channel.data.name` | -| `channel-list__item-preview` | Channel state | - | `channel.state.messages[last].text` - truncated | -| `channel-list__item-time` | Channel state | - | `channel.state.messages[last].created_at` | -| `channel-list__item-unread` | `channel.countUnread()` | `channel.markRead()` | Returns integer | -| `channel-list__item-presence` | User presence events | - | `user.online` (boolean) | -| `--active` modifier | Client-side selection state | - | Set when user clicks channel | -| New channel | - | `client.channel(type, id, { name, members })` then `channel.watch()` | - | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Channel queries | `client.queryChannels(filter, sort, options)` | Available | -| Real-time updates | `{ watch: true }` in query options | On by default - both `watch` and `state` default to `true` in `queryChannels` | -| Presence | Dashboard -> Channel Type -> "Connect Events" enabled | Off per channel type | -| Unread counts | `channel.countUnread()` | Available - relies on `channel.markRead()` being called | -| Search | `client.search(filter, query)` or `client.queryChannels` with name filter | Available | - ---- - -## Channel Header - -Top bar of an active channel. Shows channel identity, members, and actions. - -### Blueprint - -```html -
- -
- - - -
-

- - - -
-
- -
- - - - - -
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `channel-header__avatar` | `channel.data` or member data | - | `channel.data.image` (group) or other member's `user.image` (1:1) | -| `channel-header__name` | `channel.data` or member data | - | `channel.data.name` (group) or other member's `user.name` (1:1) | -| `channel-header__status` (1:1) | Presence events | - | `user.online` -> "Online"; `user.last_active` -> "Last seen X ago" | -| `channel-header__status` (group) | `channel.state.members`, `channel.state.watcher_count` | - | Count members + online watchers | -| `channel-header__action--call` | - | Initiates Stream Video call (see VIDEO.md) | Cross-product integration | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Presence / last active | "Connect Events" enabled on channel type | Off | -| Watcher count | `{ watch: true, presence: true }` on `channel.watch()` | Must pass explicitly | -| Video/audio calls | Stream Video product enabled | Separate product | - ---- - -## Message List - -Scrollable container for messages. Handles date separators, scroll-to-bottom, and real-time message injection. - -### Blueprint - -```html -
- - -
- - -
- - -
- March 17, 2026 -
- - -
- Jane added Alex to the channel -
- - -
- - -
- -
-
- -
-
- - -
- -
- - - - -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| Messages (initial) | `channel.watch()` or `channel.query({ messages: { limit: 25 } })` | - | `channel.state.messages` | -| Messages (older) | `channel.query({ messages: { limit: 25, id_lt: oldestMessageId } })` | - | Prepend to message list | -| Messages (real-time) | `channel.on('message.new', callback)` | - | Append new message to list | -| Message groups | Client-side grouping | - | Group consecutive messages by `message.user.id` within a time window | -| Date separators | Client-side | - | Insert when `message.created_at` crosses a day boundary | -| System events | `channel.on('member.added', ...)`, `channel.on('member.removed', ...)` | - | Render as `message-list__event` | -| Typing indicator | `channel.on('typing.start', ...)`, `channel.on('typing.stop', ...)` | - | `channel.state.typing` - map of userId -> event | -| Scroll to bottom | Client-side scroll position tracking | - | Show when `scrollTop < threshold` | -| `message-list__new-count` | Track messages received while scrolled up | - | Client-side counter | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Message history | `channel.watch()` or `channel.query()` | Available | -| Real-time | `channel.watch()` establishes websocket | Automatic when channel is watched | -| Typing events | "Typing Events" enabled on channel type in Dashboard | On for most types | -| Read events | "Read Events" enabled on channel type | On for most types | -| System events | Automatic on member add/remove | Available | - ---- - -## Message - -The core content unit in chat. A single message with author info, text, attachments, reactions, and thread. - -### Blueprint - -```html -
- - - - - - - - - -
- -
- - -
- - -
- Pinned by -
- -
- -
- -

-
- - -

- - -
- -
- -
-
- - -
- -
- - - - - - - -
- - - -
-
-
- - -
This message was deleted.
- - - -
- - - - -
- - - - - - -
- - -
- - -
- - - - - - - - - -
-
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `message__avatar` | In message payload | - | `message.user.image` | -| `message__author` | In message payload | - | `message.user.name` | -| `message__time` | In message payload | - | `message.created_at` | -| `message__text` | In message payload | - | `message.text` | -| `message__pinned-badge` | In message payload | - | `message.pinned`, `message.pinned_by.name` | -| `message__quoted` | In message payload | - | `message.quoted_message.user.name`, `message.quoted_message.text` | -| `message__image-item` | In message payload | - | `message.attachments[].image_url` where `type === 'image'` | -| `message__video-player` | In message payload | - | `message.attachments[].asset_url` where `type === 'video'` | -| `message__file` | In message payload | - | `message.attachments[].asset_url`, `.title`, `.file_size` where `type === 'file'` | -| `message__og-*` | In message payload | - | `message.attachments[].og_scrape_url`, `.title`, `.text`, `.image_url` | -| `message__deleted` | In message payload | - | `message.deleted_at` (truthy = deleted) | -| `message__reaction` | In message payload | - | `message.reaction_groups` (keyed by type; each has `count`, `sum_scores`, `first_reaction_at`, `last_reaction_at`) - preferred. `message.reaction_counts` still works. Also `message.own_reactions[]` | -| Reaction - add | - | `channel.sendReaction(message.id, { type: 'like' })` | Supports `{ enforce_unique: true }` option as third arg to replace all user's existing reactions | -| Reaction - remove | - | `channel.deleteReaction(message.id, 'like')` | Removes current user's reaction of that type | -| `message__thread-count` | In message payload | - | `message.reply_count` | -| `message__thread-avatars` | In message payload | - | `message.thread_participants[].image` | -| `message__thread-last` | Thread replies | - | Most recent reply's `created_at` (from `channel.getReplies()` or thread state) | -| `message__status` (read) | `channel.state.read` | - | Map of `userId -> { last_read, user }` - compare with `message.created_at` | -| Edit | - | `client.updateMessage({ id: message.id, text: newText })` | - | -| Delete | - | `client.deleteMessage(message.id)` | Sets `message.deleted_at`. Pass `{ hardDelete: true }` for permanent deletion | -| Pin | - | `client.pinMessage(message, timeoutOrExpiration)` | Accepts a message object or message id. Second arg is optional: timeout in seconds, expiration date, or null for no expiry | -| Unpin | - | `client.unpinMessage(message)` | Accepts a message object or message id | -| `message__mentioned-users` | In message payload | - | `message.mentioned_users` - enriched user objects for @mentions in the message | -| Flag | - | `client.flagMessage(message.id)` | See MODERATION.md | -| Mute user | - | `client.muteUser(userId, null, { timeout: 60 })` | Three args: userId, null, options object. `timeout` is in minutes | -| Quote | - | Send new message with `quoted_message_id: message.id` | - | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Attachments | "Uploads" enabled on channel type | On | -| Reactions | "Reactions" enabled on channel type in Dashboard | On | -| Replies/threads | "Replies" enabled on channel type | On | -| Read receipts | "Read Events" enabled on channel type | On for most types | -| URL enrichment | "URL Enrichment" enabled on channel type | On - auto-scrapes OG data server-side | -| Pinning | "Pinning" enabled on channel type | Off | -| Quoting | `quoted_message_id` on `sendMessage` | Available - no config needed | -| Message editing | - | Available - own messages by default, admin can edit any | -| Message deletion | - | Available - own messages by default, admin can delete any | - ---- - -## Message Input - -Text input for composing and sending messages. Handles attachments, mentions, slash commands, and edit mode. - -### Blueprint - -```html -
- - - - - -
- Editing message - -
- - -
- -

- -
- - -
-
- - - -
-
-
-
-
- -
-
- -
- -
-
- - -
- -
- - -
- -
-
- -
- - -
-
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `message-input__text` | - (user input) | Becomes `message.text` | - | -| `message-input__mention-dropdown` | `channel.queryMembers({ name: { $autocomplete: query } })` | - | Match typed query against channel members | -| `message-input__command-dropdown` | `channel.getConfig()` | - | `channel.config.commands[]` - name + description | -| `message-input__attachment` (image) | Local blob preview | `channel.sendImage(file)` -> CDN URL | Collect into `message.attachments[]` with `type: 'image'` | -| `message-input__attachment` (file) | Local blob preview | `channel.sendFile(file)` -> CDN URL | Collect into `message.attachments[]` with `type: 'file'` | -| Attachment remove (image) | - | `channel.deleteImage(url)` | Deletes uploaded image from CDN when user removes before sending | -| Attachment remove (file) | - | `channel.deleteFile(url)` | Deletes uploaded file from CDN when user removes before sending | -| Send (new) | - | `channel.sendMessage({ text, attachments, quoted_message_id?, mentioned_users? })` | `mentioned_users` is an array of user IDs referenced via @mentions in the text | -| Send (edit) | - | `client.updateMessage({ id, text, attachments })` | - | -| Send (thread) | - | `channel.sendMessage({ text, parent_id: parentMessage.id })` | - | -| Typing events | - | `channel.keystroke()` on input, `channel.stopTyping()` on pause | Debounced - SDK handles interval | -| `--disabled` | `channel.data.own_capabilities` | - | Check if `'send-message'` is in capabilities array | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| File uploads | "Uploads" enabled on channel type | On | -| Slash commands | Commands configured on channel type in Dashboard | `/giphy` available by default | -| @Mentions | Channel members queryable | Available - searches channel members | -| Typing indicators | "Typing Events" enabled on channel type | On for most | -| Message length | `channel.config.max_message_length` | 5000 chars (server-side default) | -| Slow mode | `channel.data.cooldown` (seconds) | Off - set per channel | - ---- - -## Thread - -Reply thread on a specific message. Opens as a side panel or overlay. - -### Blueprint - -```html -
- -
-

Thread

- - -
- - -
- -
- -
- -
- - -
-
- -
-
- - -
- - - -
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `thread__parent` | Already in message list | - | The message with `reply_count > 0` | -| `thread__replies` | `channel.getReplies(parentMessage.id, { limit: 25 })` | - | Returns `{ messages: [...] }` | -| `thread__replies` (older) | `channel.getReplies(parentId, { limit: 25, id_lt: oldestReplyId })` | - | Cursor pagination | -| `thread__replies` (real-time) | `channel.on('message.new', cb)` - filter where `message.parent_id === parentId` | - | Append to reply list | -| `thread__reply-count` | Parent message | - | `parentMessage.reply_count` | -| Reply - send | - | `channel.sendMessage({ text, parent_id: parentMessage.id })` | - | -| Reply - send to channel | - | `channel.sendMessage({ text, parent_id: parentMessage.id, show_in_channel: true })` | Shows reply in main channel too | -| All threads | `client.queryThreads()` | - | Lists all threads the current user participates in - supports pagination and filtering | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Replies/threads | "Replies" enabled on channel type in Dashboard | On | -| Thread participants | Automatic | Tracked in `message.thread_participants` | - ---- - -## Typing Indicator - -Shows who is currently typing in the channel. - -### Blueprint - -```html - -
-
- - -
-
- - - -
- - - - - -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| Typing users | `channel.on('typing.start', cb)`, `channel.on('typing.stop', cb)` | `channel.keystroke()` / `channel.stopTyping()` | `channel.state.typing` - map of userId -> `{ user }`, excludes current user. For threads: `channel.keystroke(threadId)` sends thread-specific typing events | -| `typing-indicator__avatar` | In typing event | - | `event.user.image` | -| Auto-expiry | Client-side | - | Remove user from typing state after ~7s with no new `typing.start` event | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Typing events | "Typing Events" enabled on channel type | On for most types | - ---- - -## Emoji Reaction Picker - -Overlay for selecting a reaction to add to a message. Typically triggered from message action bar or existing reaction row. - -### Blueprint - -```html -
- -
- - - - - - -
- - -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| Emoji click | - | `channel.sendReaction(message.id, { type: 'like' })` | Type is the `data-type` attribute. Pass `{ enforce_unique: true }` as third arg to replace user's existing reactions | -| Toggle off | Check `message.own_reactions` for existing reaction of same type | `channel.deleteReaction(message.id, 'like')` | - | -| Available types | - | Any string works as reaction type | No configuration needed for custom types | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Reactions | "Reactions" enabled on channel type | On | - ---- - -## Search - -Search messages across channels or within a specific channel. - -### Blueprint - -```html - -``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `search__results` | `client.search({ members: { $in: [userId] } }, query, { limit: 20 })` | - | Returns `{ results: [{ message }] }` | -| `search__results` (in-channel) | `client.search({ cid: channel.cid }, query, { limit: 20 })` | - | Filter by specific channel | -| `search__result-avatar` | In result | - | `result.message.user.image` | -| `search__result-author` | In result | - | `result.message.user.name` | -| `search__result-channel` | In result | - | `result.message.channel.name` or `result.message.channel.id` | -| `search__result-text` | In result | - | `result.message.text` - add highlights client-side | -| Result click | - | Navigate to message in channel | `result.message.channel` + `result.message.id` | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Search | "Search" enabled on channel type | On | -| Cross-channel | `client.search()` with filter across channels | Available | +For fully hand-built, bespoke UI on the low-level client - when the prebuilt components above can't be customized into the target - see [`custom-ui.md`](custom-ui.md): the prebuilt-vs-bespoke decision, the headless context-hook map, and per-region routing to the live docs. Don't hand-build a messenger you could have themed; default to the prebuilt path above. diff --git a/skills/stream-react/references/CHAT.md b/skills/stream-react/references/CHAT.md index 2eb7cfb..c5e838e 100644 --- a/skills/stream-react/references/CHAT.md +++ b/skills/stream-react/references/CHAT.md @@ -1,21 +1,20 @@ # Chat - Setup & Integration -Stream Chat provides pre-built UI components via React, React Native, Flutter, Swift, and Kotlin SDKs. This file covers setup, server routes, client patterns, and gotchas. For full component structure and wiring, see [CHAT-blueprints.md](CHAT-blueprints.md). +Stream Chat provides pre-built UI components via React, React Native, Flutter, Swift, and Kotlin SDKs. This file covers setup, server routes, client patterns, and gotchas. For the prebuilt component path (provider tree + props), see [CHAT-blueprints.md](CHAT-blueprints.md); when you write your own component for a region (even one, even via `Message=` / `WithComponents`) or go fully hand-built, see [custom-ui.md](custom-ui.md) (the completion contract). Rules: [../RULES.md](../RULES.md) (login screen first, strict mode protection, reference authority) and the cross-cutting [../../stream/RULES.md](../../stream/RULES.md) (secrets, no auto-seeding). -- **Blueprint** - HTML with BEM classes defining structure and conditional rendering -- **Wiring** - API calls to read/write each element, exact property paths -- **Requirements** - Dashboard settings, API params, and prerequisites. The **Default** column in each Requirements table reflects server-side channel-type defaults (set in the Dashboard / via the API), not values enforced by the SDK. +- **Prebuilt path** ([CHAT-blueprints.md](CHAT-blueprints.md)) - the canonical provider tree, the prebuilt component prop table, and the `WithComponents` customization mechanism. The common path for every messenger. +- **Bespoke path** ([custom-ui.md](custom-ui.md)) - the prebuilt-vs-bespoke decision + the headless context-hook map for fully hand-built UI. +- **Live docs** ([docs-map.md](docs-map.md)) - fetch the matching component / cookbook / advanced page before building any customization. Server-side channel-type **defaults** (permissions, features) are set in the Dashboard / via the API, not enforced by the SDK. ## Quick ref -- **Packages:** `stream-chat`, `stream-chat-react`; import `stream-chat-react/css/index.css` (v14+ preferred alias; v13 used `dist/css/v2/index.css`). +- **Packages:** `stream-chat`, `stream-chat-react`; import `stream-chat-react/css/index.css` (paths + version variants + the `EmojiPicker` stylesheet: [`../sdk.md`](../sdk.md) > CSS imports). - **First:** **App Integration** -> **Setup** (CLI / channel types) before UI. -- **Per feature:** Jump to section (Channel List, Message List, ...) when implementing that screen. -- **Below the next rule:** full blueprints - **do not load past it** until you implement that component. +- **Per feature:** fetch the matching live page from [docs-map.md](docs-map.md) before implementing that screen; for fully hand-built UI on the low-level client, see [custom-ui.md](custom-ui.md). -Full component blueprints: [CHAT-blueprints.md](CHAT-blueprints.md) - load only the section you are implementing. +Prebuilt component path: [CHAT-blueprints.md](CHAT-blueprints.md) - the canonical provider tree + props. Bespoke UI on the low-level client: [custom-ui.md](custom-ui.md). --- @@ -82,7 +81,7 @@ const client = StreamChat.getInstance(process.env.STREAM_API_KEY!, process.env.S } ``` - Listen for `user.banned` event to show banned state in UI -- Import `stream-chat-react/css/index.css` for default styles - the preferred aliased path (`dist/css/index.css` also resolves; v14+, the `/v2/` subpath was removed). If you use `EmojiPicker`, also import `stream-chat-react/css/emoji-picker.css` +- Import the Chat CSS once - the path, version variants, and the separate `EmojiPicker` stylesheet all live in [`../sdk.md`](../sdk.md) > CSS imports (the canonical home). - `MessageInput` was renamed/removed in v14 - use `MessageComposer` from `stream-chat-react` instead. Note: the React `` UI component is distinct from the `MessageComposer` *state class* in `stream-chat` (same name, different thing) - Token endpoint as `GET /api/token?user_id=xxx` - `upsertUsers` takes an **array** of user objects: `client.upsertUsers([{ id, name, role }])` - NOT an object keyed by ID diff --git a/skills/stream-react/references/CROSS-PRODUCT.md b/skills/stream-react/references/CROSS-PRODUCT.md index 212a6a8..94b3d80 100644 --- a/skills/stream-react/references/CROSS-PRODUCT.md +++ b/skills/stream-react/references/CROSS-PRODUCT.md @@ -2,7 +2,7 @@ When using two or more of Chat / Video / Feeds in the same app, mount all clients **once** at AppShell and provide them at the root. Per-screen components only render ``, ``, or `` from the existing providers - never re-instantiate the clients. -Source of truth: `video/react/10-advanced/06-chat-with-video.md` from the Stream docs (the messenger-clone reference app). +Source of truth: the **Chat Integration** guide (Video React > Advanced Guides in [`docs-map.md`](docs-map.md)) - `https://getstream.io/video/docs/react/advanced/chat-with-video.md`, the messenger-clone reference app. Fetch it via the map, don't guess a doc path ([`docs-map.md`](docs-map.md) > URL grounding). ## AppShell skeleton diff --git a/skills/stream-react/references/FEEDS-blueprints.md b/skills/stream-react/references/FEEDS-blueprints.md index c818860..78b06a6 100644 --- a/skills/stream-react/references/FEEDS-blueprints.md +++ b/skills/stream-react/references/FEEDS-blueprints.md @@ -76,7 +76,7 @@ Individual activity card showing author, content, reactions, comments, and actio
- +
diff --git a/skills/stream-react/references/VIDEO-blueprints.md b/skills/stream-react/references/VIDEO-blueprints.md index cce2d29..1c09237 100644 --- a/skills/stream-react/references/VIDEO-blueprints.md +++ b/skills/stream-react/references/VIDEO-blueprints.md @@ -2,7 +2,7 @@ Setup, routes, and gotchas: [VIDEO.md](VIDEO.md). Rules: [`../RULES.md`](../RULES.md) (reference authority, strict mode protection) and the cross-cutting [../../stream/RULES.md](../../stream/RULES.md) (secrets, no auto-seeding). -**Build the common path with `@stream-io/video-react-sdk`'s prebuilt components and customize via `useCallStateHooks()` + `ParticipantView`.** Only drop to the hand-built markup in **Fully custom UI (fallback)** below when the user explicitly wants fully bespoke UI - and even then, fetch the matching docs page first ([`DOCS.md`](DOCS.md)). +**Build the common path with `@stream-io/video-react-sdk`'s prebuilt components and customize via `useCallStateHooks()` + `ParticipantView`.** For fully hand-built, bespoke UI on the low-level client, see [`custom-ui.md`](custom-ui.md) - and even then, fetch the matching docs page first ([`docs-map.md`](docs-map.md)). --- @@ -54,477 +54,10 @@ const [spotlight, ...others] = useParticipants(); **Customization hooks:** the umbrella is `useCallStateHooks()`, which returns sub-hooks you destructure: `useParticipants`, `useLocalParticipant`, `useRemoteParticipants`, `useCallCallingState`, `useCameraState`, `useMicrophoneState`, `useScreenShareState`, `useDominantSpeaker`, `useParticipantCount`, `useOwnCapabilities`, `useIsCallRecordingInProgress`, ... Plus `useCall()` and `useStreamVideoClient()`. Usage: `const { useCameraState } = useCallStateHooks(); const { camera, isMute } = useCameraState(); camera.toggle();` -**Docs-first:** for any customization (replacing call controls, custom layouts, lobby preview, PiP, network quality, livestream watching, ringing, ...) fetch the matching page from [`DOCS.md`](DOCS.md) first. Component reference pages live at `https://getstream.io/video/docs/react/ui-components/...`. +**Docs-first:** for any customization (replacing call controls, custom layouts, lobby preview, PiP, network quality, livestream watching, ringing, ...) fetch the matching page from [`docs-map.md`](docs-map.md) first. Component reference pages live at `https://getstream.io/video/docs/react/ui-components/...`. --- ## Fully custom UI (fallback) -> **Use these only when the user explicitly wants bespoke, fully hand-built UI** (not the prebuilt components above). Each section gives the raw element structure + a wiring table mapping DOM to SDK calls. Still fetch the matching [`DOCS.md`](DOCS.md) page first. The BEM class names are a structural spec (elements + conditional states) - implement with Shadcn components and Tailwind utilities; do not ship the BEM classes or hand-written CSS. - -## Lobby - -Pre-join screen where users preview their camera/mic and set preferences before entering a call. - -### Blueprint - -```html -
- -
- - - -
- - -
- -
-
-
-
- -
- - -
- - -
- - - - -
- -
-

- -
- - 3 others already in this call -
-
- -
- - -
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `lobby__video` | `call.camera.state.mediaStream` | Assign stream to video element's `srcObject` | Local media stream | -| `lobby__toggle--camera` | `call.camera.state.status` | `call.camera.enable()` / `call.camera.disable()` | `'enabled'` or `'disabled'` | -| `lobby__toggle--mic` | `call.microphone.state.status` | `call.microphone.enable()` / `call.microphone.disable()` | `'enabled'` or `'disabled'` | -| `lobby__device-select--camera` | `call.camera.listDevices()` | `call.camera.select(deviceId)` | RxJS Observable of `MediaDeviceInfo[]` | -| `lobby__device-select--mic` | `call.microphone.listDevices()` | `call.microphone.select(deviceId)` | RxJS Observable of `MediaDeviceInfo[]` | -| `lobby__device-select--speaker` | `call.speaker.listDevices()` | `call.speaker.select(deviceId)` | RxJS Observable of `MediaDeviceInfo[]` | -| `lobby__call-title` | `call.state.custom.title` or `call.id` | - | Set during `call.getOrCreate({ data: { custom: { title } } })` | -| `lobby__participants-preview` | `call.state.participants` | - | Participants already joined | -| `lobby__join` | - | `call.join()` | Transitions to call layout | -| `lobby__cancel` | - | `call.leave()` | Navigates away | -| `lobby__audio-level` | `call.microphone.state.mediaStream` | - | Use `AudioContext` + `AnalyserNode` on the stream | - -> `call.{camera,microphone,speaker}.listDevices()` returns an **RxJS Observable** of `MediaDeviceInfo[]` - you must `.subscribe()` to it (it is not an array). Inside a `` context, prefer `useCameraState().devices` / `useMicrophoneState().devices` / `useSpeakerState().devices`, which are already-resolved arrays. - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Camera/mic access | Browser `getUserMedia` permission | Prompted on first access | -| Call reference | `call = client.call(callType, callId)` then `call.getOrCreate()` | Must create before lobby | -| Device enumeration | `navigator.mediaDevices.enumerateDevices()` | Available after permission grant | -| Participants preview | `call.getOrCreate()` returns current participants | Available | - ---- - -## Call Layout - -Container for the active call. Manages participant arrangement in different layout modes. - -### Blueprint - -```html -
- - - -
- - -
- - -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
- - -
- -
- - is sharing their screen -
-
- - -
- -
- - -
- - wants to join - - -
- - -
- -
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| Participant list | `call.state.participants` | - | Array of `StreamVideoParticipant` objects | -| Dominant speaker | `call.state.dominantSpeaker` | - | Updates in real-time | -| Screen share track | `participant.screenShareStream` | - | Non-null when participant is screen sharing | -| `call-layout__screen-share-video` | Screen share participant's video track | Assign to `srcObject` | `participant.screenShareStream` | -| Layout mode | Client-side state | - | Auto-switch to spotlight when screen share starts | -| Self-view | `call.state.localParticipant` | - | Current user's participant object | -| Join request | `call.on('call.permission_request', cb)` | `call.grantPermissions(userId, ['send-audio','send-video'])` / `call.revokePermissions(userId, [...])` | `permissions` (string[]) is **required** - the second arg lists the capabilities to grant/revoke | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Call joined | `call.join()` completed | - | -| Dominant speaker | Automatic | Available - determined server-side from audio levels | -| Screen sharing | `call.screenShare.enable()` | Available - browser `getDisplayMedia` | -| Layout modes | Client-side | No config - implement layout logic based on participant count and screen share state | -| Join requests | Call type configured with `join_ahead_time_seconds` or backstage mode | Off | - ---- - -## Participant Tile - -Individual participant's video/audio in the call. Used inside call layouts. - -### Blueprint - -```html -
- - - - - -
- -
- - -
-
- - - -
- - -
- - -
- - -
- - -
- - - - - -
-
- -
-``` - -### Wiring - -| Element | Read | Write | Property Path | -|---|---|---|---| -| `participant-tile__video` | Participant's video track | Assign to `srcObject` | `participant.videoStream` | -| `participant-tile__avatar` | Participant data | - | `participant.image` | -| `participant-tile__name` | Participant data | - | `participant.name` or `participant.userId` | -| `--speaking` modifier | Participant audio state | - | `participant.isSpeaking` (boolean) | -| `--muted` modifier | Participant audio state | - | `participant.audioStream` is null or `!hasAudio(participant)` (import `hasAudio` from `@stream-io/video-client`) | -| `--video-off` modifier | Participant video state | - | `participant.videoStream` is null or `!hasVideo(participant)` (import `hasVideo` from `@stream-io/video-client`) | -| `--dominant` modifier | `call.state.dominantSpeaker` | - | `participant.isDominantSpeaker` | -| Network quality | Participant stats | - | `participant.connectionQuality` - `ConnectionQuality.EXCELLENT`, `.GOOD`, `.POOR`, `.UNSPECIFIED` | -| Pin | - | Client-side state | Layout prioritizes pinned participant | -| Mute participant | - | `call.muteUser(userId, 'audio')` | Host/admin only | -| Remove participant | - | `call.kickUser({ user_id: userId })` | Host/admin only. Removes from the call; add `block: true` to also ban from rejoining. `call.blockUser(userId)` *bans* without removing - not the same as "remove" | - -### Requirements - -| Feature | Requirement | Default | -|---|---|---| -| Video rendering | Participant has granted camera permission and is publishing | - | -| Audio rendering | Attach `participant.audioStream` to an `