ai_screen_analyzer 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a6bf3e8162eb1f6bf74dbc3c95014e2dc85eac5b366ab8250c37565b3a2a3cd8
4
+ data.tar.gz: 6cc3545842cf2c2a3590c97a1303a4551e85c59f54ca5c4eb24547035608e6db
5
+ SHA512:
6
+ metadata.gz: 18c631bb87ef6ec91e4b730202dd4ad330c8cb54c711e8a2332624417bac0d07df127da950244c016651a8496608825e19303abe4ccc352036bd3a23ff0705da
7
+ data.tar.gz: b776e0d6e50be723bce6632a709715a63a448cdee294273d878237fa1ae7e865197eb21c592f9e8e44c8f1d75c45bdf53b1f3cd544f28a43761bea65d153f927
data/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2024-12-10
9
+
10
+ ### Added
11
+ - Initial release of AI Screen Analyzer gem
12
+ - `ai_screen_analyzer` helper to add AI screen analysis to any page
13
+ - Customizable floating button
14
+ - Elegant sidebar to display results
15
+ - Support for customizable prompts
16
+ - Controller to process analysis requests
17
+ - Integration with OpenAI GPT-4V API
18
+ - Installation generator
19
+ - Complete documentation
20
+
21
+ ### Features
22
+ - Screenshot capture using html2canvas
23
+ - Image upload for AI analysis
24
+ - Real-time results display
25
+ - Support for customizing colors and texts
26
+ - Responsive for mobile devices
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 AI Screen Analyzer Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # AI Screen Analyzer
2
+
3
+ A Rails gem that adds AI-powered screen analysis to any application. With a simple helper, you can capture the visual content of any page, send it for analysis with GPT-4V, and display the result in an interactive sidebar.
4
+
5
+ ![usage_sample.gif](sample.gif)
6
+
7
+ ## Features
8
+
9
+ - **Customizable Floating Button:** A sleek button that appears on any page.
10
+ - **Automatic Screenshot Capture:** Uses `html2canvas` to capture the visual content of the page.
11
+ - **AI Analysis:** Integration with OpenAI's GPT-4V for intelligent analysis.
12
+ - **Responsive Sidebar:** Displays results in an elegant, non-intrusive sidebar.
13
+ - **Customizable Prompts:** Configure different prompts for different use cases.
14
+ - **Easy Integration:** Just add one line of code to your layout.
15
+ - **No External Dependencies:** Uses only standard Rails libraries.
16
+
17
+ ## Installation
18
+
19
+ Add the gem to your `Gemfile`:
20
+
21
+ ```ruby
22
+ gem 'ai_screen_analyzer'
23
+ ```
24
+
25
+ Run bundler:
26
+
27
+ ```bash
28
+ bundle install
29
+ ```
30
+
31
+ Run the installation generator:
32
+
33
+ ```bash
34
+ rails generate ai_screen_analyzer:install
35
+ ```
36
+
37
+ ## Routes
38
+
39
+ The generator automatically mounts the engine in your host application. After installation, these routes are available by default:
40
+
41
+ - Engine mounted at: `/ai_screen_analyzer`
42
+ - Analysis endpoint: `POST /ai_screen_analyzer/analyze-screen`
43
+
44
+ If you need to mount manually (or customize the path), add this to your app's `config/routes.rb`:
45
+
46
+ ```ruby
47
+ # Mount the AiScreenAnalyzer engine, exposing its routes under /ai_screen_analyzer
48
+ mount AiScreenAnalyzer::Engine => '/ai_screen_analyzer', as: 'ai_screen_analyzer_engine'
49
+
50
+ # (Optional) Direct alias for compatibility with existing calls
51
+ post '/ai_screen_analyzer/analyze-screen', to: 'ai_screen_analyzer/ai_analyzer#analyze_screen'
52
+ ```
53
+
54
+ Notes:
55
+ - The gem's view uses route helpers to resolve the endpoint dynamically according to the mounted path, avoiding 404s when you customize the path.
56
+ - If you change the mount path, you don't need to change anything in the view; the helper will point to the new path.
57
+
58
+ ## Configuration
59
+
60
+ ### 1. OpenAI API Key
61
+
62
+ Set your OpenAI API key as an environment variable:
63
+
64
+ ```bash
65
+ export OPENAI_API_KEY='your_secret_key_here'
66
+ ```
67
+
68
+ Or configure it in `config/initializers/ai_screen_analyzer.rb`:
69
+
70
+ ```ruby
71
+ AiScreenAnalyzer.configure do |config|
72
+ config.api_key = ENV['OPENAI_API_KEY']
73
+ config.model = 'gpt-4-turbo'
74
+ end
75
+ ```
76
+
77
+ ### 2. Add the Helper to the Layout
78
+
79
+ Open your main layout (`app/views/layouts/application.html.erb`) and add:
80
+
81
+ ```erb
82
+ <body>
83
+ <%= yield %>
84
+
85
+ <%= ai_screen_analyzer %>
86
+ </body>
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ ### Basic Usage
92
+
93
+ ```erb
94
+ <%= ai_screen_analyzer %>
95
+ ```
96
+
97
+ ### With Customizations
98
+
99
+ ```erb
100
+ <%= ai_screen_analyzer(
101
+ prompt: "You are a UX expert. Analyze this interface and provide feedback.",
102
+ button_text: "Review Design",
103
+ sidebar_title: "UX Feedback"
104
+ ) %>
105
+ ```
106
+
107
+ ## Options
108
+
109
+ | Option | Description | Default |
110
+ |-------|-----------|--------|
111
+ | `prompt` | The prompt text sent to the AI along with the image | "Analyze the visible content on this screen..." |
112
+ | `button_text` | Text displayed on the floating button | "Analyze with AI" |
113
+ | `sidebar_title` | Title of the results sidebar | "AI Analysis" |
114
+
115
+ ## Usage Examples
116
+
117
+ ### UX/UI Analysis
118
+
119
+ ```erb
120
+ <%= ai_screen_analyzer(
121
+ prompt: "You are a UX/UI expert. Analyze this interface and provide feedback on: 1) Visual hierarchy, 2) Accessibility, 3) Design consistency, 4) Improvement suggestions.",
122
+ button_text: "Review Design",
123
+ sidebar_title: "UX/UI Feedback"
124
+ ) %>
125
+ ```
126
+
127
+ ### Accessibility Analysis
128
+
129
+ ```erb
130
+ <%= ai_screen_analyzer(
131
+ prompt: "You are a web accessibility (WCAG) specialist. Analyze this page and identify potential accessibility issues.",
132
+ button_text: "Check Accessibility",
133
+ sidebar_title: "Accessibility Analysis"
134
+ ) %>
135
+ ```
136
+
137
+ ### Content Analysis
138
+
139
+ ```erb
140
+ <%= ai_screen_analyzer(
141
+ prompt: "Analyze the textual content of this page. Evaluate: 1) Clarity of message, 2) Writing quality, 3) Tone appropriateness.",
142
+ button_text: "Analyze Content",
143
+ sidebar_title: "Content Analysis"
144
+ ) %>
145
+ ```
146
+
147
+ ## How It Works
148
+
149
+ 1. The user clicks the "Analyze with AI" button.
150
+ 2. The `html2canvas` JavaScript library captures an image of the page body.
151
+ 3. The image is converted to base64 and sent to the Rails backend.
152
+ 4. The controller processes the image and sends it to the GPT-4V API.
153
+ 5. The AI analyzes the image according to the provided prompt.
154
+ 6. The result is returned and displayed in the sidebar.
155
+
156
+ ## Gem Structure
157
+
158
+ ```
159
+ ai_screen_analyzer/
160
+ ├── app/
161
+ │ ├── controllers/
162
+ │ │ └── ai_screen_analyzer/
163
+ │ │ └── ai_analyzer_controller.rb
164
+ │ ├── helpers/
165
+ │ │ └── ai_screen_analyzer/
166
+ │ │ └── ai_screen_analyzer_helper.rb
167
+ │ └── views/
168
+ │ └── ai_screen_analyzer/
169
+ │ └── shared/
170
+ │ └── _ai_screen_analyzer.html.erb
171
+ ├── config/
172
+ │ └── routes.rb
173
+ ├── lib/
174
+ │ ├── ai_screen_analyzer.rb
175
+ │ ├── ai_screen_analyzer/
176
+ │ │ ├── engine.rb
177
+ │ │ └── version.rb
178
+ │ └── generators/
179
+ │ └── ai_screen_analyzer/
180
+ │ └── install_generator.rb
181
+ ├── ai_screen_analyzer.gemspec
182
+ ├── Rakefile
183
+ ├── README.md
184
+ ├── LICENSE.txt
185
+ └── CHANGELOG.md
186
+ ```
187
+
188
+ ## Requirements
189
+
190
+ - Rails 6.0 or higher
191
+ - Ruby 2.7.0 or higher
192
+ - Valid OpenAI API key (with access to the gpt-4-turbo model)
193
+
194
+ ## Troubleshooting
195
+
196
+ ### The button does not appear
197
+
198
+ Make sure the helper is being called correctly in your layout and there are no errors in the browser console.
199
+
200
+ ### Analysis is not working
201
+
202
+ Ensure that:
203
+ - The OpenAI API key is configured correctly
204
+ - You have sufficient credits in your OpenAI account
205
+ - The route is registered correctly
206
+
207
+ ### 404 error when calling analyze-screen
208
+
209
+ - Ensure the engine is mounted in the host app. By default, the generator already adds this line to your `config/routes.rb`:
210
+
211
+ ```ruby
212
+ mount AiScreenAnalyzer::Engine => '/ai_screen_analyzer', as: 'ai_screen_analyzer_engine'
213
+ ```
214
+
215
+ - If you removed the automatic mount, add it manually as above.
216
+ - Restart the server after changing routes.
217
+ - Run `bin/rails routes | grep ai_screen_analyzer` to confirm the presence of the `analyze-screen` endpoint.
218
+
219
+ ### CORS error
220
+
221
+ If you receive CORS errors when capturing the screen, ensure that all external resources are using HTTPS.
222
+
223
+ ## Contributing
224
+
225
+ Contributions are welcome! Please open an issue or submit a pull request.
226
+
227
+ ## License
228
+
229
+ This gem is licensed under the MIT License. See LICENSE.txt for details.
230
+
231
+ ## Support
232
+
233
+ For support, open an issue in the GitHub repository or contact the maintainers.
234
+
235
+ ## Changelog
236
+
237
+ See the CHANGELOG.md file for a list of all changes.
238
+
239
+ ## Roadmap
240
+
241
+ - Support for other AI models (Claude, Gemini, etc.)
242
+ - Cache of recent analyses
@@ -0,0 +1,91 @@
1
+ require 'base64'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ module AiScreenAnalyzer
6
+ class AiAnalyzerController < ActionController::Base
7
+ skip_before_action :verify_authenticity_token, only: [:analyze_screen]
8
+
9
+ def analyze_screen
10
+ begin
11
+ image_data = params[:image]
12
+ prompt = params[:prompt]
13
+
14
+ # Validar parâmetros
15
+ return render_error('Imagem não fornecida') if image_data.blank?
16
+ return render_error('Prompt não fornecido') if prompt.blank?
17
+
18
+ # Remover data URI prefix se presente
19
+ image_data = image_data.sub(/^data:image\/\w+;base64,/, '')
20
+
21
+ # Chamar API do GPT-4V
22
+ analysis = analyze_with_gpt4v(image_data, prompt)
23
+
24
+ render json: {
25
+ success: true,
26
+ analysis: analysis
27
+ }
28
+ rescue StandardError => e
29
+ render_error(e.message)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def analyze_with_gpt4v(image_data, prompt)
36
+ api_key = ENV['OPENAI_API_KEY'] || AiScreenAnalyzer.api_key
37
+ return 'Chave de API não configurada' if api_key.blank?
38
+
39
+ uri = URI('https://api.openai.com/v1/chat/completions')
40
+ http = Net::HTTP.new(uri.host, uri.port)
41
+ http.use_ssl = true
42
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
43
+
44
+ request = Net::HTTP::Post.new(uri.path)
45
+ request['Content-Type'] = 'application/json'
46
+ request['Authorization'] = "Bearer #{api_key}"
47
+
48
+ model = AiScreenAnalyzer.model || 'gpt-4-turbo'
49
+
50
+ body = {
51
+ model: model,
52
+ messages: [
53
+ {
54
+ role: 'user',
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: prompt
59
+ },
60
+ {
61
+ type: 'image_url',
62
+ image_url: {
63
+ url: "data:image/jpeg;base64,#{image_data}"
64
+ }
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ max_tokens: 1024
70
+ }
71
+
72
+ request.body = body.to_json
73
+
74
+ response = http.request(request)
75
+ result = JSON.parse(response.body)
76
+
77
+ if response.code.to_i == 200
78
+ result['choices'][0]['message']['content']
79
+ else
80
+ raise "Erro da API: #{result['error']['message']}"
81
+ end
82
+ end
83
+
84
+ def render_error(message)
85
+ render json: {
86
+ success: false,
87
+ error: message
88
+ }, status: :unprocessable_entity
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,15 @@
1
+ module AiScreenAnalyzer
2
+ module AiScreenAnalyzerHelper
3
+ def ai_screen_analyzer(options = {})
4
+ prompt = options[:prompt] || AiScreenAnalyzer.default_prompt
5
+ button_text = options[:button_text] || AiScreenAnalyzer.default_button_text
6
+ sidebar_title = options[:sidebar_title] || AiScreenAnalyzer.default_sidebar_title
7
+
8
+ render 'ai_screen_analyzer/shared/ai_screen_analyzer', {
9
+ prompt: prompt,
10
+ button_text: button_text,
11
+ sidebar_title: sidebar_title
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,374 @@
1
+ <% # AI Screen Analyzer Component %>
2
+
3
+ <div>
4
+ <%# Resolve dinamicamente o endpoint respeitando o path de montagem definido no app host %>
5
+ <% endpoint_path = (analyze_screen_path rescue '/ai_screen_analyzer/analyze-screen') %>
6
+ <!-- Botão flutuante para ativar análise -->
7
+ <button
8
+ class="button"
9
+ id="ai-analyzer-trigger"
10
+ title="Clique para analisar a tela com IA"
11
+ >
12
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
13
+ <circle cx="12" cy="12" r="1"></circle>
14
+ <path d="M12 1v6m0 6v6"></path>
15
+ <path d="M4.22 4.22l4.24 4.24m5.08 5.08l4.24 4.24"></path>
16
+ <path d="M1 12h6m6 0h6"></path>
17
+ <path d="M4.22 19.78l4.24-4.24m5.08-5.08l4.24-4.24"></path>
18
+ </svg>
19
+ <%= button_text %>
20
+ </button>
21
+
22
+ <!-- Barra lateral com resultado da análise -->
23
+ <div class="ai-analyzer-sidebar" id="ai-analyzer-sidebar">
24
+ <div class="ai-analyzer-header">
25
+ <h3><%= sidebar_title %></h3>
26
+ <button class="ai-analyzer-close" id="ai-analyzer-close">
27
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
28
+ <line x1="18" y1="6" x2="6" y2="18"></line>
29
+ <line x1="6" y1="6" x2="18" y2="18"></line>
30
+ </svg>
31
+ </button>
32
+ </div>
33
+
34
+ <div class="ai-analyzer-content" id="ai-analyzer-content">
35
+ <div class="ai-analyzer-loading" id="ai-analyzer-loading" style="display: none;">
36
+ <div class="spinner"></div>
37
+ <p>Analisando tela com IA...</p>
38
+ </div>
39
+ <div class="ai-analyzer-result" id="ai-analyzer-result" style="display: none;"></div>
40
+ <div class="ai-analyzer-error" id="ai-analyzer-error" style="display: none;"></div>
41
+ </div>
42
+ </div>
43
+
44
+ <!-- Overlay para fechar sidebar -->
45
+ <div class="ai-analyzer-overlay" id="ai-analyzer-overlay"></div>
46
+ </div>
47
+
48
+ <style>
49
+ .ai-screen-analyzer-container {
50
+ position: fixed;
51
+ z-index: 9998;
52
+ }
53
+
54
+ /* Botão flutuante */
55
+ .ai-analyzer-button {
56
+ position: fixed;
57
+ bottom: 20px;
58
+ right: 20px;
59
+ z-index: 9999;
60
+ width: 56px;
61
+ height: 56px;
62
+ border-radius: 50%;
63
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
64
+ color: white;
65
+ border: none;
66
+ cursor: pointer;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
71
+ transition: all 0.3s ease;
72
+ font-size: 0;
73
+ padding: 0;
74
+ }
75
+
76
+ .ai-analyzer-button:hover {
77
+ transform: scale(1.1);
78
+ box-shadow: 0 6px 16px rgba(102, 126, 234, 0.6);
79
+ }
80
+
81
+ .ai-analyzer-button:active {
82
+ transform: scale(0.95);
83
+ }
84
+
85
+ .ai-analyzer-button svg {
86
+ width: 24px;
87
+ height: 24px;
88
+ }
89
+
90
+ /* Overlay */
91
+ .ai-analyzer-overlay {
92
+ position: fixed;
93
+ top: 0;
94
+ left: 0;
95
+ width: 100%;
96
+ height: 100%;
97
+ background: rgba(0, 0, 0, 0.5);
98
+ z-index: 9997;
99
+ opacity: 0;
100
+ pointer-events: none;
101
+ transition: opacity 0.3s ease;
102
+ }
103
+
104
+ .ai-analyzer-overlay.active {
105
+ opacity: 1;
106
+ pointer-events: auto;
107
+ }
108
+
109
+ /* Barra lateral */
110
+ .ai-analyzer-sidebar {
111
+ position: fixed;
112
+ right: 0;
113
+ top: 0;
114
+ width: 400px;
115
+ height: 100vh;
116
+ background: white;
117
+ box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
118
+ z-index: 9999;
119
+ display: flex;
120
+ flex-direction: column;
121
+ transform: translateX(100%);
122
+ transition: transform 0.3s ease;
123
+ overflow: hidden;
124
+ }
125
+
126
+ .ai-analyzer-sidebar.active {
127
+ transform: translateX(0);
128
+ }
129
+
130
+ @media (max-width: 768px) {
131
+ .ai-analyzer-sidebar {
132
+ width: 100%;
133
+ }
134
+ }
135
+
136
+ /* Header da sidebar */
137
+ .ai-analyzer-header {
138
+ padding: 20px;
139
+ border-bottom: 1px solid #e5e7eb;
140
+ display: flex;
141
+ justify-content: space-between;
142
+ align-items: center;
143
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
144
+ color: white;
145
+ }
146
+
147
+ .ai-analyzer-header h3 {
148
+ margin: 0;
149
+ font-size: 18px;
150
+ font-weight: 600;
151
+ }
152
+
153
+ .ai-analyzer-close {
154
+ background: none;
155
+ border: none;
156
+ color: white;
157
+ cursor: pointer;
158
+ padding: 4px;
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ transition: opacity 0.2s ease;
163
+ }
164
+
165
+ .ai-analyzer-close:hover {
166
+ opacity: 0.8;
167
+ }
168
+
169
+ /* Conteúdo da sidebar */
170
+ .ai-analyzer-content {
171
+ flex: 1;
172
+ overflow-y: auto;
173
+ padding: 20px;
174
+ }
175
+
176
+ /* Loading spinner */
177
+ .ai-analyzer-loading {
178
+ display: flex;
179
+ flex-direction: column;
180
+ align-items: center;
181
+ justify-content: center;
182
+ height: 100%;
183
+ }
184
+
185
+ .spinner {
186
+ width: 40px;
187
+ height: 40px;
188
+ border: 4px solid #e5e7eb;
189
+ border-top-color: #667eea;
190
+ border-radius: 50%;
191
+ animation: spin 0.8s linear infinite;
192
+ }
193
+
194
+ @keyframes spin {
195
+ to { transform: rotate(360deg); }
196
+ }
197
+
198
+ .ai-analyzer-loading p {
199
+ margin-top: 16px;
200
+ color: #6b7280;
201
+ font-size: 14px;
202
+ }
203
+
204
+ /* Resultado */
205
+ .ai-analyzer-result {
206
+ color: #374151;
207
+ font-size: 14px;
208
+ line-height: 1.6;
209
+ }
210
+
211
+ .ai-analyzer-result h4 {
212
+ color: #667eea;
213
+ margin-top: 16px;
214
+ margin-bottom: 8px;
215
+ font-size: 14px;
216
+ font-weight: 600;
217
+ }
218
+
219
+ .ai-analyzer-result p {
220
+ margin: 0 0 12px 0;
221
+ }
222
+
223
+ .ai-analyzer-result ul,
224
+ .ai-analyzer-result ol {
225
+ margin: 8px 0 12px 20px;
226
+ padding: 0;
227
+ }
228
+
229
+ .ai-analyzer-result li {
230
+ margin-bottom: 6px;
231
+ }
232
+
233
+ /* Erro */
234
+ .ai-analyzer-error {
235
+ background: #fee2e2;
236
+ border: 1px solid #fecaca;
237
+ border-radius: 6px;
238
+ padding: 12px;
239
+ color: #991b1b;
240
+ font-size: 14px;
241
+ line-height: 1.5;
242
+ }
243
+
244
+ /* Scrollbar customizado */
245
+ .ai-analyzer-content::-webkit-scrollbar {
246
+ width: 6px;
247
+ }
248
+
249
+ .ai-analyzer-content::-webkit-scrollbar-track {
250
+ background: #f3f4f6;
251
+ }
252
+
253
+ .ai-analyzer-content::-webkit-scrollbar-thumb {
254
+ background: #d1d5db;
255
+ border-radius: 3px;
256
+ }
257
+
258
+ .ai-analyzer-content::-webkit-scrollbar-thumb:hover {
259
+ background: #9ca3af;
260
+ }
261
+ </style>
262
+
263
+ <script>
264
+ document.addEventListener('DOMContentLoaded', function() {
265
+ const triggerButton = document.getElementById('ai-analyzer-trigger');
266
+ const sidebar = document.getElementById('ai-analyzer-sidebar');
267
+ const closeButton = document.getElementById('ai-analyzer-close');
268
+ const overlay = document.getElementById('ai-analyzer-overlay');
269
+ const loadingEl = document.getElementById('ai-analyzer-loading');
270
+ const resultEl = document.getElementById('ai-analyzer-result');
271
+ const errorEl = document.getElementById('ai-analyzer-error');
272
+
273
+ const prompt = '<%= prompt %>';
274
+ const endpoint = '<%= endpoint_path %>';
275
+
276
+ function openSidebar() {
277
+ sidebar.classList.add('active');
278
+ overlay.classList.add('active');
279
+ }
280
+
281
+ function closeSidebar() {
282
+ sidebar.classList.remove('active');
283
+ overlay.classList.remove('active');
284
+ }
285
+
286
+ function showLoading() {
287
+ loadingEl.style.display = 'flex';
288
+ resultEl.style.display = 'none';
289
+ errorEl.style.display = 'none';
290
+ }
291
+
292
+ function showResult(html) {
293
+ loadingEl.style.display = 'none';
294
+ resultEl.style.display = 'block';
295
+ errorEl.style.display = 'none';
296
+ resultEl.innerHTML = html;
297
+ }
298
+
299
+ function showError(message) {
300
+ loadingEl.style.display = 'none';
301
+ resultEl.style.display = 'none';
302
+ errorEl.style.display = 'block';
303
+ errorEl.innerHTML = `<strong>Erro:</strong> ${message}`;
304
+ }
305
+
306
+ async function captureAndAnalyzeScreen() {
307
+ openSidebar();
308
+ showLoading();
309
+
310
+ try {
311
+ // Carregar html2canvas dinamicamente
312
+ const script = document.createElement('script');
313
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
314
+
315
+ script.onload = async function() {
316
+ try {
317
+ const canvas = await html2canvas(document.body, {
318
+ allowTaint: true,
319
+ useCORS: true,
320
+ scale: 1,
321
+ backgroundColor: '#ffffff'
322
+ });
323
+
324
+ const imageData = canvas.toDataURL('image/jpeg', 0.8);
325
+
326
+ // Enviar para backend
327
+ const response = await fetch(endpoint, {
328
+ method: 'POST',
329
+ headers: {
330
+ 'Content-Type': 'application/json',
331
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
332
+ },
333
+ body: JSON.stringify({
334
+ image: imageData,
335
+ prompt: prompt
336
+ })
337
+ });
338
+
339
+ if (!response.ok) {
340
+ throw new Error(`Erro ${response.status}: ${response.statusText}`);
341
+ }
342
+
343
+ const data = await response.json();
344
+
345
+ if (data.success) {
346
+ // Converter markdown para HTML simples
347
+ const html = data.analysis
348
+ .replace(/\n\n/g, '</p><p>')
349
+ .replace(/\n/g, '<br>')
350
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
351
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
352
+ .replace(/^- (.*?)$/gm, '<li>$1</li>')
353
+ .replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
354
+
355
+ showResult(`<p>${html}</p>`);
356
+ } else {
357
+ showError(data.error || 'Erro ao analisar a tela');
358
+ }
359
+ } catch (error) {
360
+ showError(error.message || 'Erro ao capturar a tela');
361
+ }
362
+ };
363
+
364
+ document.head.appendChild(script);
365
+ } catch (error) {
366
+ showError(error.message || 'Erro desconhecido');
367
+ }
368
+ }
369
+
370
+ triggerButton.addEventListener('click', captureAndAnalyzeScreen);
371
+ closeButton.addEventListener('click', closeSidebar);
372
+ overlay.addEventListener('click', closeSidebar);
373
+ });
374
+ </script>
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ AiScreenAnalyzer::Engine.routes.draw do
2
+ post 'analyze-screen', to: 'ai_analyzer#analyze_screen', as: :analyze_screen
3
+ end
@@ -0,0 +1,38 @@
1
+ module AiScreenAnalyzer
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace AiScreenAnalyzer
4
+
5
+ # Garantir que helpers do engine sejam autocarregados/eager carregados
6
+ config.autoload_paths << config.root.join('app/helpers')
7
+ config.eager_load_paths << config.root.join('app/helpers')
8
+ config.eager_load_namespaces << AiScreenAnalyzer
9
+
10
+ initializer "ai_screen_analyzer.helpers" do
11
+ # Certifica-se de que a constante do helper exista antes de qualquer constantize do Rails
12
+ helper_path = AiScreenAnalyzer::Engine.root.join('app/helpers/ai_screen_analyzer/ai_screen_analyzer_helper.rb').to_s
13
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(helper_path) unless ActiveSupport::Dependencies.autoload_paths.include?(File.dirname(helper_path))
14
+
15
+ ActiveSupport.on_load(:action_view) do
16
+ require_dependency helper_path
17
+ include AiScreenAnalyzer::AiScreenAnalyzerHelper
18
+ end
19
+
20
+ ActiveSupport.on_load(:action_controller) do
21
+ require_dependency helper_path
22
+ helper AiScreenAnalyzer::AiScreenAnalyzerHelper
23
+ end
24
+ end
25
+
26
+ config.to_prepare do
27
+ # Recarrega o helper em modo desenvolvimento para refletir mudanças
28
+ helper_path = AiScreenAnalyzer::Engine.root.join('app/helpers/ai_screen_analyzer/ai_screen_analyzer_helper.rb').to_s
29
+ begin
30
+ require_dependency helper_path
31
+ ActionView::Base.include AiScreenAnalyzer::AiScreenAnalyzerHelper
32
+ rescue LoadError, NameError
33
+ # Em ambientes onde o helper não é necessário, apenas ignore
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module AiScreenAnalyzer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
+ require "ai_screen_analyzer/version"
4
+ require "ai_screen_analyzer/engine" if defined?(Rails)
5
+
6
+ module AiScreenAnalyzer
7
+ class Error < StandardError; end
8
+
9
+ # Configuração padrão
10
+ mattr_accessor :api_key
11
+ mattr_accessor :model
12
+ mattr_accessor :default_prompt
13
+ mattr_accessor :default_button_text
14
+ mattr_accessor :default_sidebar_title
15
+
16
+ self.model = "gpt-4-turbo"
17
+ self.default_prompt = "Analise o conteúdo visível nesta tela e descreva: 1) O que você vê, 2) Elementos principais, 3) Possíveis melhorias de UX/UI"
18
+ self.default_button_text = "Analisar com IA"
19
+ self.default_sidebar_title = "Análise da IA"
20
+
21
+ def self.configure
22
+ yield self if block_given?
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ module AiScreenAnalyzer
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ desc "Instala o AI Screen Analyzer e cria um arquivo de configuração"
7
+
8
+ def create_initializer
9
+ create_file "config/initializers/ai_screen_analyzer.rb", <<~RUBY
10
+ # AI Screen Analyzer Configuration
11
+ # Configure a gem AI Screen Analyzer aqui
12
+
13
+ AiScreenAnalyzer.configure do |config|
14
+ # Defina sua chave de API da OpenAI
15
+ # config.api_key = ENV['OPENAI_API_KEY']
16
+
17
+ # Modelo de IA a ser usado (padrão: gpt-4-turbo)
18
+ # config.model = 'gpt-4-turbo'
19
+
20
+ # Prompt padrão para análise
21
+ # config.default_prompt = "Analise o conteúdo visível nesta tela..."
22
+
23
+ # Texto do botão
24
+ # config.default_button_text = "Analisar com IA"
25
+
26
+ # Título da sidebar
27
+ # config.default_sidebar_title = "Análise da IA"
28
+ end
29
+ RUBY
30
+ end
31
+
32
+ def mount_engine_routes
33
+ routes_path = "config/routes.rb"
34
+
35
+ if File.exist?(routes_path)
36
+ content = File.read(routes_path)
37
+ already_mounted = content.include?("AiScreenAnalyzer::Engine")
38
+
39
+ unless already_mounted
40
+ say_status :route, "Montando AiScreenAnalyzer::Engine em /ai_screen_analyzer", :green
41
+ route "mount AiScreenAnalyzer::Engine => '/ai_screen_analyzer', as: 'ai_screen_analyzer_engine'"
42
+ else
43
+ say_status :route, "AiScreenAnalyzer::Engine já está montado (nenhuma alteração)", :blue
44
+ end
45
+ else
46
+ say_status :warning, "Arquivo config/routes.rb não encontrado. Adicione manualmente: mount AiScreenAnalyzer::Engine => '/ai_screen_analyzer', as: 'ai_screen_analyzer_engine'", :yellow
47
+ end
48
+ end
49
+
50
+ def display_readme
51
+ puts <<~TEXT
52
+
53
+ ╔════════════════════════════════════════════════════════════════╗
54
+ ║ AI Screen Analyzer instalado com sucesso! ║
55
+ ╚════════════════════════════════════════════════════════════════╝
56
+
57
+ Próximos passos:
58
+
59
+ 1. Rotas montadas automaticamente:
60
+ - O engine foi montado em /ai_screen_analyzer
61
+ - Endpoint de análise: POST /ai_screen_analyzer/analyze-screen
62
+ - Se desejar mudar o path de montagem, ajuste em config/routes.rb e veja a seção "Rotas" do README.
63
+
64
+ 2. Configure sua chave de API da OpenAI:
65
+ - Abra config/initializers/ai_screen_analyzer.rb
66
+ - Descomente e configure as opções desejadas
67
+
68
+ 3. Adicione o helper em seu layout principal:
69
+ <%= ai_screen_analyzer %>
70
+
71
+ 4. Certifique-se de que a variável de ambiente está definida:
72
+ export OPENAI_API_KEY='sua_chave_aqui'
73
+
74
+ Para mais informações, visite:
75
+ https://github.com/seu-usuario/ai_screen_analyzer
76
+
77
+ TEXT
78
+ end
79
+ end
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ai_screen_analyzer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - José Lopes Neto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ description: AI Screen Analyzer é uma gem Rails que adiciona um botão flutuante em
84
+ qualquer página para capturar a tela, enviar para análise com GPT-4V e exibir o
85
+ resultado em uma barra lateral elegante.
86
+ email:
87
+ - jlneto@mcfox.com.br
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - LICENSE.txt
94
+ - README.md
95
+ - app/controllers/ai_screen_analyzer/ai_analyzer_controller.rb
96
+ - app/helpers/ai_screen_analyzer/ai_screen_analyzer_helper.rb
97
+ - app/views/ai_screen_analyzer/shared/_ai_screen_analyzer.html.erb
98
+ - config/routes.rb
99
+ - lib/ai_screen_analyzer.rb
100
+ - lib/ai_screen_analyzer/engine.rb
101
+ - lib/ai_screen_analyzer/version.rb
102
+ - lib/generators/ai_screen_analyzer/install_generator.rb
103
+ homepage: https://github.com/mcfox/ai_screen_analyzer
104
+ licenses:
105
+ - MIT
106
+ metadata:
107
+ allowed_push_host: https://rubygems.org
108
+ homepage_uri: https://github.com/mcfox/ai_screen_analyzer
109
+ source_code_uri: https://github.com/mcfox/ai_screen_analyzer
110
+ bug_tracker_uri: https://github.com/mcfox/ai_screen_analyzer/issues
111
+ changelog_uri: https://github.com/mcfox/ai_screen_analyzer/releases
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 2.7.0
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.5.9
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Rails helper gem para analisar telas com IA usando GPT-4V
131
+ test_files: []