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
data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// installed by the poetry `agent-embed` recipe - this file is yours: edit
|
|
4
|
+
// freely. An OPT-IN in-page GUI agent (page-agent, MIT, loaded from the
|
|
5
|
+
// pinned CDN build with ?autoInit=false - no auto agent, no demo LLM)
|
|
6
|
+
// configured for a poetry app. Wire a small form with this controller
|
|
7
|
+
// (targets: model, baseUrl, apiKey, status; actions: agent-demo#activate,
|
|
8
|
+
// agent-demo#deactivate) - see the reference demo at the poetry docs
|
|
9
|
+
// /agent page, whose loader this file mirrors.
|
|
10
|
+
//
|
|
11
|
+
// The three Turbo rules this encodes (learned the hard way):
|
|
12
|
+
// 1. Turbo replaces <body> on navigation and the agent panel lives there -
|
|
13
|
+
// remount on turbo:load.
|
|
14
|
+
// 2. Remount ONLY when idle: a running task survives the body swap
|
|
15
|
+
// headless and completes (URL-state components navigate mid-task);
|
|
16
|
+
// disposing mid-task aborts it out from under the agent.
|
|
17
|
+
// 3. On a hard load, the first turbo:load can fire before this module
|
|
18
|
+
// evaluates - catch up once at module init.
|
|
19
|
+
const SCRIPT_URL =
|
|
20
|
+
"https://cdn.jsdelivr.net/npm/page-agent@1.12.2/dist/iife/page-agent.demo.js?autoInit=false"
|
|
21
|
+
const FLAG = "poetry-agent-embed"
|
|
22
|
+
|
|
23
|
+
// Host-agnostic operator instructions for any poetry app. If your app
|
|
24
|
+
// serves its own operator register (a JSON of per-page instructions),
|
|
25
|
+
// point REGISTER_URL at it; absent, the agent still gets the poetry
|
|
26
|
+
// ground rules below plus the part contract via includeAttributes.
|
|
27
|
+
const REGISTER_URL = "/operator-register.json"
|
|
28
|
+
const POETRY_SYSTEM = `This site is built from poetry components. Every component stamps
|
|
29
|
+
data-component (its family) and data-slot (its parts) - trust those over guessing
|
|
30
|
+
from classes. Interactive components follow WAI-ARIA patterns; open surfaces are
|
|
31
|
+
portaled to the end of <body>, one at a time - never click through an overlay
|
|
32
|
+
scrim, dismiss first (Escape or outside click). Prefer clicking labels and options
|
|
33
|
+
over coordinates. Keyboard-first components (sliders, date/time segments, OTP
|
|
34
|
+
inputs): TYPE values into segments - typed input works where clicking alone never
|
|
35
|
+
sets them; sliders need arrow keys and track clicks are only coarse.`
|
|
36
|
+
|
|
37
|
+
let registerPromise = null
|
|
38
|
+
|
|
39
|
+
async function operatorRegister() {
|
|
40
|
+
registerPromise ||= fetch(REGISTER_URL)
|
|
41
|
+
.then((response) => (response.ok ? response.json() : null))
|
|
42
|
+
.catch(() => null)
|
|
43
|
+
return registerPromise
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function pageInstructions(register, url) {
|
|
47
|
+
if (!register) return null
|
|
48
|
+
const path = new URL(url, window.location.origin).pathname
|
|
49
|
+
const hit = Object.entries(register.pages || {})
|
|
50
|
+
.filter(([prefix]) => path === prefix || path.startsWith(`${prefix}/`))
|
|
51
|
+
.sort((a, b) => b[0].length - a[0].length)[0]
|
|
52
|
+
return `${hit ? hit[1] : ""}\n${register.default || ""}`.trim() || null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function mountAgent() {
|
|
56
|
+
if (window.pageAgent && !document.getElementById("page-agent-runtime_agent-panel") &&
|
|
57
|
+
window.pageAgent.status !== "running") {
|
|
58
|
+
try { window.pageAgent.dispose?.() } catch { /* replaced below */ }
|
|
59
|
+
window.pageAgent = undefined
|
|
60
|
+
}
|
|
61
|
+
if (window.pageAgent) return
|
|
62
|
+
const config = JSON.parse(sessionStorage.getItem(FLAG) || "null")
|
|
63
|
+
if (!config) return
|
|
64
|
+
|
|
65
|
+
if (!window.PageAgent) {
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
const script = document.createElement("script")
|
|
68
|
+
script.src = SCRIPT_URL
|
|
69
|
+
script.crossOrigin = "anonymous"
|
|
70
|
+
script.onload = resolve
|
|
71
|
+
script.onerror = reject
|
|
72
|
+
document.head.appendChild(script)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const register = await operatorRegister()
|
|
77
|
+
window.pageAgent = new window.PageAgent({
|
|
78
|
+
model: config.model,
|
|
79
|
+
baseURL: config.baseURL,
|
|
80
|
+
apiKey: config.apiKey,
|
|
81
|
+
language: "en-US",
|
|
82
|
+
includeAttributes: ["data-component", "data-slot"],
|
|
83
|
+
instructions: {
|
|
84
|
+
system: register?.system || POETRY_SYSTEM,
|
|
85
|
+
getPageInstructions: (url) => pageInstructions(register, url),
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
window.pageAgent.panel.show()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
document.addEventListener("turbo:load", mountAgent)
|
|
92
|
+
mountAgent()
|
|
93
|
+
|
|
94
|
+
export default class extends Controller {
|
|
95
|
+
static targets = ["model", "baseUrl", "apiKey", "status"]
|
|
96
|
+
|
|
97
|
+
connect() {
|
|
98
|
+
if (sessionStorage.getItem(FLAG)) this.note("Agent is active - the panel follows you across pages.")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async activate(event) {
|
|
102
|
+
event.preventDefault()
|
|
103
|
+
const config = {
|
|
104
|
+
model: this.modelTarget.value.trim(),
|
|
105
|
+
baseURL: this.baseUrlTarget.value.trim(),
|
|
106
|
+
apiKey: this.apiKeyTarget.value.trim(),
|
|
107
|
+
}
|
|
108
|
+
if (!config.apiKey) return this.note("An API key is required (it stays in this browser session).")
|
|
109
|
+
|
|
110
|
+
sessionStorage.setItem(FLAG, JSON.stringify(config))
|
|
111
|
+
try {
|
|
112
|
+
await mountAgent()
|
|
113
|
+
this.note("Agent mounted.")
|
|
114
|
+
} catch (error) {
|
|
115
|
+
sessionStorage.removeItem(FLAG)
|
|
116
|
+
this.note(`Could not mount the agent: ${error.message || error}`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
deactivate(event) {
|
|
121
|
+
event.preventDefault()
|
|
122
|
+
sessionStorage.removeItem(FLAG)
|
|
123
|
+
window.pageAgent?.dispose?.()
|
|
124
|
+
window.pageAgent = undefined
|
|
125
|
+
this.note("Agent deactivated and its key cleared.")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
note(text) {
|
|
129
|
+
this.statusTarget.textContent = text
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# installed by the poetry `screen-data-index` recipe - this file is yours:
|
|
4
|
+
# edit freely. Wire the route yourself (one line, so your routes.rb is
|
|
5
|
+
# never touched by an installer):
|
|
6
|
+
#
|
|
7
|
+
# resources :orders, only: :index
|
|
8
|
+
#
|
|
9
|
+
# The view renders the data-index block (installed alongside at
|
|
10
|
+
# app/views/blocks/_data_index.html.erb) with its sample rows; replace the
|
|
11
|
+
# block's static table with your records as you adapt it - the markup is
|
|
12
|
+
# yours too.
|
|
13
|
+
class OrdersController < ApplicationController
|
|
14
|
+
def index; end
|
|
15
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<%# installed by the poetry `screen-data-index` recipe - yours to adapt.
|
|
2
|
+
The screen is the data-index block in a padded page frame; the block
|
|
3
|
+
partial is the same source `bin/rails g poetry:block data-index`
|
|
4
|
+
copies in. %>
|
|
5
|
+
<div class="mx-auto max-w-5xl p-6">
|
|
6
|
+
<%= render "blocks/data_index" %>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "application_system_test_case"
|
|
4
|
+
|
|
5
|
+
# installed by the poetry `screen-data-index` recipe. Server-render
|
|
6
|
+
# assertions only - no JS driver required; the test proves the screen
|
|
7
|
+
# route renders the block's furniture.
|
|
8
|
+
class OrdersIndexTest < ApplicationSystemTestCase
|
|
9
|
+
test "the orders index renders the data-index screen" do
|
|
10
|
+
visit orders_path
|
|
11
|
+
|
|
12
|
+
assert_text "Orders"
|
|
13
|
+
assert_selector "table"
|
|
14
|
+
assert_text "New order"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# installed by the poetry `screen-settings` recipe - this file is yours:
|
|
4
|
+
# edit freely. Wire the route yourself (one line, so your routes.rb is
|
|
5
|
+
# never touched by an installer):
|
|
6
|
+
#
|
|
7
|
+
# resource :settings, only: :show
|
|
8
|
+
#
|
|
9
|
+
# The view composes the page-header, section-card, and destructive-panel
|
|
10
|
+
# blocks (installed alongside under app/views/blocks/); adapt their copy
|
|
11
|
+
# and wire the actions to your models as you go.
|
|
12
|
+
class SettingsController < ApplicationController
|
|
13
|
+
def show; end
|
|
14
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%# installed by the poetry `screen-settings` recipe - yours to adapt.
|
|
2
|
+
A settings page composed from three blocks: the page furniture, a
|
|
3
|
+
contained settings section, and the guarded danger zone. The section
|
|
4
|
+
blocks carry their own page framing; the stack just orders them. %>
|
|
5
|
+
<div class="mx-auto flex max-w-3xl flex-col gap-8 p-6">
|
|
6
|
+
<%= render "blocks/page_header" %>
|
|
7
|
+
<%= render "blocks/section_card" %>
|
|
8
|
+
<%= render "blocks/destructive_panel" %>
|
|
9
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "application_system_test_case"
|
|
4
|
+
|
|
5
|
+
# installed by the poetry `screen-settings` recipe. Server-render
|
|
6
|
+
# assertions only - no JS driver required; the test proves the composed
|
|
7
|
+
# screen renders all three blocks.
|
|
8
|
+
class SettingsShowTest < ApplicationSystemTestCase
|
|
9
|
+
test "the settings page composes header, section, and danger zone" do
|
|
10
|
+
visit settings_path
|
|
11
|
+
|
|
12
|
+
assert_text "Atlas"
|
|
13
|
+
assert_text "Usage-based billing"
|
|
14
|
+
assert_text "Revoke deploy token"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Poetry
|
|
6
|
+
# `rails g poetry:scaffold_templates`:
|
|
7
|
+
# Rails has always let an app override its generator templates
|
|
8
|
+
# from lib/templates/; what was missing was a set that renders with
|
|
9
|
+
# poetry. This copies scaffold view templates - and a matching scaffold
|
|
10
|
+
# controller template - so the STANDARD `rails g scaffold` produces
|
|
11
|
+
# poetry-composed output: a DataTable index with sanitized URL state
|
|
12
|
+
# (sortable whitelist, filter, pagination), Field-composed forms with
|
|
13
|
+
# attribute-type -> component mapping (plus column-name -> input-type
|
|
14
|
+
# heuristics and `null: false` -> required), a MetadataList show, and a
|
|
15
|
+
# destructive-variant delete. The copies are the app's to edit; re-runs
|
|
16
|
+
# never overwrite (skip-if-exists, the poetry:add contract).
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# bin/rails g poetry:scaffold_templates
|
|
20
|
+
class ScaffoldTemplatesGenerator < Rails::Generators::Base
|
|
21
|
+
source_root File.expand_path("templates", __dir__)
|
|
22
|
+
|
|
23
|
+
# The scaffold view templates the override set ships.
|
|
24
|
+
VIEW_TEMPLATES = %w[index show new edit _form partial].freeze
|
|
25
|
+
|
|
26
|
+
# The --skip-controller option description (multi-line).
|
|
27
|
+
SKIP_CONTROLLER_DESC = "Skip the scaffold_controller override (index keeps the stock " \
|
|
28
|
+
"all-records query; the generated index view expects @state/@pages " \
|
|
29
|
+
"from the poetry controller template)"
|
|
30
|
+
|
|
31
|
+
class_option :skip_controller, type: :boolean, default: false, desc: SKIP_CONTROLLER_DESC
|
|
32
|
+
|
|
33
|
+
# Step: copies the scaffold view template overrides (skip-if-exists).
|
|
34
|
+
# @api private
|
|
35
|
+
def copy_view_templates
|
|
36
|
+
VIEW_TEMPLATES.each do |name|
|
|
37
|
+
copy_file "#{name}.html.erb.tt", "lib/templates/erb/scaffold/#{name}.html.erb.tt", skip: true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Step: copies the scaffold controller override unless skipped.
|
|
42
|
+
# @api private
|
|
43
|
+
def copy_controller_template
|
|
44
|
+
return if options[:skip_controller]
|
|
45
|
+
|
|
46
|
+
copy_file "controller.rb.tt", "lib/templates/rails/scaffold_controller/controller.rb.tt", skip: true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Step: prints what the override set changes.
|
|
50
|
+
# @api private
|
|
51
|
+
def show_next_steps
|
|
52
|
+
say ""
|
|
53
|
+
say "poetry scaffold templates installed.", :green
|
|
54
|
+
say " bin/rails generate scaffold Post title:string body:text"
|
|
55
|
+
say "now produces poetry-composed views#{" and a URL-state index controller" unless options[:skip_controller]}."
|
|
56
|
+
say "The templates under lib/templates/ are yours to edit."
|
|
57
|
+
say ""
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<%%= form_with(model: <%= model_resource_name %>, class: "flex max-w-xl flex-col gap-4") do |form| %>
|
|
2
|
+
<%% if <%= singular_table_name %>.errors.any? %>
|
|
3
|
+
<%%= poetry_alert(variant: :destructive) do |alert| %>
|
|
4
|
+
<%% alert.with_title { "#{pluralize(<%= singular_table_name %>.errors.count, "error")} prohibited this <%= human_name.downcase %> from being saved" } %>
|
|
5
|
+
<ul class="list-inside list-disc">
|
|
6
|
+
<%% <%= singular_table_name %>.errors.each do |error| %>
|
|
7
|
+
<li><%%= error.full_message %></li>
|
|
8
|
+
<%% end %>
|
|
9
|
+
</ul>
|
|
10
|
+
<%% end %>
|
|
11
|
+
<%% end %>
|
|
12
|
+
|
|
13
|
+
<% attributes.each do |attribute| -%>
|
|
14
|
+
<% required = attribute.attr_options[:null] == false -%>
|
|
15
|
+
<% if attribute.password_digest? -%>
|
|
16
|
+
<%%= poetry_field(id: form.field_id(:password), label_text: "Password",
|
|
17
|
+
error: <%= singular_table_name %>.errors[:password].first) do |field| %>
|
|
18
|
+
<%%= poetry_input(type: "password", name: form.field_name(:password),
|
|
19
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
20
|
+
<%% end %>
|
|
21
|
+
|
|
22
|
+
<%%= poetry_field(id: form.field_id(:password_confirmation), label_text: "Password confirmation") do |field| %>
|
|
23
|
+
<%%= poetry_input(type: "password", name: form.field_name(:password_confirmation),
|
|
24
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
25
|
+
<%% end %>
|
|
26
|
+
<% elsif attribute.type.to_s == "boolean" -%>
|
|
27
|
+
<%%= poetry_checkbox(label: "<%= attribute.human_name %>", name: form.field_name(:<%= attribute.column_name %>),
|
|
28
|
+
checked: <%= model_resource_name %>.<%= attribute.column_name %>?, value: "1", unchecked_value: "0") %>
|
|
29
|
+
<% elsif %w[integer decimal float].include?(attribute.type.to_s) -%>
|
|
30
|
+
<%%= poetry_number_field(label: "<%= attribute.human_name %>", name: form.field_name(:<%= attribute.column_name %>),
|
|
31
|
+
value: <%= model_resource_name %>.<%= attribute.column_name %><%= attribute.type.to_s == "integer" ? ", step: 1" : "" %><%= ", required: true" if required %>) %>
|
|
32
|
+
<% elsif attribute.type.to_s == "date" -%>
|
|
33
|
+
<%%= poetry_date_field(label: "<%= attribute.human_name %>", name: form.field_name(:<%= attribute.column_name %>),
|
|
34
|
+
value: <%= model_resource_name %>.<%= attribute.column_name %><%= ", required: true" if required %>) %>
|
|
35
|
+
<% elsif %w[text rich_text].include?(attribute.type.to_s) -%>
|
|
36
|
+
<%%= poetry_field(id: form.field_id(:<%= attribute.column_name %>), label_text: "<%= attribute.human_name %>",<%= " required: true," if required %>
|
|
37
|
+
error: <%= singular_table_name %>.errors[:<%= attribute.column_name %>].first) do |field| %>
|
|
38
|
+
<%%= poetry_textarea(name: form.field_name(:<%= attribute.column_name %>),
|
|
39
|
+
value: <%= model_resource_name %>.<%= attribute.column_name %>,
|
|
40
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
41
|
+
<%% end %>
|
|
42
|
+
<% elsif attribute.attachment? || attribute.attachments? -%>
|
|
43
|
+
<%%= poetry_field(id: form.field_id(:<%= attribute.column_name %>), label_text: "<%= attribute.human_name %>") do |field| %>
|
|
44
|
+
<%%= poetry_input(type: "file", name: form.field_name(:<%= attribute.column_name %><%= attribute.attachments? ? ", multiple: true" : "" %>),
|
|
45
|
+
<%= attribute.attachments? ? "multiple: true, " : "" %>**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
46
|
+
<%% end %>
|
|
47
|
+
<% else -%>
|
|
48
|
+
<% value_expr = case attribute.type.to_s
|
|
49
|
+
when "datetime" then "#{model_resource_name}.#{attribute.column_name}&.strftime(\"%Y-%m-%dT%H:%M\")"
|
|
50
|
+
when "time" then "#{model_resource_name}.#{attribute.column_name}&.strftime(\"%H:%M\")"
|
|
51
|
+
else "#{model_resource_name}.#{attribute.column_name}"
|
|
52
|
+
end
|
|
53
|
+
input_type = if attribute.type.to_s != "string"
|
|
54
|
+
{ "datetime" => "datetime-local", "time" => "time" }.fetch(attribute.type.to_s, "text")
|
|
55
|
+
elsif %w[email email_address].include?(attribute.column_name)
|
|
56
|
+
"email"
|
|
57
|
+
elsif %w[phone phone_number].include?(attribute.column_name)
|
|
58
|
+
"tel"
|
|
59
|
+
elsif %w[url website permalink].include?(attribute.column_name)
|
|
60
|
+
"url"
|
|
61
|
+
else
|
|
62
|
+
"text"
|
|
63
|
+
end -%>
|
|
64
|
+
<%%= poetry_field(id: form.field_id(:<%= attribute.column_name %>), label_text: "<%= attribute.human_name %>",<%= " required: true," if required %>
|
|
65
|
+
error: <%= singular_table_name %>.errors[:<%= attribute.column_name %>].first) do |field| %>
|
|
66
|
+
<%%= poetry_input(type: "<%= input_type %>", name: form.field_name(:<%= attribute.column_name %>),
|
|
67
|
+
value: <%= value_expr %>,
|
|
68
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
69
|
+
<%% end %>
|
|
70
|
+
<% end -%>
|
|
71
|
+
<% end -%>
|
|
72
|
+
|
|
73
|
+
<div class="flex items-center gap-3">
|
|
74
|
+
<%%= poetry_button(type: :submit) { <%= model_resource_name %>.persisted? ? "Update <%= human_name.downcase %>" : "Create <%= human_name.downcase %>" } %>
|
|
75
|
+
<%%= poetry_button(tag: :a, variant: :ghost, href: <%= index_helper(type: :path) %>) { "Cancel" } %>
|
|
76
|
+
</div>
|
|
77
|
+
<%% end %>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<% module_namespacing do -%>
|
|
2
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
|
3
|
+
PER_PAGE = 25
|
|
4
|
+
|
|
5
|
+
before_action :set_<%= singular_table_name %>, only: %i[ show edit update destroy ]
|
|
6
|
+
|
|
7
|
+
# GET <%= route_url %> - sort/filter/page arrive as sanitized URL state
|
|
8
|
+
# (the poetry DataTable contract): State whitelists the sort column, so
|
|
9
|
+
# order_clause is injection-safe by construction.
|
|
10
|
+
def index
|
|
11
|
+
@state = Poetry::Ui::DataTable::State.from_params(
|
|
12
|
+
params,
|
|
13
|
+
sortable: %w[<%= attributes.reject { |a| a.attachment? || a.attachments? || a.rich_text? || a.password_digest? }.map(&:column_name).join(" ") %>]
|
|
14
|
+
)
|
|
15
|
+
scope = <%= class_name %>.order(@state.order_clause)
|
|
16
|
+
<% filter_column = attributes.find { |a| !a.password_digest? && a.type.to_s == "string" } -%>
|
|
17
|
+
<% if filter_column -%>
|
|
18
|
+
scope = scope.where("<%= filter_column.column_name %> LIKE ?", "%#{@state.q}%") if @state.q.present?
|
|
19
|
+
<% end -%>
|
|
20
|
+
@pages = [(scope.count.to_f / PER_PAGE).ceil, 1].max
|
|
21
|
+
@<%= plural_table_name %> = scope.offset((@state.page - 1) * PER_PAGE).limit(PER_PAGE)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# GET <%= route_url %>/1
|
|
25
|
+
def show
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# GET <%= route_url %>/new
|
|
29
|
+
def new
|
|
30
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# GET <%= route_url %>/1/edit
|
|
34
|
+
def edit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# POST <%= route_url %>
|
|
38
|
+
def create
|
|
39
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
|
|
40
|
+
|
|
41
|
+
if @<%= orm_instance.save %>
|
|
42
|
+
redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %>
|
|
43
|
+
else
|
|
44
|
+
render :new, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# PATCH/PUT <%= route_url %>/1
|
|
49
|
+
def update
|
|
50
|
+
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
|
51
|
+
redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %>, status: :see_other
|
|
52
|
+
else
|
|
53
|
+
render :edit, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# DELETE <%= route_url %>/1
|
|
58
|
+
def destroy
|
|
59
|
+
@<%= orm_instance.destroy %>
|
|
60
|
+
redirect_to <%= index_helper %>_path, notice: <%= %("#{human_name} was successfully destroyed.") %>, status: :see_other
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
# Use callbacks to share common setup or constraints between actions.
|
|
65
|
+
def set_<%= singular_table_name %>
|
|
66
|
+
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params.expect(:id)") %>
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Only allow a list of trusted parameters through.
|
|
70
|
+
def <%= "#{singular_table_name}_params" %>
|
|
71
|
+
<%- if attributes_names.empty? -%>
|
|
72
|
+
params.fetch(:<%= singular_table_name %>, {})
|
|
73
|
+
<%- else -%>
|
|
74
|
+
params.expect(<%= singular_table_name %>: [ <%= permitted_params %> ])
|
|
75
|
+
<%- end -%>
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
<% end -%>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%% content_for :title, "Editing <%= human_name.downcase %>" %>
|
|
2
|
+
|
|
3
|
+
<div class="flex max-w-xl flex-col gap-6">
|
|
4
|
+
<header class="flex flex-col gap-1">
|
|
5
|
+
<h1 class="text-2xl font-semibold tracking-tight">Editing <%= human_name.downcase %></h1>
|
|
6
|
+
</header>
|
|
7
|
+
|
|
8
|
+
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
|
|
9
|
+
|
|
10
|
+
<div class="flex items-center gap-2">
|
|
11
|
+
<%%= poetry_button(tag: :a, variant: :outline, href: <%= show_helper(type: :path) %>) { "Show this <%= human_name.downcase %>" } %>
|
|
12
|
+
<%%= poetry_button(tag: :a, variant: :ghost, href: <%= index_helper(type: :path) %>) { "Back to <%= human_name.pluralize.downcase %>" } %>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% data_columns = attributes.reject { |a| a.attachment? || a.attachments? || a.rich_text? || a.password_digest? } -%>
|
|
2
|
+
<% has_filter = data_columns.any? { |a| a.type.to_s == "string" } -%>
|
|
3
|
+
<%% content_for :title, "<%= human_name.pluralize %>" %>
|
|
4
|
+
|
|
5
|
+
<div class="flex flex-col gap-6">
|
|
6
|
+
<header class="flex flex-wrap items-end justify-between gap-3">
|
|
7
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= human_name.pluralize %></h1>
|
|
8
|
+
<%%= poetry_button(tag: :a, href: <%= new_helper(type: :path) %>) { "New <%= human_name.downcase %>" } %>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<%%= poetry_data_table(rows: @<%= plural_table_name %>, state: @state, total: @pages,
|
|
12
|
+
path: ->(p) { <%= index_helper(type: :path) %>(p) },
|
|
13
|
+
caption: "<%= human_name.pluralize %>",
|
|
14
|
+
filter: <%= has_filter %>,
|
|
15
|
+
empty_text: "No <%= human_name.pluralize.downcase %> yet.") do |t| %>
|
|
16
|
+
<% data_columns.each do |attribute| -%>
|
|
17
|
+
<%% t.with_column("<%= attribute.human_name %>", key: :<%= attribute.column_name %>, sortable: true) { |<%= singular_table_name %>| <%= singular_table_name %>.<%= attribute.column_name %> } %>
|
|
18
|
+
<% end -%>
|
|
19
|
+
<% attributes.select { |a| a.attachment? || a.attachments? || a.rich_text? }.each do |attribute| -%>
|
|
20
|
+
<%% t.with_column("<%= attribute.human_name %>") { |<%= singular_table_name %>| <%= singular_table_name %>.<%= attribute.column_name %> } %>
|
|
21
|
+
<% end -%>
|
|
22
|
+
<%% t.with_column("", classes: "text-right") { |<%= singular_table_name %>| poetry_button(tag: :a, variant: :ghost, size: :sm, href: <%= singular_table_name %>_path(<%= singular_table_name %>)) { "Show" } } %>
|
|
23
|
+
<%% end %>
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%% content_for :title, "New <%= human_name.downcase %>" %>
|
|
2
|
+
|
|
3
|
+
<div class="flex max-w-xl flex-col gap-6">
|
|
4
|
+
<header class="flex flex-col gap-1">
|
|
5
|
+
<h1 class="text-2xl font-semibold tracking-tight">New <%= human_name.downcase %></h1>
|
|
6
|
+
</header>
|
|
7
|
+
|
|
8
|
+
<%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
|
|
9
|
+
|
|
10
|
+
<%%= poetry_button(tag: :a, variant: :ghost, href: <%= index_helper(type: :path) %>) { "Back to <%= human_name.pluralize.downcase %>" } %>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div id="<%%= dom_id <%= singular_name %> %>">
|
|
2
|
+
<%%= poetry_metadata_list do |list| %>
|
|
3
|
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
|
|
4
|
+
<% if attribute.attachment? -%>
|
|
5
|
+
<%% list.with_item(label: "<%= attribute.human_name %>") { (link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %>) if <%= singular_name %>.<%= attribute.column_name %>.attached? } %>
|
|
6
|
+
<% elsif attribute.attachments? -%>
|
|
7
|
+
<%% list.with_item(label: "<%= attribute.human_name %>") { safe_join(<%= singular_name %>.<%= attribute.column_name %>.map { |file| link_to(file.filename, file) }, ", ") } %>
|
|
8
|
+
<% else -%>
|
|
9
|
+
<%% list.with_item(label: "<%= attribute.human_name %>") { <%= singular_name %>.<%= attribute.column_name %> } %>
|
|
10
|
+
<% end -%>
|
|
11
|
+
<% end -%>
|
|
12
|
+
<%% end %>
|
|
13
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%% content_for :title, "<%= human_name %>" %>
|
|
2
|
+
|
|
3
|
+
<div class="flex max-w-2xl flex-col gap-6">
|
|
4
|
+
<header class="flex flex-wrap items-end justify-between gap-3">
|
|
5
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= human_name %></h1>
|
|
6
|
+
<div class="flex items-center gap-2">
|
|
7
|
+
<%%= poetry_button(tag: :a, variant: :outline, href: <%= edit_helper(type: :path) %>) { "Edit" } %>
|
|
8
|
+
<%%= poetry_button(tag: :a, variant: :ghost, href: <%= index_helper(type: :path) %>) { "Back to <%= human_name.pluralize.downcase %>" } %>
|
|
9
|
+
</div>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<%%= render @<%= singular_table_name %> %>
|
|
13
|
+
|
|
14
|
+
<%%= form_with(model: @<%= singular_table_name %>, method: :delete,
|
|
15
|
+
data: { turbo_confirm: "Are you sure?" }) do %>
|
|
16
|
+
<%%= poetry_button(type: :submit, variant: :destructive) { "Destroy this <%= human_name.downcase %>" } %>
|
|
17
|
+
<%% end %>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require_relative "../skills_section"
|
|
5
|
+
|
|
6
|
+
module Poetry
|
|
7
|
+
# `rails g poetry:skill` - installs or refreshes poetry's Claude Code
|
|
8
|
+
# skills in the app's .claude/skills/:
|
|
9
|
+
#
|
|
10
|
+
# poetry/ the component-usage skill, GENERATED from the live
|
|
11
|
+
# registry (SKILL.md menu + per-family references) -
|
|
12
|
+
# re-run this generator after updating poetry gems
|
|
13
|
+
# poetry-design/ the taste layer (theme / compose / audit / study),
|
|
14
|
+
# curated prose riding the DESIGN.md + design-lint rails
|
|
15
|
+
# poetry-component/ the authoring layer (anatomy / documentation /
|
|
16
|
+
# audit) for components the app builds itself
|
|
17
|
+
#
|
|
18
|
+
# poetry:install runs the same step; this standalone exists as the
|
|
19
|
+
# refresh path (and for hosts that installed before the skills shipped).
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# bin/rails g poetry:skill
|
|
23
|
+
class SkillGenerator < Rails::Generators::Base
|
|
24
|
+
include Generators::SkillsSection
|
|
25
|
+
|
|
26
|
+
desc "Install the poetry Claude Code skills (usage + design + authoring) into .claude/skills/"
|
|
27
|
+
|
|
28
|
+
# Step: applies the three skill directories.
|
|
29
|
+
# @api private
|
|
30
|
+
def install_skills
|
|
31
|
+
apply_poetry_skills
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: poetry-component
|
|
3
|
+
description: >-
|
|
4
|
+
Author and audit poetry components: the canonical class anatomy
|
|
5
|
+
(section order every component follows), the documentation standard
|
|
6
|
+
(YARD + projected strings), and the audit checklist. Load this
|
|
7
|
+
WHENEVER creating a new component that inherits from
|
|
8
|
+
Poetry::Core::Component, extending an existing component family, or
|
|
9
|
+
reviewing a component file for structure and documentation - the
|
|
10
|
+
anatomy is the contract that keeps every component readable the same
|
|
11
|
+
way.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# poetry-component - the authoring layer
|
|
15
|
+
|
|
16
|
+
poetry's catalog guarantees valid, accessible components; this skill
|
|
17
|
+
covers building one of your own. A component that follows the canonical
|
|
18
|
+
anatomy is legible to every reader - human, agent, and the machine
|
|
19
|
+
surfaces (registry, contract checks, docs) that project from its
|
|
20
|
+
declarations. Load this before writing the class, not after it fails
|
|
21
|
+
review.
|
|
22
|
+
|
|
23
|
+
## Verbs
|
|
24
|
+
|
|
25
|
+
- **create** (`references/anatomy.md`) - author a new component: the
|
|
26
|
+
section order, what each DSL declares, and a complete skeleton to
|
|
27
|
+
start from.
|
|
28
|
+
- **document** (`references/documentation.md`) - the documentation
|
|
29
|
+
standard: the class docblock, YARD on public methods, and how to write
|
|
30
|
+
the projected strings (part descriptions, agent rules) that machines
|
|
31
|
+
consume verbatim.
|
|
32
|
+
- **audit** (`references/checklist.md`) - review an existing component
|
|
33
|
+
against the anatomy and the documentation standard; the checklist is
|
|
34
|
+
ordered so violations surface in file order.
|
|
35
|
+
|
|
36
|
+
## The one rule that spans all three
|
|
37
|
+
|
|
38
|
+
Declaration order inside a section is meaningful: options, parts, and
|
|
39
|
+
slots project into the registry and the agent surface in declaration
|
|
40
|
+
order. Move whole sections to match the anatomy; never reorder
|
|
41
|
+
declarations within a section unless you intend to change the projected
|
|
42
|
+
surface.
|
|
43
|
+
|
|
44
|
+
## Boundaries
|
|
45
|
+
|
|
46
|
+
- A new component inherits from `Poetry::Core::Component` (or a
|
|
47
|
+
published family's base) and lives in its own `app/components`
|
|
48
|
+
namespace. Never copy a poetry component's source into the app to
|
|
49
|
+
modify it - subclass it, or compose it in a template.
|
|
50
|
+
- Inner classes that exist only to serve a family (item builders,
|
|
51
|
+
internal wrappers) declare `internal_component!` so the registry and
|
|
52
|
+
every surface derived from it skip them.
|
|
53
|
+
- Behavior belongs to Stimulus controllers declared through
|
|
54
|
+
`use_stimulus`; a component never ships inline `<script>`.
|
|
55
|
+
- Agent tools: declare what an in-page agent may do to a rendered instance
|
|
56
|
+
with `tool :name, description:, params:, executes:, mutating:` beside
|
|
57
|
+
`use_stimulus` (`executes:` names a declared Stimulus action - validated at
|
|
58
|
+
class load); a call opts in with `webmcp: "name"`. See `references/anatomy.md`.
|