poetry-ui 0.0.2

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 (442) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +6 -0
  3. data/CHANGELOG.md +3 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +87 -0
  6. data/THIRD_PARTY_NOTICES.md +201 -0
  7. data/app/components/poetry/ui/accordion/component.html.erb +3 -0
  8. data/app/components/poetry/ui/accordion/component.rb +197 -0
  9. data/app/components/poetry/ui/accordion/preview.rb +39 -0
  10. data/app/components/poetry/ui/accordion/style.rb +37 -0
  11. data/app/components/poetry/ui/alert/component.html.erb +6 -0
  12. data/app/components/poetry/ui/alert/component.rb +61 -0
  13. data/app/components/poetry/ui/alert/preview.rb +35 -0
  14. data/app/components/poetry/ui/alert/style.rb +24 -0
  15. data/app/components/poetry/ui/alert_dialog/component.html.erb +17 -0
  16. data/app/components/poetry/ui/alert_dialog/component.rb +208 -0
  17. data/app/components/poetry/ui/alert_dialog/preview.rb +48 -0
  18. data/app/components/poetry/ui/alert_dialog/style.rb +46 -0
  19. data/app/components/poetry/ui/aspect_ratio/component.rb +63 -0
  20. data/app/components/poetry/ui/aspect_ratio/preview.rb +29 -0
  21. data/app/components/poetry/ui/aspect_ratio/style.rb +13 -0
  22. data/app/components/poetry/ui/attachment/component.html.erb +13 -0
  23. data/app/components/poetry/ui/attachment/component.rb +139 -0
  24. data/app/components/poetry/ui/attachment/preview.rb +83 -0
  25. data/app/components/poetry/ui/attachment/style.rb +59 -0
  26. data/app/components/poetry/ui/autocomplete/component.html.erb +16 -0
  27. data/app/components/poetry/ui/autocomplete/component.rb +193 -0
  28. data/app/components/poetry/ui/autocomplete/preview.rb +39 -0
  29. data/app/components/poetry/ui/autocomplete/style.rb +18 -0
  30. data/app/components/poetry/ui/avatar/component.rb +103 -0
  31. data/app/components/poetry/ui/avatar/group_preview.html.erb +11 -0
  32. data/app/components/poetry/ui/avatar/preview.rb +50 -0
  33. data/app/components/poetry/ui/avatar/style.rb +34 -0
  34. data/app/components/poetry/ui/badge/component.rb +73 -0
  35. data/app/components/poetry/ui/badge/preview.rb +54 -0
  36. data/app/components/poetry/ui/badge/style.rb +35 -0
  37. data/app/components/poetry/ui/breadcrumb/component.html.erb +32 -0
  38. data/app/components/poetry/ui/breadcrumb/component.rb +102 -0
  39. data/app/components/poetry/ui/breadcrumb/dropdown_crumb_preview.html.erb +21 -0
  40. data/app/components/poetry/ui/breadcrumb/preview.rb +45 -0
  41. data/app/components/poetry/ui/breadcrumb/style.rb +25 -0
  42. data/app/components/poetry/ui/bubble/component.html.erb +4 -0
  43. data/app/components/poetry/ui/bubble/component.rb +106 -0
  44. data/app/components/poetry/ui/bubble/preview.rb +62 -0
  45. data/app/components/poetry/ui/bubble/style.rb +47 -0
  46. data/app/components/poetry/ui/button/component.html.erb +14 -0
  47. data/app/components/poetry/ui/button/component.rb +168 -0
  48. data/app/components/poetry/ui/button/preview.rb +94 -0
  49. data/app/components/poetry/ui/button/style.rb +41 -0
  50. data/app/components/poetry/ui/button_group/component.rb +70 -0
  51. data/app/components/poetry/ui/button_group/popup_triggers_preview.html.erb +21 -0
  52. data/app/components/poetry/ui/button_group/preview.rb +46 -0
  53. data/app/components/poetry/ui/button_group/style.rb +27 -0
  54. data/app/components/poetry/ui/button_group/text_prefix_preview.html.erb +7 -0
  55. data/app/components/poetry/ui/calendar/component.html.erb +72 -0
  56. data/app/components/poetry/ui/calendar/component.rb +366 -0
  57. data/app/components/poetry/ui/calendar/preview.rb +55 -0
  58. data/app/components/poetry/ui/calendar/style.rb +55 -0
  59. data/app/components/poetry/ui/card/component.html.erb +11 -0
  60. data/app/components/poetry/ui/card/component.rb +90 -0
  61. data/app/components/poetry/ui/card/preview.rb +29 -0
  62. data/app/components/poetry/ui/card/style.rb +23 -0
  63. data/app/components/poetry/ui/carousel/component.html.erb +17 -0
  64. data/app/components/poetry/ui/carousel/component.rb +179 -0
  65. data/app/components/poetry/ui/carousel/preview.rb +67 -0
  66. data/app/components/poetry/ui/carousel/style.rb +39 -0
  67. data/app/components/poetry/ui/checkbox/component.html.erb +22 -0
  68. data/app/components/poetry/ui/checkbox/component.rb +179 -0
  69. data/app/components/poetry/ui/checkbox/preview.rb +68 -0
  70. data/app/components/poetry/ui/checkbox/style.rb +28 -0
  71. data/app/components/poetry/ui/clipboard_text/component.html.erb +15 -0
  72. data/app/components/poetry/ui/clipboard_text/component.rb +154 -0
  73. data/app/components/poetry/ui/clipboard_text/preview.rb +24 -0
  74. data/app/components/poetry/ui/clipboard_text/style.rb +23 -0
  75. data/app/components/poetry/ui/code_block/component.html.erb +13 -0
  76. data/app/components/poetry/ui/code_block/component.rb +141 -0
  77. data/app/components/poetry/ui/code_block/preview.rb +40 -0
  78. data/app/components/poetry/ui/code_block/style.rb +38 -0
  79. data/app/components/poetry/ui/collapsible/component.html.erb +4 -0
  80. data/app/components/poetry/ui/collapsible/component.rb +134 -0
  81. data/app/components/poetry/ui/collapsible/preview.rb +24 -0
  82. data/app/components/poetry/ui/combobox/component.html.erb +49 -0
  83. data/app/components/poetry/ui/combobox/component.rb +1086 -0
  84. data/app/components/poetry/ui/combobox/preview.rb +164 -0
  85. data/app/components/poetry/ui/combobox/style.rb +148 -0
  86. data/app/components/poetry/ui/command/component.html.erb +20 -0
  87. data/app/components/poetry/ui/command/component.rb +526 -0
  88. data/app/components/poetry/ui/command/dialog_component.html.erb +14 -0
  89. data/app/components/poetry/ui/command/dialog_component.rb +206 -0
  90. data/app/components/poetry/ui/command/dialog_preview.rb +39 -0
  91. data/app/components/poetry/ui/command/preview.rb +121 -0
  92. data/app/components/poetry/ui/command/style.rb +78 -0
  93. data/app/components/poetry/ui/composable_trigger.rb +63 -0
  94. data/app/components/poetry/ui/context_menu/component.html.erb +6 -0
  95. data/app/components/poetry/ui/context_menu/component.rb +358 -0
  96. data/app/components/poetry/ui/context_menu/preview.rb +111 -0
  97. data/app/components/poetry/ui/context_menu/style.rb +59 -0
  98. data/app/components/poetry/ui/data_table/component.html.erb +78 -0
  99. data/app/components/poetry/ui/data_table/component.rb +277 -0
  100. data/app/components/poetry/ui/data_table/preview.rb +68 -0
  101. data/app/components/poetry/ui/data_table/state.rb +115 -0
  102. data/app/components/poetry/ui/data_table/style.rb +42 -0
  103. data/app/components/poetry/ui/date_field/component.html.erb +7 -0
  104. data/app/components/poetry/ui/date_field/component.rb +209 -0
  105. data/app/components/poetry/ui/date_field/preview.rb +42 -0
  106. data/app/components/poetry/ui/date_field/style.rb +35 -0
  107. data/app/components/poetry/ui/date_picker/component.html.erb +23 -0
  108. data/app/components/poetry/ui/date_picker/component.rb +242 -0
  109. data/app/components/poetry/ui/date_picker/preview.rb +31 -0
  110. data/app/components/poetry/ui/date_picker/style.rb +25 -0
  111. data/app/components/poetry/ui/deferred/component.html.erb +16 -0
  112. data/app/components/poetry/ui/deferred/component.rb +90 -0
  113. data/app/components/poetry/ui/deferred/preview.rb +24 -0
  114. data/app/components/poetry/ui/dialog/component.html.erb +21 -0
  115. data/app/components/poetry/ui/dialog/component.rb +194 -0
  116. data/app/components/poetry/ui/dialog/preview.rb +33 -0
  117. data/app/components/poetry/ui/dialog/style.rb +30 -0
  118. data/app/components/poetry/ui/drawer/component.html.erb +16 -0
  119. data/app/components/poetry/ui/drawer/component.rb +207 -0
  120. data/app/components/poetry/ui/drawer/preview.rb +69 -0
  121. data/app/components/poetry/ui/drawer/style.rb +105 -0
  122. data/app/components/poetry/ui/dropdown_menu/component.html.erb +6 -0
  123. data/app/components/poetry/ui/dropdown_menu/component.rb +350 -0
  124. data/app/components/poetry/ui/dropdown_menu/preview.rb +101 -0
  125. data/app/components/poetry/ui/dropdown_menu/style.rb +60 -0
  126. data/app/components/poetry/ui/empty/component.html.erb +10 -0
  127. data/app/components/poetry/ui/empty/component.rb +79 -0
  128. data/app/components/poetry/ui/empty/preview.rb +27 -0
  129. data/app/components/poetry/ui/empty/style.rb +27 -0
  130. data/app/components/poetry/ui/family_identity.rb +52 -0
  131. data/app/components/poetry/ui/field/component.html.erb +19 -0
  132. data/app/components/poetry/ui/field/component.rb +183 -0
  133. data/app/components/poetry/ui/field/preview.html.erb +30 -0
  134. data/app/components/poetry/ui/field/preview.rb +72 -0
  135. data/app/components/poetry/ui/field/style.rb +68 -0
  136. data/app/components/poetry/ui/field_group/component.rb +61 -0
  137. data/app/components/poetry/ui/field_group/preview.html.erb +30 -0
  138. data/app/components/poetry/ui/field_group/preview.rb +20 -0
  139. data/app/components/poetry/ui/field_group/style.rb +25 -0
  140. data/app/components/poetry/ui/field_separator/component.rb +70 -0
  141. data/app/components/poetry/ui/field_separator/preview.rb +18 -0
  142. data/app/components/poetry/ui/field_separator/style.rb +20 -0
  143. data/app/components/poetry/ui/fieldset/component.rb +90 -0
  144. data/app/components/poetry/ui/fieldset/preview.html.erb +22 -0
  145. data/app/components/poetry/ui/fieldset/preview.rb +25 -0
  146. data/app/components/poetry/ui/fieldset/style.rb +18 -0
  147. data/app/components/poetry/ui/file_input/component.html.erb +14 -0
  148. data/app/components/poetry/ui/file_input/component.rb +188 -0
  149. data/app/components/poetry/ui/file_input/preview.rb +30 -0
  150. data/app/components/poetry/ui/file_input/style.rb +35 -0
  151. data/app/components/poetry/ui/hover_card/component.html.erb +6 -0
  152. data/app/components/poetry/ui/hover_card/component.rb +201 -0
  153. data/app/components/poetry/ui/hover_card/preview.rb +60 -0
  154. data/app/components/poetry/ui/hover_card/style.rb +17 -0
  155. data/app/components/poetry/ui/icon/component.rb +127 -0
  156. data/app/components/poetry/ui/icon/preview.rb +21 -0
  157. data/app/components/poetry/ui/input/component.rb +80 -0
  158. data/app/components/poetry/ui/input/preview.rb +35 -0
  159. data/app/components/poetry/ui/input/style.rb +18 -0
  160. data/app/components/poetry/ui/input_group/button_preview.html.erb +12 -0
  161. data/app/components/poetry/ui/input_group/component.rb +69 -0
  162. data/app/components/poetry/ui/input_group/preview.rb +23 -0
  163. data/app/components/poetry/ui/input_group/search_preview.html.erb +13 -0
  164. data/app/components/poetry/ui/input_group/style.rb +40 -0
  165. data/app/components/poetry/ui/input_group/textarea_preview.html.erb +10 -0
  166. data/app/components/poetry/ui/input_group_field.rb +61 -0
  167. data/app/components/poetry/ui/input_otp/component.html.erb +25 -0
  168. data/app/components/poetry/ui/input_otp/component.rb +250 -0
  169. data/app/components/poetry/ui/input_otp/preview.rb +91 -0
  170. data/app/components/poetry/ui/input_otp/style.rb +36 -0
  171. data/app/components/poetry/ui/item/component.html.erb +13 -0
  172. data/app/components/poetry/ui/item/component.rb +99 -0
  173. data/app/components/poetry/ui/item/group_preview.html.erb +15 -0
  174. data/app/components/poetry/ui/item/preview.rb +52 -0
  175. data/app/components/poetry/ui/item/style.rb +45 -0
  176. data/app/components/poetry/ui/kbd/component.rb +47 -0
  177. data/app/components/poetry/ui/kbd/group_preview.html.erb +4 -0
  178. data/app/components/poetry/ui/kbd/preview.rb +23 -0
  179. data/app/components/poetry/ui/kbd/style.rb +13 -0
  180. data/app/components/poetry/ui/label/component.rb +46 -0
  181. data/app/components/poetry/ui/label/preview.html.erb +5 -0
  182. data/app/components/poetry/ui/label/preview.rb +16 -0
  183. data/app/components/poetry/ui/label/style.rb +13 -0
  184. data/app/components/poetry/ui/link/component.rb +62 -0
  185. data/app/components/poetry/ui/link/preview.rb +31 -0
  186. data/app/components/poetry/ui/link/style.rb +20 -0
  187. data/app/components/poetry/ui/marker/component.html.erb +4 -0
  188. data/app/components/poetry/ui/marker/component.rb +81 -0
  189. data/app/components/poetry/ui/marker/preview.rb +43 -0
  190. data/app/components/poetry/ui/marker/style.rb +24 -0
  191. data/app/components/poetry/ui/menubar/component.html.erb +3 -0
  192. data/app/components/poetry/ui/menubar/component.rb +493 -0
  193. data/app/components/poetry/ui/menubar/preview.rb +164 -0
  194. data/app/components/poetry/ui/menubar/style.rb +71 -0
  195. data/app/components/poetry/ui/menus.rb +386 -0
  196. data/app/components/poetry/ui/message/component.html.erb +8 -0
  197. data/app/components/poetry/ui/message/component.rb +64 -0
  198. data/app/components/poetry/ui/message/preview.rb +33 -0
  199. data/app/components/poetry/ui/message/style.rb +29 -0
  200. data/app/components/poetry/ui/message_scroller/component.html.erb +15 -0
  201. data/app/components/poetry/ui/message_scroller/component.rb +163 -0
  202. data/app/components/poetry/ui/message_scroller/preview.rb +33 -0
  203. data/app/components/poetry/ui/message_scroller/style.rb +39 -0
  204. data/app/components/poetry/ui/metadata_list/component.rb +88 -0
  205. data/app/components/poetry/ui/metadata_list/preview.rb +55 -0
  206. data/app/components/poetry/ui/metadata_list/style.rb +35 -0
  207. data/app/components/poetry/ui/meter/component.rb +117 -0
  208. data/app/components/poetry/ui/meter/preview.rb +28 -0
  209. data/app/components/poetry/ui/meter/style.rb +16 -0
  210. data/app/components/poetry/ui/native_select/component.rb +124 -0
  211. data/app/components/poetry/ui/native_select/labelled_preview.html.erb +7 -0
  212. data/app/components/poetry/ui/native_select/preview.rb +20 -0
  213. data/app/components/poetry/ui/native_select/style.rb +22 -0
  214. data/app/components/poetry/ui/navigation_menu/bar_preview.html.erb +14 -0
  215. data/app/components/poetry/ui/navigation_menu/component.html.erb +25 -0
  216. data/app/components/poetry/ui/navigation_menu/component.rb +254 -0
  217. data/app/components/poetry/ui/navigation_menu/preview.rb +20 -0
  218. data/app/components/poetry/ui/navigation_menu/style.rb +73 -0
  219. data/app/components/poetry/ui/navigation_menu/viewport_preview.html.erb +23 -0
  220. data/app/components/poetry/ui/number_field/component.html.erb +19 -0
  221. data/app/components/poetry/ui/number_field/component.rb +263 -0
  222. data/app/components/poetry/ui/number_field/preview.rb +66 -0
  223. data/app/components/poetry/ui/number_field/style.rb +14 -0
  224. data/app/components/poetry/ui/pagination/component.html.erb +40 -0
  225. data/app/components/poetry/ui/pagination/component.rb +182 -0
  226. data/app/components/poetry/ui/pagination/preview.rb +47 -0
  227. data/app/components/poetry/ui/pagination/style.rb +27 -0
  228. data/app/components/poetry/ui/popover/component.html.erb +17 -0
  229. data/app/components/poetry/ui/popover/component.rb +203 -0
  230. data/app/components/poetry/ui/popover/preview.html.erb +29 -0
  231. data/app/components/poetry/ui/popover/preview.rb +66 -0
  232. data/app/components/poetry/ui/popover/style.rb +26 -0
  233. data/app/components/poetry/ui/popper_consumer.rb +69 -0
  234. data/app/components/poetry/ui/progress/component.rb +95 -0
  235. data/app/components/poetry/ui/progress/preview.rb +22 -0
  236. data/app/components/poetry/ui/progress/style.rb +19 -0
  237. data/app/components/poetry/ui/questionnaire/component.html.erb +80 -0
  238. data/app/components/poetry/ui/questionnaire/component.rb +396 -0
  239. data/app/components/poetry/ui/questionnaire/preview.rb +99 -0
  240. data/app/components/poetry/ui/questionnaire/style.rb +53 -0
  241. data/app/components/poetry/ui/radio_group/component.html.erb +4 -0
  242. data/app/components/poetry/ui/radio_group/component.rb +323 -0
  243. data/app/components/poetry/ui/radio_group/preview.rb +109 -0
  244. data/app/components/poetry/ui/radio_group/style.rb +53 -0
  245. data/app/components/poetry/ui/resizable/component.html.erb +10 -0
  246. data/app/components/poetry/ui/resizable/component.rb +178 -0
  247. data/app/components/poetry/ui/resizable/preview.rb +42 -0
  248. data/app/components/poetry/ui/resizable/style.rb +30 -0
  249. data/app/components/poetry/ui/scroll_area/component.rb +76 -0
  250. data/app/components/poetry/ui/scroll_area/preview.rb +35 -0
  251. data/app/components/poetry/ui/scroll_area/style.rb +19 -0
  252. data/app/components/poetry/ui/search_field/component.html.erb +15 -0
  253. data/app/components/poetry/ui/search_field/component.rb +137 -0
  254. data/app/components/poetry/ui/search_field/preview.rb +23 -0
  255. data/app/components/poetry/ui/search_field/style.rb +18 -0
  256. data/app/components/poetry/ui/select/component.html.erb +15 -0
  257. data/app/components/poetry/ui/select/component.rb +639 -0
  258. data/app/components/poetry/ui/select/preview.rb +113 -0
  259. data/app/components/poetry/ui/select/style.rb +58 -0
  260. data/app/components/poetry/ui/sensitive_input/component.html.erb +29 -0
  261. data/app/components/poetry/ui/sensitive_input/component.rb +274 -0
  262. data/app/components/poetry/ui/sensitive_input/preview.rb +40 -0
  263. data/app/components/poetry/ui/sensitive_input/style.rb +49 -0
  264. data/app/components/poetry/ui/separator/component.rb +57 -0
  265. data/app/components/poetry/ui/separator/preview.rb +19 -0
  266. data/app/components/poetry/ui/separator/style.rb +15 -0
  267. data/app/components/poetry/ui/sheet/component.html.erb +21 -0
  268. data/app/components/poetry/ui/sheet/component.rb +97 -0
  269. data/app/components/poetry/ui/sheet/preview.rb +57 -0
  270. data/app/components/poetry/ui/sheet/style.rb +47 -0
  271. data/app/components/poetry/ui/sidebar/component.html.erb +20 -0
  272. data/app/components/poetry/ui/sidebar/component.rb +249 -0
  273. data/app/components/poetry/ui/sidebar/preview.rb +21 -0
  274. data/app/components/poetry/ui/sidebar/shell_preview.html.erb +47 -0
  275. data/app/components/poetry/ui/sidebar/style.rb +139 -0
  276. data/app/components/poetry/ui/skeleton/component.rb +39 -0
  277. data/app/components/poetry/ui/skeleton/preview.rb +22 -0
  278. data/app/components/poetry/ui/skeleton/style.rb +13 -0
  279. data/app/components/poetry/ui/slider/component.html.erb +13 -0
  280. data/app/components/poetry/ui/slider/component.rb +360 -0
  281. data/app/components/poetry/ui/slider/preview.rb +73 -0
  282. data/app/components/poetry/ui/slider/style.rb +55 -0
  283. data/app/components/poetry/ui/spinner/component.rb +58 -0
  284. data/app/components/poetry/ui/spinner/preview.rb +22 -0
  285. data/app/components/poetry/ui/spinner/style.rb +12 -0
  286. data/app/components/poetry/ui/stat/component.html.erb +13 -0
  287. data/app/components/poetry/ui/stat/component.rb +102 -0
  288. data/app/components/poetry/ui/stat/preview.rb +47 -0
  289. data/app/components/poetry/ui/stat/style.rb +29 -0
  290. data/app/components/poetry/ui/switch/component.html.erb +15 -0
  291. data/app/components/poetry/ui/switch/component.rb +136 -0
  292. data/app/components/poetry/ui/switch/preview.rb +66 -0
  293. data/app/components/poetry/ui/switch/style.rb +22 -0
  294. data/app/components/poetry/ui/table/component.html.erb +3 -0
  295. data/app/components/poetry/ui/table/component.rb +93 -0
  296. data/app/components/poetry/ui/table/preview.rb +96 -0
  297. data/app/components/poetry/ui/table/style.rb +26 -0
  298. data/app/components/poetry/ui/tabs/component.html.erb +10 -0
  299. data/app/components/poetry/ui/tabs/component.rb +253 -0
  300. data/app/components/poetry/ui/tabs/preview.rb +43 -0
  301. data/app/components/poetry/ui/tabs/style.rb +49 -0
  302. data/app/components/poetry/ui/tag_group/component.html.erb +8 -0
  303. data/app/components/poetry/ui/tag_group/component.rb +203 -0
  304. data/app/components/poetry/ui/tag_group/preview.rb +43 -0
  305. data/app/components/poetry/ui/tag_group/style.rb +29 -0
  306. data/app/components/poetry/ui/textarea/component.rb +64 -0
  307. data/app/components/poetry/ui/textarea/preview.rb +75 -0
  308. data/app/components/poetry/ui/textarea/style.rb +18 -0
  309. data/app/components/poetry/ui/time_field/component.html.erb +6 -0
  310. data/app/components/poetry/ui/time_field/component.rb +85 -0
  311. data/app/components/poetry/ui/time_field/preview.rb +40 -0
  312. data/app/components/poetry/ui/time_field/style.rb +30 -0
  313. data/app/components/poetry/ui/timeline/component.rb +134 -0
  314. data/app/components/poetry/ui/timeline/preview.rb +61 -0
  315. data/app/components/poetry/ui/timeline/style.rb +54 -0
  316. data/app/components/poetry/ui/toast/component.html.erb +24 -0
  317. data/app/components/poetry/ui/toast/component.rb +178 -0
  318. data/app/components/poetry/ui/toast/preview.rb +69 -0
  319. data/app/components/poetry/ui/toast/style.rb +33 -0
  320. data/app/components/poetry/ui/toast_trigger/addressed_preview.html.erb +12 -0
  321. data/app/components/poetry/ui/toast_trigger/component.html.erb +3 -0
  322. data/app/components/poetry/ui/toast_trigger/component.rb +82 -0
  323. data/app/components/poetry/ui/toast_trigger/default_preview.html.erb +11 -0
  324. data/app/components/poetry/ui/toast_trigger/preview.rb +19 -0
  325. data/app/components/poetry/ui/toaster/component.html.erb +1 -0
  326. data/app/components/poetry/ui/toaster/component.rb +99 -0
  327. data/app/components/poetry/ui/toaster/positions_preview.html.erb +24 -0
  328. data/app/components/poetry/ui/toaster/preview.rb +46 -0
  329. data/app/components/poetry/ui/toaster/stamp_preview.html.erb +15 -0
  330. data/app/components/poetry/ui/toaster/style.rb +26 -0
  331. data/app/components/poetry/ui/toggle/component.rb +120 -0
  332. data/app/components/poetry/ui/toggle/preview.rb +79 -0
  333. data/app/components/poetry/ui/toggle/style.rb +33 -0
  334. data/app/components/poetry/ui/toggle_group/component.html.erb +4 -0
  335. data/app/components/poetry/ui/toggle_group/component.rb +252 -0
  336. data/app/components/poetry/ui/toggle_group/preview.rb +143 -0
  337. data/app/components/poetry/ui/toggle_group/style.rb +33 -0
  338. data/app/components/poetry/ui/toolbar/component.rb +125 -0
  339. data/app/components/poetry/ui/toolbar/preview.rb +41 -0
  340. data/app/components/poetry/ui/toolbar/style.rb +20 -0
  341. data/app/components/poetry/ui/tooltip/component.html.erb +20 -0
  342. data/app/components/poetry/ui/tooltip/component.rb +185 -0
  343. data/app/components/poetry/ui/tooltip/preview.rb +74 -0
  344. data/app/components/poetry/ui/tooltip/previews/provider_row.html.erb +25 -0
  345. data/app/components/poetry/ui/tooltip/style.rb +24 -0
  346. data/app/components/poetry/ui/tree/component.html.erb +32 -0
  347. data/app/components/poetry/ui/tree/component.rb +233 -0
  348. data/app/components/poetry/ui/tree/preview.rb +55 -0
  349. data/app/components/poetry/ui/tree/style.rb +29 -0
  350. data/app/components/poetry/ui/typeset/component.rb +73 -0
  351. data/app/components/poetry/ui/typeset/preview.rb +45 -0
  352. data/app/components/poetry/ui/typeset/style.rb +15 -0
  353. data/app/controllers/poetry/ui/llms_controller.rb +28 -0
  354. data/app/helpers/poetry/ui/components_helper.rb +1794 -0
  355. data/app/helpers/poetry/ui/form_builder/type_inference.rb +117 -0
  356. data/app/helpers/poetry/ui/form_builder.rb +916 -0
  357. data/app/helpers/poetry/ui/optimistic_form_builder.rb +55 -0
  358. data/app/helpers/poetry/ui/toast_stream_actions.rb +27 -0
  359. data/config/component_descriptions.yml +96 -0
  360. data/config/component_registry.yml +12257 -0
  361. data/config/locales/en.yml +93 -0
  362. data/config/routes.rb +8 -0
  363. data/config/template_classes.txt +113 -0
  364. data/lib/generators/poetry/add/add_generator.rb +218 -0
  365. data/lib/generators/poetry/agent_rules/agent_rules_generator.rb +91 -0
  366. data/lib/generators/poetry/agents/agents_generator.rb +24 -0
  367. data/lib/generators/poetry/agents_section.rb +148 -0
  368. data/lib/generators/poetry/block/block_generator.rb +67 -0
  369. data/lib/generators/poetry/block/templates/action_bar.html.erb +45 -0
  370. data/lib/generators/poetry/block/templates/app_shell.html.erb +139 -0
  371. data/lib/generators/poetry/block/templates/data_index.html.erb +85 -0
  372. data/lib/generators/poetry/block/templates/destructive_panel.html.erb +26 -0
  373. data/lib/generators/poetry/block/templates/page_header.html.erb +25 -0
  374. data/lib/generators/poetry/block/templates/section_card.html.erb +42 -0
  375. data/lib/generators/poetry/block/templates/stepper.html.erb +79 -0
  376. data/lib/generators/poetry/block/templates/top_nav.html.erb +44 -0
  377. data/lib/generators/poetry/diff/diff_generator.rb +135 -0
  378. data/lib/generators/poetry/editor/editor_generator.rb +344 -0
  379. data/lib/generators/poetry/editor/templates/poetry-check-hook +81 -0
  380. data/lib/generators/poetry/install/install_generator.rb +364 -0
  381. data/lib/generators/poetry/pagination/pagination_generator.rb +76 -0
  382. data/lib/generators/poetry/pagination/templates/kaminari_paginator.html.erb +29 -0
  383. data/lib/generators/poetry/pagination/templates/poetry_link_renderer.rb +34 -0
  384. data/lib/generators/poetry/pagination/templates/poetry_pagy_helper.rb +28 -0
  385. data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js +131 -0
  386. data/lib/generators/poetry/recipes/screen_data_index/app/controllers/orders_controller.rb +15 -0
  387. data/lib/generators/poetry/recipes/screen_data_index/app/views/orders/index.html.erb +7 -0
  388. data/lib/generators/poetry/recipes/screen_data_index/test/system/orders_index_test.rb +16 -0
  389. data/lib/generators/poetry/recipes/screen_settings/app/controllers/settings_controller.rb +14 -0
  390. data/lib/generators/poetry/recipes/screen_settings/app/views/settings/show.html.erb +9 -0
  391. data/lib/generators/poetry/recipes/screen_settings/test/system/settings_show_test.rb +16 -0
  392. data/lib/generators/poetry/scaffold_templates/scaffold_templates_generator.rb +60 -0
  393. data/lib/generators/poetry/scaffold_templates/templates/_form.html.erb.tt +77 -0
  394. data/lib/generators/poetry/scaffold_templates/templates/controller.rb.tt +78 -0
  395. data/lib/generators/poetry/scaffold_templates/templates/edit.html.erb.tt +14 -0
  396. data/lib/generators/poetry/scaffold_templates/templates/index.html.erb.tt +24 -0
  397. data/lib/generators/poetry/scaffold_templates/templates/new.html.erb.tt +11 -0
  398. data/lib/generators/poetry/scaffold_templates/templates/partial.html.erb.tt +13 -0
  399. data/lib/generators/poetry/scaffold_templates/templates/show.html.erb.tt +18 -0
  400. data/lib/generators/poetry/skill/skill_generator.rb +34 -0
  401. data/lib/generators/poetry/skill/templates/poetry-component/SKILL.md +58 -0
  402. data/lib/generators/poetry/skill/templates/poetry-component/references/anatomy.md +152 -0
  403. data/lib/generators/poetry/skill/templates/poetry-component/references/checklist.md +67 -0
  404. data/lib/generators/poetry/skill/templates/poetry-component/references/documentation.md +137 -0
  405. data/lib/generators/poetry/skill/templates/poetry-design/SKILL.md +56 -0
  406. data/lib/generators/poetry/skill/templates/poetry-design/references/audit.md +123 -0
  407. data/lib/generators/poetry/skill/templates/poetry-design/references/compose.md +73 -0
  408. data/lib/generators/poetry/skill/templates/poetry-design/references/figma.md +68 -0
  409. data/lib/generators/poetry/skill/templates/poetry-design/references/paper.md +56 -0
  410. data/lib/generators/poetry/skill/templates/poetry-design/references/study.md +47 -0
  411. data/lib/generators/poetry/skill/templates/poetry-design/references/theme.md +70 -0
  412. data/lib/generators/poetry/skills_section.rb +58 -0
  413. data/lib/poetry/ui/chat.rb +283 -0
  414. data/lib/poetry/ui/code_block_highlighter.rb +60 -0
  415. data/lib/poetry/ui/engine.rb +55 -0
  416. data/lib/poetry/ui/recipes.rb +157 -0
  417. data/lib/poetry/ui/testing/combobox.rb +167 -0
  418. data/lib/poetry/ui/testing/dialog.rb +73 -0
  419. data/lib/poetry/ui/testing/menu.rb +127 -0
  420. data/lib/poetry/ui/testing/registration.rb +58 -0
  421. data/lib/poetry/ui/testing/select.rb +147 -0
  422. data/lib/poetry/ui/testing/tester.rb +130 -0
  423. data/lib/poetry/ui/testing.rb +130 -0
  424. data/lib/poetry/ui/themes.rb +109 -0
  425. data/lib/poetry/ui/version.rb +8 -0
  426. data/lib/poetry/ui/webmcp.rb +91 -0
  427. data/lib/poetry/ui.rb +285 -0
  428. data/lib/poetry-ui.rb +5 -0
  429. data/lib/tasks/poetry/check.rake +73 -0
  430. data/lib/tasks/poetry/design.rake +293 -0
  431. data/lib/tasks/poetry/verify.rake +36 -0
  432. data/themes/default.css +1793 -0
  433. data/themes/luma.css +1764 -0
  434. data/themes/lyra.css +1768 -0
  435. data/themes/maia.css +1768 -0
  436. data/themes/mira.css +1760 -0
  437. data/themes/nova.css +1755 -0
  438. data/themes/rhea.css +1771 -0
  439. data/themes/sera.css +1757 -0
  440. data/themes/vega.css +1775 -0
  441. data/typeset/typeset.css +499 -0
  442. metadata +502 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Avatar
6
+ # The Avatar preview: the initials fallback (the deterministic base
7
+ # state), the image layer actually covering it (a data-URI svg, so the
8
+ # browser rig loads it without a network), sizes, a badge, and the
9
+ # stacked group via the sidecar template.
10
+ class Preview < Poetry::Core::Preview::Base
11
+ # A gradient + head-and-shoulders silhouette - unlike the 1x1 solid
12
+ # pixel it replaced, this is visibly an image, so the screenshot can
13
+ # tell "image loaded" apart from a tinted fallback.
14
+ IMAGE = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NC" \
15
+ "A2NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PS" \
16
+ "IwIiBzdG9wLWNvbG9yPSIjNjM2NmYxIi8+PHN0b3Agb2Zmc2V0PSIuNTUiIHN0b3AtY29sb3I9IiM4YjVjZjYiLz48c3RvcCBvZm" \
17
+ "ZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlYzQ4OTkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iNjQiIGhlaW" \
18
+ "dodD0iNjQiIGZpbGw9InVybCgjZykiLz48Y2lyY2xlIGN4PSIzMiIgY3k9IjI1IiByPSIxMCIgZmlsbD0iI2ZmZiIgb3BhY2l0eT" \
19
+ "0iLjkiLz48cGF0aCBkPSJNMTIgNjRjMi0xMyAxMC0xOSAyMC0xOXMxOCA2IDIwIDE5eiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLj" \
20
+ "kiLz48L3N2Zz4="
21
+
22
+ def default
23
+ render_component(label: "Matt Solt") { "MS" }
24
+ end
25
+
26
+ def with_image
27
+ render_component(src: IMAGE, label: "Grace Hopper") { "GH" }
28
+ end
29
+
30
+ def small_with_badge
31
+ render_component(size: :sm, label: "Ada Lovelace (online)") do |avatar|
32
+ avatar.with_badge { "" }
33
+ "AL"
34
+ end
35
+ end
36
+
37
+ def large
38
+ render_component(size: :lg, label: "Annie Easley") { "AE" }
39
+ end
40
+
41
+ def group
42
+ # An explicit per-example template: a CLASS-level preview.html.erb
43
+ # would shadow the shared default for EVERY example (the contrib
44
+ # sidecar contract), silently replacing the other previews.
45
+ render_with_template(template: "poetry/ui/avatar/group_preview")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Avatar
6
+ # Re-expressed through the cn-* theme layer.
7
+ # :image keeps the "absolute inset-0" rule inline (the
8
+ # server-native layered fallback keeps BOTH fallback and image in the
9
+ # DOM, so the image must cover the initials); the after:* ring
10
+ # machinery stays inline as a structural rule.
11
+ class Style < Poetry::Core::Style
12
+ base "cn-avatar group/avatar relative flex shrink-0 select-none " \
13
+ "after:absolute after:inset-0 after:border after:border-border " \
14
+ "after:mix-blend-darken dark:after:mix-blend-lighten"
15
+
16
+ element :image, "cn-avatar-image absolute inset-0 aspect-square size-full object-cover"
17
+ element :fallback, "cn-avatar-fallback flex size-full items-center justify-center " \
18
+ "text-sm group-data-[size=sm]/avatar:text-xs"
19
+ element :badge, "cn-avatar-badge absolute right-0 bottom-0 z-10 inline-flex items-center " \
20
+ "justify-center rounded-full bg-blend-color ring-2 select-none " \
21
+ "group-data-[size=default]/avatar:[&>svg]:size-2 " \
22
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=lg]/avatar:[&>svg]:size-2 " \
23
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=sm]/avatar:[&>svg]:hidden " \
24
+ "group-data-[size=sm]/avatar:size-2"
25
+
26
+ # The stack (poetry_avatar_group) + its overflow count.
27
+ element :group, "group/avatar-group flex -space-x-2 " \
28
+ "*:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background"
29
+ element :group_count, "cn-avatar-group-count relative flex shrink-0 items-center " \
30
+ "justify-center ring-2 ring-background"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Non-interactive status pills.
6
+ module Badge
7
+ # A small status pill. Variants carry semantic intent: the solid
8
+ # set (default/secondary/destructive/outline/ghost/link) for labels
9
+ # and emphasis, plus the soft status trio (success/warning/info) -
10
+ # the muted color-coded pills record-status columns need.
11
+ #
12
+ # Badges are non-interactive; the one interactive form is href:,
13
+ # which renders the pill as a real link.
14
+ #
15
+ # @example A soft status pill
16
+ # render Poetry::Ui::Badge::Component.new(variant: :success) { "Fulfilled" }
17
+ class Component < Poetry::Core::Component
18
+ # A status label with no text is an invisible sliver.
19
+ requires_content "the visible status text"
20
+
21
+ # The closed vocabulary for the variant axis.
22
+ VARIANTS = %i[default secondary destructive outline ghost link success warning info].freeze
23
+
24
+ # Projected into the registry, llms.txt, and the agent surface.
25
+ AGENT_RULES = [
26
+ "Badges are non-interactive status labels - never attach click handlers; use Button for " \
27
+ "actions. The one interactive form is href:, which renders the badge AS a real link " \
28
+ "(a navigational chip - the themes' [a&]:hover treatments activate).",
29
+ "The visible text is the content block: render ... { \"beta\" } - there is no label: option.",
30
+ "Pick the variant by intent (destructive = error states; success/warning/info = record " \
31
+ "status, e.g. Fulfilled/Processing/Syncing), never by color preference.",
32
+ "Status badges on one surface read as a SET: keep one treatment family per table/list - " \
33
+ "the soft trio (+ outline for neutral) together, or the solid pair together; never a " \
34
+ "solid destructive pill inside a soft status column (design lint flags the mix)."
35
+ ].freeze
36
+
37
+ style :variant, default: :default, required: true, variants: VARIANTS,
38
+ doc: "The intent axis; success/warning/info are the soft record-status treatments."
39
+
40
+ option :href, :string,
41
+ doc: "Renders the pill as a real <a> - a navigational chip; the theme's link hover treatments " \
42
+ "activate on exactly this element."
43
+
44
+ part "badge", "The status pill itself (a <span>; a real <a> when href: is given) - " \
45
+ "the whole component is this one element",
46
+ states: {
47
+ "data-variant" => { condition: "always - the resolved variant",
48
+ values: VARIANTS.map(&:to_s) }
49
+ }
50
+
51
+ # Enforces the visible-text content block.
52
+ # @api private
53
+ def before_render
54
+ ensure_content!
55
+ end
56
+
57
+ # @api private
58
+ def call
59
+ content_tag(href.present? ? :a : :span, content, **root_attributes.to_attributes)
60
+ end
61
+
62
+ # @api private
63
+ def root_attributes
64
+ attrs = { "data-slot" => "badge", "data-variant" => variant }
65
+ attrs["href"] = href if href.present?
66
+ html_attributes.merge_if_not_set(attrs.merge(component_data_attributes))
67
+ end
68
+
69
+ private :root_attributes
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Badge
6
+ class Preview < Poetry::Core::Preview::Base
7
+ def default
8
+ render_component(variant: :default) { "New" }
9
+ end
10
+
11
+ def secondary
12
+ render_component(variant: :secondary) { "Draft" }
13
+ end
14
+
15
+ def destructive
16
+ render_component(variant: :destructive) { "Failed" }
17
+ end
18
+
19
+ def outline
20
+ render_component(variant: :outline) { "Beta" }
21
+ end
22
+
23
+ def ghost
24
+ render_component(variant: :ghost) { "Muted on hover" }
25
+ end
26
+
27
+ # The link VARIANT (distinct from the badge-as-anchor example below).
28
+ def link_variant
29
+ render_component(variant: :link) { "Reference" }
30
+ end
31
+
32
+ # Badge-as-link (upstream badge#link parity): a real <a>, so the
33
+ # axe walk covers the anchor treatment and its contrast.
34
+ def link
35
+ render_component(href: "#changelog") { "v2.0 release notes" }
36
+ end
37
+
38
+ # The status vocabulary: soft tints on the status
39
+ # tokens - the axe walk holds every theme's treatment to AA here.
40
+ def success
41
+ render_component(variant: :success) { "Fulfilled" }
42
+ end
43
+
44
+ def warning
45
+ render_component(variant: :warning) { "Processing" }
46
+ end
47
+
48
+ def info
49
+ render_component(variant: :info) { "Syncing" }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Badge
6
+ # Re-expressed through the cn-* theme layer. The focus/invalid
7
+ # ring chain deviates from upstream's inline split DELIBERATELY: the
8
+ # destructive variant re-colors focus-visible:ring-* in the theme and
9
+ # can only beat the base treatment from the same layer (the
10
+ # split-side conflict rule).
11
+ class Style < Poetry::Core::Style
12
+ base "cn-badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden " \
13
+ "whitespace-nowrap [&>svg]:pointer-events-none aria-invalid:border-destructive " \
14
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 " \
15
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 " \
16
+ "group/badge"
17
+
18
+ # success/warning/info: the poetry-original soft status vocabulary
19
+ # (upstream Badge has no status variants); every theme carries the
20
+ # soft treatment on the new status tokens.
21
+ variant :variant, {
22
+ ghost: "cn-badge-variant-ghost",
23
+ link: "cn-badge-variant-link",
24
+ default: "cn-badge-variant-default",
25
+ secondary: "cn-badge-variant-secondary",
26
+ destructive: "cn-badge-variant-destructive",
27
+ outline: "cn-badge-variant-outline",
28
+ success: "cn-badge-variant-success",
29
+ warning: "cn-badge-variant-warning",
30
+ info: "cn-badge-variant-info"
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ <%= content_tag(:nav, **root_attributes.to_attributes) do %>
2
+ <ol data-slot="breadcrumb-list" class="<%= css(:list) %>">
3
+ <% entries.each_with_index do |entry, index| %>
4
+ <li data-slot="breadcrumb-item" class="<%= css(:item) %>">
5
+ <% if entry.ellipsis %>
6
+ <span data-slot="breadcrumb-ellipsis" role="presentation" aria-hidden="true" class="<%= css(:ellipsis) %>">
7
+ <%= render Poetry::Ui::Icon::Component.new(name: :ellipsis) %>
8
+ </span>
9
+ <span class="sr-only">More</span>
10
+ <% else %>
11
+ <% if entry.block %>
12
+ <%= capture(&entry.block) %>
13
+ <% elsif entry.href.present? %>
14
+ <a data-slot="breadcrumb-link" href="<%= entry.href %>" class="<%= css(:link) %>"><%= entry.label %></a>
15
+ <% else %>
16
+ <span data-slot="breadcrumb-page" role="link" aria-disabled="true" aria-current="page"
17
+ class="<%= css(:page) %>"><%= entry.label %></span>
18
+ <% end %>
19
+ <% end %>
20
+ </li>
21
+ <% unless index == entries.size - 1 %>
22
+ <li data-slot="breadcrumb-separator" role="presentation" aria-hidden="true" class="<%= css(:separator) %>">
23
+ <% if separator_block %>
24
+ <%= capture(&separator_block) %>
25
+ <% else %>
26
+ <%= render Poetry::Ui::Icon::Component.new(name: :"chevron-right", class: "cn-rtl-flip") %>
27
+ <% end %>
28
+ </li>
29
+ <% end %>
30
+ <% end %>
31
+ </ol>
32
+ <% end %>
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Ancestor-trail navigation.
6
+ module Breadcrumb
7
+ # A navigation trail of ancestor links. Declare the trail (with_item
8
+ # per crumb, with_ellipsis for a collapsed middle) and the component
9
+ # owns the <nav>/<ol> chrome, the separators, and the current-page
10
+ # semantics. An item with href: is a link; an item without href:
11
+ # is the current page (aria-current="page").
12
+ #
13
+ # @example
14
+ # <%= poetry_breadcrumb do |crumb| %>
15
+ # <% crumb.with_item("Home", href: "/") %>
16
+ # <% crumb.with_ellipsis %>
17
+ # <% crumb.with_item("Breadcrumb") %>
18
+ # <% end %>
19
+ class Component < Poetry::Core::Component
20
+ # The caller's separator block, captured by with_separator.
21
+ # @api private
22
+ attr_reader :separator_block
23
+
24
+ # Projected into the registry, llms.txt, and the agent surface.
25
+ AGENT_RULES = [
26
+ "Declare the trail with with_item(label, href:) - never hand-build the nav/ol/li chain.",
27
+ "The current page is the item WITHOUT href: (it renders aria-current=page, not a link).",
28
+ "Collapse a long middle with with_ellipsis - it announces 'More' to screen readers.",
29
+ "A BLOCK item (with_item { ... }) renders your content inside the <li> - the seat for a " \
30
+ "dropdown crumb or a custom-rendered link; you own its semantics (aria-current only " \
31
+ "applies to label items).",
32
+ "with_separator(icon: :dot) - or a block - replaces the chevron in EVERY gap; the default " \
33
+ "chevron RTL-flips, a custom glyph is used as given."
34
+ ].freeze
35
+
36
+ # Slots the component cannot render without; static checks read this without rendering.
37
+ REQUIRED_SLOTS = { item: "at least one item" }.freeze
38
+
39
+ renders_many :items,
40
+ doc: "The crumbs, in declaration order. A label with href: renders a link; without one, the " \
41
+ "current page. A block makes the <li>'s content caller-owned (a dropdown crumb, a " \
42
+ "custom-rendered link).",
43
+ renders: lambda { |label = nil, href: nil, ellipsis: false, &block|
44
+ entries << Entry.new(label: label, href: href, ellipsis: ellipsis, block: block)
45
+ nil
46
+ }
47
+
48
+ renders_one :separator,
49
+ doc: "Replaces the separator glyph in EVERY gap: an icon name, or a block for arbitrary " \
50
+ "content. Absent, the default chevron renders (with its RTL flip - a custom glyph is used " \
51
+ "as given).",
52
+ renders: lambda { |icon: nil, &block|
53
+ @separator_block = icon ? proc { render Icon::Component.new(name: icon) } : block
54
+ nil
55
+ }
56
+
57
+ part "breadcrumb", "The <nav> landmark (aria-label=breadcrumb) around the trail"
58
+ part "breadcrumb-list", "The <ol> laying crumbs and separators out as one wrapping row"
59
+ part "breadcrumb-item", "One <li> of the trail - wraps a link, the current page, the ellipsis, " \
60
+ "or a block item's own content (a dropdown crumb, a custom link)"
61
+ part "breadcrumb-link", "A crumb with href: - a real <a> to an ancestor page"
62
+ part "breadcrumb-page", "The current page (the item without href:) - aria-current=page, not a link"
63
+ part "breadcrumb-separator", "The chevron <li> between crumbs - presentational, aria-hidden"
64
+ part "breadcrumb-ellipsis", "The collapsed-middle glyph (with_ellipsis) - aria-hidden; a sibling " \
65
+ "sr-only 'More' announces it"
66
+
67
+ # Enforces the required item slot.
68
+ # @api private
69
+ def before_render
70
+ # items? is the SLOT predicate: it forces the render block (which
71
+ # populates entries) - reading @entries directly here would run
72
+ # before the block ever executed.
73
+ raise ArgumentError, "Breadcrumb requires at least one with_item" unless items?
74
+ end
75
+
76
+ # Adds a collapsed-middle marker to the trail; screen readers hear "More".
77
+ def with_ellipsis
78
+ with_item(ellipsis: true)
79
+ end
80
+
81
+ # The declared crumbs in order.
82
+ # @api private
83
+ def entries
84
+ @entries ||= []
85
+ end
86
+
87
+ # @api private
88
+ def root_attributes
89
+ html_attributes.merge_if_not_set(
90
+ { "aria-label" => "breadcrumb", "data-slot" => "breadcrumb" }.merge(component_data_attributes)
91
+ )
92
+ end
93
+
94
+ # One declared crumb.
95
+ # @api private
96
+ Entry = Data.define(:label, :href, :ellipsis, :block)
97
+
98
+ private :entries, :root_attributes
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,21 @@
1
+ <%# A middle crumb IS a dropdown (upstream breadcrumb#dropdown): the block
2
+ item hands the <li> to caller content; the ghost trigger is restyled to
3
+ read as breadcrumb text. %>
4
+ <%= poetry_breadcrumb do |crumb| %>
5
+ <% crumb.with_item("Home", href: "#") %>
6
+ <% crumb.with_item do %>
7
+ <%= poetry_dropdown_menu(align: :start) do |menu| %>
8
+ <% menu.with_trigger(variant: :ghost, size: :sm,
9
+ class: "h-auto gap-1 p-0 font-normal text-muted-foreground hover:bg-transparent hover:text-foreground") do %>
10
+ Components
11
+ <%= poetry_icon(name: :"chevron-down", class: "size-3.5") %>
12
+ <% end %>
13
+ <% menu.with_group do |group| %>
14
+ <% group.with_item { "Documentation" } %>
15
+ <% group.with_item { "Themes" } %>
16
+ <% group.with_item { "GitHub" } %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
20
+ <% crumb.with_item("Breadcrumb") %>
21
+ <% end %>
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Breadcrumb
6
+ # The Breadcrumb preview: a plain trail and a collapsed-middle trail.
7
+ class Preview < Poetry::Core::Preview::Base
8
+ def default
9
+ render_component do |crumb|
10
+ crumb.with_item("Home", href: "#")
11
+ crumb.with_item("Components", href: "#")
12
+ crumb.with_item("Breadcrumb")
13
+ end
14
+ end
15
+
16
+ def collapsed
17
+ render_component do |crumb|
18
+ crumb.with_item("Home", href: "#")
19
+ crumb.with_ellipsis
20
+ crumb.with_item("Components", href: "#")
21
+ crumb.with_item("Breadcrumb")
22
+ end
23
+ end
24
+
25
+ # with_separator swaps the chevron in every gap (upstream
26
+ # breadcrumb#separator - the dot form).
27
+ def dot_separator
28
+ render_component do |crumb|
29
+ crumb.with_separator(icon: :dot)
30
+ crumb.with_item("Home", href: "#")
31
+ crumb.with_item("Components", href: "#")
32
+ crumb.with_item("Breadcrumb")
33
+ end
34
+ end
35
+
36
+ # A block item seats a dropdown as a middle crumb (upstream
37
+ # breadcrumb#dropdown) - the axe walk holds the composed menu
38
+ # inside the nav landmark to the same bar as everything else.
39
+ def dropdown_crumb
40
+ render_with_template(template: "poetry/ui/breadcrumb/dropdown_crumb_preview")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Breadcrumb
6
+ # Re-expressed through the cn-* theme layer. The separator
7
+ # chevron wears cn-rtl-flip (template-side): upstream ships that
8
+ # NAME with no rule anywhere - dead in source - so poetry authors the
9
+ # intended RTL mirror in the theme (rtl:-scale-x-100, LTR-neutral).
10
+ # No cn-breadcrumb root name: the root's poetry surface is empty and
11
+ # empty theme rules don't survive compilation (data-slot remains the
12
+ # restyle hook).
13
+ class Style < Poetry::Core::Style
14
+ base ""
15
+
16
+ element :list, "cn-breadcrumb-list flex flex-wrap items-center wrap-break-word"
17
+ element :item, "cn-breadcrumb-item inline-flex items-center"
18
+ element :link, "cn-breadcrumb-link"
19
+ element :page, "cn-breadcrumb-page"
20
+ element :separator, "cn-breadcrumb-separator"
21
+ element :ellipsis, "cn-breadcrumb-ellipsis flex items-center justify-center"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
2
+ <%= content_tag(content_tag_name, content, **content_attributes) %>
3
+ <%= reactions %>
4
+ <% end %>
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Chat message bubbles.
6
+ module Bubble
7
+ # A chat message bubble - one per message, aligned to the sender's
8
+ # side. The content block is the message; tag: :button or :a (with
9
+ # href:) turns the bubble into a quick reply, and the reactions slot
10
+ # overlays a pill of reactions. Purely presentational - no
11
+ # JavaScript.
12
+ #
13
+ # @example An assistant reply
14
+ # render Poetry::Ui::Bubble::Component.new(variant: :secondary) { "Here's the summary." }
15
+ class Component < Poetry::Core::Component
16
+ # The closed vocabulary for the variant axis.
17
+ VARIANTS = %i[default secondary muted tinted outline ghost destructive].freeze
18
+ # The closed vocabulary for the align axis.
19
+ ALIGNS = %i[start end].freeze
20
+ # The closed vocabulary for the tag: axis.
21
+ CONTENT_TAGS = %i[div button a].freeze
22
+
23
+ # Projected into the registry, llms.txt, and the agent surface.
24
+ AGENT_RULES = [
25
+ "One Bubble per message; stack a sender's run inside poetry_bubble_group.",
26
+ "Quick replies are tag: :button (with the caller's data-action) or tag: :a + " \
27
+ "href: - never a click handler on a div.",
28
+ "ghost is for tool output / system text flowing full-width - not a visual preference.",
29
+ "Reactions REQUIRE label: (the accessible name for the cluster).",
30
+ "Inside a Message, alignment follows the Message's align - do not set both."
31
+ ].freeze
32
+
33
+ renders_one :reactions,
34
+ doc: "The reactions pill overlaid on an edge; label: names the cluster for assistive tech, " \
35
+ "side:/align: place it (default bottom end).",
36
+ renders: lambda { |label:, side: :bottom, align: :end, &block|
37
+ content_tag(:div,
38
+ class: css(:reactions), "data-slot" => "bubble-reactions",
39
+ "data-side" => side, "data-align" => align,
40
+ role: "group", "aria-label" => label, &block)
41
+ }
42
+
43
+ # The content block IS the message; an empty bubble is an empty pill.
44
+ requires_content "the message content"
45
+
46
+ style :variant, default: :default, required: true, variants: VARIANTS,
47
+ doc: "The intent axis; :ghost is for tool output / system text flowing full-width."
48
+
49
+ option :align, :symbol, default: :start,
50
+ doc: "Which side the bubble hugs; inside a Message, set the Message's align instead."
51
+ option :tag, :symbol, default: :div,
52
+ doc: "The content element: :div (default), or :button/:a for a quick reply."
53
+ option :href, :string, doc: "Renders the content as a real anchor; implies tag: :a."
54
+
55
+ validates :align, inclusion: { in: ALIGNS }
56
+ validates :tag, inclusion: { in: CONTENT_TAGS }
57
+
58
+ part "bubble", "The message surface root - variant and alignment ride here",
59
+ states: {
60
+ "data-variant" => { condition: "always - the resolved variant",
61
+ values: VARIANTS.map(&:to_s) },
62
+ "data-align" => { condition: "always - the resolved align",
63
+ values: ALIGNS.map(&:to_s) }
64
+ }
65
+ part "bubble-content", "The body (<div>, or a <button>/<a> quick reply via tag:) - " \
66
+ "the content block renders here"
67
+ part "bubble-reactions", "The reactions pill overlay (role=group, named by label:)",
68
+ states: {
69
+ "data-side" => "always - which edge the pill overlays (default bottom)",
70
+ "data-align" => "always - placement along that edge (default end)"
71
+ }
72
+
73
+ # Enforces the message content block.
74
+ # @api private
75
+ def before_render
76
+ ensure_content!
77
+ end
78
+
79
+ # @api private
80
+ def root_attributes
81
+ html_attributes.merge_if_not_set(
82
+ { "data-slot" => "bubble", "data-variant" => variant, "data-align" => align }
83
+ .merge(component_data_attributes)
84
+ )
85
+ end
86
+
87
+ # href: implies the anchor - an href on the default :div would
88
+ # otherwise be silently dropped, leaving a dead control.
89
+ # @api private
90
+ def content_tag_name
91
+ href.present? ? :a : tag
92
+ end
93
+
94
+ # @api private
95
+ def content_attributes
96
+ attrs = { class: css(:content), "data-slot" => "bubble-content" }
97
+ attrs[:href] = href if content_tag_name == :a
98
+ attrs[:type] = "button" if content_tag_name == :button
99
+ attrs
100
+ end
101
+
102
+ private :root_attributes, :content_tag_name, :content_attributes
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Bubble
6
+ # The Bubble preview matrix - one corpus for Lookbook, docs, and the
7
+ # agent visual loop.
8
+ class Preview < Poetry::Core::Preview::Base
9
+ # @!group Variants
10
+
11
+ def default
12
+ render_component { "Sounds good - see you at 10." }
13
+ end
14
+
15
+ def secondary
16
+ render_component(variant: :secondary) { "Here's the summary you asked for." }
17
+ end
18
+
19
+ def muted
20
+ render_component(variant: :muted) { "Typing indicator context." }
21
+ end
22
+
23
+ def tinted
24
+ render_component(variant: :tinted) { "A tinted assistant reply." }
25
+ end
26
+
27
+ def outline
28
+ render_component(variant: :outline) { "An outlined reply." }
29
+ end
30
+
31
+ def ghost
32
+ render_component(variant: :ghost) { "Tool output flows full-width without a surface." }
33
+ end
34
+
35
+ def destructive
36
+ render_component(variant: :destructive) { "That upload failed." }
37
+ end
38
+
39
+ # @!endgroup
40
+
41
+ def align_end
42
+ render_component(align: :end) { "Sent by me." }
43
+ end
44
+
45
+ def quick_reply_button
46
+ render_component(variant: :outline, tag: :button) { "Yes, book it" }
47
+ end
48
+
49
+ def quick_reply_link
50
+ render_component(variant: :outline, tag: :a, href: "/details") { "View details" }
51
+ end
52
+
53
+ def with_reactions
54
+ render_component do |bubble|
55
+ bubble.with_reactions(label: "Reactions: thumbs up") { "👍" }
56
+ "Great idea!"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end