rubyn 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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +251 -0
  4. data/Rakefile +12 -0
  5. data/exe/rubyn +5 -0
  6. data/lib/generators/rubyn/install_generator.rb +16 -0
  7. data/lib/rubyn/cli.rb +85 -0
  8. data/lib/rubyn/client/api_client.rb +172 -0
  9. data/lib/rubyn/commands/agent.rb +191 -0
  10. data/lib/rubyn/commands/base.rb +60 -0
  11. data/lib/rubyn/commands/config.rb +51 -0
  12. data/lib/rubyn/commands/dashboard.rb +85 -0
  13. data/lib/rubyn/commands/index.rb +101 -0
  14. data/lib/rubyn/commands/init.rb +166 -0
  15. data/lib/rubyn/commands/refactor.rb +175 -0
  16. data/lib/rubyn/commands/review.rb +61 -0
  17. data/lib/rubyn/commands/spec.rb +72 -0
  18. data/lib/rubyn/commands/usage.rb +56 -0
  19. data/lib/rubyn/config/credentials.rb +39 -0
  20. data/lib/rubyn/config/project_config.rb +42 -0
  21. data/lib/rubyn/config/settings.rb +53 -0
  22. data/lib/rubyn/context/codebase_indexer.rb +195 -0
  23. data/lib/rubyn/context/context_builder.rb +36 -0
  24. data/lib/rubyn/context/file_resolver.rb +235 -0
  25. data/lib/rubyn/context/project_scanner.rb +132 -0
  26. data/lib/rubyn/engine/app/assets/images/rubyn/RubynLogo.png +0 -0
  27. data/lib/rubyn/engine/app/assets/javascripts/rubyn/application.js +579 -0
  28. data/lib/rubyn/engine/app/assets/stylesheets/rubyn/application.css +1073 -0
  29. data/lib/rubyn/engine/app/controllers/rubyn/agent_controller.rb +26 -0
  30. data/lib/rubyn/engine/app/controllers/rubyn/application_controller.rb +44 -0
  31. data/lib/rubyn/engine/app/controllers/rubyn/dashboard_controller.rb +19 -0
  32. data/lib/rubyn/engine/app/controllers/rubyn/feedback_controller.rb +17 -0
  33. data/lib/rubyn/engine/app/controllers/rubyn/files_controller.rb +54 -0
  34. data/lib/rubyn/engine/app/controllers/rubyn/refactor_controller.rb +56 -0
  35. data/lib/rubyn/engine/app/controllers/rubyn/reviews_controller.rb +32 -0
  36. data/lib/rubyn/engine/app/controllers/rubyn/settings_controller.rb +17 -0
  37. data/lib/rubyn/engine/app/controllers/rubyn/specs_controller.rb +33 -0
  38. data/lib/rubyn/engine/app/views/layouts/rubyn/application.html.erb +63 -0
  39. data/lib/rubyn/engine/app/views/rubyn/agent/show.html.erb +22 -0
  40. data/lib/rubyn/engine/app/views/rubyn/dashboard/index.html.erb +120 -0
  41. data/lib/rubyn/engine/app/views/rubyn/files/index.html.erb +45 -0
  42. data/lib/rubyn/engine/app/views/rubyn/refactor/show.html.erb +28 -0
  43. data/lib/rubyn/engine/app/views/rubyn/reviews/show.html.erb +28 -0
  44. data/lib/rubyn/engine/app/views/rubyn/settings/show.html.erb +42 -0
  45. data/lib/rubyn/engine/app/views/rubyn/specs/show.html.erb +28 -0
  46. data/lib/rubyn/engine/config/routes.rb +13 -0
  47. data/lib/rubyn/engine/engine.rb +18 -0
  48. data/lib/rubyn/output/diff_renderer.rb +106 -0
  49. data/lib/rubyn/output/formatter.rb +123 -0
  50. data/lib/rubyn/output/spinner.rb +26 -0
  51. data/lib/rubyn/tools/base_tool.rb +74 -0
  52. data/lib/rubyn/tools/bundle_add.rb +77 -0
  53. data/lib/rubyn/tools/create_file.rb +32 -0
  54. data/lib/rubyn/tools/delete_file.rb +29 -0
  55. data/lib/rubyn/tools/executor.rb +68 -0
  56. data/lib/rubyn/tools/find_files.rb +33 -0
  57. data/lib/rubyn/tools/find_references.rb +72 -0
  58. data/lib/rubyn/tools/git_commit.rb +65 -0
  59. data/lib/rubyn/tools/git_create_branch.rb +58 -0
  60. data/lib/rubyn/tools/git_diff.rb +42 -0
  61. data/lib/rubyn/tools/git_log.rb +43 -0
  62. data/lib/rubyn/tools/git_status.rb +26 -0
  63. data/lib/rubyn/tools/list_directory.rb +82 -0
  64. data/lib/rubyn/tools/move_file.rb +35 -0
  65. data/lib/rubyn/tools/patch_file.rb +47 -0
  66. data/lib/rubyn/tools/rails_generate.rb +40 -0
  67. data/lib/rubyn/tools/rails_migrate.rb +55 -0
  68. data/lib/rubyn/tools/rails_routes.rb +35 -0
  69. data/lib/rubyn/tools/read_file.rb +45 -0
  70. data/lib/rubyn/tools/registry.rb +28 -0
  71. data/lib/rubyn/tools/run_command.rb +48 -0
  72. data/lib/rubyn/tools/run_tests.rb +52 -0
  73. data/lib/rubyn/tools/search_files.rb +82 -0
  74. data/lib/rubyn/tools/write_file.rb +30 -0
  75. data/lib/rubyn/version.rb +5 -0
  76. data/lib/rubyn/version_checker.rb +74 -0
  77. data/lib/rubyn.rb +95 -0
  78. data/sig/rubyn.rbs +4 -0
  79. metadata +379 -0
@@ -0,0 +1,1073 @@
1
+ /* Rubyn Dashboard Styles */
2
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
3
+
4
+ :root {
5
+ /* Matches admin palette: warm dark reds, ruby accents, cream text */
6
+ --rubyn-bg: #1A1016;
7
+ --rubyn-surface: #2D1B24;
8
+ --rubyn-surface-hover: hsl(330, 25%, 14%);
9
+ --rubyn-surface-raised: #3D2B34;
10
+ --rubyn-primary: #CC342D;
11
+ --rubyn-primary-soft: rgba(204, 52, 45, 0.12);
12
+ --rubyn-primary-glow: rgba(204, 52, 45, 0.3);
13
+ --rubyn-text: #FFF5F0;
14
+ --rubyn-text-dim: #A89090;
15
+ --rubyn-text-muted: #6B5A5A;
16
+ --rubyn-success: hsl(142, 71%, 45%);
17
+ --rubyn-success-soft: hsla(142, 71%, 45%, 0.12);
18
+ --rubyn-warning: hsl(38, 92%, 50%);
19
+ --rubyn-warning-soft: hsla(38, 92%, 50%, 0.12);
20
+ --rubyn-border: hsl(330, 15%, 20%);
21
+ --rubyn-border-subtle: hsl(330, 15%, 16%);
22
+ --rubyn-radius: 0.75rem;
23
+ --rubyn-radius-sm: 0.5rem;
24
+ --rubyn-radius-xs: 0.375rem;
25
+ --rubyn-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
26
+ --rubyn-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3);
27
+ --rubyn-transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
28
+ }
29
+
30
+ *, *::before, *::after {
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ body {
35
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
36
+ background: var(--rubyn-bg);
37
+ color: var(--rubyn-text);
38
+ margin: 0;
39
+ padding: 0;
40
+ line-height: 1.6;
41
+ -webkit-font-smoothing: antialiased;
42
+ -moz-osx-font-smoothing: grayscale;
43
+ }
44
+
45
+ /* ---- Layout ---- */
46
+ .rubyn-shell {
47
+ display: flex;
48
+ min-height: 100vh;
49
+ }
50
+
51
+ .rubyn-sidebar {
52
+ position: fixed;
53
+ top: 0;
54
+ left: 0;
55
+ bottom: 0;
56
+ width: 240px;
57
+ background: var(--rubyn-surface);
58
+ border-right: 1px solid var(--rubyn-border-subtle);
59
+ display: flex;
60
+ flex-direction: column;
61
+ z-index: 100;
62
+ }
63
+
64
+ .rubyn-sidebar-brand {
65
+ padding: 1.5rem 1.25rem;
66
+ border-bottom: 1px solid var(--rubyn-border-subtle);
67
+ }
68
+
69
+ .rubyn-sidebar-brand a {
70
+ color: var(--rubyn-primary);
71
+ font-size: 1.25rem;
72
+ font-weight: 700;
73
+ text-decoration: none;
74
+ letter-spacing: -0.02em;
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 0.5rem;
78
+ }
79
+
80
+ .rubyn-sidebar-brand .rubyn-logo-img {
81
+ width: 28px;
82
+ height: 28px;
83
+ border-radius: 8px;
84
+ }
85
+
86
+ .rubyn-sidebar-nav {
87
+ flex: 1;
88
+ padding: 0.75rem;
89
+ display: flex;
90
+ flex-direction: column;
91
+ gap: 2px;
92
+ }
93
+
94
+ .rubyn-sidebar-nav a {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 0.75rem;
98
+ padding: 0.625rem 0.75rem;
99
+ color: var(--rubyn-text-dim);
100
+ text-decoration: none;
101
+ font-size: 0.875rem;
102
+ font-weight: 500;
103
+ border-radius: var(--rubyn-radius-sm);
104
+ transition: all var(--rubyn-transition);
105
+ }
106
+
107
+ .rubyn-sidebar-nav a:hover {
108
+ color: var(--rubyn-text);
109
+ background: var(--rubyn-surface-hover);
110
+ }
111
+
112
+ .rubyn-sidebar-nav a.active {
113
+ color: var(--rubyn-primary);
114
+ background: var(--rubyn-primary-soft);
115
+ }
116
+
117
+ .rubyn-sidebar-nav .nav-icon {
118
+ width: 18px;
119
+ height: 18px;
120
+ opacity: 0.7;
121
+ flex-shrink: 0;
122
+ }
123
+
124
+ .rubyn-sidebar-nav a.active .nav-icon {
125
+ opacity: 1;
126
+ }
127
+
128
+ .rubyn-sidebar-footer {
129
+ padding: 1rem 1.25rem;
130
+ border-top: 1px solid var(--rubyn-border-subtle);
131
+ font-size: 0.75rem;
132
+ color: var(--rubyn-text-muted);
133
+ }
134
+
135
+ .rubyn-content {
136
+ margin-left: 240px;
137
+ flex: 1;
138
+ min-height: 100vh;
139
+ }
140
+
141
+ .rubyn-main {
142
+ max-width: 960px;
143
+ margin: 0 auto;
144
+ padding: 2.5rem 2rem;
145
+ }
146
+
147
+ /* ---- Page header ---- */
148
+ .rubyn-page-header {
149
+ margin-bottom: 2rem;
150
+ }
151
+
152
+ .rubyn-page-header h1 {
153
+ font-size: 1.5rem;
154
+ font-weight: 700;
155
+ letter-spacing: -0.025em;
156
+ margin: 0 0 0.25rem 0;
157
+ }
158
+
159
+ .rubyn-page-header p {
160
+ color: var(--rubyn-text-dim);
161
+ font-size: 0.875rem;
162
+ margin: 0;
163
+ }
164
+
165
+ /* ---- Cards ---- */
166
+ .rubyn-cards {
167
+ display: grid;
168
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
169
+ gap: 1rem;
170
+ }
171
+
172
+ .rubyn-card {
173
+ background: var(--rubyn-surface);
174
+ border-radius: var(--rubyn-radius);
175
+ padding: 1.5rem;
176
+ border: 1px solid var(--rubyn-border-subtle);
177
+ box-shadow: var(--rubyn-shadow);
178
+ transition: all var(--rubyn-transition);
179
+ }
180
+
181
+ .rubyn-card:hover {
182
+ border-color: var(--rubyn-border);
183
+ box-shadow: var(--rubyn-shadow-lg);
184
+ transform: translateY(-1px);
185
+ }
186
+
187
+ .rubyn-card h3 {
188
+ font-size: 0.75rem;
189
+ font-weight: 600;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.06em;
192
+ color: var(--rubyn-text-dim);
193
+ margin: 0 0 0.75rem 0;
194
+ }
195
+
196
+ .rubyn-stat {
197
+ font-size: 2rem;
198
+ font-weight: 700;
199
+ letter-spacing: -0.03em;
200
+ color: var(--rubyn-text);
201
+ margin: 0;
202
+ line-height: 1.2;
203
+ }
204
+
205
+ .rubyn-stat.primary { color: var(--rubyn-primary); }
206
+ .rubyn-stat.success { color: var(--rubyn-success); }
207
+
208
+ .rubyn-label {
209
+ color: var(--rubyn-text-muted);
210
+ font-size: 0.8125rem;
211
+ margin-top: 0.25rem;
212
+ }
213
+
214
+ /* ---- Stat card variants ---- */
215
+ .rubyn-card--credits { border-top: 3px solid var(--rubyn-primary); }
216
+ .rubyn-card--plan { border-top: 3px solid var(--rubyn-success); }
217
+ .rubyn-card--project { border-top: 3px solid var(--rubyn-warning); }
218
+
219
+ /* ---- Quick Actions ---- */
220
+ .rubyn-actions {
221
+ margin-top: 2.5rem;
222
+ }
223
+
224
+ .rubyn-actions h2 {
225
+ font-size: 1rem;
226
+ font-weight: 600;
227
+ margin: 0 0 1rem 0;
228
+ }
229
+
230
+ .rubyn-actions-grid {
231
+ display: grid;
232
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
233
+ gap: 0.75rem;
234
+ }
235
+
236
+ .rubyn-action-card {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 0.75rem;
240
+ padding: 1rem 1.25rem;
241
+ background: var(--rubyn-surface);
242
+ border: 1px solid var(--rubyn-border-subtle);
243
+ border-radius: var(--rubyn-radius);
244
+ color: var(--rubyn-text);
245
+ text-decoration: none;
246
+ font-size: 0.875rem;
247
+ font-weight: 500;
248
+ transition: all var(--rubyn-transition);
249
+ cursor: pointer;
250
+ }
251
+
252
+ .rubyn-action-card:hover {
253
+ border-color: var(--rubyn-primary);
254
+ background: var(--rubyn-primary-soft);
255
+ color: var(--rubyn-text);
256
+ }
257
+
258
+ .rubyn-action-icon {
259
+ width: 36px;
260
+ height: 36px;
261
+ border-radius: var(--rubyn-radius-sm);
262
+ display: flex;
263
+ align-items: center;
264
+ justify-content: center;
265
+ font-size: 1.125rem;
266
+ flex-shrink: 0;
267
+ }
268
+
269
+ .rubyn-action-icon--files { background: var(--rubyn-primary-soft); color: var(--rubyn-primary); }
270
+ .rubyn-action-icon--agent { background: var(--rubyn-success-soft); color: var(--rubyn-success); }
271
+ .rubyn-action-icon--settings { background: var(--rubyn-warning-soft); color: var(--rubyn-warning); }
272
+
273
+ /* ---- Buttons ---- */
274
+ .rubyn-btn {
275
+ display: inline-flex;
276
+ align-items: center;
277
+ gap: 0.5rem;
278
+ padding: 0.5rem 1rem;
279
+ background: var(--rubyn-primary);
280
+ color: #fff;
281
+ border: none;
282
+ border-radius: var(--rubyn-radius-xs);
283
+ cursor: pointer;
284
+ text-decoration: none;
285
+ font-size: 0.8125rem;
286
+ font-weight: 600;
287
+ font-family: inherit;
288
+ transition: all var(--rubyn-transition);
289
+ letter-spacing: 0.01em;
290
+ }
291
+
292
+ .rubyn-btn:hover {
293
+ background: #9B1B30;
294
+ box-shadow: 0 0 16px var(--rubyn-primary-glow);
295
+ }
296
+
297
+ .rubyn-btn--ghost {
298
+ background: transparent;
299
+ color: var(--rubyn-text-dim);
300
+ border: 1px solid var(--rubyn-border);
301
+ }
302
+
303
+ .rubyn-btn--ghost:hover {
304
+ color: var(--rubyn-text);
305
+ border-color: var(--rubyn-text-dim);
306
+ background: var(--rubyn-surface-hover);
307
+ box-shadow: none;
308
+ }
309
+
310
+ .rubyn-btn-sm {
311
+ display: inline-flex;
312
+ align-items: center;
313
+ padding: 0.3125rem 0.625rem;
314
+ font-size: 0.75rem;
315
+ font-weight: 500;
316
+ font-family: inherit;
317
+ background: transparent;
318
+ color: var(--rubyn-text-dim);
319
+ border: 1px solid var(--rubyn-border);
320
+ border-radius: var(--rubyn-radius-xs);
321
+ text-decoration: none;
322
+ cursor: pointer;
323
+ transition: all var(--rubyn-transition);
324
+ }
325
+
326
+ .rubyn-btn-sm:hover {
327
+ color: var(--rubyn-primary);
328
+ border-color: var(--rubyn-primary);
329
+ background: var(--rubyn-primary-soft);
330
+ }
331
+
332
+ /* ---- File list ---- */
333
+ .rubyn-file-list {
334
+ background: var(--rubyn-surface);
335
+ border: 1px solid var(--rubyn-border-subtle);
336
+ border-radius: var(--rubyn-radius);
337
+ overflow: hidden;
338
+ box-shadow: var(--rubyn-shadow);
339
+ }
340
+
341
+ .rubyn-file-item {
342
+ display: flex;
343
+ justify-content: space-between;
344
+ align-items: center;
345
+ padding: 0.75rem 1.25rem;
346
+ border-bottom: 1px solid var(--rubyn-border-subtle);
347
+ transition: background var(--rubyn-transition);
348
+ }
349
+
350
+ .rubyn-file-item:last-child {
351
+ border-bottom: none;
352
+ }
353
+
354
+ .rubyn-file-item:hover {
355
+ background: var(--rubyn-surface-hover);
356
+ }
357
+
358
+ .rubyn-file-name {
359
+ font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
360
+ font-size: 0.8125rem;
361
+ color: var(--rubyn-text);
362
+ display: flex;
363
+ align-items: center;
364
+ gap: 0.625rem;
365
+ }
366
+
367
+ .rubyn-file-icon {
368
+ color: var(--rubyn-primary);
369
+ font-size: 1rem;
370
+ opacity: 0.7;
371
+ }
372
+
373
+ .rubyn-file-actions {
374
+ display: flex;
375
+ gap: 0.375rem;
376
+ opacity: 0;
377
+ transition: opacity var(--rubyn-transition);
378
+ }
379
+
380
+ .rubyn-file-item:hover .rubyn-file-actions {
381
+ opacity: 1;
382
+ }
383
+
384
+ /* ---- File categories ---- */
385
+ .rubyn-file-category {
386
+ margin-bottom: 1rem;
387
+ }
388
+
389
+ .rubyn-file-category-header {
390
+ display: flex;
391
+ align-items: center;
392
+ justify-content: space-between;
393
+ width: 100%;
394
+ padding: 0.75rem 1.25rem;
395
+ background: var(--rubyn-surface);
396
+ border: 1px solid var(--rubyn-border-subtle);
397
+ border-radius: var(--rubyn-radius) var(--rubyn-radius) 0 0;
398
+ color: var(--rubyn-text);
399
+ cursor: pointer;
400
+ font-family: inherit;
401
+ font-size: 0.875rem;
402
+ font-weight: 600;
403
+ transition: all var(--rubyn-transition);
404
+ }
405
+
406
+ .rubyn-file-category-header:hover {
407
+ background: var(--rubyn-surface-hover);
408
+ }
409
+
410
+ .rubyn-file-category-title {
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 0.5rem;
414
+ }
415
+
416
+ .rubyn-file-category-chevron {
417
+ transition: transform var(--rubyn-transition);
418
+ }
419
+
420
+ .rubyn-file-category-chevron--collapsed {
421
+ transform: rotate(-90deg);
422
+ }
423
+
424
+ .rubyn-file-category-count {
425
+ font-size: 0.75rem;
426
+ font-weight: 500;
427
+ color: var(--rubyn-text-muted);
428
+ background: var(--rubyn-surface-raised);
429
+ padding: 0.125rem 0.5rem;
430
+ border-radius: 100px;
431
+ }
432
+
433
+ .rubyn-file-category .rubyn-file-list {
434
+ border-top: none;
435
+ border-radius: 0 0 var(--rubyn-radius) var(--rubyn-radius);
436
+ transition: max-height 200ms ease, opacity 200ms ease;
437
+ max-height: 2000px;
438
+ overflow: hidden;
439
+ opacity: 1;
440
+ }
441
+
442
+ .rubyn-file-list--collapsed {
443
+ max-height: 0 !important;
444
+ opacity: 0 !important;
445
+ border-width: 0 1px !important;
446
+ }
447
+
448
+ /* ---- Chat / Agent ---- */
449
+ .rubyn-agent-container {
450
+ display: flex;
451
+ flex-direction: column;
452
+ height: calc(100vh - 8rem);
453
+ }
454
+
455
+ .rubyn-chat {
456
+ display: flex;
457
+ flex-direction: column;
458
+ flex: 1;
459
+ min-height: 0;
460
+ }
461
+
462
+ .rubyn-messages {
463
+ flex: 1;
464
+ overflow-y: auto;
465
+ padding: 1.5rem;
466
+ background: var(--rubyn-surface);
467
+ border: 1px solid var(--rubyn-border-subtle);
468
+ border-radius: var(--rubyn-radius);
469
+ margin-bottom: 1rem;
470
+ box-shadow: var(--rubyn-shadow);
471
+ display: flex;
472
+ flex-direction: column;
473
+ gap: 1rem;
474
+ }
475
+
476
+ .rubyn-messages::-webkit-scrollbar {
477
+ width: 6px;
478
+ }
479
+
480
+ .rubyn-messages::-webkit-scrollbar-track {
481
+ background: transparent;
482
+ }
483
+
484
+ .rubyn-messages::-webkit-scrollbar-thumb {
485
+ background: var(--rubyn-border);
486
+ border-radius: 3px;
487
+ }
488
+
489
+ .rubyn-message {
490
+ max-width: 80%;
491
+ padding: 0.75rem 1rem;
492
+ border-radius: var(--rubyn-radius);
493
+ font-size: 0.875rem;
494
+ line-height: 1.6;
495
+ animation: messageIn 200ms ease-out;
496
+ }
497
+
498
+ @keyframes messageIn {
499
+ from { opacity: 0; transform: translateY(8px); }
500
+ to { opacity: 1; transform: translateY(0); }
501
+ }
502
+
503
+ .rubyn-message-you {
504
+ align-self: flex-end;
505
+ background: var(--rubyn-primary);
506
+ color: #fff;
507
+ border-bottom-right-radius: 4px;
508
+ }
509
+
510
+ .rubyn-message-rubyn {
511
+ align-self: flex-start;
512
+ background: var(--rubyn-surface-raised);
513
+ border: 1px solid var(--rubyn-border-subtle);
514
+ border-bottom-left-radius: 4px;
515
+ }
516
+
517
+ .rubyn-message-label {
518
+ font-size: 0.6875rem;
519
+ font-weight: 600;
520
+ text-transform: uppercase;
521
+ letter-spacing: 0.05em;
522
+ margin-bottom: 0.25rem;
523
+ opacity: 0.6;
524
+ }
525
+
526
+ .rubyn-message-body {
527
+ word-wrap: break-word;
528
+ }
529
+
530
+ .rubyn-chat-input {
531
+ display: flex;
532
+ gap: 0.5rem;
533
+ align-items: flex-end;
534
+ }
535
+
536
+ .rubyn-chat-input textarea {
537
+ flex: 1;
538
+ background: var(--rubyn-surface);
539
+ color: var(--rubyn-text);
540
+ border: 1px solid var(--rubyn-border);
541
+ border-radius: var(--rubyn-radius-sm);
542
+ padding: 0.75rem 1rem;
543
+ font-family: inherit;
544
+ font-size: 0.875rem;
545
+ resize: none;
546
+ transition: border-color var(--rubyn-transition);
547
+ line-height: 1.5;
548
+ }
549
+
550
+ .rubyn-chat-input textarea::placeholder {
551
+ color: var(--rubyn-text-muted);
552
+ }
553
+
554
+ .rubyn-chat-input textarea:focus {
555
+ outline: none;
556
+ border-color: var(--rubyn-primary);
557
+ box-shadow: 0 0 0 3px var(--rubyn-primary-soft);
558
+ }
559
+
560
+ .rubyn-chat-input .rubyn-btn {
561
+ padding: 0.75rem 1.25rem;
562
+ align-self: flex-end;
563
+ }
564
+
565
+ /* ---- Empty state ---- */
566
+ .rubyn-empty-chat {
567
+ display: flex;
568
+ flex-direction: column;
569
+ align-items: center;
570
+ justify-content: center;
571
+ flex: 1;
572
+ color: var(--rubyn-text-muted);
573
+ gap: 0.75rem;
574
+ padding: 3rem;
575
+ }
576
+
577
+ .rubyn-empty-chat-icon {
578
+ font-size: 2.5rem;
579
+ opacity: 0.4;
580
+ }
581
+
582
+ .rubyn-empty-chat p {
583
+ margin: 0;
584
+ font-size: 0.875rem;
585
+ }
586
+
587
+ /* ---- Tool pages (refactor, spec, review) ---- */
588
+ .rubyn-tool-container {
589
+ background: var(--rubyn-surface);
590
+ border: 1px solid var(--rubyn-border-subtle);
591
+ border-radius: var(--rubyn-radius);
592
+ box-shadow: var(--rubyn-shadow);
593
+ overflow: hidden;
594
+ }
595
+
596
+ .rubyn-tool-header {
597
+ display: flex;
598
+ align-items: center;
599
+ justify-content: space-between;
600
+ padding: 1rem 1.25rem;
601
+ border-bottom: 1px solid var(--rubyn-border-subtle);
602
+ background: var(--rubyn-surface-hover);
603
+ }
604
+
605
+ .rubyn-tool-filepath {
606
+ font-family: 'JetBrains Mono', monospace;
607
+ font-size: 0.8125rem;
608
+ color: var(--rubyn-text-dim);
609
+ display: flex;
610
+ align-items: center;
611
+ gap: 0.5rem;
612
+ }
613
+
614
+ .rubyn-tool-body {
615
+ padding: 1.5rem;
616
+ min-height: 300px;
617
+ }
618
+
619
+ .rubyn-tool-empty {
620
+ display: flex;
621
+ flex-direction: column;
622
+ align-items: center;
623
+ justify-content: center;
624
+ padding: 4rem 2rem;
625
+ text-align: center;
626
+ color: var(--rubyn-text-dim);
627
+ }
628
+
629
+ .rubyn-tool-empty p {
630
+ margin: 0.5rem 0 0 0;
631
+ font-size: 0.875rem;
632
+ }
633
+
634
+ .rubyn-tool-empty a {
635
+ color: var(--rubyn-primary);
636
+ text-decoration: none;
637
+ }
638
+
639
+ .rubyn-tool-empty a:hover {
640
+ text-decoration: underline;
641
+ }
642
+
643
+ /* ---- Tool output ---- */
644
+ .rubyn-tool-output {
645
+ margin: 0;
646
+ padding: 0;
647
+ background: transparent;
648
+ white-space: pre-wrap;
649
+ word-wrap: break-word;
650
+ font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
651
+ font-size: 0.8125rem;
652
+ line-height: 1.7;
653
+ color: var(--rubyn-text);
654
+ }
655
+
656
+ .rubyn-tool-output code {
657
+ background: transparent;
658
+ border: none;
659
+ padding: 0;
660
+ font-size: inherit;
661
+ }
662
+
663
+ /* Highlight.js overrides to match theme */
664
+ .rubyn-tool-output code.hljs {
665
+ background: transparent;
666
+ padding: 0;
667
+ }
668
+
669
+ .rubyn-message-body pre {
670
+ margin: 0.5rem 0;
671
+ border-radius: var(--rubyn-radius-sm);
672
+ overflow-x: auto;
673
+ }
674
+
675
+ .rubyn-message-body pre code.hljs {
676
+ padding: 0.75rem 1rem;
677
+ border-radius: var(--rubyn-radius-sm);
678
+ }
679
+
680
+ /* ---- Tool actions ---- */
681
+ .rubyn-tool-actions {
682
+ display: flex;
683
+ gap: 0.5rem;
684
+ padding: 1rem 1.5rem;
685
+ border-top: 1px solid var(--rubyn-border-subtle);
686
+ background: var(--rubyn-surface-hover);
687
+ }
688
+
689
+ .rubyn-btn--success {
690
+ background: var(--rubyn-success);
691
+ cursor: default;
692
+ }
693
+
694
+ .rubyn-btn--success:hover {
695
+ background: var(--rubyn-success);
696
+ box-shadow: none;
697
+ }
698
+
699
+ /* ---- Code blocks ---- */
700
+ .rubyn-code-block {
701
+ border: 1px solid var(--rubyn-border-subtle);
702
+ border-radius: var(--rubyn-radius-sm);
703
+ margin-bottom: 0.75rem;
704
+ overflow: hidden;
705
+ }
706
+
707
+ .rubyn-code-block-header {
708
+ display: flex;
709
+ align-items: center;
710
+ justify-content: space-between;
711
+ padding: 0.5rem 1rem;
712
+ background: var(--rubyn-surface-hover);
713
+ border-bottom: 1px solid var(--rubyn-border-subtle);
714
+ font-size: 0.8125rem;
715
+ }
716
+
717
+ .rubyn-code-block-actions {
718
+ display: flex;
719
+ gap: 0.375rem;
720
+ }
721
+
722
+ .rubyn-file-tag {
723
+ display: inline-block;
724
+ padding: 0.125rem 0.5rem;
725
+ border-radius: 3px;
726
+ font-size: 0.6875rem;
727
+ font-weight: 600;
728
+ text-transform: uppercase;
729
+ letter-spacing: 0.03em;
730
+ margin-right: 0.5rem;
731
+ }
732
+
733
+ .rubyn-tag-new {
734
+ background: var(--rubyn-success-soft);
735
+ color: var(--rubyn-success);
736
+ }
737
+
738
+ .rubyn-code-block .rubyn-tool-output {
739
+ margin: 0;
740
+ padding: 1rem;
741
+ max-height: 400px;
742
+ overflow-y: auto;
743
+ }
744
+
745
+ /* ---- Collapsible explanation ---- */
746
+ .rubyn-explanation {
747
+ transition: max-height 300ms ease, opacity 300ms ease;
748
+ max-height: 5000px;
749
+ opacity: 1;
750
+ overflow: hidden;
751
+ }
752
+
753
+ .rubyn-explanation--collapsed {
754
+ max-height: 0;
755
+ opacity: 0;
756
+ padding: 0;
757
+ }
758
+
759
+ /* ---- Feedback ---- */
760
+ .rubyn-feedback {
761
+ display: flex;
762
+ align-items: center;
763
+ gap: 0.5rem;
764
+ padding: 0.75rem 1.5rem;
765
+ border-top: 1px solid var(--rubyn-border-subtle);
766
+ }
767
+
768
+ .rubyn-feedback-label {
769
+ font-size: 0.8125rem;
770
+ color: var(--rubyn-text-dim);
771
+ }
772
+
773
+ .rubyn-feedback-thanks {
774
+ color: var(--rubyn-success);
775
+ }
776
+
777
+ .rubyn-feedback-btn {
778
+ display: flex;
779
+ align-items: center;
780
+ justify-content: center;
781
+ width: 32px;
782
+ height: 32px;
783
+ border: 1px solid var(--rubyn-border);
784
+ border-radius: var(--rubyn-radius-xs);
785
+ background: transparent;
786
+ color: var(--rubyn-text-dim);
787
+ cursor: pointer;
788
+ transition: all var(--rubyn-transition);
789
+ }
790
+
791
+ .rubyn-feedback-btn:hover {
792
+ color: var(--rubyn-primary);
793
+ border-color: var(--rubyn-primary);
794
+ background: var(--rubyn-primary-soft);
795
+ }
796
+
797
+ .rubyn-feedback-btn:disabled {
798
+ opacity: 0.5;
799
+ cursor: default;
800
+ }
801
+
802
+ /* ---- Tool footer (credits + feedback) ---- */
803
+ .rubyn-tool-footer {
804
+ display: flex;
805
+ align-items: center;
806
+ gap: 1rem;
807
+ padding: 0.75rem 1.5rem;
808
+ border-top: 1px solid var(--rubyn-border-subtle);
809
+ }
810
+
811
+ .rubyn-credits-badge {
812
+ font-size: 0.75rem;
813
+ font-weight: 500;
814
+ color: var(--rubyn-primary);
815
+ background: var(--rubyn-primary-soft);
816
+ padding: 0.25rem 0.625rem;
817
+ border-radius: var(--rubyn-radius-xs);
818
+ white-space: nowrap;
819
+ }
820
+
821
+ /* ---- Usage table ---- */
822
+ .rubyn-usage-section {
823
+ margin-bottom: 2rem;
824
+ }
825
+
826
+ .rubyn-usage-section h2 {
827
+ font-size: 1.125rem;
828
+ font-weight: 600;
829
+ margin: 0 0 1rem 0;
830
+ }
831
+
832
+ .rubyn-usage-table {
833
+ width: 100%;
834
+ border-collapse: collapse;
835
+ font-size: 0.8125rem;
836
+ background: var(--rubyn-surface);
837
+ border: 1px solid var(--rubyn-border-subtle);
838
+ border-radius: var(--rubyn-radius);
839
+ overflow: hidden;
840
+ }
841
+
842
+ .rubyn-usage-table th {
843
+ text-align: left;
844
+ padding: 0.625rem 1rem;
845
+ font-weight: 600;
846
+ font-size: 0.6875rem;
847
+ text-transform: uppercase;
848
+ letter-spacing: 0.05em;
849
+ color: var(--rubyn-text-dim);
850
+ background: var(--rubyn-surface-hover);
851
+ border-bottom: 1px solid var(--rubyn-border-subtle);
852
+ }
853
+
854
+ .rubyn-usage-table td {
855
+ padding: 0.5rem 1rem;
856
+ border-bottom: 1px solid var(--rubyn-border-subtle);
857
+ }
858
+
859
+ .rubyn-usage-table tr:last-child td {
860
+ border-bottom: none;
861
+ }
862
+
863
+ .rubyn-usage-command {
864
+ font-family: 'JetBrains Mono', monospace;
865
+ font-size: 0.75rem;
866
+ background: var(--rubyn-primary-soft);
867
+ color: var(--rubyn-primary);
868
+ padding: 0.125rem 0.5rem;
869
+ border-radius: var(--rubyn-radius-xs);
870
+ }
871
+
872
+ .rubyn-text-dim {
873
+ color: var(--rubyn-text-dim);
874
+ }
875
+
876
+ /* ---- Loading spinner ---- */
877
+ .rubyn-loading {
878
+ display: flex;
879
+ align-items: center;
880
+ gap: 0.75rem;
881
+ color: var(--rubyn-text-dim);
882
+ font-size: 0.875rem;
883
+ }
884
+
885
+ .rubyn-spinner {
886
+ width: 18px;
887
+ height: 18px;
888
+ border: 2px solid var(--rubyn-border);
889
+ border-top-color: var(--rubyn-primary);
890
+ border-radius: 50%;
891
+ animation: spin 600ms linear infinite;
892
+ }
893
+
894
+ @keyframes spin {
895
+ to { transform: rotate(360deg); }
896
+ }
897
+
898
+ /* ---- Pulse dots loading ---- */
899
+ .rubyn-dots {
900
+ display: inline-flex;
901
+ gap: 4px;
902
+ align-items: center;
903
+ }
904
+
905
+ .rubyn-dots span {
906
+ width: 6px;
907
+ height: 6px;
908
+ background: var(--rubyn-text-dim);
909
+ border-radius: 50%;
910
+ animation: pulse 1.2s ease-in-out infinite;
911
+ }
912
+
913
+ .rubyn-dots span:nth-child(2) { animation-delay: 0.2s; }
914
+ .rubyn-dots span:nth-child(3) { animation-delay: 0.4s; }
915
+
916
+ @keyframes pulse {
917
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
918
+ 40% { opacity: 1; transform: scale(1); }
919
+ }
920
+
921
+ /* ---- Settings ---- */
922
+ .rubyn-settings-grid {
923
+ display: flex;
924
+ flex-direction: column;
925
+ gap: 1rem;
926
+ }
927
+
928
+ .rubyn-setting-card {
929
+ background: var(--rubyn-surface);
930
+ border: 1px solid var(--rubyn-border-subtle);
931
+ border-radius: var(--rubyn-radius);
932
+ padding: 1.25rem 1.5rem;
933
+ box-shadow: var(--rubyn-shadow);
934
+ }
935
+
936
+ .rubyn-setting-card h3 {
937
+ font-size: 0.875rem;
938
+ font-weight: 600;
939
+ margin: 0 0 0.25rem 0;
940
+ }
941
+
942
+ .rubyn-setting-card p {
943
+ font-size: 0.8125rem;
944
+ color: var(--rubyn-text-dim);
945
+ margin: 0;
946
+ }
947
+
948
+ .rubyn-status-badge {
949
+ display: inline-flex;
950
+ align-items: center;
951
+ gap: 0.375rem;
952
+ font-size: 0.75rem;
953
+ font-weight: 500;
954
+ padding: 0.25rem 0.625rem;
955
+ border-radius: 100px;
956
+ margin-top: 0.5rem;
957
+ }
958
+
959
+ .rubyn-status-badge--ok {
960
+ background: var(--rubyn-success-soft);
961
+ color: var(--rubyn-success);
962
+ }
963
+
964
+ .rubyn-status-badge--warn {
965
+ background: var(--rubyn-warning-soft);
966
+ color: var(--rubyn-warning);
967
+ }
968
+
969
+ .rubyn-status-dot {
970
+ width: 6px;
971
+ height: 6px;
972
+ border-radius: 50%;
973
+ background: currentColor;
974
+ }
975
+
976
+ .rubyn-form-group {
977
+ margin: 1.25rem 0;
978
+ }
979
+
980
+ .rubyn-form-group label {
981
+ display: block;
982
+ margin-bottom: 0.375rem;
983
+ font-size: 0.8125rem;
984
+ font-weight: 500;
985
+ color: var(--rubyn-text-dim);
986
+ }
987
+
988
+ .rubyn-form-group select,
989
+ .rubyn-form-group input[type="text"] {
990
+ background: var(--rubyn-bg);
991
+ color: var(--rubyn-text);
992
+ border: 1px solid var(--rubyn-border);
993
+ border-radius: var(--rubyn-radius-xs);
994
+ padding: 0.5rem 0.75rem;
995
+ font-family: inherit;
996
+ font-size: 0.875rem;
997
+ transition: border-color var(--rubyn-transition);
998
+ }
999
+
1000
+ .rubyn-form-group select:focus,
1001
+ .rubyn-form-group input[type="text"]:focus {
1002
+ outline: none;
1003
+ border-color: var(--rubyn-primary);
1004
+ box-shadow: 0 0 0 3px var(--rubyn-primary-soft);
1005
+ }
1006
+
1007
+ .rubyn-form-group input[type="checkbox"] {
1008
+ accent-color: var(--rubyn-primary);
1009
+ }
1010
+
1011
+ /* ---- Update banner ---- */
1012
+ .rubyn-update-banner {
1013
+ background: var(--rubyn-primary-soft);
1014
+ color: var(--rubyn-primary);
1015
+ padding: 0.75rem 1rem;
1016
+ border-radius: var(--rubyn-radius-sm);
1017
+ margin-bottom: 1.5rem;
1018
+ font-size: 0.8125rem;
1019
+ font-weight: 500;
1020
+ border: 1px solid rgba(204, 52, 45, 0.2);
1021
+ display: flex;
1022
+ align-items: center;
1023
+ gap: 0.5rem;
1024
+ animation: noticeIn 300ms ease-out;
1025
+ }
1026
+
1027
+ /* ---- Notice ---- */
1028
+ .rubyn-notice {
1029
+ background: var(--rubyn-success-soft);
1030
+ color: var(--rubyn-success);
1031
+ padding: 0.75rem 1rem;
1032
+ border-radius: var(--rubyn-radius-sm);
1033
+ margin-bottom: 1.5rem;
1034
+ font-size: 0.875rem;
1035
+ font-weight: 500;
1036
+ border: 1px solid rgba(78, 204, 163, 0.2);
1037
+ animation: noticeIn 300ms ease-out;
1038
+ }
1039
+
1040
+ @keyframes noticeIn {
1041
+ from { opacity: 0; transform: translateY(-8px); }
1042
+ to { opacity: 1; transform: translateY(0); }
1043
+ }
1044
+
1045
+ /* ---- Code ---- */
1046
+ code {
1047
+ font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;
1048
+ background: var(--rubyn-surface-raised);
1049
+ padding: 0.125rem 0.5rem;
1050
+ border-radius: var(--rubyn-radius-xs);
1051
+ font-size: 0.8125rem;
1052
+ border: 1px solid var(--rubyn-border-subtle);
1053
+ }
1054
+
1055
+ /* ---- Responsive ---- */
1056
+ @media (max-width: 768px) {
1057
+ .rubyn-sidebar {
1058
+ display: none;
1059
+ }
1060
+
1061
+ .rubyn-content {
1062
+ margin-left: 0;
1063
+ }
1064
+
1065
+ .rubyn-mobile-nav {
1066
+ display: flex;
1067
+ align-items: center;
1068
+ justify-content: space-between;
1069
+ padding: 1rem;
1070
+ background: var(--rubyn-surface);
1071
+ border-bottom: 1px solid var(--rubyn-border-subtle);
1072
+ }
1073
+ }