leva 0.1.10 → 0.1.11

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/app/assets/stylesheets/leva/application.css +3083 -15
  4. data/app/controllers/leva/design_system_controller.rb +9 -0
  5. data/app/views/layouts/leva/application.html.erb +23 -24
  6. data/app/views/leva/dataset_records/index.html.erb +63 -61
  7. data/app/views/leva/dataset_records/show.html.erb +115 -25
  8. data/app/views/leva/datasets/_dataset.html.erb +11 -18
  9. data/app/views/leva/datasets/_form.html.erb +18 -14
  10. data/app/views/leva/datasets/edit.html.erb +16 -4
  11. data/app/views/leva/datasets/index.html.erb +33 -41
  12. data/app/views/leva/datasets/new.html.erb +15 -4
  13. data/app/views/leva/datasets/show.html.erb +120 -139
  14. data/app/views/leva/design_system/index.html.erb +1731 -0
  15. data/app/views/leva/experiments/_experiment.html.erb +46 -31
  16. data/app/views/leva/experiments/_form.html.erb +62 -35
  17. data/app/views/leva/experiments/edit.html.erb +17 -3
  18. data/app/views/leva/experiments/index.html.erb +41 -36
  19. data/app/views/leva/experiments/new.html.erb +40 -19
  20. data/app/views/leva/experiments/show.html.erb +155 -98
  21. data/app/views/leva/runner_results/show.html.erb +271 -54
  22. data/app/views/leva/workbench/_evaluation_area.html.erb +18 -4
  23. data/app/views/leva/workbench/_prompt_content.html.erb +116 -111
  24. data/app/views/leva/workbench/_prompt_form.html.erb +24 -23
  25. data/app/views/leva/workbench/_prompt_sidebar.html.erb +57 -12
  26. data/app/views/leva/workbench/_results_section.html.erb +274 -112
  27. data/app/views/leva/workbench/_top_bar.html.erb +16 -6
  28. data/app/views/leva/workbench/edit.html.erb +46 -15
  29. data/app/views/leva/workbench/index.html.erb +5 -8
  30. data/app/views/leva/workbench/new.html.erb +74 -42
  31. data/config/routes.rb +2 -0
  32. data/lib/leva/version.rb +1 -1
  33. metadata +4 -2
@@ -1,10 +1,23 @@
1
1
  <% content_for :title, "New Prompt" %>
2
- <div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
3
- <h1 class="text-3xl font-bold text-indigo-400 mb-6">New Prompt</h1>
4
- <%= form_with(model: @prompt, url: workbench_index_path, local: true, class: "bg-gray-800 rounded-lg shadow-lg p-6", data: { controller: "prompt-selector" }) do |form| %>
2
+ <div class="container page">
3
+ <div class="page-header">
4
+ <div class="flex items-center gap-3">
5
+ <%= link_to workbench_index_path, class: "btn btn-ghost btn-sm" do %>
6
+ <svg class="icon-sm" fill="none" viewBox="0 0 24 24" stroke="currentColor">
7
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
8
+ </svg>
9
+ <% end %>
10
+ <div>
11
+ <h1 class="page-title" style="margin-bottom: 0;">New Prompt</h1>
12
+ <p class="text-sm text-muted" style="margin: 0;">Create a new prompt template</p>
13
+ </div>
14
+ </div>
15
+ </div>
16
+
17
+ <%= form_with(model: @prompt, url: workbench_index_path, local: true, class: "card", data: { controller: "prompt-selector" }) do |form| %>
5
18
  <% if @prompt.errors.any? %>
6
- <div class="bg-red-900 border border-red-700 text-red-100 px-4 py-3 rounded-lg mb-4">
7
- <h2><%= pluralize(@prompt.errors.count, "error") %> prohibited this prompt from being saved:</h2>
19
+ <div class="form-errors">
20
+ <p class="form-errors-title"><%= pluralize(@prompt.errors.count, "error") %> prohibited this prompt from being saved:</p>
8
21
  <ul>
9
22
  <% @prompt.errors.full_messages.each do |message| %>
10
23
  <li><%= message %></li>
@@ -12,58 +25,85 @@
12
25
  </ul>
13
26
  </div>
14
27
  <% end %>
15
- <div class="mb-4">
16
- <%= form.label :name, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
17
- <%= form.text_field :name, autofocus: true, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
28
+
29
+ <div class="form-group">
30
+ <div class="flex items-center gap-2 mb-2">
31
+ <svg class="icon-sm text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
32
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
33
+ </svg>
34
+ <%= form.label :name, class: "form-label", style: "margin-bottom: 0;" %>
35
+ </div>
36
+ <%= form.text_field :name, autofocus: true, class: "form-input", placeholder: "Enter a descriptive name..." %>
18
37
  </div>
19
- <div class="mb-4">
20
- <%= form.label :system_prompt, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
21
- <%= form.text_area :system_prompt, rows: 2, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
38
+
39
+ <div class="form-group">
40
+ <div class="flex items-center gap-2 mb-2">
41
+ <svg class="icon-sm text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
42
+ <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" />
43
+ </svg>
44
+ <%= form.label :system_prompt, "System Prompt", class: "form-label", style: "margin-bottom: 0;" %>
45
+ </div>
46
+ <%= form.text_area :system_prompt, rows: 2, class: "form-textarea prompt-textarea", placeholder: "Define the AI's role and behavior..." %>
22
47
  </div>
23
- <div class="mb-4">
24
- <%= form.label :predefined_prompt, "Select Predefined Prompt", class: "block text-sm font-semibold mb-2 text-indigo-300" %>
48
+
49
+ <div class="form-group">
50
+ <div class="flex items-center gap-2 mb-2">
51
+ <svg class="icon-sm text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
52
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
53
+ </svg>
54
+ <%= form.label :predefined_prompt, "Template", class: "form-label", style: "margin-bottom: 0;" %>
55
+ </div>
25
56
  <%= form.select :predefined_prompt,
26
57
  options_for_select([['Custom Prompt', '']] + @predefined_prompts.map { |name, content| [name, content] }),
27
58
  {},
28
- class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none",
59
+ class: "form-select",
29
60
  data: { action: "change->prompt-selector#toggleUserPrompt" }
30
61
  %>
31
62
  </div>
32
- <div class="mb-4" data-prompt-selector-target="userPromptField">
33
- <%= form.label :user_prompt, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
34
- <%= form.text_area :user_prompt, rows: 5, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
63
+
64
+ <div class="form-group" data-prompt-selector-target="userPromptField">
65
+ <div class="flex items-center gap-2 mb-2">
66
+ <svg class="icon-sm text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
67
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
68
+ </svg>
69
+ <%= form.label :user_prompt, "User Prompt Template", class: "form-label", style: "margin-bottom: 0;" %>
70
+ </div>
71
+ <%= form.text_area :user_prompt, rows: 5, class: "form-textarea prompt-textarea", placeholder: "Use {{ variable }} syntax for dynamic content..." %>
35
72
  </div>
36
- <div class="mb-4 hidden" data-prompt-selector-target="promptPreview" id="prompt-preview">
37
- <h3 class="text-lg font-semibold mb-2 text-indigo-300">Prompt Preview</h3>
38
- <div
39
- class="bg-gray-700 text-white p-3 rounded-lg whitespace-pre-wrap"
73
+
74
+ <div class="form-group hidden" data-prompt-selector-target="promptPreview" id="prompt-preview">
75
+ <label class="form-label">Prompt Preview</label>
76
+ <div
77
+ class="card-subtle p-4"
78
+ style="background: var(--gray-800); white-space: pre-wrap;"
40
79
  data-prompt-selector-target="previewContent"
41
80
  id="preview-content"
42
81
  ></div>
43
82
 
44
- <!-- show-full button, hidden until overflow is detected -->
45
83
  <button
46
84
  id="show-full-preview"
47
85
  type="button"
48
- class="mt-2 text-indigo-400 underline hidden"
86
+ class="btn btn-ghost btn-sm mt-2 hidden"
49
87
  >Show full</button>
50
88
 
51
- <!-- HTML5 dialog for full text -->
52
- <dialog id="full-preview-dialog" class="p-0 m-0">
53
- <div class="bg-gray-800 text-white p-4 rounded-lg max-h-[80vh] overflow-auto whitespace-pre-wrap">
54
- <div id="dialog-content"></div>
55
- <div class="text-right mt-4">
56
- <button id="close-full-preview" class="px-4 py-2 bg-indigo-600 rounded text-white">Close</button>
57
- </div>
89
+ <dialog id="full-preview-dialog">
90
+ <div class="dialog-header">
91
+ <h3 class="dialog-title">Full Preview</h3>
92
+ </div>
93
+ <div class="dialog-body" id="dialog-content" style="white-space: pre-wrap;"></div>
94
+ <div class="dialog-footer">
95
+ <button id="close-full-preview" class="btn btn-primary">Close</button>
58
96
  </div>
59
97
  </dialog>
60
98
  </div>
61
- <div class="flex items-center justify-end space-x-4">
62
- <%= link_to "Cancel", workbench_index_path, class: "px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:bg-gray-800 hover:text-white transition-colors duration-150 ease-in-out" %>
63
- <%= form.submit "Create Prompt", class: "px-3 py-2 rounded-md text-sm font-medium bg-indigo-600 text-white shadow-lg hover:bg-indigo-700 transition-colors duration-150 ease-in-out" %>
99
+
100
+ <div class="form-actions justify-end">
101
+ <%= link_to "Cancel", workbench_index_path, class: "btn btn-ghost" %>
102
+ <%= form.submit "Create Prompt", class: "btn btn-primary" %>
64
103
  </div>
65
104
  <% end %>
66
105
  </div>
106
+
67
107
  <script>
68
108
  (() => {
69
109
  const application = Stimulus.Application.start()
@@ -98,7 +138,6 @@
98
138
  })
99
139
  })()
100
140
 
101
- // Prompt preview scrollbar and dialog functionality
102
141
  document.addEventListener('DOMContentLoaded', () => {
103
142
  const wrapper = document.getElementById('prompt-preview');
104
143
  const preview = document.getElementById('preview-content');
@@ -109,28 +148,24 @@
109
148
 
110
149
  if (!preview) return;
111
150
 
112
- // Check if the preview content is already populated by Stimulus
113
151
  const checkPreviewContent = () => {
114
152
  if (preview.textContent.trim().length > 0) {
115
- // Detect overflow
116
153
  if (preview.scrollHeight > preview.clientHeight || preview.scrollWidth > preview.clientWidth) {
117
154
  preview.style.maxHeight = '12em';
118
155
  preview.style.overflow = 'auto';
119
156
  showBtn.classList.remove('hidden');
120
157
  }
121
158
  } else {
122
- // If not populated yet, check again after a short delay
123
159
  setTimeout(checkPreviewContent, 100);
124
160
  }
125
161
  };
126
162
 
127
- // Start checking once the wrapper is visible
128
163
  const observer = new MutationObserver((mutations) => {
129
164
  mutations.forEach((mutation) => {
130
165
  if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
131
166
  if (!wrapper.classList.contains('hidden')) {
132
167
  checkPreviewContent();
133
- observer.disconnect(); // Stop observing once we've detected the change
168
+ observer.disconnect();
134
169
  }
135
170
  }
136
171
  });
@@ -138,15 +173,12 @@
138
173
 
139
174
  observer.observe(wrapper, { attributes: true });
140
175
 
141
- // Show full in dialog
142
176
  showBtn.addEventListener('click', () => {
143
177
  dialogBody.textContent = preview.textContent;
144
178
  dialog.showModal();
145
179
  });
146
180
 
147
- // Close dialog
148
181
  closeBtn.addEventListener('click', () => dialog.close());
149
182
  });
150
183
  </script>
151
- <!-- Include marked.js for Markdown parsing -->
152
184
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
data/config/routes.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  Leva::Engine.routes.draw do
2
2
  root "workbench#index"
3
3
 
4
+ get "design_system", to: "design_system#index"
5
+
4
6
  resources :datasets do
5
7
  resources :dataset_records, path: "records", only: [ :index, :show ]
6
8
  end
data/lib/leva/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Leva
2
- VERSION = "0.1.10"
2
+ VERSION = "0.1.11"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leva
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kieran Klaassen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-16 00:00:00.000000000 Z
11
+ date: 2025-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -55,6 +55,7 @@ files:
55
55
  - app/controllers/leva/application_controller.rb
56
56
  - app/controllers/leva/dataset_records_controller.rb
57
57
  - app/controllers/leva/datasets_controller.rb
58
+ - app/controllers/leva/design_system_controller.rb
58
59
  - app/controllers/leva/experiments_controller.rb
59
60
  - app/controllers/leva/runner_results_controller.rb
60
61
  - app/controllers/leva/workbench_controller.rb
@@ -82,6 +83,7 @@ files:
82
83
  - app/views/leva/datasets/index.html.erb
83
84
  - app/views/leva/datasets/new.html.erb
84
85
  - app/views/leva/datasets/show.html.erb
86
+ - app/views/leva/design_system/index.html.erb
85
87
  - app/views/leva/experiments/_experiment.html.erb
86
88
  - app/views/leva/experiments/_form.html.erb
87
89
  - app/views/leva/experiments/edit.html.erb