railspress-engine 0.1.2 → 1.2.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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +20 -0
  3. data/README.md +195 -25
  4. data/app/assets/javascripts/railspress/admin.js +39 -0
  5. data/app/assets/javascripts/railspress/markdown_mode.js +343 -0
  6. data/app/assets/stylesheets/application.css +0 -0
  7. data/app/assets/stylesheets/railspress/admin/badges.css +70 -0
  8. data/app/assets/stylesheets/railspress/admin/base.css +25 -0
  9. data/app/assets/stylesheets/railspress/admin/buttons.css +140 -0
  10. data/app/assets/stylesheets/railspress/admin/cards.css +52 -0
  11. data/app/assets/stylesheets/railspress/admin/components/exports.css +55 -0
  12. data/app/assets/stylesheets/railspress/admin/components/focal_point.css +801 -0
  13. data/app/assets/stylesheets/railspress/admin/components/imports.css +144 -0
  14. data/app/assets/stylesheets/railspress/admin/components/lexxy.css +156 -0
  15. data/app/assets/stylesheets/railspress/admin/filters.css +73 -0
  16. data/app/assets/stylesheets/railspress/admin/flash.css +26 -0
  17. data/app/assets/stylesheets/railspress/admin/forms.css +459 -0
  18. data/app/assets/stylesheets/railspress/admin/layout.css +256 -0
  19. data/app/assets/stylesheets/railspress/admin/lists.css +24 -0
  20. data/app/assets/stylesheets/railspress/admin/page.css +111 -0
  21. data/app/assets/stylesheets/railspress/admin/responsive.css +174 -0
  22. data/app/assets/stylesheets/railspress/admin/stats.css +43 -0
  23. data/app/assets/stylesheets/railspress/admin/tables.css +163 -0
  24. data/app/assets/stylesheets/railspress/admin/utilities.css +202 -0
  25. data/app/assets/stylesheets/railspress/admin/variables.css +58 -0
  26. data/app/assets/stylesheets/railspress/application.css +44 -13
  27. data/app/controllers/railspress/admin/base_controller.rb +6 -3
  28. data/app/controllers/railspress/admin/categories_controller.rb +1 -1
  29. data/app/controllers/railspress/admin/cms_transfers_controller.rb +49 -0
  30. data/app/controllers/railspress/admin/content_element_versions_controller.rb +12 -0
  31. data/app/controllers/railspress/admin/content_elements_controller.rb +143 -0
  32. data/app/controllers/railspress/admin/content_groups_controller.rb +69 -0
  33. data/app/controllers/railspress/admin/dashboard_controller.rb +6 -0
  34. data/app/controllers/railspress/admin/entities_controller.rb +157 -0
  35. data/app/controllers/railspress/admin/exports_controller.rb +55 -0
  36. data/app/controllers/railspress/admin/focal_points_controller.rb +100 -0
  37. data/app/controllers/railspress/admin/imports_controller.rb +63 -0
  38. data/app/controllers/railspress/admin/posts_controller.rb +58 -4
  39. data/app/controllers/railspress/admin/prototypes_controller.rb +30 -0
  40. data/app/controllers/railspress/admin/tags_controller.rb +1 -1
  41. data/app/controllers/railspress/application_controller.rb +1 -0
  42. data/app/helpers/railspress/admin_helper.rb +733 -0
  43. data/app/helpers/railspress/application_helper.rb +23 -0
  44. data/app/helpers/railspress/cms_helper.rb +319 -0
  45. data/app/javascript/railspress/controllers/cms_inline_editor_controller.js +147 -0
  46. data/app/javascript/railspress/controllers/content_element_form_controller.js +15 -0
  47. data/app/javascript/railspress/controllers/crop_controller.js +224 -0
  48. data/app/javascript/railspress/controllers/dropzone_controller.js +261 -0
  49. data/app/javascript/railspress/controllers/focal_point_controller.js +124 -0
  50. data/app/javascript/railspress/controllers/image_section_controller.js +94 -0
  51. data/app/javascript/railspress/controllers/index.js +37 -0
  52. data/app/javascript/railspress/index.js +62 -0
  53. data/app/jobs/railspress/export_posts_job.rb +16 -0
  54. data/app/jobs/railspress/import_posts_job.rb +44 -0
  55. data/app/models/concerns/railspress/has_focal_point.rb +242 -0
  56. data/app/models/concerns/railspress/soft_deletable.rb +23 -0
  57. data/app/models/concerns/railspress/taggable.rb +23 -0
  58. data/app/models/railspress/content_element.rb +103 -0
  59. data/app/models/railspress/content_element_version.rb +32 -0
  60. data/app/models/railspress/content_group.rb +39 -0
  61. data/app/models/railspress/export.rb +67 -0
  62. data/app/models/railspress/focal_point.rb +70 -0
  63. data/app/models/railspress/import.rb +65 -0
  64. data/app/models/railspress/post.rb +102 -15
  65. data/app/models/railspress/post_export_processor.rb +162 -0
  66. data/app/models/railspress/post_import_processor.rb +382 -0
  67. data/app/models/railspress/tag.rb +10 -3
  68. data/app/models/railspress/tagging.rb +11 -0
  69. data/app/services/railspress/content_export_service.rb +122 -0
  70. data/app/services/railspress/content_import_service.rb +228 -0
  71. data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
  72. data/app/views/active_storage/blobs/_blob.html.erb +1 -1
  73. data/app/views/layouts/railspress/admin.html.erb +3 -1
  74. data/app/views/railspress/admin/categories/index.html.erb +11 -15
  75. data/app/views/railspress/admin/cms_transfers/show.html.erb +167 -0
  76. data/app/views/railspress/admin/content_element_versions/show.html.erb +42 -0
  77. data/app/views/railspress/admin/content_elements/_form.html.erb +71 -0
  78. data/app/views/railspress/admin/content_elements/_inline_form.html.erb +32 -0
  79. data/app/views/railspress/admin/content_elements/_inline_form_frame.html.erb +6 -0
  80. data/app/views/railspress/admin/content_elements/edit.html.erb +6 -0
  81. data/app/views/railspress/admin/content_elements/index.html.erb +74 -0
  82. data/app/views/railspress/admin/content_elements/new.html.erb +6 -0
  83. data/app/views/railspress/admin/content_elements/show.html.erb +124 -0
  84. data/app/views/railspress/admin/content_groups/_form.html.erb +9 -0
  85. data/app/views/railspress/admin/content_groups/edit.html.erb +6 -0
  86. data/app/views/railspress/admin/content_groups/index.html.erb +42 -0
  87. data/app/views/railspress/admin/content_groups/new.html.erb +6 -0
  88. data/app/views/railspress/admin/content_groups/show.html.erb +92 -0
  89. data/app/views/railspress/admin/dashboard/index.html.erb +36 -1
  90. data/app/views/railspress/admin/entities/_form.html.erb +53 -0
  91. data/app/views/railspress/admin/entities/edit.html.erb +4 -0
  92. data/app/views/railspress/admin/entities/index.html.erb +74 -0
  93. data/app/views/railspress/admin/entities/new.html.erb +4 -0
  94. data/app/views/railspress/admin/entities/show.html.erb +117 -0
  95. data/app/views/railspress/admin/exports/show.html.erb +62 -0
  96. data/app/views/railspress/admin/imports/_instructions.html.erb +56 -0
  97. data/app/views/railspress/admin/imports/show.html.erb +137 -0
  98. data/app/views/railspress/admin/posts/_form.html.erb +102 -28
  99. data/app/views/railspress/admin/posts/_post_row.html.erb +40 -0
  100. data/app/views/railspress/admin/posts/index.html.erb +47 -36
  101. data/app/views/railspress/admin/posts/show.html.erb +55 -19
  102. data/app/views/railspress/admin/prototypes/image_section.html.erb +42 -0
  103. data/app/views/railspress/admin/shared/_dropzone.html.erb +84 -0
  104. data/app/views/railspress/admin/shared/_focal_point_editor.html.erb +102 -0
  105. data/app/views/railspress/admin/shared/_image_section.html.erb +159 -0
  106. data/app/views/railspress/admin/shared/_image_section_compact.html.erb +90 -0
  107. data/app/views/railspress/admin/shared/_image_section_editor.html.erb +171 -0
  108. data/app/views/railspress/admin/shared/_image_section_v2.html.erb +205 -0
  109. data/app/views/railspress/admin/shared/_sidebar.html.erb +73 -5
  110. data/app/views/railspress/admin/tags/index.html.erb +12 -16
  111. data/config/brakeman.ignore +18 -0
  112. data/config/importmap.rb +23 -0
  113. data/config/routes.rb +62 -1
  114. data/db/migrate/20241218000004_create_railspress_post_tags.rb +1 -1
  115. data/db/migrate/20241218000005_create_railspress_imports.rb +21 -0
  116. data/db/migrate/20241218000006_create_railspress_exports.rb +20 -0
  117. data/db/migrate/20241218000007_create_railspress_taggings.rb +20 -0
  118. data/db/migrate/20241218000008_drop_railspress_post_tags.rb +14 -0
  119. data/db/migrate/20241218000010_add_reading_time_to_railspress_posts.rb +5 -0
  120. data/db/migrate/20250105000002_create_railspress_focal_points.rb +20 -0
  121. data/db/migrate/20260206000001_create_railspress_content_groups.rb +18 -0
  122. data/db/migrate/20260206000002_create_railspress_content_elements.rb +21 -0
  123. data/db/migrate/20260206000003_create_railspress_content_element_versions.rb +20 -0
  124. data/db/migrate/20260207000001_add_unique_index_to_content_elements.rb +11 -0
  125. data/db/migrate/20260211112812_add_image_hint_to_railspress_content_elements.rb +7 -0
  126. data/db/migrate/20260211154040_add_required_to_railspress_content_elements.rb +5 -0
  127. data/lib/generators/railspress/entity/entity_generator.rb +89 -0
  128. data/lib/generators/railspress/entity/templates/migration.rb.tt +13 -0
  129. data/lib/generators/railspress/entity/templates/model.rb.tt +21 -0
  130. data/lib/generators/railspress/install/install_generator.rb +51 -40
  131. data/lib/generators/railspress/install/templates/initializer.rb +29 -0
  132. data/lib/railspress/engine.rb +38 -0
  133. data/lib/railspress/entity.rb +239 -0
  134. data/lib/railspress/version.rb +1 -1
  135. data/lib/railspress.rb +198 -8
  136. data/lib/tasks/railspress_tasks.rake +49 -4
  137. metadata +215 -21
  138. data/MIT-LICENSE +0 -20
  139. data/app/assets/stylesheets/railspress/admin.css +0 -1207
  140. data/app/models/railspress/post_tag.rb +0 -8
@@ -0,0 +1,144 @@
1
+ /*
2
+ * RailsPress Admin - Imports
3
+ * Import page layout and dropzone styles
4
+ */
5
+
6
+ .rp-import-layout {
7
+ display: grid;
8
+ grid-template-columns: 1fr 320px;
9
+ gap: var(--rp-space-xl);
10
+ align-items: start;
11
+ }
12
+
13
+ .rp-import-main {
14
+ min-width: 0;
15
+ }
16
+
17
+ .rp-import-sidebar {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: var(--rp-space-md);
21
+ }
22
+
23
+ /* Dropzone */
24
+ .rp-dropzone {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ justify-content: center;
29
+ gap: var(--rp-space-md);
30
+ padding: var(--rp-space-2xl);
31
+ border: 2px dashed var(--rp-border);
32
+ border-radius: var(--rp-radius);
33
+ background: var(--rp-bg);
34
+ cursor: pointer;
35
+ transition: all 0.15s ease;
36
+ text-align: center;
37
+ }
38
+
39
+ .rp-dropzone:hover {
40
+ border-color: var(--rp-primary);
41
+ background: var(--rp-primary-light);
42
+ }
43
+
44
+ .rp-dropzone-icon {
45
+ color: var(--rp-text-muted);
46
+ transition: color 0.15s ease;
47
+ }
48
+
49
+ .rp-dropzone:hover .rp-dropzone-icon {
50
+ color: var(--rp-primary);
51
+ }
52
+
53
+ .rp-dropzone-text {
54
+ display: flex;
55
+ flex-direction: column;
56
+ gap: var(--rp-space-xs);
57
+ }
58
+
59
+ .rp-dropzone-text strong {
60
+ font-size: 1rem;
61
+ color: var(--rp-text);
62
+ }
63
+
64
+ .rp-dropzone-text span {
65
+ font-size: 0.875rem;
66
+ color: var(--rp-text-muted);
67
+ }
68
+
69
+ .rp-dropzone-input {
70
+ position: absolute;
71
+ width: 1px;
72
+ height: 1px;
73
+ padding: 0;
74
+ margin: -1px;
75
+ overflow: hidden;
76
+ clip: rect(0, 0, 0, 0);
77
+ border: 0;
78
+ }
79
+
80
+ .rp-dropzone--active {
81
+ border-color: var(--rp-primary);
82
+ background: var(--rp-primary-light);
83
+ border-style: solid;
84
+ }
85
+
86
+ .rp-dropzone--active .rp-dropzone-icon {
87
+ color: var(--rp-primary);
88
+ }
89
+
90
+ /* File list */
91
+ .rp-file-list {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: var(--rp-space-xs);
95
+ margin-top: var(--rp-space-md);
96
+ padding: var(--rp-space-sm);
97
+ background: var(--rp-gray-50);
98
+ border-radius: var(--rp-radius);
99
+ }
100
+
101
+ .rp-file-item {
102
+ display: flex;
103
+ justify-content: space-between;
104
+ align-items: center;
105
+ padding: var(--rp-space-xs) var(--rp-space-sm);
106
+ background: white;
107
+ border-radius: var(--rp-radius-sm);
108
+ font-size: 0.875rem;
109
+ }
110
+
111
+ .rp-file-name {
112
+ color: var(--rp-text);
113
+ font-weight: 500;
114
+ }
115
+
116
+ .rp-file-size {
117
+ color: var(--rp-text-muted);
118
+ font-size: 0.75rem;
119
+ }
120
+
121
+ /* Import list */
122
+ .rp-import-list {
123
+ list-style: none;
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: var(--rp-space-xs);
127
+ font-size: 0.875rem;
128
+ color: var(--rp-text-muted);
129
+ }
130
+
131
+ .rp-import-list strong {
132
+ color: var(--rp-text);
133
+ font-weight: 600;
134
+ }
135
+
136
+ @media (max-width: 1023px) {
137
+ .rp-import-layout {
138
+ grid-template-columns: 1fr;
139
+ }
140
+
141
+ .rp-import-sidebar {
142
+ order: 1;
143
+ }
144
+ }
@@ -0,0 +1,156 @@
1
+ /*
2
+ * RailsPress Admin - Lexxy Editor
3
+ * Rich text editor overrides and styling
4
+ */
5
+
6
+ :where(lexxy-toolbar){
7
+ position: sticky;
8
+ top: 0;
9
+ z-index: 10;
10
+ background: var(--rp-bg-elevated, #fff);
11
+ margin-bottom: 20px;
12
+ padding-bottom: 10px;
13
+ border-bottom: 1px solid var(--rp-border);
14
+ }
15
+
16
+ /*
17
+ * Disabled custom Lexxy dropdown sizing override.
18
+ * Lexxy 0.9.8.beta manages dropdown layout internally; forcing fixed 34x34
19
+ * sizing can break toolbar dropdown/picker rendering.
20
+ */
21
+ /*
22
+ .lexxy-editor__toolbar-dropdown {
23
+ width: 34px !important;
24
+ height: 34px !important;
25
+ display: inline-flex !important;
26
+ align-items: center;
27
+ justify-content: center;
28
+ }
29
+
30
+ .lexxy-editor__toolbar-dropdown > summary {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ width: 100%;
35
+ height: 100%;
36
+ list-style: none;
37
+ cursor: pointer;
38
+ }
39
+
40
+ .lexxy-editor__toolbar-dropdown > summary::-webkit-details-marker {
41
+ display: none;
42
+ }
43
+ */
44
+
45
+ /* Rich text content spacing */
46
+ .rp-rich-text p,
47
+ .rp-rich-text blockquote,
48
+ .rp-rich-text ul,
49
+ .rp-rich-text ol,
50
+ .rp-rich-text h1,
51
+ .rp-rich-text h2,
52
+ .rp-rich-text h3,
53
+ .rp-rich-text h4,
54
+ .rp-rich-text pre,
55
+ .rp-rich-text figure {
56
+ margin-bottom: var(--rp-space-md);
57
+ }
58
+
59
+ .rp-rich-text > *:last-child {
60
+ margin-bottom: 0;
61
+ }
62
+
63
+ .rp-rich-text blockquote {
64
+ border-left: 3px solid var(--rp-border);
65
+ padding-left: var(--rp-space-md);
66
+ color: var(--rp-text-muted);
67
+ font-style: italic;
68
+ }
69
+
70
+ .rp-rich-text ul, ol {
71
+ padding-left: 16px;
72
+ }
73
+
74
+ /* ============================================
75
+ * Markdown Mode
76
+ * ============================================ */
77
+
78
+ /* Editor header with label and toggle */
79
+ .rp-editor-header {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ margin-bottom: var(--rp-space-sm);
84
+ }
85
+
86
+ .rp-editor-header .rp-label {
87
+ margin-bottom: 0;
88
+ }
89
+
90
+ /* Mode toggle button */
91
+ .rp-editor-mode-toggle {
92
+ flex-shrink: 0;
93
+ }
94
+
95
+ .rp-btn--ghost {
96
+ background: transparent;
97
+ border: 1px solid var(--rp-border);
98
+ color: var(--rp-text-muted);
99
+ }
100
+
101
+ .rp-btn--ghost:hover {
102
+ background: var(--rp-bg-subtle);
103
+ color: var(--rp-text);
104
+ border-color: var(--rp-border-hover);
105
+ }
106
+
107
+ .rp-btn--ghost[aria-pressed="true"] {
108
+ background: var(--rp-primary);
109
+ color: white;
110
+ border-color: var(--rp-primary);
111
+ }
112
+
113
+ .rp-btn--ghost svg {
114
+ vertical-align: middle;
115
+ margin-right: 4px;
116
+ }
117
+
118
+ .rp-btn--ghost span {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ }
122
+
123
+ /* Markdown textarea */
124
+ .rp-markdown-editor {
125
+ width: 100%;
126
+ min-height: 400px;
127
+ padding: var(--rp-space-md);
128
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
129
+ font-size: 14px;
130
+ line-height: 1.6;
131
+ border: 1px solid var(--rp-border);
132
+ border-radius: var(--rp-radius);
133
+ background: var(--rp-bg-elevated);
134
+ color: var(--rp-text);
135
+ resize: vertical;
136
+ tab-size: 2;
137
+ }
138
+
139
+ .rp-markdown-editor:focus {
140
+ outline: none;
141
+ border-color: var(--rp-primary);
142
+ box-shadow: 0 0 0 3px rgba(var(--rp-primary-rgb), 0.1);
143
+ }
144
+
145
+ .rp-markdown-editor::placeholder {
146
+ color: var(--rp-text-muted);
147
+ }
148
+
149
+ /* Hide hint labels based on mode */
150
+ [data-markdown-mode] [data-mode-label="markdown"][hidden] {
151
+ display: none;
152
+ }
153
+
154
+ [data-markdown-mode] [data-mode-label="rich"][hidden] {
155
+ display: none;
156
+ }
@@ -0,0 +1,73 @@
1
+ /*
2
+ * RailsPress Admin - Filters & Pagination
3
+ * Filter bar and pagination components
4
+ */
5
+
6
+ /* Filter Bar */
7
+ .rp-filter-bar {
8
+ display: flex;
9
+ align-items: center;
10
+ gap: var(--rp-space-md);
11
+ padding: var(--rp-space-md);
12
+ border-bottom: 1px solid var(--rp-border-light);
13
+ }
14
+
15
+ .rp-filter-bar__search {
16
+ flex: 1;
17
+ max-width: 300px;
18
+ }
19
+
20
+ .rp-filter-bar__filters {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: var(--rp-space-sm);
24
+ }
25
+
26
+ .rp-filter-bar .rp-select {
27
+ width: auto;
28
+ min-width: 140px;
29
+ }
30
+
31
+ /* Pagination */
32
+ .rp-pagination {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ padding: var(--rp-space-md);
37
+ border-top: 1px solid var(--rp-border-light);
38
+ }
39
+
40
+ .rp-pagination__info {
41
+ font-size: 0.875rem;
42
+ color: var(--rp-text-muted);
43
+ }
44
+
45
+ .rp-pagination__links {
46
+ display: flex;
47
+ gap: var(--rp-space-sm);
48
+ }
49
+
50
+ @media (max-width: 767px) {
51
+ .rp-filter-bar {
52
+ flex-direction: column;
53
+ align-items: stretch;
54
+ }
55
+
56
+ .rp-filter-bar__search {
57
+ max-width: none;
58
+ }
59
+
60
+ .rp-filter-bar__filters {
61
+ flex-wrap: wrap;
62
+ }
63
+
64
+ .rp-filter-bar .rp-select {
65
+ flex: 1;
66
+ min-width: 0;
67
+ }
68
+
69
+ .rp-pagination {
70
+ flex-direction: column;
71
+ gap: var(--rp-space-sm);
72
+ }
73
+ }
@@ -0,0 +1,26 @@
1
+ /*
2
+ * RailsPress Admin - Flash Messages
3
+ * Notification and alert styles
4
+ */
5
+
6
+ .rp-flash {
7
+ padding: var(--rp-space-md) var(--rp-space-lg);
8
+ border-radius: var(--rp-radius);
9
+ margin-bottom: var(--rp-space-lg);
10
+ font-weight: 500;
11
+ display: flex;
12
+ align-items: center;
13
+ gap: var(--rp-space-sm);
14
+ }
15
+
16
+ .rp-flash--notice {
17
+ background: var(--rp-success-light);
18
+ color: var(--rp-success);
19
+ border: 1px solid var(--rp-success);
20
+ }
21
+
22
+ .rp-flash--alert {
23
+ background: var(--rp-danger-light);
24
+ color: var(--rp-danger);
25
+ border: 1px solid var(--rp-danger);
26
+ }