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.
- checksums.yaml +7 -0
- data/.ai-reviewer/README.md +182 -0
- data/.ai-reviewer/ai-reviewer.sh +56 -0
- data/.ai-reviewer/build-system-prompt.sh +136 -0
- data/.ai-reviewer/extract-claude-sections.sh +32 -0
- data/.ai-reviewer/test-ai-reviewer.sh +40 -0
- data/.ai-reviewer-config.yml +190 -0
- data/.github/dependabot.yml +12 -0
- data/.github/settings.yml +70 -0
- data/.github/workflows/ai-pr-review-on-comment.yml +384 -0
- data/.github/workflows/ai-pr-review.yml +328 -0
- data/.github/workflows/license-check.yml +78 -0
- data/.github/workflows/lint.yml +79 -0
- data/.github/workflows/security.yml +131 -0
- data/.github/workflows/semgrep.yml +26 -0
- data/.github/workflows/test.yml +44 -0
- data/.gitignore +75 -0
- data/.rubocop.yml +33 -0
- data/.ruby-version +1 -0
- data/.simplecov +14 -0
- data/.stylelintignore +10 -0
- data/.stylelintrc.json +37 -0
- data/AGENTS.md +51 -0
- data/CHANGELOG.md +568 -0
- data/CLAUDE.md +632 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +327 -0
- data/LICENSE +21 -0
- data/README.md +1209 -0
- data/Rakefile +25 -0
- data/app/assets/images/cherry_blossom.svg +1525 -0
- data/app/assets/images/cherry_blossom_tree.png +0 -0
- data/app/assets/images/prysm-icon.png +0 -0
- data/app/assets/stylesheets/base.css +29 -0
- data/app/assets/stylesheets/components.css +1652 -0
- data/app/assets/stylesheets/forms.css +152 -0
- data/app/assets/stylesheets/loading.css +145 -0
- data/app/assets/stylesheets/neon_sakura.css +40 -0
- data/app/assets/stylesheets/pagy-tailwind.css +120 -0
- data/app/assets/stylesheets/theme-default.css +40 -0
- data/app/assets/stylesheets/theme-green.css +84 -0
- data/app/assets/stylesheets/theme-purple.css +94 -0
- data/app/assets/stylesheets/theme-red.css +84 -0
- data/app/assets/stylesheets/utility-borders.css +29 -0
- data/app/assets/stylesheets/utility-colors.css +185 -0
- data/app/assets/stylesheets/utility-effects.css +123 -0
- data/app/assets/stylesheets/utility-gradients.css +158 -0
- data/app/assets/stylesheets/utility-layout.css +132 -0
- data/app/assets/stylesheets/utility-reset.css +13 -0
- data/app/assets/stylesheets/utility-responsive.css +145 -0
- data/app/assets/stylesheets/utility-sizing.css +99 -0
- data/app/assets/stylesheets/utility-spacing.css +174 -0
- data/app/assets/stylesheets/utility-typography.css +97 -0
- data/app/controllers/errors_controller.rb +120 -0
- data/app/controllers/style_guide_controller.rb +117 -0
- data/app/helpers/errors_helper.rb +12 -0
- data/app/helpers/neon_sakura/navbar_helper.rb +43 -0
- data/app/helpers/style_guide_helper.rb +36 -0
- data/app/javascript/neon_sakura/dropdown.js +22 -0
- data/app/javascript/neon_sakura/navbar.js +71 -0
- data/app/javascript/neon_sakura/theme_switcher.js +187 -0
- data/app/views/errors/show.html.erb +105 -0
- data/app/views/layouts/error.html.erb +19 -0
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
- data/app/views/layouts/mission_control/jobs/_navigation.html.erb +21 -0
- data/app/views/layouts/mission_control/jobs/application.html.erb +453 -0
- data/app/views/layouts/style_guide.html.erb +416 -0
- data/app/views/shared/_file_upload.html.erb +184 -0
- data/app/views/shared/_footer.html.erb +23 -0
- data/app/views/shared/_header.html.erb +42 -0
- data/app/views/shared/_navbar.html.erb +306 -0
- data/app/views/shared/_profile_image_selector.html.erb +165 -0
- data/app/views/shared/_theme_switcher.html.erb +64 -0
- data/app/views/shared/icons/_adjustments.html.erb +10 -0
- data/app/views/shared/icons/_alert_circle.html.erb +3 -0
- data/app/views/shared/icons/_alert_triangle.html.erb +3 -0
- data/app/views/shared/icons/_archive.html.erb +3 -0
- data/app/views/shared/icons/_arrow_down.html.erb +3 -0
- data/app/views/shared/icons/_arrow_left.html.erb +3 -0
- data/app/views/shared/icons/_arrow_up.html.erb +3 -0
- data/app/views/shared/icons/_arrows_pointing_in.html.erb +10 -0
- data/app/views/shared/icons/_arrows_pointing_out.html.erb +10 -0
- data/app/views/shared/icons/_artemis_logo.html.erb +26 -0
- data/app/views/shared/icons/_auth_banner.html.erb +1 -0
- data/app/views/shared/icons/_bars.html.erb +10 -0
- data/app/views/shared/icons/_bell.html.erb +3 -0
- data/app/views/shared/icons/_book.html.erb +3 -0
- data/app/views/shared/icons/_bookmark.html.erb +3 -0
- data/app/views/shared/icons/_box.html.erb +3 -0
- data/app/views/shared/icons/_brain.html.erb +3 -0
- data/app/views/shared/icons/_briefcase.html.erb +3 -0
- data/app/views/shared/icons/_calendar.html.erb +3 -0
- data/app/views/shared/icons/_camera.html.erb +4 -0
- data/app/views/shared/icons/_chart_bar.html.erb +3 -0
- data/app/views/shared/icons/_chart_line.html.erb +10 -0
- data/app/views/shared/icons/_chart_pie.html.erb +11 -0
- data/app/views/shared/icons/_chat.html.erb +3 -0
- data/app/views/shared/icons/_check.html.erb +3 -0
- data/app/views/shared/icons/_check_circle.html.erb +3 -0
- data/app/views/shared/icons/_cherry_blossom.html.erb +1516 -0
- data/app/views/shared/icons/_cherry_blossom_silhouette.html.erb +1016 -0
- data/app/views/shared/icons/_cherry_blossom_single_flower.html.erb +1125 -0
- data/app/views/shared/icons/_cherry_blossom_tree.html.erb +159 -0
- data/app/views/shared/icons/_chevron_down.html.erb +3 -0
- data/app/views/shared/icons/_chevron_right.html.erb +9 -0
- data/app/views/shared/icons/_clipboard.html.erb +3 -0
- data/app/views/shared/icons/_clock.html.erb +3 -0
- data/app/views/shared/icons/_close.html.erb +3 -0
- data/app/views/shared/icons/_cog.html.erb +4 -0
- data/app/views/shared/icons/_crop.html.erb +10 -0
- data/app/views/shared/icons/_crown.html.erb +3 -0
- data/app/views/shared/icons/_disc.html.erb +3 -0
- data/app/views/shared/icons/_download.html.erb +3 -0
- data/app/views/shared/icons/_dragonfly.html.erb +58 -0
- data/app/views/shared/icons/_duplicate.html.erb +4 -0
- data/app/views/shared/icons/_edit.html.erb +3 -0
- data/app/views/shared/icons/_envelope.html.erb +3 -0
- data/app/views/shared/icons/_eraser.html.erb +10 -0
- data/app/views/shared/icons/_external_link.html.erb +3 -0
- data/app/views/shared/icons/_eye.html.erb +4 -0
- data/app/views/shared/icons/_file_csv.html.erb +10 -0
- data/app/views/shared/icons/_file_export.html.erb +10 -0
- data/app/views/shared/icons/_file_image.html.erb +10 -0
- data/app/views/shared/icons/_file_import.html.erb +10 -0
- data/app/views/shared/icons/_file_question.html.erb +6 -0
- data/app/views/shared/icons/_film.html.erb +3 -0
- data/app/views/shared/icons/_filter.html.erb +3 -0
- data/app/views/shared/icons/_folder.html.erb +3 -0
- data/app/views/shared/icons/_folder_open.html.erb +3 -0
- data/app/views/shared/icons/_folder_plus.html.erb +3 -0
- data/app/views/shared/icons/_globe.html.erb +3 -0
- data/app/views/shared/icons/_google.html.erb +11 -0
- data/app/views/shared/icons/_heart.html.erb +3 -0
- data/app/views/shared/icons/_heart_broken.html.erb +11 -0
- data/app/views/shared/icons/_heart_pulse.html.erb +4 -0
- data/app/views/shared/icons/_history.html.erb +11 -0
- data/app/views/shared/icons/_home.html.erb +10 -0
- data/app/views/shared/icons/_image.html.erb +3 -0
- data/app/views/shared/icons/_inbox.html.erb +3 -0
- data/app/views/shared/icons/_info_circle.html.erb +10 -0
- data/app/views/shared/icons/_key.html.erb +3 -0
- data/app/views/shared/icons/_layers.html.erb +10 -0
- data/app/views/shared/icons/_lightbulb.html.erb +10 -0
- data/app/views/shared/icons/_lightning.html.erb +3 -0
- data/app/views/shared/icons/_list.html.erb +3 -0
- data/app/views/shared/icons/_lock.html.erb +3 -0
- data/app/views/shared/icons/_logout.html.erb +3 -0
- data/app/views/shared/icons/_magazine.html.erb +3 -0
- data/app/views/shared/icons/_magic.html.erb +3 -0
- data/app/views/shared/icons/_minus.html.erb +10 -0
- data/app/views/shared/icons/_mobile.html.erb +10 -0
- data/app/views/shared/icons/_moon.html.erb +3 -0
- data/app/views/shared/icons/_network.html.erb +10 -0
- data/app/views/shared/icons/_new_item_banner.html.erb +1 -0
- data/app/views/shared/icons/_ouroboros.html.erb +24 -0
- data/app/views/shared/icons/_package.html.erb +3 -0
- data/app/views/shared/icons/_palette.html.erb +3 -0
- data/app/views/shared/icons/_paper_plane.html.erb +10 -0
- data/app/views/shared/icons/_photo.html.erb +10 -0
- data/app/views/shared/icons/_play.html.erb +4 -0
- data/app/views/shared/icons/_plus.html.erb +3 -0
- data/app/views/shared/icons/_pocket.html.erb +11 -0
- data/app/views/shared/icons/_prysm-icon.html.erb +34 -0
- data/app/views/shared/icons/_prysm.html.erb +13 -0
- data/app/views/shared/icons/_pushbullet-1.html.erb +29 -0
- data/app/views/shared/icons/_pushbullet-2.html.erb +2 -0
- data/app/views/shared/icons/_puzzle.html.erb +10 -0
- data/app/views/shared/icons/_qrcode.html.erb +3 -0
- data/app/views/shared/icons/_question.html.erb +3 -0
- data/app/views/shared/icons/_receipt.html.erb +10 -0
- data/app/views/shared/icons/_redo.html.erb +3 -0
- data/app/views/shared/icons/_refresh.html.erb +3 -0
- data/app/views/shared/icons/_rocket.html.erb +10 -0
- data/app/views/shared/icons/_rss.html.erb +3 -0
- data/app/views/shared/icons/_save.html.erb +3 -0
- data/app/views/shared/icons/_search.html.erb +3 -0
- data/app/views/shared/icons/_search_minus.html.erb +10 -0
- data/app/views/shared/icons/_search_plus.html.erb +10 -0
- data/app/views/shared/icons/_server_error.html.erb +6 -0
- data/app/views/shared/icons/_share.html.erb +3 -0
- data/app/views/shared/icons/_shield_check.html.erb +3 -0
- data/app/views/shared/icons/_sign_in.html.erb +3 -0
- data/app/views/shared/icons/_spinner.html.erb +4 -0
- data/app/views/shared/icons/_star.html.erb +3 -0
- data/app/views/shared/icons/_store.html.erb +10 -0
- data/app/views/shared/icons/_sun.html.erb +3 -0
- data/app/views/shared/icons/_sync.html.erb +3 -0
- data/app/views/shared/icons/_table.html.erb +3 -0
- data/app/views/shared/icons/_tag.html.erb +3 -0
- data/app/views/shared/icons/_tags.html.erb +11 -0
- data/app/views/shared/icons/_tools.html.erb +4 -0
- data/app/views/shared/icons/_trash.html.erb +3 -0
- data/app/views/shared/icons/_undo.html.erb +3 -0
- data/app/views/shared/icons/_unlock.html.erb +3 -0
- data/app/views/shared/icons/_upload.html.erb +3 -0
- data/app/views/shared/icons/_user.html.erb +3 -0
- data/app/views/shared/icons/_user_circle.html.erb +10 -0
- data/app/views/shared/icons/_user_plus.html.erb +10 -0
- data/app/views/shared/icons/_video.html.erb +3 -0
- data/app/views/shared/icons/_wrench.html.erb +11 -0
- data/app/views/style_guide/index.html.erb +77 -0
- data/app/views/style_guide/sections/_alerts.html.erb +114 -0
- data/app/views/style_guide/sections/_badges.html.erb +78 -0
- data/app/views/style_guide/sections/_buttons.html.erb +130 -0
- data/app/views/style_guide/sections/_cards.html.erb +84 -0
- data/app/views/style_guide/sections/_colors.html.erb +106 -0
- data/app/views/style_guide/sections/_file_upload.html.erb +135 -0
- data/app/views/style_guide/sections/_forms.html.erb +129 -0
- data/app/views/style_guide/sections/_gradients.html.erb +253 -0
- data/app/views/style_guide/sections/_header.html.erb +12 -0
- data/app/views/style_guide/sections/_icons.html.erb +55 -0
- data/app/views/style_guide/sections/_images.html.erb +40 -0
- data/app/views/style_guide/sections/_loading.html.erb +242 -0
- data/app/views/style_guide/sections/_pagination.html.erb +212 -0
- data/app/views/style_guide/sections/_profile_components.html.erb +203 -0
- data/app/views/style_guide/sections/_theme_switcher.html.erb +72 -0
- data/app/views/style_guide/sections/_typography.html.erb +65 -0
- data/bin/ai-optimize-claude-md +540 -0
- data/bin/ai-review-local +345 -0
- data/bin/ai-security-review +585 -0
- data/bin/brakeman +9 -0
- data/bin/install-hooks +57 -0
- data/bin/rake +7 -0
- data/bin/rubocop +10 -0
- data/bin/verify_setup.rb +31 -0
- data/config/brakeman.ignore +28 -0
- data/config/initializers/neon_sakura.rb +15 -0
- data/config/license_overrides.yml +13 -0
- data/config/routes.rb +21 -0
- data/config/theme_mappings.yml +61 -0
- data/docs/PRYSM_ASSETS.md +210 -0
- data/docs/plans/extract_ai_reviewer_plan.md +151 -0
- data/docs/plans/neon_sakura_gem_plan.md +138 -0
- data/lib/neon_sakura/configuration.rb +94 -0
- data/lib/neon_sakura/engine.rb +48 -0
- data/lib/neon_sakura/icon_helper.rb +54 -0
- data/lib/neon_sakura/profile_helper.rb +24 -0
- data/lib/neon_sakura/stylesheet_helper.rb +40 -0
- data/lib/neon_sakura/theme_helper.rb +63 -0
- data/lib/neon_sakura/theme_importer.rb +112 -0
- data/lib/neon_sakura/version.rb +5 -0
- data/lib/neon_sakura.rb +13 -0
- data/neon_sakura.gemspec +50 -0
- data/package.json +18 -0
- data/scripts/git-hooks/post-merge +132 -0
- data/scripts/git-hooks/pre-commit +123 -0
- data/scripts/git-hooks/pre-push +127 -0
- data/scripts/license-check.rb +587 -0
- data/settings.local.json +12 -0
- data/yarn.lock +778 -0
- 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
|