deepagents_rails 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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +309 -0
  4. data/lib/deepagents_rails/service.rb +93 -0
  5. data/lib/deepagents_rails/version.rb +3 -0
  6. data/lib/deepagents_rails.rb +31 -0
  7. data/lib/generators/deepagents/agent/agent_generator.rb +55 -0
  8. data/lib/generators/deepagents/agent/templates/agent.rb +99 -0
  9. data/lib/generators/deepagents/agent/templates/agent_spec.rb +33 -0
  10. data/lib/generators/deepagents/agent/templates/controller.rb +44 -0
  11. data/lib/generators/deepagents/agent/templates/index.html.erb +178 -0
  12. data/lib/generators/deepagents/agent/templates/show.html.erb +131 -0
  13. data/lib/generators/deepagents/controller/controller_generator.rb +51 -0
  14. data/lib/generators/deepagents/controller/templates/api_controller.rb +87 -0
  15. data/lib/generators/deepagents/controller/templates/serializer.rb +22 -0
  16. data/lib/generators/deepagents/install/install_generator.rb +41 -0
  17. data/lib/generators/deepagents/install/templates/deepagents.yml +38 -0
  18. data/lib/generators/deepagents/install/templates/initializer.rb +35 -0
  19. data/lib/generators/deepagents/model/model_generator.rb +43 -0
  20. data/lib/generators/deepagents/model/templates/conversation.rb +73 -0
  21. data/lib/generators/deepagents/model/templates/create_conversations_migration.rb +16 -0
  22. data/lib/generators/deepagents/model/templates/create_files_migration.rb +17 -0
  23. data/lib/generators/deepagents/model/templates/create_messages_migration.rb +17 -0
  24. data/lib/generators/deepagents/model/templates/file.rb +96 -0
  25. data/lib/generators/deepagents/model/templates/message.rb +30 -0
  26. data/lib/generators/deepagents/tool/templates/tool.rb +27 -0
  27. data/lib/generators/deepagents/tool/templates/tool_spec.rb +36 -0
  28. data/lib/generators/deepagents/tool/tool_generator.rb +58 -0
  29. data/lib/generators/deepagents/view/templates/_conversation.html.erb +10 -0
  30. data/lib/generators/deepagents/view/templates/_form.html.erb +33 -0
  31. data/lib/generators/deepagents/view/templates/_message.html.erb +31 -0
  32. data/lib/generators/deepagents/view/templates/index.html.erb +35 -0
  33. data/lib/generators/deepagents/view/templates/javascript.js +199 -0
  34. data/lib/generators/deepagents/view/templates/new.html.erb +10 -0
  35. data/lib/generators/deepagents/view/templates/show.html.erb +54 -0
  36. data/lib/generators/deepagents/view/templates/stylesheet.css +397 -0
  37. data/lib/generators/deepagents/view/view_generator.rb +50 -0
  38. metadata +121 -0
@@ -0,0 +1,199 @@
1
+ // DeepAgents <%= class_name %> Chat JavaScript
2
+
3
+ document.addEventListener('DOMContentLoaded', function() {
4
+ // Elements
5
+ const messagesContainer = document.getElementById('messages-container');
6
+ const messageForm = document.getElementById('message-form');
7
+ const messageInput = document.getElementById('message-input');
8
+ const fileUpload = document.getElementById('file-upload');
9
+ const filePreview = document.getElementById('file-preview');
10
+
11
+ // Auto-resize textarea
12
+ if (messageInput) {
13
+ messageInput.addEventListener('input', function() {
14
+ this.style.height = 'auto';
15
+ this.style.height = (this.scrollHeight) + 'px';
16
+ });
17
+ }
18
+
19
+ // File upload preview
20
+ if (fileUpload) {
21
+ fileUpload.addEventListener('change', function(e) {
22
+ const file = e.target.files[0];
23
+ if (!file) {
24
+ filePreview.innerHTML = '';
25
+ return;
26
+ }
27
+
28
+ const reader = new FileReader();
29
+
30
+ reader.onload = function(e) {
31
+ filePreview.innerHTML = '';
32
+
33
+ const fileElement = document.createElement('div');
34
+ fileElement.className = 'deepagents-file-item';
35
+
36
+ // Check if it's an image
37
+ if (file.type.startsWith('image/')) {
38
+ const img = document.createElement('img');
39
+ img.src = e.target.result;
40
+ img.className = 'deepagents-file-thumbnail';
41
+ fileElement.appendChild(img);
42
+ }
43
+
44
+ const fileInfo = document.createElement('div');
45
+ fileInfo.className = 'deepagents-file-info';
46
+ fileInfo.textContent = file.name;
47
+
48
+ const removeButton = document.createElement('button');
49
+ removeButton.type = 'button';
50
+ removeButton.className = 'deepagents-file-remove';
51
+ removeButton.innerHTML = '&times;';
52
+ removeButton.addEventListener('click', function() {
53
+ fileUpload.value = '';
54
+ filePreview.innerHTML = '';
55
+ });
56
+
57
+ fileElement.appendChild(fileInfo);
58
+ fileElement.appendChild(removeButton);
59
+ filePreview.appendChild(fileElement);
60
+ };
61
+
62
+ reader.readAsDataURL(file);
63
+ });
64
+ }
65
+
66
+ // Form submission
67
+ if (messageForm) {
68
+ messageForm.addEventListener('submit', function(e) {
69
+ e.preventDefault();
70
+
71
+ const formData = new FormData(this);
72
+ const conversationId = document.querySelector('.deepagents-container').dataset.conversationId;
73
+
74
+ // Check if there's a file to upload first
75
+ if (fileUpload && fileUpload.files.length > 0) {
76
+ const fileFormData = new FormData();
77
+ fileFormData.append('file', fileUpload.files[0]);
78
+
79
+ fetch(`/conversations/${conversationId}/upload`, {
80
+ method: 'POST',
81
+ body: fileFormData,
82
+ headers: {
83
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
84
+ }
85
+ })
86
+ .then(response => response.json())
87
+ .then(data => {
88
+ // Clear file upload
89
+ fileUpload.value = '';
90
+ filePreview.innerHTML = '';
91
+
92
+ // Now send the message
93
+ sendMessage(formData, conversationId);
94
+ })
95
+ .catch(error => {
96
+ console.error('Error uploading file:', error);
97
+ });
98
+ } else {
99
+ // Just send the message
100
+ sendMessage(formData, conversationId);
101
+ }
102
+ });
103
+ }
104
+
105
+ function sendMessage(formData, conversationId) {
106
+ // Add a loading message
107
+ const loadingMessage = document.createElement('div');
108
+ loadingMessage.className = 'deepagents-message assistant loading';
109
+ loadingMessage.innerHTML = `
110
+ <div class="deepagents-message-avatar">
111
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
112
+ <path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5ZM3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.58 26.58 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.933.933 0 0 1-.765.935c-.845.147-2.34.346-4.235.346-1.895 0-3.39-.2-4.235-.346A.933.933 0 0 1 3 9.219V8.062Zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a24.767 24.767 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25.286 25.286 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135Z"/>
113
+ <path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2V1.866ZM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5Z"/>
114
+ </svg>
115
+ </div>
116
+ <div class="deepagents-message-content">
117
+ <div class="deepagents-message-header">
118
+ <span class="deepagents-message-role">Assistant</span>
119
+ </div>
120
+ <div class="deepagents-message-body">
121
+ <p>Thinking...</p>
122
+ </div>
123
+ </div>
124
+ `;
125
+ messagesContainer.appendChild(loadingMessage);
126
+
127
+ // Scroll to bottom
128
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
129
+
130
+ // Clear input
131
+ messageInput.value = '';
132
+ messageInput.style.height = 'auto';
133
+
134
+ // Send the message
135
+ fetch(`/conversations/${conversationId}/run`, {
136
+ method: 'POST',
137
+ body: formData,
138
+ headers: {
139
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
140
+ }
141
+ })
142
+ .then(response => response.json())
143
+ .then(data => {
144
+ // Remove loading message
145
+ messagesContainer.removeChild(loadingMessage);
146
+
147
+ // Refresh the conversation
148
+ fetch(`/conversations/${conversationId}`, {
149
+ headers: {
150
+ 'Accept': 'text/html',
151
+ 'X-Requested-With': 'XMLHttpRequest'
152
+ }
153
+ })
154
+ .then(response => response.text())
155
+ .then(html => {
156
+ const parser = new DOMParser();
157
+ const doc = parser.parseFromString(html, 'text/html');
158
+ const newMessages = doc.querySelector('.deepagents-messages');
159
+
160
+ if (newMessages) {
161
+ messagesContainer.innerHTML = newMessages.innerHTML;
162
+ }
163
+
164
+ // Scroll to bottom
165
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
166
+ });
167
+ })
168
+ .catch(error => {
169
+ console.error('Error sending message:', error);
170
+
171
+ // Remove loading message
172
+ messagesContainer.removeChild(loadingMessage);
173
+
174
+ // Show error message
175
+ const errorMessage = document.createElement('div');
176
+ errorMessage.className = 'deepagents-message system error';
177
+ errorMessage.innerHTML = `
178
+ <div class="deepagents-message-avatar">
179
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 16 16">
180
+ <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
181
+ <path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
182
+ </svg>
183
+ </div>
184
+ <div class="deepagents-message-content">
185
+ <div class="deepagents-message-header">
186
+ <span class="deepagents-message-role">System</span>
187
+ </div>
188
+ <div class="deepagents-message-body">
189
+ <p>Error: Could not send message. Please try again.</p>
190
+ </div>
191
+ </div>
192
+ `;
193
+ messagesContainer.appendChild(errorMessage);
194
+
195
+ // Scroll to bottom
196
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
197
+ });
198
+ }
199
+ });
@@ -0,0 +1,10 @@
1
+ <%# DeepAgents %> <%= class_name %> <%# New View %>
2
+ <div class="deepagents-container">
3
+ <h1>New Conversation</h1>
4
+
5
+ <div class="deepagents-actions">
6
+ <%= link_to "Back to Conversations", #{file_name.pluralize}_path, class: "deepagents-button secondary" %>
7
+ </div>
8
+
9
+ <%= render "form", #{file_name}: @#{file_name} %>
10
+ </div>
@@ -0,0 +1,54 @@
1
+ <%# DeepAgents %> <%= class_name %> <%# Show View %>
2
+ <div class="deepagents-container" data-conversation-id="<%= @conversation.id %>">
3
+ <div class="deepagents-header">
4
+ <h1><%= @conversation.title.presence || "Conversation ##{@conversation.id}" %></h1>
5
+ <div class="deepagents-actions">
6
+ <%= link_to "Back to Conversations", #{file_name.pluralize}_path, class: "deepagents-button secondary" %>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="deepagents-conversation">
11
+ <div class="deepagents-messages" id="messages-container">
12
+ <%= render partial: "conversation", locals: { conversation: @conversation } %>
13
+ </div>
14
+
15
+ <div class="deepagents-input-container">
16
+ <%= form_with url: run_#{file_name}_path(@conversation), method: :post, class: "deepagents-form", id: "message-form", data: { remote: true } do |f| %>
17
+ <div class="deepagents-file-upload">
18
+ <%= f.file_field :file, id: "file-upload", class: "deepagents-file-input" %>
19
+ <label for="file-upload" class="deepagents-file-label">
20
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
21
+ <path d="M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0V3z"/>
22
+ </svg>
23
+ </label>
24
+ </div>
25
+
26
+ <%= f.text_area :input, id: "message-input", class: "deepagents-input", placeholder: "Type your message...", rows: 1, required: true %>
27
+
28
+ <%= f.submit "Send", class: "deepagents-send-button" %>
29
+ <% end %>
30
+
31
+ <div id="file-preview" class="deepagents-file-preview"></div>
32
+ </div>
33
+ </div>
34
+
35
+ <% if @conversation.files.any? %>
36
+ <div class="deepagents-files">
37
+ <h3>Files</h3>
38
+ <div class="deepagents-files-list">
39
+ <% @conversation.files.each do |file| %>
40
+ <div class="deepagents-file">
41
+ <% if file.image? %>
42
+ <img src="<%= file.url %>" alt="<%= file.filename %>" class="deepagents-file-preview">
43
+ <% end %>
44
+ <div class="deepagents-file-info">
45
+ <span class="deepagents-file-name"><%= file.filename %></span>
46
+ <span class="deepagents-file-timestamp"><%= time_ago_in_words(file.created_at) %> ago</span>
47
+ </div>
48
+ <%= link_to "Download", file.url, class: "deepagents-file-download", download: file.filename %>
49
+ </div>
50
+ <% end %>
51
+ </div>
52
+ </div>
53
+ <% end %>
54
+ </div>
@@ -0,0 +1,397 @@
1
+ /* DeepAgents <%= class_name %> Chat Stylesheet */
2
+
3
+ .deepagents-container {
4
+ max-width: 1000px;
5
+ margin: 0 auto;
6
+ padding: 20px;
7
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
8
+ }
9
+
10
+ .deepagents-header {
11
+ display: flex;
12
+ justify-content: space-between;
13
+ align-items: center;
14
+ margin-bottom: 20px;
15
+ }
16
+
17
+ .deepagents-actions {
18
+ margin-bottom: 20px;
19
+ }
20
+
21
+ .deepagents-button {
22
+ display: inline-block;
23
+ padding: 8px 16px;
24
+ background-color: #4a6cf7;
25
+ color: white;
26
+ border-radius: 4px;
27
+ text-decoration: none;
28
+ font-weight: 500;
29
+ border: none;
30
+ cursor: pointer;
31
+ font-size: 14px;
32
+ }
33
+
34
+ .deepagents-button:hover {
35
+ background-color: #3a5ce5;
36
+ }
37
+
38
+ .deepagents-button.secondary {
39
+ background-color: #e2e8f0;
40
+ color: #334155;
41
+ }
42
+
43
+ .deepagents-button.secondary:hover {
44
+ background-color: #cbd5e1;
45
+ }
46
+
47
+ .deepagents-conversations-list {
48
+ display: grid;
49
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
50
+ gap: 20px;
51
+ }
52
+
53
+ .deepagents-conversation-card {
54
+ border: 1px solid #e2e8f0;
55
+ border-radius: 8px;
56
+ padding: 16px;
57
+ transition: box-shadow 0.2s ease;
58
+ }
59
+
60
+ .deepagents-conversation-card:hover {
61
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
62
+ }
63
+
64
+ .deepagents-conversation-meta {
65
+ display: flex;
66
+ justify-content: space-between;
67
+ color: #64748b;
68
+ font-size: 12px;
69
+ margin-bottom: 8px;
70
+ }
71
+
72
+ .deepagents-conversation-preview {
73
+ color: #64748b;
74
+ font-size: 14px;
75
+ }
76
+
77
+ .deepagents-conversation {
78
+ display: flex;
79
+ flex-direction: column;
80
+ height: 70vh;
81
+ border: 1px solid #e2e8f0;
82
+ border-radius: 8px;
83
+ overflow: hidden;
84
+ }
85
+
86
+ .deepagents-messages {
87
+ flex-grow: 1;
88
+ overflow-y: auto;
89
+ padding: 16px;
90
+ }
91
+
92
+ .deepagents-message {
93
+ display: flex;
94
+ margin-bottom: 16px;
95
+ }
96
+
97
+ .deepagents-message-avatar {
98
+ width: 40px;
99
+ height: 40px;
100
+ border-radius: 50%;
101
+ background-color: #e2e8f0;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ margin-right: 12px;
106
+ flex-shrink: 0;
107
+ }
108
+
109
+ .deepagents-message.user .deepagents-message-avatar {
110
+ background-color: #4a6cf7;
111
+ color: white;
112
+ }
113
+
114
+ .deepagents-message.assistant .deepagents-message-avatar {
115
+ background-color: #10b981;
116
+ color: white;
117
+ }
118
+
119
+ .deepagents-message.system .deepagents-message-avatar {
120
+ background-color: #f59e0b;
121
+ color: white;
122
+ }
123
+
124
+ .deepagents-message.error .deepagents-message-avatar {
125
+ background-color: #ef4444;
126
+ color: white;
127
+ }
128
+
129
+ .deepagents-message-content {
130
+ flex-grow: 1;
131
+ }
132
+
133
+ .deepagents-message-header {
134
+ display: flex;
135
+ justify-content: space-between;
136
+ margin-bottom: 4px;
137
+ }
138
+
139
+ .deepagents-message-role {
140
+ font-weight: 600;
141
+ font-size: 14px;
142
+ }
143
+
144
+ .deepagents-message-timestamp {
145
+ color: #64748b;
146
+ font-size: 12px;
147
+ }
148
+
149
+ .deepagents-message-body {
150
+ background-color: #f8fafc;
151
+ border-radius: 8px;
152
+ padding: 12px;
153
+ }
154
+
155
+ .deepagents-message.user .deepagents-message-body {
156
+ background-color: #eff6ff;
157
+ }
158
+
159
+ .deepagents-message.assistant .deepagents-message-body {
160
+ background-color: #f0fdf4;
161
+ }
162
+
163
+ .deepagents-message.system .deepagents-message-body {
164
+ background-color: #fef3c7;
165
+ }
166
+
167
+ .deepagents-message.error .deepagents-message-body {
168
+ background-color: #fee2e2;
169
+ }
170
+
171
+ .deepagents-input-container {
172
+ border-top: 1px solid #e2e8f0;
173
+ padding: 16px;
174
+ background-color: white;
175
+ }
176
+
177
+ .deepagents-form {
178
+ display: flex;
179
+ align-items: flex-end;
180
+ }
181
+
182
+ .deepagents-input {
183
+ flex-grow: 1;
184
+ border: 1px solid #e2e8f0;
185
+ border-radius: 4px;
186
+ padding: 8px 12px;
187
+ font-size: 14px;
188
+ resize: none;
189
+ max-height: 150px;
190
+ margin-right: 8px;
191
+ }
192
+
193
+ .deepagents-input:focus {
194
+ outline: none;
195
+ border-color: #4a6cf7;
196
+ }
197
+
198
+ .deepagents-send-button {
199
+ padding: 8px 16px;
200
+ background-color: #4a6cf7;
201
+ color: white;
202
+ border: none;
203
+ border-radius: 4px;
204
+ cursor: pointer;
205
+ font-weight: 500;
206
+ }
207
+
208
+ .deepagents-send-button:hover {
209
+ background-color: #3a5ce5;
210
+ }
211
+
212
+ .deepagents-file-upload {
213
+ margin-right: 8px;
214
+ }
215
+
216
+ .deepagents-file-input {
217
+ display: none;
218
+ }
219
+
220
+ .deepagents-file-label {
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ width: 36px;
225
+ height: 36px;
226
+ background-color: #e2e8f0;
227
+ border-radius: 4px;
228
+ cursor: pointer;
229
+ }
230
+
231
+ .deepagents-file-label:hover {
232
+ background-color: #cbd5e1;
233
+ }
234
+
235
+ .deepagents-file-preview {
236
+ margin-top: 8px;
237
+ }
238
+
239
+ .deepagents-file-item {
240
+ display: flex;
241
+ align-items: center;
242
+ background-color: #f8fafc;
243
+ border-radius: 4px;
244
+ padding: 8px;
245
+ margin-top: 8px;
246
+ }
247
+
248
+ .deepagents-file-thumbnail {
249
+ width: 40px;
250
+ height: 40px;
251
+ object-fit: cover;
252
+ border-radius: 4px;
253
+ margin-right: 8px;
254
+ }
255
+
256
+ .deepagents-file-info {
257
+ flex-grow: 1;
258
+ font-size: 14px;
259
+ }
260
+
261
+ .deepagents-file-remove {
262
+ background: none;
263
+ border: none;
264
+ color: #64748b;
265
+ cursor: pointer;
266
+ font-size: 18px;
267
+ padding: 0 8px;
268
+ }
269
+
270
+ .deepagents-file-remove:hover {
271
+ color: #ef4444;
272
+ }
273
+
274
+ .deepagents-files {
275
+ margin-top: 20px;
276
+ }
277
+
278
+ .deepagents-files-list {
279
+ display: grid;
280
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
281
+ gap: 16px;
282
+ margin-top: 12px;
283
+ }
284
+
285
+ .deepagents-file {
286
+ border: 1px solid #e2e8f0;
287
+ border-radius: 8px;
288
+ overflow: hidden;
289
+ }
290
+
291
+ .deepagents-file-preview {
292
+ width: 100%;
293
+ height: 120px;
294
+ object-fit: cover;
295
+ }
296
+
297
+ .deepagents-file-name {
298
+ display: block;
299
+ font-weight: 500;
300
+ margin-bottom: 4px;
301
+ }
302
+
303
+ .deepagents-file-timestamp {
304
+ color: #64748b;
305
+ font-size: 12px;
306
+ }
307
+
308
+ .deepagents-file-download {
309
+ display: block;
310
+ text-align: center;
311
+ padding: 8px;
312
+ background-color: #4a6cf7;
313
+ color: white;
314
+ text-decoration: none;
315
+ font-size: 14px;
316
+ margin-top: 8px;
317
+ }
318
+
319
+ .deepagents-file-download:hover {
320
+ background-color: #3a5ce5;
321
+ }
322
+
323
+ .deepagents-empty-state {
324
+ text-align: center;
325
+ padding: 40px 0;
326
+ color: #64748b;
327
+ }
328
+
329
+ .deepagents-form-group {
330
+ margin-bottom: 16px;
331
+ }
332
+
333
+ .deepagents-form-group label {
334
+ display: block;
335
+ margin-bottom: 8px;
336
+ font-weight: 500;
337
+ }
338
+
339
+ .deepagents-form-control {
340
+ width: 100%;
341
+ padding: 8px 12px;
342
+ border: 1px solid #e2e8f0;
343
+ border-radius: 4px;
344
+ font-size: 14px;
345
+ }
346
+
347
+ .deepagents-form-control:focus {
348
+ outline: none;
349
+ border-color: #4a6cf7;
350
+ }
351
+
352
+ .deepagents-form-text {
353
+ display: block;
354
+ margin-top: 4px;
355
+ color: #64748b;
356
+ font-size: 12px;
357
+ }
358
+
359
+ .deepagents-form-actions {
360
+ margin-top: 24px;
361
+ }
362
+
363
+ .deepagents-error-messages {
364
+ color: #ef4444;
365
+ background-color: #fee2e2;
366
+ border: 1px solid #fecaca;
367
+ border-radius: 4px;
368
+ padding: 12px;
369
+ margin-bottom: 16px;
370
+ }
371
+
372
+ .deepagents-message.loading .deepagents-message-body {
373
+ position: relative;
374
+ }
375
+
376
+ .deepagents-message.loading .deepagents-message-body p {
377
+ display: flex;
378
+ align-items: center;
379
+ }
380
+
381
+ .deepagents-message.loading .deepagents-message-body p:after {
382
+ content: "...";
383
+ overflow: hidden;
384
+ display: inline-block;
385
+ vertical-align: bottom;
386
+ animation: ellipsis 2s infinite;
387
+ width: 0;
388
+ }
389
+
390
+ @keyframes ellipsis {
391
+ from {
392
+ width: 0;
393
+ }
394
+ to {
395
+ width: 1.25em;
396
+ }
397
+ }
@@ -0,0 +1,50 @@
1
+ module Deepagents
2
+ module Generators
3
+ class ViewGenerator < Rails::Generators::NamedBase
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ desc "Creates DeepAgents UI views for your Rails application"
7
+
8
+ class_option :skip_javascript, type: :boolean, default: false, desc: "Skip JavaScript file generation"
9
+ class_option :skip_stylesheet, type: :boolean, default: false, desc: "Skip stylesheet file generation"
10
+
11
+ def create_views
12
+ template "index.html.erb", "app/views/#{file_name.pluralize}/index.html.erb"
13
+ template "show.html.erb", "app/views/#{file_name.pluralize}/show.html.erb"
14
+ template "new.html.erb", "app/views/#{file_name.pluralize}/new.html.erb"
15
+ template "_conversation.html.erb", "app/views/#{file_name.pluralize}/_conversation.html.erb"
16
+ template "_message.html.erb", "app/views/#{file_name.pluralize}/_message.html.erb"
17
+ template "_form.html.erb", "app/views/#{file_name.pluralize}/_form.html.erb"
18
+ end
19
+
20
+ def create_javascript
21
+ return if options[:skip_javascript]
22
+
23
+ template "javascript.js", "app/javascript/#{file_name}_chat.js"
24
+
25
+ append_to_file "app/javascript/packs/application.js", <<~JS
26
+
27
+ // DeepAgents #{class_name} Chat
28
+ import "../#{file_name}_chat"
29
+ JS
30
+ end
31
+
32
+ def create_stylesheet
33
+ return if options[:skip_stylesheet]
34
+
35
+ template "stylesheet.css", "app/assets/stylesheets/#{file_name}_chat.css"
36
+ end
37
+
38
+ def display_next_steps
39
+ say "\n"
40
+ say "DeepAgents UI views for #{file_name} have been created! 🎨", :green
41
+ say "\n"
42
+ say "Next steps:", :yellow
43
+ say " 1. Ensure you have the required controller (use the controller generator if needed)"
44
+ say " 2. Add the JavaScript to your application.js if using Webpacker"
45
+ say " 3. Customize the views to match your application's design"
46
+ say "\n"
47
+ end
48
+ end
49
+ end
50
+ end