ruby_llm-agents 0.1.0 → 0.2.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.
@@ -3,16 +3,16 @@
3
3
  selected_agents = params[:agent_types].present? ? (params[:agent_types].is_a?(Array) ? params[:agent_types] : params[:agent_types].split(",")) : []
4
4
  selected_statuses = params[:statuses].present? ? (params[:statuses].is_a?(Array) ? params[:statuses] : params[:statuses].split(",")) : []
5
5
  %>
6
- <div class="bg-white rounded-xl border border-gray-200 p-4 mb-6">
6
+ <div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-4 mb-6" data-controller="filter-dropdown">
7
7
  <%= form_with url: ruby_llm_agents.executions_path, method: :get, data: { turbo_frame: "executions_content", turbo_action: "advance" }, id: "filters-form" do |f| %>
8
8
  <div class="flex flex-wrap items-center gap-3">
9
9
  <!-- Agent Type Filter (Multi-select) -->
10
10
  <div class="relative filter-dropdown" data-filter="agent_types">
11
- <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 border border-gray-200 rounded-lg hover:bg-gray-100 transition-colors <%= selected_agents.any? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
11
+ <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors <%= selected_agents.any? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
12
12
  <svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
13
13
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
14
14
  </svg>
15
- <span class="dropdown-label text-gray-700">
15
+ <span class="dropdown-label text-gray-700 dark:text-gray-200">
16
16
  <% if selected_agents.empty? %>
17
17
  All Agents
18
18
  <% elsif selected_agents.length == 1 %>
@@ -25,16 +25,16 @@
25
25
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
26
26
  </svg>
27
27
  </button>
28
- <div class="dropdown-menu hidden absolute z-10 mt-1 w-56 bg-white rounded-lg shadow-lg border border-gray-200 py-1">
29
- <div class="px-3 py-2 border-b border-gray-100">
28
+ <div class="dropdown-menu hidden absolute z-10 mt-1 w-56 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1">
29
+ <div class="px-3 py-2 border-b border-gray-100 dark:border-gray-700">
30
30
  <label class="flex items-center gap-2 cursor-pointer">
31
- <input type="checkbox" class="select-all-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="toggleAllOptions(this, 'agent_types')" <%= selected_agents.empty? ? 'checked' : '' %>>
32
- <span class="text-sm font-medium text-gray-700">All Agents</span>
31
+ <input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="toggleAllOptions(this, 'agent_types')" <%= selected_agents.empty? ? 'checked' : '' %>>
32
+ <span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Agents</span>
33
33
  </label>
34
34
  </div>
35
35
  <% agent_types.each do |agent_type| %>
36
- <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer">
37
- <input type="checkbox" name="agent_types[]" value="<%= agent_type %>" class="filter-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="updateMultiSelect('agent_types')" <%= selected_agents.include?(agent_type) ? 'checked' : '' %>>
36
+ <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer">
37
+ <input type="checkbox" name="agent_types[]" value="<%= agent_type %>" class="filter-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="updateMultiSelect('agent_types')" <%= selected_agents.include?(agent_type) ? 'checked' : '' %>>
38
38
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
39
39
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
40
40
  </svg>
@@ -46,7 +46,7 @@
46
46
 
47
47
  <!-- Status Filter (Multi-select) -->
48
48
  <div class="relative filter-dropdown" data-filter="statuses">
49
- <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 border border-gray-200 rounded-lg hover:bg-gray-100 transition-colors <%= selected_statuses.any? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
49
+ <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors <%= selected_statuses.any? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
50
50
  <% if selected_statuses.length == 1
51
51
  status_color = case selected_statuses.first
52
52
  when 'success' then 'bg-green-500'
@@ -59,7 +59,7 @@
59
59
  status_color = 'bg-gray-400'
60
60
  end %>
61
61
  <span class="w-2 h-2 rounded-full <%= status_color %>"></span>
62
- <span class="dropdown-label text-gray-700">
62
+ <span class="dropdown-label text-gray-700 dark:text-gray-200">
63
63
  <% if selected_statuses.empty? %>
64
64
  All Statuses
65
65
  <% elsif selected_statuses.length == 1 %>
@@ -72,30 +72,30 @@
72
72
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
73
73
  </svg>
74
74
  </button>
75
- <div class="dropdown-menu hidden absolute z-10 mt-1 w-44 bg-white rounded-lg shadow-lg border border-gray-200 py-1">
76
- <div class="px-3 py-2 border-b border-gray-100">
75
+ <div class="dropdown-menu hidden absolute z-10 mt-1 w-44 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1">
76
+ <div class="px-3 py-2 border-b border-gray-100 dark:border-gray-700">
77
77
  <label class="flex items-center gap-2 cursor-pointer">
78
- <input type="checkbox" class="select-all-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="toggleAllOptions(this, 'statuses')" <%= selected_statuses.empty? ? 'checked' : '' %>>
79
- <span class="text-sm font-medium text-gray-700">All Statuses</span>
78
+ <input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="toggleAllOptions(this, 'statuses')" <%= selected_statuses.empty? ? 'checked' : '' %>>
79
+ <span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Statuses</span>
80
80
  </label>
81
81
  </div>
82
- <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer">
83
- <input type="checkbox" name="statuses[]" value="success" class="filter-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('success') ? 'checked' : '' %>>
82
+ <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer">
83
+ <input type="checkbox" name="statuses[]" value="success" class="filter-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('success') ? 'checked' : '' %>>
84
84
  <span class="w-2 h-2 rounded-full bg-green-500"></span>
85
85
  Success
86
86
  </label>
87
- <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer">
88
- <input type="checkbox" name="statuses[]" value="error" class="filter-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('error') ? 'checked' : '' %>>
87
+ <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer">
88
+ <input type="checkbox" name="statuses[]" value="error" class="filter-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('error') ? 'checked' : '' %>>
89
89
  <span class="w-2 h-2 rounded-full bg-red-500"></span>
90
90
  Error
91
91
  </label>
92
- <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer">
93
- <input type="checkbox" name="statuses[]" value="running" class="filter-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('running') ? 'checked' : '' %>>
92
+ <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer">
93
+ <input type="checkbox" name="statuses[]" value="running" class="filter-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('running') ? 'checked' : '' %>>
94
94
  <span class="w-2 h-2 rounded-full bg-blue-500 animate-pulse"></span>
95
95
  Running
96
96
  </label>
97
- <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer">
98
- <input type="checkbox" name="statuses[]" value="timeout" class="filter-checkbox rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('timeout') ? 'checked' : '' %>>
97
+ <label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer">
98
+ <input type="checkbox" name="statuses[]" value="timeout" class="filter-checkbox rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:bg-gray-700" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('timeout') ? 'checked' : '' %>>
99
99
  <span class="w-2 h-2 rounded-full bg-yellow-500"></span>
100
100
  Timeout
101
101
  </label>
@@ -104,11 +104,11 @@
104
104
 
105
105
  <!-- Time Range Filter -->
106
106
  <div class="relative filter-dropdown" data-filter="days">
107
- <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 border border-gray-200 rounded-lg hover:bg-gray-100 transition-colors <%= params[:days].present? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
107
+ <button type="button" onclick="toggleDropdown(this)" class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors <%= params[:days].present? ? 'ring-2 ring-blue-500 ring-offset-1' : '' %>">
108
108
  <svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
109
109
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
110
110
  </svg>
111
- <span class="dropdown-label text-gray-700">
111
+ <span class="dropdown-label text-gray-700 dark:text-gray-200">
112
112
  <% case params[:days]
113
113
  when '1' then %>Today<%
114
114
  when '7' then %>Last 7 Days<%
@@ -120,26 +120,26 @@
120
120
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
121
121
  </svg>
122
122
  </button>
123
- <div class="dropdown-menu hidden absolute z-10 mt-1 w-40 bg-white rounded-lg shadow-lg border border-gray-200 py-1">
124
- <a href="#" onclick="selectSingleFilter('days', '', 'All Time'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 <%= params[:days].blank? ? 'bg-blue-50 text-blue-700' : '' %>">
123
+ <div class="dropdown-menu hidden absolute z-10 mt-1 w-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1">
124
+ <a href="#" onclick="selectSingleFilter('days', '', 'All Time'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 <%= params[:days].blank? ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
125
125
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
126
126
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
127
127
  </svg>
128
128
  All Time
129
129
  </a>
130
- <a href="#" onclick="selectSingleFilter('days', '1', 'Today'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 <%= params[:days] == '1' ? 'bg-blue-50 text-blue-700' : '' %>">
130
+ <a href="#" onclick="selectSingleFilter('days', '1', 'Today'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 <%= params[:days] == '1' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
131
131
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
132
132
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
133
133
  </svg>
134
134
  Today
135
135
  </a>
136
- <a href="#" onclick="selectSingleFilter('days', '7', 'Last 7 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 <%= params[:days] == '7' ? 'bg-blue-50 text-blue-700' : '' %>">
136
+ <a href="#" onclick="selectSingleFilter('days', '7', 'Last 7 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 <%= params[:days] == '7' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
137
137
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
138
138
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
139
139
  </svg>
140
140
  Last 7 Days
141
141
  </a>
142
- <a href="#" onclick="selectSingleFilter('days', '30', 'Last 30 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 <%= params[:days] == '30' ? 'bg-blue-50 text-blue-700' : '' %>">
142
+ <a href="#" onclick="selectSingleFilter('days', '30', 'Last 30 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 <%= params[:days] == '30' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
143
143
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
144
144
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
145
145
  </svg>
@@ -160,11 +160,11 @@
160
160
  <% end %>
161
161
 
162
162
  <!-- Stats Summary (right aligned) -->
163
- <div class="ml-auto flex items-center gap-4 text-sm text-gray-500">
163
+ <div class="ml-auto flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400">
164
164
  <span><%= number_to_human_short(filter_stats[:total_count]) %> executions</span>
165
- <span class="text-gray-300">|</span>
165
+ <span class="text-gray-300 dark:text-gray-600">|</span>
166
166
  <span><%= number_to_human_short(filter_stats[:total_cost], prefix: "$", precision: 2) %></span>
167
- <span class="text-gray-300">|</span>
167
+ <span class="text-gray-300 dark:text-gray-600">|</span>
168
168
  <span><%= number_to_human_short(filter_stats[:total_tokens]) %> tokens</span>
169
169
  </div>
170
170
  </div>
@@ -1,18 +1,18 @@
1
- <div class="bg-white rounded-lg shadow overflow-hidden">
1
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
2
2
  <% if executions.empty? %>
3
- <div class="px-6 py-12 text-center text-gray-500">
3
+ <div class="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
4
4
  <p class="text-lg">No executions found</p>
5
5
  <p class="text-sm mt-1">Try adjusting your filters</p>
6
6
  </div>
7
7
  <% else %>
8
8
  <div class="overflow-x-auto">
9
- <table class="min-w-full divide-y divide-gray-200">
10
- <thead class="bg-gray-50">
9
+ <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
10
+ <thead class="bg-gray-50 dark:bg-gray-900">
11
11
  <tr>
12
12
  <th
13
13
  scope="col"
14
14
  class="
15
- px-4 py-3 text-left text-xs font-medium text-gray-500
15
+ px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400
16
16
  uppercase tracking-wider
17
17
  "
18
18
  >
@@ -22,7 +22,7 @@
22
22
  <th
23
23
  scope="col"
24
24
  class="
25
- px-4 py-3 text-left text-xs font-medium text-gray-500
25
+ px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400
26
26
  uppercase tracking-wider
27
27
  "
28
28
  >
@@ -32,7 +32,7 @@
32
32
  <th
33
33
  scope="col"
34
34
  class="
35
- px-4 py-3 text-left text-xs font-medium text-gray-500
35
+ px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400
36
36
  uppercase tracking-wider
37
37
  "
38
38
  >
@@ -42,7 +42,7 @@
42
42
  <th
43
43
  scope="col"
44
44
  class="
45
- px-4 py-3 text-right text-xs font-medium text-gray-500
45
+ px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400
46
46
  uppercase tracking-wider
47
47
  "
48
48
  >
@@ -52,7 +52,7 @@
52
52
  <th
53
53
  scope="col"
54
54
  class="
55
- px-4 py-3 text-right text-xs font-medium text-gray-500
55
+ px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400
56
56
  uppercase tracking-wider
57
57
  "
58
58
  >
@@ -62,7 +62,7 @@
62
62
  <th
63
63
  scope="col"
64
64
  class="
65
- px-4 py-3 text-right text-xs font-medium text-gray-500
65
+ px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400
66
66
  uppercase tracking-wider
67
67
  "
68
68
  >
@@ -72,7 +72,7 @@
72
72
  <th
73
73
  scope="col"
74
74
  class="
75
- px-4 py-3 text-right text-xs font-medium text-gray-500
75
+ px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400
76
76
  uppercase tracking-wider
77
77
  "
78
78
  >
@@ -81,14 +81,14 @@
81
81
  </tr>
82
82
  </thead>
83
83
 
84
- <tbody class="bg-white divide-y divide-gray-100">
84
+ <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-100 dark:divide-gray-700">
85
85
  <% executions.each do |execution| %>
86
86
  <tr
87
- class="hover:bg-gray-50 transition-colors cursor-pointer"
87
+ class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors cursor-pointer"
88
88
  onclick="window.location='<%= ruby_llm_agents.execution_path(execution) %>'"
89
89
  >
90
90
  <td class="px-4 py-3 whitespace-nowrap">
91
- <span class="text-sm font-medium text-gray-900">
91
+ <span class="text-sm font-medium text-gray-900 dark:text-gray-100">
92
92
  <%= execution.agent_type.gsub(/Agent$/, '') %>
93
93
  </span>
94
94
  </td>
@@ -97,13 +97,13 @@
97
97
  <%= render "rubyllm/agents/shared/status_badge", status: execution.status %>
98
98
  </td>
99
99
 
100
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
100
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
101
101
  v<%= execution.agent_version %>
102
102
  </td>
103
103
 
104
104
  <td
105
105
  class="
106
- px-4 py-3 whitespace-nowrap text-sm text-gray-900
106
+ px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100
107
107
  text-right font-medium
108
108
  "
109
109
  >
@@ -112,7 +112,7 @@
112
112
 
113
113
  <td
114
114
  class="
115
- px-4 py-3 whitespace-nowrap text-sm text-gray-900
115
+ px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100
116
116
  text-right font-medium
117
117
  "
118
118
  >
@@ -121,22 +121,22 @@
121
121
 
122
122
  <td
123
123
  class="
124
- px-4 py-3 whitespace-nowrap text-sm text-gray-900
124
+ px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100
125
125
  text-right font-medium
126
126
  "
127
127
  >
128
128
  <%= number_with_delimiter(execution.duration_ms || 0) %>ms
129
129
  </td>
130
130
 
131
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 text-right">
131
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 text-right">
132
132
  <%= time_ago_in_words(execution.created_at) %> ago
133
133
  </td>
134
134
  </tr>
135
135
 
136
136
  <% if execution.status_error? && execution.error_message.present? %>
137
- <tr class="bg-red-50">
137
+ <tr class="bg-red-50 dark:bg-red-900/30">
138
138
  <td colspan="7" class="px-4 py-2">
139
- <p class="text-xs text-red-600">
139
+ <p class="text-xs text-red-600 dark:text-red-400">
140
140
  <span class="font-medium">
141
141
  <%= execution.error_class %>:
142
142
  </span>
@@ -158,20 +158,20 @@
158
158
 
159
159
  from_record = ((current_page - 1) * per_page) + 1
160
160
  to_record = [current_page * per_page, total_count].min %>
161
- <div class="px-4 py-4 flex items-center justify-between border-t border-gray-100">
162
- <p class="text-sm text-gray-500">
161
+ <div class="px-4 py-4 flex items-center justify-between border-t border-gray-100 dark:border-gray-700">
162
+ <p class="text-sm text-gray-500 dark:text-gray-400">
163
163
  Showing <%= from_record %>-<%= to_record %> of
164
164
  <%= number_with_delimiter(total_count) %> executions
165
165
  </p>
166
166
 
167
167
  <nav class="flex items-center space-x-1">
168
168
  <% if current_page > 1 %>
169
- <%= link_to "Previous", url_for(page: current_page - 1), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" %>
169
+ <%= link_to "Previous", url_for(page: current_page - 1), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
170
170
  <% else %>
171
171
  <span
172
172
  class="
173
- px-3 py-1.5 text-sm font-medium text-gray-400 bg-gray-100
174
- border border-gray-200 rounded-md cursor-not-allowed
173
+ px-3 py-1.5 text-sm font-medium text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800
174
+ border border-gray-200 dark:border-gray-700 rounded-md cursor-not-allowed
175
175
  "
176
176
  >
177
177
  Previous
@@ -195,7 +195,7 @@
195
195
 
196
196
  <% pages_to_show.each do |page| %>
197
197
  <% if page == :gap %>
198
- <span class="px-2 py-1.5 text-sm text-gray-500">...</span>
198
+ <span class="px-2 py-1.5 text-sm text-gray-500 dark:text-gray-400">...</span>
199
199
  <% elsif page == current_page %>
200
200
  <span
201
201
  class="
@@ -206,17 +206,17 @@
206
206
  <%= page %>
207
207
  </span>
208
208
  <% else %>
209
- <%= link_to page, url_for(page: page), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" %>
209
+ <%= link_to page, url_for(page: page), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
210
210
  <% end %>
211
211
  <% end %>
212
212
 
213
213
  <% if current_page < total_pages %>
214
- <%= link_to "Next", url_for(page: current_page + 1), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" %>
214
+ <%= link_to "Next", url_for(page: current_page + 1), data: { turbo_frame: "executions_content", turbo_action: "advance" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
215
215
  <% else %>
216
216
  <span
217
217
  class="
218
- px-3 py-1.5 text-sm font-medium text-gray-400 bg-gray-100
219
- border border-gray-200 rounded-md cursor-not-allowed
218
+ px-3 py-1.5 text-sm font-medium text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800
219
+ border border-gray-200 dark:border-gray-700 rounded-md cursor-not-allowed
220
220
  "
221
221
  >
222
222
  Next
@@ -1,4 +1,4 @@
1
- <h2 class="text-2xl font-bold text-gray-900 mb-6">Executions</h2>
1
+ <h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-6">Executions</h2>
2
2
 
3
3
  <%= turbo_frame_tag "executions_content" do %>
4
4
  <%= render partial: "rubyllm/agents/executions/filters", locals: { agent_types: @agent_types, filter_stats: @filter_stats } %>
@@ -1,5 +1,5 @@
1
1
  <div class="mb-6">
2
- <%= link_to ruby_llm_agents.executions_path, class: "inline-flex items-center text-sm text-gray-500 hover:text-gray-700" do %>
2
+ <%= link_to ruby_llm_agents.executions_path, class: "inline-flex items-center text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200" do %>
3
3
  <svg
4
4
  class="w-4 h-4 mr-1"
5
5
  fill="none"
@@ -18,26 +18,26 @@
18
18
  </div>
19
19
 
20
20
  <!-- Header -->
21
- <div class="bg-white border border-gray-200 rounded-xl p-6 mb-6">
21
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
22
22
  <div class="flex items-center justify-between">
23
23
  <div>
24
24
  <div class="flex items-center gap-3">
25
- <h2 class="text-xl font-bold text-gray-900">
25
+ <h2 class="text-xl font-bold text-gray-900 dark:text-gray-100">
26
26
  <%= @execution.agent_type.gsub(/Agent$/, '') %>
27
27
  </h2>
28
28
 
29
29
  <%= render "rubyllm/agents/shared/status_badge", status: @execution.status, size: :md %>
30
30
  </div>
31
31
 
32
- <p class="text-sm text-gray-500 mt-1">
32
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
33
33
  Execution #<%= @execution.id %> · v<%= @execution.agent_version %>
34
34
  </p>
35
35
  </div>
36
36
 
37
- <div class="text-right text-sm text-gray-500">
37
+ <div class="text-right text-sm text-gray-500 dark:text-gray-400">
38
38
  <p><%= @execution.created_at.strftime("%b %d, %Y at %H:%M") %></p>
39
39
 
40
- <p class="text-xs text-gray-400">
40
+ <p class="text-xs text-gray-400 dark:text-gray-500">
41
41
  <%= time_ago_in_words(@execution.created_at) %> ago
42
42
  </p>
43
43
  </div>
@@ -72,8 +72,8 @@
72
72
  </div>
73
73
 
74
74
  <!-- Token Breakdown -->
75
- <div class="bg-white border border-gray-200 rounded-xl p-6 mb-6">
76
- <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide mb-4">Token Usage</h3>
75
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
76
+ <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-4">Token Usage</h3>
77
77
 
78
78
  <!-- Token Distribution Bar -->
79
79
  <%
@@ -85,67 +85,67 @@
85
85
  %>
86
86
  <div class="mb-6">
87
87
  <div class="flex justify-between text-xs mb-1.5">
88
- <span class="text-blue-600 font-medium">Input: <%= number_to_human_short(input_tokens) %> (<%= input_pct %>%)</span>
89
- <span class="text-green-600 font-medium">Output: <%= number_to_human_short(output_tokens) %> (<%= output_pct %>%)</span>
88
+ <span class="text-blue-600 dark:text-blue-400 font-medium">Input: <%= number_to_human_short(input_tokens) %> (<%= input_pct %>%)</span>
89
+ <span class="text-green-600 dark:text-green-400 font-medium">Output: <%= number_to_human_short(output_tokens) %> (<%= output_pct %>%)</span>
90
90
  </div>
91
- <div class="h-2.5 bg-gray-100 rounded-full overflow-hidden flex">
91
+ <div class="h-2.5 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden flex">
92
92
  <div class="bg-blue-500 transition-all" style="width: <%= input_pct %>%"></div>
93
93
  <div class="bg-green-500 transition-all" style="width: <%= output_pct %>%"></div>
94
94
  </div>
95
95
  </div>
96
96
 
97
97
  <!-- Detailed Metrics -->
98
- <div class="grid grid-cols-2 md:grid-cols-5 gap-6 pt-4 border-t border-gray-100">
98
+ <div class="grid grid-cols-2 md:grid-cols-5 gap-6 pt-4 border-t border-gray-100 dark:border-gray-700">
99
99
  <div>
100
- <p class="text-xs text-gray-500 uppercase tracking-wide">Input</p>
101
- <p class="text-lg font-semibold text-gray-900"><%= number_to_human_short(@execution.input_tokens || 0) %></p>
102
- <p class="text-xs text-gray-400"><%= number_to_human_short(@execution.input_cost || 0, prefix: "$", precision: 4) %></p>
100
+ <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Input</p>
101
+ <p class="text-lg font-semibold text-gray-900 dark:text-gray-100"><%= number_to_human_short(@execution.input_tokens || 0) %></p>
102
+ <p class="text-xs text-gray-400 dark:text-gray-500"><%= number_to_human_short(@execution.input_cost || 0, prefix: "$", precision: 4) %></p>
103
103
  </div>
104
104
 
105
105
  <div>
106
- <p class="text-xs text-gray-500 uppercase tracking-wide">Output</p>
107
- <p class="text-lg font-semibold text-gray-900"><%= number_to_human_short(@execution.output_tokens || 0) %></p>
108
- <p class="text-xs text-gray-400"><%= number_to_human_short(@execution.output_cost || 0, prefix: "$", precision: 4) %></p>
106
+ <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Output</p>
107
+ <p class="text-lg font-semibold text-gray-900 dark:text-gray-100"><%= number_to_human_short(@execution.output_tokens || 0) %></p>
108
+ <p class="text-xs text-gray-400 dark:text-gray-500"><%= number_to_human_short(@execution.output_cost || 0, prefix: "$", precision: 4) %></p>
109
109
  </div>
110
110
 
111
111
  <div>
112
- <p class="text-xs text-gray-500 uppercase tracking-wide">Cached</p>
113
- <p class="text-lg font-semibold text-gray-900"><%= number_to_human_short(@execution.cached_tokens || 0) %></p>
112
+ <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Cached</p>
113
+ <p class="text-lg font-semibold text-gray-900 dark:text-gray-100"><%= number_to_human_short(@execution.cached_tokens || 0) %></p>
114
114
  </div>
115
115
 
116
116
  <div>
117
- <p class="text-xs text-gray-500 uppercase tracking-wide">Cache Creation</p>
118
- <p class="text-lg font-semibold text-gray-900"><%= number_to_human_short(@execution.cache_creation_tokens || 0) %></p>
117
+ <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Cache Creation</p>
118
+ <p class="text-lg font-semibold text-gray-900 dark:text-gray-100"><%= number_to_human_short(@execution.cache_creation_tokens || 0) %></p>
119
119
  </div>
120
120
 
121
121
  <div>
122
- <p class="text-xs text-gray-500 uppercase tracking-wide">Tokens/Sec</p>
123
- <p class="text-lg font-semibold text-gray-900"><%= @execution.tokens_per_second || 'N/A' %></p>
122
+ <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Tokens/Sec</p>
123
+ <p class="text-lg font-semibold text-gray-900 dark:text-gray-100"><%= @execution.tokens_per_second || 'N/A' %></p>
124
124
  </div>
125
125
  </div>
126
126
  </div>
127
127
 
128
128
  <% if @execution.status_error? %>
129
129
  <!-- Error Details -->
130
- <div class="bg-red-50 border border-red-200 rounded-lg p-6 mb-6">
131
- <h3 class="text-lg font-semibold text-red-800 mb-2">Error Details</h3>
130
+ <div class="bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg p-6 mb-6">
131
+ <h3 class="text-lg font-semibold text-red-800 dark:text-red-300 mb-2">Error Details</h3>
132
132
 
133
- <p class="font-mono text-sm text-red-700 mb-2">
133
+ <p class="font-mono text-sm text-red-700 dark:text-red-400 mb-2">
134
134
  <%= @execution.error_class %>
135
135
  </p>
136
136
 
137
- <pre class="bg-red-100 rounded p-4 text-sm text-red-900 overflow-x-auto"><%= @execution.error_message %></pre>
137
+ <pre class="bg-red-100 dark:bg-red-900/50 rounded p-4 text-sm text-red-900 dark:text-red-200 overflow-x-auto"><%= @execution.error_message %></pre>
138
138
  </div>
139
139
  <% end %>
140
140
 
141
141
  <!-- Parameters -->
142
- <div class="bg-white border border-gray-200 rounded-xl p-6 mb-6">
142
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
143
143
  <div class="flex items-center justify-between mb-4">
144
- <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Parameters</h3>
144
+ <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Parameters</h3>
145
145
  <button
146
146
  type="button"
147
147
  data-copy-json="<%= Base64.strict_encode64(JSON.pretty_generate(@execution.parameters || {})) %>"
148
- class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-md transition-colors"
148
+ class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
149
149
  >
150
150
  <svg class="w-4 h-4 copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
151
151
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
@@ -156,18 +156,18 @@
156
156
  <span>Copy</span>
157
157
  </button>
158
158
  </div>
159
- <pre class="bg-gray-50 rounded-lg p-4 text-sm overflow-x-auto font-mono"><%= highlight_json(@execution.parameters || {}) %></pre>
159
+ <pre class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto font-mono"><%= highlight_json(@execution.parameters || {}) %></pre>
160
160
  </div>
161
161
 
162
162
  <!-- Response -->
163
163
  <% if @execution.response.present? %>
164
- <div class="bg-white border border-gray-200 rounded-xl p-6 mb-6">
164
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
165
165
  <div class="flex items-center justify-between mb-4">
166
- <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Response</h3>
166
+ <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Response</h3>
167
167
  <button
168
168
  type="button"
169
169
  data-copy-json="<%= Base64.strict_encode64(JSON.pretty_generate(@execution.response)) %>"
170
- class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-md transition-colors"
170
+ class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
171
171
  >
172
172
  <svg class="w-4 h-4 copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
173
173
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
@@ -178,19 +178,19 @@
178
178
  <span>Copy</span>
179
179
  </button>
180
180
  </div>
181
- <pre class="bg-gray-50 rounded-lg p-4 text-sm overflow-x-auto max-h-96 font-mono"><%= highlight_json(@execution.response) %></pre>
181
+ <pre class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto max-h-96 font-mono"><%= highlight_json(@execution.response) %></pre>
182
182
  </div>
183
183
  <% end %>
184
184
 
185
185
  <!-- Metadata -->
186
186
  <% if @execution.metadata.present? && @execution.metadata.any? %>
187
- <div class="bg-white border border-gray-200 rounded-xl p-6 mb-6">
187
+ <div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
188
188
  <div class="flex items-center justify-between mb-4">
189
- <h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Metadata</h3>
189
+ <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Metadata</h3>
190
190
  <button
191
191
  type="button"
192
192
  data-copy-json="<%= Base64.strict_encode64(JSON.pretty_generate(@execution.metadata)) %>"
193
- class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-md transition-colors"
193
+ class="copy-json-btn inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
194
194
  >
195
195
  <svg class="w-4 h-4 copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
196
196
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
@@ -201,7 +201,7 @@
201
201
  <span>Copy</span>
202
202
  </button>
203
203
  </div>
204
- <pre class="bg-gray-50 rounded-lg p-4 text-sm overflow-x-auto font-mono"><%= highlight_json(@execution.metadata) %></pre>
204
+ <pre class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto font-mono"><%= highlight_json(@execution.metadata) %></pre>
205
205
  </div>
206
206
  <% end %>
207
207