ruby_cms 0.2.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (647) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -72
  3. data/README.md +308 -153
  4. data/Rakefile +0 -18
  5. data/SYNC_RULES.md +84 -0
  6. data/exe/ruby_cms +13 -18
  7. data/lib/generators/ruby_cms/admin_page_generator.rb +116 -60
  8. data/lib/generators/ruby_cms/templates/admin.html.erb +35 -17
  9. data/lib/generators/ruby_cms/templates/admin_page/_admin_table_content.html.erb.tt +25 -0
  10. data/lib/generators/ruby_cms/templates/admin_page/_form.html.erb.tt +36 -0
  11. data/lib/generators/ruby_cms/templates/admin_page/_row.html.erb.tt +20 -0
  12. data/lib/generators/ruby_cms/templates/admin_page/controller.rb.tt +74 -2
  13. data/lib/generators/ruby_cms/templates/admin_page/edit.html.erb.tt +14 -0
  14. data/lib/generators/ruby_cms/templates/admin_page/index.html.erb.tt +21 -10
  15. data/lib/generators/ruby_cms/templates/admin_page/new.html.erb.tt +14 -0
  16. data/lib/generators/ruby_cms/templates/assets/tailwind/application.css +225 -0
  17. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion.rb +17 -0
  18. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_content.rb +21 -0
  19. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_default_content.rb +17 -0
  20. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_default_trigger.rb +19 -0
  21. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_icon.rb +38 -0
  22. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_item.rb +28 -0
  23. data/lib/generators/ruby_cms/templates/components/ruby_ui/accordion/accordion_trigger.rb +17 -0
  24. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_page.rb +31 -0
  25. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_page_header.rb +65 -0
  26. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_resource_card.rb +28 -0
  27. data/lib/generators/ruby_cms/templates/components/ruby_ui/admin_page/admin_table_content.rb +20 -0
  28. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert.rb +36 -0
  29. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert_description.rb +17 -0
  30. data/lib/generators/ruby_cms/templates/components/ruby_ui/alert/alert_title.rb +17 -0
  31. data/lib/generators/ruby_cms/templates/components/ruby_ui/aspect_ratio/aspect_ratio.rb +33 -0
  32. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar.rb +31 -0
  33. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar_fallback.rb +17 -0
  34. data/lib/generators/ruby_cms/templates/components/ruby_ui/avatar/avatar_image.rb +26 -0
  35. data/lib/generators/ruby_cms/templates/components/ruby_ui/badge/badge.rb +60 -0
  36. data/lib/generators/ruby_cms/templates/components/ruby_ui/base.rb +29 -0
  37. data/lib/generators/ruby_cms/templates/components/ruby_ui/box/box.rb +15 -0
  38. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb.rb +17 -0
  39. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_ellipsis.rb +39 -0
  40. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_item.rb +17 -0
  41. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_link.rb +22 -0
  42. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_list.rb +17 -0
  43. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_page.rb +19 -0
  44. data/lib/generators/ruby_cms/templates/components/ruby_ui/breadcrumb/breadcrumb_separator.rb +38 -0
  45. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_button.rb +82 -0
  46. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table.rb +133 -0
  47. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_actions.rb +104 -0
  48. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_body.rb +17 -0
  49. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_checkbox_cell.rb +40 -0
  50. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_checkbox_head.rb +34 -0
  51. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_delete_modal.rb +43 -0
  52. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_header.rb +42 -0
  53. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_pagination.rb +115 -0
  54. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_action_table_row.rb +77 -0
  55. data/lib/generators/ruby_cms/templates/components/ruby_ui/bulk_action_table/bulk_actions.rb +86 -0
  56. data/lib/generators/ruby_cms/templates/components/ruby_ui/button/button.rb +113 -0
  57. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar.rb +39 -0
  58. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_body.rb +19 -0
  59. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_days.rb +104 -0
  60. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_header.rb +17 -0
  61. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_next.rb +43 -0
  62. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_prev.rb +43 -0
  63. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_title.rb +27 -0
  64. data/lib/generators/ruby_cms/templates/components/ruby_ui/calendar/calendar_weekdays.rb +33 -0
  65. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card.rb +17 -0
  66. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_content.rb +17 -0
  67. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_description.rb +17 -0
  68. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_footer.rb +17 -0
  69. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_header.rb +17 -0
  70. data/lib/generators/ruby_cms/templates/components/ruby_ui/card/card_title.rb +17 -0
  71. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel.rb +44 -0
  72. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_content.rb +23 -0
  73. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_item.rb +23 -0
  74. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_next.rb +48 -0
  75. data/lib/generators/ruby_cms/templates/components/ruby_ui/carousel/carousel_previous.rb +49 -0
  76. data/lib/generators/ruby_cms/templates/components/ruby_ui/checkbox/checkbox.rb +29 -0
  77. data/lib/generators/ruby_cms/templates/components/ruby_ui/checkbox/checkbox_group.rb +20 -0
  78. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard.rb +42 -0
  79. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_popover.rb +40 -0
  80. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_source.rb +19 -0
  81. data/lib/generators/ruby_cms/templates/components/ruby_ui/clipboard/clipboard_trigger.rb +20 -0
  82. data/lib/generators/ruby_cms/templates/components/ruby_ui/codeblock/codeblock.rb +102 -0
  83. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible.rb +25 -0
  84. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible_content.rb +18 -0
  85. data/lib/generators/ruby_cms/templates/components/ruby_ui/collapsible/collapsible_trigger.rb +19 -0
  86. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox.rb +26 -0
  87. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_checkbox.rb +25 -0
  88. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_empty_state.rb +21 -0
  89. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_item.rb +25 -0
  90. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_list.rb +18 -0
  91. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_list_group.rb +20 -0
  92. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_popover.rb +30 -0
  93. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_radio.rb +26 -0
  94. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_search_input.rb +53 -0
  95. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +25 -0
  96. data/lib/generators/ruby_cms/templates/components/ruby_ui/combobox/combobox_trigger.rb +57 -0
  97. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command.rb +9 -0
  98. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog.rb +17 -0
  99. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog_content.rb +48 -0
  100. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_dialog_trigger.rb +29 -0
  101. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_empty.rb +19 -0
  102. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_group.rb +40 -0
  103. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_input.rb +56 -0
  104. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_item.rb +32 -0
  105. data/lib/generators/ruby_cms/templates/components/ruby_ui/command/command_list.rb +17 -0
  106. data/lib/generators/ruby_cms/templates/components/ruby_ui/container/container.rb +33 -0
  107. data/lib/generators/ruby_cms/templates/components/ruby_ui/container/container_section.rb +31 -0
  108. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu.rb +26 -0
  109. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_content.rb +25 -0
  110. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_item.rb +66 -0
  111. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_label.rb +24 -0
  112. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_separator.rb +19 -0
  113. data/lib/generators/ruby_cms/templates/components/ruby_ui/context_menu/context_menu_trigger.rb +20 -0
  114. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table.rb +29 -0
  115. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_bulk_actions.rb +18 -0
  116. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_column_toggle.rb +62 -0
  117. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_expand_toggle.rb +53 -0
  118. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_form.rb +39 -0
  119. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_kaminari_adapter.rb +17 -0
  120. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_manual_adapter.rb +17 -0
  121. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagination.rb +100 -0
  122. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagination_bar.rb +15 -0
  123. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_pagy_adapter.rb +17 -0
  124. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_per_page_select.rb +35 -0
  125. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_row_checkbox.rb +30 -0
  126. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_search.rb +57 -0
  127. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_select_all_checkbox.rb +21 -0
  128. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_selection_summary.rb +25 -0
  129. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_sort_head.rb +112 -0
  130. data/lib/generators/ruby_cms/templates/components/ruby_ui/data_table/data_table_toolbar.rb +15 -0
  131. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog.rb +25 -0
  132. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_content.rb +78 -0
  133. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_description.rb +17 -0
  134. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_footer.rb +17 -0
  135. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_header.rb +17 -0
  136. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_middle.rb +17 -0
  137. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_title.rb +17 -0
  138. data/lib/generators/ruby_cms/templates/components/ruby_ui/dialog/dialog_trigger.rb +20 -0
  139. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu.rb +35 -0
  140. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_content.rb +44 -0
  141. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_item.rb +28 -0
  142. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_label.rb +17 -0
  143. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_separator.rb +19 -0
  144. data/lib/generators/ruby_cms/templates/components/ruby_ui/dropdown_menu/dropdown_menu_trigger.rb +18 -0
  145. data/lib/generators/ruby_cms/templates/components/ruby_ui/empty_state/empty_state.rb +190 -0
  146. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form.rb +15 -0
  147. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field.rb +20 -0
  148. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_error.rb +20 -0
  149. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_hint.rb +15 -0
  150. data/lib/generators/ruby_cms/templates/components/ruby_ui/form/form_field_label.rb +15 -0
  151. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card.rb +27 -0
  152. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card_content.rb +22 -0
  153. data/lib/generators/ruby_cms/templates/components/ruby_ui/hover_card/hover_card_trigger.rb +20 -0
  154. data/lib/generators/ruby_cms/templates/components/ruby_ui/input/input.rb +34 -0
  155. data/lib/generators/ruby_cms/templates/components/ruby_ui/link/link.rb +106 -0
  156. data/lib/generators/ruby_cms/templates/components/ruby_ui/masked_input/masked_input.rb +15 -0
  157. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal.rb +26 -0
  158. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_action.rb +17 -0
  159. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_cancel.rb +20 -0
  160. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_content.rb +68 -0
  161. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_description.rb +17 -0
  162. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_footer.rb +17 -0
  163. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_header.rb +17 -0
  164. data/lib/generators/ruby_cms/templates/components/ruby_ui/modal/modal_title.rb +17 -0
  165. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select.rb +39 -0
  166. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_group.rb +15 -0
  167. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_icon.rb +39 -0
  168. data/lib/generators/ruby_cms/templates/components/ruby_ui/native_select/native_select_option.rb +15 -0
  169. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination.rb +19 -0
  170. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_content.rb +17 -0
  171. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_ellipsis.rb +42 -0
  172. data/lib/generators/ruby_cms/templates/components/ruby_ui/pagination/pagination_item.rb +28 -0
  173. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover.rb +26 -0
  174. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover_content.rb +27 -0
  175. data/lib/generators/ruby_cms/templates/components/ruby_ui/popover/popover_trigger.rb +20 -0
  176. data/lib/generators/ruby_cms/templates/components/ruby_ui/progress/progress.rb +37 -0
  177. data/lib/generators/ruby_cms/templates/components/ruby_ui/radio_button/radio_button.rb +25 -0
  178. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select.rb +23 -0
  179. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_content.rb +32 -0
  180. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_group.rb +15 -0
  181. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_input.rb +22 -0
  182. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_item.rb +52 -0
  183. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_label.rb +17 -0
  184. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_trigger.rb +54 -0
  185. data/lib/generators/ruby_cms/templates/components/ruby_ui/select/select_value.rb +27 -0
  186. data/lib/generators/ruby_cms/templates/components/ruby_ui/separator/separator.rb +38 -0
  187. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet.rb +17 -0
  188. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_content.rb +77 -0
  189. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_description.rb +17 -0
  190. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_footer.rb +17 -0
  191. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_header.rb +17 -0
  192. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_middle.rb +17 -0
  193. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_title.rb +17 -0
  194. data/lib/generators/ruby_cms/templates/components/ruby_ui/sheet/sheet_trigger.rb +17 -0
  195. data/lib/generators/ruby_cms/templates/components/ruby_ui/shortcut_key/shortcut_key.rb +17 -0
  196. data/lib/generators/ruby_cms/templates/components/ruby_ui/skeleton/skeleton.rb +17 -0
  197. data/lib/generators/ruby_cms/templates/components/ruby_ui/switch/switch.rb +24 -0
  198. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table.rb +19 -0
  199. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_body.rb +17 -0
  200. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_caption.rb +17 -0
  201. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_cell.rb +17 -0
  202. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_footer.rb +17 -0
  203. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_head.rb +17 -0
  204. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_header.rb +17 -0
  205. data/lib/generators/ruby_cms/templates/components/ruby_ui/table/table_row.rb +17 -0
  206. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs.rb +25 -0
  207. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_content.rb +26 -0
  208. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_list.rb +17 -0
  209. data/lib/generators/ruby_cms/templates/components/ruby_ui/tabs/tabs_trigger.rb +28 -0
  210. data/lib/generators/ruby_cms/templates/components/ruby_ui/textarea/textarea.rb +26 -0
  211. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/set_dark_mode.rb +16 -0
  212. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/set_light_mode.rb +16 -0
  213. data/lib/generators/ruby_cms/templates/components/ruby_ui/theme_toggle/theme_toggle.rb +9 -0
  214. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip.rb +26 -0
  215. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip_content.rb +26 -0
  216. data/lib/generators/ruby_cms/templates/components/ruby_ui/tooltip/tooltip_trigger.rb +19 -0
  217. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/heading.rb +60 -0
  218. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/inline_code.rb +17 -0
  219. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/inline_link.rb +22 -0
  220. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/text.rb +53 -0
  221. data/lib/generators/ruby_cms/templates/components/ruby_ui/typography/typography_blockquote.rb +17 -0
  222. data/lib/generators/ruby_cms/templates/{ruby_cms.rb → config/initializers/admin.rb} +15 -9
  223. data/lib/generators/ruby_cms/templates/config/initializers/admin_dashboard.rb +43 -0
  224. data/lib/generators/ruby_cms/templates/config/initializers/pagy.rb +7 -0
  225. data/lib/generators/ruby_cms/templates/config/initializers/ruby_cms_core.rb +25 -0
  226. data/lib/generators/ruby_cms/templates/config/initializers/ruby_cms_custom_settings.rb +35 -0
  227. data/lib/generators/ruby_cms/templates/config/initializers/webauthn.rb +28 -0
  228. data/lib/generators/ruby_cms/templates/config/locales/en.yml +277 -0
  229. data/lib/generators/ruby_cms/templates/config/locales/nl.yml +329 -0
  230. data/lib/generators/ruby_cms/templates/controllers/admin/analytics_controller.rb +211 -0
  231. data/lib/generators/ruby_cms/templates/controllers/admin/application_controller.rb +186 -0
  232. data/lib/generators/ruby_cms/templates/controllers/admin/audit_log_entries_controller.rb +69 -0
  233. data/lib/generators/ruby_cms/templates/controllers/admin/commands_controller.rb +146 -0
  234. data/lib/generators/ruby_cms/templates/controllers/admin/content_block_versions_controller.rb +96 -0
  235. data/lib/generators/ruby_cms/templates/controllers/admin/content_blocks_controller.rb +405 -0
  236. data/lib/generators/ruby_cms/templates/controllers/admin/dashboard_controller.rb +114 -0
  237. data/lib/generators/ruby_cms/templates/controllers/admin/invitations_controller.rb +64 -0
  238. data/lib/generators/ruby_cms/templates/controllers/admin/locale_controller.rb +19 -0
  239. data/lib/generators/ruby_cms/templates/controllers/admin/media_assets_controller.rb +186 -0
  240. data/lib/generators/ruby_cms/templates/controllers/admin/notifications_controller.rb +37 -0
  241. data/lib/generators/ruby_cms/templates/controllers/admin/passkey_credentials_controller.rb +17 -0
  242. data/lib/generators/ruby_cms/templates/controllers/admin/passkey_registrations_controller.rb +50 -0
  243. data/lib/generators/ruby_cms/templates/controllers/admin/permissions_controller.rb +132 -0
  244. data/lib/generators/ruby_cms/templates/controllers/admin/redirects_controller.rb +175 -0
  245. data/lib/generators/ruby_cms/templates/controllers/admin/security_controller.rb +146 -0
  246. data/lib/generators/ruby_cms/templates/controllers/admin/settings_controller.rb +298 -0
  247. data/lib/generators/ruby_cms/templates/controllers/admin/system_health_controller.rb +11 -0
  248. data/lib/generators/ruby_cms/templates/controllers/admin/trash_controller.rb +61 -0
  249. data/lib/generators/ruby_cms/templates/controllers/admin/user_permissions_controller.rb +91 -0
  250. data/lib/generators/ruby_cms/templates/controllers/admin/users_controller.rb +220 -0
  251. data/lib/generators/ruby_cms/templates/controllers/admin/visitor_errors_controller.rb +114 -0
  252. data/lib/generators/ruby_cms/templates/controllers/admin/visual_editor_controller.rb +384 -0
  253. data/lib/generators/ruby_cms/templates/controllers/concerns/.keep +0 -0
  254. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_bulk_actions.rb +59 -0
  255. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_pagination.rb +106 -0
  256. data/lib/generators/ruby_cms/templates/controllers/concerns/admin_turbo_table.rb +46 -0
  257. data/lib/generators/ruby_cms/templates/controllers/concerns/audit_loggable.rb +28 -0
  258. data/lib/generators/ruby_cms/templates/controllers/concerns/authentication.rb +63 -0
  259. data/lib/generators/ruby_cms/templates/controllers/concerns/page_tracking.rb +45 -0
  260. data/lib/generators/ruby_cms/templates/controllers/concerns/security_monitoring.rb +102 -0
  261. data/lib/generators/ruby_cms/templates/controllers/concerns/sudo_mode.rb +29 -0
  262. data/lib/generators/ruby_cms/templates/controllers/concerns/visitor_error_capture.rb +35 -0
  263. data/lib/generators/ruby_cms/templates/controllers/errors_controller.rb +36 -0
  264. data/lib/generators/ruby_cms/templates/controllers/passkey_sessions_controller.rb +69 -0
  265. data/lib/generators/ruby_cms/templates/db/migrate/20251121085414_create_email_blocklists.rb +12 -0
  266. data/lib/generators/ruby_cms/templates/db/migrate/20251121161828_create_ip_blocklists.rb +12 -0
  267. data/lib/generators/ruby_cms/templates/db/migrate/20251205120000_add_analytics_indices.rb +48 -0
  268. data/lib/generators/ruby_cms/templates/db/migrate/20251216064753_backfill_security_fields_in_ahoy_events.rb +49 -0
  269. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000001_create_ruby_cms_permissions.rb +2 -2
  270. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000002_create_ruby_cms_user_permissions.rb +2 -2
  271. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000003_create_ruby_cms_content_blocks.rb +3 -3
  272. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260125000010_add_indexes_to_ruby_cms_tables.rb +1 -1
  273. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260127000001_add_locale_to_ruby_cms_content_blocks.rb +8 -6
  274. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260129000001_create_ruby_cms_visitor_errors.rb +4 -4
  275. data/lib/generators/ruby_cms/templates/db/migrate/20260130000001_add_referer_and_query_to_ruby_cms_visitor_errors.rb +10 -0
  276. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260130000002_create_ruby_cms_preferences.rb +2 -2
  277. data/lib/generators/ruby_cms/templates/db/migrate/20260130000003_add_category_to_ruby_cms_preferences.rb +10 -0
  278. data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260328000001_create_content_block_versions.rb +2 -2
  279. data/lib/generators/ruby_cms/templates/db/migrate/20260525120000_create_audit_log_entries.rb +24 -0
  280. data/lib/generators/ruby_cms/templates/db/migrate/20260525130000_create_redirects.rb +28 -0
  281. data/lib/generators/ruby_cms/templates/db/migrate/20260525140000_create_media_assets.rb +25 -0
  282. data/lib/generators/ruby_cms/templates/db/migrate/20260525150000_create_invitations.rb +22 -0
  283. data/lib/generators/ruby_cms/templates/db/migrate/20260525160000_create_command_runs.rb +22 -0
  284. data/lib/generators/ruby_cms/templates/db/migrate/20260525190000_add_state_to_visitor_errors.rb +20 -0
  285. data/lib/generators/ruby_cms/templates/db/migrate/20260527165017_add_discarded_at_to_visitor_errors.rb +10 -0
  286. data/lib/generators/ruby_cms/templates/db/migrate/20260527165728_add_discarded_at_to_cms_tables.rb +12 -0
  287. data/lib/generators/ruby_cms/templates/db/migrate/20260605151935_create_passkey_credentials.rb +15 -0
  288. data/lib/generators/ruby_cms/templates/db/migrate/20260605151936_add_webauthn_id_to_users.rb +22 -0
  289. data/lib/generators/ruby_cms/templates/db/migrate/20260606000001_add_authenticated_at_to_sessions.rb +14 -0
  290. data/lib/generators/ruby_cms/templates/helpers/admin/admin_page_helper.rb +9 -0
  291. data/lib/generators/ruby_cms/templates/helpers/admin/dashboard_helper.rb +16 -0
  292. data/lib/generators/ruby_cms/templates/helpers/admin/media_assets_helper.rb +38 -0
  293. data/lib/generators/ruby_cms/templates/helpers/admin/relative_time_helper.rb +37 -0
  294. data/lib/generators/ruby_cms/templates/helpers/admin/ui_helper.rb +48 -0
  295. data/lib/generators/ruby_cms/templates/helpers/cms_application_helpers.rb +70 -0
  296. data/lib/generators/ruby_cms/templates/helpers/content_blocks_helper.rb +382 -0
  297. data/lib/generators/ruby_cms/templates/helpers/settings_helper.rb +192 -0
  298. data/lib/generators/ruby_cms/templates/javascript/admin.js +6 -0
  299. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/admin_commands_controller.js +2 -2
  300. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/autosearch_controller.js +18 -0
  301. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/clickable_row_controller.js +11 -0
  302. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/clipboard_controller.js +34 -0
  303. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/command_palette_controller.js +189 -0
  304. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/commands_controller.js +300 -0
  305. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/content_block_history_controller.js +167 -0
  306. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/flash_messages_controller.js +1 -1
  307. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/inline_rich_editor_controller.js +157 -0
  308. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/media_selection_controller.js +76 -0
  309. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/media_uploader_controller.js +23 -0
  310. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/mobile_menu_controller.js +125 -0
  311. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/nav_order_sortable_controller.js +296 -0
  312. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/nav_shortcuts_controller.js +47 -0
  313. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/page_preview_controller.js +64 -3
  314. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/permission_matrix_controller.js +49 -0
  315. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/search_shortcut_controller.js +24 -0
  316. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/settings_rail_search_controller.js +17 -0
  317. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/shortcuts_overlay_controller.js +39 -0
  318. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_collapse_controller.js +135 -0
  319. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_nav_persist_controller.js +48 -0
  320. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/sidebar_toggle_trigger_controller.js +10 -0
  321. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/topbar_controller.js +88 -0
  322. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/visual_editor_controller.js +721 -0
  323. data/lib/generators/ruby_cms/templates/javascript/controllers/admin/visual_editor_page_select_controller.js +174 -0
  324. data/lib/generators/ruby_cms/templates/javascript/controllers/application.js +10 -0
  325. data/lib/generators/ruby_cms/templates/javascript/controllers/index.js +2 -0
  326. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/accordion_controller.js +97 -0
  327. data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui}/bulk_action_table_controller.js +86 -233
  328. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/calendar_controller.js +249 -0
  329. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/calendar_input_controller.js +8 -0
  330. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/carousel_controller.js +60 -0
  331. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/checkbox_group_controller.js +21 -0
  332. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/clipboard_controller.js +54 -0
  333. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/collapsible_controller.js +47 -0
  334. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/combobox_controller.js +190 -0
  335. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/command_controller.js +138 -0
  336. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/context_menu_controller.js +144 -0
  337. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js +14 -0
  338. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_controller.js +109 -0
  339. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/data_table_search_controller.js +62 -0
  340. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/dialog_controller.js +32 -0
  341. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/dropdown_menu_controller.js +149 -0
  342. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/form_field_controller.js +61 -0
  343. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/hover_card_controller.js +153 -0
  344. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/masked_input_controller.js +9 -0
  345. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/modal_controller.js +45 -0
  346. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/popover_controller.js +107 -0
  347. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/select_controller.js +192 -0
  348. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/select_item_controller.js +11 -0
  349. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/sheet_content_controller.js +7 -0
  350. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/sheet_controller.js +9 -0
  351. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/tabs_controller.js +67 -0
  352. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/theme_toggle_controller.js +30 -0
  353. data/lib/generators/ruby_cms/templates/javascript/controllers/ruby_ui/tooltip_controller.js +38 -0
  354. data/lib/generators/ruby_cms/templates/javascript/controllers/webauthn_controller.js +153 -0
  355. data/lib/generators/ruby_cms/templates/lib/ruby_cms/cli.rb +170 -0
  356. data/lib/generators/ruby_cms/templates/lib/ruby_cms/commands_registry.rb +76 -0
  357. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/content_blocks_grouping.rb +2 -6
  358. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/content_blocks_sync.rb +6 -8
  359. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/dashboard_blocks.rb +5 -5
  360. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/icons.rb +41 -10
  361. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/settings.rb +60 -10
  362. data/lib/{ruby_cms → generators/ruby_cms/templates/lib/ruby_cms}/settings_registry.rb +79 -3
  363. data/lib/generators/ruby_cms/templates/lib/ruby_cms.rb +342 -0
  364. data/lib/{tasks → generators/ruby_cms/templates/lib/tasks}/admin.rake +16 -16
  365. data/lib/generators/ruby_cms/templates/lib/tasks/ruby_cms.rake +158 -0
  366. data/lib/generators/ruby_cms/templates/models/audit_log_entry.rb +56 -0
  367. data/lib/generators/ruby_cms/templates/models/command_run.rb +29 -0
  368. data/lib/generators/ruby_cms/templates/models/concerns/content_block/searchable.rb +18 -0
  369. data/{app → lib/generators/ruby_cms/templates}/models/concerns/content_block/versionable.rb +1 -1
  370. data/lib/generators/ruby_cms/templates/models/concerns/ransackable.rb +32 -0
  371. data/{app → lib/generators/ruby_cms/templates}/models/content_block.rb +13 -5
  372. data/lib/generators/ruby_cms/templates/models/email_blocklist.rb +21 -0
  373. data/lib/generators/ruby_cms/templates/models/invitation.rb +86 -0
  374. data/lib/generators/ruby_cms/templates/models/ip_blocklist.rb +34 -0
  375. data/lib/generators/ruby_cms/templates/models/media_asset.rb +90 -0
  376. data/lib/generators/ruby_cms/templates/models/passkey_credential.rb +6 -0
  377. data/lib/generators/ruby_cms/templates/models/permission.rb +71 -0
  378. data/lib/generators/ruby_cms/templates/models/permittable.rb +69 -0
  379. data/lib/generators/ruby_cms/templates/models/preference.rb +109 -0
  380. data/lib/generators/ruby_cms/templates/models/redirect.rb +100 -0
  381. data/lib/generators/ruby_cms/templates/models/user_permission.rb +25 -0
  382. data/lib/generators/ruby_cms/templates/models/visitor_error.rb +157 -0
  383. data/lib/generators/ruby_cms/templates/nav/analytics.rb +3 -0
  384. data/lib/generators/ruby_cms/templates/nav/audit_log.rb +3 -0
  385. data/lib/generators/ruby_cms/templates/nav/commands.rb +3 -0
  386. data/lib/generators/ruby_cms/templates/nav/content_blocks.rb +3 -0
  387. data/lib/generators/ruby_cms/templates/nav/core.rb +13 -0
  388. data/lib/generators/ruby_cms/templates/nav/media.rb +3 -0
  389. data/lib/generators/ruby_cms/templates/nav/permissions.rb +3 -0
  390. data/lib/generators/ruby_cms/templates/nav/redirects.rb +3 -0
  391. data/lib/generators/ruby_cms/templates/nav/security.rb +4 -0
  392. data/lib/generators/ruby_cms/templates/nav/system_health.rb +3 -0
  393. data/lib/generators/ruby_cms/templates/nav/trash.rb +3 -0
  394. data/lib/generators/ruby_cms/templates/nav/users.rb +3 -0
  395. data/lib/generators/ruby_cms/templates/nav/visual_editor.rb +3 -0
  396. data/lib/generators/ruby_cms/templates/routes/analytics.rb +5 -0
  397. data/lib/generators/ruby_cms/templates/routes/audit_log.rb +3 -0
  398. data/lib/generators/ruby_cms/templates/routes/auth.public.rb +4 -0
  399. data/lib/generators/ruby_cms/templates/routes/auth.rb +6 -0
  400. data/lib/generators/ruby_cms/templates/routes/commands.rb +4 -0
  401. data/lib/generators/ruby_cms/templates/routes/content_blocks.rb +12 -0
  402. data/lib/generators/ruby_cms/templates/routes/core.public.rb +5 -0
  403. data/lib/generators/ruby_cms/templates/routes/core.rb +16 -0
  404. data/lib/generators/ruby_cms/templates/routes/media.rb +9 -0
  405. data/lib/generators/ruby_cms/templates/routes/passkeys.public.rb +4 -0
  406. data/lib/generators/ruby_cms/templates/routes/passkeys.rb +5 -0
  407. data/lib/generators/ruby_cms/templates/routes/permissions.rb +5 -0
  408. data/lib/generators/ruby_cms/templates/routes/redirects.rb +10 -0
  409. data/lib/generators/ruby_cms/templates/routes/security.rb +20 -0
  410. data/lib/generators/ruby_cms/templates/routes/system_health.rb +3 -0
  411. data/lib/generators/ruby_cms/templates/routes/trash.rb +5 -0
  412. data/lib/generators/ruby_cms/templates/routes/users.rb +10 -0
  413. data/lib/generators/ruby_cms/templates/routes/visual_editor.rb +5 -0
  414. data/lib/generators/ruby_cms/templates/services/admin/health_check.rb +362 -0
  415. data/lib/generators/ruby_cms/templates/services/analytics/report.rb +503 -0
  416. data/lib/generators/ruby_cms/templates/services/analytics_service.rb +305 -0
  417. data/lib/generators/ruby_cms/templates/services/audit_log.rb +60 -0
  418. data/lib/generators/ruby_cms/templates/services/command_runner.rb +79 -0
  419. data/lib/generators/ruby_cms/templates/services/security_service.rb +203 -0
  420. data/lib/generators/ruby_cms/templates/services/security_tracker.rb +90 -0
  421. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/index.html.erb +61 -50
  422. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/page_details.html.erb +15 -10
  423. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_browser_device.html.erb +2 -2
  424. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_conversions.html.erb +3 -3
  425. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_daily_activity_chart.html.erb +18 -17
  426. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_exit_pages.html.erb +4 -4
  427. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_hourly_activity_chart.html.erb +18 -17
  428. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_landing_pages.html.erb +3 -3
  429. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_os_stats.html.erb +2 -2
  430. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_popular_pages.html.erb +4 -4
  431. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_recent_activity.html.erb +4 -4
  432. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_top_referrers.html.erb +3 -3
  433. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_top_visitors.html.erb +4 -4
  434. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_utm_sources.html.erb +2 -2
  435. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/visitor_details.html.erb +15 -10
  436. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/_admin_table_content.html.erb +55 -0
  437. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/_row.html.erb +84 -0
  438. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/index.html.erb +33 -0
  439. data/lib/generators/ruby_cms/templates/views/admin/audit_log_entries/show.html.erb +89 -0
  440. data/lib/generators/ruby_cms/templates/views/admin/commands/index.html.erb +393 -0
  441. data/lib/generators/ruby_cms/templates/views/admin/content_block_versions/index.html.erb +63 -0
  442. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/content_block_versions/show.html.erb +2 -2
  443. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_audits.html.erb +49 -0
  444. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_form.html.erb +79 -0
  445. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_rich_field.html.erb +44 -0
  446. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/_row.html.erb +60 -0
  447. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/index.html.erb +84 -0
  448. data/lib/generators/ruby_cms/templates/views/admin/content_blocks/new.html.erb +18 -0
  449. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/content_blocks/show.html.erb +67 -61
  450. data/lib/generators/ruby_cms/templates/views/admin/dashboard/_gmail_token_status.html.erb +69 -0
  451. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_analytics_overview.html.erb +11 -11
  452. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_content_blocks_stats.html.erb +3 -3
  453. data/lib/generators/ruby_cms/templates/views/admin/dashboard/blocks/_notifications.html.erb +50 -0
  454. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_permissions_stats.html.erb +3 -3
  455. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_quick_actions.html.erb +15 -15
  456. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_recent_errors.html.erb +8 -8
  457. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_users_stats.html.erb +3 -3
  458. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/dashboard/blocks/_visitor_errors_stats.html.erb +3 -3
  459. data/lib/generators/ruby_cms/templates/views/admin/dashboard/index.html.erb +34 -0
  460. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_admin_table_content.html.erb +107 -0
  461. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_form.html.erb +33 -0
  462. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_grid.html.erb +58 -0
  463. data/lib/generators/ruby_cms/templates/views/admin/media_assets/_list.html.erb +43 -0
  464. data/lib/generators/ruby_cms/templates/views/admin/media_assets/edit.html.erb +13 -0
  465. data/lib/generators/ruby_cms/templates/views/admin/media_assets/index.html.erb +36 -0
  466. data/lib/generators/ruby_cms/templates/views/admin/media_assets/show.html.erb +97 -0
  467. data/lib/generators/ruby_cms/templates/views/admin/passkey_credentials/index.html.erb +30 -0
  468. data/lib/generators/ruby_cms/templates/views/admin/passkey_registrations/new.html.erb +23 -0
  469. data/lib/generators/ruby_cms/templates/views/admin/permissions/_admin_table_content.html.erb +45 -0
  470. data/lib/generators/ruby_cms/templates/views/admin/permissions/_matrix.html.erb +75 -0
  471. data/lib/generators/ruby_cms/templates/views/admin/permissions/_row.html.erb +62 -0
  472. data/lib/generators/ruby_cms/templates/views/admin/permissions/index.html.erb +65 -0
  473. data/lib/generators/ruby_cms/templates/views/admin/redirects/_admin_table_content.html.erb +77 -0
  474. data/lib/generators/ruby_cms/templates/views/admin/redirects/_form.html.erb +97 -0
  475. data/lib/generators/ruby_cms/templates/views/admin/redirects/_row.html.erb +82 -0
  476. data/lib/generators/ruby_cms/templates/views/admin/redirects/edit.html.erb +21 -0
  477. data/lib/generators/ruby_cms/templates/views/admin/redirects/index.html.erb +40 -0
  478. data/lib/generators/ruby_cms/templates/views/admin/redirects/new.html.erb +12 -0
  479. data/lib/generators/ruby_cms/templates/views/admin/security/index.html.erb +88 -0
  480. data/lib/generators/ruby_cms/templates/views/admin/security/ip_blocklist_content.html.erb +3 -0
  481. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_events_by_type.html.erb +24 -0
  482. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_blocklist.html.erb +37 -0
  483. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_blocklist_content.html.erb +41 -0
  484. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_ip_management.html.erb +11 -0
  485. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_recent_events.html.erb +59 -0
  486. data/lib/generators/ruby_cms/templates/views/admin/security/partials/_top_threat_ips.html.erb +21 -0
  487. data/lib/generators/ruby_cms/templates/views/admin/settings/index.html.erb +509 -0
  488. data/lib/generators/ruby_cms/templates/views/admin/shared/_add_button.html.erb +15 -0
  489. data/lib/generators/ruby_cms/templates/views/admin/shared/_data_table.html.erb +255 -0
  490. data/lib/generators/ruby_cms/templates/views/admin/shared/_turbo_pagination.html.erb +53 -0
  491. data/lib/generators/ruby_cms/templates/views/admin/system_health/index.html.erb +248 -0
  492. data/lib/generators/ruby_cms/templates/views/admin/trash/index.html.erb +58 -0
  493. data/lib/generators/ruby_cms/templates/views/admin/user_permissions/_row.html.erb +29 -0
  494. data/lib/generators/ruby_cms/templates/views/admin/user_permissions/index.html.erb +78 -0
  495. data/lib/generators/ruby_cms/templates/views/admin/users/_admin_table_content.html.erb +50 -0
  496. data/lib/generators/ruby_cms/templates/views/admin/users/_invite_form.html.erb +43 -0
  497. data/lib/generators/ruby_cms/templates/views/admin/users/_invites_panel.html.erb +91 -0
  498. data/lib/generators/ruby_cms/templates/views/admin/users/_row.html.erb +109 -0
  499. data/lib/generators/ruby_cms/templates/views/admin/users/index.html.erb +43 -0
  500. data/lib/generators/ruby_cms/templates/views/admin/users/show.html.erb +343 -0
  501. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/_admin_table_content.html.erb +61 -0
  502. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/_row.html.erb +90 -0
  503. data/lib/generators/ruby_cms/templates/views/admin/visitor_errors/index.html.erb +18 -0
  504. data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/visitor_errors/show.html.erb +4 -4
  505. data/lib/generators/ruby_cms/templates/views/admin/visual_editor/index.html.erb +383 -0
  506. data/{app/views/layouts/ruby_cms → lib/generators/ruby_cms/templates/views/layouts/admin}/_admin_flash_messages.html.erb +3 -3
  507. data/lib/generators/ruby_cms/templates/views/layouts/admin/_admin_sidebar.html.erb +239 -0
  508. data/lib/generators/ruby_cms/templates/views/layouts/admin/_command_palette.html.erb +65 -0
  509. data/lib/generators/ruby_cms/templates/views/layouts/admin/_shortcuts_overlay.html.erb +81 -0
  510. data/lib/generators/ruby_cms/templates/views/layouts/admin/_topbar_widgets.html.erb +9 -0
  511. data/lib/generators/ruby_cms/templates/views/layouts/admin/minimal.html.erb +71 -0
  512. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_bell.html.erb +81 -0
  513. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_profile_menu.html.erb +86 -0
  514. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_search_trigger.html.erb +9 -0
  515. data/lib/generators/ruby_cms/templates/views/layouts/admin/topbar/_shortcuts_button.html.erb +8 -0
  516. data/lib/generators/ruby_cms/templates/views/shared/_maintenance_banner.html.erb +10 -0
  517. data/lib/ruby_cms/app_wiring.rb +56 -0
  518. data/lib/ruby_cms/auth_wiring.rb +64 -0
  519. data/lib/ruby_cms/cli.rb +181 -121
  520. data/lib/ruby_cms/excludes.rb +41 -0
  521. data/lib/ruby_cms/file_installer.rb +41 -0
  522. data/lib/ruby_cms/gem_setup.rb +84 -0
  523. data/lib/ruby_cms/helper_wiring.rb +32 -0
  524. data/lib/ruby_cms/importmap_wiring.rb +41 -0
  525. data/lib/ruby_cms/installer.rb +128 -0
  526. data/lib/ruby_cms/lockfile.rb +49 -0
  527. data/lib/ruby_cms/manifest.rb +88 -0
  528. data/lib/ruby_cms/manifest_data.rb +233 -0
  529. data/lib/ruby_cms/migration_installer.rb +64 -0
  530. data/lib/ruby_cms/nav_assembler.rb +43 -0
  531. data/lib/ruby_cms/passkey_wiring.rb +42 -0
  532. data/lib/ruby_cms/path_map.rb +26 -0
  533. data/lib/ruby_cms/routes_assembler.rb +103 -0
  534. data/lib/ruby_cms/syncer.rb +81 -0
  535. data/lib/ruby_cms/updater.rb +76 -0
  536. data/lib/ruby_cms/version.rb +1 -1
  537. data/lib/ruby_cms.rb +4 -245
  538. metadata +606 -169
  539. data/.cursor/dhh.mdc +0 -698
  540. data/app/components/ruby_cms/admin/admin_page/admin_table_content.rb +0 -32
  541. data/app/components/ruby_cms/admin/admin_page.rb +0 -345
  542. data/app/components/ruby_cms/admin/admin_page_header.rb +0 -79
  543. data/app/components/ruby_cms/admin/admin_resource_card.rb +0 -55
  544. data/app/components/ruby_cms/admin/base_component.rb +0 -78
  545. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table.rb +0 -157
  546. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_actions.rb +0 -127
  547. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_body.rb +0 -15
  548. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_cell.rb +0 -43
  549. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_checkbox_head.rb +0 -35
  550. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_delete_modal.rb +0 -174
  551. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header.rb +0 -59
  552. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_header_bar.rb +0 -169
  553. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_pagination.rb +0 -192
  554. data/app/components/ruby_cms/admin/bulk_action_table/bulk_action_table_row.rb +0 -96
  555. data/app/components/ruby_cms/admin/bulk_action_table/bulk_actions.rb +0 -196
  556. data/app/controllers/concerns/ruby_cms/admin_pagination.rb +0 -120
  557. data/app/controllers/concerns/ruby_cms/admin_turbo_table.rb +0 -68
  558. data/app/controllers/concerns/ruby_cms/page_tracking.rb +0 -67
  559. data/app/controllers/concerns/ruby_cms/visitor_error_capture.rb +0 -39
  560. data/app/controllers/ruby_cms/admin/analytics_controller.rb +0 -213
  561. data/app/controllers/ruby_cms/admin/base_controller.rb +0 -132
  562. data/app/controllers/ruby_cms/admin/commands_controller.rb +0 -122
  563. data/app/controllers/ruby_cms/admin/content_block_versions_controller.rb +0 -62
  564. data/app/controllers/ruby_cms/admin/content_blocks_controller.rb +0 -391
  565. data/app/controllers/ruby_cms/admin/dashboard_controller.rb +0 -89
  566. data/app/controllers/ruby_cms/admin/locale_controller.rb +0 -21
  567. data/app/controllers/ruby_cms/admin/permissions_controller.rb +0 -66
  568. data/app/controllers/ruby_cms/admin/settings_controller.rb +0 -223
  569. data/app/controllers/ruby_cms/admin/user_permissions_controller.rb +0 -77
  570. data/app/controllers/ruby_cms/admin/users_controller.rb +0 -107
  571. data/app/controllers/ruby_cms/admin/visitor_errors_controller.rb +0 -89
  572. data/app/controllers/ruby_cms/admin/visual_editor_controller.rb +0 -345
  573. data/app/controllers/ruby_cms/errors_controller.rb +0 -35
  574. data/app/helpers/ruby_cms/admin/admin_page_helper.rb +0 -21
  575. data/app/helpers/ruby_cms/admin/bulk_action_table_helper.rb +0 -159
  576. data/app/helpers/ruby_cms/admin/dashboard_helper.rb +0 -20
  577. data/app/helpers/ruby_cms/application_helper.rb +0 -41
  578. data/app/helpers/ruby_cms/bulk_action_table_helper.rb +0 -151
  579. data/app/helpers/ruby_cms/content_blocks_helper.rb +0 -399
  580. data/app/helpers/ruby_cms/settings_helper.rb +0 -172
  581. data/app/javascript/controllers/ruby_cms/content_block_history_controller.js +0 -91
  582. data/app/javascript/controllers/ruby_cms/index.js +0 -117
  583. data/app/javascript/controllers/ruby_cms/mobile_menu_controller.js +0 -55
  584. data/app/javascript/controllers/ruby_cms/nav_order_sortable_controller.js +0 -192
  585. data/app/javascript/controllers/ruby_cms/visual_editor_controller.js +0 -325
  586. data/app/models/concerns/content_block/searchable.rb +0 -22
  587. data/app/models/ruby_cms/content_block.rb +0 -8
  588. data/app/models/ruby_cms/permission.rb +0 -57
  589. data/app/models/ruby_cms/permittable.rb +0 -37
  590. data/app/models/ruby_cms/preference.rb +0 -111
  591. data/app/models/ruby_cms/user_permission.rb +0 -12
  592. data/app/models/ruby_cms/visitor_error.rb +0 -109
  593. data/app/services/ruby_cms/analytics/report.rb +0 -512
  594. data/app/services/ruby_cms/command_runner.rb +0 -42
  595. data/app/services/ruby_cms/security_tracker.rb +0 -92
  596. data/app/views/admin/content_block_versions/index.html.erb +0 -52
  597. data/app/views/admin/content_block_versions/show.html.erb +0 -37
  598. data/app/views/layouts/ruby_cms/_admin_sidebar.html.erb +0 -122
  599. data/app/views/layouts/ruby_cms/admin.html.erb +0 -77
  600. data/app/views/layouts/ruby_cms/minimal.html.erb +0 -181
  601. data/app/views/ruby_cms/admin/commands/index.html.erb +0 -104
  602. data/app/views/ruby_cms/admin/content_block_versions/index.html.erb +0 -52
  603. data/app/views/ruby_cms/admin/content_blocks/_form.html.erb +0 -92
  604. data/app/views/ruby_cms/admin/content_blocks/_row.html.erb +0 -25
  605. data/app/views/ruby_cms/admin/content_blocks/index.html.erb +0 -62
  606. data/app/views/ruby_cms/admin/content_blocks/new.html.erb +0 -10
  607. data/app/views/ruby_cms/admin/dashboard/index.html.erb +0 -31
  608. data/app/views/ruby_cms/admin/permissions/_row.html.erb +0 -11
  609. data/app/views/ruby_cms/admin/permissions/index.html.erb +0 -62
  610. data/app/views/ruby_cms/admin/settings/index.html.erb +0 -268
  611. data/app/views/ruby_cms/admin/shared/_bulk_action_table_index.html.erb +0 -56
  612. data/app/views/ruby_cms/admin/user_permissions/index.html.erb +0 -85
  613. data/app/views/ruby_cms/admin/users/_row.html.erb +0 -17
  614. data/app/views/ruby_cms/admin/users/index.html.erb +0 -70
  615. data/app/views/ruby_cms/admin/visitor_errors/_row.html.erb +0 -35
  616. data/app/views/ruby_cms/admin/visitor_errors/index.html.erb +0 -57
  617. data/app/views/ruby_cms/admin/visual_editor/index.html.erb +0 -157
  618. data/app/views/ruby_cms/errors/not_found.html.erb +0 -92
  619. data/config/database.yml +0 -6
  620. data/config/importmap.rb +0 -40
  621. data/config/locales/en.yml +0 -198
  622. data/config/locales/nl.yml +0 -156
  623. data/config/routes.rb +0 -76
  624. data/db/migrate/20260130000001_add_referer_and_query_to_ruby_cms_visitor_errors.rb +0 -8
  625. data/db/migrate/20260130000003_add_category_to_ruby_cms_preferences.rb +0 -8
  626. data/docs/assets/ruby_cms_logo.png +0 -0
  627. data/lib/generators/ruby_cms/install_generator.rb +0 -1080
  628. data/lib/ruby_cms/app_integration.rb +0 -82
  629. data/lib/ruby_cms/commands_registry.rb +0 -40
  630. data/lib/ruby_cms/css_compiler.rb +0 -35
  631. data/lib/ruby_cms/engine/admin_permissions.rb +0 -69
  632. data/lib/ruby_cms/engine/content_blocks_tasks.rb +0 -66
  633. data/lib/ruby_cms/engine/css.rb +0 -14
  634. data/lib/ruby_cms/engine/dashboard_registration.rb +0 -66
  635. data/lib/ruby_cms/engine/navigation_registration.rb +0 -89
  636. data/lib/ruby_cms/engine.rb +0 -273
  637. /data/{app → lib/generators/ruby_cms/templates}/assets/images/ruby_cms/logo.png +0 -0
  638. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260211000001_add_ruby_cms_analytics_fields_to_ahoy_events.rb +0 -0
  639. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260212000001_use_unprefixed_cms_tables.rb +0 -0
  640. /data/{db → lib/generators/ruby_cms/templates/db}/migrate/20260409000001_add_analytics_performance_indexes.rb +0 -0
  641. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/auto_save_preference_controller.js +0 -0
  642. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/locale_tabs_controller.js +0 -0
  643. /data/{app/javascript/controllers/ruby_cms → lib/generators/ruby_cms/templates/javascript/controllers/admin}/toggle_controller.js +0 -0
  644. /data/{app → lib/generators/ruby_cms/templates}/models/concerns/content_block/publishable.rb +0 -0
  645. /data/{app → lib/generators/ruby_cms/templates}/models/content_block_version.rb +0 -0
  646. /data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_back_button.html.erb +0 -0
  647. /data/{app/views/ruby_cms → lib/generators/ruby_cms/templates/views}/admin/analytics/partials/_security_alert.html.erb +0 -0
data/README.md CHANGED
@@ -1,21 +1,44 @@
1
1
  # RubyCMS
2
2
 
3
- Reusable Rails engine for a CMS-style admin: content blocks, visual editor, versioning, pluggable dashboard, permissions, analytics, and visitor error tracking.
3
+ Standalone CLI gem for a modular CMS-style admin: content blocks, visual editor, versioning, pluggable dashboard, permissions, analytics, and visitor error tracking.
4
4
 
5
- Your app owns product features (pages, models, business logic); RubyCMS manages content workflows and admin screens.
5
+ Your app owns product features (pages, models, business logic). RubyCMS scaffolds the admin layer into your codebase so you can customize everything without a runtime engine dependency.
6
+
7
+ ## How it works
8
+
9
+ RubyCMS is **install-only**. Running `ruby_cms install` (the CLI) copies controllers, views, models, helpers, JavaScript, and `lib/ruby_cms/` into your host app for whichever modules you select. After install you can remove the gem from your `Gemfile` — the app runs entirely on copied code.
10
+
11
+ | Layer | Location after install |
12
+ |-------|------------------------|
13
+ | Registries (nav, permissions, dashboard, settings, icons) | `lib/ruby_cms/` |
14
+ | Core configuration (`RubyCms.configure`, visual-editor preview) | `config/initializers/admin.rb` |
15
+ | Sidebar nav (generated from module fragments; user-editable) | `config/initializers/admin_nav.rb` |
16
+ | Dashboard blocks | `config/initializers/admin_dashboard.rb` |
17
+ | Admin controllers & views | `app/controllers/admin/`, `app/views/admin/` |
18
+ | UI components (Phlex / RubyUI) | `app/components/ruby_ui/admin_page/` |
19
+ | Rake tasks | `lib/tasks/ruby_cms.rake` |
20
+ | Module manifest / lockfile | `.ruby_cms.yml` |
21
+
22
+ Keep the gem in your `Gemfile` only when you need install/update commands:
23
+
24
+ ```ruby
25
+ # Uncomment temporarily for install or updates:
26
+ # gem "ruby_cms", path: "../gems/ruby_cms"
27
+ ```
6
28
 
7
29
  ## Features
8
30
 
9
- - **Content blocks** -- rich text, plain text, images, links, and lists with locale support
10
- - **Content block versioning** -- automatic version history with rollback and side-by-side diffs
11
- - **Visual editor** -- inline editing on live page previews
12
- - **Pluggable dashboard** -- registry-based blocks with permission gating and host-app extensibility
13
- - **Permissions** -- key-based access control with templates and per-user assignment
14
- - **Admin page generator** -- one command to scaffold a new admin page with nav, permission, and route
15
- - **Named icon registry** -- 20+ Heroicons by name, no SVG copy-pasting
16
- - **Settings** -- DB-backed admin settings with categories and UI
17
- - **Analytics** -- Ahoy-powered visit/event tracking with dashboard drill-downs
18
- - **Visitor error tracking** -- automatic 404/500 capture with admin overview
31
+ - **Content blocks** rich text, plain text, images, links, and lists with locale support
32
+ - **Content block versioning** automatic version history with rollback and side-by-side diffs
33
+ - **Visual editor** inline editing on live page previews
34
+ - **Pluggable dashboard** registry-based blocks with permission gating and host-app extensibility
35
+ - **Permissions** key-based access control with templates and per-user assignment
36
+ - **Admin page generator** one command to scaffold a new admin page with nav, permission, and route
37
+ - **Named icon registry** 20+ Heroicons by name, no SVG copy-pasting
38
+ - **Settings** DB-backed admin settings with categories and UI
39
+ - **Analytics** Ahoy-powered visit/event tracking with dashboard drill-downs
40
+ - **Visitor error tracking** automatic 404/500 capture with admin overview
41
+ - **Admin UI components** — reusable `AdminPage`, `AdminPageHeader`, `AdminTableContent`, and `AdminResourceCard` (RubyUI / Phlex)
19
42
 
20
43
  ## Quick Start
21
44
 
@@ -23,17 +46,89 @@ Your app owns product features (pages, models, business logic); RubyCMS manages
23
46
  # Add to Gemfile
24
47
  gem "ruby_cms"
25
48
 
26
- # Install
27
- rails g ruby_cms:install
49
+ bundle install
50
+ ruby_cms install # interactive module picker
51
+ ruby_cms install --modules media,redirects # non-interactive
28
52
  rails db:migrate
29
53
  rails ruby_cms:seed_permissions
30
54
  rails ruby_cms:setup_admin
31
55
  ```
32
56
 
33
- The install generator sets up the initializer, mounts the engine at `/admin`, runs migrations, configures Tailwind, Stimulus, Action Text, and Ahoy.
57
+ The `ruby_cms install` CLI:
58
+
59
+ - Copies admin scaffold into `app/` and `lib/ruby_cms/` for selected modules
60
+ - Creates `config/initializers/admin.rb` (`RubyCms.configure` block + visual-editor preview setup)
61
+ - Generates `config/initializers/admin_nav.rb` (sidebar nav assembled from per-module fragments)
62
+ - Creates `config/initializers/admin_dashboard.rb` (dashboard blocks)
63
+ - Seeds module permissions from the manifest — no manual `register_permission_keys` needed
64
+ - Adds `namespace :admin` routes (no engine mount)
65
+ - Configures Tailwind, Stimulus, Action Text, and Ahoy
66
+ - Records installed modules in `.ruby_cms.yml`
34
67
 
35
68
  Visit `/admin` and sign in with the admin user you configured.
36
69
 
70
+ ### Adding a module later
71
+
72
+ ```bash
73
+ ruby_cms install --add media
74
+ ```
75
+
76
+ This copies the module files, seeds its permissions, and appends its nav fragment to `config/initializers/admin_nav.rb`.
77
+
78
+ ### Pulling updates from the gem
79
+
80
+ ```bash
81
+ # Re-add gem to Gemfile, then:
82
+ ruby_cms update # updates only modules in .ruby_cms.yml
83
+ ruby_cms update --only=mod1,mod2
84
+ ruby_cms update --dry-run
85
+ ```
86
+
87
+ Only changed files are overwritten. Review diffs before committing. `config/initializers/admin_nav.rb` is regenerated on every install/update, so custom sidebar entries belong in `config/initializers/admin_pages.rb` (where `rails g ruby_cms:admin_page` writes them) — that file is never touched by the gem.
88
+
89
+ ## Admin UI components
90
+
91
+ Admin views use Phlex components under `app/components/ruby_ui/admin_page/` (registered as `AdminPage`, `AdminPageHeader`, etc. via the RubyUI kit).
92
+
93
+ | Component | Purpose |
94
+ |-----------|---------|
95
+ | `RubyUI::AdminPage` | White card wrapper; optional `turbo_frame:` for full-page Turbo updates |
96
+ | `RubyUI::AdminPageHeader` | Breadcrumbs, title, subtitle, action slot |
97
+ | `RubyUI::AdminTableContent` | Turbo Frame (`id: "admin_table_content"`) for pagination, filters, search |
98
+ | `RubyUI::AdminResourceCard` | Layout constants for show/edit pages (grid, sidebar, form classes) |
99
+
100
+ ### Usage in views
101
+
102
+ ```erb
103
+ <%= AdminPage do %>
104
+ <%= AdminPageHeader(
105
+ title: "Users",
106
+ breadcrumbs: [
107
+ { label: "Admin", url: admin_root_path },
108
+ { label: "Users" }
109
+ ]
110
+ ) do %>
111
+ <%= link_to "New user", new_admin_user_path, class: "..." %>
112
+ <% end %>
113
+
114
+ <%= admin_table_content do %>
115
+ <%# table rows, pagination — replaces frame on Turbo navigation %>
116
+ <% end %>
117
+ <% end %>
118
+ ```
119
+
120
+ `admin_table_content` is provided by `Admin::AdminPageHelper`.
121
+
122
+ For resource show pages, use `RubyUI::AdminResourceCard` constants:
123
+
124
+ ```erb
125
+ <div class="<%= RubyUI::AdminResourceCard::CARD_CLASS %>">
126
+ <div class="<%= RubyUI::AdminResourceCard::GRID_CLASS %>">
127
+ ...
128
+ </div>
129
+ </div>
130
+ ```
131
+
37
132
  ## Content Blocks
38
133
 
39
134
  Use the `content_block` helper in any view:
@@ -133,12 +228,13 @@ POST /admin/content_blocks/:id/versions/:vid/rollback
133
228
 
134
229
  ## Visual Editor
135
230
 
136
- Configure preview templates in `config/initializers/ruby_cms.rb`:
231
+ Configure preview templates in `config/initializers/admin.rb`:
137
232
 
138
233
  ```ruby
139
234
  RubyCms.configure do |c|
140
235
  c.preview_templates = { "home" => "pages/home", "about" => "pages/about" }
141
- c.preview_data = ->(page_key, view) { { products: Product.limit(5) } }
236
+ c.preview_data = ->(page_key, view, variant = nil) { { products: Product.limit(5) } }
237
+ c.preview_variants = ->(page_key) { [] }
142
238
  end
143
239
  ```
144
240
 
@@ -150,67 +246,71 @@ The dashboard uses a registry-based block system. Each block is a partial with o
150
246
 
151
247
  ### Default Blocks
152
248
 
249
+ Register defaults in `config/initializers/admin_dashboard.rb` (generated at install with CMS stats blocks; customize for your app):
250
+
153
251
  | Block | Section | Permission |
154
252
  |-------|---------|------------|
155
253
  | Content blocks stats | stats | `manage_content_blocks` |
156
254
  | Users stats | stats | `manage_permissions` |
157
255
  | Permissions stats | stats | `manage_permissions` |
158
256
  | Visitor errors stats | stats | `manage_visitor_errors` |
159
- | Quick actions | main | -- |
257
+ | Quick actions | main | |
160
258
  | Recent errors | main | `manage_visitor_errors` |
161
259
  | Analytics overview | main | `manage_analytics` |
162
260
 
163
261
  ### Adding Custom Blocks
164
262
 
165
263
  ```ruby
166
- # config/initializers/ruby_cms.rb (or ruby_cms_pages.rb)
167
- Rails.application.config.to_prepare do
168
- RubyCms.dashboard_register(
169
- key: :orders_stats,
170
- label: "Orders",
171
- section: :stats, # :stats (top row) or :main (bottom grid)
172
- order: 5,
173
- partial: "admin/dashboard/orders_stats",
174
- permission: :manage_orders,
175
- span: :single, # :single or :double (grid width)
176
- data: ->(controller) {
177
- { count: Order.count, today: Order.where("created_at > ?", Date.today).count }
178
- }
179
- )
180
- end
264
+ # config/initializers/admin_dashboard.rb
265
+ RubyCms.dashboard_register(
266
+ key: :orders_stats,
267
+ label: "Orders",
268
+ section: :stats, # :stats (top row) or :main (bottom grid)
269
+ order: 5,
270
+ partial: "admin/dashboard/blocks/orders_stats",
271
+ permission: :manage_orders,
272
+ span: :single, # :single or :double (grid width)
273
+ data: ->(controller) {
274
+ { count: Order.count, today: Order.where("created_at > ?", Date.today).count }
275
+ }
276
+ )
181
277
  ```
182
278
 
183
- Then create the partial `app/views/admin/dashboard/_orders_stats.html.erb`. The `block` local contains the registration data and any computed `data`.
279
+ Then create the partial `app/views/admin/dashboard/blocks/_orders_stats.html.erb`. The `block` local contains the registration data and any computed `data`.
184
280
 
185
281
  ### Overriding Default Blocks
186
282
 
187
- Re-register with the same key to override:
283
+ Re-register with the same key to override (in `config/initializers/admin_dashboard.rb`):
188
284
 
189
285
  ```ruby
190
286
  RubyCms.dashboard_register(
191
287
  key: :quick_actions,
192
288
  label: "Quick actions",
193
289
  section: :main,
194
- partial: "admin/dashboard/my_quick_actions" # your own partial
290
+ partial: "admin/dashboard/my_quick_actions"
195
291
  )
196
292
  ```
197
293
 
198
294
  ## Navigation and Permissions
199
295
 
296
+ Navigation registration lives in **`config/initializers/admin_nav.rb`** (generated at install/add from per-module fragments; safe to edit manually). Permission keys are declared in the gem's manifest (`lib/ruby_cms/manifest.rb`) and seeded automatically at install — you do not need to call `register_permission_keys` for built-in modules. Register additional app-specific keys in `config/initializers/admin_nav.rb` or any initializer.
297
+
200
298
  ### register_page (recommended)
201
299
 
202
- One call to register a nav item and its permission:
300
+ One call to register a nav item and its permission. Place in `config/initializers/admin_nav.rb` (or any initializer):
203
301
 
204
302
  ```ruby
205
- RubyCms.register_page(
206
- key: :backups,
207
- label: "Backups",
208
- path: :admin_backups_path, # Symbol: auto-wrapped via main_app
209
- icon: :archive_box, # Named icon from RubyCms::Icons
210
- section: :main, # :main (sidebar top) or :settings (sidebar bottom)
211
- permission: :manage_backups, # Auto-registered as permission key
212
- order: 10
213
- )
303
+ Rails.application.config.to_prepare do
304
+ RubyCms.register_page(
305
+ key: :backups,
306
+ label: "Backups",
307
+ path: ->(view) { view.admin_backups_path },
308
+ icon: :archive_box,
309
+ section: :main,
310
+ permission: :manage_backups,
311
+ order: 10
312
+ )
313
+ end
214
314
  ```
215
315
 
216
316
  ### nav_register (low-level)
@@ -229,12 +329,28 @@ RubyCms.nav_register(
229
329
  )
230
330
  ```
231
331
 
332
+ ### nav_group (accordion)
333
+
334
+ ```ruby
335
+ RubyCms.nav_group(
336
+ key: :operations,
337
+ label: "Operations",
338
+ icon: :folder,
339
+ section: "main",
340
+ order: 20,
341
+ path: ->(view) { view.admin_operations_path },
342
+ children: %i[backups reports]
343
+ )
344
+ ```
345
+
346
+ Groups are hidden automatically when they have no visible children and no `path`.
347
+
232
348
  ### Path Options
233
349
 
234
350
  | Format | Example | Behavior |
235
351
  |--------|---------|----------|
236
352
  | Symbol | `:admin_backups_path` | Auto-wrapped: `view.main_app.send(:admin_backups_path)` |
237
- | Lambda | `-> (v) { v.some_path }` | Called with view context |
353
+ | Lambda | `->(view) { view.admin_backups_path }` | Called with view context |
238
354
  | String | `"/admin/backups"` | Used as-is |
239
355
 
240
356
  ### Named Icons
@@ -243,19 +359,16 @@ Use symbol keys from `RubyCms::Icons`:
243
359
 
244
360
  ```ruby
245
361
  RubyCms::Icons.available
246
- # => [:home, :pencil_square, :document_duplicate, :chart_bar, :shield_check,
247
- # :exclamation_triangle, :user_group, :cog_6_tooth, :archive_box, :folder,
248
- # :bell, :clock, :tag, :cube, :envelope, :wrench, :globe, :photograph,
249
- # :list_bullet, :plus_circle, :trash, :eye, :lock_closed, :currency_dollar]
362
+ # => [:home, :pencil_square, :document_duplicate, :chart_bar, :shield_check, ...]
250
363
  ```
251
364
 
252
365
  Raw SVG strings are also accepted for custom icons.
253
366
 
254
367
  ### Permission Keys
255
368
 
256
- Default keys: `manage_admin`, `manage_permissions`, `manage_content_blocks`, `manage_visitor_errors`, `manage_analytics`.
369
+ Built-in module permission keys (e.g. `manage_admin`, `manage_permissions`, `manage_content_blocks`, `manage_visitor_errors`, `manage_analytics`) are declared in the gem manifest and seeded automatically at install.
257
370
 
258
- Register additional keys:
371
+ Register additional app-specific keys in any initializer (e.g. `config/initializers/admin_nav.rb`):
259
372
 
260
373
  ```ruby
261
374
  RubyCms.register_permission_keys(:manage_orders, :manage_reports)
@@ -263,8 +376,6 @@ RubyCms.register_permission_keys(:manage_orders, :manage_reports)
263
376
 
264
377
  ### Permission Templates
265
378
 
266
- Group permission keys into reusable templates:
267
-
268
379
  ```ruby
269
380
  RubyCms.register_permission_template(:editor,
270
381
  label: "Editor",
@@ -272,17 +383,16 @@ RubyCms.register_permission_template(:editor,
272
383
  description: "Can manage content but not users"
273
384
  )
274
385
 
275
- # Apply to a user:
276
386
  RubyCms::Permission.apply_template!(user, :editor)
277
387
  ```
278
388
 
279
389
  ### cms_page Macro
280
390
 
281
- In controllers that inherit from `RubyCms::Admin::BaseController`, use `cms_page` to link to a registered page. The permission is looked up from the nav registry automatically:
391
+ In controllers under `Admin::ApplicationController`, use `cms_page` to enforce the permission from `register_page`:
282
392
 
283
393
  ```ruby
284
- class Admin::BackupsController < RubyCms::Admin::BaseController
285
- cms_page :backups # permission from register_page(:backups, permission: :manage_backups)
394
+ class Admin::BackupsController < Admin::ApplicationController
395
+ cms_page :backups
286
396
 
287
397
  def index
288
398
  @backups = Backup.recent
@@ -292,49 +402,50 @@ end
292
402
 
293
403
  ## Admin Page Generator
294
404
 
295
- Scaffold a complete admin page with one command:
405
+ Scaffold a complete admin page controller, data-table index, form and routes —
406
+ that follows the same conventions as the built-in modules. Pass optional
407
+ `field:type` pairs (type defaults to `string`) to scaffold the columns:
296
408
 
297
409
  ```bash
298
- rails g ruby_cms:admin_page backups
410
+ rails g ruby_cms:admin_page product name:string price:decimal published:boolean
299
411
  ```
300
412
 
301
413
  This generates:
302
414
 
303
415
  | File | Description |
304
416
  |------|-------------|
305
- | `app/controllers/admin/backups_controller.rb` | Controller with `cms_page :backups` |
306
- | `app/views/admin/backups/index.html.erb` | View template |
307
- | `config/routes.rb` | Route injection (`namespace :admin`) |
308
- | `config/initializers/ruby_cms_pages.rb` | `register_page` call |
417
+ | `app/controllers/admin/products_controller.rb` | Full CRUD controller on the shared concern stack (pagination, turbo tables, bulk delete, audit) |
418
+ | `app/views/admin/products/index.html.erb` | `AdminPage` + `AdminPageHeader` with a `data_table` |
419
+ | `app/views/admin/products/_admin_table_content.html.erb` | The data-table partial (headers from your fields) |
420
+ | `app/views/admin/products/_row.html.erb` | Row cells per field |
421
+ | `app/views/admin/products/_form.html.erb`, `new`, `edit` | Form scaffolded from your fields |
422
+ | `config/routes.rb` | `resources :products` (with `bulk_delete`) in `namespace :admin` |
423
+ | `config/initializers/admin_pages.rb` | Sidebar registration via `RubyCms.register_page` (host-owned — never overwritten by `ruby_cms update`) |
309
424
 
310
425
  ### Options
311
426
 
312
427
  ```bash
313
- rails g ruby_cms:admin_page backups \
314
- --permission=manage_backups \
315
- --icon=archive_box \
316
- --section=settings \
317
- --order=15
428
+ rails g ruby_cms:admin_page report --read-only --section=settings --icon=chart_bar --order=15
318
429
  ```
319
430
 
320
431
  | Option | Default | Description |
321
432
  |--------|---------|-------------|
322
- | `--permission` | `manage_<name>` | Permission key |
323
- | `--icon` | `folder` | Icon from `RubyCms::Icons` |
433
+ | `--permission` | `manage_<plural>` | Permission key |
434
+ | `--icon` | `folder` | Icon from `RubyCms::Icons` (`RubyCms::Icons.available`) |
324
435
  | `--section` | `main` | `main` or `settings` |
325
436
  | `--order` | `10` | Sort order in nav |
437
+ | `--read-only` | `false` | Index-only page (no form/create/update/destroy) |
438
+ | `--skip-route` | `false` | Don't touch `config/routes.rb` |
439
+ | `--skip-nav` | `false` | Don't register the page in the sidebar |
326
440
 
327
441
  After generating, run `rails ruby_cms:seed_permissions` to create the permission row in the database.
328
442
 
329
443
  ## Settings
330
444
 
331
- RubyCMS uses DB-backed settings with a registry for defaults and types:
445
+ DB-backed settings with a registry for defaults and types:
332
446
 
333
447
  ```ruby
334
- # Read
335
448
  RubyCms::Settings.get(:analytics_default_period, default: "week")
336
-
337
- # Write (admin UI or programmatic)
338
449
  RubyCms::Settings.set(:analytics_default_period, "month")
339
450
  ```
340
451
 
@@ -354,7 +465,7 @@ Settings are managed in **Admin > Settings** with tabs per category.
354
465
 
355
466
  ## Visitor Error Tracking
356
467
 
357
- Public exceptions are automatically captured when `RubyCms::VisitorErrorCapture` is included in your `ApplicationController` (added by the install generator).
468
+ Public exceptions are captured when `VisitorErrorCapture` is included in your `ApplicationController` (added by the install generator).
358
469
 
359
470
  View captured errors in **Admin > Visitor errors** with status codes, paths, timestamps, and resolution tracking.
360
471
 
@@ -362,22 +473,20 @@ View captured errors in **Admin > Visitor errors** with status codes, paths, tim
362
473
 
363
474
  RubyCMS integrates with Ahoy for server-side page view and event tracking.
364
475
 
365
- Include `RubyCms::PageTracking` in public controllers:
476
+ Include `PageTracking` in public controllers:
366
477
 
367
478
  ```ruby
368
479
  class PagesController < ApplicationController
369
- include RubyCms::PageTracking
480
+ include PageTracking
370
481
  end
371
482
  ```
372
483
 
373
- View analytics in **Admin > Analytics** with:
374
- - Visit and event counts
375
- - Popular pages
376
- - Top visitors
377
- - Configurable date ranges and periods
484
+ View analytics in **Admin > Analytics** with visit/event counts, popular pages, top visitors, and configurable date ranges.
378
485
 
379
486
  ### Customization Hooks
380
487
 
488
+ Place these in `config/initializers/admin.rb` inside the `RubyCms.configure` block:
489
+
381
490
  ```ruby
382
491
  RubyCms.configure do |c|
383
492
  c.analytics_visit_scope = ->(scope) { scope.where.not(ip: ["127.0.0.1"]) }
@@ -390,147 +499,193 @@ end
390
499
 
391
500
  ## Seeding Content Blocks from YAML
392
501
 
393
- Set the translation namespace:
394
-
395
502
  ```ruby
503
+ # config/initializers/admin.rb
396
504
  RubyCms.configure do |c|
397
505
  c.content_blocks_translation_namespace = "content_blocks"
398
506
  end
399
507
  ```
400
508
 
401
- Create locale files:
402
-
403
509
  ```yaml
404
510
  # config/locales/en.yml
405
511
  en:
406
512
  content_blocks:
407
513
  hero_title: "Welcome to my site"
408
- footer_text: "Copyright 2026"
409
514
  ```
410
515
 
411
- Import:
412
-
413
516
  ```bash
414
517
  rails ruby_cms:content_blocks:import
415
- ```
416
-
417
- Export DB content blocks back to YAML:
418
-
419
- ```bash
420
518
  rails ruby_cms:content_blocks:export
421
519
  ```
422
520
 
423
521
  ## Configuration
424
522
 
425
- All configuration happens in `config/initializers/ruby_cms.rb`:
523
+ Configuration is split across three generated files. All are plain Ruby initializers — edit them freely.
524
+
525
+ ### `config/initializers/admin.rb`
526
+
527
+ `RubyCms.configure` options plus visual-editor preview setup:
426
528
 
427
529
  ```ruby
530
+ # config/initializers/admin.rb
531
+
428
532
  RubyCms.configure do |c|
429
- # Base controller (must provide current_user + authentication)
430
533
  c.admin_base_controller = "ApplicationController"
431
-
432
- # Admin layout
433
534
  c.admin_layout = "admin/admin"
434
-
435
- # User model class name
436
535
  c.user_class_name = "User"
437
-
438
- # Allow user.admin? bypass when no Permission records exist
439
536
  c.bootstrap_admin_with_role = true
440
-
441
- # Redirect path for unauthenticated/unauthorized users
442
537
  c.unauthorized_redirect_path = "/"
443
-
444
- # Visual editor
445
538
  c.preview_templates = { "home" => "pages/home" }
446
- c.preview_data = ->(page_key, view) { {} }
447
-
448
- # Content blocks
539
+ c.preview_data = ->(page_key, _view, _variant = nil) { {} }
449
540
  c.content_blocks_translation_namespace = "content_blocks"
450
541
  c.image_content_types = %w[image/png image/jpeg image/gif image/webp]
451
542
  c.image_max_size = 5 * 1024 * 1024
452
543
  end
453
544
  ```
454
545
 
546
+ ### `config/initializers/admin_nav.rb`
547
+
548
+ Sidebar nav entries. Generated from per-module nav fragments at `ruby_cms install` / `ruby_cms install --add`. Safe to edit by hand; note it is **regenerated** (fragments appended) when you add a new module.
549
+
550
+ ```ruby
551
+ # config/initializers/admin_nav.rb
552
+
553
+ Rails.application.config.to_prepare do
554
+ RubyCms.register_page(key: :dashboard, ...)
555
+ RubyCms.nav_group(key: :content, ...)
556
+ # app-specific extra pages:
557
+ RubyCms.register_permission_keys(:manage_orders)
558
+ RubyCms.register_page(key: :orders, ...)
559
+ end
560
+ ```
561
+
562
+ ### `config/initializers/admin_dashboard.rb`
563
+
564
+ Dashboard block registrations:
565
+
566
+ ```ruby
567
+ # config/initializers/admin_dashboard.rb
568
+
569
+ RubyCms.dashboard_register(key: :orders_stats, ...)
570
+ ```
571
+
572
+ ### Boot initializers
573
+
574
+ | File | Role |
575
+ |------|------|
576
+ | `config/initializers/admin.rb` | `RubyCms.configure` + visual-editor preview (generated at install) |
577
+ | `config/initializers/admin_nav.rb` | Sidebar nav (generated from module fragments at install/add) |
578
+ | `config/initializers/admin_dashboard.rb` | Dashboard block registrations |
579
+ | `config/initializers/ruby_cms_core.rb` | Boot hooks: versionable concern, settings import, permission rows |
580
+
581
+ ### `config/application.rb`
582
+
583
+ After install, require the runtime module before the application class:
584
+
585
+ ```ruby
586
+ require_relative "../lib/ruby_cms"
587
+ ```
588
+
589
+ `lib/ruby_cms.rb` eager-loads `lib/ruby_cms/*.rb` so registries are available during boot.
590
+
455
591
  ## Rake Tasks
456
592
 
593
+ Copied to `lib/tasks/ruby_cms.rake` in the host app:
594
+
457
595
  | Task | Description |
458
596
  |------|-------------|
459
597
  | `ruby_cms:seed_permissions` | Create default permission rows + settings |
460
598
  | `ruby_cms:setup_admin` | Interactive first admin user setup |
461
- | `ruby_cms:grant_manage_admin` | Grant all permissions to a user by email |
599
+ | `ruby_cms:grant_manage_admin[email]` | Grant all permissions to a user by email |
462
600
  | `ruby_cms:content_blocks:export` | Export DB content blocks to YAML |
463
601
  | `ruby_cms:content_blocks:import` | Import content blocks from YAML |
464
602
  | `ruby_cms:content_blocks:sync` | Export + optional import |
465
603
  | `ruby_cms:import_initializer_settings` | Import initializer values into DB settings |
466
- | `ruby_cms:css:compile` | Compile admin CSS to host app |
467
- | `ruby_cms:css:compile_gem` | Compile admin CSS within the gem |
468
604
 
469
605
  ## Development
470
606
 
471
- ### Setup
607
+ ### Gem repository
608
+
609
+ This repository holds **generator templates** under `lib/generators/ruby_cms/templates/`. The published gem defines only `RubyCms::VERSION` in `lib/ruby_cms.rb` — no runtime engine.
472
610
 
473
611
  ```bash
474
612
  git clone https://github.com/jobhammer00/ruby_cms.git
475
613
  cd ruby_cms
476
614
  bundle install
477
- ```
478
-
479
- ### Running Tests
480
-
481
- ```bash
482
615
  bundle exec rspec
483
616
  ```
484
617
 
485
- Tests use an in-memory SQLite database via `spec/support/dummy_app.rb`. No separate dummy Rails app directory is needed.
618
+ ### Syncing changes from a host app
486
619
 
487
- ### CSS Compilation
620
+ When developing against a reference app (e.g. portfolio), push host-app edits back into gem templates using the `ruby_cms sync` CLI (run from the gem repo):
488
621
 
489
622
  ```bash
490
- rails ruby_cms:css:compile_gem
623
+ ruby_cms sync # default source: ../portfolio
624
+ ruby_cms sync --from ../gvexcelsior
625
+ ruby_cms sync --from ../gvexcelsior --dry-run
626
+ ruby_cms sync --from ../gvexcelsior --include-new # also add brand-new gem files
491
627
  ```
492
628
 
493
- ### Faster Docker Builds (`assets:precompile`)
494
-
495
- `assets:precompile` loads the Rails app in production mode and can be slow in Docker/Fly builds.
496
- RubyCMS now skips non-asset runtime initializers during this phase (navigation registration,
497
- dashboard registration, versioning hook, settings import, and permission seeding), which reduces
498
- precompile overhead.
629
+ This is the reverse of `ruby_cms update`. By default sync only **updates files the gem already ships** — manifest globs like `helpers/**/*` also match app-specific files (a public-site helper, a marketing Stimulus controller) that the gem doesn't own, and absorbing those would pollute it. New matches are skipped and listed; pass `--include-new` to pull them in deliberately when you really are adding a file to the gem.
499
630
 
500
- Recommended Docker layer order:
631
+ ### Architecture (after install)
501
632
 
502
- 1. Copy `Gemfile` / `Gemfile.lock`
503
- 2. Run `bundle install`
504
- 3. Copy app source
505
- 4. Run `SECRET_KEY_BASE=DUMMY bin/rails assets:precompile`
633
+ ```
634
+ config/initializers/
635
+ admin.rb # RubyCms.configure + visual-editor preview
636
+ admin_nav.rb # Sidebar nav (generated from module fragments)
637
+ admin_dashboard.rb # Dashboard block registrations
638
+ ruby_cms_core.rb # Boot hooks (versionable, settings, permissions)
506
639
 
507
- ### Architecture
640
+ lib/ruby_cms.rb # Module API: nav_register, register_page, configure
641
+ lib/ruby_cms/
642
+ settings.rb # DB-backed settings
643
+ settings_registry.rb # Setting definitions
644
+ dashboard_blocks.rb # Dashboard registry
645
+ commands_registry.rb # Admin commands registry
646
+ icons.rb # Named Heroicon SVG registry
647
+ content_blocks_sync.rb # YAML ↔ DB import/export
648
+ content_blocks_grouping.rb # Multi-locale grouping
649
+ cli.rb # setup_admin interactive CLI
508
650
 
509
- ```
510
- lib/ruby_cms.rb # Module: nav_register, register_page, permissions API
511
- lib/ruby_cms/engine.rb # Rails::Engine: config, initializers, rake tasks, nav registration
512
- lib/ruby_cms/icons.rb # Named Heroicon SVG registry
513
- lib/ruby_cms/dashboard_blocks.rb # Dashboard block registry
514
- lib/ruby_cms/settings.rb # DB-backed settings
515
- lib/ruby_cms/settings_registry.rb # Settings definitions and defaults
516
- app/controllers/ruby_cms/admin/ # Admin controllers (base, dashboard, content blocks, etc.)
517
- app/models/ # ContentBlock, ContentBlockVersion, Permission, etc.
518
- app/views/ruby_cms/admin/ # Admin views
519
- app/views/layouts/ruby_cms/ # Admin layout + sidebar
520
- app/components/ # ViewComponents (AdminPage, etc.)
521
- app/helpers/ # Content block, settings, dashboard helpers
651
+ app/controllers/admin/ # Dashboard, content blocks, analytics, etc.
652
+ app/views/admin/ # Admin ERB views
653
+ app/components/ruby_ui/admin_page/
654
+ admin_page.rb # Card wrapper (+ optional turbo_frame)
655
+ admin_page_header.rb # Breadcrumbs, title, actions
656
+ admin_table_content.rb # Turbo frame for tables
657
+ admin_resource_card.rb # Show-page layout constants
658
+ app/models/ # ContentBlock, Permission, Preference, etc.
659
+ lib/tasks/ruby_cms.rake # Seed, content blocks, setup_admin
522
660
  ```
523
661
 
524
- ### Key Extension Points
662
+ ### Key extension points
525
663
 
526
664
  | What | How |
527
665
  |------|-----|
528
- | New admin page | `rails g ruby_cms:admin_page <name>` |
529
- | New nav item | `RubyCms.register_page(...)` or `RubyCms.nav_register(...)` |
530
- | New permission | `RubyCms.register_permission_keys(:key)` |
531
- | New dashboard block | `RubyCms.dashboard_register(...)` |
666
+ | New admin page | `rails g ruby_cms:admin_page <name> [field:type ...]` — registers itself in `config/initializers/admin_pages.rb` |
667
+ | New nav item | `RubyCms.register_page(...)` or `RubyCms.nav_register(...)` in `config/initializers/admin_pages.rb` (host-owned; `admin_nav.rb` is regenerated) |
668
+ | New app-specific permission | `RubyCms.register_permission_keys(:key)` in `config/initializers/admin_pages.rb` |
669
+ | New dashboard block | `RubyCms.dashboard_register(...)` in `config/initializers/admin_dashboard.rb` |
532
670
  | New setting | `RubyCms::SettingsRegistry.register(...)` |
533
671
  | New icon | Pass raw SVG string to `icon:` parameter |
672
+ | Add optional module | `ruby_cms install --add <module>` |
673
+ | Pull gem updates | `ruby_cms update` |
674
+ | Push changes to gem | `ruby_cms sync [--from ../app]` (gem repo) |
675
+
676
+ ## Module reference
677
+
678
+ ### Base modules (always installed)
679
+
680
+ `core`, `auth`, `permissions`, `users`, `content_blocks`, `visual_editor`
681
+
682
+ ### Optional modules (`ruby_cms install` picker or `--add`)
683
+
684
+ `analytics`, `media`, `redirects`, `audit_log`, `trash`, `security`, `system_health`, `passkeys`, `commands`
685
+
686
+ App-specific features (contact forms, news, products, …) do **not** belong in the gem. Use **`rails g ruby_cms:admin_page`** to scaffold a CRUD admin page that plugs into nav, permissions, routes, and the shared table/UI stack — then add your model, mailers, and public controllers in the host app.
687
+
688
+ Permissions for each module are declared in the gem manifest (`lib/ruby_cms/manifest.rb` + `manifest_data.rb`) and seeded at install. You do not need to register them manually.
534
689
 
535
690
  ## License
536
691