neon_sakura 0.1.4

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 (251) hide show
  1. checksums.yaml +7 -0
  2. data/.ai-reviewer/README.md +182 -0
  3. data/.ai-reviewer/ai-reviewer.sh +56 -0
  4. data/.ai-reviewer/build-system-prompt.sh +136 -0
  5. data/.ai-reviewer/extract-claude-sections.sh +32 -0
  6. data/.ai-reviewer/test-ai-reviewer.sh +40 -0
  7. data/.ai-reviewer-config.yml +190 -0
  8. data/.github/dependabot.yml +12 -0
  9. data/.github/settings.yml +70 -0
  10. data/.github/workflows/ai-pr-review-on-comment.yml +384 -0
  11. data/.github/workflows/ai-pr-review.yml +328 -0
  12. data/.github/workflows/license-check.yml +78 -0
  13. data/.github/workflows/lint.yml +79 -0
  14. data/.github/workflows/security.yml +131 -0
  15. data/.github/workflows/semgrep.yml +26 -0
  16. data/.github/workflows/test.yml +44 -0
  17. data/.gitignore +75 -0
  18. data/.rubocop.yml +33 -0
  19. data/.ruby-version +1 -0
  20. data/.simplecov +14 -0
  21. data/.stylelintignore +10 -0
  22. data/.stylelintrc.json +37 -0
  23. data/AGENTS.md +51 -0
  24. data/CHANGELOG.md +568 -0
  25. data/CLAUDE.md +632 -0
  26. data/Gemfile +8 -0
  27. data/Gemfile.lock +327 -0
  28. data/LICENSE +21 -0
  29. data/README.md +1209 -0
  30. data/Rakefile +25 -0
  31. data/app/assets/images/cherry_blossom.svg +1525 -0
  32. data/app/assets/images/cherry_blossom_tree.png +0 -0
  33. data/app/assets/images/prysm-icon.png +0 -0
  34. data/app/assets/stylesheets/base.css +29 -0
  35. data/app/assets/stylesheets/components.css +1652 -0
  36. data/app/assets/stylesheets/forms.css +152 -0
  37. data/app/assets/stylesheets/loading.css +145 -0
  38. data/app/assets/stylesheets/neon_sakura.css +40 -0
  39. data/app/assets/stylesheets/pagy-tailwind.css +120 -0
  40. data/app/assets/stylesheets/theme-default.css +40 -0
  41. data/app/assets/stylesheets/theme-green.css +84 -0
  42. data/app/assets/stylesheets/theme-purple.css +94 -0
  43. data/app/assets/stylesheets/theme-red.css +84 -0
  44. data/app/assets/stylesheets/utility-borders.css +29 -0
  45. data/app/assets/stylesheets/utility-colors.css +185 -0
  46. data/app/assets/stylesheets/utility-effects.css +123 -0
  47. data/app/assets/stylesheets/utility-gradients.css +158 -0
  48. data/app/assets/stylesheets/utility-layout.css +132 -0
  49. data/app/assets/stylesheets/utility-reset.css +13 -0
  50. data/app/assets/stylesheets/utility-responsive.css +145 -0
  51. data/app/assets/stylesheets/utility-sizing.css +99 -0
  52. data/app/assets/stylesheets/utility-spacing.css +174 -0
  53. data/app/assets/stylesheets/utility-typography.css +97 -0
  54. data/app/controllers/errors_controller.rb +120 -0
  55. data/app/controllers/style_guide_controller.rb +117 -0
  56. data/app/helpers/errors_helper.rb +12 -0
  57. data/app/helpers/neon_sakura/navbar_helper.rb +43 -0
  58. data/app/helpers/style_guide_helper.rb +36 -0
  59. data/app/javascript/neon_sakura/dropdown.js +22 -0
  60. data/app/javascript/neon_sakura/navbar.js +71 -0
  61. data/app/javascript/neon_sakura/theme_switcher.js +187 -0
  62. data/app/views/errors/show.html.erb +105 -0
  63. data/app/views/layouts/error.html.erb +19 -0
  64. data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
  65. data/app/views/layouts/mission_control/jobs/_navigation.html.erb +21 -0
  66. data/app/views/layouts/mission_control/jobs/application.html.erb +453 -0
  67. data/app/views/layouts/style_guide.html.erb +416 -0
  68. data/app/views/shared/_file_upload.html.erb +184 -0
  69. data/app/views/shared/_footer.html.erb +23 -0
  70. data/app/views/shared/_header.html.erb +42 -0
  71. data/app/views/shared/_navbar.html.erb +306 -0
  72. data/app/views/shared/_profile_image_selector.html.erb +165 -0
  73. data/app/views/shared/_theme_switcher.html.erb +64 -0
  74. data/app/views/shared/icons/_adjustments.html.erb +10 -0
  75. data/app/views/shared/icons/_alert_circle.html.erb +3 -0
  76. data/app/views/shared/icons/_alert_triangle.html.erb +3 -0
  77. data/app/views/shared/icons/_archive.html.erb +3 -0
  78. data/app/views/shared/icons/_arrow_down.html.erb +3 -0
  79. data/app/views/shared/icons/_arrow_left.html.erb +3 -0
  80. data/app/views/shared/icons/_arrow_up.html.erb +3 -0
  81. data/app/views/shared/icons/_arrows_pointing_in.html.erb +10 -0
  82. data/app/views/shared/icons/_arrows_pointing_out.html.erb +10 -0
  83. data/app/views/shared/icons/_artemis_logo.html.erb +26 -0
  84. data/app/views/shared/icons/_auth_banner.html.erb +1 -0
  85. data/app/views/shared/icons/_bars.html.erb +10 -0
  86. data/app/views/shared/icons/_bell.html.erb +3 -0
  87. data/app/views/shared/icons/_book.html.erb +3 -0
  88. data/app/views/shared/icons/_bookmark.html.erb +3 -0
  89. data/app/views/shared/icons/_box.html.erb +3 -0
  90. data/app/views/shared/icons/_brain.html.erb +3 -0
  91. data/app/views/shared/icons/_briefcase.html.erb +3 -0
  92. data/app/views/shared/icons/_calendar.html.erb +3 -0
  93. data/app/views/shared/icons/_camera.html.erb +4 -0
  94. data/app/views/shared/icons/_chart_bar.html.erb +3 -0
  95. data/app/views/shared/icons/_chart_line.html.erb +10 -0
  96. data/app/views/shared/icons/_chart_pie.html.erb +11 -0
  97. data/app/views/shared/icons/_chat.html.erb +3 -0
  98. data/app/views/shared/icons/_check.html.erb +3 -0
  99. data/app/views/shared/icons/_check_circle.html.erb +3 -0
  100. data/app/views/shared/icons/_cherry_blossom.html.erb +1516 -0
  101. data/app/views/shared/icons/_cherry_blossom_silhouette.html.erb +1016 -0
  102. data/app/views/shared/icons/_cherry_blossom_single_flower.html.erb +1125 -0
  103. data/app/views/shared/icons/_cherry_blossom_tree.html.erb +159 -0
  104. data/app/views/shared/icons/_chevron_down.html.erb +3 -0
  105. data/app/views/shared/icons/_chevron_right.html.erb +9 -0
  106. data/app/views/shared/icons/_clipboard.html.erb +3 -0
  107. data/app/views/shared/icons/_clock.html.erb +3 -0
  108. data/app/views/shared/icons/_close.html.erb +3 -0
  109. data/app/views/shared/icons/_cog.html.erb +4 -0
  110. data/app/views/shared/icons/_crop.html.erb +10 -0
  111. data/app/views/shared/icons/_crown.html.erb +3 -0
  112. data/app/views/shared/icons/_disc.html.erb +3 -0
  113. data/app/views/shared/icons/_download.html.erb +3 -0
  114. data/app/views/shared/icons/_dragonfly.html.erb +58 -0
  115. data/app/views/shared/icons/_duplicate.html.erb +4 -0
  116. data/app/views/shared/icons/_edit.html.erb +3 -0
  117. data/app/views/shared/icons/_envelope.html.erb +3 -0
  118. data/app/views/shared/icons/_eraser.html.erb +10 -0
  119. data/app/views/shared/icons/_external_link.html.erb +3 -0
  120. data/app/views/shared/icons/_eye.html.erb +4 -0
  121. data/app/views/shared/icons/_file_csv.html.erb +10 -0
  122. data/app/views/shared/icons/_file_export.html.erb +10 -0
  123. data/app/views/shared/icons/_file_image.html.erb +10 -0
  124. data/app/views/shared/icons/_file_import.html.erb +10 -0
  125. data/app/views/shared/icons/_file_question.html.erb +6 -0
  126. data/app/views/shared/icons/_film.html.erb +3 -0
  127. data/app/views/shared/icons/_filter.html.erb +3 -0
  128. data/app/views/shared/icons/_folder.html.erb +3 -0
  129. data/app/views/shared/icons/_folder_open.html.erb +3 -0
  130. data/app/views/shared/icons/_folder_plus.html.erb +3 -0
  131. data/app/views/shared/icons/_globe.html.erb +3 -0
  132. data/app/views/shared/icons/_google.html.erb +11 -0
  133. data/app/views/shared/icons/_heart.html.erb +3 -0
  134. data/app/views/shared/icons/_heart_broken.html.erb +11 -0
  135. data/app/views/shared/icons/_heart_pulse.html.erb +4 -0
  136. data/app/views/shared/icons/_history.html.erb +11 -0
  137. data/app/views/shared/icons/_home.html.erb +10 -0
  138. data/app/views/shared/icons/_image.html.erb +3 -0
  139. data/app/views/shared/icons/_inbox.html.erb +3 -0
  140. data/app/views/shared/icons/_info_circle.html.erb +10 -0
  141. data/app/views/shared/icons/_key.html.erb +3 -0
  142. data/app/views/shared/icons/_layers.html.erb +10 -0
  143. data/app/views/shared/icons/_lightbulb.html.erb +10 -0
  144. data/app/views/shared/icons/_lightning.html.erb +3 -0
  145. data/app/views/shared/icons/_list.html.erb +3 -0
  146. data/app/views/shared/icons/_lock.html.erb +3 -0
  147. data/app/views/shared/icons/_logout.html.erb +3 -0
  148. data/app/views/shared/icons/_magazine.html.erb +3 -0
  149. data/app/views/shared/icons/_magic.html.erb +3 -0
  150. data/app/views/shared/icons/_minus.html.erb +10 -0
  151. data/app/views/shared/icons/_mobile.html.erb +10 -0
  152. data/app/views/shared/icons/_moon.html.erb +3 -0
  153. data/app/views/shared/icons/_network.html.erb +10 -0
  154. data/app/views/shared/icons/_new_item_banner.html.erb +1 -0
  155. data/app/views/shared/icons/_ouroboros.html.erb +24 -0
  156. data/app/views/shared/icons/_package.html.erb +3 -0
  157. data/app/views/shared/icons/_palette.html.erb +3 -0
  158. data/app/views/shared/icons/_paper_plane.html.erb +10 -0
  159. data/app/views/shared/icons/_photo.html.erb +10 -0
  160. data/app/views/shared/icons/_play.html.erb +4 -0
  161. data/app/views/shared/icons/_plus.html.erb +3 -0
  162. data/app/views/shared/icons/_pocket.html.erb +11 -0
  163. data/app/views/shared/icons/_prysm-icon.html.erb +34 -0
  164. data/app/views/shared/icons/_prysm.html.erb +13 -0
  165. data/app/views/shared/icons/_pushbullet-1.html.erb +29 -0
  166. data/app/views/shared/icons/_pushbullet-2.html.erb +2 -0
  167. data/app/views/shared/icons/_puzzle.html.erb +10 -0
  168. data/app/views/shared/icons/_qrcode.html.erb +3 -0
  169. data/app/views/shared/icons/_question.html.erb +3 -0
  170. data/app/views/shared/icons/_receipt.html.erb +10 -0
  171. data/app/views/shared/icons/_redo.html.erb +3 -0
  172. data/app/views/shared/icons/_refresh.html.erb +3 -0
  173. data/app/views/shared/icons/_rocket.html.erb +10 -0
  174. data/app/views/shared/icons/_rss.html.erb +3 -0
  175. data/app/views/shared/icons/_save.html.erb +3 -0
  176. data/app/views/shared/icons/_search.html.erb +3 -0
  177. data/app/views/shared/icons/_search_minus.html.erb +10 -0
  178. data/app/views/shared/icons/_search_plus.html.erb +10 -0
  179. data/app/views/shared/icons/_server_error.html.erb +6 -0
  180. data/app/views/shared/icons/_share.html.erb +3 -0
  181. data/app/views/shared/icons/_shield_check.html.erb +3 -0
  182. data/app/views/shared/icons/_sign_in.html.erb +3 -0
  183. data/app/views/shared/icons/_spinner.html.erb +4 -0
  184. data/app/views/shared/icons/_star.html.erb +3 -0
  185. data/app/views/shared/icons/_store.html.erb +10 -0
  186. data/app/views/shared/icons/_sun.html.erb +3 -0
  187. data/app/views/shared/icons/_sync.html.erb +3 -0
  188. data/app/views/shared/icons/_table.html.erb +3 -0
  189. data/app/views/shared/icons/_tag.html.erb +3 -0
  190. data/app/views/shared/icons/_tags.html.erb +11 -0
  191. data/app/views/shared/icons/_tools.html.erb +4 -0
  192. data/app/views/shared/icons/_trash.html.erb +3 -0
  193. data/app/views/shared/icons/_undo.html.erb +3 -0
  194. data/app/views/shared/icons/_unlock.html.erb +3 -0
  195. data/app/views/shared/icons/_upload.html.erb +3 -0
  196. data/app/views/shared/icons/_user.html.erb +3 -0
  197. data/app/views/shared/icons/_user_circle.html.erb +10 -0
  198. data/app/views/shared/icons/_user_plus.html.erb +10 -0
  199. data/app/views/shared/icons/_video.html.erb +3 -0
  200. data/app/views/shared/icons/_wrench.html.erb +11 -0
  201. data/app/views/style_guide/index.html.erb +77 -0
  202. data/app/views/style_guide/sections/_alerts.html.erb +114 -0
  203. data/app/views/style_guide/sections/_badges.html.erb +78 -0
  204. data/app/views/style_guide/sections/_buttons.html.erb +130 -0
  205. data/app/views/style_guide/sections/_cards.html.erb +84 -0
  206. data/app/views/style_guide/sections/_colors.html.erb +106 -0
  207. data/app/views/style_guide/sections/_file_upload.html.erb +135 -0
  208. data/app/views/style_guide/sections/_forms.html.erb +129 -0
  209. data/app/views/style_guide/sections/_gradients.html.erb +253 -0
  210. data/app/views/style_guide/sections/_header.html.erb +12 -0
  211. data/app/views/style_guide/sections/_icons.html.erb +55 -0
  212. data/app/views/style_guide/sections/_images.html.erb +40 -0
  213. data/app/views/style_guide/sections/_loading.html.erb +242 -0
  214. data/app/views/style_guide/sections/_pagination.html.erb +212 -0
  215. data/app/views/style_guide/sections/_profile_components.html.erb +203 -0
  216. data/app/views/style_guide/sections/_theme_switcher.html.erb +72 -0
  217. data/app/views/style_guide/sections/_typography.html.erb +65 -0
  218. data/bin/ai-optimize-claude-md +540 -0
  219. data/bin/ai-review-local +345 -0
  220. data/bin/ai-security-review +585 -0
  221. data/bin/brakeman +9 -0
  222. data/bin/install-hooks +57 -0
  223. data/bin/rake +7 -0
  224. data/bin/rubocop +10 -0
  225. data/bin/verify_setup.rb +31 -0
  226. data/config/brakeman.ignore +28 -0
  227. data/config/initializers/neon_sakura.rb +15 -0
  228. data/config/license_overrides.yml +13 -0
  229. data/config/routes.rb +21 -0
  230. data/config/theme_mappings.yml +61 -0
  231. data/docs/PRYSM_ASSETS.md +210 -0
  232. data/docs/plans/extract_ai_reviewer_plan.md +151 -0
  233. data/docs/plans/neon_sakura_gem_plan.md +138 -0
  234. data/lib/neon_sakura/configuration.rb +94 -0
  235. data/lib/neon_sakura/engine.rb +48 -0
  236. data/lib/neon_sakura/icon_helper.rb +54 -0
  237. data/lib/neon_sakura/profile_helper.rb +24 -0
  238. data/lib/neon_sakura/stylesheet_helper.rb +40 -0
  239. data/lib/neon_sakura/theme_helper.rb +63 -0
  240. data/lib/neon_sakura/theme_importer.rb +112 -0
  241. data/lib/neon_sakura/version.rb +5 -0
  242. data/lib/neon_sakura.rb +13 -0
  243. data/neon_sakura.gemspec +50 -0
  244. data/package.json +18 -0
  245. data/scripts/git-hooks/post-merge +132 -0
  246. data/scripts/git-hooks/pre-commit +123 -0
  247. data/scripts/git-hooks/pre-push +127 -0
  248. data/scripts/license-check.rb +587 -0
  249. data/settings.local.json +12 -0
  250. data/yarn.lock +778 -0
  251. metadata +503 -0
@@ -0,0 +1,145 @@
1
+ /*
2
+ * Responsive Utilities
3
+ * This file contains media query utilities for responsive design
4
+ */
5
+
6
+ /* Responsive utilities */
7
+ @media (width >= 640px) {
8
+ .sm\:py-16 {
9
+ padding-top: 4rem;
10
+ padding-bottom: 4rem;
11
+ }
12
+
13
+ .sm\:px-6 {
14
+ padding-left: 1.5rem;
15
+ padding-right: 1.5rem;
16
+ }
17
+
18
+ .sm\:flex-row {
19
+ flex-direction: row;
20
+ }
21
+
22
+ .sm\:mr-4 {
23
+ margin-right: 1rem;
24
+ }
25
+
26
+ .sm\:mb-0 {
27
+ margin-bottom: 0;
28
+ }
29
+
30
+ .sm\:mt-12 {
31
+ margin-top: 3rem;
32
+ }
33
+
34
+ .sm\:text-sm {
35
+ font-size: 0.875rem;
36
+ line-height: 1.25rem;
37
+ }
38
+
39
+ .sm\:text-base {
40
+ font-size: 1rem;
41
+ line-height: 1.5rem;
42
+ }
43
+
44
+ .sm\:text-lg {
45
+ font-size: 1.125rem;
46
+ line-height: 1.75rem;
47
+ }
48
+
49
+ .sm\:text-2xl {
50
+ font-size: 1.5rem;
51
+ line-height: 2rem;
52
+ }
53
+
54
+ .sm\:text-3xl {
55
+ font-size: 1.875rem;
56
+ line-height: 2.25rem;
57
+ }
58
+
59
+ .sm\:text-4xl {
60
+ font-size: 2.25rem;
61
+ line-height: 2.5rem;
62
+ }
63
+
64
+ .sm\:grid-cols-2 {
65
+ grid-template-columns: repeat(2, minmax(0, 1fr));
66
+ }
67
+
68
+ .sm\:grid-cols-3 {
69
+ grid-template-columns: repeat(3, minmax(0, 1fr));
70
+ }
71
+
72
+ .sm\:grid-cols-4 {
73
+ grid-template-columns: repeat(4, minmax(0, 1fr));
74
+ }
75
+
76
+ .sm\:gap-4 {
77
+ gap: 1rem;
78
+ }
79
+
80
+ .sm\:gap-5 {
81
+ gap: 1.25rem;
82
+ }
83
+
84
+ .sm\:p-4 {
85
+ padding: 1rem;
86
+ }
87
+
88
+ .sm\:inline {
89
+ display: inline;
90
+ }
91
+
92
+ .sm\:items-center {
93
+ align-items: center;
94
+ }
95
+ }
96
+
97
+ @media (width >= 768px) {
98
+ .md\:text-5xl {
99
+ font-size: 3rem;
100
+ line-height: 1;
101
+ }
102
+
103
+ .md\:gap-6 {
104
+ gap: 1.5rem;
105
+ }
106
+
107
+ .md\:grid-cols-2 {
108
+ grid-template-columns: repeat(2, minmax(0, 1fr));
109
+ }
110
+
111
+ .md\:grid-cols-3 {
112
+ grid-template-columns: repeat(3, minmax(0, 1fr));
113
+ }
114
+
115
+ .md\:grid-cols-4 {
116
+ grid-template-columns: repeat(4, minmax(0, 1fr));
117
+ }
118
+ }
119
+
120
+ @media (width >= 1024px) {
121
+ .lg\:px-8 {
122
+ padding-left: 2rem;
123
+ padding-right: 2rem;
124
+ }
125
+
126
+ .lg\:grid-cols-2 {
127
+ grid-template-columns: repeat(2, minmax(0, 1fr));
128
+ }
129
+
130
+ .lg\:grid-cols-3 {
131
+ grid-template-columns: repeat(3, minmax(0, 1fr));
132
+ }
133
+
134
+ .lg\:grid-cols-4 {
135
+ grid-template-columns: repeat(4, minmax(0, 1fr));
136
+ }
137
+
138
+ .lg\:grid-cols-5 {
139
+ grid-template-columns: repeat(5, minmax(0, 1fr));
140
+ }
141
+
142
+ .lg\:flex {
143
+ display: flex;
144
+ }
145
+ }
@@ -0,0 +1,99 @@
1
+ /*
2
+ * Sizing Utilities
3
+ * This file contains width, height, and object-fit utility classes
4
+ */
5
+
6
+ /* Sizing utilities */
7
+ .w-full {
8
+ width: 100%;
9
+ }
10
+
11
+ .w-2\.5 {
12
+ width: 0.625rem;
13
+ }
14
+
15
+ .w-3 {
16
+ width: 0.75rem;
17
+ }
18
+
19
+ .w-4 {
20
+ width: 1rem;
21
+ }
22
+
23
+ .w-5 {
24
+ width: 1.25rem;
25
+ }
26
+
27
+ .w-6 {
28
+ width: 1.5rem;
29
+ }
30
+
31
+ .w-8 {
32
+ width: 2rem;
33
+ }
34
+
35
+ .w-12 {
36
+ width: 3rem;
37
+ }
38
+
39
+ .w-24 {
40
+ width: 6rem;
41
+ }
42
+
43
+ .w-32 {
44
+ width: 8rem;
45
+ }
46
+
47
+ .w-40 {
48
+ width: 10rem;
49
+ }
50
+
51
+ .h-2\.5 {
52
+ height: 0.625rem;
53
+ }
54
+
55
+ .h-3 {
56
+ height: 0.75rem;
57
+ }
58
+
59
+ .h-4 {
60
+ height: 1rem;
61
+ }
62
+
63
+ .h-5 {
64
+ height: 1.25rem;
65
+ }
66
+
67
+ .h-6 {
68
+ height: 1.5rem;
69
+ }
70
+
71
+ .h-8 {
72
+ height: 2rem;
73
+ }
74
+
75
+ .h-12 {
76
+ height: 3rem;
77
+ }
78
+
79
+ .h-24 {
80
+ height: 6rem;
81
+ }
82
+
83
+ .h-32 {
84
+ height: 8rem;
85
+ }
86
+
87
+ .h-40 {
88
+ height: 10rem;
89
+ }
90
+
91
+ /* Flex utilities */
92
+ .flex-shrink-0 {
93
+ flex-shrink: 0;
94
+ }
95
+
96
+ /* Object fit utilities */
97
+ .object-cover {
98
+ object-fit: cover;
99
+ }
@@ -0,0 +1,174 @@
1
+ /*
2
+ * Spacing Utilities
3
+ * This file contains padding, margin, and gap utility classes
4
+ */
5
+
6
+ /* Spacing utilities - Padding */
7
+ .p-3 {
8
+ padding: 0.75rem;
9
+ }
10
+
11
+ .p-4 {
12
+ padding: 1rem;
13
+ }
14
+
15
+ .p-5 {
16
+ padding: 1.25rem;
17
+ }
18
+
19
+ .p-6 {
20
+ padding: 1.5rem;
21
+ }
22
+
23
+ .px-2 {
24
+ padding-left: 0.5rem;
25
+ padding-right: 0.5rem;
26
+ }
27
+
28
+ .px-3 {
29
+ padding-left: 0.75rem;
30
+ padding-right: 0.75rem;
31
+ }
32
+
33
+ .px-4 {
34
+ padding-left: 1rem;
35
+ padding-right: 1rem;
36
+ }
37
+
38
+ .px-6 {
39
+ padding-left: 1.5rem;
40
+ padding-right: 1.5rem;
41
+ }
42
+
43
+ .py-2 {
44
+ padding-top: 0.5rem;
45
+ padding-bottom: 0.5rem;
46
+ }
47
+
48
+ .py-2\.5 {
49
+ padding-top: 0.625rem;
50
+ padding-bottom: 0.625rem;
51
+ }
52
+
53
+ .py-3 {
54
+ padding-top: 0.75rem;
55
+ padding-bottom: 0.75rem;
56
+ }
57
+
58
+ .py-4 {
59
+ padding-top: 1rem;
60
+ padding-bottom: 1rem;
61
+ }
62
+
63
+ .py-8 {
64
+ padding-top: 2rem;
65
+ padding-bottom: 2rem;
66
+ }
67
+
68
+ .py-16 {
69
+ padding-top: 4rem;
70
+ padding-bottom: 4rem;
71
+ }
72
+
73
+ .pt-2 {
74
+ padding-top: 0.5rem;
75
+ }
76
+
77
+ /* Spacing utilities - Margin */
78
+ .mb-1 {
79
+ margin-bottom: 0.25rem;
80
+ }
81
+
82
+ .mb-2 {
83
+ margin-bottom: 0.5rem;
84
+ }
85
+
86
+ .mb-3 {
87
+ margin-bottom: 0.75rem;
88
+ }
89
+
90
+ .mb-4 {
91
+ margin-bottom: 1rem;
92
+ }
93
+
94
+ .mb-6 {
95
+ margin-bottom: 1.5rem;
96
+ }
97
+
98
+ .mb-8 {
99
+ margin-bottom: 2rem;
100
+ }
101
+
102
+ .mb-12 {
103
+ margin-bottom: 3rem;
104
+ }
105
+
106
+ .mt-2 {
107
+ margin-top: 0.5rem;
108
+ }
109
+
110
+ .mt-3 {
111
+ margin-top: 0.75rem;
112
+ }
113
+
114
+ .mt-8 {
115
+ margin-top: 2rem;
116
+ }
117
+
118
+ .mt-12 {
119
+ margin-top: 3rem;
120
+ }
121
+
122
+ .mr-2 {
123
+ margin-right: 0.5rem;
124
+ }
125
+
126
+ .mr-4 {
127
+ margin-right: 1rem;
128
+ }
129
+
130
+ .ml-2 {
131
+ margin-left: 0.5rem;
132
+ }
133
+
134
+ .ml-3 {
135
+ margin-left: 0.75rem;
136
+ }
137
+
138
+ /* Gap utilities */
139
+ .gap-1 {
140
+ gap: 0.25rem;
141
+ }
142
+
143
+ .gap-2 {
144
+ gap: 0.5rem;
145
+ }
146
+
147
+ .gap-3 {
148
+ gap: 0.75rem;
149
+ }
150
+
151
+ .gap-4 {
152
+ gap: 1rem;
153
+ }
154
+
155
+ .gap-5 {
156
+ gap: 1.25rem;
157
+ }
158
+
159
+ /* Spacing stack utilities */
160
+ .space-y-3 > * + * {
161
+ margin-top: 0.75rem;
162
+ }
163
+
164
+ .space-y-4 > * + * {
165
+ margin-top: 1rem;
166
+ }
167
+
168
+ .space-y-5 > * + * {
169
+ margin-top: 1.25rem;
170
+ }
171
+
172
+ .space-y-6 > * + * {
173
+ margin-top: 1.5rem;
174
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Typography Utilities
3
+ * This file contains font size, weight, and text utility classes
4
+ */
5
+
6
+ /* Typography */
7
+ .text-xs {
8
+ font-size: 0.75rem;
9
+ line-height: 1rem;
10
+ }
11
+
12
+ .text-sm {
13
+ font-size: 0.875rem;
14
+ line-height: 1.25rem;
15
+ }
16
+
17
+ .text-base {
18
+ font-size: 1rem;
19
+ line-height: 1.5rem;
20
+ }
21
+
22
+ .text-lg {
23
+ font-size: 1.125rem;
24
+ line-height: 1.75rem;
25
+ }
26
+
27
+ .text-xl {
28
+ font-size: 1.25rem;
29
+ line-height: 1.75rem;
30
+ }
31
+
32
+ .text-2xl {
33
+ font-size: 1.5rem;
34
+ line-height: 2rem;
35
+ }
36
+
37
+ .text-3xl {
38
+ font-size: 1.875rem;
39
+ line-height: 2.25rem;
40
+ }
41
+
42
+ .text-4xl {
43
+ font-size: 2.25rem;
44
+ line-height: 2.5rem;
45
+ }
46
+
47
+ .text-5xl {
48
+ font-size: 3rem;
49
+ line-height: 1;
50
+ }
51
+
52
+ .font-medium {
53
+ font-weight: 500;
54
+ }
55
+
56
+ .font-semibold {
57
+ font-weight: 600;
58
+ }
59
+
60
+ .font-bold {
61
+ font-weight: 700;
62
+ }
63
+
64
+ .text-center {
65
+ text-align: center;
66
+ }
67
+
68
+ .break-words {
69
+ overflow-wrap: break-word;
70
+ }
71
+
72
+ .line-clamp-3 {
73
+ display: -webkit-box;
74
+ -webkit-line-clamp: 3;
75
+ -webkit-box-orient: vertical;
76
+ overflow: hidden;
77
+ }
78
+
79
+ .whitespace-nowrap {
80
+ white-space: nowrap;
81
+ }
82
+
83
+ .no-underline {
84
+ text-decoration: none;
85
+ }
86
+
87
+ /* Bootstrap utility classes for compatibility */
88
+ .text-muted {
89
+ color: var(--color-text-muted) !important;
90
+ }
91
+
92
+ .form-text {
93
+ display: block;
94
+ margin-top: 0.25rem;
95
+ font-size: 0.875rem;
96
+ color: var(--color-text-muted);
97
+ }
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Controller for handling application errors with custom pages
4
+ # Provides consistent error pages for both UI and API requests
5
+ # nosemgrep: ruby.lang.security.missing-csrf-protection.missing-csrf-protection
6
+ class ErrorsController < ActionController::Base
7
+ # Include errors helper for route helpers in views
8
+ helper ErrorsHelper
9
+
10
+ # Skip CSRF protection for error pages (no forms to submit)
11
+ protect_from_forgery with: :null_session
12
+ # Don't check for modern browsers on error pages
13
+ skip_before_action :verify_authenticity_token, raise: false
14
+ layout "error"
15
+
16
+ # 400 Bad Request
17
+ def bad_request
18
+ @error_code = 400
19
+ @error_title = "Bad Request"
20
+ @error_message = params[:message] || "The request could not be understood by the server."
21
+ @error_details = params[:details]
22
+ @status = :bad_request
23
+
24
+ respond_to_error
25
+ end
26
+
27
+ # 401 Unauthorized
28
+ def unauthorized
29
+ @error_code = 401
30
+ @error_title = "Unauthorized"
31
+ @error_message = params[:message] || "You need to sign in or sign up before continuing."
32
+ @error_details = params[:details]
33
+ @status = :unauthorized
34
+
35
+ respond_to_error
36
+ end
37
+
38
+ # 403 Forbidden
39
+ def forbidden
40
+ @error_code = 403
41
+ @error_title = "Forbidden"
42
+ @error_message = params[:message] || "You don't have permission to access this resource."
43
+ @error_details = params[:details]
44
+ @status = :forbidden
45
+
46
+ respond_to_error
47
+ end
48
+
49
+ # 404 Not Found
50
+ def not_found
51
+ @error_code = 404
52
+ @error_title = "Page Not Found"
53
+ @error_message = params[:message] || "The page you're looking for doesn't exist."
54
+ @error_details = params[:details]
55
+ @status = :not_found
56
+
57
+ respond_to_error
58
+ end
59
+
60
+ # 406 Not Acceptable
61
+ def not_acceptable
62
+ @error_code = 406
63
+ @error_title = "Not Acceptable"
64
+ @error_message = params[:message] || "The requested resource cannot be provided in a format your browser accepts. Please try using a different browser or update your current browser."
65
+ @error_details = params[:details]
66
+ @status = :not_acceptable
67
+
68
+ respond_to_error
69
+ end
70
+
71
+ # 422 Unprocessable Entity
72
+ def unprocessable_entity
73
+ @error_code = 422
74
+ @error_title = "Unprocessable Entity"
75
+ @error_message = params[:message] || "The request was well-formed but contains invalid data."
76
+ @error_details = params[:details]
77
+ @status = :unprocessable_entity
78
+
79
+ respond_to_error
80
+ end
81
+
82
+ # 500 Internal Server Error
83
+ def internal_server_error
84
+ @error_code = 500
85
+ @error_title = "Internal Server Error"
86
+ @error_message = params[:message] || "Something went wrong on our end. We're working to fix it."
87
+ @error_details = params[:details]
88
+ @status = :internal_server_error
89
+
90
+ respond_to_error
91
+ end
92
+
93
+ # 503 Service Unavailable
94
+ def service_unavailable
95
+ @error_code = 503
96
+ @error_title = "Service Unavailable"
97
+ @error_message = params[:message] || "The service is temporarily unavailable. Please try again later."
98
+ @error_details = params[:details]
99
+ @status = :service_unavailable
100
+
101
+ respond_to_error
102
+ end
103
+
104
+ private
105
+
106
+ # Respond with appropriate format (HTML or JSON)
107
+ def respond_to_error
108
+ respond_to do |format|
109
+ format.html { render "errors/show", status: @status }
110
+ format.json do
111
+ response_data = {
112
+ success: false,
113
+ error: @error_message
114
+ }
115
+ response_data[:details] = @error_details if @error_details.present?
116
+ render json: response_data, status: @status
117
+ end
118
+ end
119
+ end
120
+ end