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,212 @@
1
+ <section id="pagination">
2
+ <h2 class="text-3xl font-bold mb-4">Pagination (Pagy)</h2>
3
+ <p class="text-text-secondary mb-6">
4
+ The gem includes Pagy pagination styling. Install the Pagy gem to use pagination in your application.
5
+ </p>
6
+
7
+ <!-- Note about Pagy dependency -->
8
+ <div class="p-4 mb-6 bg-surface border border-border rounded-lg">
9
+ <p class="text-sm text-text-secondary">
10
+ <strong>Note:</strong> Pagy is not included as a dependency of this gem.
11
+ To use pagination, add <code class="px-2 py-1 bg-background rounded text-accent">gem 'pagy'</code> to your Gemfile.
12
+ </p>
13
+ </div>
14
+
15
+ <% if !@pagy_enabled %>
16
+ <!-- Pagy Examples Disabled -->
17
+ <div class="p-8 text-center border-2 border-dashed border-border rounded-lg bg-surface">
18
+ <div class="mb-4">
19
+ <%= render_theme_icon("alert_circle", css_class: "w-12 h-12 mx-auto text-text-muted") %>
20
+ </div>
21
+ <h3 class="text-lg font-semibold mb-2">Pagy Examples Disabled</h3>
22
+ <p class="text-text-secondary mb-4">
23
+ Pagy pagination examples are disabled in the style guide configuration.
24
+ </p>
25
+ <div class="bg-background p-3 rounded font-mono text-sm mb-4 inline-block">
26
+ config.style_guide_pagy_examples = true
27
+ </div>
28
+ <p class="text-sm text-text-muted">
29
+ Add this to your NeonSakura configuration to enable Pagy examples.
30
+ </p>
31
+ </div>
32
+ <% elsif @pagy_error %>
33
+ <!-- Pagy Error -->
34
+ <div class="p-8 border-2 border-alert rounded-lg bg-surface">
35
+ <div class="mb-4">
36
+ <%= render_theme_icon("alert_triangle", css_class: "w-12 h-12 text-alert") %>
37
+ </div>
38
+ <h3 class="text-lg font-semibold mb-2 text-alert">Pagy Configuration Error</h3>
39
+ <p class="text-text-secondary mb-4">
40
+ <%= @pagy_error %>
41
+ </p>
42
+ <div class="bg-background p-3 rounded font-mono text-sm">
43
+ # In config/initializers/neon_sakura.rb<br>
44
+ NeonSakura.configure do |config|<br>
45
+ &nbsp;&nbsp;config.style_guide_pagy_examples = false<br>
46
+ end
47
+ </div>
48
+ </div>
49
+ <% elsif @pagy_available %>
50
+ <!-- Items Display -->
51
+ <div class="mb-6">
52
+ <h3 class="text-xl font-semibold mb-4">Sample Items</h3>
53
+ <div class="space-y-2">
54
+ <% @page_items.each do |item| %>
55
+ <div class="p-3 border border-border rounded bg-surface">
56
+ <strong class="text-accent"><%= item[:title] %></strong>
57
+ <span class="text-text-secondary"> - <%= item[:description] %></span>
58
+ </div>
59
+ <% end %>
60
+ </div>
61
+ </div>
62
+
63
+ <!-- Pagy Info -->
64
+ <div class="code-example-wrapper">
65
+ <div class="code-example-preview">
66
+ <h3 class="text-xl font-semibold mb-4">1. Pagy Info Helper</h3>
67
+ <p class="text-text-secondary mb-4">Shows pagination summary text</p>
68
+ <div class="p-4 bg-background rounded border border-border">
69
+ <%== @pagy.info_tag %>
70
+ </div>
71
+ </div>
72
+ <div class="code-example-code">
73
+ <pre><code>&lt;%== @pagy.info_tag %&gt;</code></pre>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- Pagy Series Nav -->
78
+ <div class="code-example-wrapper">
79
+ <div class="code-example-preview">
80
+ <h3 class="text-xl font-semibold mb-4">2. Series Navigation (JavaScript)</h3>
81
+ <p class="text-text-secondary mb-4">Interactive page number navigation with prev/next</p>
82
+ <div class="flex justify-center p-4 bg-background rounded border border-border">
83
+ <%== @pagy.series_nav_js %>
84
+ </div>
85
+ </div>
86
+ <div class="code-example-code">
87
+ <pre><code>&lt;%== @pagy.series_nav_js %&gt;</code></pre>
88
+ </div>
89
+ </div>
90
+
91
+ <!-- Pagy Series Nav (Non-JS) -->
92
+ <div class="code-example-wrapper">
93
+ <div class="code-example-preview">
94
+ <h3 class="text-xl font-semibold mb-4">3. Series Navigation (No JavaScript)</h3>
95
+ <p class="text-text-secondary mb-4">Standard page number navigation (no JavaScript required)</p>
96
+ <div class="flex justify-center p-4 bg-background rounded border border-border">
97
+ <%== @pagy.series_nav %>
98
+ </div>
99
+ </div>
100
+ <div class="code-example-code">
101
+ <pre><code>&lt;%== @pagy.series_nav %&gt;</code></pre>
102
+ </div>
103
+ </div>
104
+
105
+ <!-- Pagy Input Nav -->
106
+ <div class="code-example-wrapper">
107
+ <div class="code-example-preview">
108
+ <h3 class="text-xl font-semibold mb-4">4. Input Navigation</h3>
109
+ <p class="text-text-secondary mb-4">Text input for direct page number entry</p>
110
+ <div class="flex justify-center p-4 bg-background rounded border border-border">
111
+ <%== @pagy.input_nav_js %>
112
+ </div>
113
+ </div>
114
+ <div class="code-example-code">
115
+ <pre><code>&lt;%== @pagy.input_nav_js %&gt;</code></pre>
116
+ </div>
117
+ </div>
118
+
119
+ <!-- Controller Code Example -->
120
+ <div class="mt-6 p-6 bg-surface border border-border rounded-lg">
121
+ <h3 class="text-lg font-semibold mb-4">Controller Setup</h3>
122
+ <div class="bg-background p-4 rounded font-mono text-sm">
123
+ <pre><code class="text-text-secondary"># Include Pagy module in controller
124
+ include Pagy::Method # Pagy 43+
125
+
126
+ # Paginate a collection
127
+ @pagy, @items = pagy(:offset, Item.all, limit: 10)
128
+
129
+ # Or with ActiveRecord (most common)
130
+ @pagy, @users = pagy(:offset, User.all, limit: 20)</code></pre>
131
+ </div>
132
+ </div>
133
+
134
+ <!-- Available Helpers Reference -->
135
+ <div class="mt-6 p-6 bg-surface border border-border rounded-lg">
136
+ <h3 class="text-lg font-semibold mb-4">Available Pagy Methods (Pagy 43.x)</h3>
137
+ <div class="space-y-3 font-mono text-sm">
138
+ <div class="p-3 bg-background rounded">
139
+ <code class="text-accent">@pagy.info_tag</code>
140
+ <p class="text-text-muted text-xs mt-1">Pagination summary text (e.g., "Displaying items 1-10 of 100")</p>
141
+ </div>
142
+ <div class="p-3 bg-background rounded">
143
+ <code class="text-accent">@pagy.series_nav_js</code>
144
+ <p class="text-text-muted text-xs mt-1">Interactive page number navigation (requires JavaScript)</p>
145
+ </div>
146
+ <div class="p-3 bg-background rounded">
147
+ <code class="text-accent">@pagy.series_nav</code>
148
+ <p class="text-text-muted text-xs mt-1">Standard page number navigation (no JavaScript)</p>
149
+ </div>
150
+ <div class="p-3 bg-background rounded">
151
+ <code class="text-accent">@pagy.input_nav_js</code>
152
+ <p class="text-text-muted text-xs mt-1">Direct page number input field (requires JavaScript)</p>
153
+ </div>
154
+ <div class="p-3 bg-background rounded">
155
+ <code class="text-accent">@pagy.limit_tag_js</code>
156
+ <p class="text-text-muted text-xs mt-1">Items per page selector (requires limit extra)</p>
157
+ </div>
158
+ </div>
159
+ <p class="text-xs text-text-muted mt-4">
160
+ Note: Pagy 43.x uses instance methods on the Pagy object, not view helpers.
161
+ Load required helpers in <code>config/initializers/pagy.rb</code>
162
+ </p>
163
+ </div>
164
+ <% else %>
165
+ <!-- Pagy Not Installed -->
166
+ <div class="p-8 text-center border-2 border-dashed border-border rounded-lg bg-surface">
167
+ <div class="mb-4">
168
+ <%= render_theme_icon("package", css_class: "w-12 h-12 mx-auto text-text-muted") %>
169
+ </div>
170
+ <h3 class="text-lg font-semibold mb-2">Pagy Not Installed</h3>
171
+ <p class="text-text-secondary mb-4">
172
+ To see pagination examples, install Pagy:
173
+ </p>
174
+ <div class="bg-background p-3 rounded font-mono text-sm mb-4 inline-block">
175
+ gem 'pagy'
176
+ </div>
177
+ <p class="text-sm text-text-muted">
178
+ Then run <code class="px-2 py-1 bg-background rounded">bundle install</code> and restart the server.
179
+ </p>
180
+ </div>
181
+
182
+ <!-- Code Example (Static) -->
183
+ <div class="code-example-wrapper mt-6">
184
+ <div class="code-example-preview">
185
+ <h3 class="text-xl font-semibold mb-4">Example Implementation</h3>
186
+ <p class="text-text-secondary">Here's how Pagy pagination would look once installed:</p>
187
+ </div>
188
+ <div class="code-example-code">
189
+ <pre><code># Gemfile
190
+ gem 'pagy'
191
+
192
+ # Controller
193
+ include Pagy::Backend
194
+ @pagy, @items = pagy(Item.all, items: 10)
195
+
196
+ # View
197
+ &lt;%== pagy_nav(@pagy) %&gt;</code></pre>
198
+ </div>
199
+ </div>
200
+ <% end %>
201
+
202
+ <!-- Documentation Link -->
203
+ <div class="mt-6 p-4 bg-background border border-border rounded-lg">
204
+ <p class="text-sm">
205
+ <strong>Documentation:</strong>
206
+ <a href="https://ddnexus.github.io/pagy/" target="_blank" rel="noopener noreferrer" class="text-accent hover:underline">
207
+ Pagy Official Documentation
208
+ <%= render_theme_icon("external_link", css_class: "w-3 h-3 inline ml-1") %>
209
+ </a>
210
+ </p>
211
+ </div>
212
+ </section>
@@ -0,0 +1,203 @@
1
+ <section id="profile-components" class="mb-12">
2
+ <h2 class="text-3xl font-bold mb-6 bg-gradient-to-r from-gradient-from to-gradient-to bg-clip-text text-transparent">
3
+ Profile Components
4
+ </h2>
5
+
6
+ <p class="text-text-secondary mb-6">
7
+ Components for user profile management including Gravatar support and profile image selection.
8
+ </p>
9
+
10
+ <!-- Gravatar Helper -->
11
+ <div class="mb-8">
12
+ <h3 class="text-xl font-semibold mb-4 text-text-primary">Gravatar Helper</h3>
13
+ <p class="text-text-secondary mb-4">
14
+ The <code class="bg-surface px-2 py-1 rounded">gravatar_url</code> helper generates Gravatar URLs from email addresses.
15
+ </p>
16
+ <div class="card p-6">
17
+ <div class="flex items-center gap-4">
18
+ <img src="<%= gravatar_url("user@example.com", size: 80) %>"
19
+ alt="Gravatar Example"
20
+ class="rounded-full w-20 h-20">
21
+ <div>
22
+ <p class="text-text-primary font-medium">user@example.com</p>
23
+ <p class="text-text-muted text-sm">Default identicon for unregistered emails</p>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ <div class="mt-4">
28
+ <h4 class="text-sm font-semibold text-text-muted mb-2">Code Example:</h4>
29
+ <pre class="bg-surface p-4 rounded-lg overflow-x-auto"><code>&lt;img src="&lt;%= gravatar_url("user@example.com", size: 80) %&gt;"
30
+ alt="Gravatar"
31
+ class="rounded-full w-20 h-20"&gt;</code></pre>
32
+ </div>
33
+ </div>
34
+
35
+ <!-- Profile Image Selector (Demo) -->
36
+ <div class="mb-8">
37
+ <h3 class="text-xl font-semibold mb-4 text-text-primary">Profile Image Selector Component</h3>
38
+ <p class="text-text-secondary mb-4">
39
+ Radio card selection UI for choosing profile image source. Requires a form context and user object.
40
+ </p>
41
+ <div class="card p-6">
42
+ <p class="text-text-muted mb-4">
43
+ <strong>Note:</strong> This component requires a form builder and user object.
44
+ It's designed to be used within a form like <code class="bg-surface px-2 py-1 rounded">form_with model: @user</code>.
45
+ </p>
46
+
47
+ <!-- Visual mockup -->
48
+ <div class="profile-image-options">
49
+ <div class="profile-image-option">
50
+ <div class="profile-image-card selected">
51
+ <div class="profile-image-card-body">
52
+ <%= render "shared/icons/user", css_class: "w-15 h-15 text-text-muted" %>
53
+ <h6 class="profile-image-card-title">Default</h6>
54
+ <p class="profile-image-card-text">Generated avatar</p>
55
+ <div class="profile-image-selection-indicator">
56
+ <%= render "shared/icons/check_circle", css_class: "w-5 h-5 text-accent" %>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="profile-image-option">
63
+ <div class="profile-image-card">
64
+ <div class="profile-image-card-body">
65
+ <img src="<%= gravatar_url("demo@example.com", size: 60) %>"
66
+ alt="Gravatar"
67
+ class="profile-image-preview">
68
+ <h6 class="profile-image-card-title">
69
+ <%= render "shared/icons/user_circle", css_class: "w-4 h-4 inline-block mr-1" %>
70
+ Gravatar
71
+ </h6>
72
+ <p class="profile-image-card-text">Use your Gravatar image</p>
73
+ <div class="profile-image-selection-indicator">
74
+ <%= render "shared/icons/check_circle", css_class: "w-5 h-5 text-accent" %>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <div class="profile-image-option">
81
+ <div class="profile-image-card disabled">
82
+ <div class="profile-image-card-body">
83
+ <%= render "shared/icons/upload", css_class: "w-15 h-15 text-text-muted" %>
84
+ <h6 class="profile-image-card-title">
85
+ <%= render "shared/icons/upload", css_class: "w-4 h-4 inline-block mr-1" %>
86
+ Upload
87
+ </h6>
88
+ <p class="profile-image-card-text">Upload your own image</p>
89
+ <div class="profile-image-selection-indicator">
90
+ <%= render "shared/icons/check_circle", css_class: "w-5 h-5 text-muted" %>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ <div class="mt-4">
98
+ <h4 class="text-sm font-semibold text-text-muted mb-2">Code Example:</h4>
99
+ <pre class="bg-surface p-4 rounded-lg overflow-x-auto"><code>&lt;%= form_with model: @user do |f| %&gt;
100
+ &lt;%= render "shared/profile_image_selector",
101
+ user: @user,
102
+ form: f,
103
+ sources: [:default, :gravatar, :upload],
104
+ show_upload: true %&gt;
105
+ &lt;% end %&gt;</code></pre>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- Parameters -->
110
+ <div class="mt-8">
111
+ <h3 class="text-xl font-semibold mb-4 text-text-primary">Profile Image Selector Parameters</h3>
112
+ <div class="overflow-x-auto">
113
+ <table class="w-full">
114
+ <thead>
115
+ <tr>
116
+ <th class="text-left py-2 px-4 border-b border-border">Parameter</th>
117
+ <th class="text-left py-2 px-4 border-b border-border">Type</th>
118
+ <th class="text-left py-2 px-4 border-b border-border">Default</th>
119
+ <th class="text-left py-2 px-4 border-b border-border">Description</th>
120
+ </tr>
121
+ </thead>
122
+ <tbody>
123
+ <tr>
124
+ <td class="py-2 px-4 border-b border-border"><code>user</code></td>
125
+ <td class="py-2 px-4 border-b border-border">Object</td>
126
+ <td class="py-2 px-4 border-b border-border">Required</td>
127
+ <td class="py-2 px-4 border-b border-border">User object with profile_image_source attribute</td>
128
+ </tr>
129
+ <tr>
130
+ <td class="py-2 px-4 border-b border-border"><code>form</code></td>
131
+ <td class="py-2 px-4 border-b border-border">FormBuilder</td>
132
+ <td class="py-2 px-4 border-b border-border">Required</td>
133
+ <td class="py-2 px-4 border-b border-border">Rails form builder object</td>
134
+ </tr>
135
+ <tr>
136
+ <td class="py-2 px-4 border-b border-border"><code>field_name</code></td>
137
+ <td class="py-2 px-4 border-b border-border">Symbol</td>
138
+ <td class="py-2 px-4 border-b border-border">:profile_image_source</td>
139
+ <td class="py-2 px-4 border-b border-border">Form field name</td>
140
+ </tr>
141
+ <tr>
142
+ <td class="py-2 px-4 border-b border-border"><code>sources</code></td>
143
+ <td class="py-2 px-4 border-b border-border">Array</td>
144
+ <td class="py-2 px-4 border-b border-border">[:default, :gravatar]</td>
145
+ <td class="py-2 px-4 border-b border-border">Available image sources</td>
146
+ </tr>
147
+ <tr>
148
+ <td class="py-2 px-4 border-b border-border"><code>show_upload</code></td>
149
+ <td class="py-2 px-4 border-b border-border">Boolean</td>
150
+ <td class="py-2 px-4 border-b border-border">false</td>
151
+ <td class="py-2 px-4 border-b border-border">Show upload option (requires ActiveStorage)</td>
152
+ </tr>
153
+ </tbody>
154
+ </table>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Gravatar Helper Parameters -->
159
+ <div class="mt-8">
160
+ <h3 class="text-xl font-semibold mb-4 text-text-primary">Gravatar Helper Parameters</h3>
161
+ <div class="overflow-x-auto">
162
+ <table class="w-full">
163
+ <thead>
164
+ <tr>
165
+ <th class="text-left py-2 px-4 border-b border-border">Parameter</th>
166
+ <th class="text-left py-2 px-4 border-b border-border">Type</th>
167
+ <th class="text-left py-2 px-4 border-b border-border">Default</th>
168
+ <th class="text-left py-2 px-4 border-b border-border">Description</th>
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ <tr>
173
+ <td class="py-2 px-4 border-b border-border"><code>email</code></td>
174
+ <td class="py-2 px-4 border-b border-border">String</td>
175
+ <td class="py-2 px-4 border-b border-border">Required</td>
176
+ <td class="py-2 px-4 border-b border-border">Email address for Gravatar lookup</td>
177
+ </tr>
178
+ <tr>
179
+ <td class="py-2 px-4 border-b border-border"><code>size</code></td>
180
+ <td class="py-2 px-4 border-b border-border">Integer</td>
181
+ <td class="py-2 px-4 border-b border-border">80</td>
182
+ <td class="py-2 px-4 border-b border-border">Image size in pixels</td>
183
+ </tr>
184
+ </tbody>
185
+ </table>
186
+ </div>
187
+ </div>
188
+
189
+ <!-- Features -->
190
+ <div class="mt-8">
191
+ <h3 class="text-xl font-semibold mb-4 text-text-primary">Features</h3>
192
+ <ul class="list-disc list-inside text-text-secondary space-y-2">
193
+ <li>Gravatar integration with automatic fallback to identicon</li>
194
+ <li>Profile image selector with radio card UI</li>
195
+ <li>Multiple image source options (default, Gravatar, upload)</li>
196
+ <li>Theme-aware styling with selection indicators</li>
197
+ <li>Optional ActiveStorage integration for uploads</li>
198
+ <li>Fully accessible with hidden radio buttons</li>
199
+ <li>Responsive grid layout</li>
200
+ <li>Email normalization (lowercase, strip whitespace)</li>
201
+ </ul>
202
+ </div>
203
+ </section>
@@ -0,0 +1,72 @@
1
+ <section id="theme-switcher">
2
+ <h2 class="text-3xl font-bold mb-4">Theme Preview</h2>
3
+ <p class="text-text-secondary mb-4">
4
+ Switch themes to preview how all components look in different color schemes. Changes are saved to localStorage.
5
+ </p>
6
+
7
+ <div class="flex flex-wrap gap-3">
8
+ <% @available_themes.each do |theme| %>
9
+ <%
10
+ is_current = @current_theme[:name] == theme[:name] && @current_theme[:mode] == theme[:mode]
11
+ %>
12
+ <button
13
+ type="button"
14
+ data-theme-item
15
+ data-theme-name="<%= theme[:name] %>"
16
+ data-theme-mode="<%= theme[:mode] %>"
17
+ class="px-4 py-2 rounded-lg border-2 transition-all <%= is_current ? 'border-accent bg-accent font-semibold' : 'border-border bg-surface text-text-primary hover:border-accent' %>"
18
+ style="<%= is_current ? 'color: white;' : '' %>"
19
+ aria-label="Switch to <%= theme[:label] %>"
20
+ aria-pressed="<%= is_current %>"
21
+ >
22
+ <span class="flex items-center gap-2">
23
+ <% if theme[:mode] == 'light' %>
24
+ <%= render_theme_icon("sun", css_class: "w-4 h-4") %>
25
+ <% else %>
26
+ <%= render_theme_icon("moon", css_class: "w-4 h-4") %>
27
+ <% end %>
28
+ <%= theme[:label] %>
29
+ </span>
30
+ </button>
31
+ <% end %>
32
+ </div>
33
+ </section>
34
+
35
+ <script>
36
+ // Theme switcher initialization
37
+ document.addEventListener('DOMContentLoaded', function() {
38
+ const buttons = document.querySelectorAll('[data-theme-item]');
39
+
40
+ buttons.forEach(button => {
41
+ button.addEventListener('click', function() {
42
+ const themeName = this.dataset.themeName;
43
+ const themeMode = this.dataset.themeMode;
44
+
45
+ // Update HTML attributes
46
+ document.documentElement.setAttribute('data-theme-name', themeName);
47
+ document.documentElement.setAttribute('data-theme-mode', themeMode);
48
+
49
+ // Persist to localStorage
50
+ localStorage.setItem('neon_sakura_theme_name', themeName);
51
+ localStorage.setItem('neon_sakura_theme_mode', themeMode);
52
+
53
+ // Update button states
54
+ buttons.forEach(btn => {
55
+ const isActive = btn.dataset.themeName === themeName && btn.dataset.themeMode === themeMode;
56
+
57
+ if (isActive) {
58
+ btn.classList.add('border-accent', 'bg-accent', 'font-semibold');
59
+ btn.classList.remove('border-border', 'bg-surface', 'text-text-primary', 'hover:border-accent');
60
+ btn.style.color = 'white';
61
+ btn.setAttribute('aria-pressed', 'true');
62
+ } else {
63
+ btn.classList.remove('border-accent', 'bg-accent', 'font-semibold');
64
+ btn.classList.add('border-border', 'bg-surface', 'text-text-primary', 'hover:border-accent');
65
+ btn.style.color = '';
66
+ btn.setAttribute('aria-pressed', 'false');
67
+ }
68
+ });
69
+ });
70
+ });
71
+ });
72
+ </script>
@@ -0,0 +1,65 @@
1
+ <section id="typography">
2
+ <h2 class="text-3xl font-bold mb-4">Typography</h2>
3
+ <p class="text-text-secondary mb-6">
4
+ Text styling, heading levels, and typography utilities.
5
+ </p>
6
+
7
+ <!-- Headings -->
8
+ <div class="code-example-wrapper">
9
+ <div class="code-example-preview">
10
+ <h3 class="text-xl font-semibold mb-4">Headings</h3>
11
+ <div class="space-y-4">
12
+ <h1 class="text-4xl font-bold">Heading 1 (text-4xl font-bold)</h1>
13
+ <h2 class="text-3xl font-bold">Heading 2 (text-3xl font-bold)</h2>
14
+ <h3 class="text-2xl font-semibold">Heading 3 (text-2xl font-semibold)</h3>
15
+ <h4 class="text-xl font-semibold">Heading 4 (text-xl font-semibold)</h4>
16
+ <h5 class="text-lg font-medium">Heading 5 (text-lg font-medium)</h5>
17
+ <h6 class="text-base font-medium">Heading 6 (text-base font-medium)</h6>
18
+ </div>
19
+ </div>
20
+ <div class="code-example-code">
21
+ <pre><code>&lt;h1 class="text-4xl font-bold"&gt;Heading 1&lt;/h1&gt;
22
+ &lt;h2 class="text-3xl font-bold"&gt;Heading 2&lt;/h2&gt;</code></pre>
23
+ </div>
24
+ </div>
25
+
26
+ <!-- Text Colors -->
27
+ <div class="code-example-wrapper">
28
+ <div class="code-example-preview">
29
+ <h3 class="text-xl font-semibold mb-4">Text Colors</h3>
30
+ <div class="space-y-2">
31
+ <p class="text-text-primary">Primary text (text-text-primary)</p>
32
+ <p class="text-text-secondary">Secondary text (text-text-secondary)</p>
33
+ <p class="text-text-muted">Muted text (text-text-muted)</p>
34
+ <p class="text-accent">Accent text (text-accent)</p>
35
+ <p class="text-notification">Notification text (text-notification)</p>
36
+ <p class="text-alert">Alert text (text-alert)</p>
37
+ <p class="text-warning">Warning text (text-warning)</p>
38
+ </div>
39
+ </div>
40
+ <div class="code-example-code">
41
+ <pre><code>&lt;p class="text-text-primary"&gt;Primary text&lt;/p&gt;
42
+ &lt;p class="text-accent"&gt;Accent text&lt;/p&gt;</code></pre>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- Text Gradients -->
47
+ <div class="code-example-wrapper">
48
+ <div class="code-example-preview">
49
+ <h3 class="text-xl font-semibold mb-4">Text Gradients</h3>
50
+ <div class="space-y-4">
51
+ <h2 class="text-4xl font-bold bg-gradient-to-r from-text-gradient-from to-text-gradient-to bg-clip-text text-transparent">
52
+ Gradient Heading
53
+ </h2>
54
+ <p class="text-lg bg-gradient-to-r from-accent to-notification bg-clip-text text-transparent">
55
+ Gradient text with custom colors
56
+ </p>
57
+ </div>
58
+ </div>
59
+ <div class="code-example-code">
60
+ <pre><code>&lt;h2 class="bg-gradient-to-r from-text-gradient-from to-text-gradient-to bg-clip-text text-transparent"&gt;
61
+ Gradient Heading
62
+ &lt;/h2&gt;</code></pre>
63
+ </div>
64
+ </div>
65
+ </section>