@cometchat/skills 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/bin/install.js +163 -29
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +192 -54
- package/skills/cometchat-placement/SKILL.md +28 -0
- package/skills/cometchat-react-patterns/SKILL.md +55 -7
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ v4 takes an AI-first approach: your agent has a short conversation with you to u
|
|
|
10
10
|
npx @cometchat/skills add
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
That's it — one command for every supported framework. The installer detects what you're working with (React, Next.js, React Router, Astro, Expo, bare React Native, Angular, native Android, Flutter, or native iOS) from your project and
|
|
13
|
+
That's it — one command for every supported framework. The installer detects what you're working with (React, Next.js, React Router, Astro, Expo, bare React Native, Angular, native Android, Flutter, or native iOS) from your project, then opens an interactive picker so you can choose which AI agents to install for — Claude Code, Cursor, Codex, Cline, Kiro, Replit Agent, and [50+ more](https://github.com/vercel-labs/skills) all in one pass.
|
|
14
14
|
|
|
15
15
|
Override detection if needed:
|
|
16
16
|
|
|
@@ -24,7 +24,47 @@ npx @cometchat/skills add --family ios # Native iOS (V5 stable)
|
|
|
24
24
|
npx @cometchat/skills add --family all # install every published skill
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
### Two install shapes
|
|
28
|
+
|
|
29
|
+
**Base install (default, recommended)** — `npx @cometchat/skills add` writes only the cross-family skills (the `cometchat` dispatcher and friends). Smallest initial install. After it lands, open your project in your IDE and run `/cometchat`:
|
|
30
|
+
|
|
31
|
+
1. The dispatcher detects your framework (Vite + React → `web`, Expo → `native`, etc.)
|
|
32
|
+
2. It asks the agent to install family-specific skills on demand via `npx @cometchat/skills add --family <X> --ide <Y>`
|
|
33
|
+
3. The agent gets the 13 web skills (or 31 android, 28 flutter, etc.) at exactly the moment they're needed
|
|
34
|
+
4. Integration continues with the full skill set loaded
|
|
35
|
+
|
|
36
|
+
You install once with the multi-agent picker (Claude Code, Cursor, Codex, Cline, Kiro, Replit Agent, [50+ more](https://github.com/vercel-labs/skills)); the dispatcher handles framework-specific expansion as the project actually needs it.
|
|
37
|
+
|
|
38
|
+
**Full family install** — `npx @cometchat/skills add --family <name>` writes the dispatcher + every skill for that family upfront (web=13, android=31, flutter=28, etc.). Use when you know the framework upfront and want all skills present without runtime expansion. Power users + CI smoke tests.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Base install (recommended) — picker + dispatcher routes the rest
|
|
42
|
+
npx @cometchat/skills add
|
|
43
|
+
|
|
44
|
+
# Full family install — power-user / CI flow
|
|
45
|
+
npx @cometchat/skills add --family web # all 13 web skills upfront
|
|
46
|
+
npx @cometchat/skills add --family native # all 13 RN skills upfront
|
|
47
|
+
npx @cometchat/skills add --family android # all 31 Android skills upfront
|
|
48
|
+
# ... etc
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Direct-write to one IDE (CI / Dockerfile)
|
|
52
|
+
|
|
53
|
+
When stdin isn't a TTY (CI, Docker), or when you pass `--ide <name>`, the picker is skipped and skills are written directly to one IDE's directory. Default is base install; pass `--family <name>` for the full family install.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx @cometchat/skills add --ide claude # base install to .claude/skills/
|
|
57
|
+
npx @cometchat/skills add --ide cursor # base install to .cursor/skills/
|
|
58
|
+
npx @cometchat/skills add --ide kiro # base install to .kiro/skills/
|
|
59
|
+
npx @cometchat/skills add --ide replit # base install to .agents/skills/
|
|
60
|
+
npx @cometchat/skills add --ide copilot # base install to .github/copilot-instructions.md
|
|
61
|
+
npx @cometchat/skills add --ide all # write base install to every supported IDE
|
|
62
|
+
npx @cometchat/skills add --ide claude --family web # full web family install to .claude/skills/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Direct-write supports 10 agents: `claude`, `cursor`, `kiro`, `replit`, `copilot`, `continue`, `cline`, `aider`, `codex`, `gemini`. The interactive picker supports 55+ via the vercel-labs/skills ecosystem.
|
|
66
|
+
|
|
67
|
+
Pass `--no-picker` to force direct-write even in an interactive terminal.
|
|
28
68
|
|
|
29
69
|
> **Migrating from v3?** `npx @cometchat/skills-native add` still works (with a deprecation notice). New projects should use the unified command.
|
|
30
70
|
|
package/bin/install.js
CHANGED
|
@@ -6,6 +6,7 @@ const fs = require("fs");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const os = require("os");
|
|
8
8
|
const readline = require("readline");
|
|
9
|
+
const { spawn } = require("child_process");
|
|
9
10
|
|
|
10
11
|
// ── Lazy-load optional deps ───────────────────────────────────────────────────
|
|
11
12
|
function tryRequire(name) {
|
|
@@ -157,6 +158,26 @@ const SKILLS = [
|
|
|
157
158
|
{ name: "cometchat-ios-troubleshooting", families: ["ios"], description: "iOS troubleshooting: SPM, CocoaPods, Xcode build errors, Info.plist, runtime crashes" },
|
|
158
159
|
];
|
|
159
160
|
|
|
161
|
+
// ── Base skills (cross-family) ────────────────────────────────────────────────
|
|
162
|
+
//
|
|
163
|
+
// Skills registered for ALL six families. These are the baseline a dispatcher
|
|
164
|
+
// needs to detect framework and route — they're installed in the default
|
|
165
|
+
// interactive flow, before any family is resolved. The dispatcher (read at
|
|
166
|
+
// runtime in the user's IDE) detects the framework and asks the agent to
|
|
167
|
+
// install the relevant family skills via:
|
|
168
|
+
//
|
|
169
|
+
// npx @cometchat/skills add --family <X> --ide <Y>
|
|
170
|
+
//
|
|
171
|
+
// (Logic already in `skills/cometchat/SKILL.md` Step 1 — "If `framework` is
|
|
172
|
+
// X AND `cometchat-{X}-core` is NOT loaded: <install command>".)
|
|
173
|
+
//
|
|
174
|
+
// This list grows automatically as more cross-family skills are registered
|
|
175
|
+
// (e.g. when the calls work merges, cometchat-calls/i18n/a11y join).
|
|
176
|
+
const ALL_FAMILIES_SET = ["web", "native", "flutter", "angular", "android", "ios"];
|
|
177
|
+
const BASE_SKILLS = SKILLS.filter(s =>
|
|
178
|
+
ALL_FAMILIES_SET.every(f => s.families.includes(f))
|
|
179
|
+
);
|
|
180
|
+
|
|
160
181
|
// ── Framework → family routing ────────────────────────────────────────────────
|
|
161
182
|
const FRAMEWORK_TO_FAMILY = {
|
|
162
183
|
reactjs: "web",
|
|
@@ -384,13 +405,74 @@ function installCopilotSkills(skillsToInstall, baseDir) {
|
|
|
384
405
|
return dest;
|
|
385
406
|
}
|
|
386
407
|
|
|
408
|
+
// ── Multi-agent picker via vercel-labs/skills ────────────────────────────────
|
|
409
|
+
//
|
|
410
|
+
// Spawns `npx -y skills@<pin> add cometchat-team/cometchat-skills -s <names>`
|
|
411
|
+
// so the interactive multi-agent picker (Claude Code / Cursor / Codex /
|
|
412
|
+
// Cline / Kiro / Replit / 50+ agents) shows. The user picks which agents
|
|
413
|
+
// to write to; the skills CLI handles the per-agent path conventions.
|
|
414
|
+
//
|
|
415
|
+
// We pre-resolve the family-specific skill list (from `resolveFamilies` +
|
|
416
|
+
// SKILLS table) and pass the names via `-s name1 name2 ...` so the user
|
|
417
|
+
// only sees skills relevant to their detected family — not all 100+ in
|
|
418
|
+
// the marketplace.
|
|
419
|
+
//
|
|
420
|
+
// Pin: `skills@1.5.5` was the version verified against this codebase.
|
|
421
|
+
// Bump on intentional re-verification; pre-pin avoids breakage from a
|
|
422
|
+
// future major bump in the upstream CLI.
|
|
423
|
+
const SKILLS_CLI_PIN = "skills@1.5.5";
|
|
424
|
+
const SKILLS_REPO = "cometchat-team/cometchat-skills";
|
|
425
|
+
|
|
426
|
+
async function delegateToSkillsCli({ skills, families, isGlobal }) {
|
|
427
|
+
const skillNames = skills.map(s => s.name);
|
|
428
|
+
const familyLabel = families.includes("all") ? "all" : families.join("+");
|
|
429
|
+
|
|
430
|
+
console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} — ${c.bold(familyLabel)} family — ${skillNames.length} skills`);
|
|
431
|
+
console.log(` ${c.gray("Launching multi-agent picker via vercel-labs/skills...")}\n`);
|
|
432
|
+
|
|
433
|
+
const npxArgs = [
|
|
434
|
+
"-y", // auto-accept the npx install prompt for the skills CLI itself
|
|
435
|
+
SKILLS_CLI_PIN,
|
|
436
|
+
"add",
|
|
437
|
+
SKILLS_REPO,
|
|
438
|
+
"-s", ...skillNames, // space-separated skill names (skills CLI's flag shape)
|
|
439
|
+
];
|
|
440
|
+
if (isGlobal) npxArgs.push("-g");
|
|
441
|
+
|
|
442
|
+
return new Promise((resolve) => {
|
|
443
|
+
const child = spawn("npx", npxArgs, {
|
|
444
|
+
stdio: "inherit",
|
|
445
|
+
shell: false,
|
|
446
|
+
});
|
|
447
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
448
|
+
child.on("error", (err) => {
|
|
449
|
+
console.error(c.red(`\n ✗ Failed to spawn skills CLI: ${err.message}`));
|
|
450
|
+
console.error(c.dim(` Falling back to legacy direct-write — re-run with --ide <name> to bypass the picker.\n`));
|
|
451
|
+
resolve(2);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
387
456
|
function printHelp() {
|
|
388
457
|
console.log(`
|
|
389
458
|
${c.bold("@cometchat/skills")} — Install CometChat AI coding skills
|
|
390
459
|
|
|
391
460
|
${c.bold("Usage:")}
|
|
392
|
-
${c.cyan("npx @cometchat/skills add")}
|
|
393
|
-
${c.cyan("npx @cometchat/skills add --family <name>")}
|
|
461
|
+
${c.cyan("npx @cometchat/skills add")} Base install + interactive multi-agent picker
|
|
462
|
+
${c.cyan("npx @cometchat/skills add --family <name>")} Install full family upfront (no runtime expansion)
|
|
463
|
+
${c.cyan("npx @cometchat/skills add --ide <name>")} Direct-write base skills to one IDE (CI/scripted)
|
|
464
|
+
|
|
465
|
+
${c.bold("Two install shapes:")}
|
|
466
|
+
${c.bold("Base install")} (default — no --family flag): writes only the cross-family
|
|
467
|
+
skills (the cometchat dispatcher + cross-family helpers). Once installed,
|
|
468
|
+
open your project in your IDE and run /cometchat — the dispatcher detects
|
|
469
|
+
your framework and asks the agent to install the family-specific skills
|
|
470
|
+
via \`npx @cometchat/skills add --family <X> --ide <Y>\`. Smallest initial
|
|
471
|
+
install, dispatcher routes the rest. Recommended for most users.
|
|
472
|
+
${c.bold("Full family install")} (--family flag): writes the dispatcher + every
|
|
473
|
+
skill registered for that family (web=13, android=31, flutter=28, etc.)
|
|
474
|
+
upfront. Use when you know the project's framework and prefer all skills
|
|
475
|
+
present immediately. Used by power users + CI smoke tests.
|
|
394
476
|
|
|
395
477
|
${c.bold("Family values:")}
|
|
396
478
|
${c.cyan("web")} React / Next.js / React Router / Astro
|
|
@@ -401,7 +483,7 @@ function printHelp() {
|
|
|
401
483
|
${c.cyan("ios")} iOS native (V5 stable)
|
|
402
484
|
${c.cyan("all")} Install every skill (legacy v3 behavior)
|
|
403
485
|
|
|
404
|
-
${c.bold("IDE selection (default: claude):")}
|
|
486
|
+
${c.bold("IDE selection (direct-write mode only — default: claude):")}
|
|
405
487
|
${c.cyan("--ide cursor")} ${c.cyan("--ide kiro")} ${c.cyan("--ide copilot")} ${c.cyan("--ide replit")} ${c.cyan("--ide all")}
|
|
406
488
|
|
|
407
489
|
${c.bold("Multi-family / monorepo:")}
|
|
@@ -409,8 +491,9 @@ function printHelp() {
|
|
|
409
491
|
|
|
410
492
|
${c.bold("Other:")}
|
|
411
493
|
${c.cyan("--global")} Install globally (~/.claude/skills/, etc.)
|
|
412
|
-
${c.cyan("--clean")} Wipe existing cometchat-* skill dirs before install
|
|
494
|
+
${c.cyan("--clean")} Wipe existing cometchat-* skill dirs before install (direct-write mode only)
|
|
413
495
|
${c.cyan("--list")} Show every skill with its family tags
|
|
496
|
+
${c.cyan("--no-picker")} Force direct-write even in interactive TTY (useful for testing the legacy path)
|
|
414
497
|
|
|
415
498
|
${c.bold("After installing, open your project in your IDE and run:")}
|
|
416
499
|
${c.cyan("/cometchat")}
|
|
@@ -448,37 +531,88 @@ async function main() {
|
|
|
448
531
|
process.exit(0);
|
|
449
532
|
}
|
|
450
533
|
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
//
|
|
455
|
-
//
|
|
534
|
+
// ── Install mode selection ─────────────────────────────────────────────
|
|
535
|
+
//
|
|
536
|
+
// Three valid invocation shapes:
|
|
537
|
+
//
|
|
538
|
+
// 1. `npx @cometchat/skills add` (TTY, no --family, no --ide)
|
|
539
|
+
// → BASE INSTALL: install only cross-family skills (cometchat
|
|
540
|
+
// dispatcher + cometchat-calls + i18n + a11y) via the multi-agent
|
|
541
|
+
// picker. The dispatcher detects the framework AT RUNTIME inside
|
|
542
|
+
// the user's IDE and asks the agent to install family-specific
|
|
543
|
+
// skills via `npx @cometchat/skills add --family <X> --ide <Y>`.
|
|
544
|
+
// Smallest initial install; routing is the dispatcher's job.
|
|
545
|
+
//
|
|
546
|
+
// 2. `npx @cometchat/skills add --family <X>` (TTY)
|
|
547
|
+
// → FAMILY INSTALL: install the dispatcher + every skill registered
|
|
548
|
+
// for family X (web=13, android=31, etc.) via the picker. Use this
|
|
549
|
+
// when you know upfront which family you want and prefer all
|
|
550
|
+
// skills present immediately.
|
|
551
|
+
//
|
|
552
|
+
// 3. `npx @cometchat/skills add --ide <Y>` (or non-TTY, e.g. CI)
|
|
553
|
+
// → DIRECT WRITE: skips the picker and writes directly to one IDE's
|
|
554
|
+
// directory. With --family, writes the family subset; without, falls
|
|
555
|
+
// back to BASE skills (CI smoke). Used by Dockerfiles and the
|
|
556
|
+
// legacy single-agent flow.
|
|
557
|
+
const ideExplicit = ideIdx !== -1;
|
|
558
|
+
const familyExplicit = args.includes("--family");
|
|
559
|
+
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
560
|
+
const skipPicker = ideExplicit || !isTTY || args.includes("--no-picker");
|
|
561
|
+
|
|
562
|
+
// Resolve skill set based on mode:
|
|
563
|
+
// - --family X → install the dispatcher + every skill registered for X
|
|
564
|
+
// (legacy power-user / CI flow; everything present immediately)
|
|
565
|
+
// - No --family (with or without --ide) → BASE install only. The
|
|
566
|
+
// dispatcher handles family-specific install at runtime via its own
|
|
567
|
+
// `npx @cometchat/skills add --family <X>` invocation logic.
|
|
456
568
|
let skillsToInstall;
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
569
|
+
let families;
|
|
570
|
+
if (familyExplicit) {
|
|
571
|
+
families = await resolveFamilies(args);
|
|
572
|
+
if (families.includes("all")) {
|
|
573
|
+
skillsToInstall = SKILLS;
|
|
574
|
+
} else {
|
|
575
|
+
const seen = new Set();
|
|
576
|
+
skillsToInstall = [];
|
|
577
|
+
for (const fam of families) {
|
|
578
|
+
for (const s of SKILLS) {
|
|
579
|
+
if (s.families.includes(fam) && !seen.has(s.name)) {
|
|
580
|
+
seen.add(s.name);
|
|
581
|
+
skillsToInstall.push(s);
|
|
582
|
+
}
|
|
467
583
|
}
|
|
468
584
|
}
|
|
469
585
|
}
|
|
586
|
+
|
|
587
|
+
// If no pattern skills matched (only the dispatcher), all selected
|
|
588
|
+
// families are "coming soon" — bail with a friendly message rather than
|
|
589
|
+
// installing a half-broken set.
|
|
590
|
+
const patternSkills = skillsToInstall.filter(s => s.name !== "cometchat");
|
|
591
|
+
if (patternSkills.length === 0) {
|
|
592
|
+
const labels = families.map(f => FAMILY_LABELS[f] || f).join(", ");
|
|
593
|
+
console.log(c.yellow(`\n ⚠ Pattern skills for ${labels} aren't published yet.`));
|
|
594
|
+
console.log(` Supported families today: ${c.cyan("web")}, ${c.cyan("native")}, ${c.cyan("angular")}, ${c.cyan("android")}, ${c.cyan("flutter")}, ${c.cyan("ios")}.`);
|
|
595
|
+
console.log(` Run with one of those, or wait for ${families.join(" + ")} skills to ship.\n`);
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
// Default: base install only (whether interactive or with --ide).
|
|
600
|
+
// The dispatcher detects the framework at runtime and installs the
|
|
601
|
+
// family-specific skills via its own runtime npx invocation.
|
|
602
|
+
skillsToInstall = BASE_SKILLS;
|
|
603
|
+
families = ["base"];
|
|
604
|
+
console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} ${c.dim("(base install — dispatcher routes the rest at runtime)")}`);
|
|
605
|
+
console.log(` ${c.gray(`Installing ${BASE_SKILLS.length} cross-family ${BASE_SKILLS.length === 1 ? "skill" : "skills"}: ${BASE_SKILLS.map(s => s.name).join(", ")}.`)}`);
|
|
606
|
+
console.log(` ${c.gray("After install, open your project in your IDE → /cometchat detects your framework and asks the agent to install family-specific skills on demand.")}\n`);
|
|
470
607
|
}
|
|
471
608
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
console.log(` Supported families today: ${c.cyan("web")}, ${c.cyan("native")}, ${c.cyan("angular")}, ${c.cyan("android")}, ${c.cyan("flutter")}, ${c.cyan("ios")}.`);
|
|
480
|
-
console.log(` Run with one of those, or wait for ${families.join(" + ")} skills to ship.\n`);
|
|
481
|
-
process.exit(1);
|
|
609
|
+
if (!skipPicker) {
|
|
610
|
+
const exitCode = await delegateToSkillsCli({
|
|
611
|
+
skills: skillsToInstall,
|
|
612
|
+
families,
|
|
613
|
+
isGlobal,
|
|
614
|
+
});
|
|
615
|
+
process.exit(exitCode);
|
|
482
616
|
}
|
|
483
617
|
|
|
484
618
|
const targets = ideArg === "all" ? Object.keys(IDE_TARGETS) : [ideArg];
|
package/package.json
CHANGED
|
@@ -141,6 +141,55 @@ Save the choice into `.cometchat/config.json` under `flutter_version`.
|
|
|
141
141
|
|
|
142
142
|
Store this mental map — you'll use it throughout the conversation.
|
|
143
143
|
|
|
144
|
+
#### Then show the user what you found — Step 1.5 (the "I see you" moment)
|
|
145
|
+
|
|
146
|
+
This is the most important moment of the whole flow. After running detection + reading the project, narrate what you found in **3–5 specific, observation-grounded bullets** BEFORE asking any question. The user should feel that you understand their project before deciding whether to trust you with it.
|
|
147
|
+
|
|
148
|
+
The shape (use it verbatim — the structure earns trust):
|
|
149
|
+
|
|
150
|
+
> Taking a look at your project...
|
|
151
|
+
>
|
|
152
|
+
> - **{Framework} + {Build tool} {version}** {with TypeScript / JavaScript / etc., as detected}
|
|
153
|
+
> - **{Router or nav state}** — {one observation about how routing is set up, or "no router yet" for greenfield}
|
|
154
|
+
> - **{Auth system status}** — {"NextAuth detected → I'll wire token-based login", or "no auth detected → I'll start with dev mode + a test user; you can upgrade later"}
|
|
155
|
+
> - **{Existing CometChat state}** — {"existing `cometchat/` folder with X — I'll patch around it", or "fresh start — no prior CometChat code"}
|
|
156
|
+
> - **{One personal observation}** — {something specific you noticed: "Tailwind classes throughout", "shadcn/ui components", "monorepo with apps/* and packages/*"}
|
|
157
|
+
>
|
|
158
|
+
> Ready to set this up? I'll walk you through account setup, then ask where chat should live.
|
|
159
|
+
|
|
160
|
+
**The rules for this moment:**
|
|
161
|
+
|
|
162
|
+
1. **Be specific, not generic.** "Vite + React 19 + TypeScript" beats "a React project." Read the actual versions from `package.json`.
|
|
163
|
+
2. **Five bullets max.** Beyond five, it stops feeling observational and starts feeling like a recital. Cut to what's *interesting* about the project.
|
|
164
|
+
3. **Lead with what's load-bearing.** Framework + version, router, auth, existing CometChat, then one personal touch. The personal touch is what makes it land — show you actually looked.
|
|
165
|
+
4. **Skip the bullet if there's nothing to say.** No router on greenfield → say "no router yet"; don't invent one. No auth → say "no auth detected"; don't list every package you didn't find.
|
|
166
|
+
5. **End with a single confident question.** Not five questions. The flow continues into Step 2 (credentials) or Step 3 (placement) — let the next step ask.
|
|
167
|
+
|
|
168
|
+
**Examples of good vs bad bullets:**
|
|
169
|
+
|
|
170
|
+
| ✓ Good (specific, observational) | ✗ Bad (generic, unfounded) |
|
|
171
|
+
|---|---|
|
|
172
|
+
| "Vite + React 19 + TypeScript, Tailwind for styling" | "A React project with TypeScript" |
|
|
173
|
+
| "React Router v7 detected (`routes.ts` + `react-router.config.ts`)" | "Some routing is configured" |
|
|
174
|
+
| "NextAuth in `auth.config.ts` — I'll mint CometChat tokens server-side via your existing session cookie" | "Authentication is set up" |
|
|
175
|
+
| "shadcn/ui detected (`components/ui/*`) — I'll use your existing Button + Dialog primitives in the chat trigger" | "Some UI components are present" |
|
|
176
|
+
| "Monorepo: `apps/web` is your dashboard, `apps/marketing` is the public site — I'll integrate into apps/web" | "This is a monorepo" |
|
|
177
|
+
|
|
178
|
+
**For greenfield projects (the test case):**
|
|
179
|
+
|
|
180
|
+
> Taking a look at your project...
|
|
181
|
+
>
|
|
182
|
+
> - **Vite + React 19 + TypeScript** — fresh `cometchat-test-app` scaffold
|
|
183
|
+
> - **No router yet** — for the demo, chat will mount in `src/App.tsx` directly; we can move it to a route later
|
|
184
|
+
> - **No auth system detected** — I'll set you up in dev mode with a pre-seeded test user (`cometchat-uid-1`); production auth is a one-flag upgrade later
|
|
185
|
+
> - **Fresh start** — no existing CometChat code to patch around
|
|
186
|
+
>
|
|
187
|
+
> Ready to set this up? I'll get you a CometChat account first, then ask where chat should live.
|
|
188
|
+
|
|
189
|
+
This moment costs ~5 seconds of conversation but anchors the rest. Skip it and the user feels like they're talking to a script. Run it well and the rest of the flow feels effortless.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
144
193
|
**Compatibility baselines (the CLI enforces these):**
|
|
145
194
|
- Web: react@<18 → upgrade required; nextjs@<13 → warning; astro@<4 → warning
|
|
146
195
|
- RN: react-native@<0.70 → upgrade required; expo@<49 → upgrade required
|
|
@@ -614,29 +663,46 @@ Otherwise, use `AskUserQuestion`:
|
|
|
614
663
|
|
|
615
664
|
#### 3b. Show what you recommend and why
|
|
616
665
|
|
|
617
|
-
|
|
666
|
+
This is the second wow moment after the detection summary. Don't just list a placement — **tell the user why**. Two sentences of reasoning earn confidence; a table alone reads like a lookup.
|
|
667
|
+
|
|
668
|
+
The recommendation has two layers:
|
|
669
|
+
|
|
670
|
+
1. **The placement** — what you'll set up (route / drawer / modal / tab / widget)
|
|
671
|
+
2. **The reason** — why this fits the user's archetype (one or two sentences grounded in how their kind of app actually works)
|
|
672
|
+
|
|
673
|
+
When you write your response, lead with the reason, then the concrete placement, then offer to override:
|
|
674
|
+
|
|
675
|
+
> **For a marketplace app, I'd put a "Chat with seller" drawer on your product page + an inbox at `/messages`.**
|
|
676
|
+
>
|
|
677
|
+
> The drawer keeps buyers in the buying flow — they can ask a question without losing the listing. The inbox is for going back to past conversations. Two surfaces, one integration.
|
|
678
|
+
>
|
|
679
|
+
> Sound right, or want to try a different shape?
|
|
680
|
+
|
|
681
|
+
That's the shape. The recommendation tables below are the *what*; the reasoning column is the *why* you should narrate.
|
|
618
682
|
|
|
619
683
|
**Web family (reactjs, nextjs, react-router, astro):**
|
|
620
684
|
|
|
621
|
-
| Intent |
|
|
622
|
-
|
|
623
|
-
| **Messaging app** |
|
|
624
|
-
| **Marketplace** |
|
|
625
|
-
| **SaaS / dashboard** |
|
|
626
|
-
| **Social / community** |
|
|
627
|
-
| **Support** |
|
|
685
|
+
| Intent | Placement | Why |
|
|
686
|
+
|---|---|---|
|
|
687
|
+
| **Messaging app** | Dedicated messages page (route you pick), two-pane: conversation list + active chat | Chat IS the product. Users land directly on it; the route is the home of your app. |
|
|
688
|
+
| **Marketplace** | "Chat with seller" drawer on the product page + inbox at `/messages` | Drawer keeps buyers in the buying flow; the inbox handles "go back to a past conversation." |
|
|
689
|
+
| **SaaS / dashboard** | Modal triggered from your navbar + full messages page | Modal feels lightweight (chat without leaving your work); the page is for serious conversations. |
|
|
690
|
+
| **Social / community** | Full messenger page with tabs: Chats, Calls, Users, Groups | Discovery matters as much as messaging — users want to find people, not just their existing threads. |
|
|
691
|
+
| **Support** | Floating widget bubble in the bottom-right | One-way customer-to-team — minimal cognitive load on the customer; your team triages from the dashboard. |
|
|
628
692
|
|
|
629
693
|
**React Native family (expo, react-native):**
|
|
630
694
|
|
|
631
|
-
| Intent |
|
|
632
|
-
|
|
633
|
-
| **Messaging app** |
|
|
634
|
-
| **Marketplace** |
|
|
635
|
-
| **SaaS / productivity** |
|
|
636
|
-
| **Social / community** |
|
|
637
|
-
| **Support** |
|
|
695
|
+
| Intent | Placement | Why |
|
|
696
|
+
|---|---|---|
|
|
697
|
+
| **Messaging app** | Dedicated "Messages" bottom tab → conversations → thread | Mobile users expect chat as a first-class destination; a tab puts it one tap away. |
|
|
698
|
+
| **Marketplace** | "Chat with seller" button on the product screen → modal thread + Inbox stack screen | Modal preserves the buying context; the Inbox is the "back to a conversation" entry point. |
|
|
699
|
+
| **SaaS / productivity** | "Chat" stack screen accessible from nav, optionally bottom sheet for quick replies | Stack screen for focused conversations; bottom sheet for fast back-and-forth without leaving your current work. |
|
|
700
|
+
| **Social / community** | "Messages" bottom tab + "Message" button on profile screens → modal thread | Tab handles discovery; per-profile button is the "I want to talk to THIS person" path. |
|
|
701
|
+
| **Support** | Modal triggered from a "Help" or "Support" button in header/settings | Lightweight, doesn't compete with your product's primary tabs. |
|
|
638
702
|
|
|
639
|
-
When explaining, reference the ASCII diagrams from `cometchat-placement` (web) or `cometchat-native-placement` (RN) so the user can visualize.
|
|
703
|
+
When explaining, reference the ASCII diagrams from `cometchat-placement` (web) or `cometchat-native-placement` (RN) so the user can visualize the shape.
|
|
704
|
+
|
|
705
|
+
**One sentence to hand them control:** end the recommendation with "Sound right, or want to try a different shape?" — never "Which would you like?" The first phrasing implies you've thought it through and they can override; the second implies you're just collecting answers.
|
|
640
706
|
|
|
641
707
|
Ask: "Does this sound right, or do you want a different approach?" Let them override.
|
|
642
708
|
|
|
@@ -758,60 +824,132 @@ If the user has auth AND wants to set up production mode now:
|
|
|
758
824
|
|
|
759
825
|
If they share an example, validate it's CometChat-compatible (alphanumeric, underscores, hyphens — no spaces or special chars; max 100 chars). Firebase UIDs, Clerk user IDs, Supabase UUIDs, and Auth0 `sub` claims are all CometChat-compatible by default.
|
|
760
826
|
|
|
761
|
-
#### 3f. Confirm the plan
|
|
827
|
+
#### 3f. Confirm the plan — the third wow moment
|
|
828
|
+
|
|
829
|
+
**This is the trust contract. Show EXACTLY what you'll do BEFORE doing it.** Three sections, in this order:
|
|
830
|
+
|
|
831
|
+
1. **Files I'll create** — new files, with a one-line purpose for each
|
|
832
|
+
2. **Files I'll modify** — existing files, with the specific edit (not "wrap with provider" alone — say "wrap the children of `<Layout>` with `<CometChatProvider>` at line ~14")
|
|
833
|
+
3. **Files I won't touch** — call out the load-bearing files that stay untouched (auth config, route definitions outside the chat surface, your existing components). This is the reassurance.
|
|
834
|
+
|
|
835
|
+
Then dependencies + auth mode + an approval line that hands the user control.
|
|
836
|
+
|
|
837
|
+
The shape (use it verbatim — three sections + reassurance):
|
|
838
|
+
|
|
839
|
+
> Here's the plan:
|
|
840
|
+
>
|
|
841
|
+
> **Files I'll create**
|
|
842
|
+
> - `cometchat/CometChatProvider.tsx` — wraps the kit's auth + theme providers, gates render on login
|
|
843
|
+
> - `cometchat/init.ts` — module-level CometChat.init + login, called from the provider
|
|
844
|
+
> - `app/messages/page.tsx` — full-page messages route (your inbox)
|
|
845
|
+
> - `app/components/ChatDrawer.tsx` — the "chat with seller" drawer for product pages
|
|
846
|
+
> - `.env.local` — your CometChat App ID + Region + Auth Key (gitignored)
|
|
847
|
+
>
|
|
848
|
+
> **Files I'll modify**
|
|
849
|
+
> - `app/layout.tsx` — wrap `{children}` with `<CometChatProvider>` (one line, around line 14)
|
|
850
|
+
> - `app/products/[id]/page.tsx` — add the `<ChatDrawer />` trigger button next to the seller info (your existing layout stays)
|
|
851
|
+
> - `app/components/Navbar.tsx` — add a `<Link href="/messages">Messages</Link>` next to your existing nav items
|
|
852
|
+
>
|
|
853
|
+
> **Files I won't touch**
|
|
854
|
+
> - `auth.config.ts` — your NextAuth setup stays as-is; we'll wire CometChat to it in production-auth mode later
|
|
855
|
+
> - `tailwind.config.ts`, `globals.css` — no styling changes outside `cometchat/`
|
|
856
|
+
> - Anything under `app/products/`, `app/cart/`, etc. — your existing routes are untouched
|
|
857
|
+
>
|
|
858
|
+
> **Dependencies**
|
|
859
|
+
> `@cometchat/chat-sdk-javascript`, `@cometchat/chat-uikit-react`
|
|
860
|
+
>
|
|
861
|
+
> **Auth mode** Development (Auth Key for now; production auth is a one-flag upgrade later)
|
|
862
|
+
>
|
|
863
|
+
> **Estimated time** ~30 seconds to write the code, ~1 minute for `npm install` to finish.
|
|
864
|
+
>
|
|
865
|
+
> If anything looks off, just tell me what to change. Otherwise, say "go" and I'll write it.
|
|
866
|
+
|
|
867
|
+
**The rules for this moment:**
|
|
762
868
|
|
|
763
|
-
**
|
|
869
|
+
1. **Be specific in the modify section.** "Wrap with provider" is vague. "Wrap `{children}` with `<CometChatProvider>` at line ~14" tells the user exactly what to expect when they git-diff later.
|
|
870
|
+
2. **List the don't-touch files explicitly.** Users worry about agents stomping their auth config, their tailwind, their routes. Naming what stays untouched defuses that worry up front.
|
|
871
|
+
3. **Estimated time matters.** Two short numbers — a few seconds to write code, a couple minutes for `npm install`. Sets expectations; reduces "is it stuck?" mid-flow.
|
|
872
|
+
4. **End with a hand-off, not a yes/no.** "Say 'go' and I'll write it. Or tell me what to change." Beats "Proceed? [y/n]" — it implies the user can adjust without throwing the whole plan away.
|
|
873
|
+
5. **Never abbreviate the plan in subsequent runs.** Every integration deserves a fresh, full plan. If the user has been through this before, they can skim — but don't pre-skim for them.
|
|
764
874
|
|
|
765
|
-
**Web example (Next.js, marketplace):**
|
|
766
|
-
|
|
875
|
+
**Web example (Next.js + NextAuth, marketplace):** see the shape above.
|
|
876
|
+
|
|
877
|
+
**Web example (Vite + React, greenfield messaging app):**
|
|
878
|
+
|
|
879
|
+
> Here's the plan:
|
|
880
|
+
>
|
|
881
|
+
> **Files I'll create**
|
|
882
|
+
> - `src/cometchat/CometChatProvider.tsx` — wraps the kit's providers, gates render on login
|
|
883
|
+
> - `src/cometchat/init.ts` — `CometChat.init` + `CometChat.login` (with `cometchat-uid-1` for dev)
|
|
884
|
+
> - `src/components/ChatScreen.tsx` — your full-page chat surface (conversations + messages)
|
|
885
|
+
> - `.env` — your CometChat App ID + Region + Auth Key (gitignored)
|
|
886
|
+
>
|
|
887
|
+
> **Files I'll modify**
|
|
888
|
+
> - `src/main.tsx` — wrap `<App />` with `<CometChatProvider>` (line ~10)
|
|
889
|
+
> - `src/App.tsx` — render `<ChatScreen />` instead of the Vite default (you can move it to a route later)
|
|
767
890
|
>
|
|
768
|
-
> **
|
|
769
|
-
> - `
|
|
770
|
-
> - `
|
|
771
|
-
> - `app/components/ChatDrawer.tsx`
|
|
772
|
-
> - `.env.local`
|
|
891
|
+
> **Files I won't touch**
|
|
892
|
+
> - `vite.config.ts`, `tsconfig.*` — no build config changes
|
|
893
|
+
> - `src/index.css`, `src/App.css` — kit ships its own CSS; your styles stay
|
|
773
894
|
>
|
|
774
|
-
> **
|
|
775
|
-
> - `app/products/[id]/page.tsx` — add ChatDrawer trigger
|
|
776
|
-
> - `app/layout.tsx` — wrap with CometChatProvider
|
|
777
|
-
> - `app/components/Navbar.tsx` — add 'Messages' link
|
|
895
|
+
> **Dependencies** `@cometchat/chat-sdk-javascript`, `@cometchat/chat-uikit-react`
|
|
778
896
|
>
|
|
779
|
-
> **
|
|
897
|
+
> **Auth mode** Development (Auth Key + `cometchat-uid-1`; you can log in as `cometchat-uid-1`–`uid-5` to chat with yourself across browser windows)
|
|
780
898
|
>
|
|
781
|
-
> **
|
|
782
|
-
>
|
|
899
|
+
> **Estimated time** ~30 seconds to write, ~1 minute for `npm install`.
|
|
900
|
+
>
|
|
901
|
+
> Say "go" or tell me what to change.
|
|
783
902
|
|
|
784
903
|
**RN example (Expo Router, messaging):**
|
|
785
|
-
|
|
904
|
+
|
|
905
|
+
> Here's the plan:
|
|
906
|
+
>
|
|
907
|
+
> **Files I'll create**
|
|
908
|
+
> - `cometchat/CometChatProvider.tsx` — four-wrapper chain (gesture handler → safe area → theme → CometChat)
|
|
909
|
+
> - `cometchat/init.ts` — init + login, module-level guard
|
|
910
|
+
> - `app/(tabs)/messages.tsx` — your Messages tab
|
|
911
|
+
> - `.env` — `EXPO_PUBLIC_COMETCHAT_APP_ID` + region + auth key
|
|
786
912
|
>
|
|
787
|
-
> **
|
|
788
|
-
> - `
|
|
789
|
-
> - `app/(tabs)/
|
|
790
|
-
> -
|
|
913
|
+
> **Files I'll modify**
|
|
914
|
+
> - `app/_layout.tsx` — wrap with the four-wrapper chain (line ~10)
|
|
915
|
+
> - `app/(tabs)/_layout.tsx` — add the Messages tab as the third entry, after Home and Profile
|
|
916
|
+
> - `index.js` — `import 'react-native-gesture-handler'` at line 1 (mandatory; without it release builds break silently)
|
|
791
917
|
>
|
|
792
|
-
> **Files I'
|
|
793
|
-
> - `app
|
|
794
|
-
> - `app/(tabs)/
|
|
795
|
-
> - `index.js` — `import 'react-native-gesture-handler'` at line 1 (if missing)
|
|
918
|
+
> **Files I won't touch**
|
|
919
|
+
> - `app.json` — no Expo config changes for dev mode (production push needs them, but that's later)
|
|
920
|
+
> - Your existing `app/(tabs)/index.tsx`, `profile.tsx` — stay as-is
|
|
796
921
|
>
|
|
797
|
-
> **Dependencies (via `npx expo install`)
|
|
798
|
-
>
|
|
799
|
-
> react-native-gesture-handler, react-native-reanimated,
|
|
800
|
-
> react-native-safe-area-context, react-native-screens,
|
|
801
|
-
> @react-native-async-storage/async-storage, @react-native-community/netinfo,
|
|
802
|
-
> react-native-video, react-native-image-picker, react-native-document-picker,
|
|
803
|
-
> react-native-vector-icons, react-native-fs
|
|
922
|
+
> **Dependencies (via `npx expo install`)**
|
|
923
|
+
> `@cometchat/chat-uikit-react-native`, `@cometchat/chat-sdk-react-native`, `react-native-gesture-handler`, `react-native-reanimated`, `react-native-safe-area-context`, `react-native-screens`, `@react-native-async-storage/async-storage`, `@react-native-community/netinfo`, `react-native-video`, `react-native-image-picker`, `react-native-document-picker`, `react-native-vector-icons`, `react-native-fs`
|
|
804
924
|
>
|
|
805
|
-
> **Auth mode
|
|
806
|
-
>
|
|
925
|
+
> **Auth mode** Development (Auth Key).
|
|
926
|
+
>
|
|
927
|
+
> **Estimated time** ~30 seconds to write, ~3 minutes for `expo install` (RN deps are heavier).
|
|
928
|
+
>
|
|
929
|
+
> Say "go" or tell me what to change.
|
|
807
930
|
|
|
808
|
-
**Bare RN variant** — same as Expo, except
|
|
809
|
-
-
|
|
810
|
-
-
|
|
931
|
+
**Bare RN variant** — same as Expo, except:
|
|
932
|
+
- `npm install` instead of `npx expo install`
|
|
933
|
+
- Run `cd ios && pod install` (~1-2 min extra)
|
|
934
|
+
- Patch `ios/<Name>/Info.plist`, `android/app/src/main/AndroidManifest.xml` for camera/mic permissions
|
|
811
935
|
- Add `ios/<Name>/PrivacyInfo.xcprivacy` (Apple Privacy Manifest)
|
|
812
936
|
- Patch `android/build.gradle` for the async-storage Maven repo (v3+)
|
|
813
937
|
|
|
814
|
-
|
|
938
|
+
Surface these in the "Files I'll modify" section so the user knows they're coming.
|
|
939
|
+
|
|
940
|
+
**After approval — the writing moment:**
|
|
941
|
+
|
|
942
|
+
When the user says "go", narrate progress as you work. Don't be silent for 30 seconds while you write 5 files. Brief structured updates, one per beat:
|
|
943
|
+
|
|
944
|
+
> ✓ Created `cometchat/CometChatProvider.tsx`
|
|
945
|
+
> ✓ Created `cometchat/init.ts`
|
|
946
|
+
> ✓ Modified `src/main.tsx` (wrapped App with provider)
|
|
947
|
+
> ✓ Wrote `.env` (Auth Key hidden)
|
|
948
|
+
> Installing dependencies (this takes ~1 minute)...
|
|
949
|
+
|
|
950
|
+
The structured beats make the writing feel like a contract being executed, not a black box churning.
|
|
951
|
+
|
|
952
|
+
**If the user says no or wants changes:** go back to the relevant question and re-ask. Don't try to negotiate the plan in-line — the plan is atomic. Adjust the source decision, then regenerate the plan.
|
|
815
953
|
|
|
816
954
|
### Step 4 — Reference pattern skills
|
|
817
955
|
|
|
@@ -1278,6 +1278,34 @@ These rules apply to ALL placement patterns. Violating any of them causes integr
|
|
|
1278
1278
|
|
|
1279
1279
|
6. **Every CometChat container must have explicit dimensions.** Components fill 100% of their parent. If the parent has no height, the components collapse to zero. Always set `height`, `min-height`, or use flex/grid layout with a bounded container.
|
|
1280
1280
|
|
|
1281
|
+
6a. **Flex parents that hold a `CometChatMessageList` MUST set `minHeight: 0`** (and `minWidth: 0` for horizontal flex parents). The W3C default `min-height: auto` makes flex children refuse to shrink below their intrinsic content size — so once the conversation grows past the viewport, the list pushes the composer below the fold and the layout breaks. **This is the canonical chat-layout bug** — works fine for short conversations, breaks for long ones. The fix is one CSS property; the diagnosis takes hours if you don't know the rule.
|
|
1282
|
+
|
|
1283
|
+
```tsx
|
|
1284
|
+
/* ✓ CORRECT — header + list + composer with the flex-shrink trap fixed */
|
|
1285
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
1286
|
+
<div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
|
|
1287
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
1288
|
+
<CometChatMessageHeader user={user} />
|
|
1289
|
+
</div>
|
|
1290
|
+
<div style={{ flex: "1 1 0", minHeight: 0, overflow: "hidden" }}>
|
|
1291
|
+
<CometChatMessageList user={user} hideReplyInThreadOption />
|
|
1292
|
+
</div>
|
|
1293
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
1294
|
+
<CometChatMessageComposer user={user} />
|
|
1295
|
+
</div>
|
|
1296
|
+
</div>
|
|
1297
|
+
</div>
|
|
1298
|
+
|
|
1299
|
+
/* ✗ WRONG — list grows past the viewport once messages exceed visible area */
|
|
1300
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
1301
|
+
<CometChatMessageHeader user={user} />
|
|
1302
|
+
<CometChatMessageList user={user} /> {/* takes intrinsic height, no scroll */}
|
|
1303
|
+
<CometChatMessageComposer user={user} /> {/* falls off the bottom */}
|
|
1304
|
+
</div>
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
The wrap-each-component-in-a-flex-sized-div pattern is what makes this work. Don't pass the kit components directly as flex children — wrap them.
|
|
1308
|
+
|
|
1281
1309
|
7. **Resolve target users/groups before rendering CometChat components.** Use `CometChat.getUser(uid)` or `CometChat.getGroup(guid)` to get the full `CometChat.User` or `CometChat.Group` object. Do not pass a raw UID string to `user` props -- they expect object instances.
|
|
1282
1310
|
|
|
1283
1311
|
8. **For SSR frameworks, wrap CometChat components appropriately.** See the `cometchat-core` skill, section 5 (SSR safety), for framework-specific patterns.
|
|
@@ -468,30 +468,78 @@ Vite's HMR replaces modules without a full page reload. CometChat's SDK holds a
|
|
|
468
468
|
|
|
469
469
|
However, if you change the provider file itself during development, HMR may re-execute the module. The `initialized` flag prevents double-init, but the WebSocket connection from the previous module instance may linger. If you see duplicate messages or connection issues during development, do a full page reload (`Ctrl+Shift+R`).
|
|
470
470
|
|
|
471
|
-
### Container height
|
|
471
|
+
### Container height (and the flex-shrink trap that breaks chat layouts)
|
|
472
472
|
|
|
473
|
-
CometChat components fill 100% of their container.
|
|
473
|
+
CometChat components fill 100% of their container. Two visual bugs to avoid:
|
|
474
|
+
|
|
475
|
+
**Bug 1 — zero height:** components render with zero height because the container has no explicit dimensions.
|
|
476
|
+
**Bug 2 — message list grows past the viewport:** the list scrolls fine until it has too many messages, then pushes the composer below the fold. **This is the classic flex-shrink trap.**
|
|
477
|
+
|
|
478
|
+
The hard rule for two-pane / header+list+composer layouts: every flex container in the chain MUST have `minHeight: 0` (and `minWidth: 0` for horizontal flex). Without it, browsers default flex-children to `min-height: auto` (their intrinsic content size), so the list grows beyond the parent's bounds as messages accumulate.
|
|
474
479
|
|
|
475
480
|
```tsx
|
|
476
|
-
/* CORRECT: explicit height */
|
|
481
|
+
/* ✓ CORRECT: explicit height for a single-component surface */
|
|
477
482
|
<div style={{ height: "100vh" }}>
|
|
478
483
|
<CometChatConversations ... />
|
|
479
484
|
</div>
|
|
480
485
|
|
|
481
|
-
/* CORRECT:
|
|
486
|
+
/* ✓ CORRECT: header + list + composer with the flex-shrink trap fixed */
|
|
482
487
|
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
483
488
|
<nav>...</nav>
|
|
484
|
-
<div style={{
|
|
485
|
-
|
|
489
|
+
<div style={{
|
|
490
|
+
flex: 1,
|
|
491
|
+
display: "flex",
|
|
492
|
+
flexDirection: "column",
|
|
493
|
+
minHeight: 0, // ← THIS IS THE HARD RULE — without it, list grows past the viewport
|
|
494
|
+
}}>
|
|
495
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
496
|
+
<CometChatMessageHeader user={user} />
|
|
497
|
+
</div>
|
|
498
|
+
<div style={{ flex: "1 1 0", minHeight: 0, overflow: "hidden" }}>
|
|
499
|
+
<CometChatMessageList user={user} />
|
|
500
|
+
</div>
|
|
501
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
502
|
+
<CometChatMessageComposer user={user} />
|
|
503
|
+
</div>
|
|
486
504
|
</div>
|
|
487
505
|
</div>
|
|
488
506
|
|
|
489
|
-
/*
|
|
507
|
+
/* ✓ CORRECT: two-pane (sidebar + active chat) — both axes need min-{width,height}: 0 */
|
|
508
|
+
<div style={{ display: "flex", height: "100vh" }}>
|
|
509
|
+
<div style={{ width: 360, display: "flex", flexDirection: "column" }}>
|
|
510
|
+
<CometChatConversations onItemClick={...} />
|
|
511
|
+
</div>
|
|
512
|
+
<div style={{
|
|
513
|
+
flex: 1,
|
|
514
|
+
display: "flex",
|
|
515
|
+
flexDirection: "column",
|
|
516
|
+
minWidth: 0, // ← horizontal flex parent: prevents content from forcing horizontal scroll
|
|
517
|
+
minHeight: 0, // ← vertical flex (the inner column): prevents the list-overflow bug
|
|
518
|
+
}}>
|
|
519
|
+
{/* header / list / composer wrapped as above */}
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
/* ✗ WRONG: no height constraint — component collapses to zero */
|
|
490
524
|
<div>
|
|
491
525
|
<CometChatConversations ... />
|
|
492
526
|
</div>
|
|
527
|
+
|
|
528
|
+
/* ✗ WRONG: flex parent without minHeight: 0 — list grows past the viewport once
|
|
529
|
+
you accumulate more messages than fit on-screen. Composer falls off the bottom. */
|
|
530
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
531
|
+
<CometChatMessageHeader user={user} />
|
|
532
|
+
<CometChatMessageList user={user} /> {/* takes intrinsic content height */}
|
|
533
|
+
<CometChatMessageComposer user={user} /> {/* gets pushed below the fold */}
|
|
534
|
+
</div>
|
|
493
535
|
```
|
|
494
536
|
|
|
537
|
+
**Why `minHeight: 0` is non-obvious:** the W3C spec sets the default `min-height` of a flex item to `auto` (its intrinsic content height). For a scrollable list inside a flex column, this means the list refuses to shrink below its content even when the parent is bounded. Setting `minHeight: 0` on the flex parent overrides this, letting the list shrink and scroll within the bounded container instead of overflowing it.
|
|
538
|
+
|
|
539
|
+
**The rule, restated as something to grep for:**
|
|
540
|
+
|
|
541
|
+
> Every flex container that holds `CometChatMessageList` (directly or indirectly) MUST have `minHeight: 0` on it. Same for `minWidth: 0` on horizontal flex parents. Skip this and the layout works for short conversations and breaks for long ones.
|
|
542
|
+
|
|
495
543
|
### Vite dependency optimization
|
|
496
544
|
|
|
497
545
|
Vite pre-bundles dependencies for faster dev startup. CometChat's packages are large and may trigger Vite's "new dependency found, reloading" message on first load. This is normal and only happens once. If it causes issues, you can pre-include the packages:
|