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,453 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Mission Control - <%= page_title %></title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <meta name="viewport" content="width=device-width,initial-scale=1">
9
+ <meta name="turbo-cache-control" content="no-cache">
10
+
11
+ <link rel="icon" href="/icon.png" type="image/png">
12
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
13
+ <link rel="apple-touch-icon" href="/icon.png">
14
+
15
+ <%= stylesheet_link_tag "mission_control/jobs/bulma.min" %>
16
+ <%= stylesheet_link_tag "mission_control/jobs/application", "data-turbo-track": "reload" %>
17
+ <%= javascript_importmap_tags "application", importmap: MissionControl::Jobs.importmap %>
18
+
19
+ <style>
20
+ /* Override Bulma with our dark theme */
21
+ .mc-nav {
22
+ background-color: #111827;
23
+ padding: 1rem 0;
24
+ margin-bottom: 1.5rem;
25
+ border-bottom: 1px solid #374151;
26
+ }
27
+
28
+ .mc-nav nav {
29
+ display: flex;
30
+ justify-content: center;
31
+ flex-wrap: wrap;
32
+ gap: 1rem;
33
+ }
34
+
35
+ .mc-nav a {
36
+ display: inline-flex;
37
+ align-items: center;
38
+ color: #ffffff;
39
+ transition: color 150ms;
40
+ font-size: 0.875rem;
41
+ text-decoration: none;
42
+ }
43
+
44
+ .mc-nav a:hover {
45
+ color: #06b6d4;
46
+ }
47
+
48
+ .mc-nav svg {
49
+ width: 1rem;
50
+ height: 1rem;
51
+ margin-right: 0.5rem;
52
+ }
53
+
54
+ .mc-nav form {
55
+ display: inline;
56
+ margin: 0;
57
+ }
58
+
59
+ .mc-nav button {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ color: #ffffff;
63
+ transition: color 150ms;
64
+ font-size: 0.875rem;
65
+ text-decoration: none;
66
+ background: none;
67
+ border: none;
68
+ cursor: pointer;
69
+ padding: 0;
70
+ font-family: inherit;
71
+ }
72
+
73
+ .mc-nav button:hover {
74
+ color: #06b6d4;
75
+ }
76
+
77
+ .mc-nav button svg {
78
+ width: 1rem;
79
+ height: 1rem;
80
+ margin-right: 0.5rem;
81
+ }
82
+
83
+ /* Dropdown styles */
84
+ .mc-dropdown-container {
85
+ position: relative;
86
+ display: inline-block;
87
+ }
88
+
89
+ .mc-dropdown-toggle {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ gap: 0.25rem;
93
+ color: #ffffff;
94
+ transition: color 150ms;
95
+ font-size: 0.875rem;
96
+ text-decoration: none;
97
+ background: none;
98
+ border: none;
99
+ cursor: pointer;
100
+ padding: 0;
101
+ font-family: inherit;
102
+ }
103
+
104
+ .mc-dropdown-toggle:hover {
105
+ color: #06b6d4;
106
+ }
107
+
108
+ .mc-dropdown-menu {
109
+ display: none;
110
+ position: absolute;
111
+ top: 100%;
112
+ left: 0;
113
+ margin-top: 0.5rem;
114
+ background-color: #1f2937;
115
+ border: 1px solid #374151;
116
+ border-radius: 0.5rem;
117
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
118
+ min-width: 10rem;
119
+ z-index: 50;
120
+ }
121
+
122
+ .mc-nav .open .mc-dropdown-menu {
123
+ display: block;
124
+ }
125
+
126
+ .mc-dropdown-menu a {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 0.5rem;
130
+ padding: 0.75rem 1rem;
131
+ color: #ffffff;
132
+ text-decoration: none;
133
+ font-size: 0.875rem;
134
+ transition: all 150ms;
135
+ }
136
+
137
+ .mc-dropdown-menu a:hover {
138
+ background-color: #374151;
139
+ color: #06b6d4;
140
+ }
141
+
142
+ .mc-dropdown-menu a:last-child {
143
+ border-bottom-left-radius: 0.5rem;
144
+ border-bottom-right-radius: 0.5rem;
145
+ }
146
+
147
+ /* Dropdown divider */
148
+ .mc-dropdown-divider {
149
+ height: 1px;
150
+ background-color: #374151;
151
+ margin: 0.5rem 0;
152
+ }
153
+
154
+ /* Nested submenu */
155
+ .mc-dropdown-submenu {
156
+ position: relative;
157
+ }
158
+
159
+ .mc-dropdown-submenu-toggle {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 0.5rem;
163
+ padding: 0.75rem 1rem;
164
+ color: #ffffff;
165
+ text-decoration: none;
166
+ font-size: 0.875rem;
167
+ transition: all 150ms;
168
+ cursor: pointer;
169
+ width: 100%;
170
+ }
171
+
172
+ .mc-dropdown-submenu-toggle:hover {
173
+ background-color: #374151;
174
+ color: #06b6d4;
175
+ }
176
+
177
+ .mc-dropdown-submenu-items {
178
+ display: none;
179
+ position: absolute;
180
+ left: 100%;
181
+ top: 0;
182
+ margin-left: 0.25rem;
183
+ background-color: #1f2937;
184
+ border: 1px solid #374151;
185
+ border-radius: 0.5rem;
186
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
187
+ min-width: 10rem;
188
+ z-index: 51;
189
+ }
190
+
191
+ .mc-dropdown-submenu:hover .mc-dropdown-submenu-items {
192
+ display: block;
193
+ }
194
+
195
+ .mc-dropdown-submenu-items a {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 0.5rem;
199
+ padding: 0.75rem 1rem;
200
+ color: #ffffff;
201
+ text-decoration: none;
202
+ font-size: 0.875rem;
203
+ transition: all 150ms;
204
+ white-space: nowrap;
205
+ }
206
+
207
+ .mc-dropdown-submenu-items a:hover {
208
+ background-color: #374151;
209
+ color: #06b6d4;
210
+ }
211
+
212
+ .mc-dropdown-submenu-items a:first-child {
213
+ border-top-left-radius: 0.5rem;
214
+ border-top-right-radius: 0.5rem;
215
+ }
216
+
217
+ .mc-dropdown-submenu-items a:last-child {
218
+ border-bottom-left-radius: 0.5rem;
219
+ border-bottom-right-radius: 0.5rem;
220
+ }
221
+
222
+ /* Back link and auto-refresh inline section */
223
+ .mc-back-section {
224
+ background-color: #1f2937;
225
+ padding: 0.75rem 0;
226
+ border-bottom: 1px solid #374151;
227
+ }
228
+
229
+ .mc-back-section .container {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 1rem;
233
+ }
234
+
235
+ .mc-back-section a,
236
+ .mc-back-section button {
237
+ display: inline-flex;
238
+ align-items: center;
239
+ color: #67e8f9;
240
+ transition: color 150ms;
241
+ font-size: 0.875rem;
242
+ text-decoration: none;
243
+ background: none;
244
+ border: none;
245
+ cursor: pointer;
246
+ padding: 0;
247
+ font-family: inherit;
248
+ }
249
+
250
+ .mc-back-section a:hover,
251
+ .mc-back-section button:hover {
252
+ color: #a5f3fc;
253
+ }
254
+
255
+ .mc-back-section svg {
256
+ width: 1rem;
257
+ height: 1rem;
258
+ margin-right: 0.5rem;
259
+ }
260
+
261
+ /* Hide Mission Control's own "Back to main app" link when using custom navigation */
262
+ .mc-nav ~ section .button.is-text:first-child {
263
+ display: none;
264
+ }
265
+ </style>
266
+
267
+ <script>
268
+ // Vanilla JavaScript dropdown for Mission Control (Stimulus controllers not loaded here)
269
+ document.addEventListener('DOMContentLoaded', function() {
270
+ // Hide Mission Control's "Back to main app" link when custom nav is configured
271
+ const customNavExists = document.querySelector('.mc-nav');
272
+ if (customNavExists) {
273
+ // Find and hide any "Back to main app" links in the main section
274
+ const mainSection = document.querySelector('section.section');
275
+ if (mainSection) {
276
+ const backLinks = mainSection.querySelectorAll('a.button.is-text');
277
+ backLinks.forEach(function(link) {
278
+ if (link.textContent.includes('Back to main app') || link.textContent.includes('← Back to main app')) {
279
+ link.style.display = 'none';
280
+ }
281
+ });
282
+ }
283
+ }
284
+
285
+ const dropdownContainer = document.querySelector('.mc-dropdown-container');
286
+ const dropdownToggle = document.querySelector('.mc-dropdown-toggle');
287
+
288
+ if (dropdownToggle && dropdownContainer) {
289
+ dropdownToggle.addEventListener('click', function(event) {
290
+ event.preventDefault();
291
+ event.stopPropagation();
292
+
293
+ const isOpen = dropdownContainer.classList.contains('open');
294
+
295
+ // Close all other dropdowns
296
+ document.querySelectorAll('.mc-dropdown-container.open').forEach(function(dropdown) {
297
+ dropdown.classList.remove('open');
298
+ });
299
+
300
+ // Toggle this dropdown
301
+ if (!isOpen) {
302
+ dropdownContainer.classList.add('open');
303
+
304
+ // Close on click outside
305
+ setTimeout(function() {
306
+ document.addEventListener('click', function closeDropdown(e) {
307
+ if (!dropdownContainer.contains(e.target)) {
308
+ dropdownContainer.classList.remove('open');
309
+ document.removeEventListener('click', closeDropdown);
310
+ }
311
+ });
312
+ }, 0);
313
+ }
314
+ });
315
+ }
316
+
317
+ // Auto-refresh functionality
318
+ let autoRefreshEnabled = localStorage.getItem('jobsAutoRefresh') === 'true';
319
+ const refreshInterval = 30000; // 30 seconds
320
+ let refreshTimer;
321
+
322
+ function toggleAutoRefresh() {
323
+ autoRefreshEnabled = !autoRefreshEnabled;
324
+ localStorage.setItem('jobsAutoRefresh', autoRefreshEnabled);
325
+
326
+ if (autoRefreshEnabled) {
327
+ startAutoRefresh();
328
+ document.getElementById('auto-refresh-btn').textContent = 'Pause Auto-refresh';
329
+ } else {
330
+ stopAutoRefresh();
331
+ document.getElementById('auto-refresh-btn').textContent = 'Start Auto-refresh';
332
+ }
333
+ }
334
+
335
+ function startAutoRefresh() {
336
+ refreshTimer = setInterval(function() {
337
+ if (!document.hidden) {
338
+ Turbo.visit(window.location.href, { action: 'replace' });
339
+ }
340
+ }, refreshInterval);
341
+ }
342
+
343
+ function stopAutoRefresh() {
344
+ if (refreshTimer) {
345
+ clearInterval(refreshTimer);
346
+ }
347
+ }
348
+
349
+ // Initialize auto-refresh button
350
+ const autoRefreshBtn = document.getElementById('auto-refresh-btn');
351
+ if (autoRefreshBtn) {
352
+ autoRefreshBtn.addEventListener('click', toggleAutoRefresh);
353
+ autoRefreshBtn.textContent = autoRefreshEnabled ? 'Pause Auto-refresh' : 'Start Auto-refresh';
354
+
355
+ if (autoRefreshEnabled) {
356
+ startAutoRefresh();
357
+ }
358
+ }
359
+ });
360
+ </script>
361
+ </head>
362
+ <body>
363
+
364
+ <!-- Custom Navigation (configurable via NeonSakura.config.nav_links) -->
365
+ <% if NeonSakura.config.nav_links.present? %>
366
+ <div class="mc-nav">
367
+ <div class="container">
368
+ <nav>
369
+ <% NeonSakura.config.nav_links.each do |nav_item| %>
370
+ <% case nav_item[:type] %>
371
+ <% when "link" %>
372
+ <a href="<%= nav_item[:path] %>"<%= " target=\"#{nav_item[:target]}\" rel=\"noopener noreferrer\"" if nav_item[:target] %>>
373
+ <%= render "shared/icons/#{nav_item[:icon]}" %>
374
+ <%= nav_item[:name] %>
375
+ </a>
376
+
377
+ <% when "dropdown" %>
378
+ <div class="mc-dropdown-container">
379
+ <button class="mc-dropdown-toggle" aria-label="<%= nav_item[:name] %> menu" aria-haspopup="true" aria-expanded="false">
380
+ <%= render "shared/icons/#{nav_item[:icon]}" %>
381
+ <%= nav_item[:name] %>
382
+ <%= render "shared/icons/chevron_down", css_class: "w-3 h-3" %>
383
+ </button>
384
+ <div class="mc-dropdown-menu">
385
+ <% nav_item[:items].each do |item| %>
386
+ <% if item[:type] == "divider" %>
387
+ <div class="mc-dropdown-divider"></div>
388
+
389
+ <% elsif item[:type] == "theme_selector" %>
390
+ <%# Render theme selector as nested submenu %>
391
+ <div class="mc-dropdown-submenu">
392
+ <a href="#" class="mc-dropdown-submenu-toggle">
393
+ <%= render "shared/icons/palette", css_class: "w-3 h-3" %>
394
+ Themes
395
+ <%= render "shared/icons/chevron_right", css_class: "w-3 h-3 ml-auto" %>
396
+ </a>
397
+ <div class="mc-dropdown-submenu-items">
398
+ <% (NeonSakura.config.available_themes || []).each do |theme| %>
399
+ <a href="#"
400
+ data-theme-item
401
+ data-theme-name="<%= theme[:name] %>"
402
+ data-theme-mode="<%= theme[:mode] %>"
403
+ aria-label="Switch to <%= theme[:label] %>">
404
+ <span data-theme-checkmark style="display: none;">
405
+ <%= render "shared/icons/check", css_class: "w-3 h-3" %>
406
+ </span>
407
+ <% if theme[:mode] == 'light' %>
408
+ <%= render "shared/icons/sun", css_class: "w-3 h-3" %>
409
+ <% else %>
410
+ <%= render "shared/icons/moon", css_class: "w-3 h-3" %>
411
+ <% end %>
412
+ <%= theme[:label] %>
413
+ </a>
414
+ <% end %>
415
+ </div>
416
+ </div>
417
+
418
+ <% else %>
419
+ <a href="<%= item[:path] %>"<%= " target=\"#{item[:target]}\" rel=\"noopener noreferrer\"" if item[:target] %>>
420
+ <% if item[:icon].present? %>
421
+ <%= render "shared/icons/#{item[:icon]}" %>
422
+ <% end %>
423
+ <%= item[:name] %>
424
+ </a>
425
+ <% end %>
426
+ <% end %>
427
+ </div>
428
+ </div>
429
+
430
+ <% when "button" %>
431
+ <%= button_to nav_item[:path], method: nav_item[:method] do %>
432
+ <%= render "shared/icons/#{nav_item[:icon]}" %>
433
+ <%= nav_item[:name] %>
434
+ <% end %>
435
+ <% end %>
436
+ <% end %>
437
+ </nav>
438
+ </div>
439
+ </div>
440
+ <% end %>
441
+
442
+ <%= render "layouts/mission_control/jobs/navigation" %>
443
+
444
+ <section class="section">
445
+ <div class="container">
446
+ <%= render "layouts/mission_control/jobs/application_selection" %>
447
+ <%= render "layouts/mission_control/jobs/flash" %>
448
+ <%= yield %>
449
+ </div>
450
+ </section>
451
+
452
+ </body>
453
+ </html>