@caddis/cli 0.0.0 → 0.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.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -1
  3. package/bundles/antigravity-plugin/agents/code-reviewer.md +47 -0
  4. package/bundles/antigravity-plugin/agents/preflight.md +53 -0
  5. package/bundles/antigravity-plugin/guard_agy.py +338 -0
  6. package/bundles/antigravity-plugin/hooks.json +36 -0
  7. package/bundles/antigravity-plugin/mcp_config.json +8 -0
  8. package/bundles/antigravity-plugin/mcp_ping_server.py +55 -0
  9. package/bundles/antigravity-plugin/plugin.json +5 -0
  10. package/bundles/antigravity-plugin/session_end_agy.py +57 -0
  11. package/bundles/antigravity-plugin/skills/_registry.md +115 -0
  12. package/bundles/antigravity-plugin/skills/add-rules/SKILL.md +45 -0
  13. package/bundles/antigravity-plugin/skills/api-design/SKILL.md +249 -0
  14. package/bundles/antigravity-plugin/skills/backend-development/SKILL.md +305 -0
  15. package/bundles/antigravity-plugin/skills/best-practices/SKILL.md +500 -0
  16. package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +263 -0
  17. package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +326 -0
  18. package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +245 -0
  19. package/bundles/antigravity-plugin/skills/best-practices/references/anti-patterns.md +571 -0
  20. package/bundles/antigravity-plugin/skills/best-practices/references/before-after-examples.md +1114 -0
  21. package/bundles/antigravity-plugin/skills/best-practices/references/best-practices-guide.md +513 -0
  22. package/bundles/antigravity-plugin/skills/best-practices/references/common-workflows.md +692 -0
  23. package/bundles/antigravity-plugin/skills/best-practices/references/prompt-patterns.md +547 -0
  24. package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +57 -0
  25. package/bundles/antigravity-plugin/skills/ci-cd-pipeline/SKILL.md +315 -0
  26. package/bundles/antigravity-plugin/skills/code-documentation/SKILL.md +271 -0
  27. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +122 -0
  28. package/bundles/antigravity-plugin/skills/codebase-audit/SKILL.md +204 -0
  29. package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +157 -0
  30. package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +40 -0
  31. package/bundles/antigravity-plugin/skills/css-architecture/SKILL.md +305 -0
  32. package/bundles/antigravity-plugin/skills/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
  33. package/bundles/antigravity-plugin/skills/database-design/SKILL.md +177 -0
  34. package/bundles/antigravity-plugin/skills/db-diagram/SKILL.md +148 -0
  35. package/bundles/antigravity-plugin/skills/db-diagram/scripts/sql_to_graph.py +1212 -0
  36. package/bundles/antigravity-plugin/skills/digress/SKILL.md +61 -0
  37. package/bundles/antigravity-plugin/skills/draw-io/SKILL.md +162 -0
  38. package/bundles/antigravity-plugin/skills/draw-io/references/aws-icons.md +677 -0
  39. package/bundles/antigravity-plugin/skills/draw-io/references/layout-guidelines.md +142 -0
  40. package/bundles/antigravity-plugin/skills/draw-io/references/troubleshooting.md +118 -0
  41. package/bundles/antigravity-plugin/skills/draw-io/references/workflows.md +103 -0
  42. package/bundles/antigravity-plugin/skills/draw-io/scripts/convert-drawio-to-png.sh +25 -0
  43. package/bundles/antigravity-plugin/skills/draw-io/scripts/find_aws_icon.py +79 -0
  44. package/bundles/antigravity-plugin/skills/error-handling/SKILL.md +260 -0
  45. package/bundles/antigravity-plugin/skills/excalidraw-db/SKILL.md +38 -0
  46. package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +300 -0
  47. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +198 -0
  48. package/bundles/antigravity-plugin/skills/frontend-design/SKILL.md +193 -0
  49. package/bundles/antigravity-plugin/skills/frontend-design/references/my-tech-stack.md +127 -0
  50. package/bundles/antigravity-plugin/skills/gh-cli/SKILL.md +195 -0
  51. package/bundles/antigravity-plugin/skills/git-commit/SKILL.md +285 -0
  52. package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +577 -0
  53. package/bundles/antigravity-plugin/skills/handoff/SKILL.md +100 -0
  54. package/bundles/antigravity-plugin/skills/implement/SKILL.md +114 -0
  55. package/bundles/antigravity-plugin/skills/javascript-typescript/SKILL.md +142 -0
  56. package/bundles/antigravity-plugin/skills/kb/SKILL.md +60 -0
  57. package/bundles/antigravity-plugin/skills/mermaid-db/SKILL.md +33 -0
  58. package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +236 -0
  59. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
  60. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
  61. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/advanced-features.md +556 -0
  62. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/architecture-diagrams.md +192 -0
  63. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/c4-diagrams.md +410 -0
  64. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/class-diagrams.md +361 -0
  65. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/erd-diagrams.md +510 -0
  66. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/flowcharts.md +450 -0
  67. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/sequence-diagrams.md +394 -0
  68. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/troubleshooting.md +335 -0
  69. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/workflows.md +418 -0
  70. package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +68 -0
  71. package/bundles/antigravity-plugin/skills/mockup/SKILL.md +242 -0
  72. package/bundles/antigravity-plugin/skills/particle-art/SKILL.md +243 -0
  73. package/bundles/antigravity-plugin/skills/particle-art/references/canvas-utils.ts +171 -0
  74. package/bundles/antigravity-plugin/skills/particle-art/references/dot-field.template.tsx +203 -0
  75. package/bundles/antigravity-plugin/skills/particle-art/references/flow-field.template.tsx +263 -0
  76. package/bundles/antigravity-plugin/skills/particle-art/references/node-shape.template.tsx +261 -0
  77. package/bundles/antigravity-plugin/skills/particle-art/references/shape-sampler.ts +281 -0
  78. package/bundles/antigravity-plugin/skills/particle-art/references/stipple-morph.template.tsx +167 -0
  79. package/bundles/antigravity-plugin/skills/particle-art/references/stipple.template.tsx +175 -0
  80. package/bundles/antigravity-plugin/skills/particle-art/references/trail-ghost.template.tsx +266 -0
  81. package/bundles/antigravity-plugin/skills/particle-art/references/usage-examples.md +320 -0
  82. package/bundles/antigravity-plugin/skills/playwright/API_REFERENCE.md +653 -0
  83. package/bundles/antigravity-plugin/skills/playwright/SKILL.md +454 -0
  84. package/bundles/antigravity-plugin/skills/playwright/lib/helpers.js +441 -0
  85. package/bundles/antigravity-plugin/skills/playwright/package.json +26 -0
  86. package/bundles/antigravity-plugin/skills/playwright/run.js +228 -0
  87. package/bundles/antigravity-plugin/skills/prd/SKILL.md +107 -0
  88. package/bundles/antigravity-plugin/skills/preflight/SKILL.md +435 -0
  89. package/bundles/antigravity-plugin/skills/python/SKILL.md +388 -0
  90. package/bundles/antigravity-plugin/skills/react-best-practices/SKILL.md +269 -0
  91. package/bundles/antigravity-plugin/skills/react-dev/README.md +404 -0
  92. package/bundles/antigravity-plugin/skills/react-dev/SKILL.md +459 -0
  93. package/bundles/antigravity-plugin/skills/react-dev/examples/generic-components.md +579 -0
  94. package/bundles/antigravity-plugin/skills/react-dev/examples/server-components.md +579 -0
  95. package/bundles/antigravity-plugin/skills/react-dev/references/event-handlers.md +574 -0
  96. package/bundles/antigravity-plugin/skills/react-dev/references/hooks.md +456 -0
  97. package/bundles/antigravity-plugin/skills/react-dev/references/react-19-patterns.md +638 -0
  98. package/bundles/antigravity-plugin/skills/react-dev/references/react-router.md +1002 -0
  99. package/bundles/antigravity-plugin/skills/react-dev/references/tanstack-router.md +587 -0
  100. package/bundles/antigravity-plugin/skills/refactoring/SKILL.md +486 -0
  101. package/bundles/antigravity-plugin/skills/resume/SKILL.md +36 -0
  102. package/bundles/antigravity-plugin/skills/security-review/SKILL.md +196 -0
  103. package/bundles/antigravity-plugin/skills/setup-project-ai/SKILL.md +61 -0
  104. package/bundles/antigravity-plugin/skills/ship/SKILL.md +107 -0
  105. package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +103 -0
  106. package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +102 -0
  107. package/bundles/antigravity-plugin/skills/skill-creator/LICENSE.txt +202 -0
  108. package/bundles/antigravity-plugin/skills/skill-creator/SKILL.md +491 -0
  109. package/bundles/antigravity-plugin/skills/skill-creator/agents/analyzer.md +274 -0
  110. package/bundles/antigravity-plugin/skills/skill-creator/agents/comparator.md +202 -0
  111. package/bundles/antigravity-plugin/skills/skill-creator/agents/grader.md +223 -0
  112. package/bundles/antigravity-plugin/skills/skill-creator/assets/eval_review.html +146 -0
  113. package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  114. package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  115. package/bundles/antigravity-plugin/skills/skill-creator/references/schemas.md +430 -0
  116. package/bundles/antigravity-plugin/skills/skill-creator/scripts/__init__.py +0 -0
  117. package/bundles/antigravity-plugin/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  118. package/bundles/antigravity-plugin/skills/skill-creator/scripts/generate_report.py +326 -0
  119. package/bundles/antigravity-plugin/skills/skill-creator/scripts/improve_description.py +247 -0
  120. package/bundles/antigravity-plugin/skills/skill-creator/scripts/package_skill.py +136 -0
  121. package/bundles/antigravity-plugin/skills/skill-creator/scripts/quick_validate.py +103 -0
  122. package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_eval.py +310 -0
  123. package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_loop.py +328 -0
  124. package/bundles/antigravity-plugin/skills/skill-creator/scripts/utils.py +47 -0
  125. package/bundles/antigravity-plugin/skills/sql/SKILL.md +321 -0
  126. package/bundles/antigravity-plugin/skills/tdd/SKILL.md +37 -0
  127. package/bundles/antigravity-plugin/skills/tdd-workflow/SKILL.md +188 -0
  128. package/bundles/antigravity-plugin/skills/technical-writing/SKILL.md +286 -0
  129. package/bundles/antigravity-plugin/skills/test-strategy/SKILL.md +155 -0
  130. package/bundles/antigravity-plugin/skills/ui-brief/SKILL.md +84 -0
  131. package/bundles/antigravity-plugin/skills/ui-review/SKILL.md +176 -0
  132. package/bundles/antigravity-plugin/skills/ui-review/references/framework-fixes.md +471 -0
  133. package/bundles/antigravity-plugin/skills/ui-review/references/visual-checklist.md +236 -0
  134. package/bundles/antigravity-plugin/skills/usage-review/SKILL.md +77 -0
  135. package/bundles/antigravity-plugin/skills/use-model/SKILL.md +64 -0
  136. package/bundles/antigravity-plugin/skills/using-git-worktrees/SKILL.md +217 -0
  137. package/bundles/antigravity-plugin/skills/version/SKILL.md +18 -0
  138. package/bundles/antigravity-plugin/skills/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
  139. package/bundles/antigravity-plugin/skills/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
  140. package/bundles/antigravity-plugin/skills/warm-editorial-ui/SKILL.md +732 -0
  141. package/bundles/antigravity-plugin/skills/webapp-testing/LICENSE.txt +202 -0
  142. package/bundles/antigravity-plugin/skills/webapp-testing/SKILL.md +97 -0
  143. package/bundles/antigravity-plugin/skills/webapp-testing/examples/console_logging.py +35 -0
  144. package/bundles/antigravity-plugin/skills/webapp-testing/examples/element_discovery.py +40 -0
  145. package/bundles/antigravity-plugin/skills/webapp-testing/examples/static_html_automation.py +33 -0
  146. package/bundles/antigravity-plugin/skills/webapp-testing/scripts/with_server.py +106 -0
  147. package/bundles/antigravity-plugin/skills/windows-deployment/SKILL.md +880 -0
  148. package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +384 -0
  149. package/bundles/antigravity-plugin/statusline-command-agy.sh +91 -0
  150. package/bundles/antigravity-plugin/warm_start_agy.py +149 -0
  151. package/bundles/manifest.json +6 -0
  152. package/dist/cli.js +5363 -0
  153. package/package.json +61 -4
@@ -0,0 +1,579 @@
1
+ # Generic Component Patterns
2
+
3
+ Generic components provide type safety while maintaining reusability. TypeScript infers generic type parameters from prop values — no manual type annotations needed at call site.
4
+
5
+ ## Generic Table Component
6
+
7
+ Full-featured table with typed columns, custom rendering, sorting.
8
+
9
+ ```typescript
10
+ type Column<T> = {
11
+ key: keyof T;
12
+ header: string;
13
+ render?: (value: T[keyof T], item: T) => React.ReactNode;
14
+ sortable?: boolean;
15
+ };
16
+
17
+ type TableProps<T> = {
18
+ data: T[];
19
+ columns: Column<T>[];
20
+ keyExtractor: (item: T) => string | number;
21
+ onSort?: (key: keyof T, direction: 'asc' | 'desc') => void;
22
+ className?: string;
23
+ };
24
+
25
+ function Table<T>({
26
+ data,
27
+ columns,
28
+ keyExtractor,
29
+ onSort,
30
+ className,
31
+ }: TableProps<T>) {
32
+ const [sortKey, setSortKey] = React.useState<keyof T | null>(null);
33
+ const [sortDir, setSortDir] = React.useState<'asc' | 'desc'>('asc');
34
+
35
+ const handleSort = (key: keyof T) => {
36
+ const newDir = sortKey === key && sortDir === 'asc' ? 'desc' : 'asc';
37
+ setSortKey(key);
38
+ setSortDir(newDir);
39
+ onSort?.(key, newDir);
40
+ };
41
+
42
+ return (
43
+ <table className={className}>
44
+ <thead>
45
+ <tr>
46
+ {columns.map((col) => (
47
+ <th key={String(col.key)}>
48
+ {col.sortable ? (
49
+ <button onClick={() => handleSort(col.key)}>
50
+ {col.header}
51
+ {sortKey === col.key && (sortDir === 'asc' ? ' ↑' : ' ↓')}
52
+ </button>
53
+ ) : (
54
+ col.header
55
+ )}
56
+ </th>
57
+ ))}
58
+ </tr>
59
+ </thead>
60
+ <tbody>
61
+ {data.map((item) => (
62
+ <tr key={keyExtractor(item)}>
63
+ {columns.map((col) => (
64
+ <td key={String(col.key)}>
65
+ {col.render
66
+ ? col.render(item[col.key], item)
67
+ : String(item[col.key])}
68
+ </td>
69
+ ))}
70
+ </tr>
71
+ ))}
72
+ </tbody>
73
+ </table>
74
+ );
75
+ }
76
+ ```
77
+
78
+ Usage:
79
+
80
+ ```typescript
81
+ type User = {
82
+ id: number;
83
+ name: string;
84
+ email: string;
85
+ role: 'admin' | 'user';
86
+ createdAt: Date;
87
+ };
88
+
89
+ function UserTable({ users }: { users: User[] }) {
90
+ const [sortedUsers, setSortedUsers] = React.useState(users);
91
+
92
+ const handleSort = (key: keyof User, direction: 'asc' | 'desc') => {
93
+ const sorted = [...users].sort((a, b) => {
94
+ if (a[key] < b[key]) return direction === 'asc' ? -1 : 1;
95
+ if (a[key] > b[key]) return direction === 'asc' ? 1 : -1;
96
+ return 0;
97
+ });
98
+ setSortedUsers(sorted);
99
+ };
100
+
101
+ return (
102
+ <Table
103
+ data={sortedUsers}
104
+ columns={[
105
+ { key: 'name', header: 'Name', sortable: true },
106
+ {
107
+ key: 'email',
108
+ header: 'Email',
109
+ render: (email) => <a href={`mailto:${email}`}>{email}</a>,
110
+ },
111
+ {
112
+ key: 'role',
113
+ header: 'Role',
114
+ render: (role) => (
115
+ <span className={role === 'admin' ? 'badge-admin' : 'badge-user'}>
116
+ {role}
117
+ </span>
118
+ ),
119
+ },
120
+ {
121
+ key: 'createdAt',
122
+ header: 'Created',
123
+ render: (date) => new Date(date).toLocaleDateString(),
124
+ sortable: true,
125
+ },
126
+ ]}
127
+ keyExtractor={(user) => user.id}
128
+ onSort={handleSort}
129
+ />
130
+ );
131
+ }
132
+ ```
133
+
134
+ ## Generic Select/Dropdown
135
+
136
+ Type-safe select with custom option rendering, searching.
137
+
138
+ ```typescript
139
+ type SelectProps<T> = {
140
+ options: T[];
141
+ value: T | null;
142
+ onChange: (value: T) => void;
143
+ getLabel: (option: T) => string;
144
+ getValue: (option: T) => string | number;
145
+ placeholder?: string;
146
+ disabled?: boolean;
147
+ searchable?: boolean;
148
+ };
149
+
150
+ function Select<T>({
151
+ options,
152
+ value,
153
+ onChange,
154
+ getLabel,
155
+ getValue,
156
+ placeholder = 'Select...',
157
+ disabled = false,
158
+ searchable = false,
159
+ }: SelectProps<T>) {
160
+ const [search, setSearch] = React.useState('');
161
+ const [isOpen, setIsOpen] = React.useState(false);
162
+
163
+ const filtered = searchable
164
+ ? options.filter((opt) =>
165
+ getLabel(opt).toLowerCase().includes(search.toLowerCase())
166
+ )
167
+ : options;
168
+
169
+ const handleSelect = (option: T) => {
170
+ onChange(option);
171
+ setIsOpen(false);
172
+ setSearch('');
173
+ };
174
+
175
+ return (
176
+ <div className="select-wrapper">
177
+ <button
178
+ onClick={() => setIsOpen(!isOpen)}
179
+ disabled={disabled}
180
+ className="select-trigger"
181
+ >
182
+ {value ? getLabel(value) : placeholder}
183
+ </button>
184
+
185
+ {isOpen && (
186
+ <div className="select-dropdown">
187
+ {searchable && (
188
+ <input
189
+ type="text"
190
+ value={search}
191
+ onChange={(e) => setSearch(e.target.value)}
192
+ placeholder="Search..."
193
+ autoFocus
194
+ />
195
+ )}
196
+ <ul>
197
+ {filtered.map((option) => (
198
+ <li
199
+ key={getValue(option)}
200
+ onClick={() => handleSelect(option)}
201
+ className={value === option ? 'selected' : ''}
202
+ >
203
+ {getLabel(option)}
204
+ </li>
205
+ ))}
206
+ </ul>
207
+ </div>
208
+ )}
209
+ </div>
210
+ );
211
+ }
212
+ ```
213
+
214
+ Usage:
215
+
216
+ ```typescript
217
+ type Country = {
218
+ code: string;
219
+ name: string;
220
+ flag: string;
221
+ };
222
+
223
+ const countries: Country[] = [
224
+ { code: 'US', name: 'United States', flag: '🇺🇸' },
225
+ { code: 'CA', name: 'Canada', flag: '🇨🇦' },
226
+ { code: 'MX', name: 'Mexico', flag: '🇲🇽' },
227
+ ];
228
+
229
+ function CountrySelector() {
230
+ const [country, setCountry] = React.useState<Country | null>(null);
231
+
232
+ return (
233
+ <Select
234
+ options={countries}
235
+ value={country}
236
+ onChange={setCountry}
237
+ getLabel={(c) => `${c.flag} ${c.name}`}
238
+ getValue={(c) => c.code}
239
+ searchable
240
+ placeholder="Select country"
241
+ />
242
+ );
243
+ }
244
+ ```
245
+
246
+ ## Generic List with Render Props
247
+
248
+ Flexible list rendering with type-safe render props.
249
+
250
+ ```typescript
251
+ type ListProps<T> = {
252
+ items: T[];
253
+ renderItem: (item: T, index: number) => React.ReactNode;
254
+ renderEmpty?: () => React.ReactNode;
255
+ keyExtractor: (item: T, index: number) => string | number;
256
+ className?: string;
257
+ as?: 'ul' | 'ol' | 'div';
258
+ };
259
+
260
+ function List<T>({
261
+ items,
262
+ renderItem,
263
+ renderEmpty,
264
+ keyExtractor,
265
+ className,
266
+ as: Component = 'ul',
267
+ }: ListProps<T>) {
268
+ if (items.length === 0 && renderEmpty) {
269
+ return <>{renderEmpty()}</>;
270
+ }
271
+
272
+ return (
273
+ <Component className={className}>
274
+ {items.map((item, index) => {
275
+ const key = keyExtractor(item, index);
276
+ const element = renderItem(item, index);
277
+
278
+ return Component === 'div' ? (
279
+ <div key={key}>{element}</div>
280
+ ) : (
281
+ <li key={key}>{element}</li>
282
+ );
283
+ })}
284
+ </Component>
285
+ );
286
+ }
287
+ ```
288
+
289
+ Usage:
290
+
291
+ ```typescript
292
+ type Task = {
293
+ id: string;
294
+ title: string;
295
+ completed: boolean;
296
+ priority: 'low' | 'medium' | 'high';
297
+ };
298
+
299
+ function TaskList({ tasks }: { tasks: Task[] }) {
300
+ return (
301
+ <List
302
+ items={tasks}
303
+ keyExtractor={(task) => task.id}
304
+ renderItem={(task) => (
305
+ <div className={`task priority-${task.priority}`}>
306
+ <input type="checkbox" checked={task.completed} readOnly />
307
+ <span className={task.completed ? 'completed' : ''}>{task.title}</span>
308
+ </div>
309
+ )}
310
+ renderEmpty={() => (
311
+ <div className="empty-state">No tasks yet. Add one to get started!</div>
312
+ )}
313
+ as="div"
314
+ />
315
+ );
316
+ }
317
+ ```
318
+
319
+ ## Constrained Generic Components
320
+
321
+ Use constraints to ensure required properties or methods.
322
+
323
+ ```typescript
324
+ // Constraint: items must have `id` property
325
+ type HasId = { id: string | number };
326
+
327
+ type GridProps<T extends HasId> = {
328
+ items: T[];
329
+ renderCard: (item: T) => React.ReactNode;
330
+ columns?: 2 | 3 | 4;
331
+ };
332
+
333
+ function Grid<T extends HasId>({
334
+ items,
335
+ renderCard,
336
+ columns = 3,
337
+ }: GridProps<T>) {
338
+ return (
339
+ <div className={`grid grid-cols-${columns}`}>
340
+ {items.map((item) => (
341
+ <div key={item.id} className="grid-item">
342
+ {renderCard(item)}
343
+ </div>
344
+ ))}
345
+ </div>
346
+ );
347
+ }
348
+
349
+ // Usage - type must have `id` property
350
+ type Product = {
351
+ id: number;
352
+ name: string;
353
+ price: number;
354
+ image: string;
355
+ };
356
+
357
+ <Grid
358
+ items={products}
359
+ renderCard={(product) => (
360
+ <div>
361
+ <img src={product.image} alt={product.name} />
362
+ <h3>{product.name}</h3>
363
+ <p>${product.price}</p>
364
+ </div>
365
+ )}
366
+ columns={3}
367
+ />;
368
+ ```
369
+
370
+ ## Multiple Generic Parameters
371
+
372
+ Components with multiple type parameters.
373
+
374
+ ```typescript
375
+ type PairProps<K, V> = {
376
+ pairs: Array<[K, V]>;
377
+ renderKey: (key: K) => React.ReactNode;
378
+ renderValue: (value: V) => React.ReactNode;
379
+ };
380
+
381
+ function KeyValueList<K, V>({ pairs, renderKey, renderValue }: PairProps<K, V>) {
382
+ return (
383
+ <dl>
384
+ {pairs.map(([key, value], index) => (
385
+ <React.Fragment key={index}>
386
+ <dt>{renderKey(key)}</dt>
387
+ <dd>{renderValue(value)}</dd>
388
+ </React.Fragment>
389
+ ))}
390
+ </dl>
391
+ );
392
+ }
393
+
394
+ // Usage
395
+ type MetricKey = 'cpu' | 'memory' | 'disk';
396
+ type MetricValue = { current: number; max: number };
397
+
398
+ const metrics: Array<[MetricKey, MetricValue]> = [
399
+ ['cpu', { current: 45, max: 100 }],
400
+ ['memory', { current: 8, max: 16 }],
401
+ ['disk', { current: 250, max: 500 }],
402
+ ];
403
+
404
+ <KeyValueList
405
+ pairs={metrics}
406
+ renderKey={(key) => <strong>{key.toUpperCase()}</strong>}
407
+ renderValue={(val) => (
408
+ <span>
409
+ {val.current}/{val.max}
410
+ </span>
411
+ )}
412
+ />;
413
+ ```
414
+
415
+ ## Generic Form Field Component
416
+
417
+ Reusable form field with type-safe value handling.
418
+
419
+ ```typescript
420
+ type FieldProps<T> = {
421
+ name: string;
422
+ label: string;
423
+ value: T;
424
+ onChange: (value: T) => void;
425
+ type: 'text' | 'number' | 'email' | 'password';
426
+ error?: string;
427
+ required?: boolean;
428
+ };
429
+
430
+ function FormField<T extends string | number>({
431
+ name,
432
+ label,
433
+ value,
434
+ onChange,
435
+ type,
436
+ error,
437
+ required = false,
438
+ }: FieldProps<T>) {
439
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
440
+ const newValue =
441
+ type === 'number' ? (Number(e.target.value) as T) : (e.target.value as T);
442
+ onChange(newValue);
443
+ };
444
+
445
+ return (
446
+ <div className="form-field">
447
+ <label htmlFor={name}>
448
+ {label}
449
+ {required && <span className="required">*</span>}
450
+ </label>
451
+ <input
452
+ id={name}
453
+ name={name}
454
+ type={type}
455
+ value={value}
456
+ onChange={handleChange}
457
+ required={required}
458
+ aria-invalid={!!error}
459
+ aria-describedby={error ? `${name}-error` : undefined}
460
+ />
461
+ {error && (
462
+ <span id={`${name}-error`} className="error">
463
+ {error}
464
+ </span>
465
+ )}
466
+ </div>
467
+ );
468
+ }
469
+
470
+ // Usage
471
+ function UserForm() {
472
+ const [name, setName] = React.useState('');
473
+ const [age, setAge] = React.useState(0);
474
+
475
+ return (
476
+ <form>
477
+ <FormField
478
+ name="name"
479
+ label="Name"
480
+ value={name}
481
+ onChange={setName}
482
+ type="text"
483
+ required
484
+ />
485
+ <FormField
486
+ name="age"
487
+ label="Age"
488
+ value={age}
489
+ onChange={setAge}
490
+ type="number"
491
+ />
492
+ </form>
493
+ );
494
+ }
495
+ ```
496
+
497
+ ## Generic Modal/Dialog
498
+
499
+ Type-safe modal with generic result type.
500
+
501
+ ```typescript
502
+ type ModalProps<T> = {
503
+ isOpen: boolean;
504
+ onClose: (result?: T) => void;
505
+ title: string;
506
+ children: (submit: (result: T) => void) => React.ReactNode;
507
+ };
508
+
509
+ function Modal<T>({ isOpen, onClose, title, children }: ModalProps<T>) {
510
+ if (!isOpen) return null;
511
+
512
+ const handleSubmit = (result: T) => {
513
+ onClose(result);
514
+ };
515
+
516
+ return (
517
+ <div className="modal-overlay">
518
+ <div className="modal">
519
+ <header>
520
+ <h2>{title}</h2>
521
+ <button onClick={() => onClose()}>×</button>
522
+ </header>
523
+ <div className="modal-body">{children(handleSubmit)}</div>
524
+ </div>
525
+ </div>
526
+ );
527
+ }
528
+
529
+ // Usage
530
+ type UserFormData = { name: string; email: string };
531
+
532
+ function App() {
533
+ const [showModal, setShowModal] = React.useState(false);
534
+ const [formData, setFormData] = React.useState<UserFormData>({
535
+ name: '',
536
+ email: '',
537
+ });
538
+
539
+ const handleModalClose = (result?: UserFormData) => {
540
+ if (result) {
541
+ console.log('User submitted:', result);
542
+ }
543
+ setShowModal(false);
544
+ };
545
+
546
+ return (
547
+ <>
548
+ <button onClick={() => setShowModal(true)}>Add User</button>
549
+
550
+ <Modal<UserFormData>
551
+ isOpen={showModal}
552
+ onClose={handleModalClose}
553
+ title="Add New User"
554
+ >
555
+ {(submit) => (
556
+ <form
557
+ onSubmit={(e) => {
558
+ e.preventDefault();
559
+ submit(formData);
560
+ }}
561
+ >
562
+ <input
563
+ value={formData.name}
564
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
565
+ placeholder="Name"
566
+ />
567
+ <input
568
+ value={formData.email}
569
+ onChange={(e) => setFormData({ ...formData, email: e.target.value })}
570
+ placeholder="Email"
571
+ />
572
+ <button type="submit">Submit</button>
573
+ </form>
574
+ )}
575
+ </Modal>
576
+ </>
577
+ );
578
+ }
579
+ ```