agent-notes 2.0.4__py3-none-any.whl

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 (162) hide show
  1. agent_notes/VERSION +1 -0
  2. agent_notes/__init__.py +1 -0
  3. agent_notes/__main__.py +4 -0
  4. agent_notes/cli.py +348 -0
  5. agent_notes/commands/__init__.py +27 -0
  6. agent_notes/commands/_install_helpers.py +262 -0
  7. agent_notes/commands/build.py +170 -0
  8. agent_notes/commands/doctor.py +112 -0
  9. agent_notes/commands/info.py +95 -0
  10. agent_notes/commands/install.py +99 -0
  11. agent_notes/commands/list.py +169 -0
  12. agent_notes/commands/memory.py +430 -0
  13. agent_notes/commands/regenerate.py +152 -0
  14. agent_notes/commands/set_role.py +143 -0
  15. agent_notes/commands/uninstall.py +26 -0
  16. agent_notes/commands/update.py +169 -0
  17. agent_notes/commands/validate.py +199 -0
  18. agent_notes/commands/wizard.py +720 -0
  19. agent_notes/config.py +154 -0
  20. agent_notes/data/agents/agents.yaml +352 -0
  21. agent_notes/data/agents/analyst.md +45 -0
  22. agent_notes/data/agents/api-reviewer.md +47 -0
  23. agent_notes/data/agents/architect.md +46 -0
  24. agent_notes/data/agents/coder.md +28 -0
  25. agent_notes/data/agents/database-specialist.md +45 -0
  26. agent_notes/data/agents/debugger.md +47 -0
  27. agent_notes/data/agents/devil.md +47 -0
  28. agent_notes/data/agents/devops.md +38 -0
  29. agent_notes/data/agents/explorer.md +23 -0
  30. agent_notes/data/agents/integrations.md +44 -0
  31. agent_notes/data/agents/lead.md +216 -0
  32. agent_notes/data/agents/performance-profiler.md +44 -0
  33. agent_notes/data/agents/refactorer.md +48 -0
  34. agent_notes/data/agents/reviewer.md +44 -0
  35. agent_notes/data/agents/security-auditor.md +44 -0
  36. agent_notes/data/agents/system-auditor.md +38 -0
  37. agent_notes/data/agents/tech-writer.md +32 -0
  38. agent_notes/data/agents/test-runner.md +36 -0
  39. agent_notes/data/agents/test-writer.md +39 -0
  40. agent_notes/data/cli/claude.yaml +25 -0
  41. agent_notes/data/cli/copilot.yaml +18 -0
  42. agent_notes/data/cli/opencode.yaml +22 -0
  43. agent_notes/data/commands/brainstorm.md +8 -0
  44. agent_notes/data/commands/debug.md +9 -0
  45. agent_notes/data/commands/review.md +10 -0
  46. agent_notes/data/global-claude.md +290 -0
  47. agent_notes/data/global-copilot.md +27 -0
  48. agent_notes/data/global-opencode.md +40 -0
  49. agent_notes/data/hooks/session-context.md.tpl +19 -0
  50. agent_notes/data/models/claude-haiku-4-5.yaml +15 -0
  51. agent_notes/data/models/claude-opus-4-1.yaml +16 -0
  52. agent_notes/data/models/claude-opus-4-5.yaml +16 -0
  53. agent_notes/data/models/claude-opus-4-6.yaml +16 -0
  54. agent_notes/data/models/claude-opus-4-7.yaml +15 -0
  55. agent_notes/data/models/claude-sonnet-4-5.yaml +16 -0
  56. agent_notes/data/models/claude-sonnet-4-6.yaml +15 -0
  57. agent_notes/data/models/claude-sonnet-4.yaml +16 -0
  58. agent_notes/data/pricing.yaml +33 -0
  59. agent_notes/data/roles/orchestrator.yaml +5 -0
  60. agent_notes/data/roles/reasoner.yaml +5 -0
  61. agent_notes/data/roles/scout.yaml +5 -0
  62. agent_notes/data/roles/worker.yaml +5 -0
  63. agent_notes/data/rules/code-quality.md +9 -0
  64. agent_notes/data/rules/safety.md +10 -0
  65. agent_notes/data/scripts/cost-report +211 -0
  66. agent_notes/data/skills/brainstorming/SKILL.md +57 -0
  67. agent_notes/data/skills/code-review/SKILL.md +64 -0
  68. agent_notes/data/skills/debugging-protocol/SKILL.md +51 -0
  69. agent_notes/data/skills/docker-compose/SKILL.md +318 -0
  70. agent_notes/data/skills/docker-compose-advanced/SKILL.md +575 -0
  71. agent_notes/data/skills/docker-dockerfile/SKILL.md +385 -0
  72. agent_notes/data/skills/docker-dockerfile-languages/SKILL.md +293 -0
  73. agent_notes/data/skills/git/SKILL.md +87 -0
  74. agent_notes/data/skills/rails-active-storage/SKILL.md +321 -0
  75. agent_notes/data/skills/rails-broadcasting/SKILL.md +374 -0
  76. agent_notes/data/skills/rails-concerns/SKILL.md +806 -0
  77. agent_notes/data/skills/rails-controllers/SKILL.md +510 -0
  78. agent_notes/data/skills/rails-controllers-advanced/SKILL.md +441 -0
  79. agent_notes/data/skills/rails-helpers/SKILL.md +677 -0
  80. agent_notes/data/skills/rails-initializers/SKILL.md +79 -0
  81. agent_notes/data/skills/rails-javascript/SKILL.md +567 -0
  82. agent_notes/data/skills/rails-jobs/SKILL.md +700 -0
  83. agent_notes/data/skills/rails-kamal/SKILL.md +483 -0
  84. agent_notes/data/skills/rails-lib/SKILL.md +101 -0
  85. agent_notes/data/skills/rails-mailers/SKILL.md +321 -0
  86. agent_notes/data/skills/rails-migrations/SKILL.md +268 -0
  87. agent_notes/data/skills/rails-models/SKILL.md +459 -0
  88. agent_notes/data/skills/rails-models-advanced/SKILL.md +398 -0
  89. agent_notes/data/skills/rails-routes/SKILL.md +804 -0
  90. agent_notes/data/skills/rails-style/SKILL.md +538 -0
  91. agent_notes/data/skills/rails-testing-controllers/SKILL.md +343 -0
  92. agent_notes/data/skills/rails-testing-models/SKILL.md +296 -0
  93. agent_notes/data/skills/rails-testing-system/SKILL.md +375 -0
  94. agent_notes/data/skills/rails-validations/SKILL.md +108 -0
  95. agent_notes/data/skills/rails-view-components/SKILL.md +511 -0
  96. agent_notes/data/skills/rails-view-components-advanced/SKILL.md +376 -0
  97. agent_notes/data/skills/rails-views/SKILL.md +413 -0
  98. agent_notes/data/skills/rails-views-advanced/SKILL.md +450 -0
  99. agent_notes/data/skills/refactoring-protocol/SKILL.md +64 -0
  100. agent_notes/data/skills/tdd/SKILL.md +57 -0
  101. agent_notes/data/templates/__init__.py +1 -0
  102. agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
  103. agent_notes/data/templates/frontmatter/__init__.py +1 -0
  104. agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
  105. agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
  106. agent_notes/data/templates/frontmatter/__pycache__/cursor.cpython-314.pyc +0 -0
  107. agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
  108. agent_notes/data/templates/frontmatter/claude.py +44 -0
  109. agent_notes/data/templates/frontmatter/opencode.py +104 -0
  110. agent_notes/doctor_checks.py +189 -0
  111. agent_notes/domain/__init__.py +17 -0
  112. agent_notes/domain/agent.py +34 -0
  113. agent_notes/domain/cli_backend.py +40 -0
  114. agent_notes/domain/diagnostics.py +29 -0
  115. agent_notes/domain/diff.py +44 -0
  116. agent_notes/domain/model.py +27 -0
  117. agent_notes/domain/role.py +13 -0
  118. agent_notes/domain/rule.py +13 -0
  119. agent_notes/domain/skill.py +15 -0
  120. agent_notes/domain/state.py +46 -0
  121. agent_notes/install_state.py +11 -0
  122. agent_notes/registries/__init__.py +16 -0
  123. agent_notes/registries/_base.py +46 -0
  124. agent_notes/registries/agent_registry.py +107 -0
  125. agent_notes/registries/cli_registry.py +89 -0
  126. agent_notes/registries/model_registry.py +85 -0
  127. agent_notes/registries/role_registry.py +64 -0
  128. agent_notes/registries/rule_registry.py +80 -0
  129. agent_notes/registries/skill_registry.py +141 -0
  130. agent_notes/services/__init__.py +8 -0
  131. agent_notes/services/diagnostics/__init__.py +47 -0
  132. agent_notes/services/diagnostics/_checks.py +272 -0
  133. agent_notes/services/diagnostics/_display.py +346 -0
  134. agent_notes/services/diagnostics/_fix.py +169 -0
  135. agent_notes/services/diff.py +349 -0
  136. agent_notes/services/fs.py +195 -0
  137. agent_notes/services/install_state_builder.py +210 -0
  138. agent_notes/services/installer.py +293 -0
  139. agent_notes/services/memory_backend.py +155 -0
  140. agent_notes/services/rendering.py +329 -0
  141. agent_notes/services/session_context.py +23 -0
  142. agent_notes/services/settings_writer.py +79 -0
  143. agent_notes/services/state_store.py +249 -0
  144. agent_notes/services/ui.py +419 -0
  145. agent_notes/services/user_config.py +62 -0
  146. agent_notes/services/validation.py +67 -0
  147. agent_notes/state.py +21 -0
  148. agent_notes-2.0.4.dist-info/METADATA +14 -0
  149. agent_notes-2.0.4.dist-info/RECORD +162 -0
  150. agent_notes-2.0.4.dist-info/WHEEL +5 -0
  151. agent_notes-2.0.4.dist-info/entry_points.txt +2 -0
  152. agent_notes-2.0.4.dist-info/licenses/LICENSE +21 -0
  153. agent_notes-2.0.4.dist-info/top_level.txt +2 -0
  154. tests/conftest.py +20 -0
  155. tests/functional/__init__.py +0 -0
  156. tests/functional/test_build_commands.py +88 -0
  157. tests/functional/test_registries.py +128 -0
  158. tests/integration/__init__.py +0 -0
  159. tests/integration/test_build_output.py +129 -0
  160. tests/plugins/__init__.py +0 -0
  161. tests/plugins/test_agents.py +93 -0
  162. tests/plugins/test_skills.py +77 -0
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: rails-initializers
3
+ description: "Rails configuration initializers: extensions, CORS, filtering, and custom config"
4
+ group: rails
5
+ ---
6
+
7
+ # Initializers Guide
8
+
9
+ Guide for Rails configuration and boot-time setup.
10
+
11
+ ---
12
+
13
+ ## File Structure
14
+
15
+ ```
16
+ config/initializers/
17
+ ├── extensions.rb # Load lib/ extensions
18
+ ├── assets.rb # Asset pipeline config
19
+ ├── filter_parameter_logging.rb
20
+ ├── inflections.rb
21
+ └── cors.rb
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Common Initializers
27
+
28
+ ### Load Extensions
29
+
30
+ ```ruby
31
+ # config/initializers/extensions.rb
32
+ Dir["#{Rails.root}/lib/rails_ext/*"].each { |path|
33
+ require "rails_ext/#{File.basename(path)}"
34
+ }
35
+ ```
36
+
37
+ ### Filter Parameters
38
+
39
+ ```ruby
40
+ # config/initializers/filter_parameter_logging.rb
41
+ Rails.application.config.filter_parameters += [
42
+ :passw, :secret, :token, :_key, :crypt, :salt, :certificate
43
+ ]
44
+ ```
45
+
46
+ ### CORS
47
+
48
+ ```ruby
49
+ # config/initializers/cors.rb
50
+ Rails.application.config.middleware.insert_before 0, Rack::Cors do
51
+ allow do
52
+ origins "example.com"
53
+ resource "*", headers: :any, methods: [:get, :post, :put, :delete, :options]
54
+ end
55
+ end
56
+ ```
57
+
58
+ ### Custom Configuration
59
+
60
+ ```ruby
61
+ # config/initializers/app_config.rb
62
+ Rails.application.config.x.api_key = ENV["API_KEY"]
63
+ Rails.application.config.x.feature_flags = {
64
+ new_dashboard: true,
65
+ experimental_feature: false
66
+ }
67
+
68
+ # Usage
69
+ Rails.configuration.x.api_key
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Summary
75
+
76
+ - **Boot Time**: Code runs when app starts
77
+ - **Configuration**: Set up libraries, features
78
+ - **Load Order**: Matters for dependencies
79
+ - **Environment Specific**: Use ENV for secrets
@@ -0,0 +1,567 @@
1
+ ---
2
+ name: rails-javascript
3
+ description: "Rails frontend: Stimulus controllers, Turbo patterns, Importmap, and view transitions"
4
+ group: rails
5
+ ---
6
+
7
+ # JavaScript & Stimulus
8
+
9
+ Frontend patterns with Stimulus, Turbo, and Importmap (Rails 8 defaults).
10
+
11
+ ---
12
+
13
+ ## Philosophy
14
+
15
+ 1. **HTML over the wire** - Server renders HTML, not JSON
16
+ 2. **Progressive enhancement** - Works without JavaScript
17
+ 3. **Sprinkles of interactivity** - Stimulus for behavior
18
+ 4. **One controller per behavior** - Focused, reusable
19
+ 5. **Turbo for speed** - Fast navigation without SPAs
20
+
21
+ ---
22
+
23
+ ## File Structure
24
+
25
+ ```
26
+ app/javascript/
27
+ ├── application.js
28
+ └── controllers/
29
+ ├── index.js
30
+ ├── dialog_controller.js
31
+ ├── auto_submit_controller.js
32
+ ├── navigable_list_controller.js
33
+ └── drag_drop_controller.js
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Stimulus Controller Template
39
+
40
+ ```javascript
41
+ // app/javascript/controllers/dialog_controller.js
42
+ import { Controller } from "@hotwired/stimulus"
43
+
44
+ export default class extends Controller {
45
+ static targets = [ "dialog", "content" ]
46
+ static values = { open: Boolean, dismissable: { type: Boolean, default: true } }
47
+ static classes = [ "open", "closing" ]
48
+
49
+ connect() {
50
+ this.boundHandleEscape = this.handleEscape.bind(this)
51
+ document.addEventListener("keydown", this.boundHandleEscape)
52
+ }
53
+
54
+ disconnect() {
55
+ document.removeEventListener("keydown", this.boundHandleEscape)
56
+ }
57
+
58
+ open() {
59
+ this.openValue = true
60
+ this.dialogTarget.showModal()
61
+ this.dialogTarget.classList.add(this.openClass)
62
+ }
63
+
64
+ close() {
65
+ if (!this.dismissableValue) return
66
+
67
+ this.openValue = false
68
+ this.dialogTarget.classList.add(this.closingClass)
69
+
70
+ setTimeout(() => {
71
+ this.dialogTarget.close()
72
+ this.dialogTarget.classList.remove(this.openClass, this.closingClass)
73
+ }, 200)
74
+ }
75
+
76
+ handleEscape(event) {
77
+ if (event.key === "Escape" && this.openValue) {
78
+ this.close()
79
+ }
80
+ }
81
+
82
+ // Private methods with #
83
+ #cleanup() {
84
+ // Implementation
85
+ }
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Stimulus Patterns
92
+
93
+ ### Auto-Submit Form
94
+
95
+ ```javascript
96
+ import { Controller } from "@hotwired/stimulus"
97
+
98
+ export default class extends Controller {
99
+ static targets = [ "form" ]
100
+ static values = { delay: { type: Number, default: 300 } }
101
+
102
+ submit() {
103
+ clearTimeout(this.timeout)
104
+
105
+ this.timeout = setTimeout(() => {
106
+ this.formTarget.requestSubmit()
107
+ }, this.delayValue)
108
+ }
109
+ }
110
+ ```
111
+
112
+ ```erb
113
+ <%= form_with model: @filter, data: { controller: "auto-submit", action: "input->auto-submit#submit" } do |f| %>
114
+ <%= f.text_field :query, data: { auto_submit_target: "form" } %>
115
+ <% end %>
116
+ ```
117
+
118
+ ### Keyboard Navigation
119
+
120
+ ```javascript
121
+ import { Controller } from "@hotwired/stimulus"
122
+
123
+ export default class extends Controller {
124
+ static targets = [ "item" ]
125
+
126
+ connect() {
127
+ this.index = 0
128
+ this.highlight(0)
129
+ }
130
+
131
+ navigate(event) {
132
+ switch(event.key) {
133
+ case "ArrowDown":
134
+ event.preventDefault()
135
+ this.next()
136
+ break
137
+ case "ArrowUp":
138
+ event.preventDefault()
139
+ this.previous()
140
+ break
141
+ case "Enter":
142
+ event.preventDefault()
143
+ this.select()
144
+ break
145
+ }
146
+ }
147
+
148
+ next() {
149
+ this.index = Math.min(this.index + 1, this.itemTargets.length - 1)
150
+ this.highlight(this.index)
151
+ }
152
+
153
+ previous() {
154
+ this.index = Math.max(this.index - 1, 0)
155
+ this.highlight(this.index)
156
+ }
157
+
158
+ select() {
159
+ this.itemTargets[this.index]?.click()
160
+ }
161
+
162
+ highlight(index) {
163
+ this.itemTargets.forEach((item, i) => {
164
+ item.classList.toggle("highlighted", i === index)
165
+ })
166
+ this.itemTargets[index]?.scrollIntoView({ block: "nearest" })
167
+ }
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Turbo Patterns
174
+
175
+ ### Turbo Frames
176
+
177
+ ```erb
178
+ <%# Lazy loading %>
179
+ <turbo-frame id="stats" src="<%= stats_path %>" loading="lazy">
180
+ <p>Loading stats...</p>
181
+ </turbo-frame>
182
+
183
+ <%# Navigation within frame %>
184
+ <turbo-frame id="modal">
185
+ <%= link_to "Edit", edit_card_path(@card) %>
186
+ </turbo-frame>
187
+
188
+ <%# Break out of frame %>
189
+ <%= link_to "View All", cards_path, data: { turbo_frame: "_top" } %>
190
+ ```
191
+
192
+ ### Turbo Streams
193
+
194
+ ```ruby
195
+ # Controller
196
+ def create
197
+ @card = Card.create!(card_params)
198
+
199
+ respond_to do |format|
200
+ format.turbo_stream
201
+ end
202
+ end
203
+ ```
204
+
205
+ ```erb
206
+ <%# create.turbo_stream.erb %>
207
+ <%= turbo_stream.prepend "cards", @card %>
208
+ <%= turbo_stream.update "flash", partial: "shared/flash" %>
209
+ ```
210
+
211
+ ### Turbo Drive
212
+
213
+ ```erb
214
+ <%# Disable Turbo for link %>
215
+ <%= link_to "External", external_path, data: { turbo: false } %>
216
+
217
+ <%# Confirm before navigation %>
218
+ <%= link_to "Delete", @card, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Testing JavaScript
224
+
225
+ ### System Tests with JavaScript
226
+
227
+ ```ruby
228
+ class DialogTest < ApplicationSystemTestCase
229
+ test "opening and closing dialog", js: true do
230
+ visit cards_path
231
+
232
+ click_on "New Card"
233
+ assert_selector "dialog[open]"
234
+
235
+ click_on "Cancel"
236
+ assert_no_selector "dialog[open]"
237
+ end
238
+ end
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Importmap (Rails 8 Default)
244
+
245
+ Rails 8 defaults to importmap for JavaScript management - no build step required.
246
+
247
+ ### Configuration
248
+
249
+ ```ruby
250
+ # config/importmap.rb
251
+ pin "application"
252
+ pin "@hotwired/turbo-rails", to: "turbo.min.js"
253
+ pin "@hotwired/stimulus", to: "stimulus.min.js"
254
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
255
+
256
+ # Pin your controllers
257
+ pin_all_from "app/javascript/controllers", under: "controllers"
258
+
259
+ # Add third-party libraries
260
+ pin "local-time" # from jspm.io
261
+ pin "sortablejs" # from unpkg.com
262
+ ```
263
+
264
+ ### Adding JavaScript Libraries
265
+
266
+ ```bash
267
+ # Import from CDN
268
+ bin/importmap pin local-time
269
+
270
+ # Pin specific version
271
+ bin/importmap pin sortablejs@1.15.0
272
+
273
+ # Check current pins
274
+ bin/importmap json
275
+ ```
276
+
277
+ ### Application Setup
278
+
279
+ ```javascript
280
+ // app/javascript/application.js
281
+ import "@hotwired/turbo-rails"
282
+ import "./controllers"
283
+
284
+ // Optional: Import libraries
285
+ import LocalTime from "local-time"
286
+ LocalTime.start()
287
+ ```
288
+
289
+ **When to use alternatives:**
290
+ - **esbuild/webpack**: Need npm packages not available via importmap
291
+ - **propshaft**: Simpler asset pipeline (no import maps)
292
+ - **vite**: Modern bundler with HMR
293
+
294
+ ---
295
+
296
+ ## Turbo 8 Features
297
+
298
+ ### Page Refresh (Morphing)
299
+
300
+ Turbo 8 can morph entire pages while preserving scroll position and form state.
301
+
302
+ ```erb
303
+ <%# Enable for entire app %>
304
+ <%# app/views/layouts/application.html.erb %>
305
+ <meta name="turbo-refresh-method" content="morph">
306
+ <meta name="turbo-refresh-scroll" content="preserve">
307
+ ```
308
+
309
+ ```ruby
310
+ # Enable for specific actions
311
+ class CardsController < ApplicationController
312
+ def index
313
+ # Will use morphing for page updates
314
+ @cards = Card.all
315
+ end
316
+ end
317
+
318
+ # Force refresh after form submit
319
+ def create
320
+ @card = Card.create!(card_params)
321
+ redirect_to cards_path, status: :see_other # Triggers refresh
322
+ end
323
+ ```
324
+
325
+ ### Turbo Permanent Elements
326
+
327
+ Preserve elements across page navigations:
328
+
329
+ ```erb
330
+ <div id="player" data-turbo-permanent>
331
+ <%# Audio player persists across navigation %>
332
+ <audio controls src="<%= @podcast.audio_url %>"></audio>
333
+ </div>
334
+
335
+ <div id="flash" data-turbo-permanent>
336
+ <%# Flash messages persist during loading %>
337
+ <%= render "shared/flash" %>
338
+ </div>
339
+ ```
340
+
341
+ ### Prefetching
342
+
343
+ Speed up navigation by prefetching links:
344
+
345
+ ```erb
346
+ <%# Prefetch on hover %>
347
+ <%= link_to "View Card", @card, data: { turbo_prefetch: true } %>
348
+
349
+ <%# Instant navigation - prefetch on mousedown %>
350
+ <%= link_to "Dashboard", dashboard_path, data: { turbo_preload: "instant" } %>
351
+ ```
352
+
353
+ ---
354
+
355
+ ## View Transitions (CSS)
356
+
357
+ Smooth page transitions using CSS View Transitions API with Turbo.
358
+
359
+ ### Enable View Transitions
360
+
361
+ ```erb
362
+ <%# app/views/layouts/application.html.erb %>
363
+ <meta name="view-transition" content="same-origin">
364
+ ```
365
+
366
+ ### CSS Animations
367
+
368
+ ```css
369
+ /* app/assets/stylesheets/application.css */
370
+
371
+ /* Fade transition */
372
+ ::view-transition-old(root),
373
+ ::view-transition-new(root) {
374
+ animation-duration: 0.3s;
375
+ }
376
+
377
+ /* Slide transition */
378
+ ::view-transition-old(root) {
379
+ animation-name: slide-out;
380
+ }
381
+
382
+ ::view-transition-new(root) {
383
+ animation-name: slide-in;
384
+ }
385
+
386
+ @keyframes slide-out {
387
+ from { transform: translateX(0); }
388
+ to { transform: translateX(-100%); }
389
+ }
390
+
391
+ @keyframes slide-in {
392
+ from { transform: translateX(100%); }
393
+ to { transform: translateX(0); }
394
+ }
395
+
396
+ /* Named transitions for specific elements */
397
+ .card-detail {
398
+ view-transition-name: card-detail;
399
+ }
400
+ ```
401
+
402
+ **Browser Support:** Chrome/Edge 111+, Safari 18+. Gracefully degrades in older browsers.
403
+
404
+ ---
405
+
406
+ ## Production-Ready Stimulus Controllers
407
+
408
+ ### Clipboard Controller
409
+
410
+ ```javascript
411
+ // controllers/clipboard_controller.js
412
+ import { Controller } from "@hotwired/stimulus"
413
+
414
+ export default class extends Controller {
415
+ static targets = [ "source", "button" ]
416
+ static values = {
417
+ successMessage: { type: String, default: "Copied!" },
418
+ successDuration: { type: Number, default: 2000 }
419
+ }
420
+
421
+ copy(event) {
422
+ event.preventDefault()
423
+
424
+ navigator.clipboard.writeText(this.sourceTarget.value || this.sourceTarget.textContent)
425
+ .then(() => this.showSuccess())
426
+ .catch(() => this.showError())
427
+ }
428
+
429
+ showSuccess() {
430
+ const originalText = this.buttonTarget.textContent
431
+ this.buttonTarget.textContent = this.successMessageValue
432
+
433
+ setTimeout(() => {
434
+ this.buttonTarget.textContent = originalText
435
+ }, this.successDurationValue)
436
+ }
437
+
438
+ showError() {
439
+ this.buttonTarget.textContent = "Failed to copy"
440
+ }
441
+ }
442
+ ```
443
+
444
+ ```erb
445
+ <div data-controller="clipboard">
446
+ <input data-clipboard-target="source" value="<%= @share_url %>" readonly>
447
+ <button data-action="click->clipboard#copy" data-clipboard-target="button">
448
+ Copy URL
449
+ </button>
450
+ </div>
451
+ ```
452
+
453
+ ### Dropdown Controller
454
+
455
+ ```javascript
456
+ // controllers/dropdown_controller.js
457
+ import { Controller } from "@hotwired/stimulus"
458
+
459
+ export default class extends Controller {
460
+ static targets = [ "menu" ]
461
+ static classes = [ "open" ]
462
+
463
+ connect() {
464
+ this.boundHandleClickOutside = this.handleClickOutside.bind(this)
465
+ }
466
+
467
+ toggle(event) {
468
+ event.stopPropagation()
469
+
470
+ if (this.menuTarget.classList.contains(this.openClass)) {
471
+ this.close()
472
+ } else {
473
+ this.open()
474
+ }
475
+ }
476
+
477
+ open() {
478
+ this.menuTarget.classList.add(this.openClass)
479
+ document.addEventListener("click", this.boundHandleClickOutside)
480
+ }
481
+
482
+ close() {
483
+ this.menuTarget.classList.remove(this.openClass)
484
+ document.removeEventListener("click", this.boundHandleClickOutside)
485
+ }
486
+
487
+ handleClickOutside(event) {
488
+ if (!this.element.contains(event.target)) {
489
+ this.close()
490
+ }
491
+ }
492
+
493
+ disconnect() {
494
+ document.removeEventListener("click", this.boundHandleClickOutside)
495
+ }
496
+ }
497
+ ```
498
+
499
+ ```erb
500
+ <div data-controller="dropdown" class="dropdown">
501
+ <button data-action="click->dropdown#toggle">
502
+ Options ▾
503
+ </button>
504
+
505
+ <div data-dropdown-target="menu" data-dropdown-open-class="open" class="dropdown-menu">
506
+ <%= link_to "Edit", edit_card_path(@card) %>
507
+ <%= link_to "Delete", @card, method: :delete %>
508
+ </div>
509
+ </div>
510
+ ```
511
+
512
+ ### Toggle Controller
513
+
514
+ ```javascript
515
+ // controllers/toggle_controller.js
516
+ import { Controller } from "@hotwired/stimulus"
517
+
518
+ export default class extends Controller {
519
+ static targets = [ "toggleable" ]
520
+ static classes = [ "hidden" ]
521
+
522
+ toggle() {
523
+ this.toggleableTargets.forEach(target => {
524
+ target.classList.toggle(this.hiddenClass)
525
+ })
526
+ }
527
+
528
+ show() {
529
+ this.toggleableTargets.forEach(target => {
530
+ target.classList.remove(this.hiddenClass)
531
+ })
532
+ }
533
+
534
+ hide() {
535
+ this.toggleableTargets.forEach(target => {
536
+ target.classList.add(this.hiddenClass)
537
+ })
538
+ }
539
+ }
540
+ ```
541
+
542
+ ```erb
543
+ <div data-controller="toggle">
544
+ <button data-action="click->toggle#toggle">
545
+ Show/Hide Details
546
+ </button>
547
+
548
+ <div data-toggle-target="toggleable" data-toggle-hidden-class="hidden" class="hidden">
549
+ <p>Additional details...</p>
550
+ </div>
551
+ </div>
552
+ ```
553
+
554
+ ---
555
+
556
+ ## Summary
557
+
558
+ - **Importmap**: Rails 8 default, no build step required
559
+ - **Stimulus**: One controller per behavior, focused and reusable
560
+ - **Targets**: Reference DOM elements
561
+ - **Values**: Pass data from HTML to JavaScript
562
+ - **Actions**: Respond to events
563
+ - **Turbo 8**: Page refresh with morphing, prefetching
564
+ - **View Transitions**: Smooth CSS animations between pages
565
+ - **Turbo Frames**: Lazy loading, scoped navigation
566
+ - **Turbo Streams**: Real-time updates
567
+ - **Progressive Enhancement**: Works without JavaScript