brainzlab 0.1.1 → 0.1.2

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/lib/brainzlab/beacon/client.rb +209 -0
  4. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  5. data/lib/brainzlab/beacon.rb +215 -0
  6. data/lib/brainzlab/configuration.rb +341 -3
  7. data/lib/brainzlab/cortex/cache.rb +59 -0
  8. data/lib/brainzlab/cortex/client.rb +141 -0
  9. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  10. data/lib/brainzlab/cortex.rb +227 -0
  11. data/lib/brainzlab/dendrite/client.rb +232 -0
  12. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  13. data/lib/brainzlab/dendrite.rb +195 -0
  14. data/lib/brainzlab/devtools/assets/devtools.css +1106 -0
  15. data/lib/brainzlab/devtools/assets/devtools.js +322 -0
  16. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  17. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +500 -0
  18. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  19. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  20. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  21. data/lib/brainzlab/devtools/middleware/database_handler.rb +180 -0
  22. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  23. data/lib/brainzlab/devtools/middleware/error_page.rb +376 -0
  24. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +155 -0
  25. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +94 -0
  26. data/lib/brainzlab/devtools.rb +75 -0
  27. data/lib/brainzlab/flux/buffer.rb +96 -0
  28. data/lib/brainzlab/flux/client.rb +70 -0
  29. data/lib/brainzlab/flux/provisioner.rb +57 -0
  30. data/lib/brainzlab/flux.rb +174 -0
  31. data/lib/brainzlab/instrumentation/active_record.rb +18 -1
  32. data/lib/brainzlab/instrumentation/aws.rb +179 -0
  33. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  34. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  35. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  36. data/lib/brainzlab/instrumentation/resque.rb +115 -0
  37. data/lib/brainzlab/instrumentation/solid_queue.rb +198 -0
  38. data/lib/brainzlab/instrumentation/stripe.rb +164 -0
  39. data/lib/brainzlab/instrumentation/typhoeus.rb +104 -0
  40. data/lib/brainzlab/instrumentation.rb +72 -0
  41. data/lib/brainzlab/nerve/client.rb +217 -0
  42. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  43. data/lib/brainzlab/nerve.rb +219 -0
  44. data/lib/brainzlab/pulse/instrumentation.rb +35 -2
  45. data/lib/brainzlab/pulse/propagation.rb +1 -1
  46. data/lib/brainzlab/pulse/tracer.rb +1 -1
  47. data/lib/brainzlab/pulse.rb +1 -1
  48. data/lib/brainzlab/rails/log_subscriber.rb +1 -2
  49. data/lib/brainzlab/rails/railtie.rb +36 -3
  50. data/lib/brainzlab/recall/provisioner.rb +17 -0
  51. data/lib/brainzlab/recall.rb +6 -1
  52. data/lib/brainzlab/reflex.rb +2 -2
  53. data/lib/brainzlab/sentinel/client.rb +218 -0
  54. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  55. data/lib/brainzlab/sentinel.rb +165 -0
  56. data/lib/brainzlab/signal/client.rb +62 -0
  57. data/lib/brainzlab/signal/provisioner.rb +55 -0
  58. data/lib/brainzlab/signal.rb +136 -0
  59. data/lib/brainzlab/synapse/client.rb +290 -0
  60. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  61. data/lib/brainzlab/synapse.rb +270 -0
  62. data/lib/brainzlab/utilities/circuit_breaker.rb +265 -0
  63. data/lib/brainzlab/utilities/health_check.rb +296 -0
  64. data/lib/brainzlab/utilities/log_formatter.rb +256 -0
  65. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  66. data/lib/brainzlab/utilities.rb +17 -0
  67. data/lib/brainzlab/vault/cache.rb +80 -0
  68. data/lib/brainzlab/vault/client.rb +198 -0
  69. data/lib/brainzlab/vault/provisioner.rb +49 -0
  70. data/lib/brainzlab/vault.rb +268 -0
  71. data/lib/brainzlab/version.rb +1 -1
  72. data/lib/brainzlab/vision/client.rb +128 -0
  73. data/lib/brainzlab/vision/provisioner.rb +136 -0
  74. data/lib/brainzlab/vision.rb +157 -0
  75. data/lib/brainzlab.rb +101 -0
  76. metadata +60 -1
@@ -0,0 +1,500 @@
1
+ <div class="brainz-debug-panel<%= ' collapsed' unless @expand_by_default %>" data-controller="devtools">
2
+ <%# Calculate issues count and performance score %>
3
+ <%-
4
+ issues_count = 0
5
+ n_plus_ones = @database[:n_plus_ones] || []
6
+ slow_queries = (@database[:queries] || []).select { |q| (q[:duration] || 0) > 100 }
7
+ slow_views = (@views[:templates] || []).select { |v| (v[:duration] || 0) > 50 }
8
+ query_count = @database[:total_count] || 0
9
+ memory_delta = @memory[:delta_mb] || 0
10
+
11
+ issues_count += n_plus_ones.length
12
+ issues_count += slow_queries.length
13
+ issues_count += slow_views.length
14
+ issues_count += 1 if query_count > 20
15
+ issues_count += 1 if memory_delta > 50
16
+
17
+ # Calculate performance score (0-100)
18
+ score = 100
19
+ duration = @timing[:duration_ms] || 0
20
+
21
+ # Response time penalties
22
+ score -= 10 if duration > 500
23
+ score -= 10 if duration > 1000
24
+ score -= 10 if duration > 2000
25
+
26
+ # N+1 queries (major issue)
27
+ score -= n_plus_ones.length * 15
28
+
29
+ # Slow queries
30
+ score -= slow_queries.length * 5
31
+
32
+ # Query count penalties
33
+ score -= 10 if query_count > 20
34
+ score -= 10 if query_count > 50
35
+
36
+ # Memory penalties
37
+ score -= 10 if memory_delta > 50
38
+ score -= 10 if memory_delta > 100
39
+
40
+ # Slow views
41
+ score -= slow_views.length * 5
42
+
43
+ # Clamp to 0-100
44
+ score = [[score, 0].max, 100].min
45
+
46
+ # Score grade
47
+ score_grade = case score
48
+ when 90..100 then 'excellent'
49
+ when 70..89 then 'good'
50
+ when 50..69 then 'warning'
51
+ else 'poor'
52
+ end
53
+ -%>
54
+
55
+ <!-- Toolbar (always visible) -->
56
+ <div class="brainz-debug-toolbar" data-action="click->devtools#togglePanel">
57
+ <img src="<%= asset_url('logo.svg') %>" alt="BrainzLab" class="brainz-debug-logo">
58
+
59
+ <!-- Performance Score -->
60
+ <div class="brainz-score <%= score_grade %>">
61
+ <svg class="brainz-score-ring" viewBox="0 0 36 36">
62
+ <circle class="brainz-score-bg" cx="18" cy="18" r="16" fill="none" stroke-width="3"/>
63
+ <circle class="brainz-score-progress" cx="18" cy="18" r="16" fill="none" stroke-width="3"
64
+ stroke-dasharray="<%= score %>, 100"
65
+ transform="rotate(-90 18 18)"/>
66
+ </svg>
67
+ <span class="brainz-score-value"><%= score %></span>
68
+ </div>
69
+
70
+ <span class="brainz-debug-stat <%= duration_class(@timing[:duration_ms]) %>">
71
+ <strong><%= format_duration(@timing[:duration_ms]) %></strong>
72
+ </span>
73
+
74
+ <span class="brainz-debug-stat<%= ' warning' if query_count > 20 %>">
75
+ <strong><%= query_count %></strong> queries
76
+ (<%= format_duration(@database[:total_duration_ms]) %>)
77
+ </span>
78
+
79
+ <%- if n_plus_ones.any? -%>
80
+ <span class="brainz-debug-stat error">
81
+ <strong><%= n_plus_ones.length %></strong> N+1
82
+ </span>
83
+ <%- end -%>
84
+
85
+ <span class="brainz-debug-stat<%= memory_class(@memory[:delta_mb]) %>">
86
+ <strong><%= sprintf('%.1f', memory_delta) %>MB</strong>
87
+ </span>
88
+
89
+ <span class="brainz-debug-stat <%= status_class(@response[:status]) %>">
90
+ <strong><%= @response[:status] || 200 %></strong>
91
+ </span>
92
+
93
+ <span class="brainz-keyboard-hint">Ctrl+Shift+B</span>
94
+
95
+ <span class="brainz-debug-expand">
96
+ <svg width="12" height="12" viewBox="0 0 12 12">
97
+ <path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
98
+ </svg>
99
+ </span>
100
+ </div>
101
+
102
+ <!-- Tabs -->
103
+ <div class="brainz-debug-tabs">
104
+ <button class="brainz-debug-tab active" data-tab="request" data-devtools-target="tab" data-action="click->devtools#switchTab">Request</button>
105
+ <button class="brainz-debug-tab" data-tab="queries" data-devtools-target="tab" data-action="click->devtools#switchTab">
106
+ Queries <span class="badge"><%= query_count %></span>
107
+ </button>
108
+ <button class="brainz-debug-tab" data-tab="views" data-devtools-target="tab" data-action="click->devtools#switchTab">
109
+ Views <span class="badge"><%= (@views[:templates] || []).length %></span>
110
+ </button>
111
+ <button class="brainz-debug-tab<%= ' has-issues' if issues_count > 0 %>" data-tab="issues" data-devtools-target="tab" data-action="click->devtools#switchTab">
112
+ Issues <span class="badge<%= ' error' if issues_count > 0 %>"><%= issues_count %></span>
113
+ </button>
114
+ <button class="brainz-debug-tab" data-tab="timeline" data-devtools-target="tab" data-action="click->devtools#switchTab">Timeline</button>
115
+ </div>
116
+
117
+ <!-- Content Panes -->
118
+ <div class="brainz-debug-content">
119
+ <!-- Request Pane -->
120
+ <div class="brainz-debug-pane active" data-pane="request" data-devtools-target="pane">
121
+ <table class="brainz-info-table">
122
+ <tr>
123
+ <th>Method</th>
124
+ <td><%= h(@request[:method]) %></td>
125
+ </tr>
126
+ <tr>
127
+ <th>Path</th>
128
+ <td><%= h(@request[:path]) %></td>
129
+ </tr>
130
+ <%- if @controller[:name] -%>
131
+ <tr>
132
+ <th>Controller</th>
133
+ <td><%= h(@controller[:name]) %>#<%= h(@controller[:action]) %></td>
134
+ </tr>
135
+ <%- end -%>
136
+ <tr>
137
+ <th>Status</th>
138
+ <td><span class="<%= status_class(@response[:status]) %>"><%= @response[:status] %></span></td>
139
+ </tr>
140
+ <tr>
141
+ <th>Request ID</th>
142
+ <td><code><%= h(@request[:request_id]) %></code></td>
143
+ </tr>
144
+ <tr>
145
+ <th>Duration</th>
146
+ <td><%= format_duration(@timing[:duration_ms]) %></td>
147
+ </tr>
148
+ </table>
149
+
150
+ <%- if @request[:params] && !@request[:params].empty? -%>
151
+ <h4 style="margin: 1rem 0 0.5rem; font-size: 0.8125rem; color: #718096;">Parameters</h4>
152
+ <pre class="brainz-code-block"><%= json_pretty(@request[:params]) %></pre>
153
+ <%- end -%>
154
+
155
+ <%- if @user && !@user.empty? -%>
156
+ <h4 style="margin: 1rem 0 0.5rem; font-size: 0.8125rem; color: #718096;">User</h4>
157
+ <pre class="brainz-code-block"><%= json_pretty(@user) %></pre>
158
+ <%- end -%>
159
+ </div>
160
+
161
+ <!-- Queries Pane -->
162
+ <div class="brainz-debug-pane" data-pane="queries" data-devtools-target="pane">
163
+ <%- queries = @database[:queries] || [] -%>
164
+ <%- if queries.any? -%>
165
+ <table class="brainz-query-table">
166
+ <thead>
167
+ <tr>
168
+ <th width="80">Time</th>
169
+ <th width="100">Name</th>
170
+ <th>Query</th>
171
+ <th width="150">Source</th>
172
+ </tr>
173
+ </thead>
174
+ <tbody>
175
+ <%- queries.each do |query| -%>
176
+ <tr class="brainz-query-row<%= ' cached' if query[:cached] %><%= ' slow' if (query[:duration] || 0) > 100 %>">
177
+ <td class="brainz-query-duration <%= query_duration_class(query[:duration]) %>">
178
+ <%= sprintf('%.2f', query[:duration] || 0) %>ms
179
+ </td>
180
+ <td><%= h(query[:name] || 'SQL') %></td>
181
+ <td class="brainz-query-sql" title="<%= h(query[:sql]) %>">
182
+ <%= h(truncate(query[:sql], 80)) %>
183
+ </td>
184
+ <td style="font-size: 0.75rem; color: #A0AEC0;"><%= h(query[:source]) %></td>
185
+ </tr>
186
+ <%- end -%>
187
+ </tbody>
188
+ </table>
189
+ <%- else -%>
190
+ <p style="color: #718096; font-size: 0.875rem;">No SQL queries recorded.</p>
191
+ <%- end -%>
192
+ </div>
193
+
194
+ <!-- Views Pane -->
195
+ <div class="brainz-debug-pane" data-pane="views" data-devtools-target="pane">
196
+ <%- templates = @views[:templates] || [] -%>
197
+ <%- if templates.any? -%>
198
+ <table class="brainz-view-table">
199
+ <thead>
200
+ <tr>
201
+ <th width="80">Time</th>
202
+ <th width="80">Type</th>
203
+ <th>Template</th>
204
+ </tr>
205
+ </thead>
206
+ <tbody>
207
+ <%- templates.each do |view| -%>
208
+ <tr class="<%= 'slow' if (view[:duration] || 0) > 50 %>">
209
+ <td style="font-family: var(--brainz-mono); font-size: 0.75rem;" class="<%= 'brainz-slow' if (view[:duration] || 0) > 50 %>">
210
+ <%= sprintf('%.2f', view[:duration] || 0) %>ms
211
+ </td>
212
+ <td><%= h(view[:type]) %></td>
213
+ <td style="font-family: var(--brainz-mono); font-size: 0.75rem;"><%= h(view[:template]) %></td>
214
+ </tr>
215
+ <%- end -%>
216
+ </tbody>
217
+ </table>
218
+ <%- else -%>
219
+ <p style="color: #718096; font-size: 0.875rem;">No view renders recorded.</p>
220
+ <%- end -%>
221
+ </div>
222
+
223
+ <!-- Issues Pane -->
224
+ <div class="brainz-debug-pane" data-pane="issues" data-devtools-target="pane">
225
+ <%- if issues_count == 0 -%>
226
+ <div class="brainz-no-issues">
227
+ <svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
228
+ <path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" stroke-linecap="round" stroke-linejoin="round"/>
229
+ </svg>
230
+ <p>Looking good!</p>
231
+ <span>No performance issues detected in this request. Your code is running efficiently.</span>
232
+ </div>
233
+ <%- else -%>
234
+ <div class="brainz-issues-container">
235
+
236
+ <%# N+1 Queries %>
237
+ <%- if n_plus_ones.any? -%>
238
+ <div class="brainz-issue-section">
239
+ <div class="brainz-issue-header error">
240
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
241
+ <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" stroke-linecap="round" stroke-linejoin="round"/>
242
+ </svg>
243
+ <span>N+1 Query Detection</span>
244
+ <span class="issue-count"><%= n_plus_ones.length %> found</span>
245
+ </div>
246
+ <%- n_plus_ones.each do |n1| -%>
247
+ <div class="brainz-issue-item">
248
+ <div class="brainz-issue-content">
249
+ <span class="brainz-issue-metric critical"><%= n1[:count] %>x</span>
250
+ similar queries totaling <strong><%= format_duration(n1[:total_duration_ms]) %></strong>
251
+ <%- if n1[:source] -%>
252
+ <span class="brainz-issue-source"><%= h(n1[:source]) %></span>
253
+ <%- end -%>
254
+ <code><%= h(truncate(n1[:sample_query], 100)) %></code>
255
+ </div>
256
+ <button class="brainz-copy-to-ai-btn" type="button"
257
+ data-action="click->devtools#copyToAi"
258
+ data-issue-type="n_plus_one"
259
+ data-n1-count="<%= n1[:count] %>"
260
+ data-n1-duration="<%= n1[:total_duration_ms] %>"
261
+ data-n1-source="<%= h(n1[:source] || '') %>"
262
+ data-n1-query="<%= h(n1[:sample_query] || '') %>"
263
+ data-n1-pattern="<%= h(n1[:pattern] || '') %>">
264
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
265
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" stroke-linecap="round" stroke-linejoin="round"/>
266
+ </svg>
267
+ Copy
268
+ </button>
269
+ </div>
270
+ <%- end -%>
271
+ </div>
272
+ <%- end -%>
273
+
274
+ <%# Slow Queries %>
275
+ <%- if slow_queries.any? -%>
276
+ <div class="brainz-issue-section">
277
+ <div class="brainz-issue-header warning">
278
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
279
+ <circle cx="12" cy="12" r="10"/>
280
+ <path d="M12 6v6l4 2" stroke-linecap="round" stroke-linejoin="round"/>
281
+ </svg>
282
+ <span>Slow Queries</span>
283
+ <span class="issue-count"><%= slow_queries.length %> over 100ms</span>
284
+ </div>
285
+ <%- slow_queries.each do |query| -%>
286
+ <div class="brainz-issue-item">
287
+ <div class="brainz-issue-content">
288
+ <span class="brainz-issue-metric slow"><%= sprintf('%.0f', query[:duration]) %>ms</span>
289
+ <span class="brainz-issue-name"><%= h(query[:name] || 'SQL') %></span>
290
+ <%- if query[:source] -%>
291
+ <span class="brainz-issue-source"><%= h(query[:source]) %></span>
292
+ <%- end -%>
293
+ <code><%= h(truncate(query[:sql], 100)) %></code>
294
+ </div>
295
+ <button class="brainz-copy-to-ai-btn" type="button"
296
+ data-action="click->devtools#copyToAi"
297
+ data-issue-type="slow_query"
298
+ data-query-duration="<%= query[:duration] %>"
299
+ data-query-sql="<%= h(query[:sql] || '') %>"
300
+ data-query-source="<%= h(query[:source] || '') %>"
301
+ data-query-name="<%= h(query[:name] || '') %>">
302
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
303
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" stroke-linecap="round" stroke-linejoin="round"/>
304
+ </svg>
305
+ Copy
306
+ </button>
307
+ </div>
308
+ <%- end -%>
309
+ </div>
310
+ <%- end -%>
311
+
312
+ <%# Too Many Queries %>
313
+ <%- if query_count > 20 -%>
314
+ <div class="brainz-issue-section">
315
+ <div class="brainz-issue-header warning">
316
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
317
+ <path d="M4 7v10c0 2 1 3 3 3h10c2 0 3-1 3-3V7c0-2-1-3-3-3H7c-2 0-3 1-3 3z"/>
318
+ <path d="M9 12h6M12 9v6" stroke-linecap="round"/>
319
+ </svg>
320
+ <span>Excessive Database Calls</span>
321
+ <span class="issue-count"><%= query_count %> queries</span>
322
+ </div>
323
+ <div class="brainz-issue-item">
324
+ <div class="brainz-issue-content">
325
+ <span class="brainz-issue-metric slow"><%= query_count %></span>
326
+ queries executed in this request
327
+ <span class="brainz-issue-hint">Use eager loading or caching to reduce database round-trips.</span>
328
+ </div>
329
+ <button class="brainz-copy-to-ai-btn" type="button"
330
+ data-action="click->devtools#copyToAi"
331
+ data-issue-type="too_many_queries"
332
+ data-query-count="<%= query_count %>"
333
+ data-controller-name="<%= h(@controller[:name] || '') %>"
334
+ data-action-name="<%= h(@controller[:action] || '') %>">
335
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
336
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" stroke-linecap="round" stroke-linejoin="round"/>
337
+ </svg>
338
+ Copy
339
+ </button>
340
+ </div>
341
+ </div>
342
+ <%- end -%>
343
+
344
+ <%# Slow Views %>
345
+ <%- if slow_views.any? -%>
346
+ <div class="brainz-issue-section">
347
+ <div class="brainz-issue-header warning">
348
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
349
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
350
+ <path d="M3 9h18M9 21V9" stroke-linecap="round"/>
351
+ </svg>
352
+ <span>Slow View Rendering</span>
353
+ <span class="issue-count"><%= slow_views.length %> over 50ms</span>
354
+ </div>
355
+ <%- slow_views.each do |view| -%>
356
+ <div class="brainz-issue-item">
357
+ <div class="brainz-issue-content">
358
+ <span class="brainz-issue-metric slow"><%= sprintf('%.0f', view[:duration]) %>ms</span>
359
+ <span class="brainz-issue-name"><%= h(view[:type]) %></span>
360
+ <code><%= h(view[:template]) %></code>
361
+ </div>
362
+ <button class="brainz-copy-to-ai-btn" type="button"
363
+ data-action="click->devtools#copyToAi"
364
+ data-issue-type="slow_view"
365
+ data-view-duration="<%= view[:duration] %>"
366
+ data-view-template="<%= h(view[:template] || '') %>"
367
+ data-view-type="<%= h(view[:type] || '') %>">
368
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
369
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" stroke-linecap="round" stroke-linejoin="round"/>
370
+ </svg>
371
+ Copy
372
+ </button>
373
+ </div>
374
+ <%- end -%>
375
+ </div>
376
+ <%- end -%>
377
+
378
+ <%# High Memory Usage %>
379
+ <%- if memory_delta > 50 -%>
380
+ <div class="brainz-issue-section">
381
+ <div class="brainz-issue-header warning">
382
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
383
+ <path d="M22 12h-4l-3 9L9 3l-3 9H2" stroke-linecap="round" stroke-linejoin="round"/>
384
+ </svg>
385
+ <span>High Memory Allocation</span>
386
+ <span class="issue-count">+<%= sprintf('%.0f', memory_delta) %>MB</span>
387
+ </div>
388
+ <div class="brainz-issue-item">
389
+ <div class="brainz-issue-content">
390
+ <span class="brainz-issue-metric critical">+<%= sprintf('%.0f', memory_delta) %>MB</span>
391
+ memory allocated during this request
392
+ <span class="brainz-issue-hint">Check for large data loads or inefficient object creation.</span>
393
+ </div>
394
+ <button class="brainz-copy-to-ai-btn" type="button"
395
+ data-action="click->devtools#copyToAi"
396
+ data-issue-type="high_memory"
397
+ data-memory-delta="<%= memory_delta %>"
398
+ data-memory-before="<%= @memory[:before_mb] %>"
399
+ data-memory-after="<%= @memory[:after_mb] %>"
400
+ data-controller-name="<%= h(@controller[:name] || '') %>"
401
+ data-action-name="<%= h(@controller[:action] || '') %>">
402
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
403
+ <path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" stroke-linecap="round" stroke-linejoin="round"/>
404
+ </svg>
405
+ Copy
406
+ </button>
407
+ </div>
408
+ </div>
409
+ <%- end -%>
410
+
411
+ </div>
412
+ <%- end -%>
413
+ </div>
414
+
415
+ <!-- Timeline Pane -->
416
+ <div class="brainz-debug-pane" data-pane="timeline" data-devtools-target="pane">
417
+ <%- total = (@timing[:duration_ms] || 1).to_f -%>
418
+ <%- db_ms = @database[:total_duration_ms] || 0 -%>
419
+ <%- views_ms = @views[:total_duration_ms] || 0 -%>
420
+ <%- other_ms = [total - db_ms - views_ms, 0].max -%>
421
+ <%- db_pct = (db_ms / total * 100).round -%>
422
+ <%- views_pct = (views_ms / total * 100).round -%>
423
+ <%- other_pct = [100 - db_pct - views_pct, 0].max -%>
424
+
425
+ <!-- Total Time Header -->
426
+ <div class="timeline-header">
427
+ <span class="timeline-total"><%= format_duration(@timing[:duration_ms]) %></span>
428
+ <span class="timeline-label">total request time</span>
429
+ </div>
430
+
431
+ <!-- Timeline Bar -->
432
+ <div class="timeline-bar">
433
+ <%- if db_pct > 0 -%>
434
+ <div class="timeline-segment db" style="width: <%= [db_pct, 5].max %>%">
435
+ <span class="timeline-segment-label">DB</span>
436
+ </div>
437
+ <%- end -%>
438
+ <%- if views_pct > 0 -%>
439
+ <div class="timeline-segment views" style="width: <%= [views_pct, 5].max %>%">
440
+ <span class="timeline-segment-label">Views</span>
441
+ </div>
442
+ <%- end -%>
443
+ <%- if other_pct > 5 -%>
444
+ <div class="timeline-segment other" style="width: <%= other_pct %>%">
445
+ <span class="timeline-segment-label">Other</span>
446
+ </div>
447
+ <%- end -%>
448
+ </div>
449
+
450
+ <!-- Stats Grid -->
451
+ <div class="timeline-stats">
452
+ <div class="timeline-stat">
453
+ <div class="timeline-stat-icon db">
454
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
455
+ <ellipse cx="12" cy="5" rx="9" ry="3"/>
456
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
457
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
458
+ </svg>
459
+ </div>
460
+ <div class="timeline-stat-content">
461
+ <div class="timeline-stat-value"><%= format_duration(db_ms) %></div>
462
+ <div class="timeline-stat-label">Database · <%= @database[:total_count] || 0 %> queries</div>
463
+ </div>
464
+ <div class="timeline-stat-pct"><%= db_pct %>%</div>
465
+ </div>
466
+
467
+ <div class="timeline-stat">
468
+ <div class="timeline-stat-icon views">
469
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
470
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
471
+ <path d="M3 9h18M9 21V9"/>
472
+ </svg>
473
+ </div>
474
+ <div class="timeline-stat-content">
475
+ <div class="timeline-stat-value"><%= format_duration(views_ms) %></div>
476
+ <div class="timeline-stat-label">Views · <%= (@views[:templates] || []).length %> templates</div>
477
+ </div>
478
+ <div class="timeline-stat-pct"><%= views_pct %>%</div>
479
+ </div>
480
+
481
+ <div class="timeline-stat">
482
+ <div class="timeline-stat-icon other">
483
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
484
+ <circle cx="12" cy="12" r="10"/>
485
+ <path d="M12 6v6l4 2"/>
486
+ </svg>
487
+ </div>
488
+ <div class="timeline-stat-content">
489
+ <div class="timeline-stat-value"><%= format_duration(other_ms) %></div>
490
+ <div class="timeline-stat-label">Ruby · controller, middleware</div>
491
+ </div>
492
+ <div class="timeline-stat-pct"><%= other_pct %>%</div>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </div>
497
+ </div>
498
+
499
+ <link rel="stylesheet" href="<%= asset_url('devtools.css') %>">
500
+ <script src="<%= asset_url('devtools.js') %>"></script>