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.
- checksums.yaml +7 -0
- data/.gitattributes +6 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/THIRD_PARTY_NOTICES.md +201 -0
- data/app/components/poetry/ui/accordion/component.html.erb +3 -0
- data/app/components/poetry/ui/accordion/component.rb +197 -0
- data/app/components/poetry/ui/accordion/preview.rb +39 -0
- data/app/components/poetry/ui/accordion/style.rb +37 -0
- data/app/components/poetry/ui/alert/component.html.erb +6 -0
- data/app/components/poetry/ui/alert/component.rb +61 -0
- data/app/components/poetry/ui/alert/preview.rb +35 -0
- data/app/components/poetry/ui/alert/style.rb +24 -0
- data/app/components/poetry/ui/alert_dialog/component.html.erb +17 -0
- data/app/components/poetry/ui/alert_dialog/component.rb +208 -0
- data/app/components/poetry/ui/alert_dialog/preview.rb +48 -0
- data/app/components/poetry/ui/alert_dialog/style.rb +46 -0
- data/app/components/poetry/ui/aspect_ratio/component.rb +63 -0
- data/app/components/poetry/ui/aspect_ratio/preview.rb +29 -0
- data/app/components/poetry/ui/aspect_ratio/style.rb +13 -0
- data/app/components/poetry/ui/attachment/component.html.erb +13 -0
- data/app/components/poetry/ui/attachment/component.rb +139 -0
- data/app/components/poetry/ui/attachment/preview.rb +83 -0
- data/app/components/poetry/ui/attachment/style.rb +59 -0
- data/app/components/poetry/ui/autocomplete/component.html.erb +16 -0
- data/app/components/poetry/ui/autocomplete/component.rb +193 -0
- data/app/components/poetry/ui/autocomplete/preview.rb +39 -0
- data/app/components/poetry/ui/autocomplete/style.rb +18 -0
- data/app/components/poetry/ui/avatar/component.rb +103 -0
- data/app/components/poetry/ui/avatar/group_preview.html.erb +11 -0
- data/app/components/poetry/ui/avatar/preview.rb +50 -0
- data/app/components/poetry/ui/avatar/style.rb +34 -0
- data/app/components/poetry/ui/badge/component.rb +73 -0
- data/app/components/poetry/ui/badge/preview.rb +54 -0
- data/app/components/poetry/ui/badge/style.rb +35 -0
- data/app/components/poetry/ui/breadcrumb/component.html.erb +32 -0
- data/app/components/poetry/ui/breadcrumb/component.rb +102 -0
- data/app/components/poetry/ui/breadcrumb/dropdown_crumb_preview.html.erb +21 -0
- data/app/components/poetry/ui/breadcrumb/preview.rb +45 -0
- data/app/components/poetry/ui/breadcrumb/style.rb +25 -0
- data/app/components/poetry/ui/bubble/component.html.erb +4 -0
- data/app/components/poetry/ui/bubble/component.rb +106 -0
- data/app/components/poetry/ui/bubble/preview.rb +62 -0
- data/app/components/poetry/ui/bubble/style.rb +47 -0
- data/app/components/poetry/ui/button/component.html.erb +14 -0
- data/app/components/poetry/ui/button/component.rb +168 -0
- data/app/components/poetry/ui/button/preview.rb +94 -0
- data/app/components/poetry/ui/button/style.rb +41 -0
- data/app/components/poetry/ui/button_group/component.rb +70 -0
- data/app/components/poetry/ui/button_group/popup_triggers_preview.html.erb +21 -0
- data/app/components/poetry/ui/button_group/preview.rb +46 -0
- data/app/components/poetry/ui/button_group/style.rb +27 -0
- data/app/components/poetry/ui/button_group/text_prefix_preview.html.erb +7 -0
- data/app/components/poetry/ui/calendar/component.html.erb +72 -0
- data/app/components/poetry/ui/calendar/component.rb +366 -0
- data/app/components/poetry/ui/calendar/preview.rb +55 -0
- data/app/components/poetry/ui/calendar/style.rb +55 -0
- data/app/components/poetry/ui/card/component.html.erb +11 -0
- data/app/components/poetry/ui/card/component.rb +90 -0
- data/app/components/poetry/ui/card/preview.rb +29 -0
- data/app/components/poetry/ui/card/style.rb +23 -0
- data/app/components/poetry/ui/carousel/component.html.erb +17 -0
- data/app/components/poetry/ui/carousel/component.rb +179 -0
- data/app/components/poetry/ui/carousel/preview.rb +67 -0
- data/app/components/poetry/ui/carousel/style.rb +39 -0
- data/app/components/poetry/ui/checkbox/component.html.erb +22 -0
- data/app/components/poetry/ui/checkbox/component.rb +179 -0
- data/app/components/poetry/ui/checkbox/preview.rb +68 -0
- data/app/components/poetry/ui/checkbox/style.rb +28 -0
- data/app/components/poetry/ui/clipboard_text/component.html.erb +15 -0
- data/app/components/poetry/ui/clipboard_text/component.rb +154 -0
- data/app/components/poetry/ui/clipboard_text/preview.rb +24 -0
- data/app/components/poetry/ui/clipboard_text/style.rb +23 -0
- data/app/components/poetry/ui/code_block/component.html.erb +13 -0
- data/app/components/poetry/ui/code_block/component.rb +141 -0
- data/app/components/poetry/ui/code_block/preview.rb +40 -0
- data/app/components/poetry/ui/code_block/style.rb +38 -0
- data/app/components/poetry/ui/collapsible/component.html.erb +4 -0
- data/app/components/poetry/ui/collapsible/component.rb +134 -0
- data/app/components/poetry/ui/collapsible/preview.rb +24 -0
- data/app/components/poetry/ui/combobox/component.html.erb +49 -0
- data/app/components/poetry/ui/combobox/component.rb +1086 -0
- data/app/components/poetry/ui/combobox/preview.rb +164 -0
- data/app/components/poetry/ui/combobox/style.rb +148 -0
- data/app/components/poetry/ui/command/component.html.erb +20 -0
- data/app/components/poetry/ui/command/component.rb +526 -0
- data/app/components/poetry/ui/command/dialog_component.html.erb +14 -0
- data/app/components/poetry/ui/command/dialog_component.rb +206 -0
- data/app/components/poetry/ui/command/dialog_preview.rb +39 -0
- data/app/components/poetry/ui/command/preview.rb +121 -0
- data/app/components/poetry/ui/command/style.rb +78 -0
- data/app/components/poetry/ui/composable_trigger.rb +63 -0
- data/app/components/poetry/ui/context_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/context_menu/component.rb +358 -0
- data/app/components/poetry/ui/context_menu/preview.rb +111 -0
- data/app/components/poetry/ui/context_menu/style.rb +59 -0
- data/app/components/poetry/ui/data_table/component.html.erb +78 -0
- data/app/components/poetry/ui/data_table/component.rb +277 -0
- data/app/components/poetry/ui/data_table/preview.rb +68 -0
- data/app/components/poetry/ui/data_table/state.rb +115 -0
- data/app/components/poetry/ui/data_table/style.rb +42 -0
- data/app/components/poetry/ui/date_field/component.html.erb +7 -0
- data/app/components/poetry/ui/date_field/component.rb +209 -0
- data/app/components/poetry/ui/date_field/preview.rb +42 -0
- data/app/components/poetry/ui/date_field/style.rb +35 -0
- data/app/components/poetry/ui/date_picker/component.html.erb +23 -0
- data/app/components/poetry/ui/date_picker/component.rb +242 -0
- data/app/components/poetry/ui/date_picker/preview.rb +31 -0
- data/app/components/poetry/ui/date_picker/style.rb +25 -0
- data/app/components/poetry/ui/deferred/component.html.erb +16 -0
- data/app/components/poetry/ui/deferred/component.rb +90 -0
- data/app/components/poetry/ui/deferred/preview.rb +24 -0
- data/app/components/poetry/ui/dialog/component.html.erb +21 -0
- data/app/components/poetry/ui/dialog/component.rb +194 -0
- data/app/components/poetry/ui/dialog/preview.rb +33 -0
- data/app/components/poetry/ui/dialog/style.rb +30 -0
- data/app/components/poetry/ui/drawer/component.html.erb +16 -0
- data/app/components/poetry/ui/drawer/component.rb +207 -0
- data/app/components/poetry/ui/drawer/preview.rb +69 -0
- data/app/components/poetry/ui/drawer/style.rb +105 -0
- data/app/components/poetry/ui/dropdown_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/dropdown_menu/component.rb +350 -0
- data/app/components/poetry/ui/dropdown_menu/preview.rb +101 -0
- data/app/components/poetry/ui/dropdown_menu/style.rb +60 -0
- data/app/components/poetry/ui/empty/component.html.erb +10 -0
- data/app/components/poetry/ui/empty/component.rb +79 -0
- data/app/components/poetry/ui/empty/preview.rb +27 -0
- data/app/components/poetry/ui/empty/style.rb +27 -0
- data/app/components/poetry/ui/family_identity.rb +52 -0
- data/app/components/poetry/ui/field/component.html.erb +19 -0
- data/app/components/poetry/ui/field/component.rb +183 -0
- data/app/components/poetry/ui/field/preview.html.erb +30 -0
- data/app/components/poetry/ui/field/preview.rb +72 -0
- data/app/components/poetry/ui/field/style.rb +68 -0
- data/app/components/poetry/ui/field_group/component.rb +61 -0
- data/app/components/poetry/ui/field_group/preview.html.erb +30 -0
- data/app/components/poetry/ui/field_group/preview.rb +20 -0
- data/app/components/poetry/ui/field_group/style.rb +25 -0
- data/app/components/poetry/ui/field_separator/component.rb +70 -0
- data/app/components/poetry/ui/field_separator/preview.rb +18 -0
- data/app/components/poetry/ui/field_separator/style.rb +20 -0
- data/app/components/poetry/ui/fieldset/component.rb +90 -0
- data/app/components/poetry/ui/fieldset/preview.html.erb +22 -0
- data/app/components/poetry/ui/fieldset/preview.rb +25 -0
- data/app/components/poetry/ui/fieldset/style.rb +18 -0
- data/app/components/poetry/ui/file_input/component.html.erb +14 -0
- data/app/components/poetry/ui/file_input/component.rb +188 -0
- data/app/components/poetry/ui/file_input/preview.rb +30 -0
- data/app/components/poetry/ui/file_input/style.rb +35 -0
- data/app/components/poetry/ui/hover_card/component.html.erb +6 -0
- data/app/components/poetry/ui/hover_card/component.rb +201 -0
- data/app/components/poetry/ui/hover_card/preview.rb +60 -0
- data/app/components/poetry/ui/hover_card/style.rb +17 -0
- data/app/components/poetry/ui/icon/component.rb +127 -0
- data/app/components/poetry/ui/icon/preview.rb +21 -0
- data/app/components/poetry/ui/input/component.rb +80 -0
- data/app/components/poetry/ui/input/preview.rb +35 -0
- data/app/components/poetry/ui/input/style.rb +18 -0
- data/app/components/poetry/ui/input_group/button_preview.html.erb +12 -0
- data/app/components/poetry/ui/input_group/component.rb +69 -0
- data/app/components/poetry/ui/input_group/preview.rb +23 -0
- data/app/components/poetry/ui/input_group/search_preview.html.erb +13 -0
- data/app/components/poetry/ui/input_group/style.rb +40 -0
- data/app/components/poetry/ui/input_group/textarea_preview.html.erb +10 -0
- data/app/components/poetry/ui/input_group_field.rb +61 -0
- data/app/components/poetry/ui/input_otp/component.html.erb +25 -0
- data/app/components/poetry/ui/input_otp/component.rb +250 -0
- data/app/components/poetry/ui/input_otp/preview.rb +91 -0
- data/app/components/poetry/ui/input_otp/style.rb +36 -0
- data/app/components/poetry/ui/item/component.html.erb +13 -0
- data/app/components/poetry/ui/item/component.rb +99 -0
- data/app/components/poetry/ui/item/group_preview.html.erb +15 -0
- data/app/components/poetry/ui/item/preview.rb +52 -0
- data/app/components/poetry/ui/item/style.rb +45 -0
- data/app/components/poetry/ui/kbd/component.rb +47 -0
- data/app/components/poetry/ui/kbd/group_preview.html.erb +4 -0
- data/app/components/poetry/ui/kbd/preview.rb +23 -0
- data/app/components/poetry/ui/kbd/style.rb +13 -0
- data/app/components/poetry/ui/label/component.rb +46 -0
- data/app/components/poetry/ui/label/preview.html.erb +5 -0
- data/app/components/poetry/ui/label/preview.rb +16 -0
- data/app/components/poetry/ui/label/style.rb +13 -0
- data/app/components/poetry/ui/link/component.rb +62 -0
- data/app/components/poetry/ui/link/preview.rb +31 -0
- data/app/components/poetry/ui/link/style.rb +20 -0
- data/app/components/poetry/ui/marker/component.html.erb +4 -0
- data/app/components/poetry/ui/marker/component.rb +81 -0
- data/app/components/poetry/ui/marker/preview.rb +43 -0
- data/app/components/poetry/ui/marker/style.rb +24 -0
- data/app/components/poetry/ui/menubar/component.html.erb +3 -0
- data/app/components/poetry/ui/menubar/component.rb +493 -0
- data/app/components/poetry/ui/menubar/preview.rb +164 -0
- data/app/components/poetry/ui/menubar/style.rb +71 -0
- data/app/components/poetry/ui/menus.rb +386 -0
- data/app/components/poetry/ui/message/component.html.erb +8 -0
- data/app/components/poetry/ui/message/component.rb +64 -0
- data/app/components/poetry/ui/message/preview.rb +33 -0
- data/app/components/poetry/ui/message/style.rb +29 -0
- data/app/components/poetry/ui/message_scroller/component.html.erb +15 -0
- data/app/components/poetry/ui/message_scroller/component.rb +163 -0
- data/app/components/poetry/ui/message_scroller/preview.rb +33 -0
- data/app/components/poetry/ui/message_scroller/style.rb +39 -0
- data/app/components/poetry/ui/metadata_list/component.rb +88 -0
- data/app/components/poetry/ui/metadata_list/preview.rb +55 -0
- data/app/components/poetry/ui/metadata_list/style.rb +35 -0
- data/app/components/poetry/ui/meter/component.rb +117 -0
- data/app/components/poetry/ui/meter/preview.rb +28 -0
- data/app/components/poetry/ui/meter/style.rb +16 -0
- data/app/components/poetry/ui/native_select/component.rb +124 -0
- data/app/components/poetry/ui/native_select/labelled_preview.html.erb +7 -0
- data/app/components/poetry/ui/native_select/preview.rb +20 -0
- data/app/components/poetry/ui/native_select/style.rb +22 -0
- data/app/components/poetry/ui/navigation_menu/bar_preview.html.erb +14 -0
- data/app/components/poetry/ui/navigation_menu/component.html.erb +25 -0
- data/app/components/poetry/ui/navigation_menu/component.rb +254 -0
- data/app/components/poetry/ui/navigation_menu/preview.rb +20 -0
- data/app/components/poetry/ui/navigation_menu/style.rb +73 -0
- data/app/components/poetry/ui/navigation_menu/viewport_preview.html.erb +23 -0
- data/app/components/poetry/ui/number_field/component.html.erb +19 -0
- data/app/components/poetry/ui/number_field/component.rb +263 -0
- data/app/components/poetry/ui/number_field/preview.rb +66 -0
- data/app/components/poetry/ui/number_field/style.rb +14 -0
- data/app/components/poetry/ui/pagination/component.html.erb +40 -0
- data/app/components/poetry/ui/pagination/component.rb +182 -0
- data/app/components/poetry/ui/pagination/preview.rb +47 -0
- data/app/components/poetry/ui/pagination/style.rb +27 -0
- data/app/components/poetry/ui/popover/component.html.erb +17 -0
- data/app/components/poetry/ui/popover/component.rb +203 -0
- data/app/components/poetry/ui/popover/preview.html.erb +29 -0
- data/app/components/poetry/ui/popover/preview.rb +66 -0
- data/app/components/poetry/ui/popover/style.rb +26 -0
- data/app/components/poetry/ui/popper_consumer.rb +69 -0
- data/app/components/poetry/ui/progress/component.rb +95 -0
- data/app/components/poetry/ui/progress/preview.rb +22 -0
- data/app/components/poetry/ui/progress/style.rb +19 -0
- data/app/components/poetry/ui/questionnaire/component.html.erb +80 -0
- data/app/components/poetry/ui/questionnaire/component.rb +396 -0
- data/app/components/poetry/ui/questionnaire/preview.rb +99 -0
- data/app/components/poetry/ui/questionnaire/style.rb +53 -0
- data/app/components/poetry/ui/radio_group/component.html.erb +4 -0
- data/app/components/poetry/ui/radio_group/component.rb +323 -0
- data/app/components/poetry/ui/radio_group/preview.rb +109 -0
- data/app/components/poetry/ui/radio_group/style.rb +53 -0
- data/app/components/poetry/ui/resizable/component.html.erb +10 -0
- data/app/components/poetry/ui/resizable/component.rb +178 -0
- data/app/components/poetry/ui/resizable/preview.rb +42 -0
- data/app/components/poetry/ui/resizable/style.rb +30 -0
- data/app/components/poetry/ui/scroll_area/component.rb +76 -0
- data/app/components/poetry/ui/scroll_area/preview.rb +35 -0
- data/app/components/poetry/ui/scroll_area/style.rb +19 -0
- data/app/components/poetry/ui/search_field/component.html.erb +15 -0
- data/app/components/poetry/ui/search_field/component.rb +137 -0
- data/app/components/poetry/ui/search_field/preview.rb +23 -0
- data/app/components/poetry/ui/search_field/style.rb +18 -0
- data/app/components/poetry/ui/select/component.html.erb +15 -0
- data/app/components/poetry/ui/select/component.rb +639 -0
- data/app/components/poetry/ui/select/preview.rb +113 -0
- data/app/components/poetry/ui/select/style.rb +58 -0
- data/app/components/poetry/ui/sensitive_input/component.html.erb +29 -0
- data/app/components/poetry/ui/sensitive_input/component.rb +274 -0
- data/app/components/poetry/ui/sensitive_input/preview.rb +40 -0
- data/app/components/poetry/ui/sensitive_input/style.rb +49 -0
- data/app/components/poetry/ui/separator/component.rb +57 -0
- data/app/components/poetry/ui/separator/preview.rb +19 -0
- data/app/components/poetry/ui/separator/style.rb +15 -0
- data/app/components/poetry/ui/sheet/component.html.erb +21 -0
- data/app/components/poetry/ui/sheet/component.rb +97 -0
- data/app/components/poetry/ui/sheet/preview.rb +57 -0
- data/app/components/poetry/ui/sheet/style.rb +47 -0
- data/app/components/poetry/ui/sidebar/component.html.erb +20 -0
- data/app/components/poetry/ui/sidebar/component.rb +249 -0
- data/app/components/poetry/ui/sidebar/preview.rb +21 -0
- data/app/components/poetry/ui/sidebar/shell_preview.html.erb +47 -0
- data/app/components/poetry/ui/sidebar/style.rb +139 -0
- data/app/components/poetry/ui/skeleton/component.rb +39 -0
- data/app/components/poetry/ui/skeleton/preview.rb +22 -0
- data/app/components/poetry/ui/skeleton/style.rb +13 -0
- data/app/components/poetry/ui/slider/component.html.erb +13 -0
- data/app/components/poetry/ui/slider/component.rb +360 -0
- data/app/components/poetry/ui/slider/preview.rb +73 -0
- data/app/components/poetry/ui/slider/style.rb +55 -0
- data/app/components/poetry/ui/spinner/component.rb +58 -0
- data/app/components/poetry/ui/spinner/preview.rb +22 -0
- data/app/components/poetry/ui/spinner/style.rb +12 -0
- data/app/components/poetry/ui/stat/component.html.erb +13 -0
- data/app/components/poetry/ui/stat/component.rb +102 -0
- data/app/components/poetry/ui/stat/preview.rb +47 -0
- data/app/components/poetry/ui/stat/style.rb +29 -0
- data/app/components/poetry/ui/switch/component.html.erb +15 -0
- data/app/components/poetry/ui/switch/component.rb +136 -0
- data/app/components/poetry/ui/switch/preview.rb +66 -0
- data/app/components/poetry/ui/switch/style.rb +22 -0
- data/app/components/poetry/ui/table/component.html.erb +3 -0
- data/app/components/poetry/ui/table/component.rb +93 -0
- data/app/components/poetry/ui/table/preview.rb +96 -0
- data/app/components/poetry/ui/table/style.rb +26 -0
- data/app/components/poetry/ui/tabs/component.html.erb +10 -0
- data/app/components/poetry/ui/tabs/component.rb +253 -0
- data/app/components/poetry/ui/tabs/preview.rb +43 -0
- data/app/components/poetry/ui/tabs/style.rb +49 -0
- data/app/components/poetry/ui/tag_group/component.html.erb +8 -0
- data/app/components/poetry/ui/tag_group/component.rb +203 -0
- data/app/components/poetry/ui/tag_group/preview.rb +43 -0
- data/app/components/poetry/ui/tag_group/style.rb +29 -0
- data/app/components/poetry/ui/textarea/component.rb +64 -0
- data/app/components/poetry/ui/textarea/preview.rb +75 -0
- data/app/components/poetry/ui/textarea/style.rb +18 -0
- data/app/components/poetry/ui/time_field/component.html.erb +6 -0
- data/app/components/poetry/ui/time_field/component.rb +85 -0
- data/app/components/poetry/ui/time_field/preview.rb +40 -0
- data/app/components/poetry/ui/time_field/style.rb +30 -0
- data/app/components/poetry/ui/timeline/component.rb +134 -0
- data/app/components/poetry/ui/timeline/preview.rb +61 -0
- data/app/components/poetry/ui/timeline/style.rb +54 -0
- data/app/components/poetry/ui/toast/component.html.erb +24 -0
- data/app/components/poetry/ui/toast/component.rb +178 -0
- data/app/components/poetry/ui/toast/preview.rb +69 -0
- data/app/components/poetry/ui/toast/style.rb +33 -0
- data/app/components/poetry/ui/toast_trigger/addressed_preview.html.erb +12 -0
- data/app/components/poetry/ui/toast_trigger/component.html.erb +3 -0
- data/app/components/poetry/ui/toast_trigger/component.rb +82 -0
- data/app/components/poetry/ui/toast_trigger/default_preview.html.erb +11 -0
- data/app/components/poetry/ui/toast_trigger/preview.rb +19 -0
- data/app/components/poetry/ui/toaster/component.html.erb +1 -0
- data/app/components/poetry/ui/toaster/component.rb +99 -0
- data/app/components/poetry/ui/toaster/positions_preview.html.erb +24 -0
- data/app/components/poetry/ui/toaster/preview.rb +46 -0
- data/app/components/poetry/ui/toaster/stamp_preview.html.erb +15 -0
- data/app/components/poetry/ui/toaster/style.rb +26 -0
- data/app/components/poetry/ui/toggle/component.rb +120 -0
- data/app/components/poetry/ui/toggle/preview.rb +79 -0
- data/app/components/poetry/ui/toggle/style.rb +33 -0
- data/app/components/poetry/ui/toggle_group/component.html.erb +4 -0
- data/app/components/poetry/ui/toggle_group/component.rb +252 -0
- data/app/components/poetry/ui/toggle_group/preview.rb +143 -0
- data/app/components/poetry/ui/toggle_group/style.rb +33 -0
- data/app/components/poetry/ui/toolbar/component.rb +125 -0
- data/app/components/poetry/ui/toolbar/preview.rb +41 -0
- data/app/components/poetry/ui/toolbar/style.rb +20 -0
- data/app/components/poetry/ui/tooltip/component.html.erb +20 -0
- data/app/components/poetry/ui/tooltip/component.rb +185 -0
- data/app/components/poetry/ui/tooltip/preview.rb +74 -0
- data/app/components/poetry/ui/tooltip/previews/provider_row.html.erb +25 -0
- data/app/components/poetry/ui/tooltip/style.rb +24 -0
- data/app/components/poetry/ui/tree/component.html.erb +32 -0
- data/app/components/poetry/ui/tree/component.rb +233 -0
- data/app/components/poetry/ui/tree/preview.rb +55 -0
- data/app/components/poetry/ui/tree/style.rb +29 -0
- data/app/components/poetry/ui/typeset/component.rb +73 -0
- data/app/components/poetry/ui/typeset/preview.rb +45 -0
- data/app/components/poetry/ui/typeset/style.rb +15 -0
- data/app/controllers/poetry/ui/llms_controller.rb +28 -0
- data/app/helpers/poetry/ui/components_helper.rb +1794 -0
- data/app/helpers/poetry/ui/form_builder/type_inference.rb +117 -0
- data/app/helpers/poetry/ui/form_builder.rb +916 -0
- data/app/helpers/poetry/ui/optimistic_form_builder.rb +55 -0
- data/app/helpers/poetry/ui/toast_stream_actions.rb +27 -0
- data/config/component_descriptions.yml +96 -0
- data/config/component_registry.yml +12257 -0
- data/config/locales/en.yml +93 -0
- data/config/routes.rb +8 -0
- data/config/template_classes.txt +113 -0
- data/lib/generators/poetry/add/add_generator.rb +218 -0
- data/lib/generators/poetry/agent_rules/agent_rules_generator.rb +91 -0
- data/lib/generators/poetry/agents/agents_generator.rb +24 -0
- data/lib/generators/poetry/agents_section.rb +148 -0
- data/lib/generators/poetry/block/block_generator.rb +67 -0
- data/lib/generators/poetry/block/templates/action_bar.html.erb +45 -0
- data/lib/generators/poetry/block/templates/app_shell.html.erb +139 -0
- data/lib/generators/poetry/block/templates/data_index.html.erb +85 -0
- data/lib/generators/poetry/block/templates/destructive_panel.html.erb +26 -0
- data/lib/generators/poetry/block/templates/page_header.html.erb +25 -0
- data/lib/generators/poetry/block/templates/section_card.html.erb +42 -0
- data/lib/generators/poetry/block/templates/stepper.html.erb +79 -0
- data/lib/generators/poetry/block/templates/top_nav.html.erb +44 -0
- data/lib/generators/poetry/diff/diff_generator.rb +135 -0
- data/lib/generators/poetry/editor/editor_generator.rb +344 -0
- data/lib/generators/poetry/editor/templates/poetry-check-hook +81 -0
- data/lib/generators/poetry/install/install_generator.rb +364 -0
- data/lib/generators/poetry/pagination/pagination_generator.rb +76 -0
- data/lib/generators/poetry/pagination/templates/kaminari_paginator.html.erb +29 -0
- data/lib/generators/poetry/pagination/templates/poetry_link_renderer.rb +34 -0
- data/lib/generators/poetry/pagination/templates/poetry_pagy_helper.rb +28 -0
- data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js +131 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/controllers/orders_controller.rb +15 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/views/orders/index.html.erb +7 -0
- data/lib/generators/poetry/recipes/screen_data_index/test/system/orders_index_test.rb +16 -0
- data/lib/generators/poetry/recipes/screen_settings/app/controllers/settings_controller.rb +14 -0
- data/lib/generators/poetry/recipes/screen_settings/app/views/settings/show.html.erb +9 -0
- data/lib/generators/poetry/recipes/screen_settings/test/system/settings_show_test.rb +16 -0
- data/lib/generators/poetry/scaffold_templates/scaffold_templates_generator.rb +60 -0
- data/lib/generators/poetry/scaffold_templates/templates/_form.html.erb.tt +77 -0
- data/lib/generators/poetry/scaffold_templates/templates/controller.rb.tt +78 -0
- data/lib/generators/poetry/scaffold_templates/templates/edit.html.erb.tt +14 -0
- data/lib/generators/poetry/scaffold_templates/templates/index.html.erb.tt +24 -0
- data/lib/generators/poetry/scaffold_templates/templates/new.html.erb.tt +11 -0
- data/lib/generators/poetry/scaffold_templates/templates/partial.html.erb.tt +13 -0
- data/lib/generators/poetry/scaffold_templates/templates/show.html.erb.tt +18 -0
- data/lib/generators/poetry/skill/skill_generator.rb +34 -0
- data/lib/generators/poetry/skill/templates/poetry-component/SKILL.md +58 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/anatomy.md +152 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/checklist.md +67 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/documentation.md +137 -0
- data/lib/generators/poetry/skill/templates/poetry-design/SKILL.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/audit.md +123 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/compose.md +73 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/figma.md +68 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/paper.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/study.md +47 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/theme.md +70 -0
- data/lib/generators/poetry/skills_section.rb +58 -0
- data/lib/poetry/ui/chat.rb +283 -0
- data/lib/poetry/ui/code_block_highlighter.rb +60 -0
- data/lib/poetry/ui/engine.rb +55 -0
- data/lib/poetry/ui/recipes.rb +157 -0
- data/lib/poetry/ui/testing/combobox.rb +167 -0
- data/lib/poetry/ui/testing/dialog.rb +73 -0
- data/lib/poetry/ui/testing/menu.rb +127 -0
- data/lib/poetry/ui/testing/registration.rb +58 -0
- data/lib/poetry/ui/testing/select.rb +147 -0
- data/lib/poetry/ui/testing/tester.rb +130 -0
- data/lib/poetry/ui/testing.rb +130 -0
- data/lib/poetry/ui/themes.rb +109 -0
- data/lib/poetry/ui/version.rb +8 -0
- data/lib/poetry/ui/webmcp.rb +91 -0
- data/lib/poetry/ui.rb +285 -0
- data/lib/poetry-ui.rb +5 -0
- data/lib/tasks/poetry/check.rake +73 -0
- data/lib/tasks/poetry/design.rake +293 -0
- data/lib/tasks/poetry/verify.rake +36 -0
- data/themes/default.css +1793 -0
- data/themes/luma.css +1764 -0
- data/themes/lyra.css +1768 -0
- data/themes/maia.css +1768 -0
- data/themes/mira.css +1760 -0
- data/themes/nova.css +1755 -0
- data/themes/rhea.css +1771 -0
- data/themes/sera.css +1757 -0
- data/themes/vega.css +1775 -0
- data/typeset/typeset.css +499 -0
- 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>
|