senren-ui 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 (182) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +33 -0
  3. data/CONTRIBUTING.md +63 -0
  4. data/LICENSE +21 -0
  5. data/README.md +135 -0
  6. data/Rakefile +22 -0
  7. data/docs/visual_style.md +51 -0
  8. data/lib/generators/senren/component/component_generator.rb +62 -0
  9. data/lib/generators/senren/component/templates/component.html.erb.tt +3 -0
  10. data/lib/generators/senren/component/templates/component.rb.tt +13 -0
  11. data/lib/generators/senren/component/templates/component_test.rb.tt +16 -0
  12. data/lib/generators/senren/component/templates/controller.js.tt +23 -0
  13. data/lib/generators/senren/component/templates/system_test.rb.tt +7 -0
  14. data/lib/generators/senren/install/install_generator.rb +67 -0
  15. data/lib/generators/senren/install/templates/base_component.rb.tt +45 -0
  16. data/lib/generators/senren/install/templates/conventions.md.tt +66 -0
  17. data/lib/generators/senren/install/templates/installed_components.yml.tt +4 -0
  18. data/lib/generators/senren/install/templates/senren.css.tt +164 -0
  19. data/lib/senren/rails/component_copier.rb +111 -0
  20. data/lib/senren/rails/doctor.rb +86 -0
  21. data/lib/senren/rails/engine.rb +16 -0
  22. data/lib/senren/rails/host_paths.rb +36 -0
  23. data/lib/senren/rails/installer.rb +83 -0
  24. data/lib/senren/rails/llms_writer.rb +149 -0
  25. data/lib/senren/rails/registry.rb +161 -0
  26. data/lib/senren/rails/skill_writer.rb +166 -0
  27. data/lib/senren/rails/version.rb +7 -0
  28. data/lib/senren/rails.rb +39 -0
  29. data/lib/tasks/senren.rake +74 -0
  30. data/registry/components.yml +1053 -0
  31. data/registry/groups.yml +25 -0
  32. data/registry/recipes.yml +79 -0
  33. data/templates/components/accordion/accordion_component.html.erb +16 -0
  34. data/templates/components/accordion/accordion_component.rb +31 -0
  35. data/templates/components/activity_feed/activity_feed_component.html.erb +22 -0
  36. data/templates/components/activity_feed/activity_feed_component.rb +19 -0
  37. data/templates/components/alert/alert_component.html.erb +9 -0
  38. data/templates/components/alert/alert_component.rb +18 -0
  39. data/templates/components/alert_dialog/alert_dialog_component.html.erb +34 -0
  40. data/templates/components/alert_dialog/alert_dialog_component.rb +21 -0
  41. data/templates/components/api_key_field/api_key_field_component.html.erb +13 -0
  42. data/templates/components/api_key_field/api_key_field_component.rb +20 -0
  43. data/templates/components/app_shell/app_shell_component.html.erb +28 -0
  44. data/templates/components/app_shell/app_shell_component.rb +24 -0
  45. data/templates/components/aspect_ratio/aspect_ratio_component.html.erb +3 -0
  46. data/templates/components/aspect_ratio/aspect_ratio_component.rb +14 -0
  47. data/templates/components/avatar/avatar_component.html.erb +27 -0
  48. data/templates/components/avatar/avatar_component.rb +30 -0
  49. data/templates/components/badge/badge_component.html.erb +1 -0
  50. data/templates/components/badge/badge_component.rb +16 -0
  51. data/templates/components/billing_plan_card/billing_plan_card_component.html.erb +28 -0
  52. data/templates/components/billing_plan_card/billing_plan_card_component.rb +27 -0
  53. data/templates/components/breadcrumb/breadcrumb_component.html.erb +23 -0
  54. data/templates/components/breadcrumb/breadcrumb_component.rb +30 -0
  55. data/templates/components/bulk_action_bar/bulk_action_bar_component.html.erb +12 -0
  56. data/templates/components/bulk_action_bar/bulk_action_bar_component.rb +24 -0
  57. data/templates/components/button/button_component.html.erb +6 -0
  58. data/templates/components/button/button_component.rb +29 -0
  59. data/templates/components/calendar/calendar_component.html.erb +21 -0
  60. data/templates/components/calendar/calendar_component.rb +30 -0
  61. data/templates/components/card/card_component.html.erb +13 -0
  62. data/templates/components/card/card_component.rb +17 -0
  63. data/templates/components/carousel/carousel_component.html.erb +68 -0
  64. data/templates/components/carousel/carousel_component.rb +34 -0
  65. data/templates/components/checkbox/checkbox_component.html.erb +8 -0
  66. data/templates/components/checkbox/checkbox_component.rb +19 -0
  67. data/templates/components/checkbox_group/checkbox_group_component.html.erb +10 -0
  68. data/templates/components/checkbox_group/checkbox_group_component.rb +30 -0
  69. data/templates/components/clipboard/clipboard_component.html.erb +7 -0
  70. data/templates/components/clipboard/clipboard_component.rb +17 -0
  71. data/templates/components/codeblock/codeblock_component.html.erb +11 -0
  72. data/templates/components/codeblock/codeblock_component.rb +31 -0
  73. data/templates/components/collapsible/collapsible_component.html.erb +9 -0
  74. data/templates/components/collapsible/collapsible_component.rb +19 -0
  75. data/templates/components/combobox/combobox_component.html.erb +19 -0
  76. data/templates/components/combobox/combobox_component.rb +38 -0
  77. data/templates/components/command/command_component.html.erb +22 -0
  78. data/templates/components/command/command_component.rb +38 -0
  79. data/templates/components/context_menu/context_menu_component.html.erb +11 -0
  80. data/templates/components/context_menu/context_menu_component.rb +11 -0
  81. data/templates/components/data_table/data_table_component.html.erb +50 -0
  82. data/templates/components/data_table/data_table_component.rb +42 -0
  83. data/templates/components/date_picker/date_picker_component.html.erb +5 -0
  84. data/templates/components/date_picker/date_picker_component.rb +21 -0
  85. data/templates/components/dialog/dialog_component.html.erb +38 -0
  86. data/templates/components/dialog/dialog_component.rb +22 -0
  87. data/templates/components/dropdown_menu/dropdown_menu_component.html.erb +12 -0
  88. data/templates/components/dropdown_menu/dropdown_menu_component.rb +36 -0
  89. data/templates/components/empty_state/empty_state_component.html.erb +18 -0
  90. data/templates/components/empty_state/empty_state_component.rb +22 -0
  91. data/templates/components/filter_bar/filter_bar_component.html.erb +5 -0
  92. data/templates/components/filter_bar/filter_bar_component.rb +15 -0
  93. data/templates/components/form/form_component.html.erb +3 -0
  94. data/templates/components/form/form_component.rb +18 -0
  95. data/templates/components/hover_card/hover_card_component.html.erb +10 -0
  96. data/templates/components/hover_card/hover_card_component.rb +11 -0
  97. data/templates/components/input/input_component.html.erb +1 -0
  98. data/templates/components/input/input_component.rb +28 -0
  99. data/templates/components/invite_member_dialog/invite_member_dialog_component.html.erb +35 -0
  100. data/templates/components/invite_member_dialog/invite_member_dialog_component.rb +26 -0
  101. data/templates/components/label/label_component.html.erb +4 -0
  102. data/templates/components/label/label_component.rb +19 -0
  103. data/templates/components/link/link_component.html.erb +1 -0
  104. data/templates/components/link/link_component.rb +25 -0
  105. data/templates/components/masked_input/masked_input_component.html.erb +1 -0
  106. data/templates/components/masked_input/masked_input_component.rb +18 -0
  107. data/templates/components/native_select/native_select_component.html.erb +14 -0
  108. data/templates/components/native_select/native_select_component.rb +52 -0
  109. data/templates/components/page_header/page_header_component.html.erb +20 -0
  110. data/templates/components/page_header/page_header_component.rb +19 -0
  111. data/templates/components/pagination/pagination_component.html.erb +11 -0
  112. data/templates/components/pagination/pagination_component.rb +24 -0
  113. data/templates/components/popover/popover_component.html.erb +9 -0
  114. data/templates/components/popover/popover_component.rb +11 -0
  115. data/templates/components/progress/progress_component.html.erb +11 -0
  116. data/templates/components/progress/progress_component.rb +26 -0
  117. data/templates/components/radio_button/radio_button_component.html.erb +8 -0
  118. data/templates/components/radio_button/radio_button_component.rb +19 -0
  119. data/templates/components/rich_text_editor_lite/rich_text_editor_lite_component.html.erb +32 -0
  120. data/templates/components/rich_text_editor_lite/rich_text_editor_lite_component.rb +30 -0
  121. data/templates/components/search_input/search_input_component.html.erb +14 -0
  122. data/templates/components/search_input/search_input_component.rb +18 -0
  123. data/templates/components/select/select_component.html.erb +1 -0
  124. data/templates/components/select/select_component.rb +19 -0
  125. data/templates/components/separator/separator_component.html.erb +1 -0
  126. data/templates/components/separator/separator_component.rb +12 -0
  127. data/templates/components/settings_section/settings_section_component.html.erb +20 -0
  128. data/templates/components/settings_section/settings_section_component.rb +18 -0
  129. data/templates/components/sheet/sheet_component.html.erb +37 -0
  130. data/templates/components/sheet/sheet_component.rb +27 -0
  131. data/templates/components/shortcut_key/shortcut_key_component.html.erb +6 -0
  132. data/templates/components/shortcut_key/shortcut_key_component.rb +15 -0
  133. data/templates/components/sidebar/sidebar_component.html.erb +14 -0
  134. data/templates/components/sidebar/sidebar_component.rb +37 -0
  135. data/templates/components/skeleton/skeleton_component.html.erb +1 -0
  136. data/templates/components/skeleton/skeleton_component.rb +13 -0
  137. data/templates/components/stat_card/stat_card_component.html.erb +20 -0
  138. data/templates/components/stat_card/stat_card_component.rb +31 -0
  139. data/templates/components/switch/switch_component.html.erb +11 -0
  140. data/templates/components/switch/switch_component.rb +19 -0
  141. data/templates/components/table/table_component.html.erb +26 -0
  142. data/templates/components/table/table_component.rb +35 -0
  143. data/templates/components/tabs/tabs_component.html.erb +18 -0
  144. data/templates/components/tabs/tabs_component.rb +35 -0
  145. data/templates/components/team_member_list/team_member_list_component.html.erb +22 -0
  146. data/templates/components/team_member_list/team_member_list_component.rb +26 -0
  147. data/templates/components/textarea/textarea_component.html.erb +1 -0
  148. data/templates/components/textarea/textarea_component.rb +23 -0
  149. data/templates/components/theme_toggle/theme_toggle_component.html.erb +4 -0
  150. data/templates/components/theme_toggle/theme_toggle_component.rb +15 -0
  151. data/templates/components/tooltip/tooltip_component.html.erb +9 -0
  152. data/templates/components/tooltip/tooltip_component.rb +16 -0
  153. data/templates/components/top_nav/top_nav_component.html.erb +21 -0
  154. data/templates/components/top_nav/top_nav_component.rb +44 -0
  155. data/templates/components/typography/typography_component.html.erb +1 -0
  156. data/templates/components/typography/typography_component.rb +24 -0
  157. data/templates/controllers/accordion_controller.js +27 -0
  158. data/templates/controllers/alert_dialog_controller.js +38 -0
  159. data/templates/controllers/api_key_field_controller.js +36 -0
  160. data/templates/controllers/calendar_controller.js +16 -0
  161. data/templates/controllers/carousel_controller.js +50 -0
  162. data/templates/controllers/clipboard_controller.js +17 -0
  163. data/templates/controllers/collapsible_controller.js +13 -0
  164. data/templates/controllers/combobox_controller.js +64 -0
  165. data/templates/controllers/command_controller.js +80 -0
  166. data/templates/controllers/context_menu_controller.js +36 -0
  167. data/templates/controllers/data_table_controller.js +34 -0
  168. data/templates/controllers/date_picker_controller.js +17 -0
  169. data/templates/controllers/dialog_controller.js +50 -0
  170. data/templates/controllers/dropdown_menu_controller.js +92 -0
  171. data/templates/controllers/hover_card_controller.js +17 -0
  172. data/templates/controllers/invite_member_dialog_controller.js +28 -0
  173. data/templates/controllers/masked_input_controller.js +30 -0
  174. data/templates/controllers/popover_controller.js +42 -0
  175. data/templates/controllers/rich_text_editor_lite_controller.js +443 -0
  176. data/templates/controllers/select_controller.js +10 -0
  177. data/templates/controllers/sheet_controller.js +34 -0
  178. data/templates/controllers/sidebar_controller.js +10 -0
  179. data/templates/controllers/tabs_controller.js +41 -0
  180. data/templates/controllers/theme_toggle_controller.js +24 -0
  181. data/templates/controllers/tooltip_controller.js +10 -0
  182. metadata +257 -0
@@ -0,0 +1,1053 @@
1
+ # Senren UI - canonical component registry.
2
+ # Schema:
3
+ # <name>:
4
+ # category: actions|forms|overlays|navigation|layout|data|saas|rich
5
+ # client: default behavior (bool)
6
+ # can_have_client: whether --client is meaningful
7
+ # controller: Stimulus identifier (or null)
8
+ # stub: true => scaffolded placeholder, false/absent => full
9
+ # files: paths copied into host app (relative to host root)
10
+ # depends_on: [<name>, ...]
11
+ # pairs_with: [<name>, ...]
12
+ # variants: [<string>, ...]
13
+ # accessibility: [<string>, ...]
14
+ # ai:
15
+ # use_for: [<string>, ...]
16
+ # avoid: [<string>, ...]
17
+
18
+ components:
19
+
20
+ # ---------------------------------------------------------------- Phase 1
21
+ button:
22
+ category: actions
23
+ client: false
24
+ can_have_client: true
25
+ files:
26
+ - app/components/senren/button_component.rb
27
+ - app/components/senren/button_component.html.erb
28
+ depends_on: []
29
+ pairs_with: [form, dialog, dropdown_menu, alert_dialog]
30
+ variants: [default, primary, secondary, destructive, ghost, link]
31
+ accessibility:
32
+ - Icon-only buttons must include aria-label.
33
+ - Use button for actions and anchor for navigation.
34
+ ai:
35
+ use_for: [primary action, secondary action, form submit, destructive action]
36
+ avoid: [using destructive variant for non-destructive actions]
37
+
38
+ link:
39
+ category: actions
40
+ client: false
41
+ can_have_client: false
42
+ files:
43
+ - app/components/senren/link_component.rb
44
+ - app/components/senren/link_component.html.erb
45
+ depends_on: []
46
+ pairs_with: [button, breadcrumb]
47
+ variants: [default, muted, destructive]
48
+ accessibility: [Use real anchors for navigation, never buttons.]
49
+ ai:
50
+ use_for: [in-app navigation, external links]
51
+ avoid: [triggering destructive actions via a link]
52
+
53
+ badge:
54
+ category: data
55
+ client: false
56
+ can_have_client: false
57
+ files:
58
+ - app/components/senren/badge_component.rb
59
+ - app/components/senren/badge_component.html.erb
60
+ depends_on: []
61
+ pairs_with: [card, table, avatar]
62
+ variants: [default, secondary, success, warning, destructive, outline]
63
+ accessibility: [Provide sufficient color contrast; do not encode meaning by color alone.]
64
+ ai:
65
+ use_for: [status, priority, counts, tags]
66
+ avoid: [interactive actions disguised as badges]
67
+
68
+ typography:
69
+ category: layout
70
+ client: false
71
+ can_have_client: false
72
+ files:
73
+ - app/components/senren/typography_component.rb
74
+ - app/components/senren/typography_component.html.erb
75
+ depends_on: []
76
+ pairs_with: [card, page_header]
77
+ variants: [h1, h2, h3, h4, p, lead, large, small, muted]
78
+ accessibility: [Heading levels must be sequential; do not skip levels.]
79
+ ai:
80
+ use_for: [page titles, body copy, captions]
81
+ avoid: [styling without semantic tag matching]
82
+
83
+ separator:
84
+ category: layout
85
+ client: false
86
+ can_have_client: false
87
+ files:
88
+ - app/components/senren/separator_component.rb
89
+ - app/components/senren/separator_component.html.erb
90
+ depends_on: []
91
+ pairs_with: [card, settings_section]
92
+ variants: [horizontal, vertical]
93
+ accessibility: [role=separator with aria-orientation.]
94
+ ai:
95
+ use_for: [grouping content blocks]
96
+ avoid: [decorative-only use without semantics]
97
+
98
+ skeleton:
99
+ category: data
100
+ client: false
101
+ can_have_client: false
102
+ files:
103
+ - app/components/senren/skeleton_component.rb
104
+ - app/components/senren/skeleton_component.html.erb
105
+ depends_on: []
106
+ pairs_with: [card, table]
107
+ variants: [default, circle, text]
108
+ accessibility: [aria-hidden=true while loading; pair with aria-busy on parent.]
109
+ ai:
110
+ use_for: [loading placeholders]
111
+ avoid: [permanent decoration]
112
+
113
+ avatar:
114
+ category: data
115
+ client: false
116
+ can_have_client: false
117
+ files:
118
+ - app/components/senren/avatar_component.rb
119
+ - app/components/senren/avatar_component.html.erb
120
+ depends_on: []
121
+ pairs_with: [card, team_member_list, dropdown_menu]
122
+ variants: [sm, md, lg]
123
+ accessibility: [alt text required; fallback to initials with role=img and aria-label.]
124
+ ai:
125
+ use_for: [user thumbnail, team list, comments]
126
+ avoid: [decorative-only without alt]
127
+
128
+ alert:
129
+ category: data
130
+ client: false
131
+ can_have_client: false
132
+ files:
133
+ - app/components/senren/alert_component.rb
134
+ - app/components/senren/alert_component.html.erb
135
+ depends_on: []
136
+ pairs_with: [form, page_header]
137
+ variants: [default, info, success, warning, destructive]
138
+ accessibility: [role=alert for error states; role=status for non-urgent updates.]
139
+ ai:
140
+ use_for: [form feedback, page-level notices]
141
+ avoid: [transient toasts - use a toast component instead]
142
+
143
+ card:
144
+ category: layout
145
+ client: false
146
+ can_have_client: false
147
+ files:
148
+ - app/components/senren/card_component.rb
149
+ - app/components/senren/card_component.html.erb
150
+ depends_on: []
151
+ pairs_with: [button, badge, separator]
152
+ variants: [default, muted, outline]
153
+ accessibility: [Use heading inside header slot; landmark roles only when meaningful.]
154
+ ai:
155
+ use_for: [grouping related content, dashboard widgets, list items]
156
+ avoid: [nesting cards inside cards]
157
+
158
+ aspect_ratio:
159
+ category: layout
160
+ client: false
161
+ can_have_client: false
162
+ files:
163
+ - app/components/senren/aspect_ratio_component.rb
164
+ - app/components/senren/aspect_ratio_component.html.erb
165
+ depends_on: []
166
+ pairs_with: [card, avatar]
167
+ variants: [square, video, portrait, ultrawide]
168
+ accessibility: [Inner media must include alt or aria-label.]
169
+ ai:
170
+ use_for: [media, image placeholders, embeds]
171
+ avoid: [forcing aspect on text content]
172
+
173
+ # ---------------------------------------------------------------- Phase 2
174
+ label:
175
+ category: forms
176
+ client: false
177
+ can_have_client: false
178
+ files:
179
+ - app/components/senren/label_component.rb
180
+ - app/components/senren/label_component.html.erb
181
+ depends_on: []
182
+ pairs_with: [input, textarea, checkbox, switch, native_select]
183
+ variants: [default, required]
184
+ accessibility: [Always associate via for=field-id.]
185
+ ai:
186
+ use_for: [labeling form controls]
187
+ avoid: [using placeholder as the only label]
188
+
189
+ form:
190
+ category: forms
191
+ client: false
192
+ can_have_client: false
193
+ files:
194
+ - app/components/senren/form_component.rb
195
+ - app/components/senren/form_component.html.erb
196
+ depends_on: [button]
197
+ pairs_with: [input, textarea, native_select, label, alert]
198
+ variants: [default]
199
+ accessibility: [Group related fields with fieldset/legend; show errors next to fields.]
200
+ ai:
201
+ use_for: [Rails form_with wrappers]
202
+ avoid: [bypassing CSRF or Rails form helpers]
203
+
204
+ input:
205
+ category: forms
206
+ client: false
207
+ can_have_client: false
208
+ files:
209
+ - app/components/senren/input_component.rb
210
+ - app/components/senren/input_component.html.erb
211
+ depends_on: [label]
212
+ pairs_with: [form, label, alert]
213
+ variants: [default, error]
214
+ accessibility: [Pair with label; use aria-invalid and aria-describedby on errors.]
215
+ ai:
216
+ use_for: [text, email, password, number, search, url]
217
+ avoid: [storing structured data in a single text input]
218
+
219
+ textarea:
220
+ category: forms
221
+ client: false
222
+ can_have_client: false
223
+ files:
224
+ - app/components/senren/textarea_component.rb
225
+ - app/components/senren/textarea_component.html.erb
226
+ depends_on: [label]
227
+ pairs_with: [form, label, alert]
228
+ variants: [default, error]
229
+ accessibility: [Pair with label; provide visible character counter for limits.]
230
+ ai:
231
+ use_for: [multi-line text]
232
+ avoid: [rich-text editing - use rich_text_editor_lite]
233
+
234
+ checkbox:
235
+ category: forms
236
+ client: false
237
+ can_have_client: false
238
+ files:
239
+ - app/components/senren/checkbox_component.rb
240
+ - app/components/senren/checkbox_component.html.erb
241
+ depends_on: [label]
242
+ pairs_with: [form, label, checkbox_group]
243
+ variants: [default]
244
+ accessibility: [Always pair with label or aria-label.]
245
+ ai:
246
+ use_for: [boolean preferences, multi-select via checkbox_group]
247
+ avoid: [single-choice scenarios - use radio_button]
248
+
249
+ checkbox_group:
250
+ category: forms
251
+ client: false
252
+ can_have_client: false
253
+ files:
254
+ - app/components/senren/checkbox_group_component.rb
255
+ - app/components/senren/checkbox_group_component.html.erb
256
+ depends_on: [checkbox, label]
257
+ pairs_with: [form, label]
258
+ variants: [default]
259
+ accessibility: [Wrap in fieldset/legend.]
260
+ ai:
261
+ use_for: [multi-select boolean groups]
262
+ avoid: [single-choice groups]
263
+
264
+ radio_button:
265
+ category: forms
266
+ client: false
267
+ can_have_client: false
268
+ files:
269
+ - app/components/senren/radio_button_component.rb
270
+ - app/components/senren/radio_button_component.html.erb
271
+ depends_on: [label]
272
+ pairs_with: [form, label]
273
+ variants: [default]
274
+ accessibility: [Group via fieldset/legend; share name attribute.]
275
+ ai:
276
+ use_for: [single-choice option groups]
277
+ avoid: [multi-select - use checkbox_group]
278
+
279
+ native_select:
280
+ category: forms
281
+ client: false
282
+ can_have_client: false
283
+ files:
284
+ - app/components/senren/native_select_component.rb
285
+ - app/components/senren/native_select_component.html.erb
286
+ depends_on: [label]
287
+ pairs_with: [form, label]
288
+ variants: [default, error]
289
+ accessibility: [Pair with label; native option text must be meaningful.]
290
+ ai:
291
+ use_for: [short option lists, mobile-friendly selects]
292
+ avoid: [long lists - prefer combobox]
293
+
294
+ select:
295
+ category: forms
296
+ client: true
297
+ can_have_client: true
298
+ controller: senren--select
299
+ files:
300
+ - app/components/senren/select_component.rb
301
+ - app/components/senren/select_component.html.erb
302
+ - app/javascript/controllers/senren/select_controller.js
303
+ depends_on: [label, button]
304
+ pairs_with: [form, label]
305
+ variants: [default, error]
306
+ accessibility: [aria-haspopup=listbox; arrow keys for navigation; type-ahead.]
307
+ ai:
308
+ use_for: [styled selects with custom option rendering]
309
+ avoid: [very long lists - use combobox]
310
+
311
+ switch:
312
+ category: forms
313
+ client: false
314
+ can_have_client: false
315
+ files:
316
+ - app/components/senren/switch_component.rb
317
+ - app/components/senren/switch_component.html.erb
318
+ depends_on: [label]
319
+ pairs_with: [form, label, settings_section]
320
+ variants: [default]
321
+ accessibility: [role=switch with aria-checked.]
322
+ ai:
323
+ use_for: [boolean toggles, settings]
324
+ avoid: [actions that need confirmation - use button + dialog]
325
+
326
+ masked_input:
327
+ category: forms
328
+ client: true
329
+ can_have_client: true
330
+ controller: senren--masked-input
331
+ files:
332
+ - app/components/senren/masked_input_component.rb
333
+ - app/components/senren/masked_input_component.html.erb
334
+ - app/javascript/controllers/senren/masked_input_controller.js
335
+ depends_on: [input, label]
336
+ pairs_with: [form, label]
337
+ variants: [default, error]
338
+ accessibility: [Always include a description of expected format.]
339
+ ai:
340
+ use_for: [phone, postal code, credit card, custom formats]
341
+ avoid: [free text fields - use input]
342
+
343
+ # ---------------------------------------------------------------- Phase 3
344
+ dialog:
345
+ category: overlays
346
+ client: true
347
+ can_have_client: true
348
+ controller: senren--dialog
349
+ files:
350
+ - app/components/senren/dialog_component.rb
351
+ - app/components/senren/dialog_component.html.erb
352
+ - app/javascript/controllers/senren/dialog_controller.js
353
+ depends_on: [button]
354
+ pairs_with: [form, alert_dialog]
355
+ variants: [default]
356
+ accessibility:
357
+ - Must have a visible title.
358
+ - Must support Escape key close.
359
+ - Must trap focus and restore on close.
360
+ ai:
361
+ use_for: [confirmation modal, short form modal]
362
+ avoid: [long multi-step workflows, full page replacement]
363
+
364
+ alert_dialog:
365
+ category: overlays
366
+ client: true
367
+ can_have_client: true
368
+ controller: senren--alert-dialog
369
+ files:
370
+ - app/components/senren/alert_dialog_component.rb
371
+ - app/components/senren/alert_dialog_component.html.erb
372
+ - app/javascript/controllers/senren/alert_dialog_controller.js
373
+ depends_on: [button, dialog]
374
+ pairs_with: [button]
375
+ variants: [default, destructive]
376
+ accessibility:
377
+ - role=alertdialog with aria-labelledby.
378
+ - Confirm action receives initial focus only when non-destructive.
379
+ ai:
380
+ use_for: [destructive action confirmation, irreversible operations]
381
+ avoid: [routine modal forms - use dialog]
382
+
383
+ dropdown_menu:
384
+ category: overlays
385
+ client: true
386
+ can_have_client: true
387
+ controller: senren--dropdown-menu
388
+ files:
389
+ - app/components/senren/dropdown_menu_component.rb
390
+ - app/components/senren/dropdown_menu_component.html.erb
391
+ - app/javascript/controllers/senren/dropdown_menu_controller.js
392
+ depends_on: [button]
393
+ pairs_with: [button, table, top_nav]
394
+ variants: [default]
395
+ accessibility:
396
+ - role=menu with role=menuitem children.
397
+ - Arrow keys to navigate; Escape to close; type-ahead optional.
398
+ ai:
399
+ use_for: [contextual actions on rows, account menus]
400
+ avoid: [primary navigation - use top_nav or sidebar]
401
+
402
+ popover:
403
+ category: overlays
404
+ client: true
405
+ can_have_client: true
406
+ controller: senren--popover
407
+ files:
408
+ - app/components/senren/popover_component.rb
409
+ - app/components/senren/popover_component.html.erb
410
+ - app/javascript/controllers/senren/popover_controller.js
411
+ depends_on: [button]
412
+ pairs_with: [button, form]
413
+ variants: [default]
414
+ accessibility: [aria-expanded on trigger; manage focus on open.]
415
+ ai:
416
+ use_for: [small contextual UI like filters or color pickers]
417
+ avoid: [large content - use dialog or sheet]
418
+
419
+ tooltip:
420
+ category: overlays
421
+ client: true
422
+ can_have_client: true
423
+ controller: senren--tooltip
424
+ files:
425
+ - app/components/senren/tooltip_component.rb
426
+ - app/components/senren/tooltip_component.html.erb
427
+ - app/javascript/controllers/senren/tooltip_controller.js
428
+ depends_on: []
429
+ pairs_with: [button, badge, icon]
430
+ variants: [default]
431
+ accessibility:
432
+ - Use aria-describedby; never put critical info in tooltips alone.
433
+ - Keyboard focus must show the tooltip.
434
+ ai:
435
+ use_for: [supplementary hints on hover/focus]
436
+ avoid: [storing required information only in tooltip]
437
+
438
+ hover_card:
439
+ category: overlays
440
+ client: true
441
+ can_have_client: true
442
+ controller: senren--hover-card
443
+ files:
444
+ - app/components/senren/hover_card_component.rb
445
+ - app/components/senren/hover_card_component.html.erb
446
+ - app/javascript/controllers/senren/hover_card_controller.js
447
+ depends_on: []
448
+ pairs_with: [avatar, link]
449
+ variants: [default]
450
+ accessibility: [Provide keyboard activation; do not block touch users.]
451
+ ai:
452
+ use_for: [user hover previews, link previews]
453
+ avoid: [interactive forms - use popover or dialog]
454
+
455
+ sheet:
456
+ category: overlays
457
+ client: true
458
+ can_have_client: true
459
+ controller: senren--sheet
460
+ files:
461
+ - app/components/senren/sheet_component.rb
462
+ - app/components/senren/sheet_component.html.erb
463
+ - app/javascript/controllers/senren/sheet_controller.js
464
+ depends_on: [button]
465
+ pairs_with: [form]
466
+ variants: [right, left, top, bottom]
467
+ accessibility:
468
+ - Trap focus when open.
469
+ - Escape to close.
470
+ - aria-modal=true while open.
471
+ ai:
472
+ use_for: [side panels, mobile filters, edit panels]
473
+ avoid: [destructive confirmation - use alert_dialog]
474
+
475
+ context_menu:
476
+ category: overlays
477
+ client: true
478
+ can_have_client: true
479
+ controller: senren--context-menu
480
+ files:
481
+ - app/components/senren/context_menu_component.rb
482
+ - app/components/senren/context_menu_component.html.erb
483
+ - app/javascript/controllers/senren/context_menu_controller.js
484
+ depends_on: [dropdown_menu]
485
+ pairs_with: [table, data_table]
486
+ variants: [default]
487
+ accessibility:
488
+ - Triggered by right-click and keyboard equivalent (Shift+F10).
489
+ - Must be reachable without a pointer.
490
+ ai:
491
+ use_for: [right-click actions on rows or canvases]
492
+ avoid: [primary navigation; main page actions]
493
+
494
+ # ------------------------------------------------------ Phase 4 (stubs)
495
+ breadcrumb:
496
+ category: navigation
497
+ client: false
498
+ can_have_client: false
499
+ stub: false
500
+ files:
501
+ - app/components/senren/breadcrumb_component.rb
502
+ - app/components/senren/breadcrumb_component.html.erb
503
+ depends_on: [link]
504
+ pairs_with: [page_header]
505
+ variants: [default]
506
+ accessibility: [aria-label="Breadcrumb"; mark current page with aria-current=page.]
507
+ ai:
508
+ use_for: [hierarchical navigation context]
509
+ avoid: [non-hierarchical pages]
510
+
511
+ tabs:
512
+ category: navigation
513
+ client: true
514
+ can_have_client: true
515
+ controller: senren--tabs
516
+ stub: false
517
+ files:
518
+ - app/components/senren/tabs_component.rb
519
+ - app/components/senren/tabs_component.html.erb
520
+ - app/javascript/controllers/senren/tabs_controller.js
521
+ depends_on: []
522
+ pairs_with: [card]
523
+ variants: [default, underline]
524
+ accessibility: [role=tablist with arrow-key navigation and aria-controls.]
525
+ ai:
526
+ use_for: [switching between related views in the same context]
527
+ avoid: [non-related sections - use a separate page]
528
+
529
+ accordion:
530
+ category: navigation
531
+ client: true
532
+ can_have_client: true
533
+ controller: senren--accordion
534
+ stub: false
535
+ files:
536
+ - app/components/senren/accordion_component.rb
537
+ - app/components/senren/accordion_component.html.erb
538
+ - app/javascript/controllers/senren/accordion_controller.js
539
+ depends_on: []
540
+ pairs_with: [card]
541
+ variants: [single, multiple]
542
+ accessibility: [Buttons toggle aria-expanded; panels use aria-hidden.]
543
+ ai:
544
+ use_for: [FAQ, collapsible content sections]
545
+ avoid: [primary navigation]
546
+
547
+ collapsible:
548
+ category: navigation
549
+ client: true
550
+ can_have_client: true
551
+ controller: senren--collapsible
552
+ stub: false
553
+ files:
554
+ - app/components/senren/collapsible_component.rb
555
+ - app/components/senren/collapsible_component.html.erb
556
+ - app/javascript/controllers/senren/collapsible_controller.js
557
+ depends_on: []
558
+ pairs_with: [sidebar, settings_section]
559
+ variants: [default]
560
+ accessibility: [aria-expanded on trigger; aria-controls on panel.]
561
+ ai:
562
+ use_for: [single show/hide toggles]
563
+ avoid: [groups - use accordion]
564
+
565
+ sidebar:
566
+ category: navigation
567
+ client: true
568
+ can_have_client: true
569
+ controller: senren--sidebar
570
+ stub: false
571
+ files:
572
+ - app/components/senren/sidebar_component.rb
573
+ - app/components/senren/sidebar_component.html.erb
574
+ - app/javascript/controllers/senren/sidebar_controller.js
575
+ depends_on: [button]
576
+ pairs_with: [app_shell, top_nav]
577
+ variants: [default, compact]
578
+ accessibility: [role=navigation with aria-label; collapsed state retains tab order.]
579
+ ai:
580
+ use_for: [primary app navigation in dashboards]
581
+ avoid: [marketing sites - use top_nav]
582
+
583
+ theme_toggle:
584
+ category: navigation
585
+ client: true
586
+ can_have_client: true
587
+ controller: senren--theme-toggle
588
+ stub: false
589
+ files:
590
+ - app/components/senren/theme_toggle_component.rb
591
+ - app/components/senren/theme_toggle_component.html.erb
592
+ - app/javascript/controllers/senren/theme_toggle_controller.js
593
+ depends_on: [button]
594
+ pairs_with: [top_nav]
595
+ variants: [default]
596
+ accessibility: [Announce current theme via aria-pressed or aria-label.]
597
+ ai:
598
+ use_for: [light/dark/system theme switching]
599
+ avoid: [non-theme settings]
600
+
601
+ shortcut_key:
602
+ category: navigation
603
+ client: false
604
+ can_have_client: false
605
+ stub: false
606
+ files:
607
+ - app/components/senren/shortcut_key_component.rb
608
+ - app/components/senren/shortcut_key_component.html.erb
609
+ depends_on: []
610
+ pairs_with: [tooltip, command]
611
+ variants: [default]
612
+ accessibility: [Use kbd elements; avoid relying on key visuals alone for instructions.]
613
+ ai:
614
+ use_for: [displaying keyboard shortcuts in tooltips and menus]
615
+ avoid: [as a button - shortcut_key is presentational]
616
+
617
+ # ------------------------------------------------------ Phase 5 (stubs)
618
+ table:
619
+ category: data
620
+ client: false
621
+ can_have_client: false
622
+ stub: false
623
+ files:
624
+ - app/components/senren/table_component.rb
625
+ - app/components/senren/table_component.html.erb
626
+ depends_on: []
627
+ pairs_with: [pagination, dropdown_menu, badge]
628
+ variants: [default, compact]
629
+ accessibility: [Use real th/scope; caption for table summary.]
630
+ ai:
631
+ use_for: [tabular data display]
632
+ avoid: [layout - use grid/flex]
633
+
634
+ pagination:
635
+ category: data
636
+ client: false
637
+ can_have_client: false
638
+ stub: false
639
+ files:
640
+ - app/components/senren/pagination_component.rb
641
+ - app/components/senren/pagination_component.html.erb
642
+ depends_on: [button, link]
643
+ pairs_with: [table, data_table]
644
+ variants: [default]
645
+ accessibility: [aria-current=page on current; nav landmark with label.]
646
+ ai:
647
+ use_for: [paged lists and tables]
648
+ avoid: [infinite scroll feeds]
649
+
650
+ progress:
651
+ category: data
652
+ client: false
653
+ can_have_client: false
654
+ stub: false
655
+ files:
656
+ - app/components/senren/progress_component.rb
657
+ - app/components/senren/progress_component.html.erb
658
+ depends_on: []
659
+ pairs_with: [card, alert]
660
+ variants: [default, success, warning, destructive]
661
+ accessibility: [role=progressbar with aria-valuenow/min/max.]
662
+ ai:
663
+ use_for: [task progress, file upload, loading bars]
664
+ avoid: [decorative animation]
665
+
666
+ clipboard:
667
+ category: data
668
+ client: true
669
+ can_have_client: true
670
+ controller: senren--clipboard
671
+ stub: false
672
+ files:
673
+ - app/components/senren/clipboard_component.rb
674
+ - app/components/senren/clipboard_component.html.erb
675
+ - app/javascript/controllers/senren/clipboard_controller.js
676
+ depends_on: [button]
677
+ pairs_with: [api_key_field, codeblock]
678
+ variants: [default]
679
+ accessibility: [Announce copy success via aria-live region.]
680
+ ai:
681
+ use_for: [copy to clipboard buttons]
682
+ avoid: [non-text content]
683
+
684
+ codeblock:
685
+ category: rich
686
+ client: false
687
+ can_have_client: true
688
+ stub: false
689
+ files:
690
+ - app/components/senren/codeblock_component.rb
691
+ - app/components/senren/codeblock_component.html.erb
692
+ depends_on: []
693
+ pairs_with: [clipboard]
694
+ variants: [default]
695
+ accessibility: [Use pre/code; preserve whitespace.]
696
+ ai:
697
+ use_for: [displaying code samples]
698
+ avoid: [running code - this is read-only display]
699
+
700
+ command:
701
+ category: rich
702
+ client: true
703
+ can_have_client: true
704
+ controller: senren--command
705
+ stub: false
706
+ files:
707
+ - app/components/senren/command_component.rb
708
+ - app/components/senren/command_component.html.erb
709
+ - app/javascript/controllers/senren/command_controller.js
710
+ depends_on: [dialog, input]
711
+ pairs_with: [shortcut_key, dropdown_menu]
712
+ variants: [default]
713
+ accessibility: [aria-activedescendant for current option; type-ahead filtering.]
714
+ ai:
715
+ use_for: [command palettes, quick search]
716
+ avoid: [persistent navigation]
717
+
718
+ combobox:
719
+ category: forms
720
+ client: true
721
+ can_have_client: true
722
+ controller: senren--combobox
723
+ stub: false
724
+ files:
725
+ - app/components/senren/combobox_component.rb
726
+ - app/components/senren/combobox_component.html.erb
727
+ - app/javascript/controllers/senren/combobox_controller.js
728
+ depends_on: [input, button]
729
+ pairs_with: [form, label]
730
+ variants: [default, error]
731
+ accessibility: [WAI-ARIA combobox pattern; aria-expanded; aria-activedescendant.]
732
+ ai:
733
+ use_for: [searchable selects, large option lists]
734
+ avoid: [short, fixed lists - use native_select]
735
+
736
+ calendar:
737
+ category: forms
738
+ client: true
739
+ can_have_client: true
740
+ controller: senren--calendar
741
+ stub: false
742
+ files:
743
+ - app/components/senren/calendar_component.rb
744
+ - app/components/senren/calendar_component.html.erb
745
+ - app/javascript/controllers/senren/calendar_controller.js
746
+ depends_on: [button]
747
+ pairs_with: [date_picker]
748
+ variants: [default]
749
+ accessibility: [grid with row/columnheader; arrow keys to navigate days.]
750
+ ai:
751
+ use_for: [date display and selection]
752
+ avoid: [time-only selection]
753
+
754
+ date_picker:
755
+ category: forms
756
+ client: true
757
+ can_have_client: true
758
+ controller: senren--date-picker
759
+ stub: false
760
+ files:
761
+ - app/components/senren/date_picker_component.rb
762
+ - app/components/senren/date_picker_component.html.erb
763
+ - app/javascript/controllers/senren/date_picker_controller.js
764
+ depends_on: [calendar, popover, input]
765
+ pairs_with: [form, label]
766
+ variants: [default, error]
767
+ accessibility: [Pair input with hidden calendar; provide manual typing fallback.]
768
+ ai:
769
+ use_for: [date input fields]
770
+ avoid: [date ranges - use a range component once added]
771
+
772
+ carousel:
773
+ category: rich
774
+ client: true
775
+ can_have_client: true
776
+ controller: senren--carousel
777
+ stub: false
778
+ files:
779
+ - app/components/senren/carousel_component.rb
780
+ - app/components/senren/carousel_component.html.erb
781
+ - app/javascript/controllers/senren/carousel_controller.js
782
+ depends_on: [button]
783
+ pairs_with: [card]
784
+ variants: [default]
785
+ accessibility: [Provide pause/prev/next controls; respect prefers-reduced-motion.]
786
+ ai:
787
+ use_for: [media galleries, marketing highlights]
788
+ avoid: [primary navigation, important content gating]
789
+
790
+ # ------------------------------------------------------ Phase 6
791
+ app_shell:
792
+ category: saas
793
+ client: false
794
+ can_have_client: false
795
+ stub: false
796
+ files:
797
+ - app/components/senren/app_shell_component.rb
798
+ - app/components/senren/app_shell_component.html.erb
799
+ depends_on: [sidebar, top_nav]
800
+ pairs_with: [page_header]
801
+ variants: [default, compact]
802
+ accessibility: [Provide skip-to-content link; main landmark for page body.]
803
+ ai:
804
+ use_for: [SaaS dashboard layout root]
805
+ avoid: [marketing pages]
806
+
807
+ top_nav:
808
+ category: saas
809
+ client: false
810
+ can_have_client: false
811
+ stub: false
812
+ files:
813
+ - app/components/senren/top_nav_component.rb
814
+ - app/components/senren/top_nav_component.html.erb
815
+ depends_on: [link, button, dropdown_menu]
816
+ pairs_with: [app_shell, sidebar]
817
+ variants: [default]
818
+ accessibility: [role=banner; nav landmark with label.]
819
+ ai:
820
+ use_for: [global header with brand and account menu]
821
+ avoid: [primary in-app navigation - use sidebar]
822
+
823
+ page_header:
824
+ category: saas
825
+ client: false
826
+ can_have_client: false
827
+ stub: false
828
+ files:
829
+ - app/components/senren/page_header_component.rb
830
+ - app/components/senren/page_header_component.html.erb
831
+ depends_on: [typography, button]
832
+ pairs_with: [breadcrumb, app_shell]
833
+ variants: [default]
834
+ accessibility: [Use h1 for page title.]
835
+ ai:
836
+ use_for: [titled page header with actions]
837
+ avoid: [stacking multiple page_headers per page]
838
+
839
+ empty_state:
840
+ category: saas
841
+ client: false
842
+ can_have_client: false
843
+ stub: false
844
+ files:
845
+ - app/components/senren/empty_state_component.rb
846
+ - app/components/senren/empty_state_component.html.erb
847
+ depends_on: [button, typography]
848
+ pairs_with: [card, table]
849
+ variants: [default, illustrated]
850
+ accessibility: [Provide a meaningful heading and a primary action.]
851
+ ai:
852
+ use_for: [zero-result lists, no-data dashboards]
853
+ avoid: [error states - use alert]
854
+
855
+ stat_card:
856
+ category: saas
857
+ client: false
858
+ can_have_client: false
859
+ stub: false
860
+ files:
861
+ - app/components/senren/stat_card_component.rb
862
+ - app/components/senren/stat_card_component.html.erb
863
+ depends_on: [card, typography]
864
+ pairs_with: [badge]
865
+ variants: [default, success, warning, destructive]
866
+ accessibility: [Avoid color-only meaning; include label and value text.]
867
+ ai:
868
+ use_for: [dashboard KPI tiles]
869
+ avoid: [interactive widgets - use card with explicit controls]
870
+
871
+ settings_section:
872
+ category: saas
873
+ client: false
874
+ can_have_client: false
875
+ stub: false
876
+ files:
877
+ - app/components/senren/settings_section_component.rb
878
+ - app/components/senren/settings_section_component.html.erb
879
+ depends_on: [card, separator, typography]
880
+ pairs_with: [form, switch, button]
881
+ variants: [default]
882
+ accessibility: [Each section has a heading and consistent label/value layout.]
883
+ ai:
884
+ use_for: [grouped settings inside a settings page]
885
+ avoid: [unrelated content blocks]
886
+
887
+ data_table:
888
+ category: saas
889
+ client: true
890
+ can_have_client: true
891
+ controller: senren--data-table
892
+ stub: false
893
+ files:
894
+ - app/components/senren/data_table_component.rb
895
+ - app/components/senren/data_table_component.html.erb
896
+ - app/javascript/controllers/senren/data_table_controller.js
897
+ depends_on: [table, dropdown_menu, pagination]
898
+ pairs_with: [filter_bar, search_input]
899
+ variants: [default]
900
+ accessibility: [Inherits from table; columns must have th/scope.]
901
+ ai:
902
+ use_for: [sortable, filterable record lists]
903
+ avoid: [static layout grids]
904
+
905
+ filter_bar:
906
+ category: saas
907
+ client: false
908
+ can_have_client: false
909
+ stub: false
910
+ files:
911
+ - app/components/senren/filter_bar_component.rb
912
+ - app/components/senren/filter_bar_component.html.erb
913
+ depends_on: [button, dropdown_menu]
914
+ pairs_with: [data_table, search_input]
915
+ variants: [default]
916
+ accessibility: [Filter controls are real form controls with labels.]
917
+ ai:
918
+ use_for: [list filters above tables]
919
+ avoid: [primary navigation]
920
+
921
+ search_input:
922
+ category: saas
923
+ client: false
924
+ can_have_client: false
925
+ stub: false
926
+ files:
927
+ - app/components/senren/search_input_component.rb
928
+ - app/components/senren/search_input_component.html.erb
929
+ depends_on: [input, label]
930
+ pairs_with: [filter_bar, data_table]
931
+ variants: [default]
932
+ accessibility: [aria-label or visible label; type=search; submit on Enter.]
933
+ ai:
934
+ use_for: [search above lists or in headers]
935
+ avoid: [global navigation - use command]
936
+
937
+ bulk_action_bar:
938
+ category: saas
939
+ client: false
940
+ can_have_client: false
941
+ stub: false
942
+ files:
943
+ - app/components/senren/bulk_action_bar_component.rb
944
+ - app/components/senren/bulk_action_bar_component.html.erb
945
+ depends_on: [button, dropdown_menu]
946
+ pairs_with: [data_table, table]
947
+ variants: [default]
948
+ accessibility: [Show count of selected items in an aria-live region.]
949
+ ai:
950
+ use_for: [acting on multiple selected rows]
951
+ avoid: [single-row actions - use dropdown_menu]
952
+
953
+ team_member_list:
954
+ category: saas
955
+ client: false
956
+ can_have_client: false
957
+ stub: false
958
+ files:
959
+ - app/components/senren/team_member_list_component.rb
960
+ - app/components/senren/team_member_list_component.html.erb
961
+ depends_on: [avatar, badge, button, dropdown_menu]
962
+ pairs_with: [invite_member_dialog, search_input]
963
+ variants: [default]
964
+ accessibility: [Use semantic list markup.]
965
+ ai:
966
+ use_for: [team management pages]
967
+ avoid: [arbitrary user lists - compose with table or card]
968
+
969
+ invite_member_dialog:
970
+ category: saas
971
+ client: true
972
+ can_have_client: true
973
+ controller: senren--invite-member-dialog
974
+ stub: false
975
+ files:
976
+ - app/components/senren/invite_member_dialog_component.rb
977
+ - app/components/senren/invite_member_dialog_component.html.erb
978
+ - app/javascript/controllers/senren/invite_member_dialog_controller.js
979
+ depends_on: [dialog, form, input, button]
980
+ pairs_with: [team_member_list]
981
+ variants: [default]
982
+ accessibility: [Inherit dialog accessibility; validate email server-side.]
983
+ ai:
984
+ use_for: [team invitation flow]
985
+ avoid: [bulk imports - use a dedicated screen]
986
+
987
+ billing_plan_card:
988
+ category: saas
989
+ client: false
990
+ can_have_client: false
991
+ stub: false
992
+ files:
993
+ - app/components/senren/billing_plan_card_component.rb
994
+ - app/components/senren/billing_plan_card_component.html.erb
995
+ depends_on: [card, button, badge, typography]
996
+ pairs_with: [settings_section]
997
+ variants: [default, current, recommended]
998
+ accessibility: [Use heading; price and features must be readable.]
999
+ ai:
1000
+ use_for: [pricing page or billing settings]
1001
+ avoid: [feature comparison tables - use table]
1002
+
1003
+ api_key_field:
1004
+ category: saas
1005
+ client: true
1006
+ can_have_client: true
1007
+ controller: senren--api-key-field
1008
+ stub: false
1009
+ files:
1010
+ - app/components/senren/api_key_field_component.rb
1011
+ - app/components/senren/api_key_field_component.html.erb
1012
+ - app/javascript/controllers/senren/api_key_field_controller.js
1013
+ depends_on: [input, button, clipboard]
1014
+ pairs_with: [settings_section]
1015
+ variants: [default]
1016
+ accessibility: [Reveal/hide must be reachable by keyboard; copy via clipboard with announcement.]
1017
+ ai:
1018
+ use_for: [secret key display with reveal/copy/regenerate]
1019
+ avoid: [storing secrets in plain text on the server side]
1020
+
1021
+ activity_feed:
1022
+ category: saas
1023
+ client: false
1024
+ can_have_client: false
1025
+ stub: false
1026
+ files:
1027
+ - app/components/senren/activity_feed_component.rb
1028
+ - app/components/senren/activity_feed_component.html.erb
1029
+ depends_on: [avatar, badge, typography]
1030
+ pairs_with: [card]
1031
+ variants: [default]
1032
+ accessibility: [Use a list; each entry has a relative time element.]
1033
+ ai:
1034
+ use_for: [audit logs, comment threads, change history]
1035
+ avoid: [transient notifications - use alert or toast]
1036
+
1037
+ rich_text_editor_lite:
1038
+ category: rich
1039
+ client: true
1040
+ can_have_client: true
1041
+ controller: senren--rich-text-editor-lite
1042
+ stub: false
1043
+ files:
1044
+ - app/components/senren/rich_text_editor_lite_component.rb
1045
+ - app/components/senren/rich_text_editor_lite_component.html.erb
1046
+ - app/javascript/controllers/senren/rich_text_editor_lite_controller.js
1047
+ depends_on: [button]
1048
+ pairs_with: [form, label]
1049
+ variants: [default]
1050
+ accessibility: [Toolbar buttons must have aria-label; use contenteditable role=textbox.]
1051
+ ai:
1052
+ use_for: [light formatting (bold/italic/links/lists) only]
1053
+ avoid: [tables, embeds, media - out of scope for v0.1]