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
data/CLAUDE.md ADDED
@@ -0,0 +1,632 @@
1
+ # CLAUDE.md
2
+
3
+ ## Overview
4
+
5
+ This document describes the neon_sakura gem and its architecture for use with Claude. The gem extracts styling and theming components from a Rails application to create a reusable set of UI elements that can be shared across multiple Rails projects.
6
+
7
+ ## Git Workflow
8
+
9
+ **IMPORTANT: Always ask for permission before committing or pushing any changes.**
10
+ - Stage changes and show `git status`
11
+ - Describe what will be committed
12
+ - Wait for explicit user approval before running `git commit`
13
+ - Wait for explicit user approval before running `git push`
14
+
15
+ ## Purpose
16
+
17
+ The neon_sakura gem is a Rails engine designed to provide:
18
+ - Shared styling components with a dark-themed UI
19
+ - Tailwind-inspired utility classes
20
+ - Reusable UI components like navigation, cards, badges, buttons, tables, alerts
21
+ - Form styling and pagination components
22
+ - Layout templates for application and error pages
23
+ - SVG icon components for common UI elements
24
+
25
+ ## Documentation Updates
26
+
27
+ When making changes to the gem, ensure that:
28
+ 1. CHANGELOG.md is updated with a description of the changes
29
+ 2. README.md is updated to reflect any changes in functionality or usage
30
+ 3. Any new features or modifications to existing behavior are documented
31
+
32
+ ## Usage
33
+
34
+ Add to your Gemfile:
35
+ ```ruby
36
+ gem 'neon_sakura'
37
+ ```
38
+
39
+ Then run:
40
+ ```bash
41
+ bundle install
42
+ ```
43
+
44
+ Include assets in your application layout:
45
+ ```erb
46
+ <%# Load neon_sakura stylesheets (Propshaft-compatible) %>
47
+ <%= neon_sakura_stylesheets %>
48
+
49
+ <%# Load your app-specific styles %>
50
+ <%= stylesheet_link_tag 'application', 'data-turbo-track': 'reload' %>
51
+
52
+ <%# JavaScript %>
53
+ <%= javascript_include_tag 'application', 'data-turbo-track': 'reload' %>
54
+ ```
55
+
56
+ The `neon_sakura_stylesheets` helper loads all CSS files in the correct order for proper cascade. This is necessary because Propshaft (Rails 8) does not process `@import` statements in CSS files.
57
+
58
+ Use the provided layouts and components in your views.
59
+
60
+ ## Style Guide (Development Only)
61
+
62
+ The gem includes a comprehensive style guide at `/style-guide` that showcases all UI components with live previews and code examples. The style guide is automatically enabled in development/test environments and completely hidden in production.
63
+
64
+ ### Configuration
65
+
66
+ ```ruby
67
+ # config/initializers/neon_sakura.rb
68
+ NeonSakura.configure do |config|
69
+ # Enable/disable the entire style guide (default: true)
70
+ config.enable_style_guide = true
71
+
72
+ # Control navbar link position: :start, :end, :before_logout, or integer (default: :end)
73
+ config.style_guide_navbar_position = :end
74
+
75
+ # Enable/disable Pagy pagination examples (default: true)
76
+ # When true: Auto-detects Pagy and shows working examples
77
+ # When false: Hides pagination section entirely
78
+ config.style_guide_pagy_examples = true
79
+ end
80
+ ```
81
+
82
+ ### Pagy Integration
83
+
84
+ The style guide includes pagination examples using Pagy:
85
+ - **Default behavior** (`style_guide_pagy_examples = true`): Auto-detects Pagy gem and shows working examples
86
+ - **If Pagy not installed**: Shows helpful error message with installation instructions
87
+ - **If disabled** (`style_guide_pagy_examples = false`): Hides pagination section entirely
88
+ - **Error handling**: Gracefully catches Pagy initialization errors and displays configuration help
89
+
90
+ Pagy is NOT a required dependency - the style guide works without it.
91
+
92
+ ## Architecture
93
+
94
+ The gem follows the typical Rails engine pattern with:
95
+ - Asset pipeline integration for CSS and JS
96
+ - Layout templates for consistent page structure
97
+ - Component partials for reusable UI elements
98
+ - SVG icon system for consistent iconography
99
+
100
+ ## Stack
101
+
102
+ - Rails 8 (primary)
103
+ - SQLite (3 databases: main, queue, cache)
104
+ - SolidQueue, Puma, Propshaft
105
+ - Turbo/Stimulus, Pagy, Oj, Node 24
106
+
107
+ ## Dev Rules
108
+
109
+ - All code must follow Rails 8 conventions
110
+ - Use Oj (`Oj.load/dump`) instead of JSON (2-10x faster)
111
+ - Follow Rails Omakase style compliance
112
+ - 2 spaces indentation for Ruby/JS/CSS/HTML
113
+ - 4 spaces indentation for Python
114
+ - Line length: 120 chars (Ruby), 88 (Python)
115
+ - No tabs, no trailing whitespace
116
+
117
+ ## UI & Styling
118
+
119
+ - **Never use** inline styles (`style="..."` attributes)
120
+ - **Never use** embedded `<style>` tags
121
+ - All styles must be in CSS files: `app/assets/stylesheets/`
122
+ - Use existing utility classes from `base.css`
123
+ - Dark theme color palette (bg-gray-900, bg-gray-800, text-white, text-cyan-300, etc.)
124
+ - WCAG 2.1 AA accessibility compliance
125
+ - Proper `aria-label` on forms and interactive elements
126
+ - Responsive design (mobile-first)
127
+
128
+ ## Icon Usage
129
+
130
+ **CRITICAL**: Always verify icons exist before using them in views to avoid MissingTemplate errors.
131
+
132
+ ### Icon Workflow
133
+
134
+ 1. **Search for existing icons first**:
135
+ ```bash
136
+ # List all available icons
137
+ ls app/views/shared/icons/
138
+
139
+ # Search for specific icon by name
140
+ ls app/views/shared/icons/ | grep -i "search_term"
141
+
142
+ # Example: Find alert-related icons
143
+ ls app/views/shared/icons/ | grep -i "alert"
144
+ # Returns: _alert_circle.html.erb, _alert_triangle.html.erb
145
+ ```
146
+
147
+ 2. **Use the correct icon name** (without underscore prefix or file extension):
148
+ ```erb
149
+ <!-- Correct -->
150
+ <%= render_theme_icon("alert_circle", css_class: "w-5 h-5") %>
151
+
152
+ <!-- Wrong - will cause MissingTemplate error -->
153
+ <%= render_theme_icon("alert", css_class: "w-5 h-5") %>
154
+ <%= render_theme_icon("info", css_class: "w-5 h-5") %>
155
+ ```
156
+
157
+ 3. **If the desired icon doesn't exist**, create a new one:
158
+
159
+ **a. Find an appropriate SVG**:
160
+ - Source: [Heroicons](https://heroicons.com/) (MIT License, commercially safe, no attribution required)
161
+ - Download the 24x24 outline version
162
+ - Alternative: Create custom SVG following the same style
163
+
164
+ **b. Create the icon partial**:
165
+ ```bash
166
+ # File naming: app/views/shared/icons/_icon_name.html.erb
167
+ # Use snake_case, prefix with underscore
168
+ touch app/views/shared/icons/_new_icon_name.html.erb
169
+ ```
170
+
171
+ **c. Icon template structure**:
172
+ ```erb
173
+ <%# Icon: New Icon Name (brief description) %>
174
+ <%# Source: https://heroicons.com/ (if applicable) %>
175
+ <svg xmlns="http://www.w3.org/2000/svg"
176
+ fill="none"
177
+ viewBox="0 0 24 24"
178
+ stroke-width="1.5"
179
+ stroke="currentColor"
180
+ class="<%= css_class %>">
181
+ <!-- SVG path data here -->
182
+ <path stroke-linecap="round" stroke-linejoin="round" d="..." />
183
+ </svg>
184
+ ```
185
+
186
+ **d. Key requirements**:
187
+ - Use `currentColor` for stroke/fill (allows color control via CSS)
188
+ - Accept `css_class` parameter for size and color classes
189
+ - Add comment header with icon name and source
190
+ - Follow existing icon structure (see other icons for examples)
191
+
192
+ 4. **Test the new icon**:
193
+ ```bash
194
+ # Verify icon renders without errors
195
+ bundle exec rake test
196
+
197
+ # Check icon appears in style guide
198
+ # Visit /style-guide in development
199
+ ```
200
+
201
+ ### Common Icon Names
202
+
203
+ Icons use descriptive snake_case names:
204
+ - `alert_circle` - Circle with exclamation (info/warning)
205
+ - `alert_triangle` - Triangle with exclamation (error/danger)
206
+ - `check_circle` - Circle with checkmark (success)
207
+ - `cog` - Settings/configuration
208
+ - `palette` - Theme/color settings
209
+ - `package` - Package/module
210
+ - `external_link` - External link indicator
211
+ - `plus` - Add/create
212
+ - `download` - Download action
213
+
214
+ **Pro tip**: Run `ls app/views/shared/icons/ | sed 's/_//' | sed 's/.html.erb//' | sort` to get a clean list of all available icon names.
215
+
216
+ ## Navbar Synchronization
217
+
218
+ **CRITICAL**: When updating the main navbar (`app/views/shared/_navbar.html.erb`), ensure that the Mission Control navbar (`app/views/layouts/mission_control/jobs/application.html.erb`) stays synchronized:
219
+
220
+ - **Colors**: All navbar links should be white (#ffffff) with cyan (#06b6d4) hover
221
+ - **Dropdown styling**: Dropdown menu items follow the same color scheme
222
+ - **Logout button**: Uses same styling as other navbar links
223
+ - **Theme switcher**: Both navbars should support theme switching
224
+ - **Special item types**: Both navbars must handle divider and theme_selector types
225
+
226
+ When making navbar changes, update BOTH files to maintain consistency across the application.
227
+
228
+ ## Asset Pipeline Architecture
229
+
230
+ - No preprocessing in dev, auto-digest in prod
231
+ - CSS organization via `app/assets/stylesheets/application.css`
232
+ - No inline styles or embedded style tags
233
+ - Check that new CSS is properly imported in application.css
234
+
235
+ ## Multi-Theme System
236
+
237
+ Neon Sakura uses a comprehensive theme system with CSS custom properties. **NEVER use hardcoded colors** - always use CSS variables.
238
+
239
+ ### Theme Architecture
240
+
241
+ Each theme is defined by two dimensions:
242
+ - **Name**: `green` or `purple` (color family)
243
+ - **Mode**: `light` or `dark` (brightness)
244
+
245
+ Themes are applied via data attributes on the `<html>` element:
246
+ ```html
247
+ <html data-theme-name="purple" data-theme-mode="dark">
248
+ ```
249
+
250
+ ### CSS Custom Properties (Variables)
251
+
252
+ **CRITICAL**: ALL colors must use CSS variables. Never hardcode hex colors in utility classes or components.
253
+
254
+ Available theme variables:
255
+ - `--color-text-primary` - Primary text color
256
+ - `--color-text-secondary` - Secondary text color
257
+ - `--color-text-muted` - Muted/disabled text
258
+ - `--color-background` - Page background
259
+ - `--color-surface` - Card/panel backgrounds
260
+ - `--color-accent` - Primary accent color (purple or green depending on theme)
261
+ - `--color-notification` - Success/notification color
262
+ - `--color-alert` - Error/alert color
263
+ - `--color-warning` - Warning color
264
+ - `--color-border` - Border color
265
+ - `--gradient-from` / `--gradient-to` - Button background gradients
266
+ - `--gradient-from-hover` / `--gradient-to-hover` - Button hover gradients
267
+ - `--text-gradient-from` / `--text-gradient-to` - Text gradients for headings/links
268
+
269
+ ### Built-in Themes
270
+
271
+ 1. **Purple Dark** (default) - Primary accent: #a855f7, Gradient: Blue → Purple
272
+ 2. **Purple Light** - Primary accent: #a855f7, Gradient: Purple → Purple
273
+ 3. **Green Dark** - Primary accent: #10b981, Gradient: Green → Cyan
274
+ 4. **Green Light** - Primary accent: #009990, Gradient: Green → Cyan
275
+ 5. **Red Dark** - Primary accent: #ef4444, Gradient: Red → Orange
276
+ 6. **Red Light** - Primary accent: #dc2626, Gradient: Red → Orange
277
+
278
+ ### Theme Development Workflow
279
+
280
+ **When adding new styles:**
281
+ 1. ✅ **ALWAYS** use `var(--color-*)` instead of hardcoded colors
282
+ 2. ✅ Use `color-mix()` for intermediate shades when needed
283
+ 3. ✅ Test your changes in ALL 4 themes (green/purple × light/dark)
284
+ 4. ❌ **NEVER** hardcode hex colors like `#a855f7` or `#10b981`
285
+ 5. ❌ **NEVER** use theme-specific colors (e.g., "purple" or "green" in class names)
286
+
287
+ **Good examples:**
288
+ ```css
289
+ .my-component {
290
+ color: var(--color-text-primary);
291
+ background: var(--color-surface);
292
+ border: 1px solid var(--color-border);
293
+ }
294
+
295
+ .my-button {
296
+ background: linear-gradient(to right, var(--gradient-from), var(--gradient-to));
297
+ }
298
+
299
+ .my-heading {
300
+ background: linear-gradient(to right, var(--text-gradient-from), var(--text-gradient-to));
301
+ background-clip: text;
302
+ -webkit-background-clip: text;
303
+ -webkit-text-fill-color: transparent;
304
+ }
305
+ ```
306
+
307
+ **Bad examples:**
308
+ ```css
309
+ /* ❌ NEVER do this */
310
+ .my-component {
311
+ color: #ffffff; /* Use var(--color-text-primary) */
312
+ background: #1f2937; /* Use var(--color-surface) */
313
+ }
314
+
315
+ .my-button {
316
+ background: linear-gradient(to right, #2563eb, #9333ea); /* Use var(--gradient-from), var(--gradient-to) */
317
+ }
318
+ ```
319
+
320
+ ### Adding New Themes
321
+
322
+ To add a new theme, update `base.css` with new color definitions:
323
+
324
+ ```css
325
+ :root[data-theme-name="custom"][data-theme-mode="dark"] {
326
+ --color-text-primary: #your-color;
327
+ --color-background: #your-color;
328
+ --color-surface: #your-color;
329
+ --color-accent: #your-color;
330
+ --color-notification: #your-color;
331
+ --color-alert: #your-color;
332
+ --color-warning: #your-color;
333
+ --color-border: #your-color;
334
+ --gradient-from: #your-color;
335
+ --gradient-to: #your-color;
336
+ --gradient-from-hover: #your-color;
337
+ --gradient-to-hover: #your-color;
338
+ --text-gradient-from: #your-color;
339
+ --text-gradient-to: #your-color;
340
+ }
341
+ ```
342
+
343
+ ### Theme Testing
344
+
345
+ Before committing style changes, test in ALL 6 themes:
346
+ 1. **Purple Dark** (default) - Check default appearance
347
+ 2. **Purple Light** - Check text visibility and contrast
348
+ 3. **Green Dark** - Ensure gradients are green→cyan, not purple
349
+ 4. **Green Light** - Check contrast and visibility
350
+ 5. **Red Dark** - Ensure gradients are red→orange
351
+ 6. **Red Light** - Check text visibility and red accent visibility
352
+
353
+ Common issues to check:
354
+ - Text visibility on buttons in light themes (should be white)
355
+ - Gradient colors match theme color family:
356
+ - Green themes: green → cyan gradients
357
+ - Purple themes: blue → purple gradients
358
+ - Red themes: red → orange gradients
359
+ - Border visibility in all themes
360
+ - Hover states are visible and distinct
361
+ - Accent color is appropriate for theme (green/purple/red)
362
+
363
+ ### Migrating Hardcoded Colors
364
+
365
+ When refactoring existing code with hardcoded colors:
366
+
367
+ 1. Identify the semantic meaning of the color
368
+ 2. Map to appropriate CSS variable:
369
+ - `#111827`, `#0f172a` → `var(--color-background)`
370
+ - `#1f2937`, `#1e293b` → `var(--color-surface)`
371
+ - `#374151`, `#334155`, `#dee2e6` → `var(--color-border)`
372
+ - `#ffffff`, `#e2e8f0`, `#545E75` → `var(--color-text-primary)`
373
+ - `#a855f7`, `#10b981`, `#009990` → `var(--color-accent)`
374
+ - `#10b981` → `var(--color-notification)`
375
+ - `#ef4444`, `#DE5959` → `var(--color-alert)`
376
+ - `#f59e0b` → `var(--color-warning)`
377
+ 3. Use `color-mix()` for intermediate shades
378
+ 4. Test in all themes
379
+
380
+ ## CSS File Structure
381
+
382
+ Neon Sakura uses a modular CSS architecture with a flat file structure for Propshaft compatibility.
383
+
384
+ ### File Hierarchy
385
+
386
+ All CSS files are in the root `app/assets/stylesheets/` directory (no subdirectories):
387
+
388
+ ```
389
+ app/assets/stylesheets/
390
+ ├── neon_sakura.css (complete manifest)
391
+ ├── base.css (manifest: themes + utilities)
392
+ ├── theme-default.css (:root default theme)
393
+ ├── theme-purple.css (purple light + dark)
394
+ ├── theme-green.css (green light + dark)
395
+ ├── theme-red.css (red light + dark)
396
+ ├── utility-reset.css (body reset)
397
+ ├── utility-layout.css (display, flex, grid)
398
+ ├── utility-sizing.css (width, height)
399
+ ├── utility-spacing.css (padding, margin, gap)
400
+ ├── utility-colors.css (bg/text colors)
401
+ ├── utility-borders.css (borders, radius)
402
+ ├── utility-typography.css (fonts, text)
403
+ ├── utility-gradients.css (gradients)
404
+ ├── utility-effects.css (shadows, transitions, hover/focus)
405
+ ├── utility-responsive.css (media queries)
406
+ ├── components.css (UI components)
407
+ ├── forms.css (form styling)
408
+ └── pagy-tailwind.css (pagination)
409
+ ```
410
+
411
+ **Why flat structure?** Propshaft serves all assets from a flat `/assets/` URL path. CSS `@import` statements with subdirectory paths (e.g., `@import "themes/purple.css"`) cause 404 errors because the browser looks for `/assets/themes/purple.css`, which doesn't exist in Propshaft's URL structure.
412
+
413
+ ### Load Order (CRITICAL)
414
+
415
+ The CSS load order is critical for the cascade:
416
+
417
+ 1. **Themes** (defines CSS variables)
418
+ 2. **Utilities** (uses CSS variables)
419
+ 3. **Components/Forms** (uses both)
420
+
421
+ **IMPORTANT**: With Propshaft, `@import` statements are **NOT** processed by the asset pipeline. They are resolved by the browser, which can cause issues when loading CSS from gems.
422
+
423
+ **Recommended approach**: Use the `neon_sakura_stylesheets` helper in your layout:
424
+
425
+ ```erb
426
+ <%= neon_sakura_stylesheets %>
427
+ ```
428
+
429
+ This helper loads all CSS files individually in the correct order, ensuring proper cascade and browser compatibility.
430
+
431
+ **Alternative approach**: Load files individually via `stylesheet_link_tag` in the correct order (see the helper source for the exact order).
432
+
433
+ ### Adding New Styles
434
+
435
+ When adding new styles, follow this file organization:
436
+
437
+ | Style Type | File | Example |
438
+ |------------|------|---------|
439
+ | Theme colors | `theme-*.css` | New color variables for a theme |
440
+ | Layout utilities | `utility-layout.css` | `.flex`, `.grid`, `.block` |
441
+ | Sizing utilities | `utility-sizing.css` | `.w-*`, `.h-*` |
442
+ | Spacing utilities | `utility-spacing.css` | `.p-*`, `.m-*`, `.gap-*` |
443
+ | Color utilities | `utility-colors.css` | `.bg-*`, `.text-*` |
444
+ | Border utilities | `utility-borders.css` | `.border-*`, `.rounded-*` |
445
+ | Typography | `utility-typography.css` | `.text-*`, `.font-*` |
446
+ | Gradients | `utility-gradients.css` | `.bg-gradient-*`, `.from-*`, `.to-*` |
447
+ | Effects | `utility-effects.css` | `.shadow-*`, `.transition-*`, `.hover:*` |
448
+ | Responsive | `utility-responsive.css` | `@media` queries |
449
+ | UI components | `components.css` | `.card`, `.badge`, `.nav` |
450
+ | Form styling | `forms.css` | Input, button, select styles |
451
+ | Pagination | `pagy-tailwind.css` | Pagy navigation |
452
+
453
+ ### Editing Existing Styles
454
+
455
+ **DO NOT** create new files for one-off changes. Edit the corresponding file:
456
+
457
+ - **Theme colors**: Edit `theme-purple.css`, `theme-green.css`, or `theme-red.css`
458
+ - **Utility classes**: Edit the appropriate `utility-*.css` file
459
+ - **Components**: Edit `components.css`
460
+ - **Forms**: Edit `forms.css`
461
+
462
+ ### Creating New Themes
463
+
464
+ To create a new theme (e.g., `blue`):
465
+
466
+ 1. Create `app/assets/stylesheets/theme-blue.css`
467
+ 2. Define both light and dark variants:
468
+ ```css
469
+ [data-theme-name="blue"][data-theme-mode="light"] {
470
+ --color-text-primary: #...;
471
+ --color-background: #...;
472
+ /* ... all required variables */
473
+ }
474
+
475
+ [data-theme-name="blue"][data-theme-mode="dark"] {
476
+ --color-text-primary: #...;
477
+ --color-background: #...;
478
+ /* ... all required variables */
479
+ }
480
+ ```
481
+ 3. Import in both `base.css` and `neon_sakura.css` after existing themes:
482
+ ```css
483
+ @import "theme-default.css";
484
+ @import "theme-purple.css";
485
+ @import "theme-green.css";
486
+ @import "theme-red.css";
487
+ @import "theme-blue.css"; /* Add new theme here */
488
+ ```
489
+ 4. Test in both light and dark modes
490
+
491
+ ## Accessibility Requirements
492
+
493
+ - All interactive elements must have proper `aria-label` or `aria-labelledby`
494
+ - WCAG 2.1 AA compliance for color contrast
495
+ - Proper heading structure (`h1`, `h2`, etc.)
496
+ - Semantic HTML elements
497
+
498
+ ## Security
499
+
500
+ - OWASP Top 10 vulnerabilities (SQL injection, XSS, CSRF, etc.)
501
+ - Path traversal attacks in file operations
502
+ - Insecure redirects (must use `only_path: true`)
503
+ - Mass assignment vulnerabilities
504
+ - Exposed secrets or credentials
505
+ - External links must have `rel: "noopener noreferrer"`
506
+ - Proper use of `sanitize` over `raw` for HTML
507
+ - JavaScript: No `innerHTML`, use `addEventListener`, proper escaping with `<%= j variable %>`
508
+
509
+ ## Code Style
510
+
511
+ - DRY principle (Don't Repeat Yourself)
512
+ - Ruby/JS/CSS/HTML: 2 spaces indentation
513
+ - Python: 4 spaces indentation
514
+ - Line length: 120 chars (Ruby), 88 (Python)
515
+ - No tabs, no trailing whitespace
516
+ - Rails Omakase style compliance
517
+ - Use Oj (`Oj.load/dump`) instead of JSON (2-10x faster)
518
+ - Proper error handling and edge cases
519
+
520
+ ## CSS Linting
521
+
522
+ Neon Sakura uses Stylelint with the industry-standard configuration for CSS code quality and consistency.
523
+
524
+ ### Setup
525
+
526
+ 1. **Install dependencies** (first time only):
527
+ ```bash
528
+ yarn install
529
+ ```
530
+
531
+ 2. **Run Stylelint manually**:
532
+ ```bash
533
+ yarn lint:css # Check for issues
534
+ yarn lint:css:fix # Auto-fix issues
535
+ ```
536
+
537
+ ### Configuration
538
+
539
+ - **Config file**: `.stylelintrc.json` - Uses `stylelint-config-standard` for industry-standard CSS linting
540
+ - **Ignore file**: `.stylelintignore` - Excludes vendor files and third-party libraries
541
+ - **Pre-commit hook**: Automatically runs on `git commit` (can be skipped with `--no-verify`)
542
+
543
+ ### Rules
544
+
545
+ Stylelint enforces:
546
+ - Consistent formatting and spacing
547
+ - Valid CSS syntax
548
+ - No duplicate selectors or properties
549
+ - Proper use of vendor prefixes
550
+ - Color function notation consistency
551
+ - Theme-aware custom properties
552
+
553
+ ### Custom Rules
554
+
555
+ The gem's configuration customizes the standard ruleset to:
556
+ - Allow Tailwind CSS at-rules (`@tailwind`, `@apply`, etc.)
557
+ - Allow flexible class naming patterns (for utility classes)
558
+ - Allow custom property patterns (for CSS variables)
559
+ - Support vendor prefixes (for browser compatibility)
560
+ - Use legacy color function notation for better browser support
561
+
562
+ ### Common Issues
563
+
564
+ **Issue**: `Unexpected unknown at-rule "@import"`
565
+ **Fix**: Ensure you're using `@import` in manifest files only (base.css, neon_sakura.css)
566
+
567
+ **Issue**: `Expected class selector to match pattern`
568
+ **Fix**: This rule is disabled - use any class naming convention needed
569
+
570
+ **Issue**: `Unexpected vendor-prefixed property`
571
+ **Fix**: This rule is disabled - vendor prefixes are allowed for compatibility
572
+
573
+ ### Integration with Git Hooks
574
+
575
+ Stylelint runs automatically in the pre-commit hook:
576
+ 1. Only runs if yarn and package.json are present
577
+ 2. Checks all staged CSS files
578
+ 3. Fails commit if issues are found
579
+ 4. Provides auto-fix command: `yarn lint:css:fix`
580
+
581
+ To skip Stylelint (not recommended):
582
+ ```bash
583
+ git commit --no-verify
584
+ ```
585
+
586
+ ## Search Services
587
+
588
+ - **Search Services Pattern:**
589
+ ```ruby
590
+ def initialize(bypass_cache: false, **options) # Config only, NO search_term
591
+ @bypass_cache = bypass_cache
592
+ end
593
+
594
+ def search(search_term) # search_term HERE
595
+ search_with_benchmark(search_term)
596
+ end
597
+ ```
598
+ - Use `HttpRequestService` with 24h HTTP cache
599
+ - Benchmark execution time for searches
600
+ - Sort results by date (newest first)
601
+ - Services return structured data (arrays of hashes)
602
+
603
+ ## Testing
604
+
605
+ - Minimum 80% overall test coverage (enforced by CI)
606
+ - Minimum 20% per-file coverage
607
+ - No coverage drops allowed
608
+ - No skipped tests (`skip` is forbidden)
609
+ - Use WebMock for HTTP mocking (NEVER use `define_method` globally - causes test pollution)
610
+ - Health checks for service classes: `domain_up?`, `search_working?`
611
+ - Test both success and error cases
612
+ - Fixtures for WebMock responses
613
+
614
+ ## Pull Requests
615
+
616
+ - PR descriptions must start with "# What does this PR do?"
617
+ - Pre-commit: RuboCop, Brakeman, Bundle Audit, migrations, secrets
618
+ - Pre-push: Schema consistency, asset compilation, TODO/FIXME
619
+ - Never skip hooks without good reason
620
+
621
+ ## AI PR Reviewer
622
+
623
+ For each PR (new or updates) that modifies code, Claude will:
624
+ 1. Run pre-commit checks (RuboCop, Brakeman, Bundle Audit, migrations, secrets)
625
+ 2. Run pre-push checks (schema consistency, asset compilation, TODO/FIXME)
626
+ 3. Run the AI code review with the correct system prompt
627
+ 4. If code quality issues or security flaws are found, it will fail the PR checks
628
+
629
+ The AI reviewer uses the following system prompt:
630
+ You are an expert Rails 8 code reviewer for the Neon Sakura gem project.
631
+
632
+ Always add newlines at the end of new code files
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) { |_repo_name| "https://github.com/TRex22/neon_sakura" }
6
+
7
+ # Specify your gem's dependencies in neon_sakura.gemspec
8
+ gemspec