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,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Poetry
6
+ # Namespace for poetry's Rails generators and their shared step
7
+ # modules.
8
+ module Generators
9
+ # The host-facing AGENTS.md section: a short, registry-derived
10
+ # pointer layer for coding agents working in the host app - llms.txt
11
+ # stays the catalog, this is the front door to it. Marker-bounded so a
12
+ # re-run refreshes poetry's section without touching anything the host
13
+ # wrote around it.
14
+ #
15
+ # Shared by poetry:install and the standalone poetry:agents (which
16
+ # exists so hosts that must NOT re-run install - e.g. a docs app with a
17
+ # hand-scoped style registry - can still refresh the pointer). NOTE:
18
+ # Thor only registers methods added directly on the generator class as
19
+ # steps, so each generator declares its own public step and calls
20
+ # apply_agents_section.
21
+ #
22
+ # @api private
23
+ module AgentsSection
24
+ BEGIN_MARKER = "<!-- poetry:agents:begin -->"
25
+ END_MARKER = "<!-- poetry:agents:end -->"
26
+
27
+ # Writes or refreshes the marker-bounded poetry section in the
28
+ # host's AGENTS.md.
29
+ def apply_agents_section
30
+ path = File.join(destination_root, "AGENTS.md")
31
+ if File.exist?(path) && File.read(path).include?(BEGIN_MARKER)
32
+ gsub_file "AGENTS.md",
33
+ /#{Regexp.escape(BEGIN_MARKER)}.*#{Regexp.escape(END_MARKER)}/mo,
34
+ agents_section.chomp
35
+ elsif File.exist?(path)
36
+ append_to_file "AGENTS.md", "\n#{agents_section}"
37
+ else
38
+ create_file "AGENTS.md", agents_section
39
+ end
40
+ end
41
+
42
+ # The marker-bounded section text, registry-derived.
43
+ def agents_section
44
+ <<~MD
45
+ #{BEGIN_MARKER}
46
+ ## Building UI with poetry (#{agents_component_counts})
47
+
48
+ - FIRST MOVE on any UI brief: call the poetry MCP `compose` tool with the
49
+ task text, before writing any ERB. It routes to the matching vetted
50
+ block (source included, adapt in place - the winning path for screens)
51
+ or to the right components. No MCP? `bin/rails g poetry:block --list`
52
+ and start from the closest block. Composing a screen from scratch when
53
+ a block matched is the known losing path.
54
+ - Compose with the `poetry_*` helpers; never hand-write `cn-*` classes, raw
55
+ hex/oklch colors, or off-scale arbitrary values - tokens and variants carry
56
+ the design.
57
+ - Machine catalog: `/poetry/llms.txt` (index + blocks) and `/poetry/llms-full.txt`
58
+ (full contracts + Stimulus wiring: targets / values / actions / events).
59
+ - Check comes LAST: `bin/rails poetry:check` as the FINAL action, after
60
+ the last edit (unknown components/slots/variants/wiring, icon names,
61
+ enum values, typed-slot props, helper + setter arity, yield-less
62
+ blocks, setter keywords, required content blocks, required slots,
63
+ did-you-mean, `--json`; needs the `herb` gem in the Gemfile). An edit
64
+ made after your last check is unverified markup - re-run it.
65
+ - Faster: the `poetry` MCP server (`.mcp.json`: command `bundle`, args
66
+ `["exec", "poetry-agent"]`, the poetry-agent gem) serves ten tools from the live registry
67
+ with no app boot - `compose`, `build_page`, `list_components`,
68
+ `describe_component`, `check`, `list_blocks`, `describe_block`,
69
+ `list_recipes`, `get_skill`, and `guidance`. Prefer `compose` (or `build_page` for a
70
+ whole screen) to start, and its `check` tool when iterating. Wire it
71
+ into every editor at once with `bin/rails g poetry:editor` (MCP
72
+ configs for VS Code / Cursor / Claude Code / Zed / RubyMine plus
73
+ registry-driven snippets).
74
+ - Browser agents (WebMCP): opt a rendered component into the user's own
75
+ agent with `webmcp: "name"` on the helper call - Combobox, Dialog, Sheet,
76
+ Drawer, and Tabs declare tools (`describe_component` at `full` lists them);
77
+ declare a form as a tool with `poetry_webmcp_form(tool: { name:,
78
+ description: })` (autosubmit is GET-only). Needs the poetry-agent gem and
79
+ `registerPoetryAgent(application)`; `poetry:check` gates the opt-ins.
80
+ - One visual theme per app (chosen at install with `--theme`); components
81
+ read tokens, never restate them.
82
+ - Component identity: pass `key:` (a record, or a literal string) on any
83
+ poetry component inside a collection loop, a fragment-cache block, or
84
+ a broadcast partial - keyed ids follow the record across Turbo morph
85
+ reorders and stay stable inside cached fragments, where random ids
86
+ force replacement. `key: record` derives via dom_id (a host `to_key`
87
+ override propagates); explicit `id:` wins outright; repeated NEW
88
+ records need explicit keys. `poetry:check` warns on unkeyed
89
+ components in cache blocks and loops. Full story: the Stable IDs guide on the poetry docs site.
90
+ - Upgrading poetry gems: after `bundle update`, re-run
91
+ `bin/rails g poetry:install` - the vendored token/theme/safelist
92
+ files refresh (the installed theme sticks; `--theme` switches),
93
+ new wiring appends, this section and the skills regenerate. Then
94
+ rebuild CSS and run the suite. `bin/rails g poetry:diff` reports
95
+ where copied-in components drift from the installed gems.
96
+ - `bin/rails g poetry:scaffold_templates` retargets the STANDARD
97
+ `rails g scaffold` to emit poetry-composed views (DataTable index
98
+ with URL state, Field-composed forms) plus a matching controller -
99
+ prefer scaffolding over hand-writing CRUD views.
100
+ - Claude Code skills: `poetry` (component contracts by family),
101
+ `poetry-design` (theme / compose / audit / study / figma / paper -
102
+ the taste layer), and `poetry-component` (anatomy / documentation /
103
+ audit - the authoring layer) live under `.claude/skills/` - load
104
+ `poetry` whenever writing ERB, `poetry-design` whenever composing a
105
+ page or screen, BEFORE building (any page task is a design task,
106
+ not only ones that mention design), and `poetry-component` whenever
107
+ authoring or reviewing an app-owned component. Install/refresh:
108
+ `bin/rails g poetry:skill`.
109
+ - Design interop: `bin/rails poetry:design:export` writes this app's
110
+ DESIGN.md (tokens + treatment) for external design skills.
111
+ - Overriding the theme: token-level restyling goes through
112
+ `poetry:design:import` (design-overrides.css) - which also ingests a
113
+ Figma variables export (`bin/rails poetry:figma:import[export.json]`)
114
+ or a Paper "Copy theme" (`bin/rails poetry:paper:import[theme.css]`),
115
+ dropping any swatch that fails WCAG AA. Host CSS that targets
116
+ theme-owned `cn-*` classes is allowed ONLY as a declared
117
+ override - a dated, reasoned entry under `overrides:` in
118
+ config/poetry_components.yml (`bin/rails poetry:design:overrides`
119
+ reports drift and prints the paste-ready declaration). Declare
120
+ only after the user confirms intent; never declare to skip a fix.
121
+ #{END_MARKER}
122
+ MD
123
+ end
124
+
125
+ # The human count line (components + charts + blocks) for the
126
+ # section heading.
127
+ def agents_component_counts
128
+ parts = ["#{agents_registry_size(Poetry::Ui.root)} components"]
129
+ # Tolerant on charts: a stubbed/partial gem without a registry file
130
+ # (the install-test stub) just drops out of the count.
131
+ charts = defined?(Poetry::Charts::Engine) && agents_registry_size(Poetry::Charts.root)
132
+ parts << "#{charts} chart components" if charts
133
+ blocks = agents_registry_size(Poetry::Ui.root, section: "blocks")
134
+ parts << "#{blocks} blocks" if blocks&.positive?
135
+ parts.join(" + ")
136
+ end
137
+
138
+ # One section's entry count from a gem's committed registry (nil
139
+ # when the gem ships none).
140
+ def agents_registry_size(root, section: "components")
141
+ path = root.join("config/component_registry.yml")
142
+ return nil unless File.exist?(path)
143
+
144
+ (YAML.safe_load_file(path)[section] || {}).size
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "yaml"
5
+
6
+ module Poetry
7
+ # `rails g poetry:block <name>` - copies a vetted composed
8
+ # screen into the app as source the app OWNS. Blocks are starting points
9
+ # an agent (or a person) edits, not components it configures: the file
10
+ # lands under app/views/blocks/ as a partial, the sample content is meant
11
+ # to be replaced in place, and `poetry check` keeps validating the result
12
+ # like any other template. `--list` (or no argument) prints the catalog
13
+ # from the registry's blocks section.
14
+ #
15
+ # @example
16
+ # bin/rails g poetry:block login-form
17
+ class BlockGenerator < Rails::Generators::Base
18
+ source_root File.expand_path("templates", __dir__)
19
+
20
+ argument :block_name, type: :string, required: false,
21
+ banner: "block-name", desc: "the block to copy in (see --list)"
22
+ class_option :list, type: :boolean, default: false, desc: "list the available blocks"
23
+
24
+ desc "Copy a vetted poetry block (a composed screen) into app/views/blocks/ as app-owned source"
25
+
26
+ # The header stamped on every copied block: names the generating
27
+ # command and hands ownership to the app.
28
+ OWNERSHIP_HEADER = "<%%# generated by `bin/rails g poetry:block %<name>s` - this file is yours: " \
29
+ "rename, move, and edit freely (sample content is meant to be replaced). " \
30
+ "Catalog: /poetry/llms.txt; verify edits with poetry check. %%>"
31
+
32
+ # Step: copies the named block (or prints the catalog with --list).
33
+ # @api private
34
+ def copy_block
35
+ return print_catalog if options[:list] || block_name.nil?
36
+
37
+ name = block_name.tr("_", "-")
38
+ entry = catalog[name]
39
+ unless entry
40
+ say_status :error, "unknown block #{name.inspect}", :red
41
+ print_catalog
42
+ raise Thor::Error, "unknown block #{name.inspect}"
43
+ end
44
+
45
+ source = Poetry::Ui.root.join(entry.fetch("template")).read
46
+ body = source.sub(/\A<%#\s*poetry:block[^%]*%>\n?/, "")
47
+ header = format(OWNERSHIP_HEADER, name: name)
48
+ create_file "app/views/blocks/_#{name.tr("-", "_")}.html.erb", "#{header}\n#{body}"
49
+ say_status :render, "<%= render \"blocks/#{name.tr("-", "_")}\" %>", :blue
50
+ end
51
+
52
+ private
53
+
54
+ def catalog
55
+ @catalog ||= YAML.safe_load_file(
56
+ Poetry::Ui.root.join(Poetry::Core::Registry::RELATIVE_PATH)
57
+ )["blocks"] || {}
58
+ end
59
+
60
+ def print_catalog
61
+ say "poetry blocks (bin/rails g poetry:block <name>):"
62
+ catalog.each do |name, entry|
63
+ say " #{name.ljust(18)} #{entry["title"]} - #{entry["description"]}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,45 @@
1
+ <%# poetry:block title="Action bar" description="Bulk actions over a selectable table: a selection-driven floating bar with the live count, the actions, and clear - shown while rows are selected, Escape clears, focus never steals." keywords="bulk, selection, batch, actions, action bar, selected, archive, delete rows, multi-select, table actions" %>
2
+ <%# ActionBar is a BLOCK, not a component: the bar
3
+ is plain markup wired to two small engines - the data_table's
4
+ selectable: engine dispatches selection-change, and the bar's
5
+ controller shows/hides on it, retains the last non-zero count while
6
+ animating out, announces "Actions available." once per appearance,
7
+ and Escape anywhere inside clears the selection. The wrapper div
8
+ PAIRS them positionally (shared parent) - no id contract. Swap the
9
+ sample actions for your bulk endpoints: the checkboxes are real form
10
+ values (selected_ids[]), so a wrapping form_with posts them as-is. %>
11
+ <% invoice = Struct.new(:id, :number, :customer, :amount) %>
12
+ <% rows = local_assigns[:rows] || [
13
+ invoice.new(1, "INV-001", "Acme", "$250.00"),
14
+ invoice.new(2, "INV-002", "Globex", "$1,150.00"),
15
+ invoice.new(3, "INV-003", "Initech", "$420.00")
16
+ ] %>
17
+ <% state = Poetry::Ui::DataTable::State.from_params(params, sortable: %w[number customer]) %>
18
+ <div class="relative mx-auto max-w-3xl pb-16">
19
+ <%= poetry_data_table(rows: rows, state: state, path: ->(p) { "?#{p.to_query}" },
20
+ caption: "Recent invoices.",
21
+ selectable: ->(row) { row.id }) do |table| %>
22
+ <% table.with_column("Invoice", key: :number, sortable: true) { |row| row.number } %>
23
+ <% table.with_column("Customer", key: :customer, sortable: true) { |row| row.customer } %>
24
+ <% table.with_column("Amount", classes: "text-right") { |row| row.amount } %>
25
+ <% end %>
26
+ <div hidden
27
+ data-controller="poetry--core--action-bar"
28
+ data-poetry--core--action-bar-label-value="%{count} selected"
29
+ data-available-label="Actions available."
30
+ data-action="keydown->poetry--core--action-bar#keydown"
31
+ data-slot="action-bar"
32
+ class="absolute inset-x-8 bottom-2 z-10 mx-auto flex max-w-xl items-center gap-3 rounded-lg border bg-popover px-4 py-2 text-popover-foreground shadow-lg transition-[opacity,translate] data-open:translate-y-0 data-open:opacity-100 not-data-open:translate-y-2 not-data-open:opacity-0">
33
+ <span data-slot="action-bar-count" class="text-sm font-medium tabular-nums"
34
+ data-poetry--core--action-bar-target="count"></span>
35
+ <span class="ms-auto flex items-center gap-2" data-slot="action-bar-actions" aria-label="Actions">
36
+ <%= poetry_button(variant: :outline, size: :sm) { "Export" } %>
37
+ <%= poetry_button(variant: :destructive, size: :sm) { "Archive" } %>
38
+ </span>
39
+ <%= poetry_button(variant: :ghost, size: :"icon-sm", label: "Clear selection",
40
+ "data-slot": "action-bar-clear",
41
+ data: { action: "click->poetry--core--action-bar#clear" }) do %>
42
+ <%= poetry_icon(name: :x) %>
43
+ <% end %>
44
+ </div>
45
+ </div>
@@ -0,0 +1,139 @@
1
+ <%# poetry:block title="App shell" description="The frame every screen composes into: an icon-collapsible sidebar with grouped nav, badges and a user footer, plus a topbar with breadcrumb that answers the collapsed state, and a stat-card content grid." keywords="dashboard, shell, layout, sidebar, workspace, admin, console, overview, stats, chrome" %>
2
+ <%= poetry_sidebar(collapsible: :icon) do |shell| %>
3
+ <% shell.with_nav do %>
4
+ <%= poetry_sidebar_header do %>
5
+ <%# The collapse-aware header shape (a menu button, never a bare
6
+ div): at icon width the button shrinks to the tile and the
7
+ truncating spans disappear with it. %>
8
+ <%= poetry_sidebar_menu do %>
9
+ <%= poetry_sidebar_menu_item do %>
10
+ <%= poetry_sidebar_menu_button(size: :lg) do %>
11
+ <div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
12
+ <%= poetry_icon(name: :"audio-waveform", class: "size-4") %>
13
+ </div>
14
+ <div class="grid flex-1 text-left text-sm leading-tight">
15
+ <span class="truncate font-semibold">Meridian</span>
16
+ <span class="truncate text-xs">Operations</span>
17
+ </div>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
21
+ <% end %>
22
+ <%= poetry_sidebar_content do %>
23
+ <%= poetry_sidebar_group do %>
24
+ <%= poetry_sidebar_group_label { "Platform" } %>
25
+ <%= poetry_sidebar_menu do %>
26
+ <%= poetry_sidebar_menu_item do %>
27
+ <%= poetry_sidebar_menu_button(href: "/dashboard", active: true) do %>
28
+ <%= poetry_icon(name: :"layout-dashboard") %><span>Dashboard</span>
29
+ <% end %>
30
+ <% end %>
31
+ <%= poetry_sidebar_menu_item do %>
32
+ <%= poetry_sidebar_menu_button(href: "/orders") do %>
33
+ <%= poetry_icon(name: :package) %><span>Orders</span>
34
+ <% end %>
35
+ <%= poetry_sidebar_menu_badge do %>12<% end %>
36
+ <% end %>
37
+ <%= poetry_sidebar_menu_item do %>
38
+ <%= poetry_sidebar_menu_button(href: "/customers") do %>
39
+ <%= poetry_icon(name: :users) %><span>Customers</span>
40
+ <% end %>
41
+ <% end %>
42
+ <% end %>
43
+ <% end %>
44
+ <%= poetry_sidebar_group do %>
45
+ <%= poetry_sidebar_group_label { "Resources" } %>
46
+ <%= poetry_sidebar_menu do %>
47
+ <%= poetry_sidebar_menu_item do %>
48
+ <%= poetry_sidebar_menu_button(href: "/docs") do %>
49
+ <%= poetry_icon(name: :"book-open") %><span>Documentation</span>
50
+ <% end %>
51
+ <% end %>
52
+ <%= poetry_sidebar_menu_item do %>
53
+ <%= poetry_sidebar_menu_button(href: "/support") do %>
54
+ <%= poetry_icon(name: :"life-buoy") %><span>Support</span>
55
+ <% end %>
56
+ <% end %>
57
+ <% end %>
58
+ <% end %>
59
+ <% end %>
60
+ <%= poetry_sidebar_footer do %>
61
+ <%# The footer rides the same collapse-aware shape as the header: at
62
+ icon width the menu button shrinks to the avatar tile and the
63
+ truncating spans clip with it instead of spilling into the inset. %>
64
+ <%= poetry_sidebar_menu do %>
65
+ <%= poetry_sidebar_menu_item do %>
66
+ <% profile = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNjM2NmYxIi8+PHN0b3Agb2Zmc2V0PSIuNTUiIHN0b3AtY29sb3I9IiM4YjVjZjYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlYzQ4OTkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIGZpbGw9InVybCgjZykiLz48Y2lyY2xlIGN4PSIzMiIgY3k9IjI1IiByPSIxMCIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjkiLz48cGF0aCBkPSJNMTIgNjRjMi0xMyAxMC0xOSAyMC0xOXMxOCA2IDIwIDE5eiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iLjkiLz48L3N2Zz4=" %>
67
+ <%= poetry_sidebar_menu_button(size: :lg) do %>
68
+ <%= poetry_avatar(src: profile, label: "Matt Solt", class: "size-8 rounded-lg") { "MS" } %>
69
+ <div class="grid flex-1 text-left text-sm leading-tight">
70
+ <span class="truncate font-semibold">Matt Solt</span>
71
+ <span class="truncate text-xs">matt@roboruby.com</span>
72
+ </div>
73
+ <% end %>
74
+ <% end %>
75
+ <% end %>
76
+ <% end %>
77
+ <%= poetry_sidebar_rail %>
78
+ <% end %>
79
+ <% shell.with_inset do %>
80
+ <header class="flex h-14 shrink-0 items-center gap-2 border-b px-4 transition-[height] group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
81
+ <%= poetry_sidebar_trigger %>
82
+ <%= poetry_separator(orientation: :vertical, class: "h-4") %>
83
+ <%= poetry_breadcrumb do |crumb| %>
84
+ <% crumb.with_item("Meridian", href: "/") %>
85
+ <% crumb.with_item("Dashboard") %>
86
+ <% end %>
87
+ </header>
88
+ <div class="flex flex-col gap-6 p-6">
89
+ <div class="flex flex-wrap items-end justify-between gap-4">
90
+ <div class="space-y-1">
91
+ <h1 class="text-2xl font-semibold tracking-tight">Dashboard</h1>
92
+ <p class="text-sm text-muted-foreground">Today across the Meridian workspace.</p>
93
+ </div>
94
+ <%= poetry_button(variant: :outline) do |button| %>
95
+ <% button.with_leading { poetry_icon(name: :download) } %>
96
+ Export
97
+ <% end %>
98
+ </div>
99
+ <div class="grid gap-4 md:grid-cols-3">
100
+ <%= poetry_card(title_tag: :h2) do |card| %>
101
+ <% card.with_title { "Revenue" } %>
102
+ <% card.with_description { "Last 30 days" } %>
103
+ <div class="text-2xl font-semibold tabular-nums">$48,210</div>
104
+ <p class="text-xs text-muted-foreground">+12.4% from the previous period</p>
105
+ <% end %>
106
+ <%= poetry_card(title_tag: :h2) do |card| %>
107
+ <% card.with_title { "Open orders" } %>
108
+ <% card.with_description { "Awaiting fulfillment" } %>
109
+ <div class="text-2xl font-semibold tabular-nums">42</div>
110
+ <p class="text-xs text-muted-foreground">12 due before Friday</p>
111
+ <% end %>
112
+ <%= poetry_card(title_tag: :h2) do |card| %>
113
+ <% card.with_title { "On-time rate" } %>
114
+ <% card.with_description { "Trailing quarter" } %>
115
+ <div class="text-2xl font-semibold tabular-nums">98.2%</div>
116
+ <p class="text-xs text-muted-foreground">Target is 97.5%</p>
117
+ <% end %>
118
+ </div>
119
+ <%= poetry_card(title_tag: :h2) do |card| %>
120
+ <% card.with_title { "Recent activity" } %>
121
+ <% card.with_description { "The latest changes across orders and customers." } %>
122
+ <ul class="space-y-3 text-sm">
123
+ <li class="flex items-center justify-between gap-4">
124
+ <span>ORD-1042 marked fulfilled by Matt</span>
125
+ <span class="text-xs text-muted-foreground tabular-nums">2m ago</span>
126
+ </li>
127
+ <li class="flex items-center justify-between gap-4">
128
+ <span>Northwind Traders added a shipping address</span>
129
+ <span class="text-xs text-muted-foreground tabular-nums">18m ago</span>
130
+ </li>
131
+ <li class="flex items-center justify-between gap-4">
132
+ <span>Refund approved for ORD-1040</span>
133
+ <span class="text-xs text-muted-foreground tabular-nums">1h ago</span>
134
+ </li>
135
+ </ul>
136
+ <% end %>
137
+ </div>
138
+ <% end %>
139
+ <% end %>
@@ -0,0 +1,85 @@
1
+ <%# poetry:block title="Data index" description="A contained records screen: title bar with primary action, search-and-filter toolbar, status-badged table with a totals footer, and a result count paired with pagination." keywords="index, list, listing, table, records, directory, filter, pagination, management, browse, catalog" %>
2
+ <section class="space-y-4">
3
+ <div class="flex flex-wrap items-end justify-between gap-4">
4
+ <div class="space-y-1">
5
+ <h2 class="text-lg font-semibold tracking-tight">Orders</h2>
6
+ <p class="text-sm text-muted-foreground">Fulfillment status across every open order.</p>
7
+ </div>
8
+ <%= poetry_button do |button| %>
9
+ <% button.with_leading { poetry_icon(name: :plus) } %>
10
+ New order
11
+ <% end %>
12
+ </div>
13
+ <div class="flex flex-wrap items-center gap-3">
14
+ <%= poetry_label(for_id: "orders-search", class: "sr-only") { "Search orders" } %>
15
+ <%= poetry_input_group(class: "max-w-xs") do %>
16
+ <%= poetry_input_group_addon do %>
17
+ <%= poetry_icon(name: :search) %>
18
+ <% end %>
19
+ <%= poetry_input_group_input(id: "orders-search", name: "q", placeholder: "Search orders…") %>
20
+ <% end %>
21
+ <%= poetry_label(for_id: "orders-status", class: "sr-only") { "Filter by status" } %>
22
+ <%= poetry_native_select(name: "status", id: "orders-status",
23
+ options: [["All statuses", "all"], ["Fulfilled", "fulfilled"],
24
+ ["Processing", "processing"], ["Refunded", "refunded"]]) %>
25
+ <%= poetry_button_group("aria-label": "Layout", class: "ml-auto") do %>
26
+ <%= poetry_button(variant: :outline, size: :sm, "aria-pressed": "true") { "List" } %>
27
+ <%= poetry_button(variant: :outline, size: :sm, "aria-pressed": "false") { "Board" } %>
28
+ <% end %>
29
+ </div>
30
+ <div class="overflow-hidden rounded-lg border">
31
+ <%= poetry_table do %>
32
+ <%= poetry_table_caption(class: "sr-only") { "Open orders with customer, status, and total." } %>
33
+ <%= poetry_table_header do %>
34
+ <%= poetry_table_row do %>
35
+ <%= poetry_table_head { "Order" } %>
36
+ <%= poetry_table_head { "Customer" } %>
37
+ <%= poetry_table_head { "Status" } %>
38
+ <%= poetry_table_head(class: "text-right") { "Total" } %>
39
+ <% end %>
40
+ <% end %>
41
+ <%= poetry_table_body do %>
42
+ <%= poetry_table_row do %>
43
+ <%= poetry_table_cell(class: "font-medium") { "ORD-1042" } %>
44
+ <%= poetry_table_cell { "Northwind Traders" } %>
45
+ <%= poetry_table_cell { poetry_badge(variant: :success) { "Fulfilled" } } %>
46
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$1,250.00" } %>
47
+ <% end %>
48
+ <%= poetry_table_row do %>
49
+ <%= poetry_table_cell(class: "font-medium") { "ORD-1041" } %>
50
+ <%= poetry_table_cell { "Lumen Labs" } %>
51
+ <%= poetry_table_cell { poetry_badge(variant: :warning) { "Processing" } } %>
52
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$482.50" } %>
53
+ <% end %>
54
+ <%= poetry_table_row do %>
55
+ <%= poetry_table_cell(class: "font-medium") { "ORD-1040" } %>
56
+ <%= poetry_table_cell { "Aperture Optics" } %>
57
+ <%= poetry_table_cell { poetry_badge(variant: :outline) { "Refunded" } } %>
58
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$96.00" } %>
59
+ <% end %>
60
+ <%= poetry_table_row do %>
61
+ <%= poetry_table_cell(class: "font-medium") { "ORD-1039" } %>
62
+ <%= poetry_table_cell { "Gable & Sons" } %>
63
+ <%= poetry_table_cell { poetry_badge(variant: :warning) { "Processing" } } %>
64
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$2,040.00" } %>
65
+ <% end %>
66
+ <%= poetry_table_row do %>
67
+ <%= poetry_table_cell(class: "font-medium") { "ORD-1038" } %>
68
+ <%= poetry_table_cell { "Fern & Field" } %>
69
+ <%= poetry_table_cell { poetry_badge(variant: :success) { "Fulfilled" } } %>
70
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$318.75" } %>
71
+ <% end %>
72
+ <% end %>
73
+ <%= poetry_table_footer do %>
74
+ <%= poetry_table_row do %>
75
+ <%= poetry_table_cell(colspan: 3) { "Total" } %>
76
+ <%= poetry_table_cell(class: "text-right tabular-nums") { "$4,187.25" } %>
77
+ <% end %>
78
+ <% end %>
79
+ <% end %>
80
+ </div>
81
+ <div class="flex flex-wrap items-center justify-between gap-4">
82
+ <p class="text-sm text-muted-foreground">Showing 5 of 42 orders</p>
83
+ <%= poetry_pagination(current: 3, total: 9, current_variant: :filled, path: ->(page) { "?page=#{page}" }) %>
84
+ </div>
85
+ </section>
@@ -0,0 +1,26 @@
1
+ <%# poetry:block title="Destructive panel" description="A guarded destructive action: heading and consequences in plain language, a severity-tinted alert with the blast radius, and a cancel/confirm action pair." keywords="deletion, deactivate, irreversible, permanently" %>
2
+ <%# Page framing: the panel keeps its container + breathing room when it
3
+ is the page's subject (a judged-run lesson); drop the outer wrapper
4
+ when composing into an already-padded frame. %>
5
+ <div class="mx-auto max-w-xl p-6">
6
+ <section class="space-y-4 rounded-lg border border-destructive/50 p-6" aria-labelledby="destructive-panel-title">
7
+ <div class="space-y-1">
8
+ <h2 id="destructive-panel-title" class="text-base font-semibold">Revoke deploy token</h2>
9
+ <p class="text-sm text-muted-foreground">The token stops working immediately. Three services signed their last deploy with it — their next runs will fail until a replacement is configured.</p>
10
+ </div>
11
+ <%# Icon + title only: an alert DESCRIPTION rides the destructive tint,
12
+ which sits at 4.49:1 on white (the standing token-retune ledger) -
13
+ the blast radius lives in the AA-clean muted copy above instead. %>
14
+ <%= poetry_alert(variant: :destructive) do |alert| %>
15
+ <% alert.with_icon(name: "triangle-alert") %>
16
+ <% alert.with_title { "This cannot be undone" } %>
17
+ <% end %>
18
+ <div class="flex items-center justify-end gap-2">
19
+ <%= poetry_button(variant: :outline) { "Cancel" } %>
20
+ <%= poetry_button(variant: :destructive) do |button| %>
21
+ <% button.with_leading { poetry_icon(name: :trash) } %>
22
+ Revoke token
23
+ <% end %>
24
+ </div>
25
+ </section>
26
+ </div>
@@ -0,0 +1,25 @@
1
+ <%# poetry:block title="Page header" description="The furniture a screen opens with: breadcrumb trail, page title with supporting description, and a right-aligned action group over a grounding rule." keywords="header, breadcrumb" %>
2
+ <header class="flex flex-col gap-4">
3
+ <%= poetry_breadcrumb do |crumb| %>
4
+ <% crumb.with_item("Home", href: "/") %>
5
+ <% crumb.with_item("Projects", href: "/projects") %>
6
+ <% crumb.with_item("Atlas") %>
7
+ <% end %>
8
+ <div class="flex flex-wrap items-end justify-between gap-4">
9
+ <div class="space-y-1">
10
+ <h1 class="text-2xl font-semibold tracking-tight">Atlas</h1>
11
+ <p class="text-sm text-muted-foreground">Shipping schedules, owners, and release health for the Atlas program.</p>
12
+ </div>
13
+ <div class="flex items-center gap-2">
14
+ <%= poetry_button(variant: :outline) do |button| %>
15
+ <% button.with_leading { poetry_icon(name: :download) } %>
16
+ Export
17
+ <% end %>
18
+ <%= poetry_button do |button| %>
19
+ <% button.with_leading { poetry_icon(name: :plus) } %>
20
+ New project
21
+ <% end %>
22
+ </div>
23
+ </div>
24
+ <%= poetry_separator %>
25
+ </header>
@@ -0,0 +1,42 @@
1
+ <%# poetry:block title="Section card" description="A contained content section: header row with title, supporting description and a status badge, body copy with a feature list, and a footer pairing meta text with a call-to-action." keywords="settings, section, panel, account, detail, summary, form" %>
2
+ <%# Page framing: a section keeps its container + breathing room when it is
3
+ the page's subject - a bare card at the viewport origin reads cramped
4
+ (a judged-run lesson). Drop the wrapper only when composing into an
5
+ already-padded frame like the app-shell content area. %>
6
+ <div class="mx-auto max-w-md p-6">
7
+ <%= poetry_card do |card| %>
8
+ <% card.with_title { "Usage-based billing" } %>
9
+ <% card.with_description { "Meter API calls and storage, invoiced monthly." } %>
10
+ <% card.with_action do %>
11
+ <%= poetry_badge(variant: :secondary) { "Beta" } %>
12
+ <% end %>
13
+ <% card.with_footer do %>
14
+ <div class="flex w-full items-center justify-between gap-4">
15
+ <span class="text-xs text-muted-foreground">Updated 2 days ago</span>
16
+ <%# The CTA reads as interactive (v1.1): always-underlined with a
17
+ trailing arrow - the affordance the judged card pairs kept
18
+ rewarding. %>
19
+ <%= poetry_link(href: "/docs/billing", underline: :always, class: "inline-flex items-center gap-1") do %>
20
+ Learn more <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
21
+ <% end %>
22
+ </div>
23
+ <% end %>
24
+ <div class="space-y-3 text-sm">
25
+ <p>Pay for what the workspace actually uses. Metering runs hourly and the invoice line items match the usage dashboard.</p>
26
+ <ul class="space-y-2">
27
+ <li class="flex items-center gap-2">
28
+ <%= poetry_icon(name: :check, class: "size-4 text-muted-foreground") %>
29
+ <span>Per-request API metering</span>
30
+ </li>
31
+ <li class="flex items-center gap-2">
32
+ <%= poetry_icon(name: :check, class: "size-4 text-muted-foreground") %>
33
+ <span>Storage billed by the gigabyte-hour</span>
34
+ </li>
35
+ <li class="flex items-center gap-2">
36
+ <%= poetry_icon(name: :check, class: "size-4 text-muted-foreground") %>
37
+ <span>Spend alerts before every threshold</span>
38
+ </li>
39
+ </ul>
40
+ </div>
41
+ <% end %>
42
+ </div>