fluyenta-ruby 0.1.14

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 (121) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +68 -0
  3. data/LICENSE +11 -0
  4. data/README.md +571 -0
  5. data/lib/brainzlab/beacon/client.rb +227 -0
  6. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  7. data/lib/brainzlab/beacon.rb +215 -0
  8. data/lib/brainzlab/configuration.rb +676 -0
  9. data/lib/brainzlab/context.rb +90 -0
  10. data/lib/brainzlab/cortex/cache.rb +59 -0
  11. data/lib/brainzlab/cortex/client.rb +159 -0
  12. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  13. data/lib/brainzlab/cortex.rb +223 -0
  14. data/lib/brainzlab/debug.rb +305 -0
  15. data/lib/brainzlab/dendrite/client.rb +250 -0
  16. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  17. data/lib/brainzlab/dendrite.rb +195 -0
  18. data/lib/brainzlab/development/logger.rb +150 -0
  19. data/lib/brainzlab/development/store.rb +121 -0
  20. data/lib/brainzlab/development.rb +72 -0
  21. data/lib/brainzlab/devtools/assets/devtools.css +1329 -0
  22. data/lib/brainzlab/devtools/assets/devtools.js +396 -0
  23. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  24. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +511 -0
  25. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  26. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  27. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  28. data/lib/brainzlab/devtools/middleware/database_handler.rb +177 -0
  29. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  30. data/lib/brainzlab/devtools/middleware/error_page.rb +377 -0
  31. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +159 -0
  32. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +98 -0
  33. data/lib/brainzlab/devtools.rb +75 -0
  34. data/lib/brainzlab/errors.rb +490 -0
  35. data/lib/brainzlab/flux/buffer.rb +96 -0
  36. data/lib/brainzlab/flux/client.rb +68 -0
  37. data/lib/brainzlab/flux/provisioner.rb +124 -0
  38. data/lib/brainzlab/flux.rb +184 -0
  39. data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
  40. data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
  41. data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
  42. data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
  43. data/lib/brainzlab/instrumentation/action_mailer.rb +182 -0
  44. data/lib/brainzlab/instrumentation/action_view.rb +380 -0
  45. data/lib/brainzlab/instrumentation/active_job.rb +569 -0
  46. data/lib/brainzlab/instrumentation/active_record.rb +559 -0
  47. data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
  48. data/lib/brainzlab/instrumentation/active_support_cache.rb +730 -0
  49. data/lib/brainzlab/instrumentation/aws.rb +183 -0
  50. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  51. data/lib/brainzlab/instrumentation/delayed_job.rb +234 -0
  52. data/lib/brainzlab/instrumentation/elasticsearch.rb +209 -0
  53. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  54. data/lib/brainzlab/instrumentation/faraday.rb +181 -0
  55. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  56. data/lib/brainzlab/instrumentation/grape.rb +293 -0
  57. data/lib/brainzlab/instrumentation/graphql.rb +252 -0
  58. data/lib/brainzlab/instrumentation/httparty.rb +193 -0
  59. data/lib/brainzlab/instrumentation/mongodb.rb +187 -0
  60. data/lib/brainzlab/instrumentation/net_http.rb +114 -0
  61. data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
  62. data/lib/brainzlab/instrumentation/railties.rb +134 -0
  63. data/lib/brainzlab/instrumentation/redis.rb +324 -0
  64. data/lib/brainzlab/instrumentation/resque.rb +114 -0
  65. data/lib/brainzlab/instrumentation/sidekiq.rb +265 -0
  66. data/lib/brainzlab/instrumentation/solid_queue.rb +194 -0
  67. data/lib/brainzlab/instrumentation/stripe.rb +163 -0
  68. data/lib/brainzlab/instrumentation/typhoeus.rb +106 -0
  69. data/lib/brainzlab/instrumentation.rb +360 -0
  70. data/lib/brainzlab/nerve/client.rb +235 -0
  71. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  72. data/lib/brainzlab/nerve.rb +219 -0
  73. data/lib/brainzlab/pulse/client.rb +203 -0
  74. data/lib/brainzlab/pulse/instrumentation.rb +401 -0
  75. data/lib/brainzlab/pulse/propagation.rb +241 -0
  76. data/lib/brainzlab/pulse/provisioner.rb +114 -0
  77. data/lib/brainzlab/pulse/tracer.rb +111 -0
  78. data/lib/brainzlab/pulse.rb +294 -0
  79. data/lib/brainzlab/rails/log_formatter.rb +807 -0
  80. data/lib/brainzlab/rails/log_subscriber.rb +334 -0
  81. data/lib/brainzlab/rails/railtie.rb +606 -0
  82. data/lib/brainzlab/recall/buffer.rb +66 -0
  83. data/lib/brainzlab/recall/client.rb +158 -0
  84. data/lib/brainzlab/recall/logger.rb +116 -0
  85. data/lib/brainzlab/recall/provisioner.rb +130 -0
  86. data/lib/brainzlab/recall.rb +175 -0
  87. data/lib/brainzlab/reflex/breadcrumbs.rb +55 -0
  88. data/lib/brainzlab/reflex/client.rb +150 -0
  89. data/lib/brainzlab/reflex/provisioner.rb +116 -0
  90. data/lib/brainzlab/reflex.rb +421 -0
  91. data/lib/brainzlab/sentinel/client.rb +236 -0
  92. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  93. data/lib/brainzlab/sentinel.rb +165 -0
  94. data/lib/brainzlab/signal/client.rb +60 -0
  95. data/lib/brainzlab/signal/provisioner.rb +115 -0
  96. data/lib/brainzlab/signal.rb +136 -0
  97. data/lib/brainzlab/synapse/client.rb +308 -0
  98. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  99. data/lib/brainzlab/synapse.rb +270 -0
  100. data/lib/brainzlab/testing/event_store.rb +377 -0
  101. data/lib/brainzlab/testing/helpers.rb +650 -0
  102. data/lib/brainzlab/testing/matchers.rb +391 -0
  103. data/lib/brainzlab/testing.rb +327 -0
  104. data/lib/brainzlab/utilities/circuit_breaker.rb +290 -0
  105. data/lib/brainzlab/utilities/health_check.rb +294 -0
  106. data/lib/brainzlab/utilities/log_formatter.rb +254 -0
  107. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  108. data/lib/brainzlab/utilities.rb +17 -0
  109. data/lib/brainzlab/vault/cache.rb +80 -0
  110. data/lib/brainzlab/vault/client.rb +216 -0
  111. data/lib/brainzlab/vault/provisioner.rb +49 -0
  112. data/lib/brainzlab/vault.rb +262 -0
  113. data/lib/brainzlab/version.rb +5 -0
  114. data/lib/brainzlab/vision/client.rb +175 -0
  115. data/lib/brainzlab/vision/provisioner.rb +136 -0
  116. data/lib/brainzlab/vision.rb +155 -0
  117. data/lib/brainzlab-sdk.rb +3 -0
  118. data/lib/brainzlab.rb +306 -0
  119. data/lib/generators/brainzlab/install/install_generator.rb +63 -0
  120. data/lib/generators/brainzlab/install/templates/brainzlab.rb.tt +77 -0
  121. metadata +251 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * BrainzLab DevTools - Stimulus Controller
3
+ */
4
+ (function() {
5
+ 'use strict';
6
+
7
+ // ============================================
8
+ // DARK MODE SUPPORT
9
+ // ============================================
10
+ // Sync with brainzlab-theme localStorage key (used across all BrainzLab products)
11
+ function initDarkMode() {
12
+ const theme = localStorage.getItem('brainzlab-theme');
13
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
14
+
15
+ if (theme === 'dark' || (!theme && prefersDark)) {
16
+ document.documentElement.classList.add('dark');
17
+ } else {
18
+ document.documentElement.classList.remove('dark');
19
+ }
20
+ }
21
+
22
+ // Listen for theme changes from other windows/tabs
23
+ function setupThemeListener() {
24
+ window.addEventListener('storage', function(e) {
25
+ if (e.key === 'brainzlab-theme') {
26
+ initDarkMode();
27
+ }
28
+ });
29
+
30
+ // Also listen for system preference changes
31
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
32
+ // Only react if no explicit theme is set
33
+ if (!localStorage.getItem('brainzlab-theme')) {
34
+ if (e.matches) {
35
+ document.documentElement.classList.add('dark');
36
+ } else {
37
+ document.documentElement.classList.remove('dark');
38
+ }
39
+ }
40
+ });
41
+ }
42
+
43
+ // Initialize dark mode immediately
44
+ initDarkMode();
45
+ setupThemeListener();
46
+
47
+ // Load Stimulus if not available
48
+ let stimulusApp = null;
49
+ let StimulusController = null;
50
+
51
+ async function loadStimulus() {
52
+ if (stimulusApp) return { app: stimulusApp, Controller: StimulusController };
53
+
54
+ // Check if already loading
55
+ if (window._stimulusLoading) return window._stimulusLoading;
56
+
57
+ window._stimulusLoading = new Promise((resolve) => {
58
+ const script = document.createElement('script');
59
+ script.src = 'https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js';
60
+ script.onload = () => {
61
+ StimulusController = window.Stimulus.Controller;
62
+ stimulusApp = window.Stimulus.Application.start();
63
+ resolve({ app: stimulusApp, Controller: StimulusController });
64
+ };
65
+ document.head.appendChild(script);
66
+ });
67
+
68
+ return window._stimulusLoading;
69
+ }
70
+
71
+ // Stimulus Controller Definition
72
+ const DevtoolsController = {
73
+ static: {
74
+ targets: ['panel', 'tab', 'pane', 'toast', 'restoreBtn']
75
+ },
76
+
77
+ // Lifecycle
78
+ connect() {
79
+ this.loadState();
80
+ this.bindKeyboardShortcuts();
81
+ },
82
+
83
+ disconnect() {
84
+ this.unbindKeyboardShortcuts();
85
+ },
86
+
87
+ // Actions
88
+ togglePanel() {
89
+ // Don't toggle if minimized - use restorePanel instead
90
+ if (this.element.classList.contains('minimized')) {
91
+ return;
92
+ }
93
+ this.element.classList.toggle('collapsed');
94
+ this.saveState();
95
+ },
96
+
97
+ minimizePanel(event) {
98
+ if (event) {
99
+ event.stopPropagation();
100
+ event.preventDefault();
101
+ }
102
+ this.element.classList.add('minimized');
103
+ this.saveState();
104
+ },
105
+
106
+ restorePanel(event) {
107
+ if (event) {
108
+ event.stopPropagation();
109
+ event.preventDefault();
110
+ }
111
+ this.element.classList.remove('minimized');
112
+ this.saveState();
113
+ },
114
+
115
+ switchTab(event) {
116
+ event.stopPropagation();
117
+ const tabName = event.currentTarget.dataset.tab;
118
+
119
+ // Update tab buttons
120
+ this.element.querySelectorAll('[data-devtools-target="tab"]').forEach(t => {
121
+ t.classList.toggle('active', t.dataset.tab === tabName);
122
+ });
123
+
124
+ // Update content panes
125
+ this.element.querySelectorAll('[data-devtools-target="pane"]').forEach(p => {
126
+ p.classList.toggle('active', p.dataset.pane === tabName);
127
+ });
128
+
129
+ this.saveState();
130
+ },
131
+
132
+ copyToAi(event) {
133
+ event.stopPropagation();
134
+ event.preventDefault();
135
+
136
+ const button = event.currentTarget;
137
+ const issueType = button.dataset.issueType;
138
+ const prompt = this.buildPrompt(issueType, button);
139
+
140
+ this.copyToClipboard(prompt);
141
+ this.showToast('Copied to clipboard');
142
+
143
+ // Visual feedback
144
+ button.classList.add('copied');
145
+ setTimeout(() => button.classList.remove('copied'), 1500);
146
+ },
147
+
148
+ copySql(event) {
149
+ const cell = event.currentTarget;
150
+ const sql = cell.getAttribute('title') || cell.textContent;
151
+ this.copyToClipboard(sql);
152
+ this.showToast('SQL copied');
153
+ },
154
+
155
+ // Prompt Builders
156
+ buildPrompt(issueType, button) {
157
+ const builders = {
158
+ n_plus_one: () => this.buildN1Prompt(button),
159
+ slow_query: () => this.buildSlowQueryPrompt(button),
160
+ too_many_queries: () => this.buildTooManyQueriesPrompt(button),
161
+ slow_view: () => this.buildSlowViewPrompt(button),
162
+ high_memory: () => this.buildHighMemoryPrompt(button)
163
+ };
164
+
165
+ return builders[issueType]?.() || 'Unknown issue type';
166
+ },
167
+
168
+ buildN1Prompt(btn) {
169
+ const { n1Count, n1Duration, n1Source, n1Query, n1Pattern } = btn.dataset;
170
+ return `Fix this N+1 query issue in my Rails application:
171
+
172
+ ## Problem
173
+ Detected ${n1Count || 'unknown'}x similar queries (${n1Duration || 'unknown'}ms total) from:
174
+ \`${n1Source || 'unknown location'}\`
175
+
176
+ ## Sample Query
177
+ \`\`\`sql
178
+ ${n1Query || ''}
179
+ \`\`\`
180
+
181
+ ## Pattern
182
+ \`\`\`
183
+ ${n1Pattern || ''}
184
+ \`\`\`
185
+
186
+ ## Instructions
187
+ 1. Find the source code at the location mentioned above
188
+ 2. Identify why this query is being executed multiple times
189
+ 3. Suggest a fix using eager loading (includes/preload/eager_load) or other optimization
190
+ 4. Show me the before and after code`;
191
+ },
192
+
193
+ buildSlowQueryPrompt(btn) {
194
+ const { queryDuration, querySql, querySource, queryName } = btn.dataset;
195
+ return `Optimize this slow database query in my Rails application:
196
+
197
+ ## Problem
198
+ Query taking ${queryDuration || 'unknown'}ms (threshold: 100ms)
199
+ Source: \`${querySource || 'unknown location'}\`
200
+ Name: ${queryName || 'SQL'}
201
+
202
+ ## Query
203
+ \`\`\`sql
204
+ ${querySql || ''}
205
+ \`\`\`
206
+
207
+ ## Instructions
208
+ 1. Analyze why this query might be slow
209
+ 2. Check if there are missing database indexes
210
+ 3. Suggest query optimizations or restructuring
211
+ 4. If applicable, suggest caching strategies
212
+ 5. Show me the recommended changes`;
213
+ },
214
+
215
+ buildTooManyQueriesPrompt(btn) {
216
+ const { queryCount, controllerName, actionName } = btn.dataset;
217
+ return `Reduce the number of database queries in my Rails application:
218
+
219
+ ## Problem
220
+ ${queryCount || 'unknown'} queries executed in a single request (threshold: 20)
221
+ Controller: ${controllerName || 'unknown'}#${actionName || 'unknown'}
222
+
223
+ ## Instructions
224
+ 1. Look at the controller action and identify what data is being loaded
225
+ 2. Find opportunities to use eager loading (includes/preload/eager_load)
226
+ 3. Identify if any queries can be combined or eliminated
227
+ 4. Check for queries inside loops (potential N+1)
228
+ 5. Suggest caching for frequently accessed data
229
+ 6. Show me the before and after code`;
230
+ },
231
+
232
+ buildSlowViewPrompt(btn) {
233
+ const { viewDuration, viewTemplate, viewType } = btn.dataset;
234
+ return `Optimize this slow view render in my Rails application:
235
+
236
+ ## Problem
237
+ ${viewType || 'template'} taking ${viewDuration || 'unknown'}ms to render (threshold: 50ms)
238
+ Template: ${viewTemplate || 'unknown'}
239
+
240
+ ## Instructions
241
+ 1. Look at the template and identify expensive operations
242
+ 2. Check for complex logic that should be moved to helpers or presenters
243
+ 3. Look for database queries being made in the view
244
+ 4. Identify if fragment caching could help
245
+ 5. Check for unnecessary partial renders or loops
246
+ 6. Suggest optimizations and show me the recommended changes`;
247
+ },
248
+
249
+ buildHighMemoryPrompt(btn) {
250
+ const { memoryDelta, memoryBefore, memoryAfter, controllerName, actionName } = btn.dataset;
251
+ return `Investigate high memory usage in my Rails application:
252
+
253
+ ## Problem
254
+ Request allocated +${memoryDelta || 'unknown'}MB of memory (threshold: 50MB)
255
+ Memory before: ${memoryBefore || 'unknown'}MB
256
+ Memory after: ${memoryAfter || 'unknown'}MB
257
+ Controller: ${controllerName || 'unknown'}#${actionName || 'unknown'}
258
+
259
+ ## Instructions
260
+ 1. Look at the controller action and identify what data is being loaded
261
+ 2. Check for loading large datasets into memory (use find_each/find_in_batches)
262
+ 3. Look for creating many objects in loops
263
+ 4. Check if large files or blobs are being processed
264
+ 5. Identify if streaming responses could help
265
+ 6. Suggest memory optimizations and show me the recommended changes`;
266
+ },
267
+
268
+ // Utilities
269
+ copyToClipboard(text) {
270
+ if (navigator.clipboard?.writeText) {
271
+ navigator.clipboard.writeText(text);
272
+ } else {
273
+ const textarea = document.createElement('textarea');
274
+ textarea.value = text;
275
+ textarea.style.cssText = 'position:fixed;opacity:0';
276
+ document.body.appendChild(textarea);
277
+ textarea.select();
278
+ document.execCommand('copy');
279
+ document.body.removeChild(textarea);
280
+ }
281
+ },
282
+
283
+ showToast(message) {
284
+ // Remove existing toast
285
+ const existing = document.querySelector('.brainz-toast');
286
+ if (existing) existing.remove();
287
+
288
+ const toast = document.createElement('div');
289
+ toast.className = 'brainz-toast';
290
+ toast.textContent = message;
291
+ document.body.appendChild(toast);
292
+
293
+ requestAnimationFrame(() => toast.classList.add('visible'));
294
+
295
+ setTimeout(() => {
296
+ toast.classList.remove('visible');
297
+ setTimeout(() => toast.remove(), 200);
298
+ }, 2000);
299
+ },
300
+
301
+ // State Management
302
+ saveState() {
303
+ try {
304
+ const activeTab = this.element.querySelector('[data-devtools-target="tab"].active');
305
+ sessionStorage.setItem('brainz-devtools-state', JSON.stringify({
306
+ collapsed: this.element.classList.contains('collapsed'),
307
+ minimized: this.element.classList.contains('minimized'),
308
+ activeTab: activeTab?.dataset.tab || 'request'
309
+ }));
310
+ } catch (e) { /* ignore */ }
311
+ },
312
+
313
+ loadState() {
314
+ try {
315
+ const state = JSON.parse(sessionStorage.getItem('brainz-devtools-state'));
316
+ if (state?.minimized) {
317
+ this.element.classList.add('minimized');
318
+ } else if (state?.collapsed) {
319
+ this.element.classList.add('collapsed');
320
+ }
321
+ if (state?.activeTab) {
322
+ const tab = this.element.querySelector(`[data-tab="${state.activeTab}"]`);
323
+ if (tab) tab.click();
324
+ }
325
+ } catch (e) { /* ignore */ }
326
+ },
327
+
328
+ // Keyboard Shortcuts
329
+ bindKeyboardShortcuts() {
330
+ this._keyHandler = (e) => {
331
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'B') {
332
+ e.preventDefault();
333
+ // If minimized, restore first
334
+ if (this.element.classList.contains('minimized')) {
335
+ this.restorePanel();
336
+ } else {
337
+ this.togglePanel();
338
+ }
339
+ }
340
+ if (e.key === 'Escape' && !this.element.classList.contains('collapsed') && !this.element.classList.contains('minimized')) {
341
+ this.togglePanel();
342
+ }
343
+ };
344
+ document.addEventListener('keydown', this._keyHandler);
345
+ },
346
+
347
+ unbindKeyboardShortcuts() {
348
+ if (this._keyHandler) {
349
+ document.removeEventListener('keydown', this._keyHandler);
350
+ }
351
+ }
352
+ };
353
+
354
+ // Register Stimulus controller
355
+ async function initialize() {
356
+ const panel = document.querySelector('.brainz-debug-panel');
357
+ if (!panel) return;
358
+
359
+ // Load Stimulus and register controller
360
+ const { app, Controller } = await loadStimulus();
361
+
362
+ // Register the devtools controller
363
+ app.register('devtools', class extends Controller {
364
+ static targets = ['tab', 'pane', 'restoreBtn'];
365
+
366
+ connect() { DevtoolsController.connect.call(this); }
367
+ disconnect() { DevtoolsController.disconnect.call(this); }
368
+ togglePanel() { DevtoolsController.togglePanel.call(this); }
369
+ minimizePanel(e) { DevtoolsController.minimizePanel.call(this, e); }
370
+ restorePanel(e) { DevtoolsController.restorePanel.call(this, e); }
371
+ switchTab(e) { DevtoolsController.switchTab.call(this, e); }
372
+ copyToAi(e) { DevtoolsController.copyToAi.call(this, e); }
373
+ copySql(e) { DevtoolsController.copySql.call(this, e); }
374
+
375
+ buildPrompt(...args) { return DevtoolsController.buildPrompt.call(this, ...args); }
376
+ buildN1Prompt(...args) { return DevtoolsController.buildN1Prompt.call(this, ...args); }
377
+ buildSlowQueryPrompt(...args) { return DevtoolsController.buildSlowQueryPrompt.call(this, ...args); }
378
+ buildTooManyQueriesPrompt(...args) { return DevtoolsController.buildTooManyQueriesPrompt.call(this, ...args); }
379
+ buildSlowViewPrompt(...args) { return DevtoolsController.buildSlowViewPrompt.call(this, ...args); }
380
+ buildHighMemoryPrompt(...args) { return DevtoolsController.buildHighMemoryPrompt.call(this, ...args); }
381
+ copyToClipboard(...args) { return DevtoolsController.copyToClipboard.call(this, ...args); }
382
+ showToast(...args) { return DevtoolsController.showToast.call(this, ...args); }
383
+ saveState() { DevtoolsController.saveState.call(this); }
384
+ loadState() { DevtoolsController.loadState.call(this); }
385
+ bindKeyboardShortcuts() { DevtoolsController.bindKeyboardShortcuts.call(this); }
386
+ unbindKeyboardShortcuts() { DevtoolsController.unbindKeyboardShortcuts.call(this); }
387
+ });
388
+ }
389
+
390
+ // Initialize on DOM ready
391
+ if (document.readyState === 'loading') {
392
+ document.addEventListener('DOMContentLoaded', initialize);
393
+ } else {
394
+ initialize();
395
+ }
396
+ })();
@@ -0,0 +1,6 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="32" height="32" rx="8" fill="#FF6B35"/>
3
+ <path d="M8 12C8 10.8954 8.89543 10 10 10H22C23.1046 10 24 10.8954 24 12V13C24 13.5523 23.5523 14 23 14H9C8.44772 14 8 13.5523 8 13V12Z" fill="white"/>
4
+ <path d="M8 17C8 16.4477 8.44772 16 9 16H23C23.5523 16 24 16.4477 24 17V18C24 18.5523 23.5523 19 23 19H9C8.44772 19 8 18.5523 8 18V17Z" fill="white" fill-opacity="0.8"/>
5
+ <path d="M8 22C8 21.4477 8.44772 21 9 21H18C18.5523 21 19 21.4477 19 22C19 22.5523 18.5523 23 18 23H9C8.44772 23 8 22.5523 8 22Z" fill="white" fill-opacity="0.6"/>
6
+ </svg>